暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

解决Oracle以DBLink访问MogDB时字段名大小写问题

接上一个文章:

Oracle通过透明网关以DBLINK形式连接MogDB的方法

问题遗留:字段名必须小写且加双引号

遗留了一个问题, SQL通过字段名访问时dblink,必须明确写小写且必须加双引号,如:

运行以下SQL会报错:


select relname from pg_class@mogdb;
select relname from pg_class@mogdb
*
ERROR at line 1:
ORA-00904: "RELNAME": invalid identifier


原因分析

什么原因呢?

可以看到,报错时,字段名是大写的,因为Oracle默认认为,如果select 字段名不加双引号,就会转换为大写字段名。

而pg系(包括opengauss/MogDB), 字段名默认小写。


跟踪了一下,透明网关通过运行以下语句

在$ORACLE_HOME/hs/log/*.trc里面可以看到:


Entered hgodtab at 2022/09/08-12:56:20
count:1
table: pg_class
SQL text from hgodtab, id=0, len=36 ...
00: 73656C65 6374202A 2066726F 6D202270 [select * from "p]
10: 675F636C 61737322 20776865 72652031 [g_class" where 1]
20: 203D2030 [ = 0]
Entered hgodscr_process_sellist_description at 2022/09/08-12:56:20
Entered hgopcda at 2022/09/08-12:56:20
Column:1(relname): dtype:12 (VARCHAR), prc/scl:63/0, nullbl:0, octet:63, sign:1, radix:0


 hoadaMOD bit-values found (0x200:TREAT_AS_CHAR,0x20:NEGATIVE_HOADADTY)
DTY NULL-OK LEN MAXBUFLEN PR/SC CST IND MOD name
12 VARCHAR N 63 63 0/ 0 0 0 200 relname
-18 UNSIGNED LONG N 4 4 0/ 0 0 0 20 relnamespace
-18 UNSIGNED LONG N 4 4 0/ 0 0 0 20 reltype
-18 UNSIGNED LONG N 4 4 0/ 0 0 0 20 reloftype
-18 UNSIGNED LONG N 4 4 0/ 0 0 0 20 relowner
-18 UNSIGNED LONG N 4 4 0/ 0 0 0 20 relam
-18 UNSIGNED LONG N 4 4 0/ 0 0 0 20 relfilenode
-18 UNSIGNED LONG N 4 4 0/ 0 0 0 20 reltablespace
8 DOUBLE N 8 8 53/ 0 0 0 0 relpages
8 DOUBLE N 8 8 53/ 0 0 0 0 reltuples
4 INTEGER N 4 4 0/ 0 0 0 0 relallvisible
-18 UNSIGNED LONG N 4 4 0/ 0 0 0 20 reltoastrelid
-18 UNSIGNED LONG N 4 4 0/ 0 0 0 20 reltoastidxid
-18 UNSIGNED LONG N 4 4 0/ 0 0 0 20 reldeltarelid
-18 UNSIGNED LONG N 4 4 0/ 0 0 0 20 reldeltaidx
-18 UNSIGNED LONG N 4 4 0/ 0 0 0 20 relcudescrelid
-18 UNSIGNED LONG N 4 4 0/ 0 0 0 20 relcudescidx
12 VARCHAR N 5 5 0/ 0 0 0 200 relhasindex
12 VARCHAR N 5 5 0/ 0 0 0 200 relisshared
1 CHAR N 1 1 0/ 0 0 0 200 relpersistence
1 CHAR N 1 1 0/ 0 0 0 200 relkind
5 SMALLINT N 2 2 0/ 0 0 0 0 relnatts
5 SMALLINT N 2 2 0/ 0 0 0 0 relchecks
12 VARCHAR N 5 5 0/ 0 0 0 200 relhasoids
12 VARCHAR N 5 5 0/ 0 0 0 200 relhaspkey
12 VARCHAR N 5 5 0/ 0 0 0 200 relhasrules
12 VARCHAR N 5 5 0/ 0 0 0 200 relhastriggers
12 VARCHAR N 5 5 0/ 0 0 0 200 relhassubclass
-7 BIT N 1 1 0/ 0 0 0 20 relcmprs
12 VARCHAR N 5 5 0/ 0 0 0 200 relhasclusterkey
12 VARCHAR N 5 5 0/ 0 0 0 200 relrowmovement
1 CHAR N 1 1 0/ 0 0 0 200 parttype
12 VARCHAR N 255 255 0/ 0 0 0 200 relfrozenxid
12 VARCHAR Y 255 255 0/ 0 0 0 200 relacl
12 VARCHAR Y 255 255 0/ 0 0 0 200 reloptions
1 CHAR Y 1 1 0/ 0 0 0 200 relreplident
-18 UNSIGNED LONG Y 4 4 0/ 0 0 0 20 relfrozenxid64
-18 UNSIGNED LONG Y 4 4 0/ 0 0 0 20 relbucket
12 VARCHAR Y 255 255 0/ 0 0 0 200 relbucketkey
-18 UNSIGNED LONG Y 4 4 0/ 0 0 0 20 relminmxid


因此,大小写不匹配,就报错了。


如何解决呢?显然,我们手上没有Oracle gateway的源码。但幸运的是,opengauss connector odbc的源码(后面简称OG ODBC)是有的。


OG ODBC源码分析

会是什么地方返回这个信息呢?

根据这个函数hgodscr_process_sellist_description 的名称猜测,大概率是调用ODBC的 SQLDecribeCol函数。而SQLDecribeCol在OG ODBC里对应的是PGAPI_DecribeCol




PGAPI_DecribeCol在results.c 里面

检查代码发现,字段名返回的是szColName这个变量。



变量在这里赋的值,是col_name(Local变量)的copy.


因此,在这里,把对应的col_name 改为大写,则Oracle gateway获取的列名就是大写。

修改源码

简单修改:



考虑到不能变动默认行为,新增了一个ODBC参数ForceUpperCaseIdentifier及变量force_upper_case_identifier,只有变量force_upper_case_identifier为真时,才会做这个动作,也就是说,如果不是特意修改force_upper_case_identifier这个变量,行为上和以前表现一样。


这个参数怎么加呢?可以参考另一个参数 LowerCaseIdentifier.

包括:

  1. 定义一个INI参数名

dlg_specific.h:#define INI_FORCEUPPERCASEIDENTIFIER "ForceUpperCaseIdentifier"

2,在Struct ConnInfo里面新增一个变量(位置根据lower_case_identifier找)

psqlodbc.h: signed char force_upper_case_identifier;

3,默认初始化为 -1 (False),(位置根据lower_case_identifier找)

dlg_specific.c: conninfo->force_upper_case_identifier = -1;

4, 根据INI配置文件里的值进行设置(位置根据lower_case_identifier找)

dlg_specific.c: ci->force_upper_case_identifier = atoi(temp);

dlg_specific.c: CORR_VALCPY(force_upper_case_identifier);


加上对results.c里的更改,就组成完整的代码了。

实施并测试

编译并安装OG ODBC

make && make install


调整上一篇文章中的odbc.ini ,在对应的DSN下面增加这一行

ForceUpperCaseIdentifier=1


重新测试,跑通了。



检查trc, 里面的字段名果然都变成大写了。


总结

对于实施人员而言,就是两个步骤

1, 使用定制版OG ODBC

2, odbc.ini里面增加 ForceUpperCaseIdentifier=1


新的遗留问题


当然,如果你在mogdb里面用了大小写敏感的字段名,想在Oracle里面通过双引号来访问他,就会不能用。

MogDB端:

create table test_case_intensive (test1 int, "Test2" int);

Oracle端:

13:58:58 SQL> select * from test_case_intensive@mogdb;
select * from test_case_intensive@mogdb
*
ERROR at line 1:
ORA-28500: connection from ORACLE to a non-Oracle system returned this message:
ERROR: column a1.test2 does not exist;
No query has been executed with that handle {42703,NativeErr = 1}
ORA-02063: preceding 3 lines from MOGDB

SQL> rollback;


13:59:30 SQL> select test1 from test_case_intensive@mogdb;

no rows selected

13:59:44 SQL> select test2 from test_case_intensive@mogdb;
select test2 from test_case_intensive@mogdb
*
ERROR at line 1:
ORA-28500: connection from ORACLE to a non-Oracle system returned this message:
ERROR: column a1.test2 does not exist;
No query has been executed with that handle {42703,NativeErr = 1}
ORA-02063: preceding 3 lines from MOGDB


Elapsed: 00:00:00.00
13:59:51 SQL> rollback;

Rollback complete.

Elapsed: 00:00:00.00
14:00:32 SQL> select "Test2" from test_case_intensive@mogdb;
select "Test2" from test_case_intensive@mogdb
*
ERROR at line 1:
ORA-00904: "Test2": invalid identifier


Elapsed: 00:00:00.00



鱼和熊掌不可兼得,出现这种情况,只能是

  1.  odbc.ini里面的ForceUpperCaseIdentifier去掉,或者设置为0, 
  2. 把init<GATEWAY_SID>.ora 里的HS_FDS_QUOTE_IDENTIFIER设为TRUE
  3. 然后Oracle段访问dblink时,统一加双引号了,默认的字段名用小写+双引号,默认表名用小写+双引号,特意大小写敏感字段名大小写根据实际情况加双引号。


修改odbc.ini/init.ora后:

14:04:18 SQL> select "test1","Test2" from "test_case_intensive"@mogdb;

no rows selected


最后修改时间:2022-09-09 12:09:52
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论