PostgreSQL 加载动态库详解

PostgreSQL 支持通过动态库的方式扩展PG的功能,在调用动态库涉及的函数时会自动加载这些库。
但是某些动态库可能需要预加载,例如使用了hook的动态库,或者是需要初始化共享内存,需要fork work process的动态库。
举个例子:
pg_stat_statements这个插件依赖的动态库就使用了HOOK,并且需要初始化共享内存。

下面再细分一下:
.1. 需要初始化共享内存,或者fork work process的动态库,必须在数据库启动时加载(将来数据库支持在启动后动态创建共享内存,或者动态fork work process的话,可以不需要在数据库启动时加载)。
.2. 需要用到HOOK,但是对应的so没有数据库函数来触发自动LOAD的情况,则需要独立的LOAD过程。例如auto_explain, pg_plan_hint插件。他们没有函数,但是需要用到HOOK。
_PG_init接口就是被这些插件使用的自动装置。

a dynamically loaded file can contain initialization and finalization functions.
If the file includes a function named _PG_init, that function will be called immediately after loading the file. The function receives no parameters and should return void.
If the file includes a function named _PG_fini, that function will be called immediately before unloading the file. Likewise, the function receives no parameters and should return void.
Note that _PG_fini will only be called during an unload of the file, not during process termination.

下面来聊一下如何预加载so:
加载SO分为2种
.1. 数据库启动时加载,通过shared_preload_libraries参数控制。
配置shared_preload_libraries参数,必须重启数据库。
例子(可以省略.so,如果没有配置相对路径,则在dynamic_library_path参数提供的路径中查找对应的SO文件):

shared_preload_libraries = 'auto_explain,$libdir/plugins/pg_hint_plan.so'

.2. 用户与数据库建立连接时加载,通过两个参数控制。
local_preload_libraries
这个参数允许普通用户修改,只能动态加载$libdir/plugins下面的so文件。如果是加载$libdir下面的so文件,会报错(原因见本文末尾代码)。
如果未指定相对目录,自动到$libdir/plugins/中搜索so。
(PostgreSQL在涉及动态库时,就把$libdir/plugins列为DBA应该有责任对so把关,仅将安全的so放到$libdir/plugins目录中,因为允许普通用户动态加载)
例子 :

alter role test set local_preload_libraries='pg_hint_plan';
 或
alter role test set local_preload_libraries='$libdir/plugins/pg_hint_plan';

session_preload_libraries
这个参数只允许超级用户修改,能动态加载所有目录下面的so文件,如果未指定相对目录,自动到dynamic_library_path指定的目录中搜索so。
例子 :

alter role all set session_preload_libraries='pg_hint_plan';
 或
alter role all set session_preload_libraries='$libdir/pg_hint_plan';

如何让一个普通用户可以在不重启数据库的情况下,预加载非$libdir/plugins目录中的so呢?
可以让超级用户修改这个普通用户的session_preload_libraries参数。

alter role test set session_preload_libraries='$libdir/pg_hint_plan';

除了预加载(启动数据库时加载,或者链接数据库时加载),PostgreSQL还有一种方法加载so,使用LOAD语句。
同样分为普通用户和超级用户的概念。
普通用户只能动态加载$libdir/plugins下面的so文件。如果未指定相对目录,自动到$libdir/plugins/中搜索so。
超级用户能动态加载所有目录下面的so文件,如果未指定相对目录,自动到dynamic_library_path指定的目录中搜索so。

load用法如下:

LOAD -- load a shared library file
Synopsis  

LOAD 'filename'  

Description
This command loads a shared library file into the PostgreSQL server's address space. If the file has been loaded already, the command does nothing. Shared library files that contain C functions are automatically loaded whenever one of their functions is called. Therefore, an explicit LOAD is usually only needed to load a library that modifies the server's behavior through "hooks" rather than providing a set of functions.  

The file name is specified in the same way as for shared library names in CREATE FUNCTION; in particular, one can rely on a search path and automatic addition of the system's standard shared library file name extension. See Section 35.9 for more information on this topic.  

Non-superusers can 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.)  

Compatibility  

LOAD is a PostgreSQL extension.

动态库搜索路径的配置
dynamic_library_path (string)

The default value for this parameter is '$libdir'.
If the value is set to an empty string '', the automatic path search is turned off.

例如

dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'

代码

/*
 * process any libraries that should be preloaded at backend start
 */
void
process_session_preload_libraries(void)
{
    load_libraries(session_preload_libraries_string,
                   "session_preload_libraries",
                   false);
    load_libraries(local_preload_libraries_string,
                   "local_preload_libraries",
                   true);
}
load_libraries(const char libraries, const char gucname, bool restricted)
...
    foreach(l, elemlist)
    {
        char       tok = (char ) lfirst(l);
        char       *filename;

        filename = pstrdup(tok);
        canonicalize_path(filename);
        / If restricting, insert $libdir/plugins if not mentioned already /
        if (restricted && first_dir_separator(filename) == NULL)
        {
            char       *expanded;

            expanded = psprintf("$libdir/plugins/%s", filename);
            pfree(filename);
            filename = expanded;
        }
        load_file(filename, restricted);
        ereport(DEBUG1,
                (errmsg("loaded library \"%s\"", filename)));
        pfree(filename);
    }
