]> granicus.if.org Git - postgresql/commitdiff
Add pgmagic header block to store compile-time constants:
authorBruce Momjian <bruce@momjian.us>
Tue, 30 May 2006 14:09:32 +0000 (14:09 +0000)
committerBruce Momjian <bruce@momjian.us>
Tue, 30 May 2006 14:09:32 +0000 (14:09 +0000)
It now only checks four things:

Major version number (7.4 or 8.1 for example)
NAMEDATALEN
FUNC_MAX_ARGS
INDEX_MAX_KEYS

The three constants were chosen because:

1. We document them in the config page in the docs
2. We mark them as changable in pg_config_manual.h
3. Changing any of these will break some of the more popular modules:

FUNC_MAX_ARGS changes fmgr interface, every module uses this NAMEDATALEN
changes syscache interface, every PL as well as tsearch uses this
INDEX_MAX_KEYS breaks tsearch and anything using GiST.

Martijn van Oosterhout

doc/src/sgml/xfunc.sgml
src/backend/utils/fmgr/dfmgr.c
src/include/pgmagic.h [new file with mode: 0644]
src/test/regress/regress.c

index 27cb8c90ca1d75d7c24a1618189a259cfe35ebcc..1f03d7cd90b84b91d2a2d3b6a8769f5e93cadcf8 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.112 2006/04/23 03:39:52 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.113 2006/05/30 14:09:32 momjian Exp $ -->
 
  <sect1 id="xfunc">
   <title>User-Defined Functions</title>
@@ -1148,6 +1148,13 @@ CREATE FUNCTION square_root(double precision) RETURNS double precision
     that fails as well, the load will fail.
    </para>
 
+   <para>
+    After the module has been found, PostgreSQL looks for its magic block.
+    This block contains information about the environment a module was
+    compiled in. The server uses this to verify the module was compiled
+    under the same assumptions and environment as the backend.
+   </para>
+
    <para>
     The user ID the <productname>PostgreSQL</productname> server runs
     as must be able to traverse the path to the file you intend to
@@ -1951,6 +1958,26 @@ concat_text(PG_FUNCTION_ARGS)
        </para>
       </listitem>
 
+      <listitem>
+       <para>
+        To ensure your module is not loaded into an incompatible backend, it
+        is recommended to include a magic block. To do this you must include
+        the header <filename>pgmagic.h</filename> and declare the block as
+        follows:
+       </para>
+
+<programlisting>
+#include "pgmagic.h"
+
+PG_MODULE_MAGIC;
+</programlisting>
+
+       <para>
+        If the module consists of multiple source files, this only needs to
+        be done in one of them.
+       </para>
+      </listitem>
+
       <listitem>
        <para>
         Symbol names defined within object files must not conflict
index 582c1b0565d945d1809945f88b957d02ecf8ddde..5379b89902d80249d2687b00626990106d1538c6 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.82 2006/03/05 15:58:46 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.83 2006/05/30 14:09:32 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,7 @@
 #include "dynloader.h"
 #include "miscadmin.h"
 #include "utils/dynamic_loader.h"
-
+#include "pgmagic.h"
 
 /*
  * List of dynamically loaded files (kept in malloc'd memory).
@@ -60,6 +60,9 @@ static char *find_in_dynamic_libpath(const char *basename);
 static char *expand_dynamic_library_name(const char *name);
 static char *substitute_libpath_macro(const char *name);
 
+/* Magic structure that module needs to match to be accepted */
+static 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.)
@@ -116,6 +119,7 @@ load_external_function(char *filename, char *funcname,
 
        if (file_scanner == NULL)
        {
+               PGModuleMagicFunction magic_func;
                /*
                 * File not loaded yet.
                 */
@@ -146,6 +150,45 @@ load_external_function(char *filename, char *funcname,
                                                        fullname, load_error)));
                }
 
