Add server support for "plugin" libraries that can be used for add-on tasks
authorTom Lane
Tue, 15 Aug 2006 18:26:59 +0000 (18:26 +0000)
committerTom Lane
Tue, 15 Aug 2006 18:26:59 +0000 (18:26 +0000)
such as debugging and performance measurement.  This consists of two features:
a table of "rendezvous variables" that allows separately-loaded shared
libraries to communicate, and a new GUC setting "local_preload_libraries"
that allows libraries to be loaded into specific sessions without explicit
cooperation from the client application.  To make local_preload_libraries
as flexible as possible, we do not restrict its use to superusers; instead,
it is restricted to load only libraries stored in $libdir/plugins/.  The
existing LOAD command has also been modified to allow non-superusers to
LOAD libraries stored in this directory.

This patch also renames the existing GUC variable preload_libraries to
shared_preload_libraries (after a suggestion by Simon Riggs) and does some
code refactoring in dfmgr.c to improve clarity.

Korry Douglas, with a little help from Tom Lane.

doc/src/sgml/config.sgml
doc/src/sgml/ref/load.sgml
src/backend/postmaster/postmaster.c
src/backend/tcop/postgres.c
src/backend/tcop/utility.c
src/backend/utils/fmgr/dfmgr.c
src/backend/utils/init/miscinit.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/include/fmgr.h
src/include/miscadmin.h

index 706ccba56c871217ec9160e877d949e3dfcfb3c7..b35fa9c1ccf6f55162594a96ced1d6d49ea43c50 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
 
   Server Configuration
@@ -949,10 +949,10 @@ SET ENABLE_SEQSCAN TO OFF;
       
      
      
-     preload-libraries" xreflabel="preload_libraries">
-      preload_libraries (string)
+     shared-preload-libraries" xreflabel="shared_preload_libraries">
+      shared_preload_libraries (string)
       
-       preload_libraries configuration parameter
+       shared_preload_libraries configuration parameter
       
       
        
@@ -963,6 +963,7 @@ SET ENABLE_SEQSCAN TO OFF;
         mylib.so (or on some platforms,
         mylib.sl) to be preloaded from the installation's
         standard library directory.
+        This parameter can only be set at server start.
        
 
        
@@ -3642,6 +3643,60 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
        
       
      
+     
+     
+      local_preload_libraries (string)
+      
+       local_preload_libraries configuration parameter
+      
+      
+       $libdir/plugins
+      
+      
+       
+        This variable specifies one or more shared libraries that are
+        to be preloaded at connection start.  If more than one library
+        is to be loaded, separate their names with commas.
+        This parameter cannot be changed after the start of a particular
+        session.
+       
+
+       
+        Because this is not a superuser-only option, the libraries
+        that can be loaded are restricted to those appearing in the
+        plugins subdirectory of the installation's
+        standard library directory.  (It is the database administrator's
+        responsibility to ensure that only safe libraries
+        are installed there.)  Entries in local_preload_libraries
+        can specify this directory explicitly, for example
+        $libdir/plugins/mylib, or just specify
+        the library name — mylib would have
+        the same effect as $libdir/plugins/mylib.
+       
+
+       
+        There is no performance advantage to loading a library at session
+        start rather than when it is first used.  Rather, the intent of
+        this feature is to allow debugging or performance-measurement
+        libraries to be loaded into specific sessions without an explicit
+        LOAD command being given.  For example, debugging could
+        be enabled for all sessions under a given user name by setting
+        this parameter with ALTER USER SET.
+       
+
+       
+        If a specified library is not found,
+        the connection attempt will fail.
+       
+
+       
+        Every  PostgreSQL-supported library has a magic
+        block that is checked to guarantee compatibility.  
+        For this reason, non-PostgreSQL libraries cannot be 
+        loaded in this way.
+       
+      
+     
 
      
     
index b29dfd4ed4197ce81b7cefd41735068853032796..6a6e9d9098e86024831d8bba4529d8f215aa6acc 100644 (file)
@@ -1,5 +1,5 @@
 
 
 
@@ -44,6 +44,19 @@ LOAD 'filename'
    shared library file name extension.  See  for
    more information on this topic.
   
