1211Bug with integer literals in PLSQL

[20171211]Bug with integer literals in PLSQL.txt

--//链接 http://orasql.org/2017/12/09/bug-with-integer-literals-in-plsql/
--//重复测试:
--//作者的观点好像是如果在PL/SQL中定义的整形变量并引用太多,最后出现异常.

1.环境:
SCOTT@book> @ &r/ver1
PORT_STRING                    VERSION        BANNER
------------------------------ -------------- --------------------------------------------------------------------------------
x86_64/Linux 2.4.xx            11.2.0.4.0     Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

We can reproduce this bug even with an anonymous pl/sql block. The following test case uses 32768 integer literals from
1000001 to 1032768 and prints 5 other integers:

set head off
spool /tmp/n.txt
select  'n:='||to_char(rownum+1000000)||';' from dual connect by level <=32768;
spool off

--//将/tmp/n.txt 内容修改如下:

$ cat /tmp/n.txt
declare n number;
begin
  n:=1000001; -- this part
  n:=1000002; -- creates
  n:=1000003; -- 32768
   ...        -- integer
  n:=1032768; -- literals
    dbms_output.put_line('100000='||100000); -- it should print: 100000=100000
    dbms_output.put_line('32766 ='||32766);
    dbms_output.put_line('32767 ='||32767);   
    dbms_output.put_line('32768 ='||32768);
    dbms_output.put_line('32769 ='||32769);
end;
/

SCOTT@book> set serverout on
SCOTT@book> @ /tmp/n.txt
100000=1000001
32766 =32766
32767 =32767
32768 =1000002
32769 =1000003

PL/SQL procedure successfully completed.

This test case well demonstrates wrong results:

* instead of 100000 we get 10000001, which is the value from first line after "begin", ie 1st integer literal in the
  code,
* for 32766 and 32767 oracle returns right values
* instead of 32768 (==32767+1) it returns 10000002, which is the integer from 2nd line, ie 2nd integer literal in the
  code,
* instead of 32769 (==32767+2) it returns 10000003, which is the integer from 3rd line, ie 3rd integer literal in the
  code

After several tests I can make a conclusion:

It doesn't matter what plsql_optimize_level or plsql_code_type you set, was debug enabled or not, the behaviour is the
same.

It seems that this is a kind of PL/SQL optimization: during parsing, oracle leaves integer literal in place if its value
is in range -32768..32767 (16bit signed int), but if its value is out of this range, oracle adds this value into array
of integers' constants and replaces the value with the index of this element in this array. But because of index value
overflow in cases when a count of such integer literals becomes larger than 32768, instead of Nth element of this array,
oracle returns Mth element, where M is mod(N,32767).

So we can describe this behaviour using first test case:

declare n number;
begin
  n:=1000001; -- this part
  n:=1000002; -- creates
  n:=1000003; -- 32768
   ...        -- integer
  n:=1032768; -- literals
    dbms_output.put_line('100000='||100000); -- it should print 100000, ie 32768th element of array, but prints 10000001
                                             -- where 10000001 is the 1st element of array (1==mod(32768,32767))
    dbms_output.put_line('32766 ='||32766);  -- these 2 lines print right values,
    dbms_output.put_line('32767 ='||32767);  -- because their values are in the range of -32768..32767
    dbms_output.put_line('32768 ='||32768);  -- this line contains 32769th element and prints 2nd element of array (2==mod(32769,32767))
    dbms_output.put_line('32769 ='||32769);  -- this line contains 32770th element and prints 3nd element of array (3==mod(32770,32767))
end;

The following query can help you to find objects which can potentially have this problem:

select
  s.owner,s.name,s.type
,sum(regexp_count(text,'(\W|^)3\d{4,}([^.0-9]|$)')) nums_count -- this regexp counts integer literals >= 30000
from dba_source s
where
    owner='&owner'
and type in ('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY')
group by s.owner,s.name,s.type
having sum(regexp_count(text,'(\W|^)3\d{4,}([^.0-9]|$)'))>32767 -- filter only objects which have >=32767 integer literal

Workaround:

You may noticed that I wrote about INTEGER literals only, so the easiest workaround is to make them FLOAT – just add
"." to the end of each literal:

declare n number;
begin
  n:=1000001.;
  n:=1000002.;
  n:=1000003.;
   ...      
  n:=1032768.;
    dbms_output.put_line('100000='||100000.);
    dbms_output.put_line('32766 ='||32766.);
    dbms_output.put_line('32767 ='||32767.);   
    dbms_output.put_line('32768 ='||32768.);
    dbms_output.put_line('32769 ='||32769.);
end;
/

SCOTT@book> @ /tmp/n.txt
100000=100000
32766 =32766
32767 =32767
32768 =32768
32769 =32769
PL/SQL procedure successfully completed.

--//我的测试写成这样也ok.作者的输出dbms_output.put_line('32769 ='||32769.);后面带点.;
declare n number;
begin
  n:=1000001.;
  n:=1000002.;
  n:=1000003.;
   ...      
  n:=1032768.;
    dbms_output.put_line('100000='||100000);
    dbms_output.put_line('32766 ='||32766);
    dbms_output.put_line('32767 ='||32767);   
    dbms_output.put_line('32768 ='||32768);
    dbms_output.put_line('32769 ='||32769);
end;
/
SCOTT@book> @ /tmp/n.txt
100000=100000
32766 =32766
32767 =32767
32768 =32768
32769 =32769
PL/SQL procedure successfully completed.

