2017-12-02 01:30:56 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* sharedfileset.c
|
|
|
|
* Shared temporary file management.
|
|
|
|
*
|
2018-01-03 05:30:12 +01:00
|
|
|
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
|
2017-12-02 01:30:56 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
|
|
|
* src/backend/storage/file/sharedfileset.c
|
|
|
|
*
|
|
|
|
* SharefFileSets provide a temporary namespace (think directory) so that
|
|
|
|
* files can be discovered by name, and a shared ownership semantics so that
|
|
|
|
* shared files survive until the last user detaches.
|
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "access/hash.h"
|
|
|
|
#include "catalog/pg_tablespace.h"
|
|
|
|
#include "commands/tablespace.h"
|
|
|
|
#include "miscadmin.h"
|
|
|
|
#include "storage/dsm.h"
|
|
|
|
#include "storage/sharedfileset.h"
|
|
|
|
#include "utils/builtins.h"
|
|
|
|
|
|
|
|
static void SharedFileSetOnDetach(dsm_segment *segment, Datum datum);
|
|
|
|
static void SharedFileSetPath(char *path, SharedFileSet *fileset, Oid tablespace);
|
|
|
|
static void SharedFilePath(char *path, SharedFileSet *fileset, const char *name);
|
|
|
|
static Oid ChooseTablespace(const SharedFileSet *fileset, const char *name);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize a space for temporary files that can be opened for read-only
|
|
|
|
* access by other backends. Other backends must attach to it before
|
|
|
|
* accessing it. Associate this SharedFileSet with 'seg'. Any contained
|
|
|
|
* files will be deleted when the last backend detaches.
|
|
|
|
*
|
|
|
|
* 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 when there are no backends attached.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SharedFileSetInit(SharedFileSet *fileset, dsm_segment *seg)
|
|
|
|
{
|
|
|
|
static uint32 counter = 0;
|
|
|
|
|
|
|
|
SpinLockInit(&fileset->mutex);
|
|
|
|
fileset->refcnt = 1;
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
fileset->tablespaces[0] = DEFAULTTABLESPACE_OID;
|
|
|
|
fileset->ntablespaces = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Register our cleanup callback. */
|
|
|
|
on_dsm_detach(seg, SharedFileSetOnDetach, PointerGetDatum(fileset));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attach to a set of directories that was created with SharedFileSetInit.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SharedFileSetAttach(SharedFileSet *fileset, dsm_segment *seg)
|
|
|
|
{
|
|
|
|
bool success;
|
|
|
|
|
|
|
|
SpinLockAcquire(&fileset->mutex);
|
|
|
|
if (fileset->refcnt == 0)
|
|
|
|
success = false;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++fileset->refcnt;
|
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
SpinLockRelease(&fileset->mutex);
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
|
|
errmsg("could not attach to a SharedFileSet that is already destroyed")));
|
|
|
|
|
|
|
|
/* Register our cleanup callback. */
|
|
|
|
on_dsm_detach(seg, SharedFileSetOnDetach, PointerGetDatum(fileset));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a new file in the given set.
|
|
|
|
*/
|
|
|
|
File
|
|
|
|
SharedFileSetCreate(SharedFileSet *fileset, const char *name)
|
|
|
|
{
|
|
|
|
char path[MAXPGPATH];
|
|
|
|
File file;
|
|
|
|
|
|
|
|
SharedFilePath(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);
|
|
|
|
SharedFileSetPath(filesetpath, fileset, tablespace);
|
|
|
|
PathNameCreateTemporaryDir(tempdirpath, filesetpath);
|
|
|
|
file = PathNameCreateTemporaryFile(path, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open a file that was created with SharedFileSetCreate(), possibly in
|
|
|
|
* another backend.
|
|
|
|
*/
|
|
|
|
File
|
|
|
|
SharedFileSetOpen(SharedFileSet *fileset, const char *name)
|
|
|
|
{
|
|
|
|
char path[MAXPGPATH];
|
|
|
|
File file;
|
|
|
|
|
|
|
|
SharedFilePath(path, fileset, name);
|
|
|
|
file = PathNameOpenTemporaryFile(path);
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete a file that was created with PathNameCreateShared().
|
|
|
|
* Return true if the file existed, false if didn't.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
SharedFileSetDelete(SharedFileSet *fileset, const char *name,
|
|
|
|
bool error_on_failure)
|
|
|
|
{
|
|
|
|
char path[MAXPGPATH];
|
|
|
|
|
|
|
|
SharedFilePath(path, fileset, name);
|
|
|
|
|
|
|
|
return PathNameDeleteTemporaryFile(path, error_on_failure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete all files in the set.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SharedFileSetDeleteAll(SharedFileSet *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)
|
|
|
|
{
|
|
|
|
SharedFileSetPath(dirpath, fileset, fileset->tablespaces[i]);
|
|
|
|
PathNameDeleteTemporaryDir(dirpath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Callback function that will be invoked when this backend detaches from a
|
|
|
|
* DSM segment holding a SharedFileSet that it has created or attached to. If
|
|
|
|
* we are the last to detach, then try to remove the directories and
|
|
|
|
* everything in them. We can't raise an error on failures, because this runs
|
|
|
|
* in error cleanup paths.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
SharedFileSetOnDetach(dsm_segment *segment, Datum datum)
|
|
|
|
{
|
|
|
|
bool unlink_all = false;
|
|
|
|
SharedFileSet *fileset = (SharedFileSet *) DatumGetPointer(datum);
|
|
|
|
|
|
|
|
SpinLockAcquire(&fileset->mutex);
|
|
|
|
Assert(fileset->refcnt > 0);
|
|
|
|
if (--fileset->refcnt == 0)
|
|
|
|
unlink_all = true;
|
|
|
|
SpinLockRelease(&fileset->mutex);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are the last to detach, we delete the directory in all
|
|
|
|
* tablespaces. Note that we are still actually attached for the rest of
|
|
|
|
* this function so we can safely access its data.
|
|
|
|
*/
|
|
|
|
if (unlink_all)
|
|
|
|
SharedFileSetDeleteAll(fileset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build the path for the directory holding the files backing a SharedFileSet
|
|
|
|
* in a given tablespace.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
SharedFileSetPath(char *path, SharedFileSet *fileset, Oid tablespace)
|
|
|
|
{
|
|
|
|
char tempdirpath[MAXPGPATH];
|
|
|
|
|
|
|
|
TempTablespacePath(tempdirpath, tablespace);
|
2018-05-02 21:52:54 +02:00
|
|
|
snprintf(path, MAXPGPATH, "%s/%s%lu.%u.sharedfileset",
|
2017-12-02 01:30:56 +01:00
|
|
|
tempdirpath, PG_TEMP_FILE_PREFIX,
|
2018-05-02 21:52:54 +02:00
|
|
|
(unsigned long) fileset->creator_pid, fileset->number);
|
2017-12-02 01:30:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sorting hat to determine which tablespace a given shared temporary file
|
|
|
|
* belongs in.
|
|
|
|
*/
|
|
|
|
static Oid
|
|
|
|
ChooseTablespace(const SharedFileSet *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 SharedFileSet.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
SharedFilePath(char *path, SharedFileSet *fileset, const char *name)
|
|
|
|
{
|
|
|
|
char dirpath[MAXPGPATH];
|
|
|
|
|
|
|
|
SharedFileSetPath(dirpath, fileset, ChooseTablespace(fileset, name));
|
|
|
|
snprintf(path, MAXPGPATH, "%s/%s", dirpath, name);
|
|
|
|
}
|