2. KingbaseES V8.3和 V8.6 兼容性
本部分包含如下章节:
KingbaseES V8.3 和 V8.6 兼容特性概览
2.1. KingbaseES V8.3 和 V8.6 兼容特性概览
本章节包含以下内容:
2.1.1. 兼容性开关
KingbaseES用户可通过设置相关的数据库兼容开关,部分或全部启用Oracle兼容特性。在实际应用中,用户可采用以下途径设置Oracle兼容开关:
在数据库实例data目录下的kingbase.conf文件中配置
在数据库初始化时设置
在用户会话中设置
KingbaseES提供了多个Oracle特性兼容开关。在Oracle移植过程中,用户可按需使用这些开关。下表列出KingbaseES提供的Oracle兼容特性开关。
2.1.2. 模式和对象
本节内容旨在为移植过程的相关模式修改操作提供参考指南。
2.1.2.1. 扩展数据类型
为兼容Oracle的数据类型,KingbaseES扩展了Oracle的NUMBER、VARCHAR2、CHAR(n)和DATE类型。该措施使得移植Oracle的Create Table等DDL语句时,无需任何修改就能直接在KingbaseES环境中运行。
下面各表对比了KingbaseES和Oracle在各种数据类型上的异同点。
2.1.2.2. 模式
V8.6 中search_path中的模式名,需要写成小写;
例:
V8.3:
show search_path ; search_path ----------------- "$USER", PUBLIC (1 row)
V8.6 :
show search_path ; search_path ----------------- "$user", public (1 row)
2.1.2.3. 大小写敏感
例:
V8.3:
show case_sensitive ; case_sensitive ---------------- on (1 row)
V8.6 :
show enable_ci ; enable_ci ---------------- on (1 row)
2.1.2.4. 序列
V8.3中select * from sequencename可以得到10列信息, V8.6 直接select * from sequencename只有3列,其余列可通过select * FROM all_sequences WHERE sequence_name=UPPER('sequencename')找到。
例:
V8.3:
CREATE SEQUENCE serial START 1;
select * from serial;
SEQUENCE_NAME | LAST_VALUE | START_VALUE | INCREMENT_BY | MAX_VALUE | MIN_VALUE | CACHE_VALUE | LOG_CNT | I
S_CYCLED | IS_CALLED
---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+--
---------+-----------
SERIAL | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f
| f
(1 row)
V8.6 :
CREATE SEQUENCE serial START 1;
select * from serial ;
last_value | log_cnt | is_called
------------+---------+-----------
1 | 0 | f
(1 row)
select * FROM all_sequences WHERE sequence_name=UPPER('serial');
sequence_owner | sequence_name | min_value | max_value | increment_by | cycle_flag | order_flag | cache_siz
e | last_number
----------------+---------------+-----------+---------------------+--------------+------------+------------+----------
--+-------------
abcd | SERIAL | 1 | 9223372036854775807 | 1 | f | t |
1 |
(1 row)
2.1.2.5. 同义词
V8.6 找同义词时,对于同义词指向的对象,首先依然作为同义词进行递归查找和成环检测,而不是把同义词指向的对象首先按照普通对象查找,这个处理顺序和R3不同。
2.1.2.6. 分区
V8.6 分区支持的兼容V8.3,支持分区的alter,支持全局索引,支持interval分区,不支持reference分区
2.1.2.7. 全局临时表
V8.3支持本地临时表,不支持全局临时表。
V8.6 支持本地临时表和全局临时表。
2.1.2.8. kdb_schedule
目前龙芯平台没有 kdb_schedule 依赖的系统库libboost_system.so.1.69.0, ES安装程序将这个库放到了Server/lib下,使用kdb_schedule之前需要设置export LD_LIBRARY_PATH 中包含Server/lib。
export LD_LIBRARY_PATH = $LD_LIBRARY_PATH:/Kingbase_install_dir/Server/lib
2.1.3. SQL语句
对于大多数常用的Oracle SQL语句,KingbaseES均提供了原生支持。该措施使得Oracle应用程序移植到KingbaseES系统时,通常只需很少的代码变动。
下面给出KingbaseES中原生支持的Oracle SQL语句。此外,若未做特殊说明,本节示例的代码在KingbaseES和Oracle上均可运行。
2.1.3.1. 支持CREATE TABLE WITH OIDS语句
V8.6 支持create table with oids,表中的系统隐含列不再包含OID,但是这样创建的表,包含了一个用户隐含列oid,类型是oid。对于oracle rowid,需要用户创建隐含列rowid,类型是oid。
例:
create table tt (a int) with oids; ERROR: syntax error at or near "oids" create table tt1 (a int); select oid,relname from pg_class where relname = 'tt1'; oid | relname -------+--------- 16403 | tt1 (1 row)
2.1.3.2. select * from sequencename 语句
V8.3中select * from sequencename可以得到10列信息, V8.6 直接select * from sequencename只有3列,其余列可通过select * FROM all_sequences WHERE sequence_name=UPPER('sequencename')找到。
例:
V8.3:
CREATE SEQUENCE serial START 1;select * from serial; SEQUENCE_NAME | LAST_VALUE | START_VALUE | INCREMENT_BY | MAX_VALUE | MIN_VALUE | CACHE_VALUE | LOG_CNT | IS_CYCLED | IS_CALLED ---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- SERIAL | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f (1 row)
V8.6 :
CREATE SEQUENCE serial START 1;
select * from serial ;
last_value | log_cnt | is_called
------------+---------+-----------
1 | 0 | f
(1 row)
select * FROM all_sequences WHERE sequence_name=UPPER('serial');
sequence_owner | sequence_name | min_value | max_value | increment_by | cycle_flag | order_flag | cache_size | last_number | start_value
----------------+---------------+-----------+---------------------+--------------+------------+------------+------------+-------------+-------------
system | SERIAL | 1 | 9223372036854775807 | 1 | f | t | 1 | | 1
2.1.3.3. 函数 sys_guid()
V8.6 默认输出为name类型,如希望输出为bytea类型,需在配置文件中修改guid_default_return_type='bytea'并重启数据库,再通过以下命令实现:
select alter_sys_guid();
如希望输出为name类型,需在配置文件中修改guid_default_return_type='name'并重启数据库,再通过以下命令实现:
select alter_sys_guid();
2.1.3.4. 函数 get_byte(bit, int)
V8.6 不支持get_byte(bit,int)函数
V8.3:
test=# SELECT GET_BYTE(X'164da53ef', 4);
GET_BYTE
----------
239
(1 row)
V8.6 :
test=# SELECT GET_BYTE(X'164da53ef', 4) ;
ERROR: function get_byte(bit, integer) does not exist
LINE 1: SELECT GET_BYTE(X'164da53ef', 4) ;
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
2.1.3.5. 操作符
相比 V8.3, V8.6 完善了自定义操作符的处理逻辑。对于R3中所限制的自定义操作符中最后一个字符是‘+’或者‘-’的,如果前面包含‘~! @ ^ & ` %’这些字符中的任一字符,则不可以创建自定义操作符。比如在 V8.3中,创建自定义操作符‘~+’,将会报错,而 V8.6 中允许创建, V8.6 中禁用的自定义操作符有10个,包括:"!=+", "!=-", "^=+", "^=-", "||+", "||-", "^+", "^-", " |+", " |-"。这样也是为了尽可能保证处理逻辑与原型一致的基础上,还可以兼容Oracle的操作符。基于上面的处理逻辑,在 V8.3中,比如遇到"%-"这类操作符,实际上是会被当做两个(甚至多个)独立的操作符‘%’和‘-’,而 V8.6 中会认为这是用户自定义的操作符,会当做一个整体。所以在 V8.6 中如果想要将‘%-’操作符当做两个独立的操作符,需要修改应用,在操作符中间插入空格。这一点影响了应用兼容性。
2.1.4. PL/SQL语言
对于大多数常用的 Oracle PL/SQL语句,KingbaseES均提供了支持。该措施使得Oracle应用程序移植到KingbaseES系统时,通常只需很少的代码变动。
下面给出KingbaseES中支持的Oracle PL/SQL语句。此外,若未做特殊说明,本节示例的代码在KingbaseES和Oracle上均可运行。
2.1.4.1. Internal关键字
区别:
V8.3函数创建带internal关键字;REVOKE EXECUTE ON INTERNAL FUNCTION
V8.6 函数创建不支持internal关键字;REVOKE语句中也不支持INTERNAL关键字
升级改写方案:
删除internal关键字
例2-1
V8.3:
CREATE OR REPLACE internal function pr1() return int AS $$
BEGIN
return 1;
END;
$$language plsql;
REVOKE EXECUTE ON INTERNAL FUNCTION
V8.6 :
CREATE OR REPLACE function pr1() return int AS $$
BEGIN
return 1;
END;
$$language plsql;
REVOKE EXECUTE ON FUNCTION
2.1.4.2. forall
区别:
V8.3支持forall语法及其功能
V8.6 不支持forall语法和功能
升级改写方案:
FORALL基本用法:
迭代器为lower..higher
迭代器为indices of collection [between lower and higher]
迭代器为values of index_collection
改写规则:
公有规则:
forall语句中有'save exception'的,忽略掉'save exception';
将'forall'改为'for';迭代器语句结束加上'loop'; dml语句结尾加上'end loop;';
三类的各自的规则:
1类:不做其他额外改动;
2类:
首先,
如果没有between..and
将迭代器变为 collection.first..collection.last;
否则将迭代器变为 lower..higher;
然后,将dml语句包裹在一个if语句中,条件是索引j有效;
3类:将迭代器变为 index_collection.first..index_collection.last;
将dml语句包括在一个if语句中,条件是keys(j)有效;
将dml语句内通过索引引用集合depts(j)改为depts(keys(j));
仍然存在的问题:
异常SQL%BULK_EXPCETIONS相关的,不支持
关联数组用于indices/values of 表现与oracle不一致
oracle的关联数组有exists方法,我们没有,改写失效
--------------------------------------------------------------------
--1 lower..higher
DECLARE
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList(22,44,66);
BEGIN
FORALL j IN depts.FIRST..depts.LAST
DELETE FROM emp_temp WHERE department_id = depts(j);
END;
/
--1 改写
DECLARE
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList(22,44,66);
BEGIN
FOR j IN depts.FIRST..depts.LAST
loop
DELETE FROM emp_temp WHERE department_id = depts(j);
end loop;
END;
/
--2a indices of collection between .. and ..
DECLARE
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList(11, 22, 44, 66);
BEGIN
FORALL j IN indices of depts between depts.first + 1 and depts.last
DELETE FROM emp_temp WHERE department_id = depts(j);
END;
/
--2a 改写
DECLARE
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList(11, 22, 44, 66);
BEGIN
FOR j IN depts.first+1..depts.last
loop
--dbms_output.put_line(j);
if depts.exists(j) then
DELETE FROM emp_temp WHERE department_id = depts(j);
end if;
end loop;
END;
/
--2b indices of collection
DECLARE
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList(22, 44, 66);
BEGIN
FORALL j IN indices of depts
DELETE FROM emp_temp WHERE department_id = depts(j);
END;
/
--2b 改写
DECLARE
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList(22, 44, 66);
BEGIN
FOR j IN depts.first..depts.last
loop
--dbms_output.put_line(j);
if depts.exists(j) then
DELETE FROM emp_temp WHERE department_id = depts(j);
end if;
end loop;
END;
/
--3 values of
DECLARE
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList(22,44,66);
TYPE NumListInt IS TABLE OF int;
keys NumListInt := NumListInt(1,2,3);
BEGIN
FORALL j IN values of keys
DELETE FROM emp_temp WHERE department_id = depts(j);
END;
/
--3 改写
DECLARE
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList(22,44,66);
TYPE NumListInt IS TABLE OF int;
keys NumListInt := NumListInt(1,2,3);
BEGIN
FOR j IN keys.first..keys.last
loop
--dbms_output.put_line(j);
if keys.exists(j) then
DELETE FROM emp_temp WHERE department_id = depts(keys(j));
end if;
end loop;
END;
/
------------------------------------------------------------------ end
------------如果要测试,先执行以下
CREATE TABLE employees(
employee_id NUMBER(6),
first_name VARCHAR2(20),
last_name VARCHAR2(25),
department_id NUMBER(4),
salary NUMBER(6)
);
INSERT INTO employees VALUES (1,'zhang','san',11,10);
INSERT INTO employees VALUES (2,'li','si',22,20);
INSERT INTO employees VALUES (3,'liu','xiang',33,30);
INSERT INTO employees VALUES (4,'sun','yang',44,40);
INSERT INTO employees VALUES (5,'ye','shiwen',55,50);
INSERT INTO employees VALUES (6,'chen','yibing',66,60);
--
INSERT INTO emp_temp VALUES (2,'li','si',22,20);
INSERT INTO emp_temp VALUES (4,'sun','yang',44,40);
INSERT INTO emp_temp VALUES (6,'chen','yibing',66,60);
SELECT * FROM employees;
CREATE TABLE emp_temp AS
SELECT * FROM employees
ORDER BY employee_id, department_id;
SELECT * from emp_temp;
drop table emp_temp;
drop table employees;
----------------------------------------------------------------
--存在的问题
--关联数组的情况, 不一致
--2
DECLARE
TYPE NumList IS TABLE OF pls_integer index by pls_integer;
depts NumList;
BEGIN
depts(1) := 1;
depts(3) := 2;
depts(5) := 3;
FORALL j IN indices of depts between depts.first and depts.last
DELETE FROM emp_temp WHERE department_id = depts(j);
END;
/
--3 values of
DECLARE
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList(22,44,66);
TYPE NumListInt IS TABLE OF pls_integer index by pls_integer;
keys NumListInt;
BEGIN
keys(1) := 1;
keys(3) := 2;
keys(5) := 3;
FORALL j IN values of keys
DELETE FROM emp_temp WHERE department_id = depts(j);
END;
/
------------------
CREATE OR REPLACE PACKAGE BODY "CDS"."CDS_PKG_COMMON_FUNC" IS v_g_Debug BOOLEAN := FALSE;
PROCEDURE CDS_P_DEBUG_SET(in_debug IN BOOLEAN) IS BEGIN v_g_Debug := in_debug; END CDS_P_DEBUG_SET;
FUNCTION CDS_F_SPLIT_DATA (source_data IN VARCHAR2, delimiter_data IN VARCHAR2) RETURN cds_t_str_split IS
j INT := 0;
i INT := 1;
source_data_len INT := 0;
delimiter_data_len INT := 0;
str VARCHAR2(4000);
str_split cds_t_str_split := cds_t_str_split();
BEGIN
source_data_len := LENGTH(source_data);
delimiter_data_len := LENGTH(delimiter_data);
WHILE j < source_data_len LOOP j := INSTR(source_data, delimiter_data, i);
IF j = 0 THEN
j := source_data_len;
str := SUBSTR(source_data, i);
str_split.EXTEND;
str_split(str_split.COUNT) := trim(str);
IF i >= source_data_len THEN
EXIT;
END IF;
ELSE
str := SUBSTR(source_data, i, j - i);
if str is not null then str_split.EXTEND;
str_split(str_split.COUNT) := trim(str);
end if;
i := j + delimiter_data_len;
END IF;
END LOOP;
RETURN str_split; END;
END CDS_PKG_COMMON_FUNC;
---
declare
TYPE t_student_var IS TABLE OF VARCHAR2(100);
v_tbl_name t_student_var := t_student_var();--初始化
BEGIN
raise notice 'v_tbl_name: %', v_tbl_name;
v_tbl_name.extend;--扩展空间
select x into v_tbl_name(1) from test1 where id = 1;
v_tbl_name.extend;--扩展空间
select x into v_tbl_name(2) from test1 where id = 2;
v_tbl_name.extend;--扩展空间
select x into v_tbl_name(3) from test1 where id = 3;
dbms_output.put_line('v_tbl_name(1): %'|| v_tbl_name(1));
dbms_output.put_line('v_tbl_name(2): %', v_tbl_name(2);
dbms_output.put_line('v_tbl_name(3): %', v_tbl_name(3);
raise notice 'v_tbl_name: %', v_tbl_name;
end;
CREATE OR REPLACE PACKAGE CDS_PKG_COMMON_FUNC AS
FUNCTION CDS_F_SPLIT_DATA(source_data IN VARCHAR2, delimiter_data IN VARCHAR2) RETURN cds_t_str_split;
END CDS_PKG_COMMON_FUNC;
CREATE OR REPLACE PACKAGE BODY CDS_PKG_COMMON_FUNC IS
v_g_Debug BOOLEAN := FALSE;
PROCEDURE CDS_P_DEBUG_SET(in_debug IN BOOLEAN) IS
BEGIN
v_g_Debug := in_debug;
END CDS_P_DEBUG_SET;
FUNCTION CDS_F_SPLIT_DATA(source_data IN VARCHAR2, delimiter_data IN VARCHAR2)
RETURN cds_t_str_split IS
j INT := 0;
i INT := 1;
source_data_len INT := 0;
delimiter_data_len INT := 0;
str VARCHAR2(4000);
str_split cds_t_str_split := cds_t_str_split();
BEGIN
source_data_len := LENGTH(source_data);
delimiter_data_len := LENGTH(delimiter_data);
WHILE j < source_data_len LOOP
j := INSTR(source_data, delimiter_data, i);
IF j = 0 THEN
j := source_data_len;
str := SUBSTR(source_data, i);
str_split.EXTEND;
str_split(str_split.COUNT) := trim(str);
IF i >= source_data_len THEN
EXIT;
END IF;
ELSE
str := SUBSTR(source_data, i, j - i);
if str is not null then
str_split.EXTEND;
str_split(str_split.COUNT) := trim(str);
end if;
i := j + delimiter_data_len;
END IF;
END LOOP;
RETURN str_split;
END;
END CDS_PKG_COMMON_FUNC;
2.1.4.3. oracle语法指定语言
区别:
V8.3创建oracle语法函数时能够指定LANGUAGE xxx语言
V8.6 创建oracle语法函数时不支持指定LANGUAGE xxx语言
升级改写方案:
删除LANGUAGE xxx语言子句
V8.3:
CREATE OR REPLACE function pr1 return int
LANGUAGE plsql
AS
BEGIN
return 1;
END;
V8.6 :
CREATE OR REPLACE function pr1 return int
AS
BEGIN
return 1;
END;
2.1.4.4. 嵌套表定义CHAR类型省略长度
区别:
V8.3嵌套表定义CHAR类型省略长度,赋值超长, V8.3截断
V8.6 嵌套表定义CHAR类型省略长度,赋值超长,|version|报错
升级改写方案:
嵌套表定义CHAR类型省略长度,赋值正确
V8.3:
DO $$DECLARE
TYPE type_name10 IS TABLE OF CHAR NOT NULL;
nttypeelement10 type_name10:=type_name10('AAAA');
BEGIN
RAISE NOTICE 'nttypeelement10(1)=%',nttypeelement10(1);
EXCEPTION WHEN VALUE_ERROR THEN
RAISE NOTICE 'VALUE_ERROR';
END$$;
V8.6 :
DO $$DECLARE
TYPE type_name10 IS TABLE OF CHAR NOT NULL;
nttypeelement10 type_name10:=type_name10('A');
BEGIN
RAISE NOTICE 'nttypeelement10(1)=%',nttypeelement10(1);
EXCEPTION WHEN VALUE_ERROR THEN
RAISE NOTICE 'VALUE_ERROR';
END$$;
2.1.4.5. CREATE PACKAGE
区别:
V8.3CREATE PACKAGE 中文名的包 AS a int END;创建成功
V8.6 CREATE PACKAGE 中文名的包 AS a int END;创建失败
升级改写方案:
由于R6在包语法解析时更加严格,因此需要在a int后加’;’
V8.3:
CREATE PACKAGE 中文名的包 AS a int END;
V8.6 :
CREATE PACKAGE 中文名的包 AS a int; END;
2.1.5. 版本新增能力和变更能力明细
V8.6 和 V8.3 版本新增以及变更能力明细如下表所示:
2.1.6. 支持的客户端编程接口兼容性
V8.6 和 V8.3 客户端编程接口兼容性如下表所示:




