從一個案例看PL/SQL代碼片的編譯與運行

來源:互聯網
上載者:User

從一個案例看PL/SQL代碼片的編譯與運行

PL/SQL語言是Oracle針對資料庫商務邏輯需求開發的一種面向過程的結構化編程語句。在Oracle核心中,存在PL/SLQ引擎和SQL引擎兩個重要組成部分,分別用於處理結構化的PL/SQL語句和SQL語句。

同所有進階語言一樣,PL/SQL語句同樣存在編譯和運行兩個關鍵步驟。在Compile環節,主要實現文法許可權檢查、對象方法檢查和文法結構檢查。在運行Runtime階段,相同的過程其實還是會進行,一些語句錯誤都是在運行時發生檢測。

本篇主要通過一個錯誤案例,詳細分析一下兩者的特點和差異。

1、問題說明

朋友向筆者諮詢一個問題,為什麼寫好的PL/SQL匿名塊在執行狀態時,報錯沒有被exception所捕獲。說明:基於資料保護原因,程式碼片段為類比版本。

實驗環境為11gR2,具體版本號碼為11.2.0.4。

SQL> select * from v$version;

BANNER

--------------------------------------------------------------------------------

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

PL/SQL Release 11.2.0.4.0 - Production

CORE    11.2.0.4.0    Production

TNS for Linux: Version 11.2.0.4.0 - Production

NLSRTL Version 11.2.0.4.0 – Production

類比程式碼片段如下:

SQL> declare

  2    i varchar2(10);

  3  begin

  4    select err

  5    into i 

  6    from t

  7    where rownum<2;

  8  exception

  9    when others then

 10      dbms_output.put_line('Errors Catch!');

 11  end;

 12  /

  select err

        *

ERROR at line 4:

ORA-06550: line 4, column 10:

PL/SQL: ORA-00904: "ERR": invalid identifier

ORA-06550: line 4, column 3:

PL/SQL: SQL Statement ignored

朋友的問題是,資料表T的確沒有err列,但是執行的時候為什麼沒有在exception部分被捕獲,而是直接報錯了呢?

資料表T的描述如下:

SQL> desc t;

Name    Type              Nullable Default Comments 

------- ------------------ -------- ------- -------- 

USER_ID VARCHAR2(100 BYTE) Y             

2、解析與測試

這個問題直觀看比較唬人,但是仔細分析起來還是有概念障礙的問題。這就是程式的編譯時間和運行時。

我們編寫PL/SQL程式的時候,無論是procedure還是package,都有一個顯示compile的動作,成功之後我們才可以exec執行程式。在compile的時候,所進行的工作對於任何語言編譯器來講,都是差不多的。

常見的大多數都是驗證和校正,比如使用變數是否定義?該使用者有無使用對象、變數許可權?變數可見度範圍?或者文法使用是否正確?經過compile之後,編譯器會認為程式已經具備了執行的前提條件,會將其轉化為目標對象。

但是,經過編譯的程式,執行過程(也就是運行時Runtime)就萬無一失嗎?肯定不是,記憶體、CPU、磁碟的資源約束,內在邏輯操作的錯誤、數字運算的錯誤都會引起一系列的運行時故障。我們說,PL/SQL中的exception,就是針對runtime的錯誤而言的。

一種假設:錯誤使用的是一個匿名塊。在執行過程中,實際上是編譯和運行兩步走的情況。程式還沒有進入runtime階段,都被編譯compile發現了錯誤,拋了出來。

我們通過一系列簡單實驗,來證明結論。首先,建立一個單獨的預存程序,不執行只是編譯,看結果如何?

SQL> create or replace procedure TEST

  2  as

  3    i varchar2(10);

  4  begin

  5    select err

  6    into i 

  7    from t

  8    where rownum<2;

  9  exception

 10    when others then

 11      dbms_output.put_line('Errors Catch!');

 12  end;

 13  /

Warning: Procedure created with compilation errors.

編譯錯誤,通過show error顯示:

SQL> show error             

Errors for PROCEDURE TEST:

LINE/COL ERROR

-------- -----------------------------------------------------------------

5/3      PL/SQL: SQL Statement ignored

5/10    PL/SQL: ORA-00904: "ERR": invalid identifier

錯誤資訊和匿名塊的完全相同。此時並沒有進入運行時,可以推想匿名塊的報錯是在編譯時間發生的。

那麼怎麼能夠迴避編譯時間檢查,將這個錯誤“送到”運行時,讓exception捕獲到呢?我們可以使用字串。

SQL> create or replace procedure TEST

  2  as

  3    i varchar2(10);

  4  begin

  5    execute immediate 'select err from t where rownum<2'

  6            into i;

  7 

  8  exception

  9    when others then

 10      dbms_output.put_line('Errors Catch!');

 11  end;

 12  /

Procedure created.

execute immediate可以直接執行字串類SQL語句。該預存程序成功建立,說明PL/SQL引擎沒有進行針對字串的檢查。

執行:

SQL> set serveroutput on;

SQL> exec test;

Errors Catch!

PL/SQL procedure successfully completed.

果然可以被exception捕獲,同預想效果相同。根據這個思路,修改朋友的代碼如下:

SQL> declare

  2    i varchar2(10);

  3  begin

  4    execute immediate 'select err from t where rownum<2'

  5            into i;

  6             

  7  exception

  8    when others then

  9      dbms_output.put_line('Errors Catch!');

 10  end;

 11  /

Errors Catch!

PL/SQL procedure successfully completed.

順利通過編譯Compile檢查,到Runtime時候報錯。

3、結論

PL/SQL語句是我們進行資料庫結構化、過程化處理的重要工具。應該意識到,PL/SQL本身也是一種程式設計語言,都需要Compile和Runtime階段,每個階段有不同的任務使命。

Oracle資料庫之PL/SQL程式基礎設計 

PL/SQL Developer實用技巧分享

使用PL/Scope分析PL/SQL代碼 

在PL/SQL中使用日期類型

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.