2016年2月14日 星期日

包二(限制包的行為、DBMS_OUTPUT包) (PL/SQL 十六)

※限制包的行為

可參考官方網站
常用的有四種:
WNDS:Writes No Database State-->不能寫入資料庫的狀態,所以不能新增、修改、刪除
RNDS:Reads No Database State-->不能讀資料庫的狀態,所以不能查詢
WNPS:Writes No Package State-->不能寫入包的狀態,所以不能賦值,如var := 'x';
RNPS:Reads No Package State-->不能讀包的狀態,所以不能使用變數


CREATE OR REPLACE PACKAGE xxx_pkg IS
    ooo VARCHAR2(10) := 'ooo';
    
    FUNCTION a(p NUMBER) RETURN NUMBER;
    FUNCTION b(p VARCHAR2) RETURN NUMBER;
    FUNCTION c(p1 NUMBER, p2 VARCHAR2) RETURN VARCHAR2;
    FUNCTION d(p1 VARCHAR2, p2 NUMBER) RETURN VARCHAR2;
    
    PRAGMA RESTRICT_REFERENCES(a, WNDS);
    PRAGMA RESTRICT_REFERENCES(b, RNDS);
    PRAGMA RESTRICT_REFERENCES(c, WNPS);
    PRAGMA RESTRICT_REFERENCES(d, RNPS);
END;

※定義四個限制包的行為


CREATE OR REPLACE PACKAGE BODY xxx_pkg IS
    FUNCTION a(p NUMBER) RETURN NUMBER IS
    BEGIN
    DELETE FROM DEPT WHERE DEPTNO = p;
    RETURN 0;
    END a;
    
    FUNCTION b(p VARCHAR2) RETURN NUMBER IS
    xxx DEPT%ROWTYPE;
    BEGIN
    SELECT * INTO xxx FROM DEPT WHERE DNAME = p;
    RETURN 0;
    END b;
    
    FUNCTION c(p1 NUMBER, p2 VARCHAR2) RETURN VARCHAR2 IS
    BEGIN
    ooo := 'xxx';
    RETURN '';
    END c;
    
    FUNCTION d(p1 VARCHAR2, p2 NUMBER) RETURN VARCHAR2 IS
    BEGIN
    RETURN ooo;
    END d;
END;

※包體故意違反後編譯,就會出現如下的錯誤
Compilation errors for PACKAGE BODY OOO.XXX_PKG

Error: PLS-00452: Subprogram 'A' violates its associated pragma
Line: 3
Text: FUNCTION a(p NUMBER) RETURN NUMBER IS

Error: PLS-00452: Subprogram 'B' violates its associated pragma
Line: 9
Text: FUNCTION b(p VARCHAR2) RETURN NUMBER IS

Error: PLS-00452: Subprogram 'C' violates its associated pragma
Line: 16
Text: FUNCTION c(p1 NUMBER, p2 VARCHAR2) RETURN VARCHAR2 IS

Error: PLS-00452: Subprogram 'D' violates its associated pragma
Line: 22
Text: FUNCTION d(p1 VARCHAR2, p2 NUMBER) RETURN VARCHAR2 IS

Error: Hint: Parameter 'p1' is declared but never used in 'c'
Line: 16
Text: FUNCTION c(p1 NUMBER, p2 VARCHAR2) RETURN VARCHAR2 IS

Error: Hint: Parameter 'p2' is declared but never used in 'c'
Line: 16
Text: FUNCTION c(p1 NUMBER, p2 VARCHAR2) RETURN VARCHAR2 IS

Error: Hint: Parameter 'p1' is declared but never used in 'd'
Line: 22
Text: FUNCTION d(p1 VARCHAR2, p2 NUMBER) RETURN VARCHAR2 IS

Error: Hint: Parameter 'p2' is declared but never used in 'd'
Line: 22
Text: FUNCTION d(p1 VARCHAR2, p2 NUMBER) RETURN VARCHAR2 IS

