内容


带初学者体验 Informix 虚拟表的接口共享库使用之旅

编写扩展块来访问外部数据源和应用程序

引言

关于本教程

Informix 通过虚拟表接口(VTI)提供对外部数据源的访问。外部数据源包括平面文件、其他数据库、互联网提要和其他来源。

VTI 提供一组称为目的函数 的 hooks。作为一名开发人员,您的任务就是创建实现 VTI 目的函数的访问方法 以及必要的额外用户定义例程(UDRs)来访问您的外部数据源。

本教程向您展示如何编译并将您的 VTI UDR 作为共享库运行。在示例中为清晰起见,VTI 目的函数位于一个源文件中,辅助的 C 函数位于一个单独的源文件中。VTI 目的函数被编译并链接到一个名为 tutorial.bld 的 datablade 库中。辅助的 C 函数被编译并链接到一个名为 libtutorial 的单独共享库中。编译 VTI 目的函数和辅助 C 函数到独立库中的一个好处是,辅助 C 函数可由多个 VTI 库使用。

先决条件

您必须安装有 Informix 服务器。本教程使用 Informix 版本 11.50 和 11.70 进行了验证。后期版本预计类似。

您需要中级编程技能以及对 C 编程语言的一些背景知识。您需要有安装和配置 Informix 的实践知识。本教程使用 Informix 虚拟设备来提供一个公共环境。您可以从 IBM Informix 免费下载网站下载 Informix 虚拟设备,这列在 参考资料 部分。您还可以通过在 IBM 网站上搜索 IBM 虚拟设备 来找到 Informix 虚拟设备,或者您可以转至 IBM 免费下载网页来搜索 Informix 虚拟设备

您可以在 Informix Information Center 中查找 Informix 虚拟设备的文档,也可以在引导映像之后在设备本身查找该文档。

系统要求

本教程假定您在使用 Linux,但基本的概念适用且演示代码可被移植到任何支持 Informix 的系统。

概述

本教程展示如何使用共享库连同 VTI。共享库的优势在于,仅在需要时才动态加载它们。这就将您的流程限制为仅在需要时才增长。

本教程还向您展示如何调试位于共享库中的 UDR 代码。请注意,共享库中的符号在加载共享库之前不可见。

该演示中的共享库实现一个打印方法。该方法用于在简单的 SQL 选择语句期间为调用的 VTI 目的函数实现一个跟踪功能。您可以根据该演示创建您自己的自定义 C 库。您的自定义库可由 VTI 目的函数使用,且可以执行任何一般任务。

该演示 VTI UDR 仅显示完整的目的函数集中的 4 个。服务器调用一个目的函数来执行明确定义的任务,即访问和管理您的外部数据源。您将需要为完整的 VTI UDR 实现所有其余的目的函数。参阅 参考资料 中更大、更高级的示例。

了解和编译演示

目录结构

首先从本教程的 下载 部分下载和解压 tutorial.zip 文件。教程代码是在相对路径 tutorial 下组织的。压缩的文件及其描述如下:

tutorial/tut_shared/sample.c
提供由 tutorial 访问方法目的函数使用的辅助支持函数的 C 源代码。
tutorial/tut_shared/sample.h
tutorial 访问方法目的函数使用的辅助支持函数的 C 源代码头文件。包含在对这些代码进行调用的 C 源代码中。
tutorial/tut_shared/makefile
将辅助支持函数编译到共享库 libtutorial.so 中。
tutorial/tutorial.c
提供 tutorial 访问方法目的函数的 C 源代码。出于方便开发而添加了额外的常用头文件。
tutorial/makefile
tutorial 访问方法目的函数编译到 tutorial.bld 库中。
tutorial/tutorial.sql
创建 tutorial 访问方法和定义其目的函数的 SQL 文件。
tutorial/tutorial_example.sql
使用 tutorial 访问方法创建示例表的 SQL 文件。
tutorial/drop_tutorial.sql
删除 tutorial 访问方法及其相关目的函数的 SQL 文件。
tutorial_drop_tutorial_example.sql
删除 example 表的 SQL 文件。
tutorial/space.sh
创建 sbspace 的示例 shell 脚本。请注意,Informix 虚拟设备上的 demo_on 数据库已经定义了一个 sbspace,因此无需显式创建一个 sbspace。

编译 UDR 代码

