使用PL/Scope分析你的PL/SQL代码

从11g開始Oracle引入了PL/Scope 用于编译器收集PL/SQL程序单元的全部标识符(变量名、常量名、程序名等)。

收集到的信息可通过一系列静态数据字典视图获取。

可帮助我们了解标识符的声明。定义。引用,调用或赋值以及所在源码的位置。

使用PL/Scope, 开发人员能够运行复杂的代码分析。

1、启用 Enabling PL/Scope

ALTER SESSION SET plscope_settings='IDENTIFIERS:ALL'
/

plscope_settings 參数有2个可选值:

IDENTIFIERS:ALL or IDENTIFIERS:NONE(默认不收集)

2、关于视图 ALL_IDENTIFIERS View

当1中參数设置为IDENTIFIERS:ALL,同一时候在同一会话中编译程序单元后。该单元全部标识符信息被收集到视图ALL_IDENTIFIERS中。

下面是该视图字段简单介绍:

【OWNER】 The owner of the program unit containing the identifier

【NAME】 The name of the identifier

【TYPE】 The type of the identifier, such as FORALL OUT (an out argument), CONSTANT, PACKAGE, or RECORD

【SIGNATURE】 签名。用于区分同名标识符的唯一字符串;

A unique string for each distinct identifier, across all program units, useful for distinguishing between different identifiers that happen to have the same name

【OBJECT_NAME】 The name of the program unit containing the identifier OBJECT_TYPE The type of the program unit containing the identifier, such as PACKAGE, TRIGGER, or PROCEDURE

【USAGE】 针对标识符的操作类型 The type of usage of the identifier (such as a declaration or an assignment)

【USAGE_ID】 A sequentially generated integer value for an identifier, unique within its program unit

【USAGE_CONTEXT_ID】A foreign key reflexive back to USAGE_ID; in essence, the parent of this identifier appearance (for example, the context of a variable’s declaration is the name of the subprogram in which the variable is declared)

【LINE】 标识符出现的行 The number of the line on which the identifier appears

【COL】 标识符出现的列 The column position in the line at which the identifier appears

你能够获取给定程序单元的全部标识符信息:

SELECT *
FROM all_identifiers ai
WHERE ai.owner = USER
AND ai.object_type = '<program_type>'
AND ai.object_name = '<program_name>'
ORDER BY line

3、PL/Scope追踪的标识符使用方法 Usages Tracked by PL/Scope

ASSIGNMENT: 赋值操作。包括:=。FETCH.. INTO以及OUT 、IN OUT模式參数。

CALL:调用操作。

DECLARATION: 声明。

Indicates that the identifier is declared.

REFERENCE: 引用。Indicates that an identifier is used in the program without a change in its value. Examples include raising an exception, passing the identifier to an IN or IN OUT mode parameter of a subprogram or USING clause of EXECUTE IMMEDIATE, and using the identifier in a %TYPE declaration.

DEFINITION:定义。Tells the compiler how to implement or use a previously declared identifier. The following identifier types will have a DEFINITION row in ALL_IDENTIFIERS: FUNCTION, OBJECT, PACKAGE, PROCEDURE, TRIGGER, and EXCEPTION.

这些使用方法便于更加easy获取关于程序单元的具体信息。

如果我想看看程序单元中的变量的声明部分:

SELECT ai.object_name
, ai.object_type
, ai.name variable_name
, ai.name context_name
FROM all_identifiers ai
WHERE ai.owner = USER AND
ai.TYPE = 'VARIABLE' AND
ai.usage = 'DECLARATION'
ORDER BY ai.object_name,
ai.object_type, ai.usage_id

4、理解标识符的层级关系 Using Usage IDs to Understand Identifier Hierarchy

一个包能够包括一个或多个子程序;一个子程序能够有一个或多个參数。你能够使用PL/Scope探索这样的层级关系。

比如:

Code Listing 1: Defining the plscope_demo package 

CREATE OR REPLACE PACKAGE plscope_demo
IS
PROCEDURE my_procedure (param1_in IN INTEGER
, param2 IN employees.last_name%TYPE
);
END plscope_demo;
/
CREATE OR REPLACE PACKAGE BODY plscope_demo
IS
PROCEDURE my_procedure (param1_in IN INTEGER
, param2 IN employees.last_name%TYPE
)
IS
c_no_such CONSTANT NUMBER := 100;
l_local_variable NUMBER;
BEGIN
IF param1_in > l_local_variable
THEN
DBMS_OUTPUT.put_line (param2);
ELSE
DBMS_OUTPUT.put_line (c_no_such);
END IF;
END my_procedure;
END plscope_demo;
/