+               /* Check the magic function to determine compatability */
+               magic_func = pg_dlsym( file_scanner->handle, PG_MAGIC_FUNCTION_NAME_STRING );
+               if( magic_func )
+               {
+                       Pg_magic_struct *module_magic_data = magic_func();
+                       if( module_magic_data->len != magic_data.len ||
+                           memcmp( module_magic_data, &magic_data, magic_data.len ) != 0 )
+                       {
+                               pg_dlclose( file_scanner->handle );
+                               
+                               if( module_magic_data->len != magic_data.len )
+                                       ereport(ERROR,
+                                               (errmsg("incompatible library \"%s\": Magic block length mismatch",
+                                                               fullname)));
+                               if( module_magic_data->version != magic_data.version )
+                                       ereport(ERROR,
+                                               (errmsg("incompatible library \"%s\": Version mismatch",
+                                                               fullname),
+                                                errdetail("Expected %d.%d, got %d.%d", 
+                                                       magic_data.version/100, magic_data.version % 100,
+                                                       module_magic_data->version/100, module_magic_data->version % 100)));
+                                                       
+                               if( module_magic_data->magic != magic_data.magic )
+                                       ereport(ERROR,
+                                               (errmsg("incompatible library \"%s\": Magic constant mismatch",
+                                                               fullname),
+                                        errdetail("Expected 0x%08X, got 0x%08X", 
+                                               magic_data.magic, magic_data.magic)));
+                               /* Should never get here */
+                               ereport(ERROR,(errmsg("incompatible library \"%s\": Reason unknown",
+                                                               fullname)));
+                       }
+               }
+               else
+               /* Currently we do not penalize modules for not having a
+                  magic block, it would break every external module in
+                  existance. At some point though... */
+                       ereport(LOG, (errmsg("external library \"%s\" did not have magic block", fullname )));
+               
                /* OK to link it into list */
                if (file_list == NULL)
                        file_list = file_scanner;
diff --git a/src/include/pgmagic.h b/src/include/pgmagic.h
new file mode 100644 (file)
index 0000000..4568046
--- /dev/null
@@ -0,0 +1,73 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgmagic.h
+ *     Defines a magic block that can mark a module in a way so show that
+ *      it is compatible with the server it is being loaded into.
+ *
+ * This file is intended to be included into modules that wish to load
+ * themselves into the backend. All they need to do is include this header
+ * into one of the source files and include the line:
+ *
+ * PG_MODULE_MAGIC;
+ *
+ * The trailing semi-colon is optional. To work with versions of PostgreSQL
+ * that do not support this, you may put an #ifdef/endif block around it.
+ *
+ * Note, there is space available, particularly in the bitfield part. If it
+ * turns out that a change has happened within a major release that would
+ * require all modules to be recompiled, just setting one unused bit there
+ * will do the trick.
+ *
+ * Originally written by Martijn van Oosterhout <kleptog@svana.org>
+ *
+ * $PostgreSQL: pgsql/src/include/pgmagic.h,v 1.1 2006/05/30 14:09:32 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PGMAGIC_H
+#define PGMAGIC_H
+
+#include "c.h"
+
+/* The main structure in which the magic is stored. the length field is used
+ * to detect major changes */
+
+typedef struct {
+  int len;
+  int version;
+  int magic;
+} Pg_magic_struct;
+
+/* Declare the module magic function. It needs to be a function as the dlsym
+ * in the backend is only guarenteed to work on functions, not data */
+
+typedef Pg_magic_struct *(*PGModuleMagicFunction) (void);
+
+#define PG_MAGIC_FUNCTION_NAME Pg_magic_func
+#define PG_MAGIC_FUNCTION_NAME_STRING "Pg_magic_func"
+
+#define PG_MODULE_MAGIC     \
+extern DLLIMPORT Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void);       \
+Pg_magic_struct *                                            \
+PG_MAGIC_FUNCTION_NAME(void) \
+{ \
+  static Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA; \
+  return &Pg_magic_data; \
+}
+
+    /* Common user adjustable constants */
+#define PG_MODULE_MAGIC_CONST \
+   ((INDEX_MAX_KEYS <<  0) +                                    \
+    (FUNC_MAX_ARGS  <<  8) +                                    \
+    (NAMEDATALEN    << 16))
+
+/* Finally, the actual data block */
+#define PG_MODULE_MAGIC_DATA                                    \
+{                                                               \
+  sizeof(Pg_magic_struct),                                      \
+  PG_VERSION_NUM / 100,       /* Major version of postgres */   \
+  PG_MODULE_MAGIC_CONST,   /* Constants users can configure */  \
+}
+
+#endif  /* PGMAGIC_H */
index 7c58f950cbb535548cbacac4361e18d57a4c44b0..0f37f19204b6cffc21a7c5272e28a579bc6c6688 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.65 2006/01/11 20:12:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.66 2006/05/30 14:09:32 momjian Exp $
  */
 
 #include "postgres.h"
@@ -9,6 +9,7 @@
 #include "utils/geo_decls.h"   /* includes <math.h> */
 #include "executor/executor.h" /* For GetAttributeByName */
 #include "commands/sequence.h" /* for nextval() */
+#include "pgmagic.h"
 
 #define P_MAXDIG 12
 #define LDELIM                 '('
@@ -27,7 +28,7 @@ extern int    oldstyle_length(int n, text *t);
 extern Datum int44in(PG_FUNCTION_ARGS);
 extern Datum int44out(PG_FUNCTION_ARGS);
 
-
+PG_MODULE_MAGIC;
 /*
  * Distance from a point to a path
  */