+
+  
+   $libdir/plugins
+  
+
+  
+   Non-superusers may only apply LOAD to library files
+   located in $libdir/plugins/ — the specified
+   filename must begin
+   with exactly that string.  (It is the database administrator's
+   responsibility to ensure that only safe libraries
+   are installed there.)
+  
  
 
  
index eefa974dee9f38a5d959e8ed40c6a8d2b9acb692..130415c74cb7e8b6dd8b42debdb88f068734d0a7 100644 (file)
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.498 2006/08/08 19:15:07 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.499 2006/08/15 18:26:58 tgl Exp $
  *
  * NOTES
  *
@@ -709,7 +709,7 @@ PostmasterMain(int argc, char *argv[])
    /*
     * process any libraries that should be preloaded at postmaster start
     */
-   process_preload_libraries();
+   process_shared_preload_libraries();
 
    /*
     * Remove old temporary files.  At this point there can be no other
index 9cb61c16cb578c9efd643be36b8a9782557ce476..af9578b15ac32152cf30e7723ff26ae835b9f4b2 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.498 2006/08/13 22:18:08 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.499 2006/08/15 18:26:58 tgl Exp $
  *
  * NOTES
  *   this is the "main" module of the postgres backend and
@@ -3000,6 +3000,12 @@ PostgresMain(int argc, char *argv[], const char *username)
    if (IsUnderPostmaster && Log_disconnections)
        on_proc_exit(log_disconnections, 0);
 
+   /*
+    * process any libraries that should be preloaded at backend start
+    * (this likewise can't be done until GUC settings are complete)
+    */
+   process_local_preload_libraries();
+
    /*
     * Send this backend's cancellation info to the frontend.
     */
