之前整理了一篇 postgresql FDW概念、用法与原理小结_Hehuyi_postgres_fdw、mysql_fdw、file_fdw的用法。

        最近遇到pg访问SqlServer数据的需求,过程有点坎坷,记录一下。

一、 安装tds_fdw

1. freetds安装

tds_fdw依赖于freetds,因此要先安装freetds

yum install gcc -y

wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-5.noarch.rpm

rpm -ivh epel-release-7-14.noarch.rpm 
yum install freetds -y
yum install freetds-devel -y

2. 下载并解压tds_fdw包

下载地址:https://github.com/tds-fdw/tds_fdw

上传至服务器并解压,规范起见一般跟其他插件放在一起

unzip tds_fdw-master.zip
mv tds_fdw-master tds_fdw

3. 编译安装tds_fdw

关于 USE_PGXS=1 参考:USE_PGXS 在 extension 编译中的作用_weixin_3027541

make USE_PGXS=1
make USE_PGXS=1 install

二、 tds_fdw 访问 SQL Server数据

还是四部曲:extension -> server -> user mapping -> foreign table

1. extension

必须用超级用户postgres在指定db创建

CREATE EXTENSION tds_fdw;

如果后续不打算用postgres创建,需要给普通用户授权

GRANT USAGE ON FOREIGN DATA WRAPPER tds_fdw TO dba_rw;

2. server

CREATE SERVER mssql_server
FOREIGN DATA WRAPPER tds_fdw
OPTIONS (servername '目标端ip', port '1433', database '目标库名', tds_version '7.3');

-- 低版本tds有各种bug,可以手动指定tds版本
-- 还可以指定 msg_handler 'notice',输出信息会更多,一般用于报错排查

3. user mapping

CREATE USER MAPPING FOR dba_rw
SERVER mssql_server
OPTIONS (username '目标库用户', password 'xxxx');

4. foreign table

CREATE FOREIGN TABLE mssql_fdw_tmptab (id varchar(32) NOT NULL)
SERVER mssql_server 
OPTIONS (schema_name 'dbo', table_name '目标表名');

5. 测试查询外部表

psql -Udba_rw -dmydb

select id from mssql_fdw_tmptab limit 5;

NOTICE:  tds_fdw: Query executed correctly
NOTICE:  tds_fdw: Getting results
                id                
----------------------------------
 007443fc-e602-45fe-bf08-0575dd46
 007bcf86-19ad-49cb-8b8b-6e84177c
 00a12c2e-5fb4-4b0f-bb7f-0b5a9129
 013ccacb-a55d-473a-952d-80703926
 0173c812-f13f-4257-a559-44bb7aed
(5 rows)

三、 报错分析处理

1. tds_fdw make USE_PGXS=1报错

