【问题标题】:PL/SQL : IF-ELSIF-ELSE -> ELSE block is not considered as a basic blockPL/SQL : IF-ELSIF-ELSE -> ELSE 块不被视为基本块
【发布时间】:2022-01-03 08:57:45
【问题描述】:

这是 ORACLE 文档提供的 PL/SQL 基本块的定义。

基本块是指PL/SQL代码的单入口单出口块

这是来自互联网的基本块的定义

基本块是一组始终按顺序执行的语句。基本块的特点是——它们不包含任何类型的跳转语句。中间没有分支或停止的可能性。所有语句都按照它们出现的顺序执行。

我正在尝试使用 DBMS_PLSQL_CODE_COVERAGE 包查找 PL/SQL 代码的代码覆盖率,该包允许查找 PL/SQL 块级覆盖率。这里我对 IF-ELSIF-ELSE 代码覆盖率有些疑问。最主要的是ELSE block of IF-ELSIF-ELSE is not considered as a basic block。请参考以下代码。

这是我的第一个例子。在第二列中,所有值(0 和 1)代表一个基本块。在这里你可以清楚地看到 IF 和 ELSE 条件都算作基本块,这对我来说是相当公平的。

现在,这让我很困扰。请参考以下代码。

这里你可以看到所有的 IF 和 ELSIF 块都算作基本块,这没关系。但是 ELSE 块不被认为是基本块,也就是说,根据基本块的定义,如果执行到最后一个 ELSIF 块,无论 ELSE 的哪个部分也被执行。这会影响代码覆盖率。

但在实际执行中,这不会发生。但是 DBMS_PLSQL_CODE_COVERAGE 包给了我这个输出。有人可以解释为什么会这样吗?我不知道这个包的内部实现。我所知道的是它使用 DBMS_PROFILER 来获取代码覆盖率。

如果您对此感到好奇,上面的表格提供给我的是哪个查询。

SELECT LISTAGG(ccb.col, ',') WITHIN GROUP (ORDER BY ccb.col) AS col,
       LISTAGG(ccb.covered, ',') WITHIN GROUP (ORDER BY ccb.col) AS covered,
       s.line,
       LISTAGG(ccb.not_feasible, ',') WITHIN GROUP (ORDER BY ccb.col) AS not_feasible,
       s.text
FROM   user_source s
       JOIN dbmspcc_units ccu ON s.name = ccu.name AND s.type = ccu.type
       LEFT OUTER JOIN dbmspcc_blocks ccb ON ccu.run_id = ccb.run_id AND ccu.object_id = ccb.object_id AND s.line = ccb.line
WHERE  s.name = 'DEMO_UTILITY_TST'
AND    s.type = 'PACKAGE BODY'
AND    ccu.run_id = 248
GROUP BY s.line, s.text
ORDER BY 3;

DBMS_PLSQL_CODE_COVERAGE package documentation

【问题讨论】:

标签: sql oracle plsql code-coverage


【解决方案1】:

检查您的plsql_optimize_level。当这是 2 或更高时,编译器可以大幅重新排列您的代码。作为docs say

0 维持评估顺序,因此维持侧面的模式 Oracle9i 的影响、异常和程序包初始化和 较早的版本。还删除了新的语义标识 BINARY_INTEGER 和 PLS_INTEGER 并恢复之前的规则 整数表达式的评估。虽然代码会运行一些 比在 Oracle9i 中更快,使用 0 级将失去大部分 Oracle Database 10g 中 PL/SQL 的性能提升。

1 对 PL/SQL 程序进行广泛的优化 包括消除不必要的计算和异常, 但通常不会将源代码移出其原始源 顺序。

2 应用广泛的现代优化技术 级别 1 的更改,包括可能移动源代码的更改 离原来的位置比较远。

3 应用广泛的优化技术 2 级,自动包括未特别指定的技术 请求。

在您的示例中,第一个 ifelse 都分支 return -1。所以编译器将它们合并到一个级别 2 或更高级别的块中。

举一个极端的例子。 ifelse 都在return 0 下面的函数中分支。函数中没有其他代码。

plsql_optimize_level = 1,块没有改变。所以代码覆盖率报告两者都被覆盖(假设进行了适当的测试):

alter session set plsql_optimize_level = 1;
create or replace function f ( p int ) 
  return int as
  retval int;
begin
  if p < 10 then
    return 0;
  else
    return 0;
  end if;
end f;
/

declare
  run_id pls_integer;
begin
  dbms_plsql_code_coverage.create_coverage_tables ( true );
  run_id := dbms_plsql_code_coverage.start_coverage('TEST');
  dbms_output.put_line ( f ( 9 ) );
  dbms_output.put_line ( f ( 99 ) );
  dbms_plsql_code_coverage.stop_coverage;
end;
/

select max(ccb.covered) as covered,
       s.line,
       max(ccb.covered) as not_feasible,
       rtrim ( s.text, chr(10) ) text
from   user_source s
join   dbmspcc_units ccu 
on     s.name = ccu.name and s.type = ccu.type
left outer join dbmspcc_blocks ccb
on     ccu.run_id = ccb.run_id and ccu.object_id = ccb.object_id and s.line = ccb.line
group by s.line, s.text
order by s.line;

   COVERED       LINE NOT_FEASIBLE TEXT                                              
---------- ---------- ------------ --------------------------------------------------
         1          1            1 function f ( p int )                              
<null>              2 <null>         return int as                                   
<null>              3 <null>         retval int;                                     
<null>              4 <null>       begin                                             
<null>              5 <null>         if p < 10 then                                  
         1          6            1     return 0;                                     
<null>              7 <null>         else                                            
         1          8            1     return 0;                                     
<null>              9 <null>         end if;                                         
<null>             10 <null>       end f; 

但是将覆盖率增加到 2,编译器会将它们合并在一起。代码覆盖率报告都没有被覆盖!

alter session set plsql_optimize_level = 2;
alter function f compile;

declare
  run_id pls_integer;
begin
  dbms_plsql_code_coverage.create_coverage_tables ( true );
  run_id := dbms_plsql_code_coverage.start_coverage('TEST');
  dbms_output.put_line ( f ( 9 ) );
  dbms_output.put_line ( f ( 99 ) );
  dbms_plsql_code_coverage.stop_coverage;
end;
/

select max(ccb.covered) as covered,
       s.line,
       max(ccb.covered) as not_feasible,
       rtrim ( s.text, chr(10) ) text
from   user_source s
join   dbmspcc_units ccu 
on     s.name = ccu.name and s.type = ccu.type
left outer join dbmspcc_blocks ccb
on     ccu.run_id = ccb.run_id and ccu.object_id = ccb.object_id and s.line = ccb.line
group by s.line, s.text
order by s.line;

   COVERED       LINE NOT_FEASIBLE TEXT                                              
---------- ---------- ------------ --------------------------------------------------
         1          1            1 function f ( p int )                              
<null>              2 <null>         return int as                                   
<null>              3 <null>         retval int;                                     
<null>              4 <null>       begin                                             
<null>              5 <null>         if p < 10 then                                  
<null>              6 <null>           return 0;                                     
<null>              7 <null>         else                                            
<null>              8 <null>           return 0;                                     
<null>              9 <null>         end if;                                         
<null>             10 <null>       end f;    

TL;DR在运行代码覆盖测试之前设置plsql_optimize_level = 1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-26
    • 2013-10-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多