oracle_fdw 使用手册

1 概述

oracle_fdw 用于让 UDB 通过 Oracle Client(OCI)访问 Oracle 数据库,并把 Oracle 的表(或查询结果)映射为 UDB 内可查询的外部表。

1.1 适用范围与选型建议

  • 当目标数据源为 Oracle,优先使用 oracle_fdw;相比通用 ODBC 方案,专用 FDW 通常在类型映射、谓词下推、执行计划展示等方面更可控。

  • oracle_fdw 依赖 Oracle Client/Instant Client 的运行时环境;外部访问由 UDB 服务进程发起,因此环境变量必须对服务进程生效。

1.2 工作链路

SQL 查询
  → UDB 服务进程
    → oracle_fdw
      → Oracle Client(OCI / Instant Client)
        → Oracle 服务器

交付包通常已包含 oracle_fdw 扩展文件;正常情况下无需额外准备,只需在 ud_sql 中创建扩展、创建外部服务器与外表即可使用。若连接失败,再参考本文“连接失败排查”。

2 快速开始

2.1 前提

  • UDB 服务已启动,ud_sql 可正常进入(例如 unvdb=#)。

  • 目标 Oracle 可访问(网络可达、账号具备权限)。

  • Oracle 用户至少具备 CREATE SESSION 权限,并对目标表/视图具备 SELECT 权限。

路径约定(示例,按现场替换):

  • <安装包目录>:UDB 安装/解压目录,例如 /home/unvdb/work/UDB-TX-26

  • <数据目录>:UDB 数据目录,例如 /home/unvdb/work/unvdb-data

  • 环境变量推荐写入:<数据目录>/env.sh(示例:/home/unvdb/work/unvdb-data/env.sh),并在启动服务前 source 使其对服务进程生效

2.2 最小可用 SQL(复制后替换占位符)

占位符说明:

  • <oracle_host>:Oracle 主机地址

  • <oracle_port>:Oracle 监听端口(默认场景常见为 1521,现场以实际为准)

  • <oracle_service>:服务名(service name)

  • <oracle_user> / <oracle_pwd>:Oracle 用户与口令

  • <oracle_table>:要映射的 Oracle 表名(以 Oracle 数据字典为准,常见为大写)

  • <tns_alias>:TNS 别名(使用 tnsnames.ora 时)

建议在执行前先确认三件事:Oracle 的主机/端口/服务名;Oracle 用户是否有权限;目标表是否存在且列结构与下方外表定义匹配。

提示:下面快速开始中的外表列 id/name 仅为示例(用于演示环境的简单表)。实际使用时,请按目标 Oracle 表的列顺序与类型调整外表列定义;不要假设所有目标表都是两列且列名/类型固定。

如何确认 <oracle_service>(service name)与端口:

  • 最简单:向 Oracle DBA 获取一条可用连接串(Easy Connect 或 tnsnames.ora 中的别名)。

  • 如果现场有 tnsnames.ora:直接使用其中的 <tns_alias>(见下方 TNS 示例),或从文件中读取 HOST/PORT/SERVICE_NAME 填入 Easy Connect。

  • 如果有权限在 Oracle 上执行 SQL,可查询服务名:SELECT name FROM v$services;

如何确认目标表列顺序/类型(用于定义外表列):

  • 如果能在 Oracle 上执行 SQL:SELECT column_id, column_name, data_type FROM all_tab_columns WHERE owner='<oracle_user>' AND table_name='<oracle_table>' ORDER BY column_id;

CREATE EXTENSION IF NOT EXISTS oracle_fdw;

DROP SERVER IF EXISTS oracle_svr CASCADE;
CREATE SERVER oracle_svr
FOREIGN DATA WRAPPER oracle_fdw
OPTIONS (dbserver '//<oracle_host>:<oracle_port>/<oracle_service>');

DROP USER MAPPING IF EXISTS FOR CURRENT_USER SERVER oracle_svr;
CREATE USER MAPPING FOR CURRENT_USER
SERVER oracle_svr
OPTIONS (user '<oracle_user>', password '<oracle_pwd>');

DROP FOREIGN TABLE IF EXISTS ft_oracle_demo;
CREATE FOREIGN TABLE ft_oracle_demo (
  id   numeric,
  name text
)
SERVER oracle_svr
OPTIONS (schema '<oracle_user>', table '<oracle_table>');

SELECT * FROM ft_oracle_demo;

要点:

  • dbserver 推荐使用 Easy Connect://host:port/service(port 常见为 1521,现场以实际为准)

  • 客户现场如已维护 tnsnames.ora,也可使用 TNS 别名方式:OPTIONS (dbserver '<tns_alias>'),并在 <数据目录>/env.sh 中设置 TNS_ADMIN 指向 tnsnames.ora 所在目录

  • 外表列需与 Oracle 侧列类型兼容;首次验证建议先选简单表或只映射少量列

  • schema 通常为 Oracle 用户名;table 名称以 Oracle 数据字典为准(常见为大写)

示例(TNS 别名方式):

DROP SERVER IF EXISTS oracle_svr CASCADE;
CREATE SERVER oracle_svr
FOREIGN DATA WRAPPER oracle_fdw
OPTIONS (dbserver '<tns_alias>');
export TNS_ADMIN=<tnsnames.ora所在目录>

3 常见用法

3.1 映射 Oracle 表

CREATE FOREIGN TABLE ft_t1 (
  id   numeric OPTIONS (key 'true'),
  name text
)
SERVER oracle_svr
OPTIONS (schema '<oracle_user>', table 'T1');

说明:

  • 这是 oracle_fdw 的限制:对外表执行 UPDATE/DELETE 时,至少需要一列标记为主键列(OPTIONS (key 'true')),否则会报错 “no primary key column specified for foreign Oracle table”

  • 与“快速开始”的区别:快速开始的外表示例主要用于验证 SELECT 跑通;若你还希望对 Oracle 端数据执行 UPDATE/DELETE,需要像本节这样在外表列上标记 key(联合主键则每个主键列都要标记)

3.2 映射 Oracle 查询(只读建议)

CREATE FOREIGN TABLE ft_q1 (
  id   numeric,
  name text
)
SERVER oracle_svr
OPTIONS (table '(SELECT id, name FROM T1 WHERE id <= 100)', readonly 'true');

说明:

  • 映射查询时通常不再设置 schema;以 table '(SELECT ...)' 为准。

  • 写入(INSERT/UPDATE/DELETE)仅可能在“简单查询”上工作;复杂查询建议开启 readonly

4 诊断与运维

4.1 查看诊断信息

SELECT oracle_diag();

用途:查看 oracle_fdw 与 Oracle client 的版本、以及服务进程视角的关键环境信息。

也可传入外部服务器名,额外显示 Oracle server 版本(示例):

SELECT oracle_diag('<foreign_server_name>');

说明:<foreign_server_name> 为你实际创建的 FOREIGN SERVER 名称(例如前文示例为 oracle_svr,现场也可能叫 ora_svr)。

4.2 关闭当前会话缓存连接

SELECT oracle_close_connections();

适用:修改了连接相关环境或希望释放 Oracle 会话资源。

5 连接失败排查(仅当报错时看)

5.1 典型报错:OCIEnvCreate failed

常见原因(按优先级):

  • Oracle client 库不在服务进程可见的库搜索路径中(LD_LIBRARY_PATH 对服务进程未生效)

  • Instant Client 缺少必要 libclntsh.so* 软链接或依赖缺失

  • UDB 与 Oracle client 架构/位数不一致

建议按顺序处理:

  1. ud_sql 执行 SELECT oracle_diag();,核对版本与环境是否符合预期

  2. 确认“启动 UDB 服务进程之前”已加载环境(例如数据目录的 env.sh);修改后需重启服务使其生效

  3. 若现场使用自备 Instant Client,确保 libclntsh.so* 及依赖库齐全,并按 Oracle 要求创建必要软链接

可直接使用的定位命令:

  • 第 1 步(数据库侧诊断):

SELECT oracle_diag();
  • 第 2 步(确认 env.sh 生效到服务进程):

# 1) 找到 UDB 服务进程 PID(按现场实际进程名调整关键字)
pgrep -af 'unvdbsvr|unvdb'

# 2) 查看该 PID 进程实际继承到的环境变量(以服务进程为准)
PID=<上一步看到的PID>
tr '\0' '\n' < /proc/$PID/environ | egrep '^(LD_LIBRARY_PATH|TNS_ADMIN|ORACLE_HOME)='

# 3) 推荐把环境变量写入 <数据目录>/env.sh(示例:/home/unvdb/work/unvdb-data/env.sh)
grep -nE '^(export )?(LD_LIBRARY_PATH|TNS_ADMIN|ORACLE_HOME)=' <数据目录>/env.sh

说明:

  • 重点看第 2) 的输出:如果服务进程环境里没有 LD_LIBRARY_PATH(或不包含 Oracle client 目录),oracle_fdw 很可能无法加载 libclntsh.so*等依赖库。

  • 修改 <数据目录>/env.sh 后需要先关闭 UDB 服务进程,然后 source <数据目录>/env.sh 再启动,新的环境变量才会对服务进程生效。

