目 录CONTENT

文章目录

【YashanDB】ORACLE-SQL/PLSQL兼容性测试记录

DarkAthena
2025-11-25 / 0 评论 / 0 点赞 / 10 阅读 / 0 字

【YashanDB】ORACLE-SQL/PLSQL兼容性测试记录

本文内容比较零散,正常兼容的就不提了,仅记录测试过程中一些需要注意的点,不代表产品本身整体的好坏。

测试环境 :

  • 源端 :ORACLE 11g ,百万行级PLSQL对象的复杂系统
  • 目标端 :YashanDB 23.4.2.104
  • 迁移工具 :YMP 23.4.3.2

要点和问题记录

  1. YMP迁移评估先尝试原SQL创建,如果失败则自动改写,但改写并不是针对失败原因进行改写,可能会把新版本已经支持的语法改写成错误的,因此这个改写规则没有根据目标库版本进行区分,或者是区分不够精确。
  2. YMP迁移的评估是真实执行DDL语句,可以选择在内置库上执行或者目标库上执行
  3. YMP迁移可以在评估后,在评估结果中可以手动修改不兼容的对象,并更新整体评估结果(似乎能处理依赖关系?)
  4. 内核23.4.2版本新增支持subtype,之前版本不支持,而且YMP在之前内核版本上也会进行错误改写,改写后创建也是报错的
  5. sqlcode和ORACLE不一致,对于将内置sqlcode绑定到自定义异常名称的场景,无法兼容,需要手动修改绑定的内置sqlcode为Yashan对应的sqlcode,但目前Yashan并未提供sqlcode对应关系。像下面这个用例会报错,因为要求第二个参数为正数
PRAGMA EXCEPTION_INIT(lock_error,-54);  /* oracle -54 可能对应yashan 2024 ? */
  1. 23.4.2版本,如果代码中有根据sqlcode的值进行if分支处理,这部分代码需要手动重写
if sqlcode>-20000 
  then ***;
else ****;
end if;
  1. package里支持的子对象个数有限,最多1024个,超过会报错

YAS-05275
Message: too many package children
Action:自定义高级包内变量、类型、子过程体等成员个数不能超过最大值1024,请减少自定义高级包中定义的成员的个数

  1. 建表语句不支持 ROWDEPENDENCIES,且YMP不会自动改写
  2. 不支持以下授权 ON COMMIT REFRESH/DEBUG/QUERY REWRITE,且YMP不会自动改写
  3. with语句里不能使用pivot语法,“PIVOT clause is not allowed in CTE”

截止到这里,暂时无法通过全量迁移来判断了,需内核去掉 package 里只能有1024个子对象的限制才能继续。相关需求已提交给Yashan,但由于本次属于个人自行测试,没有正式项目,因此排期未定。
-----2025年9月3日


后面因为考ycp,用的是23.2版本,顺便测了下我别的ORACLE环境。所以后面混合两种版本的不兼容记录,部分特性是老版本不支持,新版本支持了。如果没有特别说明版本,均指23.4.2.104版本

  1. 在 v23.4.1 之前 不支持包内函数重载
create package as_zip is
  function get_file_list
    ( p_dir varchar2
    , p_zip_file varchar2
    , p_encoding varchar2 := null
    )
  return varchar2;
  function get_file_list
    ( p_zipped_blob blob
    , p_encoding varchar2 := null
    )
  return varchar2;
end;
/

[8:12]YAS-05290 duplicate item "GET_FILE_LIST" in package
  1. 不支持DBMS_ID数据类型 (这个用得少)
FORMAT_TYPE_CSV   CONSTANT DBMS_ID := 'CSV';
  1. RAW类型最大长度在v23.4.1 之前只有8000,之后增加到65534
