Kingbase ES 游标跨事务及其案例一则

发布时间 2023-09-18 14:32:08作者: KINGBASE研究院

前言

本文介绍游标在PLSQL和SQL层跨事务使用案例,及其案例一则。

本文验证版本:

db0=# select version();
db0-# /
            version

----------------------------------------

 KingbaseES V008R006C007B0024 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-28), 64-bit
(1 row)

一,PLSQL块中的游标跨事务

原生plpgsql跨事务例子,不支持跨事务

drop table t2;

create table t2(id int,name varchar(20));
insert into t2 values (1,'aaa'), (2,'bbb'), (3,'ccc');
create or replace procedure testcur02
as
$$
declare
    cur1 refcursor;
    vid numeric;
    vname varchar(20);
begin
    open cur1 for select * from t2;
    commit;
    fetch cur1 into vid,vname;   --因为cur1在commit后被自动释放,所以将报错不存在
    raise notice 'vid:%,vname:%',vid,vname;
    close cur1;
end;
$$ language plpgsql;

call testcur02();


--运行结果
db0=# call testcur02();
db0-#
db0-# /
error:  cursor "<unnamed portal 8>" does not exist
context:  pl/pgsql function testcur02() line 9 at fetch

kes-plsql默认支持存储过程中跨事务

drop table t2;

create table t2(id int,name varchar(20));
insert into t2 values (1,'aaa'), (2,'bbb'), (3,'ccc');
create or replace procedure testcur01
is
declare
    cur1 refcursor;
    vid numeric;
    vname varchar(20);
begin
    open cur1 for select * from t2;
    commit;
    fetch cur1 into vid,vname;   --cur1在commit后不会被自动释放
    raise notice 'vid:%,vname:%',vid,vname;
    close cur1;
end;

call testcur01();

--运行结果
db0=# call testcur01();
db0-# /
notice:  vid:1,vname:aaa
call
db0=#

二,SQL层的游标和事务

SQL层也有游标语句,也可声明游标,为了验证跨事务问题,以fetch all in语句为例。

create or replace procedure testcur04( pcur1 out refcursor)
is
begin
    open pcur1 for select * from t2;
end;


db0=# begin;   ---开启事务
db0-# /
BEGIN
db0=# call testcur04('vcur');   ----获取游标结果集
db0-# /

 pcur1
-------

 vcur
(1 row)

db0=# fetch all in vcur;    ---一次性提取游标结果集
db0-# /
 id | name
----+------
  1 | aaa
  2 | bbb
  3 | ccc
(3 rows)

db0=#

在早前的V7版本,可以通过开启 enable_out_refcursor_holdable 参数为on,
实现在SQL层跨事务执行上述游标结果集(要求存储过程的参数模式为out 或inout),目前实测来看V008R006C007B0024已不支持该参数。

三,游标跨事务案例一则

这是一个V7版本上产生的问题,

问题描述客户应用层代码使用fetch al in获取游标结果集,但是报错游标不存在。完整的截取客户代码如下;