5.2 现场自备 Oracle Client/Instant Client 时的环境变量

以下变量必须在“启动 UDB 服务进程之前”生效:

export LD_LIBRARY_PATH=<instantclient_or_oracle_home_lib>:$LD_LIBRARY_PATH
export TNS_ADMIN=<network_admin_dir>

推荐落地方式(写入 <数据目录>/env.sh 并在启动前加载):

source <数据目录>/env.sh
<数据目录>/start.sh

5.3 依赖库自检

目的:确认交付包中的 Oracle client 运行库齐全、软链接正确、依赖库无缺失,并且对 UDB 服务进程可见。

假设交付包目录为 <安装包目录>(示例:/home/unvdb/work/UDB-TX-26)。

  1. 确认 Oracle client 库是否存在(示例路径,按实际替换):

ls -l <安装包目录>/lib/libclntsh.so* <安装包目录>/lib/libociei.so* 2>/dev/null
ls -l <安装包目录>/lib/ | egrep 'libclntsh|libociei|libnnz|libons|libclntshcore' || true
  1. 确认软链接是否指向真实文件:

readlink -f <安装包目录>/lib/libclntsh.so
readlink -f <安装包目录>/lib/libociei.so 2>/dev/null
  1. 用 ldd 检查依赖是否缺失(出现 not found 就是“库缺失或路径未生效”):

ldd <安装包目录>/lib/oracle_fdw.so | egrep 'not found|libclntsh|libociei' || true
ldd <安装包目录>/lib/libclntsh.so* | egrep 'not found' || true

说明:

  • 如果 ldd 输出包含 not found,通常需要补齐缺失 so,或确保 LD_LIBRARY_PATH 通过 <数据目录>/env.sh 在启动前生效。

  • 软链接只能解决“库名匹配/SONAME 找得到”,不能替代真实库文件与完整依赖链。