type convert_any_charset_type is RECORD(
    original_str varchar2(4000),
    original_HEX RAW(32767),
    to_str       varchar2(4000),
    to_hex       raw(32767),
    from_charset varchar2(50),
    to_charset   varchar2(50),
    sql_text     varchar2(200));
  1. 没有内置数据类型 ora_mining_varchar2_nt

  2. 在v23.4.1之前不支持索引表类型

create package test_index_table_type is
 TYPE REC_COL_VALUE
   IS
      RECORD(COL_ID             NUMBER(3), COL_VALUE      VARCHAR2(2400));

  TYPE TAB_COL_VALUE
   IS
      table of REC_COL_VALUE index by binary_integer;
end;
/
[8:30]YAS-04115 ";" expected but missing
  1. 不支持UTL_SMTP

  2. 不支持多态表

CREATE OR REPLACE EDITIONABLE PACKAGE "POLY_PKG" AS

  FUNCTION my_ptf(tab IN TABLE)
  RETURN TABLE PIPELINED
    ROW POLYMORPHIC USING poly_pkg;

  FUNCTION describe (tab IN OUT DBMS_TF.table_t)
    RETURN DBMS_TF.describe_t;

  PROCEDURE fetch_rows;
END poly_pkg;
/
  1. varchar类型最大长度在v23.4.1 之前只有8000,之后增加到65534
TYPE t_string_table IS TABLE OF VARCHAR2(32767);

YAS-04204,number of String length must be between 1 and 32000
  1. v23.4.4开始支持 utl_http

  2. UTL_FILE和DBMS_LOB支持不完全(常量、数据类型、函数)

  3. v23.2的文档里说支持pipelined,但实测不对

CREATE OR REPLACE EDITIONABLE PACKAGE  "FUN_GETDATE" is
  type tb is table of data_view_20211224%rowtype;
  function getdate(fun_date varchar2) return tb
    pipelined;
end fun_getdate;
/
CREATE OR REPLACE EDITIONABLE PACKAGE BODY  "FUN_GETDATE" is
  function getdate(fun_date varchar2) return tb
    pipelined is
  begin
    for rec in (select *
                  from data_view_20211224 a
                 where a.date_type = fun_date) loop
      PIPE ROW(rec);
    end loop;
    RETURN;
  end;
end fun_getdate;
/

YAS-04209,unexpected word pipelined
  1. v23.2不支持子函数子程序

  2. 很多权限不支持

GRANT DEBUG CONNECT SESSION TO "DB_DEMO";
GRANT CREATE EXTERNAL JOB TO "DB_DEMO";
GRANT CREATE ANY JOB TO "DB_DEMO";
GRANT DEBUG ANY PROCEDURE TO "DB_DEMO";
GRANT READ ON "SYS"."DB_BACKUP_DIR" TO "DB_DEMO";
GRANT ON COMMIT REFRESH ON "DB_DEMO1"."T_TB" TO "DB_DEMO";
GRANT WRITE ON "SYS"."DB_BACKUP_DIR" TO "DB_DEMO";
GRANT EXECUTE ON "SYS"."DB_BACKUP_DIR" TO "DB_DEMO";
GRANT QUERY REWRITE ON "DB_DEMO1"."T_MAINC" TO "DB_DEMO";
GRANT EXECUTE ON "SYS"."DBMS_LOCK" TO "DB_DEMO";
GRANT CREATE EXTERNAL JOB TO "DB_DEMO";
  1. v23.2不支持ORACLE语法的自定义聚合函数

  2. v23.2个人版不支持子分区

  3. v23.2的current_timestamp不支持指定非0精度

create table xxx(
	"C_DATE_CURRENT_TIMESTAMP3" DATE DEFAULT current_timestamp(3));

[2:43]YAS-04323 arguments count must be 0
  1. 表字段不支持使用自定义类型
CREATE OR REPLACE TYPE "AAA"."CUST_ADDRESS_TYP_NEW"                                          AS OBJECT
    ( street_address     VARCHAR2(40)
    , postal_code        VARCHAR2(10)
    , city               VARCHAR2(30)
    , state_province     VARCHAR2(10)
    , country_id         CHAR(2)
    );