You can then execute a hierarchical query, specifying the usage_context_id column as the parent of a row in the ALL_IDENTIFIERS view, to see the hierarchy of identifiers shown in Listing 2.

你能够运行一个层级查询,指定usage_context_id作为父级行:

Code Listing 2: Querying against ALL_IDENTIFIERS view to see the hierarchy of identifiers 

WITH plscope_hierarchy
AS (SELECT line
, col
, name
, TYPE
, usage
, usage_id
, usage_context_id
FROM all_identifiers
WHERE owner = USER
AND object_name = 'PLSCOPE_DEMO'
AND object_type = 'PACKAGE BODY')
SELECT LPAD ('-', 3 * (LEVEL - 1))
|| TYPE
|| ' '
|| name
|| ' ('
|| usage
|| ')'
identifier_hierarchy
FROM plscope_hierarchy
START WITH usage_context_id = 0
CONNECT BY PRIOR usage_id = usage_context_id
ORDER SIBLINGS BY line, col PACKAGE PLSCOPE_DEMO (DEFINITION)
PROCEDURE MY_PROCEDURE (DEFINITION)
FORMAL IN PARAM1_IN (DECLARATION)
SUBTYPE INTEGER (REFERENCE)
FORMAL IN PARAM2 (DECLARATION)
CONSTANT C_NO_SUCH (DECLARATION)
CONSTANT C_NO_SUCH (ASSIGNMENT)
NUMBER DATATYPE NUMBER (REFERENCE)
VARIABLE L_LOCAL_VARIABLE (DECLARATION)
NUMBER DATATYPE NUMBER (REFERENCE)
FORMAL IN PARAM1_IN (REFERENCE)
VARIABLE L_LOCAL_VARIABLE (REFERENCE)

5、使用签名区分标识符 Using a Signature to Differentiate Between Identifiers

考虑下面情况:

PROCEDURE plscope_demo_proc
IS
plscope_demo_proc NUMBER;
BEGIN
DECLARE
plscope_demo_proc EXCEPTION;
BEGIN
RAISE plscope_demo_proc;
END; plscope_demo_proc := 1;
END plscope_demo_proc;

同一标识符plscope_demo_proc出现多次代表了不同的对象。

麻烦之处在于它仍然是合法的代码。跟谁说理去!

。。

依照以往使用ALL_SOURCE非常难区分开来。而使用PL/Scope则显得轻松很多:

Code Listing 3: Distinguishing between identifiers with the same name 

SELECT line
, TYPE
, usage
, signature
FROM all_identifiers
WHERE owner = USER
AND object_name = 'PLSCOPE_DEMO_PROC'
AND name = 'PLSCOPE_DEMO_PROC'
ORDER BY line LINE TYPE USAGE SIGNATURE
1 PROCEDURE DEFINITION 51B3B5C5404AE8307DA49F42E0279915
1 PROCEDURE DECLARATION 51B3B5C5404AE8307DA49F42E0279915
3 VARIABLE DECLARATION 021B597943C0F31AD3938ACDAAF276F3
6 EXCEPTION DECLARATION 98E0183501FB350439CA44E3E511F60C
8 EXCEPTION REFERENCE 98E0183501FB350439CA44E3E511F60C
11 VARIABLE ASSIGNMENT 021B597943C0F31AD3938ACDAAF276F3

另一个小问题,同一个签名出现2次?

原因是同一标识符有多个USAGE, 那么我们假如我仅仅需查看全部变量的赋值和引用操作:

Code Listing 4: Querying all assignments and references to the PLSCOPE_DEMO_PROC variable 

SELECT usg.line
, usg.TYPE
, usg.usage
FROM all_identifiers dcl,
all_identifiers usg
WHERE
dcl.owner = USER
AND dcl.object_name = 'PLSCOPE_DEMO_PROC'
AND dcl.name = 'PLSCOPE_DEMO_PROC'
and dcl.usage = 'DECLARATION'
and dcl.type = 'VARIABLE'
and usg.signature = dcl.signature
and usg.usage <> 'DECLARATION'
ORDER BY line

6、验证命名是否规范 Validate Naming Conventions

如果我有下面要求:

IN parameters: end with _in

OUT parameters: end with _out

IN OUT parameters: end with _io

为了验证一个程序单元符合这个规则,我将针对FORMAL IN, FORMAL OUT, or FORMAL IN OUT检索其声明情况。

如果我声明了下面測试包:

Code Listing 5: Creating the package specification for plscope_demo 

CREATE OR REPLACE PACKAGE plscope_demo
IS
PROCEDURE my_procedure (param1_in IN INTEGER, param2 IN DATE); FUNCTION my_function (param1 IN INTEGER
, in_param2 IN DATE
, param3_in IN employees.last_name%TYPE
)
RETURN VARCHAR2;
END plscope_demo; Code Listing 6: Querying to find naming violations SELECT prog.name subprogram, parm.name parameter
FROM all_identifiers parm, all_identifiers prog
WHERE parm.owner = USER
AND parm.object_name = 'PLSCOPE_DEMO'
AND parm.object_type = 'PACKAGE'
AND prog.owner = parm.owner
AND prog.object_name = parm.object_name
AND prog.object_type = parm.object_type
AND parm.usage_context_id = prog.usage_id
AND parm.TYPE IN ('FORMAL IN', 'FORMAL IN OUT', 'FORMAL OUT')
AND parm.usage = 'DECLARATION'
AND ( (parm.TYPE = 'FORMAL IN'
AND LOWER (parm.name) NOT LIKE '%\_in' ESCAPE '\')
OR (parm.TYPE = 'FORMAL OUT'
AND LOWER (parm.name) NOT LIKE '%\_out' ESCAPE '\')
OR (parm.TYPE = 'FORMAL IN OUT'
AND LOWER (parm.name) NOT LIKE '%\_io' ESCAPE '\'))
ORDER BY prog.name, parm.name

‘7、识别违反最佳做法的操作 Identify Violations of Best Practices

1)声明在包说明中的变量 Variables declared in the specification of a package。

这样的情况下不论什么对包有运行权限的用户都可直接读取该变量。

2)已声明但未在程序中抛出的异常 Exception declared but not raised in a program unit.

以上2类操作都是不合理的。

检查第一种情况简单:

SELECT object_name, name, line
FROM all_identifiers ai
WHERE ai.owner = USER
AND ai.TYPE = 'VARIABLE'
AND ai.usage = 'DECLARATION'
AND ai.object_type = 'PACKAGE';

另外一种情况,先要观察一下异常在程序中的各种使用类型(USAGES)

PROCEDURE plscope_demo_proc
IS
e_bad_data EXCEPTION;
PRAGMA EXCEPTION_INIT (
e_bad_data, -20900);
BEGIN
RAISE e_bad_data;
EXCEPTION
WHEN e_bad_data
THEN
log_error ();
END plscope_demo_proc;

Let’s see what PL/Scope has to say about the e_bad_data identifier:

SELECT line
, TYPE
, usage
FROM all_identifiers
WHERE owner = USER
AND object_name =
'PLSCOPE_DEMO_PROC'
AND name = 'E_BAD_DATA'
ORDER BY line
/ LINE TYPE USAGE
----- ------------ ---------------
3 EXCEPTION DECLARATION
4 EXCEPTION ASSIGNMENT
6 EXCEPTION REFERENCE
8 EXCEPTION REFERENCE

能够判断出EXCEPTION_INIT被当做赋值操作。RAISE statement and the WHEN clause被觉得是引用操作。

如此一来,我们声明一下语句就可以:

Code Listing 7: Querying all subprograms in which an exception is declared but not referenced 

WITH subprograms_with_exception
AS (SELECT DISTINCT owner
, object_name
, object_type
, name
FROM all_identifiers has_exc
WHERE has_exc.owner = USER
AND has_exc.usage = 'DECLARATION'
AND has_exc.TYPE = 'EXCEPTION'),
subprograms_with_raise_handle
AS (SELECT DISTINCT owner
, object_name
, object_type
, name
FROM all_identifiers with_rh
WHERE with_rh.owner = USER
AND with_rh.usage = 'REFERENCE'
AND with_rh.TYPE = 'EXCEPTION')
SELECT *
FROM subprograms_with_exception
MINUS
SELECT *
FROM subprograms_with_raise_handle
;

使用PL/Scope分析PL/SQL代码的更多相关文章

  1. 同样的一句SQL语句在pl/sql 代码块中count 没有数据,但是直接用SQl 执行却可以count 得到结果

    pl/sql 代码块: SELECT count(distinct t2.so_nbr) INTO v_count2 FROM KFGL_YW_STEP_qd t2 WHERE t2.partitio ...

  2. 将PL/SQL代码封装在机灵的包中

    将代码封装在机灵的包中 http://www.oracle.com/technetwork/issue-archive/2013/13-jan/o13plsql-1872456.html 绝大多数基于 ...

  3. 某音乐类App评论相关API的分析及SQL注入尝试

    关键字:APIfen.工具使用.sql注入 涉及工具/包:Fiddler.Burpsuite.Js2Py.Closure Compiler.selenium.phantomjs.sqlmap 摘要: ...

  4. 使用gitbase 分析git 仓库代码

      gitbase 是一个基于golang 开发的开源git 仓库sql 接口查询引擎,基于此工具,我们可以方便的分析git 仓库代码的情况 而且可以基于源码的分析,还是很强大的 安装 直接使用编译的 ...

  5. [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构

    [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构 目录 [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构 0x00 摘要 0x01 文件简介 0x02 总体架构 0x03 总体代码 0x0 ...

  6. Windows下使用doxygen阅读和分析C/C++代码

    Windows下使用doxygen阅读和分析C/C++代码 转自:http://blog.sina.com.cn/s/blog_63d902570100gwk6.html 虽然使用各种IDE或者Sou ...

  7. EntityFramework 7 如何查看执行的 SQL 代码?

    EF 其他版本:EntityFramework 如何查看执行的 SQL 代码? 在 EF7 中,并没有 Context.Database.Log 属性访问方式,但改变更加强大了,我们可以使用下面方式配 ...

  8. EntityFramework 如何查看执行的 SQL 代码?

    在 VS 调试的时候,如果我们项目中使用的是 EntityFramework,查看 SQL 执行代码就不像 ADO.NET 那样直观了,我们需要设置下,可以参考下: How can I log the ...

  9. 仅个人兴趣,自己通过搜索他人的成果,结合自己的理解,来分析discuz的代码。

    仅个人兴趣,自己通过搜索他人的成果,结合自己的理解,来分析discuz的代码. discuz 版本: 3.2

随机推荐

  1. Appium Python 五:元素定位

    总结 单个元素定位: driver.find_element_by_accessibility_id(id) driver.find_element_by_android_uiautomator(ui ...

  2. picture control控件

    注意:picture control控件,需要先更改其ID再使用. CImage myImage; CFileDialog fileDlg(TRUE,NULL,NULL,OFN_ALLOWMULTIS ...

  3. java线程(上)Thread和Runnable的区别

    首先讲一下进程和线程的区别: 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程. 线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈 ...

  4. Linux高性能server编程——多线程编程(下)

    多线程编程 条件变量 假设说相互排斥锁是用于同步线程对共享数据的訪问的话.那么条件变量则是用于线程之间同步共享数据的值. 条件变量提供了一种线程间的通信机制:当某个共享数据达到某个值得时候,唤醒等待这 ...

  5. java 内省(Introspector)

    开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以sun公司开发了一套API,专门用于操作java对象的属性. 当然你也可以用反射来操作JavaB ...

  6. top(topas),vmstat,iostat在linux和AIX操作系统下显示情况

    top(topas),vmstat,iostat在linux和AIX操作系统下显示情况 分类: Linux基础 2013-08-09 17:26 1093人阅读 评论(0) 收藏 举报 详细列出各命令 ...

  7. Windwos在cmd如何复制文本

    生活的琐事,总是要解决. 01.Win+R打开运行窗口 cmd--回车 02. 勾选快速编辑模式 注意: 快速编辑模式就是可以Ctrl+c(复制).Ctrl+v(粘贴)

  8. Centos定时执行python脚本

    其实就是linux的定时任务.老记不住参数,这次写下来,省着老百度.本文没有技术含量,请大家不要吐槽. ================================================ ...

  9. java 运行时环境和编译器环境

    必须要保证运行环境高于编译环境 1.编译器的环境设置 单击项目右键-> Properties -> Java Compiler -> 5或6 如果编译器的环境高于运行时环境会报错. ...

  10. 一般web典型的项目目录结构

    本文转自:http://blog.sina.com.cn/s/blog_4758a28b0100l3lp.html WebRoot-       -common   (系统框架公用jsp 如foote ...