...
load_file(const char *filename, bool restricted)
{
    char       *fullname;

    / Apply security restriction if requested /
    if (restricted)
        check_restricted_library_name(filename);
...
/*
 * 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)));
}

参考
http://www.postgresql.org/docs/9.5/static/runtime-config-client.html#RUNTIME-CONFIG-CLIENT-PRELOAD
http://www.postgresql.org/docs/9.5/static/sql-load.html
http://www.postgresql.org/docs/9.5/static/xfunc-c.html#XFUNC-C-DYNLOAD

时间: 2024-09-20 17:52:57

PostgreSQL 加载动态库详解的相关文章

JDBC数据库连接过程及驱动加载与设计模式详解_java

首先要导入JDBC的jar包: 接下来,代码: Class.forName(xxx.xx.xx)返回的是一个类 Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段. JDBC连接数据库 • 创建一个以JDBC连接数据库的程序,包含7个步骤: 1.加载JDBC驱动程序: 在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机), 这通过java.lang.Class类的静态方法forName(String

loaded-在xcode6 上加载动态库

问题描述 在xcode6 上加载动态库 在xcode6 上加载自己创建的动态库时会出现 "dyld: Library not loaded: @rpath/.... Reason: image not found"这个问题除了把 Linked FrameWorks and Libraries的所在动态库选项改为 option 外有没有其他解决办法呢?

linux服务器-Linux动态加载动态库无法进去gdb调试

问题描述 Linux动态加载动态库无法进去gdb调试 动态加载动态库无法单步调试,断点可以设置成功,也能进入断点,就是不能单步调试

linux-Linux下, 加载动态库报宽字符错误.

问题描述 Linux下, 加载动态库报宽字符错误. 请教: 如题: 程序启动时加载某动态库(.so)时,提示加载失败 具体提示为 load lib???????.so fail, reason:Invalid or incomplete multibyte or wide character. 解决方案 可能是字符集不同的原因

Android编程动态加载布局实例详解【附demo源码】_Android

本文实例讲述了Android编程动态加载布局的方法.分享给大家供大家参考,具体如下: 由于前段时间项目需要,需要在一个页面上加载根据不同的按钮加载不同的布局页面,当时想到用 tabhot .不过美工提供的界面图完全用不上tabhot ,所以想到了动态加载的方法来解决这一需求.在这里我整理了一下,写了一个 DEMO 希望大家以后少走点弯路. 首先,我们先把界面的框架图画出来,示意图如下: 中间白色部门是一个线性布局文件,我喜欢在画图的时候用不同的颜色将一块布局标示出来,方便查看.布局文件代码如下:

Android动态加载Activity原理详解_Android

activity的启动流程 加载一个Activity肯定不会像加载一般的类那样,因为activity作为系统的组件有自己的生命周期,有系统的很多回调控制,所以自定义一个DexClassLoader类加载器来加载插件中的Activity肯定是不可以的. 首先不得不了解一下activity的启动流程,当然只是简单的看一下,太详细的话很难研究清楚. 通过startActivity启动后,最终通过AMS进行跨进程回调到ApplicationThread的scheduleLaunchActivity,这时

Android动态加载Activity原理详解

activity的启动流程 加载一个Activity肯定不会像加载一般的类那样,因为activity作为系统的组件有自己的生命周期,有系统的很多回调控制,所以自定义一个DexClassLoader类加载器来加载插件中的Activity肯定是不可以的. 首先不得不了解一下activity的启动流程,当然只是简单的看一下,太详细的话很难研究清楚. 通过startActivity启动后,最终通过AMS进行跨进程回调到ApplicationThread的scheduleLaunchActivity,这时

web.xml 中的listener、 filter、servlet 加载顺序及其详解

在项目中总会遇到一些关于加载的优先级问题,近期也同样遇到过类似的,所以自己查找资料总结了下,下面有些是转载其他人的,毕竟人家写的不错,自己也就不重复造轮子了,只是略加点了自己的修饰.         首先可以肯定的是,加载顺序与它们在 web.xml 文件中的先后顺序无关.即不会因为 filter 写在 listener 的前面而会先加载 filter.最终得出的结论是:listener -> filter -> servlet         同时还存在着这样一种配置节:context-pa

php类的自动加载操作实例详解_php技巧

本文实例讲述了php类的自动加载操作.分享给大家供大家参考,具体如下: 类的自动加载 在外面的页面中,并不需要去引入类文件,但程序会在需要一个类的时候自动去"动态加载"该类. ① 创建一个对象的时候new ② 直接使用一个类名(操作静态属性与方法) 使用__autoload魔术函数 当出现两种情况时候,就会调用该函数,该函数需要我们预先定义,在其中写好加载类文件的通用语句 function __autoload($name){ require './lib/'.$name.'.clas