本教程已在 11.70 Informix 虚拟设备上进行过测试。如果您在另一个操作系统上运行,可能需要移植代码。以下步骤描述如何在 11.70 Informix 虚拟设备上编译教程。

  1. 作为用户 root 编译教程。以根用户身份登录并解压教程代码,它位于 $INFORMIXDIR/extend/tutorial 中。在该示例中,$INFORMIXDIR 假定是 /opt/IBM/informix。如果您的 INFORMIXDIR 不是 /opt/IBM/informix,那么相应地修改指令。
  2. 编译 /root/tutorial/tut_shared 中的源代码,如 清单 1 所示。
    清单 1. 编译源代码
    cd /opt/IBM/informix/extend/tutorial/tut_shared
    make
  3. 清理之前的任何版本,如 清单 2 所示。
    清单 2. 清理以前的版本
    rm -f sample
    rm -f *.o
    rm -f *.so*
    rm -f /opt/IBM/informix/lib/libtutorial.so.1
  4. 将辅助代码编译到 libtutorial.so* 中,如 清单 3 所示。
    清单 3. 编译辅助代码
    gcc -Wall -fPIC -c *.c -I.
    gcc -shared -Wl,-soname,libtutorial.so.1 -o libtutorial.so.1.0 *.o
  5. 更改文件所有权并创建设置各种库版本的链接,如 清单 4 所示。其他链接支持在其他 UNIX 类的操作系统上的适当执行。更改文件所有权不是严格必要的,但是让用户 informix 拥有 $INFORMIXDIR/extend 树中的文件是一个最佳实践。
    清单 4. 更改所有权
    chown informix:informix .;
        su informix -c "ln -s libtutorial.so.1.0 libtutorial.so";
        su informix -c "ln -s libtutorial.so.1.0 libtutorial.so.1" 
    chown informix:informix *
  6. 将共享库复制到 $INFORMIXDIR/lib,将其所有者设置为 informix,如 清单 5 所示。
    清单 5. 将所有者设置为 informix
    cp libtutorial.so.1.0 /opt/IBM/informix/lib/libtutorial.so.1;
        chown informix:informix /opt/IBM/informix/lib/libtutorial.so.1
  7. 编译 /opt/IBM/informix/extend/tutorial 中的源代码,如 清单 6 所示。
    清单 6. 编译源代码
    cd /opt/IBM/informix/extend/tutorial
    make
  8. 清理之前的任何版本,如 清单 7 所示。
    清单 7. 清理以前的版本
    rm -f *.bld *.o *.so* *~
    rm -f /usr/lib/libtutorial.so.1
  9. 输入 chown informix:informix 更改当前目录的所有权。
  10. 编译访问方法目的函数,如 清单 8 所示。
    清单 8. 编译访问方法目的函数
    gcc -g -fPIC -I/opt/IBM/informix/incl/public
        -I/opt/IBM/informix/extend/tutorial/tut_shared
        -L/opt/IBM/informix/extend/tutorial/tut_shared -c tutorial.c
    gcc -shared -g -fPIC -I/opt/IBM/informix/incl/public
        -I/opt/IBM/informix/extend/tutorial/tut_shared
        -L/opt/IBM/informix/extend/tutorial/tut_shared
        -o tutorial.bld tutorial.o -ltutorial
  11. 创建设置各种库版本的几个链接,如 清单 9 所示。其他链接支持在其他 UNIX 类的操作系统上的适当执行。
    清单 9. 设置库版本
    su informix -c "ln -s 
      /opt/IBM/informix/extend/tutorial/tut_shared/libtutorial.so.1.0 libtutorial.so"
    su informix -c "ln -s 
      /opt/IBM/informix/extend/tutorial/tut_shared/libtutorial.so.1.0 libturorial.so.1"
  12. 创建一个到 /usr/lib 的教程共享库的链接,如 清单 10 所示。该教程假定自动搜索 /usr/lib,且它不需要包含在 LD_LIBRARY_PATH 环境变量中。如果您的环境不是这样,添加 $INFORMIXDIR/extend/tutorial/tut_sharedt 到 LD_LIBRARY_PATH。
    清单 10. 创建到共享库的链接
    ln -s /opt/IBM/informix/extend/tutorial/tut_shared/libtutorial.
      so.1.0 /usr/lib/libtutorial.so.1
  13. 更改文件的所有权,如 清单 11 所示。这并不严格必要,但让用户 informix 拥有 $INFORMIXDIR/extend 树中的文件是一种最佳实践。
    清单 11. 更改所有权
    chown informix:informix *
  14. 输入 onmode -ky 停止 Informix 服务器运行,以配置服务器。
  15. 修改服务器 onconfig 文件来为 UDR 代码定义一个 VPCLASS,该代码在 tutorial 访问方法目的函数被调用时执行,如 清单 12 所示。这是一个最佳实践。在其 VP 中执行您的 UDR 代码将您的 UDR 代码与 Informix 服务器隔离开来,从而降低您的 UDR 代码导致 Informix 服务器崩溃或中止的风险。这个生成文件首先删除一个 tutorial VPCLASS 的任何之前的定义,然后添加 VPCLASS。出于操作系统的优先老化,新的 VPCLASS 不会随时间降低性能。
    清单 12. 修改 onconfig 文件
    awk 'BEGIN { } { if ( ! /VPCLASS tutorial,num=1,noage/ ) print; }'
        /opt/IBM/informix/etc/onconfig.demo_on >/tmp/onconfig
    cp tmp/onconfig /opt/IBM/informix/etc/onconfig.demo_on
    echo "VPCLASS tutorial,num=1,noage"
        >>/opt/IBM/informix/etc/onconfig.demo_on
  16. 对包含 tutorial 访问方法目的函数的库设置权限,如 清单 13 所示。
    清单 13. 设置权限
    chmod 555 /opt/IBM/informix/extend/tutorial/tutorial.bld
  17. 选择性地回显 LD_LIBRARY_PATH 的值作参考之用,如 清单 14 所示。
    清单 14. 回显 LD_LIBRARY_PATH
    LD_LIBRARY_PATH = /opt/IBM/informix/clidriver/lib:
        /opt/IBM/informix/lib/esql:
        /opt/IBM/informix/lib/dmi:
        /opt/IBM/informix/lib/cli:
        /opt/IBM/informix/lib/c++:
        /opt/IBM/informix/lib
  18. 输入 oninit 启动 Informix 服务器。
  19. 服务器启动之后,使用 onstat -g sch 命令验证您的 VP 是正确定义的。tutorial VP 应当同时列在 Scheduler 和 Thread Migration Statistics 部分。