index 7d6941ddcef93aa57f389711ae122666e8a63a09..ce86a90ba3f570e5f562851bead9d97486447e16 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.265 2006/08/12 20:05:56 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.266 2006/08/15 18:26:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -886,12 +886,9 @@ ProcessUtility(Node *parsetree,
            {
                LoadStmt   *stmt = (LoadStmt *) parsetree;
 
-               if (!superuser())
-                   ereport(ERROR,
-                           (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                            errmsg("must be superuser to do LOAD")));
                closeAllVfds(); /* probably not necessary... */
-               load_file(stmt->filename);
+               /* Allowed names are restricted if you're not superuser */
+               load_file(stmt->filename, !superuser());
            }
            break;
 
index fd2c54c2118ae1a33519f6d49b38d0f4df54fa34..3c2b34e3e63fa0656be478a8f69ddd92a51dd8f1 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.87 2006/08/08 19:15:08 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.88 2006/08/15 18:26:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #endif
 #include "miscadmin.h"
 #include "utils/dynamic_loader.h"
+#include "utils/hsearch.h"
 
 
 /* signatures for PostgreSQL-specific library init/fini functions */
 typedef void (*PG_init_t)(void);
 typedef void (*PG_fini_t)(void);
 
+/* hashtable entry for rendezvous variables */
+typedef struct
+{ 
+   char    varName[NAMEDATALEN];   /* hash key (must be first) */
+   void   *varValue;
+} rendezvousHashEntry;
+
 /*
  * List of dynamically loaded files (kept in malloc'd memory).
  */
@@ -62,10 +70,13 @@ static DynamicFileList *file_tail = NULL;
 
 char      *Dynamic_library_path;
 
+static void *internal_load_library(const char *libname);
+static void internal_unload_library(const char *libname);
 static bool file_exists(const char *name);
-static char *find_in_dynamic_libpath(const char *basename);
 static char *expand_dynamic_library_name(const char *name);
+static void check_restricted_library_name(const char *name);
 static char *substitute_libpath_macro(const char *name);
+static char *find_in_dynamic_libpath(const char *basename);
 
 /* Magic structure that module needs to match to be accepted */
 static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA;
@@ -73,7 +84,7 @@ static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA;
 
 /*
  * Load the specified dynamic-link library file, and look for a function
- * named funcname in it.  (funcname can be NULL to just load the file.)
+ * named funcname in it.
  *
  * If the function is not found, we raise an error if signalNotFound is true,
  * else return (PGFunction) NULL.  Note that errors in loading the library
@@ -88,37 +99,107 @@ PGFunction
 load_external_function(char *filename, char *funcname,
                       bool signalNotFound, void **filehandle)
 {
-   DynamicFileList *file_scanner;
+   char       *fullname;
+   void       *lib_handle;
    PGFunction  retval;
+
+   /* Expand the possibly-abbreviated filename to an exact path name */
+   fullname = expand_dynamic_library_name(filename);
+
+   /* Load the shared library, unless we already did */
+   lib_handle = internal_load_library(fullname);
+
+   /* Return handle if caller wants it */
+   if (filehandle)
+       *filehandle = lib_handle;
+
+   /* Look up the function within the library */
+   retval = pg_dlsym(lib_handle, funcname);
+
+   if (retval == NULL && signalNotFound)
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                errmsg("could not find function \"%s\" in file \"%s\"",
+                       funcname, fullname)));
+
+   pfree(fullname);
+   return retval;
+}
+
+/*
+ * This function loads a shlib file without looking up any particular
+ * function in it. If the same shlib has previously been loaded,
+ * unload and reload it.
+ *
+ * When 'restrict' is true, only libraries in the presumed-secure
+ * directory $libdir/plugins may be referenced.
+ */
+void
+load_file(const char *filename, bool restrict)
+{
+   char       *fullname;
+
+   /* Apply security restriction if requested */
+   if (restrict)
+       check_restricted_library_name(filename);
+
+   /* Expand the possibly-abbreviated filename to an exact path name */
+   fullname = expand_dynamic_library_name(filename);
+
+   /* Unload the library if currently loaded */
+   internal_unload_library(fullname);
+
+   /* Load the shared library */
+   (void) internal_load_library(fullname);
+
+   pfree(fullname);
+}
+
+/*
+ * Lookup a function whose library file is already loaded.
+ * Return (PGFunction) NULL if not found.
+ */
+PGFunction
+lookup_external_function(void *filehandle, char *funcname)
+{
+   return pg_dlsym(filehandle, funcname);
+}
+
+
+/*
+ * Load the specified dynamic-link library file, unless it already is
+ * loaded.  Return the pg_dl* handle for the file.
+ *
+ * Note: libname is expected to be an exact name for the library file.
+ */
+static void *
+internal_load_library(const char *libname)
+{
+   DynamicFileList *file_scanner;
    PGModuleMagicFunction magic_func;
    char       *load_error;
    struct stat stat_buf;
-   char       *fullname;
    PG_init_t   PG_init;
 
-   fullname = expand_dynamic_library_name(filename);
-   if (!fullname)
-       fullname = pstrdup(filename);
-   /* at this point fullname is always freshly palloc'd */
-
    /*
     * Scan the list of loaded FILES to see if the file has been loaded.
     */
    for (file_scanner = file_list;
         file_scanner != NULL &&
-        strcmp(fullname, file_scanner->filename) != 0;
+        strcmp(libname, file_scanner->filename) != 0;
         file_scanner = file_scanner->next)
        ;
+
    if (file_scanner == NULL)
    {
        /*
         * Check for same files - different paths (ie, symlink or link)
         */
-       if (stat(fullname, &stat_buf) == -1)
+       if (stat(libname, &stat_buf) == -1)
            ereport(ERROR,
                    (errcode_for_file_access(),
                     errmsg("could not access file \"%s\": %m",
-                           fullname)));
+                           libname)));
 
        for (file_scanner = file_list;
             file_scanner != NULL &&
@@ -133,21 +214,21 @@ load_external_function(char *filename, char *funcname,
         * File not loaded yet.
         */
        file_scanner = (DynamicFileList *)
-           malloc(sizeof(DynamicFileList) + strlen(fullname));
+           malloc(sizeof(DynamicFileList) + strlen(libname));
        if (file_scanner == NULL)
            ereport(ERROR,
                    (errcode(ERRCODE_OUT_OF_MEMORY),
                     errmsg("out of memory")));
 
        MemSet(file_scanner, 0, sizeof(DynamicFileList));
-       strcpy(file_scanner->filename, fullname);
+       strcpy(file_scanner->filename, libname);
        file_scanner->device = stat_buf.st_dev;
 #ifndef WIN32
        file_scanner->inode = stat_buf.st_ino;
 #endif
        file_scanner->next = NULL;
 
-       file_scanner->handle = pg_dlopen(fullname);
+       file_scanner->handle = pg_dlopen(file_scanner->filename);
        if (file_scanner->handle == NULL)
        {
            load_error = (char *) pg_dlerror();
@@ -156,7 +237,7 @@ load_external_function(char *filename, char *funcname,
            ereport(ERROR,
                    (errcode_for_file_access(),
                     errmsg("could not load library \"%s\": %s",
-                           fullname, load_error)));
+                           libname, load_error)));
        }
 
        /* Check the magic function to determine compatibility */
