通过DBLINK调用空间数据库含SDO_CS.transform的函数连接重置问题
你遇到的这个连接重置问题,本质是Oracle 11.2.0.4版本的已知bug:当跨dblink调用涉及MDSYS.SDO_GEOMETRY对象类型的空间函数时,Oracle在处理远程调用的对象序列化/反序列化过程中会出现内存异常或进程崩溃,导致连接被重置。
之所以调用无SDO_GEOMETRY参数的MDSYS函数(比如tfm_axis_orientation_to_wkt)正常,是因为这类函数仅处理基础数据类型,不需要传递自定义的空间对象,避开了这个bug触发的条件。
针对这个问题,有几个可行的解决方案,按优先级排序:
1. 升级Oracle补丁(最优解)
Oracle官方针对这类跨dblink调用空间函数的bug发布过补丁包,你可以在Oracle Support上查找对应11.2.0.4版本的补丁(比如Bug 19623432、Bug 20841478这类和SDO跨dblink调用相关的补丁),安装后即可解决问题。如果你的环境允许升级补丁,这是最彻底的解决方式。
2. 重构函数逻辑,避免传递SDO_GEOMETRY对象
既然你的需求只是转换点的X坐标,完全可以避开SDO_CS.transform(需要处理SDO_GEOMETRY对象),改用接受数值参数的SDO_CS.TRANSFORM_COORDINATE函数。这个函数直接接收原始坐标值、原SRID和目标SRID,返回转换后的坐标,全程不需要处理SDO_GEOMETRY对象,自然不会触发bug。
修改后的函数代码如下:
FUNCTION TEST_GEOMETRY ( p_compteur E_COMPTEUR_ABONNE.numero_reel%type ) return number is w_x number; w_y number; w_srid number; w_transformed_coord MDSYS.SDO_COORDINATE_TYPE; begin begin -- 直接提取点坐标和原SRID,避免完整的SDO_GEOMETRY对象 select sdo_point.x, sdo_point.y, srid into w_x, w_y, w_srid from E_COMPTEUR_ABONNE where numero_reel = p_compteur; EXCEPTION WHEN no_data_found then return null; -- Compteur non trouvée end; -- 使用TRANSFORM_COORDINATE转换坐标,返回SDO_COORDINATE_TYPE类型(仅包含x/y数值) w_transformed_coord := MDSYS.SDO_CS.TRANSFORM_COORDINATE(w_x, w_y, w_srid, 4326); return w_transformed_coord.x; end TEST_GEOMETRY;
修改后,跨dblink调用这个函数就不会再出现连接重置的问题,因为全程没有传递SDO_GEOMETRY这种复杂对象类型。
3. 间接获取转换结果(备选方案)
如果因为某些原因无法修改函数逻辑或升级补丁,可以在空间数据库端创建一个临时表,通过存储过程先完成坐标转换并插入临时表,再从临时表查询结果。不过这种方法步骤繁琐,维护成本较高,仅作为备选:
- 在空间数据库创建临时表:
CREATE GLOBAL TEMPORARY TABLE TMP_TRANSFORMED_COORD ( compteur varchar2(50), x_coord number ) ON COMMIT DELETE ROWS;
- 创建存储过程完成转换并插入临时表:
PROCEDURE TRANSFORM_COMPTEUR_COORD(p_compteur varchar2) is w_geometry MDSYS.SDO_GEOMETRY; w_geometry_transformed MDSYS.SDO_GEOMETRY; begin select geometry into w_geometry from E_COMPTEUR_ABONNE where numero_reel = p_compteur; w_geometry_transformed := MDSYS.SDO_CS.transform(w_geometry,4326); insert into TMP_TRANSFORMED_COORD(compteur, x_coord) values(p_compteur, w_geometry_transformed.sdo_point.x); EXCEPTION WHEN no_data_found then null; end TRANSFORM_COMPTEUR_COORD;
- 跨dblink调用时,先执行存储过程,再查询临时表:
BEGIN test.transform_compteur_coord@elyx('09P010650'); END; / select x_coord from test.tmp_transformed_coord@elyx where compteur='09P010650';
内容的提问来源于stack exchange,提问作者Hector