[2023-07-07 09:56:20.605 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: SELECT nspname, proname, proretset, prorettypedef, pronargs, proargtypes, protype, proargnames, proargmodes, proargtypmods FROM sys_catalog.sys_namespace JOIN sys_catalog.sys_proc on sys_proc.pronamespace = sys_namespace.oid AND (not proretset) AND nspname NOT LIKE 'SYS_%' ESCAPE '' AND nspname != 'INFORMATION_SCHEMA' LEFT JOIN sys_catalog.sys_package ON sys_proc.propkgoid = sys_package.oid AND sys_package.pkgnamespace = sys_namespace.oid WHERE 1 AND proname LIKE 'PROC_KBV7_OCI_0X1794F90' ESCAPE '' ORDER BY nspname, proname, proretset

---第一段黑体字

[2023-07-07 09:56:20.607 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: SET ENABLE_OUT_REFCURSOR_HOLDABLE TO on
[2023-07-07 09:56:20.608 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: call PROC_KBV7_OCI_0x1794f90('KDBCUR_178A2F0_0')
[2023-07-07 09:56:20.609 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: fetch all in KDBCUR_178A2F0_0
[2023-07-07 09:56:20.609 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: drop procedure if exists PROC_KBV7_OCI_0x1794f90(REFCURSOR)
[2023-07-07 09:56:20.611 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: close KDBCUR_178A2F0_0

[2023-07-07 09:56:20.613 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: parse _PLAN0X178D4B0: select object_name from user_objects where object_name = 'TRIGGER_PKG' and (object_type = 'PACKAGE' or object_type = 'PACKAGE BODY') and status<>'VALID'
[2023-07-07 09:56:20.616 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: EXECUTE _PLAN0x178d4b0()
[2023-07-07 09:56:20.616 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]DETAIL: prepare: select object_name from user_objects where object_name = 'TRIGGER_PKG' and (object_type = 'PACKAGE' or object_type = 'PACKAGE BODY') and status<>'VALID'
[2023-07-07 09:56:20.623 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: close _PLAN0X178D4B0
[2023-07-07 09:56:20.624 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: SELECT PRONAME, PROTYPE FROM SYS_PROC, SYS_PACKAGE WHERE SYS_PROC.PROPKGOID = SYS_PACKAGE.OID AND PKGNAME = UPPER('TRIGGER_PKG');
[2023-07-07 09:56:20.626 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: SELECT nspname, proname, proretset, prorettypedef, pronargs, proargtypes, protype, proargnames, proargmodes, proargtypmods FROM sys_catalog.sys_namespace JOIN sys_catalog.sys_proc on sys_proc.pronamespace = sys_namespace.oid AND (not proretset) AND nspname NOT LIKE 'SYS_%' ESCAPE '' AND nspname != 'INFORMATION_SCHEMA' LEFT JOIN sys_catalog.sys_package ON sys_proc.propkgoid = sys_package.oid AND sys_package.pkgnamespace = sys_namespace.oid WHERE 1 AND pkgname LIKE UPPER('TRIGGER_PKG') ESCAPE '' AND proname LIKE UPPER('SELECT_TRIGGER_ACTIONS') ESCAPE '' ORDER BY nspname, proname, proretset
[2023-07-07 09:56:20.628 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: CREATE OR REPLACE PROCEDURE PROC_KBV7_OCI_0x1786320(INTERNAL_PREFIX_0 INOUT REFCURSOR)
AS
begin TRIGGER_PKG.select_trigger_actions(INTERNAL_PREFIX_0); end;
[2023-07-07 09:56:20.631 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: SELECT nspname, proname, proretset, prorettypedef, pronargs, proargtypes, protype, proargnames, proargmodes, proargtypmods FROM sys_catalog.sys_namespace JOIN sys_catalog.sys_proc on sys_proc.pronamespace = sys_namespace.oid AND (not proretset) AND nspname NOT LIKE 'SYS_%' ESCAPE '' AND nspname != 'INFORMATION_SCHEMA' LEFT JOIN sys_catalog.sys_package ON sys_proc.propkgoid = sys_package.oid AND sys_package.pkgnamespace = sys_namespace.oid WHERE 1 AND proname LIKE 'PROC_KBV7_OCI_0X1786320' ESCAPE '' ORDER BY nspname, proname, proretset

---第二段黑体字

[2023-07-07 09:56:20.633 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: call PROC_KBV7_OCI_0x1786320('KDBCUR_1789020_1')
[2023-07-07 09:56:20.634 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: fetch all in KDBCUR_1789020_1
2023-07-07 09:56:20.634 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]ERROR: cursor "KDBCUR_1789020_1" does not exist
[2023-07-07 09:56:20.634 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]STATEMENT: fetch all in KDBCUR_1789020_1
[2023-07-07 09:56:20.635 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: drop procedure if exists PROC_KBV7_OCI_0x1786320(REFCURSOR)
[2023-07-07 09:56:20.636 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]LOG: statement: close KDBCUR_1789020_1
[2023-07-07 09:56:20.636 CST] [192.1.153.142][192.1.153.142(49637)][EMS][34][D5000][0]ERROR: cursor "KDBCUR_1789020_1" does not exist

从上面第一段黑体的日志可以看出,SET ENABLE_OUT_REFCURSOR_HOLDABLE TO on使游标可以跨事务执行,并且fetch all in KDBCUR_178A2F0_0执行游标提取成功,但是在第二段日志中同样的操作却出现了报错“ cursor "KDBCUR_1789020_1" does not exist”问题。

通过自己编写缩小的案例实测:

create or replace procedure proc_test01 (
    pcur inout refcursor  
)
as
begin
    open for select * from t1;
end ;

SET ENABLE_OUT_REFCURSOR_HOLDABLE TO on;

CALL PROC_TEST01('KDBCUR')

fetch all in KDBCUR

以上以上代码是能获取到结果集,但是必须要求proc_test01的参数为inout或者out模式,最终通过追查客户PROC_KBV7_OCI_0x1786320存储过程的定义,发现其未将参数模式定位为inout或者out模式,而是IN模式,故而问题找到原因。