Add server support for "plugin" libraries that can be used for add-on tasks
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 15 Aug 2006 18:26:59 +0000 (18:26 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.73 2006/08/08 19:15:07 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.74 2006/08/15 18:26:58 tgl Exp $ -->
 
 <chapter Id="runtime-config">
   <title>Server Configuration</title>
@@ -949,10 +949,10 @@ SET ENABLE_SEQSCAN TO OFF;
       </listitem>
      </varlistentry>
      
-     <varlistentry id="guc-preload-libraries" xreflabel="preload_libraries">
-      <term><varname>preload_libraries</varname> (<type>string</type>)</term>
+     <varlistentry id="guc-shared-preload-libraries" xreflabel="shared_preload_libraries">
+      <term><varname>shared_preload_libraries</varname> (<type>string</type>)</term>
       <indexterm>
-       <primary><varname>preload_libraries</> configuration parameter</primary>
+       <primary><varname>shared_preload_libraries</> configuration parameter</primary>
       </indexterm>
       <listitem>
        <para>
@@ -963,6 +963,7 @@ SET ENABLE_SEQSCAN TO OFF;
         <literal>mylib.so</> (or on some platforms,
         <literal>mylib.sl</>) to be preloaded from the installation's
         standard library directory.
+        This parameter can only be set at server start.
        </para>
 
        <para>
@@ -3642,6 +3643,60 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
        </para>
       </listitem>
      </varlistentry>
+     
+     <varlistentry id="guc-local-preload-libraries" xreflabel="local_preload_libraries">
+      <term><varname>local_preload_libraries</varname> (<type>string</type>)</term>
+      <indexterm>
+       <primary><varname>local_preload_libraries</> configuration parameter</primary>
+      </indexterm>
+      <indexterm>
+       <primary><filename>$libdir/plugins</></primary>
+      </indexterm>
+      <listitem>
+       <para>
+        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.
+       </para>
+
+       <para>
+        Because this is not a superuser-only option, the libraries
+        that can be loaded are restricted to those appearing in the
+        <filename>plugins</> subdirectory of the installation's
+        standard library directory.  (It is the database administrator's
+        responsibility to ensure that only <quote>safe</> libraries
+        are installed there.)  Entries in <varname>local_preload_libraries</>
+        can specify this directory explicitly, for example
+        <literal>$libdir/plugins/mylib</literal>, or just specify
+        the library name &mdash; <literal>mylib</literal> would have
+        the same effect as <literal>$libdir/plugins/mylib</literal>.
+       </para>
+
+       <para>
+        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
+        <command>LOAD</> command being given.  For example, debugging could
+        be enabled for all sessions under a given user name by setting
+        this parameter with <command>ALTER USER SET</>.
+       </para>
+
+       <para>
+        If a specified library is not found,
+        the connection attempt will fail.
+       </para>
+
+       <para>
+        Every  PostgreSQL-supported library has a <quote>magic
+        block</> that is checked to guarantee compatibility.  
+        For this reason, non-PostgreSQL libraries cannot be 
+        loaded in this way.
+       </para>
+      </listitem>
+     </varlistentry>
 
      </variablelist>
     </sect2>
index b29dfd4ed4197ce81b7cefd41735068853032796..6a6e9d9098e86024831d8bba4529d8f215aa6acc 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/load.sgml,v 1.21 2005/01/04 00:39:53 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/load.sgml,v 1.22 2006/08/15 18:26:58 tgl Exp $
 -->
 
 <refentry id="SQL-LOAD">
@@ -44,6 +44,19 @@ LOAD '<replaceable class="PARAMETER">filename</replaceable>'
    shared library file name extension.  See <xref linkend="xfunc-c"> for
    more information on this topic.
   </para>
+
+  <indexterm>
+   <primary><filename>$libdir/plugins</></primary>
+  </indexterm>
+
+  <para>
+   Non-superusers may only apply <command>LOAD</> to library files
+   located in <filename>$libdir/plugins/</> &mdash; the specified
+   <replaceable class="PARAMETER">filename</replaceable> must begin
+   with exactly that string.  (It is the database administrator's
+   responsibility to ensure that only <quote>safe</> libraries
+   are installed there.)
+  </para>
  </refsect1>
 
  <refsect1 id="sql-load-compat">
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 <peter_e@gmx.net>.
  *
  * 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 */