diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index 1f03d7cd90b84b91d2a2d3b6a8769f5e93cadcf8..b803ea2c9ee5a1bc32ae9069ec9ee6c0685daeca 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -1,4 +1,4 @@ - + User-Defined Functions @@ -1148,13 +1148,6 @@ CREATE FUNCTION square_root(double precision) RETURNS double precision that fails as well, the load will fail. - - 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. - - The user ID the PostgreSQL server runs as must be able to traverse the path to the file you intend to @@ -1960,31 +1953,36 @@ concat_text(PG_FUNCTION_ARGS) - 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 pgmagic.h and declare the block as - follows: + Symbol names defined within object files must not conflict + with each other or with symbols defined in the + PostgreSQL server executable. You + will have to rename your functions or variables if you get + error messages to this effect. + - -#include "pgmagic.h" + + + To ensure your module is not loaded into an incompatible server, it + is recommended to include a magic block. This allows + the server to detect obvious incompatibilities, such as a module + compiled for a different major version of + PostgreSQL. It is likely that magic + blocks will be required in future releases. To include a magic + block, write this in one (and only one) of your module source files, + after having included the header fmgr.h: + + +#ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; +#endif - If the module consists of multiple source files, this only needs to - be done in one of them. - - - - - - Symbol names defined within object files must not conflict - with each other or with symbols defined in the - PostgreSQL server executable. You - will have to rename your functions or variables if you get - error messages to this effect. + The #ifdef test can be omitted if your code doesn't + need to compile against pre-8.2 PostgreSQL + releases. diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c index 5379b89902d80249d2687b00626990106d1538c6..a54ca550dd7d5c5971e315cbf235f8b8135c45b1 100644 --- a/src/backend/utils/fmgr/dfmgr.c +++ b/src/backend/utils/fmgr/dfmgr.c @@ -8,19 +8,18 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.83 2006/05/30 14:09:32 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.84 2006/05/30 21:21:30 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include #include #include "dynloader.h" #include "miscadmin.h" #include "utils/dynamic_loader.h" -#include "pgmagic.h" + /* * List of dynamically loaded files (kept in malloc'd memory). @@ -61,7 +60,8 @@ 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; +static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA; + /* * Load the specified dynamic-link library file, and look for a function @@ -82,6 +82,7 @@ load_external_function(char *filename, char *funcname, { DynamicFileList *file_scanner; PGFunction retval; + PGModuleMagicFunction magic_func; char *load_error; struct stat stat_buf; char *fullname; @@ -119,7 +120,6 @@ load_external_function(char *filename, char *funcname, if (file_scanner == NULL) { - PGModuleMagicFunction magic_func; /* * File not loaded yet. */ @@ -150,44 +150,53 @@ 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 ) + /* Check the magic function to determine compatibility */ + magic_func = (PGModuleMagicFunction) + 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 ) + const Pg_magic_struct *magic_data_ptr = (*magic_func) (); + + if (magic_data_ptr->len != magic_data.len || + memcmp(magic_data_ptr, &magic_data, magic_data.len) != 0) { - pg_dlclose( file_scanner->handle ); - - if( module_magic_data->len != magic_data.len ) + /* copy data block before unlinking library */ + Pg_magic_struct module_magic_data = *magic_data_ptr; + + /* try to unlink library */ + pg_dlclose(file_scanner->handle); + free((char *) file_scanner); + + /* + * Report suitable error. It's probably not worth writing + * a separate error message for each field; only the most + * common case of wrong major version gets its own message. + */ + if (module_magic_data.version != magic_data.version) 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", + (errmsg("incompatible library \"%s\": version mismatch", + fullname), + errdetail("Server is version %d.%d, library is version %d.%d.", + magic_data.version/100, + magic_data.version % 100, + module_magic_data.version/100, + module_magic_data.version % 100))); + ereport(ERROR, + (errmsg("incompatible library \"%s\": magic block mismatch", 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 ))); + { + /* + * Currently we do not reject modules for not having a + * magic block, it would break every external module in + * existence. At some point though, this will become an ERROR. + */ + ereport(LOG, + (errmsg("library \"%s\" does not have a magic block", + fullname))); + } /* OK to link it into list */ if (file_list == NULL) diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 4a663135dcbe6d63a9cb11cb5eb90f0cca035e40..4472b3fcc99410cb0d4b5041e67dab7ecec590cb 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.100 2006/04/04 19:35:36 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.101 2006/05/30 21:21:30 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -66,7 +66,7 @@ typedef struct TransactionId fn_xmin; /* for checking up-to-dateness */ CommandId fn_cmin; PGFunction user_fn; /* the function's address */ - Pg_finfo_record *inforec; /* address of its info record */ + const Pg_finfo_record *inforec; /* address of its info record */ } CFuncHashTabEntry; static HTAB *CFuncHash = NULL; @@ -78,7 +78,7 @@ static void fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedur static void fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple); static CFuncHashTabEntry *lookup_C_func(HeapTuple procedureTuple); static void record_C_func(HeapTuple procedureTuple, - PGFunction user_fn, Pg_finfo_record *inforec); + PGFunction user_fn, const Pg_finfo_record *inforec); static Datum fmgr_oldstyle(PG_FUNCTION_ARGS); static Datum fmgr_security_definer(PG_FUNCTION_ARGS); @@ -276,7 +276,7 @@ fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple) Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); CFuncHashTabEntry *hashentry; PGFunction user_fn; - Pg_finfo_record *inforec; + const Pg_finfo_record *inforec; Oldstyle_fnextra *fnextra; bool isnull; int i; @@ -405,12 +405,12 @@ fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple) * can validate the information record for a function not yet entered into * pg_proc. */ -Pg_finfo_record * +const Pg_finfo_record * fetch_finfo_record(void *filehandle, char *funcname) { char *infofuncname; PGFInfoFunction infofunc; - Pg_finfo_record *inforec; + const Pg_finfo_record *inforec; static Pg_finfo_record default_inforec = {0}; /* Compute name of info func */ @@ -493,7 +493,7 @@ lookup_C_func(HeapTuple procedureTuple) */ static void record_C_func(HeapTuple procedureTuple, - PGFunction user_fn, Pg_finfo_record *inforec) + PGFunction user_fn, const Pg_finfo_record *inforec) { Oid fn_oid = HeapTupleGetOid(procedureTuple); CFuncHashTabEntry *entry; diff --git a/src/include/fmgr.h b/src/include/fmgr.h index 0d6e72594d2cbc810ddea8311568f19a0e27f44d..a0749a5fa6bc0159feb624fd6216bb3393559c13 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -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.43 2006/04/04 19:35:37 tgl Exp $ + * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.44 2006/05/30 21:21:30 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -293,7 +293,7 @@ typedef struct } Pg_finfo_record; /* Expected signature of an info function */ -typedef Pg_finfo_record *(*PGFInfoFunction) (void); +typedef const Pg_finfo_record *(*PGFInfoFunction) (void); /* * Macro to build an info function associated with the given function name. @@ -301,16 +301,75 @@ typedef Pg_finfo_record *(*PGFInfoFunction) (void); * doesn't hurt to add DLLIMPORT in case they don't. */ #define PG_FUNCTION_INFO_V1(funcname) \ -extern DLLIMPORT Pg_finfo_record * CppConcat(pg_finfo_,funcname) (void); \ -Pg_finfo_record * \ +extern DLLIMPORT const Pg_finfo_record * CppConcat(pg_finfo_,funcname)(void); \ +const Pg_finfo_record * \ CppConcat(pg_finfo_,funcname) (void) \ { \ - static Pg_finfo_record my_finfo = { 1 }; \ + static const Pg_finfo_record my_finfo = { 1 }; \ return &my_finfo; \ } \ extern int no_such_variable +/*------------------------------------------------------------------------- + * Support for verifying backend compatibility of loaded modules + * + * If a loaded module includes the macro call + * PG_MODULE_MAGIC; + * (put this in only one source file), then we can check for obvious + * incompatibility, such as being compiled for a different major PostgreSQL + * version. + * + * To compile with versions of PostgreSQL that do not support this, + * you may put an #ifdef/#endif test around it. + * + * The specific items included in the magic block are intended to be ones that + * are custom-configurable and especially likely to break dynamically loaded + * modules if they were compiled with other values. Also, the length field + * can be used to detect definition changes. + *------------------------------------------------------------------------- + */ + +/* Definition of the magic block structure */ +typedef struct +{ + int len; /* sizeof(this struct) */ + int version; /* PostgreSQL major version */ + int funcmaxargs; /* FUNC_MAX_ARGS */ + int indexmaxkeys; /* INDEX_MAX_KEYS */ + int namedatalen; /* NAMEDATALEN */ +} Pg_magic_struct; + +/* The actual data block contents */ +#define PG_MODULE_MAGIC_DATA \ +{ \ + sizeof(Pg_magic_struct), \ + PG_VERSION_NUM / 100, \ + FUNC_MAX_ARGS, \ + INDEX_MAX_KEYS, \ + NAMEDATALEN \ +} + +/* + * Declare the module magic function. It needs to be a function as the dlsym + * in the backend is only guaranteed to work on functions, not data + */ +typedef const 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 const Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void); \ +const Pg_magic_struct * \ +PG_MAGIC_FUNCTION_NAME(void) \ +{ \ + static const Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA; \ + return &Pg_magic_data; \ +} \ +extern int no_such_variable + + /*------------------------------------------------------------------------- * Support routines and macros for callers of fmgr-compatible functions *------------------------------------------------------------------------- @@ -414,7 +473,7 @@ extern bytea *OidSendFunctionCall(Oid functionId, Datum val); /* * Routines in fmgr.c */ -extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname); +extern const Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname); extern void clear_external_function_hash(void *filehandle); extern Oid fmgr_internal_function(const char *proname); extern Oid get_fn_expr_rettype(FmgrInfo *flinfo); diff --git a/src/include/pgmagic.h b/src/include/pgmagic.h deleted file mode 100644 index 456804618c2fd787de0579420cdd86b1c28e8a3f..0000000000000000000000000000000000000000 --- a/src/include/pgmagic.h +++ /dev/null @@ -1,73 +0,0 @@ -/*------------------------------------------------------------------------- - * - * 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 - * - * $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 */ diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c index 0f37f19204b6cffc21a7c5272e28a579bc6c6688..b4968502ccc5baf87b11fd8c8edbd13a1bb72db7 100644 --- a/src/test/regress/regress.c +++ b/src/test/regress/regress.c @@ -1,5 +1,5 @@ /* - * $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.66 2006/05/30 14:09:32 momjian Exp $ + * $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.67 2006/05/30 21:21:30 tgl Exp $ */ #include "postgres.h" @@ -9,7 +9,6 @@ #include "utils/geo_decls.h" /* includes */ #include "executor/executor.h" /* For GetAttributeByName */ #include "commands/sequence.h" /* for nextval() */ -#include "pgmagic.h" #define P_MAXDIG 12 #define LDELIM '(' @@ -28,7 +27,11 @@ extern int oldstyle_length(int n, text *t); extern Datum int44in(PG_FUNCTION_ARGS); extern Datum int44out(PG_FUNCTION_ARGS); +#ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; +#endif + + /* * Distance from a point to a path */