配置 sbspace 并定义 tutorial 访问方法和 UDRs

配置 sbspace

如果您在使用 Informix 虚拟设备上的 demo_on Informix 实例,已经定义了 sbspace 供您使用。在这种情况下,您无需再做任何工作。

如果您需要定义 sbspace,如有必要修改 shell 脚本 space.sh 然后执行它。该 shell 脚本创建一个区块,然后添加 sbspace。输入 onstat -d 命令来显示您的区块和空间。您应当会看到您的 sbspace 列出来了。

定义 tutorial 访问方法和 UDRs

目前在教程中,您已经编译了 UDR 代码到两个库中:一个包含 tutorial 访问方法目的函数的库和一个包含目的函数使用的辅助函数的库。您还配置了 Informix 服务器,以便它能支持新的访问方法。

下一步是定义访问方法及其函数,以便 Informix 服务器能使用它们。以下步骤描述如何执行 SQL 代码。tutorial/tutorial.sql 文件创建 tutorial 数据库并定义 UDR 例程和 tutorial 访问方法。tutorial/drop_tutorial.sql 文件取消对 UDR 例程和 tutorial 访问方法的定义,然后删除 tutorial 数据库。

  1. informix 用户身份登录。
  2. 通过输入 清单 15 中的代码执行 tutorial.sql 文件。
    清单 15. 执行 tutorial.sql
    cd /opt/IBM/informix/extend/tutorial
    dbaccess - tutorial.sql

    第一个 SQL 语句创建 tutorial 数据库,如 清单 16 所示。

    清单 16. 创建 tutorial 数据库
    create database tutorial;
    database tutorial;

    下一个 SQL 语句定义每个 UDR 例程,如 清单 17 所示。

    清单 17. 定义 UDR 例程
    create function tutorial_open (pointer)
    returns integer
    with (variant, class="tutorial")
    external name
    "$INFORMIXDIR/extend/tutorial/tutorial.bld(tutorial_open)"
    language c;
    grant execute on function tutorial_open (pointer) to public;

    请注意有关 tutorial_loadlib 函数的一些事项:该函数不是正确的访问方法目的函数。它作为一种最佳实践包含在访问方法目的函数中。Informix 服务器在执行 SQL 语句 execute function tutorial_loadlib() 时调用该函数。提供该函数并执行该 SQL 语句确保可以加载库,并确保 UDR 得到正确调用和执行。

    在定义 UDR 例程之后,tutorial 访问方法被定义。该定义实现该函数。例如,am_beginscan 映射到 UDR,如 清单 18 所示。

    清单 18. 将代码映射到 UDR
    tutorial_beginscan. 
    create primary access_method tutorial (
        am_beginscan = tutorial_beginscan,
        ...
        am_update = tutorial_update );
  3. 如果您想撤销 tutorial.sql 做的工作,可以执行 drop_tutorial.sql,如 清单 19 所示。
    清单 19. 撤销 tutorial.sql
    cd /opt/IBM/informix/extend/tutorial
    dbaccess - drop_tutorial.sql

    注意您应当在删除 UDR 例程之前删除 tutorial 访问方法。如果您首先删除 UDR 例程将产生一条错误消息。