@@ -184,7 +265,7 @@ load_external_function(char *filename, char *funcname,
                if (module_magic_data.version != magic_data.version)
                    ereport(ERROR,
                            (errmsg("incompatible library \"%s\": version mismatch",
-                                   fullname),
+                                   libname),
                             errdetail("Server is version %d.%d, library is version %d.%d.",
                                       magic_data.version/100,
                                       magic_data.version % 100,
@@ -192,7 +273,7 @@ load_external_function(char *filename, char *funcname,
                                       module_magic_data.version % 100)));
                ereport(ERROR,
                        (errmsg("incompatible library \"%s\": magic block mismatch",
-                               fullname)));
+                               libname)));
            }
        }
        else
@@ -203,7 +284,7 @@ load_external_function(char *filename, char *funcname,
            /* complain */
            ereport(ERROR,
                    (errmsg("incompatible library \"%s\": missing magic block",
-                           fullname),
+                           libname),
                     errhint("Extension libraries are now required to use the PG_MODULE_MAGIC macro.")));
        }
 
@@ -222,77 +303,48 @@ load_external_function(char *filename, char *funcname,
        file_tail = file_scanner;
    }
 
-   /* Return handle if caller wants it. */
-   if (filehandle)
-       *filehandle = file_scanner->handle;
-
-   /*
-    * If funcname is NULL, we only wanted to load the file.
-    */
-   if (funcname == NULL)
-   {
-       pfree(fullname);
-       return NULL;
-   }
-
-   retval = pg_dlsym(file_scanner->handle, funcname);
-
-   if (retval == NULL && signalNotFound)
-       ereport(ERROR,
-               (errcode(ERRCODE_UNDEFINED_FUNCTION),
-                errmsg("could not find function \"%s\" in file \"%s\"",
-                       funcname, fullname)));
-
-   pfree(fullname);
-   return retval;
+   return file_scanner->handle;
 }
 
 /*
- * This function loads a shlib file without looking up any particular
- * function in it. If the same shlib has previously been loaded,
- * unload and reload it.
+ * Unload the specified dynamic-link library file, if it is loaded.
+ *
+ * Note: libname is expected to be an exact name for the library file.
  */
-void
-load_file(char *filename)
+static void
+internal_unload_library(const char *libname)
 {
    DynamicFileList *file_scanner,
               *prv,
               *nxt;
    struct stat stat_buf;
-   char       *fullname;
    PG_fini_t   PG_fini;
 
-   fullname = expand_dynamic_library_name(filename);
-   if (!fullname)
-       fullname = pstrdup(filename);
-   /* at this point fullname is always freshly palloc'd */
-
    /*
     * We need to do stat() in order to determine whether this is the same
     * file as a previously loaded file; it's also handy so as to give a good
     * error message if bogus file name given.
     */
-   if (stat(fullname, &stat_buf) == -1)
+   if (stat(libname, &stat_buf) == -1)
        ereport(ERROR,
                (errcode_for_file_access(),
-                errmsg("could not access file \"%s\": %m", fullname)));
+                errmsg("could not access file \"%s\": %m", libname)));
 
    /*
     * We have to zap all entries in the list that match on either filename or
-    * inode, else load_external_function() won't do anything.
+    * inode, else internal_load_library() will still think it's present.
     */
    prv = NULL;
    for (file_scanner = file_list; file_scanner != NULL; file_scanner = nxt)
    {
        nxt = file_scanner->next;
-       if (strcmp(fullname, file_scanner->filename) == 0 ||
+       if (strcmp(libname, file_scanner->filename) == 0 ||
            SAME_INODE(stat_buf, *file_scanner))
        {
            if (prv)
                prv->next = nxt;
            else
                file_list = nxt;
-           clear_external_function_hash(file_scanner->handle);
 
            /*
             * If the library has a _PG_fini() function, call it.
@@ -301,6 +353,7 @@ load_file(char *filename)
            if (PG_fini)
                (*PG_fini)();
 
+           clear_external_function_hash(file_scanner->handle);
            pg_dlclose(file_scanner->handle);
            free((char *) file_scanner);
            /* prv does not change */
@@ -308,23 +361,8 @@ load_file(char *filename)
        else
            prv = file_scanner;
    }
-
-   load_external_function(fullname, NULL, false, NULL);
-
-   pfree(fullname);
-}
-
-/*
- * Lookup a function whose library file is already loaded.
- * Return (PGFunction) NULL if not found.
- */
-PGFunction
-lookup_external_function(void *filehandle, char *funcname)
-{
-   return pg_dlsym(filehandle, funcname);
 }
 
-
 static bool
 file_exists(const char *name)
 {
@@ -353,9 +391,9 @@ file_exists(const char *name)
  * the name.  Else (no slash) try to expand using search path (see
  * find_in_dynamic_libpath below); if that works, return the fully
  * expanded file name. If the previous failed, append DLSUFFIX and
- * try again.  If all fails, return NULL.
+ * try again.  If all fails, just return the original name.
  *
- * A non-NULL result will always be freshly palloc'd.
+ * The result will always be freshly palloc'd.
  */
 static char *
 expand_dynamic_library_name(const char *name)
@@ -402,9 +440,28 @@ expand_dynamic_library_name(const char *name)
        pfree(full);
    }
 
-   return NULL;
+   /*
+    * If we can't find the file, just return the string as-is.
+    * The ensuing load attempt will fail and report a suitable message.
+    */
+   return pstrdup(name);
 }
 
+/*
+ * Check a restricted library name.  It must begin with "$libdir/plugins/"
+ * and there must not be any directory separators after that (this is
+ * sufficient to prevent ".." style attacks).
+ */
+static void
+check_restricted_library_name(const char *name)
+{
+   if (strncmp(name, "$libdir/plugins/", 16) != 0 ||
+       first_dir_separator(name + 16) != NULL)
+       ereport(ERROR,
+               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                errmsg("access to library \"%s\" is not allowed",
+                       name)));
+}
 
 /*
  * Substitute for any macros appearing in the given string.
@@ -418,6 +475,7 @@ substitute_libpath_macro(const char *name)
 
    AssertArg(name != NULL);
 
+   /* Currently, we only recognize $libdir at the start of the string */
    if (name[0] != '$')
        return pstrdup(name);
 
@@ -428,7 +486,8 @@ substitute_libpath_macro(const char *name)
        strncmp(name, "$libdir", strlen("$libdir")) != 0)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_NAME),
-           errmsg("invalid macro name in dynamic library path: %s", name)));
+                errmsg("invalid macro name in dynamic library path: %s",
+                       name)));
 
    ret = palloc(strlen(pkglib_path) + strlen(sep_ptr) + 1);
 
@@ -513,3 +572,58 @@ find_in_dynamic_libpath(const char *basename)
 
    return NULL;
 }
