PL、SQL构建代码分析工具之构建实用程序
- 格式:pdf
- 大小:140.30 KB
- 文档页数:7
PL/SQL构建代码分析工具之构建实用程序
构建实用程序验证代码质量的内部详情
许多开发人员和开发经理所面对的一个突出问题就是找到改进代码质量的方法。于是我
决定以一种独特的方式来解决这一难题。在随后的几个月中,我将在OTN上发布一系列文
章说明我是如何构建Codecheck的。Codecheck是依赖数据字典中的信息,执行重要任务
(即分析程序包以检测这些程序包是否包含重载多义性)的一个PL/SQL程序包。由于我采
用的是自顶而下的设计方法并运用了许多我在《OraclePL/SQL最佳实战技巧》中推荐的
最佳实战技巧,因此不想只是简单地呈现结果,而更愿意邀请您和我一同演练这一过程。边
条“构建实用程序的步骤”位于本文的结束处。
深入了解这一实用程序要花费一些时间,因为我想借这个机会实际经历一下开发生命周
期中的几个阶段。在以后的几个月中,我将进行以下工作:
确定我希望Codecheck解决的问题并明确需求。
给出验证实用程序正确运行的测试案例。
研究有助于解决问题的相关技术。
给出实用程序的整体设计(结果发现测试要求会对我的设计产生影响)。
逐步细化以采用易于编写、理解和部署的代码块来构建解决方案。
利用utPLSQL单元测试框架自动对实用程序进行复杂的回归测试。
(关于构建Codecheck的每篇文章分别描述哪一部分内容的详细信息,请参见边条
“构建实用程序的步骤”)。
通过对Codecheck及其相关概念的学习,您将了解到OraclePL/SQL最新、最好的一
些特性,如多层集合。此外,我还将提供一套平台,您可以在该平台上构建和添加自己的QA
检查,例如检查参数是否太多或太少、查找所有程序包都要用到的程序,并确保代码符合命
名规则。也许亲身演练的最大好处在于有机会看到实际运用的一些最佳实战技巧,这可能是
学习如何使用这些技巧最简单的方法。
我需要声明一点:我已经完成了Codecheck的一个运行版本。我计划一边写下该实用
程序的构造经过,一边对其进行改进。因此,该系列每一篇文章的下载都将包含一个
Codecheck(codecheck.zip)。请随意下载并立即使用。如果您在调试的过程中遇到什么问
题,或是有一些改进意见,请发送至steven@stevenfeuerstein.com.确定问题:程序包中的重载多义性
为了写出高质量的程序而顾及到方方面面会令人发疯。因此,我打算集中到几个典型的
问题,以免屡屡受挫而不得不放弃创建Codecheck的初衷。编写和成功编译包含不可调用
程序的PL/SQL程序包是极有可能的,而这并没有多大的意义,不是吗?让我们来看看这一
奇怪的情形是怎样出现的。
OraclePL/SQL支持负载,也就是所谓的静态多态性。这就意味着,您能在任何声明段
或程序包中用相同的名称定义两个或多个程序,只要这些程序区别显着(通常是参数列表不
同),编译器能够辨别您想要使用哪个程序。重载对于提高代码的可用性来说是一项有用的
技术。然而,它也会带来一些问题,特别是如果参数列表很长,其中的参数有些有缺省值,
有些没有,则尤其如此。
为了向您证实开发人员的确会面临这一问题,我挑选了几个可能会出错的例子,这些例
子是以下面所示的程序包说明开头的:
CREATEORREPLACEPACKAGEsalespkgISPROCEDUREcalc_total(zone_inIN
VARCHAR2);
PROCEDUREcalc_total(reg_inINVARCHAR2);ENDsalespkg;/
这一部分在编译时不会有任何问题,正如其程序体一样。其中有两段重载程序,都命名
为calc_total.其中之一接收一个地段,例如''ZONE15'‘,然后计算该地段的总销售额。
而第二个程序则接收一个区域,例如''SOUTHWEST'’,然后计算该区域的总销售额。但当
我试图调用其中某个程序,却出现一个错误。
SQL>execsalespkg.calc_total(‘'ZONE15'’)
BEGINsalespkg.calc_total(‘'ZONE15'’);END;*ERRORatline1:ORA-06550:
line1,column7:PLS-00307:toomanydeclarationsof''CALC_TOTAL''matchthis
call
该错误消息明确地指出了这一问题:“ToomanydeclarationsofCALC_TOTALmatch
thiscall.”(有多个CALC_TOTAL的声明与该调用相匹配)。您可以看到,计算机并不是
十分的聪明。您我都可以看出''ZONE15''是一个地段;难道PL/SQL编译器就不能识别
出这是“地段”的calc_total吗(即带有zone_in参数的重载)?不幸的是,编译器并不
是这样工作的。‘'ZONE15''是字符串的字面意义,编译器无法分析。编译器无法知道应
该使用哪段程序,然后就甩手不管了。
我们该如何解决这一问题呢?在这种特定情况下,我可以通过使用指定的参数值来消除
多义性:
BEGINsalespkg.calc_total(zone_in=>''ZONE15'’);END;在这个实例,我想要告诉编译器使用特定的参数(zone_in)。因为这两个重载程序中,
只有一个程序的参数名与之相符,这样编译器就知道该调用这两个中的哪一个了。尽管如此,
不得不使用参数名引用来调用一个过程或一个函数还是令人难以接受。这明显是一个糟糕
的设计—而且会越来越糟。考虑下面的程序包说明:
CREATEORREPLACEPACKAGEsalespkgISPROCEDUREcalc_total(zone_inIN
VARCHAR2);
PROCEDUREcalc_total(zone_inINCHAR);ENDsalespkg;/
同样,这个程序包在编译时也不会有任何问题。但现在又面临另一种情况:无法调用这
些过程中的任意一个。他们共享程序名和参数名。唯一的区别就是数据类型。VARCHAR2当
然不同于CHAR,因此编译器会让您轻松过关。但不幸地是,这两种数据类型的差异性还不
够大,这将导致在真正试图使用代码的时侯出现难题。考虑下面的一段代码:
BEGINsalespkg.calc_total(‘'ZONE15'’);END;
''ZONE15''是固定长还是可变长?PL/SQL文档中说道“Allstringliterals
exceptthenullstring(‘''’)havedatatypeCHAR,”(所有非空串的字符串文字
都是CHAR型)但是编译器并没有意识到这个问题。非常奇怪,即使向程序传送了一个显式
声明为固定长度的字符串,仍然会出现问题。
SQL>DECLARE2l_zoneCHAR(6):=''ZONE15'‘;3BEGIN4
salespkg.calc_total(l_zone);5END;6/salespkg.calc_total(l_zone);*ERROR
atline4:PLS-00307:toomanydeclarationsof''CALC_TOTAL''matchthiscall
如您所见,确实可以定义这样一个程序包重载,即它可以顺利通过编译,但要么不可用,
要么只能通过“非自然行为”使用,例如强制使用参数名引用。
我将采用什么样的解决方案呢?
为了识别这些重载问题,我产生如下的想法:也许我可以构建一个实用程序“自动”扫
描程序包,检查所有可能的有效程序调用置换,并提醒我注意那些多义重载。我能够作到这
一点吗?看起来我应该要能够将程序包定义解析为各段程序,同时解析出每个程序的参数列
表。我应该能够取出这些参数的数据类型和缺省值。我如何才能获得这些信息呢?遗憾的是,
我对PL/SQL解析器没有API级的访问权限,特别是不能从PL/SQL自身内部来进行访问。
而且我压根不想考虑自己编写一个解析器。那么一个积极的(深受困扰的)实用程序构造者
会如何去做呢?他会查找可以替代的方法。
还有没有其他的方法可以从程序中提取这一信息呢?我想起每当我编译一个PL/SQL
程序,Oracle数据库就会对源代码进行解析并将其装载到数据字典中。然后就可以提供各
种数据字典视图给出对所存储代码的不同描述。ALL_SOURCE揭示了源代码。
ALL_DEPENDENCIES显示各个对象之间的依赖性。ALL_OBJECTS告诉我哪个程序是INVALID.也许有某个数据字典视图有助于解决这一问题。我该如何找到它呢?数据字典中有很多视
图,而且这些视图都非常晦涩。
为了能对此有所帮助,我构建了一个名为dd_view_scan的实用程序,该程序可以找到
符合需要的那些视图(参见表1;完整的实现行位于ddviewscan.sp文件中)。使用
dd_view_scan,我可以轻松搜索视图集以便找到那些可能会提供帮助的数据源。接下来我会
对某个特定的视图进行深入分析,看看它是否真的包含我所需要的信息。例如,如果我希望
分析程序的重载,那么我就不仅需要知道程序名,还需要检查参数列表(也就是传递的参数)。
首先我会向dd_view_scan查询含有单词parameter或argument的那些数据字典视图。您
可以在列表2中查看到相应的结果。(注意:我使用Oracle9iRelease2得到这些结果。
使用早期版本运行相同的查询可能会得到不同的结果。)
仔细检查该列表,ALL_ARGUMENTS引起了我的注意。其他的参数看起来要更具针对性,
不是我想要的那种。当我仔细分析该参数,我找到了程序包名、参数名和数据类型(参见列
表3)。这些看起来就比较接近我的目标,有必要深入查看一下。让我们继续进行探讨。
研究:有关ALL_ARGUMENTS的全部内容
下一步,查看Oracle文档集。我急切地去查看Oracle9i文档,希望能从中找到答案。
我从浏览器中打开文档,使用MasterIndex,立即缩小了ALL_ARGUMENTS的范围。表1显
示了对该视图每一列的描述。使用下面的查询,您可以顺便获得虽不相同但还相近的信息(参
见all_arguments_cols.sql):
SELECTcolumn_name,commentsFROMall_col_commentsWHERE
table_name=''ALL_ARGUMENTS''
遗憾的是,您在表1中所见的就是Oracle可提供的所有信息。无论如何,我们已经
迈出了第一步。许多列的引入都是不言而喻的。而对于其他的,例如OVERLOAD、POSITION和
DATA_LEVEL,就不太清楚了。
下面,我需要确信我已经理解了ALL_ARGUMENTS的内容。我还必须证实ALL_ARGUMENTS
包含了Oracle所说的它包含的东西。如果您研究Oracle技术超过六个月,那么您就会明
白任何事都不能想当然。文档中对某技术有某种说法,并不意味着该技术本身就是那样的。
某个作者(例如StevenFeuerstein)说某技术能够在其笔记本电脑中Windows2000下的
Oracle9iRelease2上运行,并不意味着该技术在您的系统中也会以同样的方式运行。自
己进行测试来验证取决于您的应用程序的那些操作是非常重要的。
下面就是我所作的一些工作:我将名为allargs_test的程序包(位于