执行 tutorial 访问方法目的函数

目前在教程中,您定义了 tutorial 访问方法及其 UDR 例程。完成以下步骤,发出导致 Informix 服务器调用 VTI UDR 例程的 SQL 语句。

  1. 输入 清单 20 中的代码执行 tutorial_example.sql 中的 SQL。
    清单 20. 执行 SQL
    cd /opt/IBM/informix/extend/tutorial
    dbaccess - tutorial_example.sql

    这个 SQL 文件加载 tutorial UDR 库,然后创建一个位于 sbspace 且使用 tutorial 访问方法的表 example。当 Informix 服务器执行这些 SQL 语句时,它调用 tutorial 方法目的函数来增加或完成请求的任务。在本教程的演示中,这些方法仅调用位于 tutorial 共享库中的辅助函数 print()。Informix 服务器的联机日志文件包含输出。使用 onstat -m 命令来显示您的 Informix 服务器联机日志文件的最后部分,如 清单 21 所示。

    清单 21. 查看联机日志文件
    trace: loadlib
    trace: create
    trace: open
    trace: close
  2. 如果您想撤销 tutorial_example.sql 所做的工作,输入 drop_tutorial_example.sql 命令,如 清单 22 所示。
    清单 22. 删除 tutorial 示例
    cd /opt/IBM/informix/extend/tutorial
    dbaccess - drop_tutorial_example.sql

调试

使用 gdb、多个共享库和您的 UDRs 进行调试

当共享库是应用程序的一部分时,调试就很有难度。最常见的一个难题是获取对共享库中的符号的访问。在您的共享库中的符号可用于 gdb 之前,必须首先加载共享库。

下面是让共享库的调试简单一点的一些技巧:

  • 确保所有库都使用编译器调试标志予以编译,比如如果您在使用 cc 或 gcc,标志就是 -g
  • 使用 gdb dirsource 命令专门指向您的共享库的源代码。
  • 使用 dl 接口,比如 dlopen、dlsym、dlclose 等,来显式地控制共享库的加载。为此,添加 -ldl 到您的编译命令。这增加了对可加载内容的大量控制,代价就是增加了复杂性且可能具有特定于平台的问题。将该方法作为最后一招使用。
  • 大方使用断点。如果您发现您在处理线程、一长串共享库或应返回的 gdb 悬挂方法,您可以在其后的行上设置断点,以便程序继续使用 gdb 中的 c 命令。

以下步骤提供调试您的 UDRs 的一种方法。

  1. 让您的 Informix 服务器正常运行。
  2. 确保加载了您的共享库。例如,要确保加载了 tutorial.bld 库,运行 dbaccess 并手动执行 SQL 语句 execute function tutorial_loadlib();
  3. 通过发出 onstat -g glo 命令查找 tutorial VP 的进程 ID。您应当看到输出类似于 清单 23。在该例中,您的 tutorial VP 的 PID 是 11411
清单 23. 查找 PID 的输出
IBM Informix Dynamic Server Version 11.70.UC1DE -- On-Line -- Up 00:01:06 -- 177036 Kbytes

MT global info:
sessions threads  vps      lngspins
0        27       14       0       

          sched calls     thread switches yield 0   yield n   yield forever
