LCOV - code coverage report
Current view: top level - src/backend/storage/file - sharedfileset.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 67 69 97.1 %
Date: 2019-08-24 16:07:17 Functions: 10 10 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * sharedfileset.c
       4             :  *    Shared temporary file management.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/storage/file/sharedfileset.c
      11             :  *
      12             :  * SharedFileSets provide a temporary namespace (think directory) so that
      13             :  * files can be discovered by name, and a shared ownership semantics so that
      14             :  * shared files survive until the last user detaches.
      15             :  *
      16             :  *-------------------------------------------------------------------------
      17             :  */
      18             : 
      19             : #include "postgres.h"
      20             : 
      21             : #include <limits.h>
      22             : 
      23             : #include "catalog/pg_tablespace.h"
      24             : #include "commands/tablespace.h"
      25             : #include "miscadmin.h"
      26             : #include "storage/dsm.h"
      27             : #include "storage/sharedfileset.h"
      28             : #include "utils/builtins.h"
      29             : #include "utils/hashutils.h"
      30             : 
      31             : static void SharedFileSetOnDetach(dsm_segment *segment, Datum datum);
      32             : static void SharedFileSetPath(char *path, SharedFileSet *fileset, Oid tablespace);
      33             : static void SharedFilePath(char *path, SharedFileSet *fileset, const char *name);
      34             : static Oid  ChooseTablespace(const SharedFileSet *fileset, const char *name);
      35             : 
      36             : /*
      37             :  * Initialize a space for temporary files that can be opened for read-only
      38             :  * access by other backends.  Other backends must attach to it before
      39             :  * accessing it.  Associate this SharedFileSet with 'seg'.  Any contained
      40             :  * files will be deleted when the last backend detaches.
      41             :  *
      42             :  * Files will be distributed over the tablespaces configured in
      43             :  * temp_tablespaces.
      44             :  *
      45             :  * Under the covers the set is one or more directories which will eventually
      46             :  * be deleted when there are no backends attached.
      47             :  */
      48             : void
      49         184 : SharedFileSetInit(SharedFileSet *fileset, dsm_segment *seg)
      50             : {
      51             :     static uint32 counter = 0;
      52             : 
      53         184 :     SpinLockInit(&fileset->mutex);
      54         184 :     fileset->refcnt = 1;
      55         184 :     fileset->creator_pid = MyProcPid;
      56         184 :     fileset->number = counter;
      57         184 :     counter = (counter + 1) % INT_MAX;
      58             : 
      59             :     /* Capture the tablespace OIDs so that all backends agree on them. */
      60         184 :     PrepareTempTablespaces();
      61         184 :     fileset->ntablespaces =
      62         184 :         GetTempTablespaces(&fileset->tablespaces[0],
      63             :                            lengthof(fileset->tablespaces));
      64         184 :     if (fileset->ntablespaces == 0)
      65             :     {
      66         184 :         fileset->tablespaces[0] = DEFAULTTABLESPACE_OID;
      67         184 :         fileset->ntablespaces = 1;
      68             :     }
      69             : 
      70             :     /* Register our cleanup callback. */
      71         184 :     on_dsm_detach(seg, SharedFileSetOnDetach, PointerGetDatum(fileset));
      72         184 : }
      73             : 
      74             : /*
      75             :  * Attach to a set of directories that was created with SharedFileSetInit.
      76             :  */
      77             : void
      78         300 : SharedFileSetAttach(SharedFileSet *fileset, dsm_segment *seg)
      79             : {
      80             :     bool        success;
      81             : 
      82         300 :     SpinLockAcquire(&fileset->mutex);
      83         300 :     if (fileset->refcnt == 0)
      84           0 :         success = false;
      85             :     else
      86             :     {
      87         300 :         ++fileset->refcnt;
      88         300 :         success = true;
      89             :     }
      90         300 :     SpinLockRelease(&fileset->mutex);
      91             : 
      92         300 :     if (!success)
      93           0 :         ereport(ERROR,
      94             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
      95             :                  errmsg("could not attach to a SharedFileSet that is already destroyed")));
      96             : 
      97             :     /* Register our cleanup callback. */
      98         300 :     on_dsm_detach(seg, SharedFileSetOnDetach, PointerGetDatum(fileset));
      99         300 : }
     100             : 
     101             : /*
     102             :  * Create a new file in the given set.
     103             :  */
     104             : File
     105        1342 : SharedFileSetCreate(SharedFileSet *fileset, const char *name)
     106             : {
     107             :     char        path[MAXPGPATH];
     108             :     File        file;
     109             : 
     110        1342 :     SharedFilePath(path, fileset, name);
     111        1342 :     file = PathNameCreateTemporaryFile(path, false);
     112             : 
     113             :     /* If we failed, see if we need to create the directory on demand. */
     114        1342 :     if (file <= 0)
     115             :     {
     116             :         char        tempdirpath[MAXPGPATH];
     117             :         char        filesetpath[MAXPGPATH];
     118         190 :         Oid         tablespace = ChooseTablespace(fileset, name);
     119             : 
     120         190 :         TempTablespacePath(tempdirpath, tablespace);
     121         190 :         SharedFileSetPath(filesetpath, fileset, tablespace);
     122         190 :         PathNameCreateTemporaryDir(tempdirpath, filesetpath);
     123         190 :         file = PathNameCreateTemporaryFile(path, true);
     124             :     }
     125             : 
     126        1342 :     return file;
     127             : }
     128             : 
     129             : /*
     130             :  * Open a file that was created with SharedFileSetCreate(), possibly in
     131             :  * another backend.
     132             :  */
     133             : File
     134        2588 : SharedFileSetOpen(SharedFileSet *fileset, const char *name)
     135             : {
     136             :     char        path[MAXPGPATH];
     137             :     File        file;
     138             : 
     139        2588 :     SharedFilePath(path, fileset, name);
     140        2588 :     file = PathNameOpenTemporaryFile(path);
     141             : 
     142        2588 :     return file;
     143             : }
     144             : 
     145             : /*
     146             :  * Delete a file that was created with SharedFileSetCreate().
     147             :  * Return true if the file existed, false if didn't.
     148             :  */
     149             : bool
     150        1342 : SharedFileSetDelete(SharedFileSet *fileset, const char *name,
     151             :                     bool error_on_failure)
     152             : {
     153             :     char        path[MAXPGPATH];
     154             : 
     155        1342 :     SharedFilePath(path, fileset, name);
     156             : 
     157        1342 :     return PathNameDeleteTemporaryFile(path, error_on_failure);
     158             : }
     159             : 
     160             : /*
     161             :  * Delete all files in the set.
     162             :  */
     163             : void
     164         216 : SharedFileSetDeleteAll(SharedFileSet *fileset)
     165             : {
     166             :     char        dirpath[MAXPGPATH];
     167             :     int         i;
     168             : 
     169             :     /*
     170             :      * Delete the directory we created in each tablespace.  Doesn't fail
     171             :      * because we use this in error cleanup paths, but can generate LOG
     172             :      * message on IO error.
     173             :      */
     174         432 :     for (i = 0; i < fileset->ntablespaces; ++i)
     175             :     {
     176         216 :         SharedFileSetPath(dirpath, fileset, fileset->tablespaces[i]);
     177         216 :         PathNameDeleteTemporaryDir(dirpath);
     178             :     }
     179         216 : }
     180             : 
     181             : /*
     182             :  * Callback function that will be invoked when this backend detaches from a
     183             :  * DSM segment holding a SharedFileSet that it has created or attached to.  If
     184             :  * we are the last to detach, then try to remove the directories and
     185             :  * everything in them.  We can't raise an error on failures, because this runs
     186             :  * in error cleanup paths.
     187             :  */
     188             : static void
     189         484 : SharedFileSetOnDetach(dsm_segment *segment, Datum datum)
     190             : {
     191         484 :     bool        unlink_all = false;
     192         484 :     SharedFileSet *fileset = (SharedFileSet *) DatumGetPointer(datum);
     193             : 
     194         484 :     SpinLockAcquire(&fileset->mutex);
     195             :     Assert(fileset->refcnt > 0);
     196         484 :     if (--fileset->refcnt == 0)
     197         184 :         unlink_all = true;
     198         484 :     SpinLockRelease(&fileset->mutex);
     199             : 
     200             :     /*
     201             :      * If we are the last to detach, we delete the directory in all
     202             :      * tablespaces.  Note that we are still actually attached for the rest of
     203             :      * this function so we can safely access its data.
     204             :      */
     205         484 :     if (unlink_all)
     206         184 :         SharedFileSetDeleteAll(fileset);
     207         484 : }
     208             : 
     209             : /*
     210             :  * Build the path for the directory holding the files backing a SharedFileSet
     211             :  * in a given tablespace.
     212             :  */
     213             : static void
     214        5678 : SharedFileSetPath(char *path, SharedFileSet *fileset, Oid tablespace)
     215             : {
     216             :     char        tempdirpath[MAXPGPATH];
     217             : 
     218        5678 :     TempTablespacePath(tempdirpath, tablespace);
     219       11356 :     snprintf(path, MAXPGPATH, "%s/%s%lu.%u.sharedfileset",
     220             :              tempdirpath, PG_TEMP_FILE_PREFIX,
     221        5678 :              (unsigned long) fileset->creator_pid, fileset->number);
     222        5678 : }
     223             : 
     224             : /*
     225             :  * Sorting hat to determine which tablespace a given shared temporary file
     226             :  * belongs in.
     227             :  */
     228             : static Oid
     229        5462 : ChooseTablespace(const SharedFileSet *fileset, const char *name)
     230             : {
     231        5462 :     uint32      hash = hash_any((const unsigned char *) name, strlen(name));
     232             : 
     233        5462 :     return fileset->tablespaces[hash % fileset->ntablespaces];
     234             : }
     235             : 
     236             : /*
     237             :  * Compute the full path of a file in a SharedFileSet.
     238             :  */
     239             : static void
     240        5272 : SharedFilePath(char *path, SharedFileSet *fileset, const char *name)
     241             : {
     242             :     char        dirpath[MAXPGPATH];
     243             : 
     244        5272 :     SharedFileSetPath(dirpath, fileset, ChooseTablespace(fileset, name));
     245        5272 :     snprintf(path, MAXPGPATH, "%s/%s", dirpath, name);
     246        5272 : }

Generated by: LCOV version 1.13