-bash-4.2$ make USE_PGXS=1 PG_CONFIG=/bin/pg_config
 Makefile:54: /usr/lib64/pgsql/pgxs/src/makefiles/pgxs.mk: No such file or directory
 make: *** No rule to make target `/usr/lib64/pgsql/pgxs/src/makefiles/pgxs.mk'.  Stop.

       网上很多文章都解释是因为缺少 postgresql-devel包,但我们的pg是通过编译安装的,实际并不缺少这个包。find搜索可以查到其路径:

find / -name pgxs.mk

/.../contrib/9.6.2/lib/pgxs/src/makefiles/pgxs.mk
/.../postgresql-9.6.2/src/makefiles/pgxs.mk
/.../tmp_install/.../lib/pgxs/src/makefiles/pgxs.mk

        从tds_fdw的Makefile文件可以看到,报错的路径来自

PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)

       也就是 pg_config --pgxs 命令,执行可以看到,确实是 /usr/lib64/pgsql/pgxs/src/makefiles/pgxs.mk

这个命令结果与postgres用户的 LD_LIBRARY_PATH 环境变量有关,因此解决方法就是将 /.../contrib/9.6.2/lib 路径加入LD_LIBRARY_PATH 环境变量。

      设置环境变量后重新执行 make USE_PGXS=1 ,编译成功。

2. 外部表查询报错 OS #: -1, OS Msg: , Level: 16

兴高采烈走到最后一步时,测试查询外部表报错

select count(*) from mssql_fdw_tmptab;

ERROR:  DB-Library error: DB #: 20018, DB Msg: General SQL Server error: Check messages from the SQL Server, OS #: -1, OS Msg: , Level: 16

这个报错信息简单得令人发指,基本什么都看不出来...

查询文档发现可以给 fdw server 指定 msg_handler 'notice',查看详细信息(默认不输出)

可以在创建时指定,但平时会有很多多余信息

CREATE SERVER mssql_server
FOREIGN DATA WRAPPER tds_fdw
OPTIONS (servername '目标端ip', port '1433', database '目标库名', tds_version '7.3',msg_handler 'notice');

也可以在需要时设置

ALTER SERVER mssql_server OPTIONS (ADD msg_handler 'notice');
-- 配合客户端 client_min_messages 设置为 debug3,可以看到更多信息(非必须)
SET client_min_messages=DEBUG3;

设置后输出的报错信息如下:

mydb=> select count(*) from mssql_fdw_tmptab;
 DEBUG:  StartTransactionCommand
 DEBUG:  StartTransaction
 DEBUG:  name: unnamed; blockState:       DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: 
 DEBUG:  tds_fdw: Using remote estimate
 DEBUG:  tds_fdw: Getting query
 DEBUG:  tds_fdw: Value of query is SELECT NULL FROM [dbo].[tmptab]
 DEBUG:  tds_fdw: Initiating DB-Library
 DEBUG:  tds_fdw: Getting login structure
 DEBUG:  tds_fdw: Setting login user to db_r
 DEBUG:  tds_fdw: Setting login password to xxx
 DEBUG:  tds_fdw: Connection string is xxx:1433
 DEBUG:  tds_fdw: Connecting to server
NOTICE:  DB-Library notice: Msg #: 5701, Msg state: 2, Msg: Changed database context to 'master'., Server: xxxx, Process: , Line: 1, Level: 0
 NOTICE:  DB-Library notice: Msg #: 5703, Msg state: 1, Msg: Changed language setting to us_english., Server: xxxx, Process: , Line: 1, Level: 0
 DEBUG:  tds_fdw: Connected successfully
 DEBUG:  tds_fdw: Setting database command to SELECT NULL FROM [dbo].[tmptab]
 DEBUG:  tds_fdw: Executing the query
NOTICE:  DB-Library notice: Msg #: 208, Msg state: 1, Msg: Invalid object name 'dbo.tmptab'., Server: xxx, Process: , Line: 1, Level: 16
 ERROR:  DB-Library error: DB #: 20018, DB Msg: General SQL Server error: Check messages from the SQL Server, OS #: -1, OS Msg: , Level: 16

        报错  Invalid object name,其实是因为mssql_server创建时没有指定DB,默认使用了master,因此它找不到这个对象。

        解决方法是在create server时指定database参数(参考前面)。

3. 外部表查询报错 OS #: 0, OS Msg: Success, Level: 9

原报错为

ERROR:  DB-Library error: DB #: 20002, DB Msg: Adaptive Server connection failed (xxx), OS #: 0, OS Msg: Success, Level: 9

从上面看客户端debug的信息没有太大用处,设置msg_handler基本够了

ALTER SERVER mssql_server OPTIONS (ADD msg_handler 'notice');

设置后输出的报错信息如下:

mydb=> select count(*) from mssql_fdw_tmptab;
NOTICE:  DB-Library notice: Msg #: 4060, Msg state: 1, Msg: Cannot open database "work" requested by the login. The login failed., Server: xxxx, Process: , Line: 1, Level: 11
 NOTICE:  DB-Library notice: Msg #: 18456, Msg state: 1, Msg: Login failed for user 'db_r'., Server: xxxx, Process: , Line: 1, Level: 14
 ERROR:  DB-Library error: DB #: 20002, DB Msg: Adaptive Server connection failed (xxxx), OS #: 0, OS Msg: Success, Level: 9

可以看到报错为无权限登录目标db 'work',需要检查并修改SqlServer用户权限。

4. 外部表查询报错 OS #: -1, OS Msg: , Level: 14 

原报错为

ERROR:  DB-Library error: DB #: 20018, DB Msg: General SQL Server error: Check messages from the SQL Server, OS #: -1, OS Msg: , Level: 14

ALTER SERVER mssql_server OPTIONS (ADD msg_handler 'notice');

设置后输出的报错信息如下:

mydb=> select count(*) from mssql_fdw_tmptab;
 NOTICE:  DB-Library notice: Msg #: 5701, Msg state: 2, Msg: Changed database context to 'work'., Server: xxxx, Process: , Line: 1, Level: 0
 NOTICE:  DB-Library notice: Msg #: 5703, Msg state: 1, Msg: Changed language setting to us_english., Server: xxxx, Process: , Line: 1, Level: 0
NOTICE:  DB-Library notice: Msg #: 229, Msg state: 5, Msg: The SELECT permission was denied on the object 'DBRequirement', database 'bpm', schema 'dbo'., Server: xxxx, Process: , Line: 1, Level: 14
 ERROR:  DB-Library error: DB #: 20018, DB Msg: General SQL Server error: Check messages from the SQL Server, OS #: -1, OS Msg: , Level: 14

可以看到报错为无权限访问DBRequirement表,SqlServer用户需要加上对应权限。

参考:

https://github.com/tds-fdw/tds_fdw

SQL Server and PostgreSQL Foreign Data Wrapper Configuration - Part 3

Using Foreign Data Wrapper from PostgreSQL - executing procedures in SQL Server?