※也可以限制只能讀取資料庫等,如:
PRAGMA RESTRICT_REFERENCES(a, WNDS, WNPS, RNPS);
也就是用逗號隔開



※DBMS_OUTPUT包

※PUT_LINE、ENABLE、DISABLE、PUT、NEW_LINE

DECLARE
BEGIN
    DBMS_OUTPUT.PUT_LINE('目前看得見');
    DBMS_OUTPUT.DISABLE;
    DBMS_OUTPUT.PUT_LINE('看不見');
    
    DBMS_OUTPUT.ENABLE;
    DBMS_OUTPUT.PUT_LINE('看得見');
    
    DBMS_OUTPUT.PUT('ww');
    DBMS_OUTPUT.PUT('w.');
    DBMS_OUTPUT.NEW_LINE;
    DBMS_OUTPUT.PUT('因為後面沒有執行NEW_LINE,所以看不見');
END;

※結果:
看得見
www.

※DISABLE方法是將整個輸出關閉,不管之前有沒有,一切全洗掉,類似SET SERVEROUTPUT OFF

※PUT後面必需有接換行的方法才會印出,如NEW_LINE、PUT_LINE

※debug時,寫DBMS_OUTPUT.PUT_LINE寫太多時報錯,可用exception印SQLCODE,有可能是報「-20000」的錯,因為超過buffer大小,預設是20000,可參考官網

以下是不設定 buffer 的做法:
declare 
begin
    DBMS_OUTPUT.ENABLE(null);
    for i in 1..1000 loop
        dbms_output.put_line('1234567890');
    end loop;
end;

※給 null 和空都可以,或者用以下的方式

※如果兩個都設定,以程式碼為主



※GET_LINE

DECLARE
    line1 VARCHAR2(20);
    line2 VARCHAR2(20);
    status INTEGER;
BEGIN
    DBMS_OUTPUT.ENABLE;
    
    DBMS_OUTPUT.PUT('http:');
    DBMS_OUTPUT.PUT('//');
    DBMS_OUTPUT.NEW_LINE;
    
    DBMS_OUTPUT.PUT('www.');
    DBMS_OUTPUT.PUT('google.com');
    DBMS_OUTPUT.NEW_LINE;
    
    DBMS_OUTPUT.GET_LINE(line1, status);
    -- DBMS_OUTPUT.GET_LINE(line2, status);
    
    DBMS_OUTPUT.PUT_LINE('line1=' || line1);
    -- DBMS_OUTPUT.PUT_LINE('line2=' || line2);
    
    DBMS_OUTPUT.PUT_LINE('status=' || status);
END;

※結果:
l1=http://
xxx_out=0

※一直到最後的NEW_LINE是上一個程式碼講過的,但一執行到GET_LINE就什麼都印不出來了,然後要到PUT_LINE又會印出,印出的是第一次NEW_LINE上面的全部內容,也就是http和//兩行,而最後的xxx_out只有兩個值,1和0,0表示成功印出,1表示一行都沒有,可參考官方網站,所以將ENABLE到第二次的NEW_LINE註解就會印出1了


※GET_LINES、CHARARR

DECLARE
    lines DBMS_OUTPUT.CHARARR;
    -- lines DBMSOUTPUT_LINESARRAY;
    
    numlines INTEGER := 2;
BEGIN
    DBMS_OUTPUT.ENABLE;
    
    DBMS_OUTPUT.PUT('http:');
    DBMS_OUTPUT.PUT('//');
    DBMS_OUTPUT.NEW_LINE;
    
    DBMS_OUTPUT.PUT('www.');
    DBMS_OUTPUT.PUT('google.com');
    DBMS_OUTPUT.NEW_LINE;
    
    DBMS_OUTPUT.GET_LINES(lines, numlines);
    FOR i IN lines.FIRST..lines.LAST LOOP
        DBMS_OUTPUT.PUT_LINE(lines(i));
    END LOOP;
END;

※如果將numlines設為1就等同於GET_LINE

※註解那行也可以,官網有寫

沒有留言:

張貼留言