--//如果我修改如下,注解2行:
declare n number;
begin
n:=1000001;
n:=1000002;
n:=1000003;
n:=1000004;
--n:=1000005;
--n:=1000006;
n:=1000007;
....
n:=1032767;
n:=1032768;
    dbms_output.put_line('100000='||100000); -- it should print: 100000=100000
    dbms_output.put_line('32766 ='||32766);
    dbms_output.put_line('32767 ='||32767);
    dbms_output.put_line('32768 ='||32768);
    dbms_output.put_line('32769 ='||32769);
end;
/

SCOTT@book> @ /tmp/n.txt
100000=100000
32766 =32766
32767 =32767
32768 =0
32769 =1000001
PL/SQL procedure successfully completed.

--//如果我修改如下,注解3行:
SCOTT@book> @ /tmp/n.txt
100000=100000
32766 =32766
32767 =32767
32768 =32768
32769 =0
PL/SQL procedure successfully completed.

时间: 2024-09-20 20:44:28

1211Bug with integer literals in PLSQL的相关文章

“Clang” CFE Internals Manual---中文版---&quot;Clang&quot;C语言前端内部手册

原文地址:http://clang.llvm.org/docs/InternalsManual.html 译者:史宁宁(snsn1984)                                                                                           "Clang"C语言前端内部手册 简介 这个文档描述了比较重要的API中的一部分API,还表述了Clang C语言前端中的一些内部设计想法.这个文档的目的是既把握住高层次

Struts Validator验证器使用指南和使用方法

Struts Validator验证器使用指南 (根据Struts Validator Guide) 作者: David Winterfeldt大卫  James Turner詹姆斯          Rob Leland罗伯特 翻译:   侯思超 验证器: 从0.5版,验证器在一些form中就已经实现了,他最初包含在开发人员包中,后来核心代码挪到Jakarta Commons包中和Struts特别扩展中作为 Struts 1.1的一部分.许多开发者为方便一直使用struts验证器,这篇文档首先

SQL和PLSQL

 过滤和排序.txt SQL>--where SQL>--查询工资大于2500的员工信息 SQL>select *   2  formemp   3 where sal>2500; form emp * 第 2 行出现错误: ORA-00923:未找到要求的 FROM 关键字 SQL>ed 已写入 file afiedt.buf     1 select *   2  formemp   3* where sal>2500 SQL> / form emp *

Oracle使用PLSQL的中文乱码显示全是问号问题

问题 这两天刚将PLSQL与Oracle配置好,可是在PLSQL中插入数据时,出现一个问题,PLSQL中的表里无法显示中文,中文无法保存.无法输出,中文在表中显示问号,如图: 原因 经过一番查证,发现问题的源头不是出现在PLSQL上,而是出现在我们的Oracle上,由于我们的Oracle数据库里的字符集不支持中文导致的,既然知道了原因,就好办了,我们就配置我们的Oracle字符集,让他支持中文. 解决办法 我的Oracle是11g版本的,下面以我的为例,为大家讲解下设置的过程. 步骤一 开始-运

plsql developer-如何访问另一台计算机中的oracle数据库

问题描述 如何访问另一台计算机中的oracle数据库 有两台笔记本,一个配置高一些,安装了Oracle数据库,另一台配置低一些,没有安装,该怎么配置可以使用plsql developer工具访问高配置电脑中的数据库呢?求大神解答,详细一点哈!本人有点笨,不胜感激! 解决方案 参考:http://blog.csdn.net/metal1/article/details/41799669http://www.jb51.net/article/48676.htm 解决方案二: 首先oracle数据库得

oracle使用plsql配置instantclient的问题?

问题描述 oracle使用plsql配置instantclient的问题? 网上的一些关于的文章会提到配置环境变量的问题,比如:NLS_lANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBKTNS_ADMIN=F:MyJobOracle11gInstantClientADMINPath=F:MyJobOracle11gInstantClient;_但是有的机子上不配置环境变量也可以使用,机器都是64位的,不知道是什么原因,差别在哪里?是不是服务器和客户端在同一台机子上就不用

Object转Integer类型失败,转Long型可以,方法返回值为1,并没有超过两个类型的范围

问题描述 Object转Integer类型失败,转Long型可以,方法返回值为1,并没有超过两个类型的范围 在网上找的尚硅谷的教学视频 其中有添加权限一段 String hql="select count(*) from Right r where r.rightUrl=?"; //此处不能用integer只能Long Long count=(Long) this.uniqueResult(hql, url); System.out.println(count); 下面是uniqueR

在Oracle下使用PLSQL Developer创建简单的触发器

环境:PLSQL Developer 7.1.5 + Oracle 11.2.0 问题:在Oracle下使用PLSQL Developer创建简单 的触发器. 解决: 步骤一:在PLSQL Developer左边的My Objects展开列表下找到Triggers, 点击右键,选择新建(New): 步骤二:在弹出的对话框里填 入Trigger的名字(Name).触发时间(Fires).事件(Event).表或视图(Table or View),填完后选择 OK(确定):

MySQL中int和integer的区别

环境: MySQLSever 5.1 问题:MySQL 中int和integer什么区别 答案:没有区别 分析: · INT[(M)][UNSIGNED] [ZEROFILL] 普通大小的整数.带符号的范围是- 2147483648到2147483647.无符号的范围是0到4294967295. ·INTEGER[(M)] [UNSIGNED] [ZEROFILL] 这是INT的同义词.使用两个名字只是为了简化和方便,没有本质上的区别. 查看本栏目更多精彩内容:http://www.biance