/
CREATE TABLE "AAA"."CUSTOMER_ADDRESSES" 
   (	"ADD_ID" NUMBER, 
	"ADDRESS" REF "AAA"."CUST_ADDRESS_TYP_NEW" , 
	SCOPE FOR ("ADDRESS") IS "AAA"."ADDRESS_TABLE" 
   ) SEGMENT CREATION IMMEDIATE 
  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 
 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS" ;
  
YAS-04209,unexpected word "AAA"
  1. 表行不支持使用自定义类型
CREATE OR REPLACE TYPE "AAA"."CUST_ADDRESS_TYP_NEW"                                          AS OBJECT
    ( street_address     VARCHAR2(40)
    , postal_code        VARCHAR2(10)
    , city               VARCHAR2(30)
    , state_province     VARCHAR2(10)
    , country_id         CHAR(2)
    );
/
CREATE TABLE "AAA"."ADDRESS_TABLE" OF "AAA"."CUST_ADDRESS_TYP_NEW" 
 OIDINDEX  ( PCTFREE 10 INITRANS 2 MAXTRANS 255 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS" ) 
 SEGMENT CREATION IMMEDIATE 
  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 
 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS" ;
  1. v23.2不支持对同一个列创建两个索引 ,v23.4支持
create table TAB1
(
  id   NUMBER,
  col1 VARCHAR2(10)
);
-- Create/Recreate indexes 
create unique index IND_TAB1_COL1 on TAB1 (COL1);
create unique index IND_TAB1_COL1_DESC on TAB1 (COL1 DESC);

YAS-02043,columns have been indexed
  1. YAS-04225,invalid word COMPUTE
CREATE UNIQUE INDEX "AAA"."SYS_C0012318" ON "AAA"."TABLE_O" ("ID") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS" 
  PARALLEL ;
  1. 表字段约束不支持格式校验
ALTER TABLE "AAA"."J_PURCHASEORDER" ADD CONSTRAINT "ENSURE_JSON" CHECK (po_document IS JSON) ENABLE ;

YAS-04115,"NULL" expected but missing
  1. 不支持TO_BLOB/TO_CLOB函数

  2. v23.2不支持DBMS_SQL

  3. 存储过程里定义游标报错?

CREATE OR REPLACE EDITIONABLE FUNCTION  "SQL_TO_MARKDOWN" (
                       l_sql           IN varchar2,
                    --   l_text          OUT clob,
                       l_head          IN varchar2 DEFAULT NULL) RETURN clob 
                       authid current_user IS
                       
    cur_   SYS_REFCURSOR;
    l_null number;
    l_ct   number;
    l_text CLOB;
    CURSOR get_columns IS
      SELECT t2.column_value.getrootelement() name,
             EXTRACTVALUE(t2.column_value, 'node()') VALUE
        FROM (SELECT * FROM TABLE(XMLSEQUENCE(cur_))) t1,
             TABLE(XMLSEQUENCE(EXTRACT(t1.column_value, '/ROW/node()'))) t2;
    pragma autonomous_transaction; 
   
  BEGIN
  return null;
 end;
/ 
YAS-04115 "Extract Format Type" expected but missing
  1. 函数入参类型不支持指定"CHARACTER SET ANY_CS"
CREATE OR REPLACE EDITIONABLE FUNCTION "CSLX"."URLDECODE" (url         IN VARCHAR2 CHARACTER SET ANY_CS,
                                     url_charset IN VARCHAR2 DEFAULT 'AL32UTF8')
  RETURN VARCHAR2 IS
  L_RETURN VARCHAR2(2000);
BEGIN
  select CONVERT(UTL_RAW.cast_to_varchar2(LISTAGG(CASE
                                                    WHEN LENGTH(A) = 3 THEN
                                                     HEXTORAW(REPLACE(A, '%'))
                                                    ELSE
                                                     UTL_RAW.cast_to_raw(A)
                                                  END) within group (order by 1)
                                                  ),
                 url_charset)
    INTO L_RETURN
    from (select REGEXP_SUBSTR(url, '(%.{2}|.)', 1, LEVEL) A
            from DUAL
          CONNECT BY LENGTH(REGEXP_SUBSTR(url, '(%.{2}|.)', 1, LEVEL)) > 0) A;
  RETURN L_RETURN;
END;
/
  1. DBMS_SCHEDULER支持不完整
CREATE OR REPLACE EDITIONABLE PROCEDURE "CSLX"."RUN_LINUX_SH_TEXT" (i_script varchar2) is
  sched_job_name varchar2(30);
begin
  sched_job_name := dbms_scheduler.generate_job_name(prefix => 'SCRIPT_');
  DBMS_SCHEDULER.create_job(job_name            => sched_job_name,
                            job_type            => 'EXECUTABLE',
                            job_action          => '/bin/sh',
                            number_of_arguments => 2,
                            enabled             => false,
                            auto_drop           => true);
  DBMS_SCHEDULER.set_job_argument_value(sched_job_name, 1, '-c');
  DBMS_SCHEDULER.set_job_argument_value(sched_job_name, 2, i_script);
  DBMS_SCHEDULER.run_job(job_name => sched_job_name);
end;
/

YAS-04253 PL/SQL compiling errors:
[4:21] YAS-04243 invalid identifier "DBMS_SCHEDULER"."GENERATE_JOB_NAME"
[11:3] YAS-05398 invalid procedure "DBMS_SCHEDULER"."SET_JOB_ARGUMENT_VALUE"
[12:3] YAS-05398 invalid procedure "DBMS_SCHEDULER"."SET_JOB_ARGUMENT_VALUE"
  1. 不支持使用q转义字符串
select  q'{1}' from dual;

[1:10]YAS-04115 "FROM" expected but missing
  1. 不支持 OWA_UTIL.who_called_me

  2. v23.4开始支持parallel_enable

  3. v23.2不支持函数的DETERMINISTIC属性

  4. 不支持系统触发器

CREATE OR REPLACE EDITIONABLE TRIGGER "AAA"."UNDROPSYSCONSTRAINT" 
  before alter or create on database

YAS-04115,“INSERT|DELETE|UPDATE” expected but missing

  1. V23.2不支持instead of触发器

  2. dbms_xmlgen支持不完善

CREATE OR REPLACE EDITIONABLE PROCEDURE "SQL_TO_CSV_XSLT" (p_sql       varchar2,
                                 p_filename  varchar2 ,
                                 p_dir       varchar2,
                                 p_DELIMITER varchar2 default ',') as
  l_ctx            dbms_xmlgen.ctxhandle;
  l_num_rows       pls_integer;
  l_xml            xmltype;
  l_transform      xmltype;
  l_xml_stylesheet varchar2(4000);
  --

  l_csv clob;
begin
 l_xml_stylesheet:= q'^<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
  <xsl:apply-templates select="ROWSET/ROW[1]" />
  </xsl:template>
  <xsl:template match="ROW">
  <xsl:apply-templates mode="th" />
  <xsl:apply-templates select="../ROW" mode="td" />
  </xsl:template>
  <xsl:template match="ROW/*" mode="th">
  <xsl:value-of select="local-name()" />
  <xsl:if test="position() != last()">
  <xsl:text>:1</xsl:text>
  </xsl:if>
  </xsl:template>
  <xsl:template match="ROW" mode="td">
  <xsl:text>&#xd;</xsl:text><xsl:text>&#xa;</xsl:text><xsl:apply-templates />
  </xsl:template>
  <xsl:template match="ROW/*">
  <xsl:apply-templates />
  <xsl:if test="position() != last()">
  <xsl:text>:1</xsl:text>
  </xsl:if>
  </xsl:template>
  </xsl:stylesheet>^';
  l_xml_stylesheet := replace(l_xml_stylesheet, ':1', p_DELIMITER);
  l_ctx            := dbms_xmlgen.newcontext(p_sql);
  dbms_xmlgen.setnullhandling(l_ctx, dbms_xmlgen.empty_tag);
  l_xml      := dbms_xmlgen.getxmltype(l_ctx, dbms_xmlgen.none);
  l_num_rows := dbms_xmlgen.getnumrowsprocessed(l_ctx);
  dbms_xmlgen.closecontext(l_ctx);
  if l_num_rows > 0 then
    l_transform := l_xml.transform(xmltype(l_xml_stylesheet));
  end if;
  l_csv:=dbms_xmlgen.convert(l_transform.getclobval(),
                                           dbms_xmlgen.entity_decode);
   dbms_lob.clob2file(cl        => l_csv,
                      flocation => p_dir,
                      fname     => p_filename)    ;
--  dbms_output.put_line(l_csv);

exception
  when others then
    raise;
end;
/

YAS-04253 PL/SQL compiling errors:
[5:20] YAS-04229 invalid datatype
[14:22] YAS-04251 symbol ; expected
[39:3] YAS-04243 invalid identifier "L_CTX"
[40:3] YAS-05398 invalid procedure "DBMS_XMLGEN"."SETNULLHANDLING"
[41:17] YAS-04243 invalid identifier "DBMS_XMLGEN"."GETXMLTYPE"
[42:17] YAS-04243 invalid identifier "DBMS_XMLGEN"."GETNUMROWSPROCESSED"
[43:3] YAS-05398 invalid procedure "DBMS_XMLGEN"."CLOSECONTEXT"
[45:20] YAS-04243 invalid identifier "L_XML"."TRANSFORM"
[47:10] YAS-04243 invalid identifier "DBMS_XMLGEN"."CONVERT"
[49:4] YAS-05398 invalid procedure "DBMS_LOB"."CLOB2FILE"

  1. 不支持"DBMS_UTILITY".“FORMAT_ERROR_BACKTRACE”
CREATE OR REPLACE EDITIONABLE PROCEDURE  "TEST_BT_A" (i_err_bt varchar2 default dbms_utility.format_error_backtrace,i_sqlerm varchar2 default sqlerrm) is
begin
  dbms_output.put_line(i_err_bt ||i_sqlerm);
end;
/

[1:55]YAS-04243 invalid identifier "DBMS_UTILITY"."FORMAT_ERROR_BACKTRACE"
  1. 游标不支持for update
CREATE OR REPLACE EDITIONABLE PROCEDURE "TEST81_P" IS
  CURSOR cur_ IS
    SELECT id, processed FROM test81_t WHERE processed = 'N' FOR UPDATE;
BEGIN
  FOR rec IN cur_ LOOP
    UPDATE test81_t2 SET processed = 'Y' WHERE CURRENT OF cur_;
  END LOOP;
END;
/

YAS-04209,unexpected word for update

后记

本文记录的这些问题,除了那个多态表例子是我造的,其他的均是真正来自于有生产环境在用的系统。
YashanDB 将在2025年11月26号发布v23.5版本,届时估计本文中提到的有些问题点可能就已经没问题了,所以我赶在新版本发布前先把这篇发出来,避免被即将来领的大批同质化文章所掩盖。至于后续会不会复测一遍这些问题点,再说吧,有空就测测。

本文看似列出了非常多的不兼容点,但只有真正在国产数据库兼容适配路上战斗过的,才会知道几十甚至几百个不兼容点根本不算什么,其他有些自称ORACLE兼容性99%的国产数据库测下来问题可能不比这个少。所以不要相信宣传材料中对ORACLE兼容性百分比的描述,ORACLE兼容性百分比在没有严格定语的情况下就是个伪指标。

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin
博主关闭了所有页面的评论