+
+
+/*
+ * Find (or create) a rendezvous variable that one dynamically 
+ * loaded library can use to meet up with another.
+ *
+ * On the first call of this function for a particular varName,
+ * a "rendezvous variable" is created with the given name.
+ * The value of the variable is a void pointer (initially set to NULL).
+ * Subsequent calls with the same varName just return the address of
+ * the existing variable.  Once created, a rendezvous variable lasts
+ * for the life of the process.
+ *
+ * Dynamically loaded libraries can use rendezvous variables
+ * to find each other and share information: they just need to agree
+ * on the variable name and the data it will point to.
+ */
+void **
+find_rendezvous_variable(const char *varName)
+{
+   static HTAB         *rendezvousHash = NULL;
+
+   char                 key[NAMEDATALEN];
+   rendezvousHashEntry *hentry;
+   bool                 found;
+
+   /* Create a hashtable if we haven't already done so in this process */
+   if (rendezvousHash == NULL)
+   {
+       HASHCTL ctl;
+
+       MemSet(&ctl, 0, sizeof(ctl));
+       ctl.keysize    = NAMEDATALEN;
+       ctl.entrysize  = sizeof(rendezvousHashEntry);
+       rendezvousHash = hash_create("Rendezvous variable hash",
+                                    16,
+                                    &ctl,
+                                    HASH_ELEM);
+   }
+
+   /* Turn the varName into a fixed-size string */
+   StrNCpy(key, varName, sizeof(key));
+
+   /* Find or create the hashtable entry for this varName */
+   hentry = (rendezvousHashEntry *) hash_search(rendezvousHash,
+                                                key,
+                                                HASH_ENTER,
+                                                &found);
+
+   /* Initialize to NULL if first time */
+   if (!found)
+       hentry->varValue = NULL;
+
+   return &hentry->varValue;
+}
index b238aaec5ce7ad90295e5e128abc4f11d8b923b4..3b2cb2f9c89554fd08a69a34e4e8f40871f8b3b1 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.156 2006/08/08 19:15:08 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.157 2006/08/15 18:26:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1097,24 +1097,31 @@ ValidatePgVersion(const char *path)
  *-------------------------------------------------------------------------
  */
 
-/* GUC variable: list of library names to be preloaded */
-char      *preload_libraries_string = NULL;
+/* 
+ * GUC variables: lists of library names to be preloaded at postmaster
+ * start and at backend start
+ */
+char      *shared_preload_libraries_string = NULL;
+char      *local_preload_libraries_string = NULL;
 
 /*
- * process any libraries that should be preloaded at postmaster start
+ * load the shared libraries listed in 'libraries'
+ *
+ * 'gucname': name of GUC variable, for error reports
+ * 'restrict': if true, force libraries to be in $libdir/plugins/
  */
-void
-process_preload_libraries(void)
+static void
+load_libraries(const char *libraries, const char *gucname, bool restrict)
 {
    char       *rawstring;
    List       *elemlist;
    ListCell   *l;
 
-   if (preload_libraries_string == NULL)
-       return;
+   if (libraries == NULL || libraries[0] == '\0')
+       return;                 /* nothing to do */
 
    /* Need a modifiable copy of string */
-   rawstring = pstrdup(preload_libraries_string);
+   rawstring = pstrdup(libraries);
 
    /* Parse string into list of identifiers */
    if (!SplitIdentifierString(rawstring, ',', &elemlist))
@@ -1124,7 +1131,8 @@ process_preload_libraries(void)
        list_free(elemlist);
        ereport(LOG,
                (errcode(ERRCODE_SYNTAX_ERROR),
-        errmsg("invalid list syntax for parameter \"preload_libraries\"")));
+                errmsg("invalid list syntax in parameter \"%s\"",
+                       gucname)));
        return;
    }
 
@@ -1135,12 +1143,45 @@ process_preload_libraries(void)
 
        filename = pstrdup(tok);
        canonicalize_path(filename);
-       (void) load_external_function(filename, NULL, true, NULL);
+       /* If restricting, insert $libdir/plugins if not mentioned already */
+       if (restrict && first_dir_separator(filename) == NULL)
+       {
+           char   *expanded;
+
+           expanded = palloc(strlen("$libdir/plugins/") + strlen(filename) + 1);
+           strcpy(expanded, "$libdir/plugins/");
+           strcat(expanded, filename);
+           pfree(filename);
+           filename = expanded;
+       }
+       load_file(filename, restrict);
        ereport(LOG,
-               (errmsg("preloaded library \"%s\"", filename)));
+               (errmsg("loaded library \"%s\"", filename)));
        pfree(filename);
    }
 
    pfree(rawstring);
    list_free(elemlist);
 }
