zoukankan      html  css  js  c++  java
  • PostgreSql扩展Sql-动态加载共享库(C函数)

    • 基于 psql (PostgreSQL) 10.4

    pg_language表定义了函数实现所使用的语言。主要支持了C语言和SQL语句。一些可选的语言包括pl/pgsql、tcl和perl。

    ligang=# select lanname, lanispl, lanpltrusted, lanplcallfoid, laninline, lanvalidator from pg_language;
     lanname  | lanispl | lanpltrusted | lanplcallfoid | laninline | lanvalidator 
    ----------+---------+--------------+---------------+-----------+--------------
     internal | f       | f            |             0 |         0 |         2246
     c        | f       | f            |             0 |         0 |         2247
     sql      | f       | t            |             0 |         0 |         2248
     plpgsql  | t       | t            |         13198 |     13199 |        13200
    

    pg_proc表对函数进行了定义。每一个函数在该表中都对应一个元组,包含函数名。输入参数类型,返回类型以及对函数的定义(可能是文本,可能是一段编译型语句,也可能是对可执行代码的引用)。编译过的函数可以静态地链接到服务器上,或者在存储在共享库内,当第一次使用该库时动态的载入。

    ligang=# select proname,prolang, prorettype,proargtypes, prosrc,probin from pg_proc where proname like '%square%';
     proname | prolang | prorettype | proargtypes |           prosrc           |       probin       
    ---------+---------+------------+-------------+----------------------------+--------------------
     square  |   13201 |         23 | 23          | begin return $1 * $1; end; | 
     squares |      13 |         23 | 23          | squares_return_int         | $libdir/squares.so
    

    查看其数据类型

    ligang=# select oid , typname from pg_type where oid = 23;
     oid | typname 
    -----+---------
      23 | int4
    (1 row)
    

    以下是示例函数:

    C: 与内建SQL类型等效的C类型

    int
    square_int (int x)
    {
    	return x * x;
    }
    

    把上面的函数编译成共享库文件,这样声明:

    CREATE FUNCTION square(int) RETURNS int
    AS '/path/to/square.so', 'square_int'
    LANGUAGE 'C';
    

    PL/PGSQL:

    ligang=# create function square(int) returns int as 'begin return $1 * $1; end;' LANGUAGE 'plpgsql';
    CREATE FUNCTION
    ligang=# 
    ligang=# 
    ligang=# select square(4);
     square 
    --------
         16
    

    建立用户函数动态库

    • 新建代码
      #include "postgres.h"
      #include "fmgr.h"

      int 
      square_int(int x)
      {
          return x * x;
      }
      
    • 编译 - 添加共享库

      [ligang@yfslcentos71 include]$ gcc -I`pg_config --includedir-server` -c squares.c 
      [ligang@yfslcentos71 include]$ gcc -shared squares.o -o squares.so 
      [ligang@yfslcentos71 include]$ cp squares.so `pg_config --libdir`/
      
    • Pg数据库装载
      ligang=# create function squares(int) returns int as '$libdir/squares.so', 'square_int' LANGUAGE 'c' STRICT;

    关于PG_MODULE_MAGIC

    为了确保不会错误加载共享库文件,从PostgreSQL 开始将检查那个文件的"magic block",这允许服务器以检查明显的不兼容性。

    #ifdef PG_MODULE_MAGIC
    PG_MODULE_MAGIC;
    #endif
    

    如果不打算兼容8.2 PostgreSQL之前的版本, #ifdef测试也可以省略

    源码修改为:

    #include "postgres.h" 
    #include "fmgr.h"
    
    #ifdef PG_MODULE_MAGIC
    PG_MODULE_MAGIC;
    #endif
    
    int 
    square_int(int x)
    {
        return x * x;
    }
    

    版本约定

    版本0约定

    版本-0方法中,此风格 C 函数的参数和结果用普通 C 风格声明, 但是要小心使用上面显示的 SQL 数据类型的 C 表现形式。 (以前版本;)

    #include "postgres.h" 
    #include "fmgr.h"
    
    #ifdef PG_MODULE_MAGIC
    PG_MODULE_MAGIC;
    #endif
    
    int 
    square_int(int x)
    {
        return x * x;
    }
    

    版本1约定 (应当使用该版本)

    版本-1调用约定使用宏消除大多数传递参数和结果的复杂性。版本-1风格函数的C定义总是下面这样:

    Datum funcname(PG_FUNCTION_ARGS);
    

    另外,宏调用:

    PG_FUNCTION_INFO_V1(funcname);
    

    也必须出现在同一个源文件里(通常就可以写在函数自身前面)。 对那些internal语言函数而言,不需要调用这个宏, 因为PostgreSQL目前假设内部函数都是版本-1。不过,对于动态加载的函数, 它是必须的。

    每个实际参数都是用一个对应该参数的数据类型的 PG_GETARG_xxx()宏抓取的, 用返回类型的PG_RETURN_xxx()宏返回结果。 PG_GETARG_xxx()接受要抓取的函数参数的编号 (从 0 开始)作为其参数。PG_RETURN_xxx() 接受实际要返回的数值为自身的参数。

    关于PG_GETARG_XXX 定义于 src/include/fmgr.h

    /* Macros for fetching arguments of standard types */
    
    #define PG_GETARG_DATUM(n)	 (fcinfo->arg[n])
    #define PG_GETARG_INT32(n)	 DatumGetInt32(PG_GETARG_DATUM(n))
    #define PG_GETARG_UINT32(n)  DatumGetUInt32(PG_GETARG_DATUM(n))
    #define PG_GETARG_INT16(n)	 DatumGetInt16(PG_GETARG_DATUM(n))
    #define PG_GETARG_UINT16(n)  DatumGetUInt16(PG_GETARG_DATUM(n))
    #define PG_GETARG_CHAR(n)	 DatumGetChar(PG_GETARG_DATUM(n))
    #define PG_GETARG_BOOL(n)	 DatumGetBool(PG_GETARG_DATUM(n))
    #define PG_GETARG_OID(n)	 DatumGetObjectId(PG_GETARG_DATUM(n))
    #define PG_GETARG_POINTER(n) DatumGetPointer(PG_GETARG_DATUM(n))
    #define PG_GETARG_CSTRING(n) DatumGetCString(PG_GETARG_DATUM(n))
    #define PG_GETARG_NAME(n)	 DatumGetName(PG_GETARG_DATUM(n))
    /* these macros hide the pass-by-reference-ness of the datatype: */
    #define PG_GETARG_FLOAT4(n)  DatumGetFloat4(PG_GETARG_DATUM(n))
    #define PG_GETARG_FLOAT8(n)  DatumGetFloat8(PG_GETARG_DATUM(n))
    #define PG_GETARG_INT64(n)	 DatumGetInt64(PG_GETARG_DATUM(n))
    /* use this if you want the raw, possibly-toasted input datum: */
    #define PG_GETARG_RAW_VARLENA_P(n)	((struct varlena *) PG_GETARG_POINTER(n))
    /* use this if you want the input datum de-toasted: */
    #define PG_GETARG_VARLENA_P(n) PG_DETOAST_DATUM(PG_GETARG_DATUM(n))
    /* and this if you can handle 1-byte-header datums: */
    #define PG_GETARG_VARLENA_PP(n) PG_DETOAST_DATUM_PACKED(PG_GETARG_DATUM(n))
    

    代码

    #include "postgres.h" 
    #include "fmgr.h"
    
    
    #ifdef PG_MODULE_MAGIC
    PG_MODULE_MAGIC;
    #endif
    
    PG_FUNCTION_INFO_V1(squares_return_int);
    
    Datum squares_return_int(PG_FUNCTION_ARGS)
    {
        int32 arg = PG_GETARG_INT32(0);
        PG_RETURN_INT32(arg * arg);
    }
    

    编译

    [ligang@yfslcentos71 include]$ gcc -I`pg_config --includedir-server` -c squares.c 
    [ligang@yfslcentos71 include]$ gcc -shared squares.o -o squares.so 
    /usr/bin/ld: squares.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
    squares.o: could not read symbols: Bad value
    
    [ligang@yfslcentos71 include]$ gcc  -I`pg_config --includedir-server` -fPIC  -c squares.c
    [ligang@yfslcentos71 include]$ gcc -shared squares.o -o squares.so
    [ligang@yfslcentos71 include]$ 
    [ligang@yfslcentos71 include]$ cp squares.so `pg_config --libdir`/
    

    SQL声明函数

    ligang=# create function squares(int) returns int as '$libdir/squares.so', 'squares_return_int' LANGUAGE 'c'  STRICT; 
    CREATE FUNCTION
    

    补充

    • 函数声明为"strict"(严格),意思是说如果任何输入值为NULL, 那么系统应该自动假设一个NULL的结果。这样处理可以让我们避免在函数代码里面检查 NULL输入。如果不这样处理,我们就得明确检查NULL, 比如为每个传递引用的参数检查空指针。对于传值类型的参数,我们甚至没有办法检查!

    参考Postgresql 9.4手册

  • 相关阅读:
    Android_bug之 task ':app:mergeDebugResources'. > Some file crunching failed, see logs f
    linux下vi命令大全[转]
    百度地图api 常用demo
    Mac之vim普通命令使用[转]
    java写文件
    java读取文件
    Android中对Log日志文件的分析[转]
    Android实用代码模块集锦
    java 位运算
    MyEclipse自带maven找不到或自己外置安装
  • 原文地址:https://www.cnblogs.com/vagabond/p/9546083.html
Copyright © 2011-2022 走看看