從一個案例看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階段,每個階段有不同的任務使命。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/17203031/viewspace-1962370/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C程式從編譯到執行C程式編譯
- 如何編寫一個前端框架之三-程式碼執行沙箱(譯)前端框架
- Linux C++ 開發2 - 編寫、編譯、執行第一個程式LinuxC++編譯
- SQL執行內幕:從執行原理看調優的本質SQL
- Oracle PL/SQL程式碼中的註釋OracleSQL
- pl/sql developer的一個小問題SQLDeveloper
- 瞭解JDK、JRE、JVM及Java程式的編譯與執行JDKJVMJava編譯
- Java程式碼的編譯與反編譯那些事兒Java編譯
- 編譯執行、解釋執行的一些思考編譯
- SQL優化案例-從執行計劃定位SQL問題(三)SQL優化
- python程式碼混淆與編譯Python編譯
- 【推理引擎】從原始碼看ONNXRuntime的執行流程原始碼
- SQL最佳化案例-從執行計劃定位SQL問題(三)SQL
- 深入淺出JVM(七)之執行引擎的解釋執行與編譯執行JVM編譯
- 編寫一個 Makefile 檔案,對階段專案一的程式碼進行自動化編譯編譯
- (嵌入式)Windows與Ubantu下的C語言程式的編譯執行WindowsC語言編譯
- 【TUNE_ORACLE】列出LOOP套LOOP的PL/SQL程式碼SQL參考OracleOOPSQL
- Idea除錯Rocketmq原始碼編譯執行Idea除錯MQ原始碼編譯
- [譯] Python 的多執行緒與多程式Python執行緒
- win10執行PL/SQL無反應如何解決_win10系統與PL/SQL不相容怎麼處理Win10SQL
- Java編譯和執行的命令Java編譯
- Python是如何編譯執行的Python編譯
- 從編譯後的程式碼,分析 Angular @Injectable 的工作原理編譯Angular
- 今天寫了一個統計執行sql次數的指令碼SQL指令碼
- Oracle優化案例-從執行計劃定位SQL問題(三)Oracle優化SQL
- 08 Windows批處理之執行編譯後的程式Windows編譯
- MXFlutter0.2.5支援直接編譯FlutterUI為可執行的JS程式碼Flutter編譯UIJS
- 【譯】使用Set使你的程式碼執行更快
- java程式碼-編譯-打包-執行_雲原生時代筆記總結Java編譯筆記
- 從cmake解決clion編譯生成的可執行檔案(.exe)不可執行的問題編譯
- PL/SQL程式設計急速上手SQL程式設計
- 編寫 SQL 程式碼時常犯的九個錯誤SQL
- 計算機執行彙編程式碼的原理計算機
- 記一個JavaScript圖片輪播思路與程式碼JavaScript
- 使用Python編寫一個多執行緒的12306搶票程式Python執行緒
- MySQL中普通sql與預編譯sql 區別MySql編譯
- 從django的SECRET_KEY到程式碼執行Django
- Spring AI 與 Ollama 在本地執行案例原始碼SpringAI原始碼
- 【JDK命令列 一】手動編譯Java原始碼與執行位元組碼命令合集(含外部依賴引用)JDK命令列編譯Java原始碼