total:    56749           2320            489       710       657      
per sec:  0               0               0         0         0        

Virtual processor summary:
 class       vps       usercpu   syscpu    total   
 cpu         1         0.11      0.49      0.60    
 aio         3         0.00      0.02      0.02    
 lio         1         0.00      0.00      0.00    
 pio         1         0.00      0.00      0.00    
 adm         1         0.00      0.04      0.04    
 soc         1         0.00      0.01      0.01    
 msc         1         0.00      0.00      0.00    
 jvp         1         0.00      0.00      0.00    
 fifo        1         0.00      0.00      0.00    
 bts         1         0.00      0.00      0.00    
 idsxmlvp    1         0.00      0.00      0.00    
 tutorial    1         0.04      0.02      0.06    
 total       14        0.15      0.58      0.73    

Individual virtual processors:
 vp    pid       class       usercpu   syscpu    total     Thread    Eff  
 1     11406     cpu         0.11      0.49      0.60      1.98      30%
 2     11407     adm         0.00      0.04      0.04      0.00       0%
 3     11408     bts         0.00      0.00      0.00      0.00       0%
 4     11409     idsxmlvp    0.00      0.00      0.00      0.00       0%
 5     11410     jvp         0.00      0.00      0.00      0.00       0%
 6     11411     tutorial    0.04      0.02      0.06      0.29      20%
 7     11412     lio         0.00      0.00      0.00      0.02       0%
 8     11413     pio         0.00      0.00      0.00      0.01       0%
 9     11414     aio         0.00      0.02      0.02      0.08      25%
 10    11415     msc         0.00      0.00      0.00      0.01       0%
 11    11416     fifo        0.00      0.00      0.00      0.01       0%
 12    11417     aio         0.00      0.00      0.00      0.01       0%
 13    11418     soc         0.00      0.01      0.01      NA         NA
 14    11419     aio         0.00      0.00      0.00      0.01       0%
                 tot         0.15      0.58      0.73
  1. 以根用户身份运行 gdb,并附着 tutorial VP gdb -pid 11411。当 gdb 启动时,您应当能够在您的 VTI UDRs 中设置断点,如 清单 24 所示。
清单 24. 设置断点
gdb --pid 11411
GNU gdb (GDB; SUSE Linux Enterprise 11) 6.8.50.20081120-cvs
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i586-suse-linux".
For bug reporting instructions, please see:
<http://bugs.opensuse.org/>.
Attaching to process 11411
Reading symbols from /opt/IBM/informix/bin/oninit...(no debugging symbols found)...done.
Reading symbols from /lib/libpthread.so.0...(no debugging symbols found)...done.
...
Reading symbols from /opt/IBM/informix/extend/tutorial/tutorial.bld...done.
Loaded symbols for /opt/IBM/informix/extend/tutorial/tutorial.bld
Reading symbols from /usr/lib/libtutorial.so.1...done.
Loaded symbols for /usr/lib/libtutorial.so.1
0xffffe430 in __kernel_vsyscall ()
(gdb) b tutorial_loadlib
Breakpoint 1 at 0xb788c52e: file tutorial.c, line 36.
(gdb) c
Continuing.
  1. 在另一个窗口中,运行 dbaccess
  2. 执行一条让 Informix 服务器调用您的 tutorial_loadlib() UDR 的 SQL 语句,比如 dbaccess - tutorial_example.sql
  3. 查看您在上面设置的断点,如 清单 25 所示。
    清单 25. 断点结果
    Breakpoint 1, tutorial_loadlib (fparam=0x4c842920) at tutorial.c:36
    36		print("loadlib");
    (gdb) s
    38		return MI_TRUE;
    (gdb) c
    Continuing.

结束语

虚拟表接口(VTI)是一个强大的 Informix Dynamic Server 特性,允许用户编写扩展块来访问外部数据源和应用程序。该教程展示了如何使用 VTI 应用程序编译和链接共享库,以及如何编写访问方法及其目的函数。该教程审查了如何使用目的函数,以及如何使用 GDB 调试 VTI 应用程序。有了这个常识,您就可以开始使用虚拟表接口开发自定义应用程序了。


下载资源


相关主题

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Information Management
ArticleID=659802
ArticleTitle=带初学者体验 Informix 虚拟表的接口共享库使用之旅
publish-date=05172011