+
+/*
+ * process any libraries that should be preloaded at postmaster start
+ */
+void
+process_shared_preload_libraries(void)
+{
+   load_libraries(shared_preload_libraries_string,
+                  "shared_preload_libraries",
+                  false);
+}
+
+/*
+ * process any libraries that should be preloaded at backend start
+ */
+void
+process_local_preload_libraries(void)
+{
+   load_libraries(local_preload_libraries_string,
+                  "local_preload_libraries",
+                  true);
+}
index 0e16035ef6a14629c8745b7a46e7e9c15530e800..69c67471fc0d1ef19ae25d1b3cdcb5156ba8471c 100644 (file)
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut .
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.341 2006/08/14 02:27:26 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.342 2006/08/15 18:26:59 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -1964,12 +1964,22 @@ static struct config_string ConfigureNamesString[] =
    },
 
    {
-       {"preload_libraries", PGC_POSTMASTER, RESOURCES_KERNEL,
+       {"shared_preload_libraries", PGC_POSTMASTER, RESOURCES_KERNEL,
            gettext_noop("Lists shared libraries to preload into server."),
            NULL,
            GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_SUPERUSER_ONLY
        },
-       &preload_libraries_string,
+       &shared_preload_libraries_string,
+       "", NULL, NULL
+   },
+
+   {
+       {"local_preload_libraries", PGC_BACKEND, CLIENT_CONN_OTHER,
+           gettext_noop("Lists shared libraries to preload into each backend."),
+           NULL,
+           GUC_LIST_INPUT | GUC_LIST_QUOTE
+       },
+       &local_preload_libraries_string,
        "", NULL, NULL
    },
 
index edecda549ee894975d1daaa6465ace403e7b4287..b0c6cdf7634d12f4281beccfc60e253181277346 100644 (file)
 
 #max_files_per_process = 1000      # min 25
                    # (change requires restart)
-#preload_libraries = ''            # (change requires restart)
+#shared_preload_libraries = ''     # (change requires restart)
 
 # - Cost-Based Vacuum Delay -
 
 
 #explain_pretty_print = on
 #dynamic_library_path = '$libdir'
+#local_preload_libraries = ''
 
 
 #---------------------------------------------------------------------------
index 5b904cbad429daff466cd9f5d99ca1b8c0601b55..2e9e3f718209f7c19029ee27a8dbaa710c038cd9 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.45 2006/05/31 20:58:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.46 2006/08/15 18:26:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -488,7 +488,8 @@ extern char *Dynamic_library_path;
 extern PGFunction load_external_function(char *filename, char *funcname,
                       bool signalNotFound, void **filehandle);
 extern PGFunction lookup_external_function(void *filehandle, char *funcname);
-extern void load_file(char *filename);
+extern void load_file(const char *filename, bool restrict);
+extern void **find_rendezvous_variable(const char *varName);
 
 
 /*
index 07590c92942327858045e83ff73cfc252367a76f..8a050f5b7dc5a239642be27b76de4a355a339e2c 100644 (file)
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.187 2006/08/08 19:15:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.188 2006/08/15 18:26:59 tgl Exp $
  *
  * NOTES
  *   some of the information in this file should be moved to other files.
@@ -307,7 +307,8 @@ extern void BaseInit(void);
 
 /* in utils/init/miscinit.c */
 extern bool IgnoreSystemIndexes;
-extern char *preload_libraries_string;
+extern char *shared_preload_libraries_string;
+extern char *local_preload_libraries_string;
 
 extern void SetReindexProcessing(Oid heapOid, Oid indexOid);
 extern void ResetReindexProcessing(void);
@@ -319,6 +320,7 @@ extern void TouchSocketLockFile(void);
 extern void RecordSharedMemoryInLockFile(unsigned long id1,
                             unsigned long id2);
 extern void ValidatePgVersion(const char *path);
-extern void process_preload_libraries(void);
+extern void process_shared_preload_libraries(void);
+extern void process_local_preload_libraries(void);
 
 #endif   /* MISCADMIN_H */