通過指定子程序的名稱和任何實參,調用子程序,這與調用獨立存儲過程或函數的方式相同。

可以使用一個或多個限定符來調用子程序,也可以不使用限定符來調用子程序,這些限定符是父級子程序的名稱或帶標記的匿名塊,這些塊構成了從中聲明子程序的祖先層次結構。

該調用指定為點分隔的限定符列表,以子程序名稱及其任何參數結尾,如下所示:

[[qualifier_1.][...]qualifier_n.]subprog [(arguments)]

如果指定,則 qualifier_n 是已在其聲明部分中聲明 subprog 的子程序。前面的限定符列表必須位于沿從 qualifier_n 到 qualifier_1 的層次結構向上的連續路徑中。qualifier_1 可以是該路徑中的任何祖先子程序以及以下任意項:

  • 包含子程序的獨立存儲過程名稱。
  • 包含子程序的獨立函數名稱。
  • 包含子程序的包名稱。
  • 包含具有對象類型方法的子程序的對象類型名稱。
  • 在 DECLARE 關鍵字之前包括的匿名塊標簽(如果聲明部分存在),或在 BEGIN關鍵字之前包括的匿名塊標簽(如果沒有聲明部分)。
說明 qualifier_1 不能是 schema 名稱,否則在調用子程序時將引發錯誤。此PolarDB PostgreSQL版(兼容Oracle)限制與 Oracle 數據庫不兼容,后者允許使用 schema 名稱作為限定符。

arguments 是要傳遞給子存儲過程或子函數的實參的列表。

在調用時,將按如下所示執行子程序搜索:

  • 使用其類型(即,子存儲過程或子函數)的被調用子程序名稱以及指定順序的任何限定符(稱為調用列表)查找位于同一層次順序中的一組匹配塊。該搜索從塊層次結構開始,其中最低層級是從中調用子程序的塊。當從上到下觀察代碼時,子程序的聲明必須位于調用它的代碼行之前的 SPL 代碼中。
  • 如果調用列表與從調用子程序的塊開始的塊層次結構不匹配,則將通過從上一個開頭塊的父級開始匹配調用列表來進行比較。換句話說,將沿著層次結構向上進行比較。
  • 如果存在祖先的同輩塊,則調用列表比較還包括同輩塊的層次結構,但始終沿向上層級進行比較,從不比較同輩塊的后代。
  • 此比較過程將沿層次結構向繼續執行,直到找到第一個完全匹配項為止,此時將調用找到的子程序。請注意,匹配子程序的形參列表必須符合為被調用子程序指定的實參列表,否則在調用子程序時會發生錯誤。
  • 如果在向上一直搜索到獨立程序后未找到匹配項,則在調用子程序時將引發錯誤。
說明 子程序調用的PolarDB PostgreSQL版(兼容Oracle)搜索算法與 Oracle 數據庫不兼容。對于 Oracle,搜索會查找第一個限制符(即 qualifier_1)的匹配項。找到這樣的匹配項后,調用的所有其余限定符、子程序名稱、子程序類型和參數必須與找到第一個限定符匹配項的層次結構內容相匹配,否則將引發錯誤。對于PolarDB PostgreSQL版(兼容Oracle),除非調用的所有限定符、子程序名稱和子程序類型與層次結構內容相匹配,否則找不到匹配項。如果最初未找到這樣的精確匹配,PolarDB PostgreSQL版(兼容Oracle)將沿著層次結構繼續向上進行搜索。

子程序相對于從中調用的塊的位置可按如下所示訪問:

  • 本地塊中聲明的子程序可從同一塊的可執行部分或異常部分調用。
  • 父塊或其他祖先塊中聲明的子程序可從父塊或其他祖先塊的子塊調用。
  • 同輩塊中聲明的子程序可從同輩塊或同輩塊的任何后代塊調用。

但是,無法訪問子程序相對于從中調用的塊的以下位置:

  • 塊中聲明的子程序是嘗試從中調用的塊的后代。
  • 塊中聲明的子程序是嘗試從中調用的同輩塊的后代。

以下各示例闡釋了先前所述的各種條件。

調用本地聲明的子程序

以下示例包含一個塊層次結構,這些塊包含在獨立存儲過程 level_0 中。在存儲過程 level_1a 的可執行部分中,顯示了使用限定符和不使用限定符調用本地存儲過程 level_2a 的方式。

另請注意,無論是否使用限定符,都不允許訪問本地存儲過程 level_2a 的后代(即存儲過程 level_3a)。這些調用將在示例中被注釋掉。

CREATE OR REPLACE PROCEDURE level_0
IS
    PROCEDURE level_1a
    IS
        PROCEDURE level_2a
        IS
            PROCEDURE level_3a
            IS
            BEGIN
                DBMS_OUTPUT.PUT_LINE('........ BLOCK level_3a');
                DBMS_OUTPUT.PUT_LINE('........ END BLOCK level_3a');
            END level_3a;
        BEGIN
            DBMS_OUTPUT.PUT_LINE('...... BLOCK level_2a');
            DBMS_OUTPUT.PUT_LINE('...... END BLOCK level_2a');
        END level_2a;
    BEGIN
        DBMS_OUTPUT.PUT_LINE('.. BLOCK level_1a');
        level_2a;                              -- Local block called
        level_1a.level_2a;                     -- Qualified local block called
        level_0.level_1a.level_2a;             -- Double qualified local block called
--        level_3a;                            -- Error - Descendant of local block
--        level_2a.level_3a;                   -- Error - Descendant of local block
        DBMS_OUTPUT.PUT_LINE('.. END BLOCK level_1a');
    END level_1a;
BEGIN
    DBMS_OUTPUT.PUT_LINE('BLOCK level_0');
    level_1a;
    DBMS_OUTPUT.PUT_LINE('END BLOCK level_0');
END level_0;

當調用獨立存儲過程時,輸出如下所示,這指示存儲過程 level_2a 已成功從存儲過程 level_1a 的可執行部分調用。

BEGIN
    level_0;
END;

BLOCK level_0
.. BLOCK level_1a
...... BLOCK level_2a
...... END BLOCK level_2a
...... BLOCK level_2a
...... END BLOCK level_2a
...... BLOCK level_2a
...... END BLOCK level_2a
.. END BLOCK level_1a
END BLOCK level_0

如果您嘗試通過對注釋掉的后代塊的任何調用來運行存儲過程 level_0,則會發生錯誤。

調用祖先塊中聲明的子程序

以下示例顯示如何相對于調用的塊來調用父塊及其他祖先塊中聲明的子程序。

在此示例中,存儲過程 level_3a 的可執行部分調用存儲過程 level_2a(即其父塊)。(請注意,使用 v_cnt 是為了避免無限循環。)

CREATE OR REPLACE PROCEDURE level_0
IS
    v_cnt           NUMBER(2) := 0;
    PROCEDURE level_1a
    IS
        PROCEDURE level_2a
        IS
            PROCEDURE level_3a
            IS
            BEGIN
                DBMS_OUTPUT.PUT_LINE('........ BLOCK level_3a');
                v_cnt := v_cnt + 1;
                IF v_cnt < 2 THEN
                    level_2a;                  -- Parent block called
                END IF;
                DBMS_OUTPUT.PUT_LINE('........ END BLOCK level_3a');
            END level_3a;
        BEGIN
            DBMS_OUTPUT.PUT_LINE('...... BLOCK level_2a');
            level_3a;                          -- Local block called
            DBMS_OUTPUT.PUT_LINE('...... END BLOCK level_2a');
        END level_2a;
    BEGIN
        DBMS_OUTPUT.PUT_LINE('.. BLOCK level_1a');
        level_2a;                              -- Local block called
        DBMS_OUTPUT.PUT_LINE('.. END BLOCK level_1a');
    END level_1a;
BEGIN
    DBMS_OUTPUT.PUT_LINE('BLOCK level_0');
    level_1a;
    DBMS_OUTPUT.PUT_LINE('END BLOCK level_0');
END level_0;

下面是結果輸出:

BEGIN
    level_0;
END;

BLOCK level_0
.. BLOCK level_1a
...... BLOCK level_2a
........ BLOCK level_3a
...... BLOCK level_2a
........ BLOCK level_3a
........ END BLOCK level_3a
...... END BLOCK level_2a
........ END BLOCK level_3a
...... END BLOCK level_2a
.. END BLOCK level_1a
END BLOCK level_0

在類似的示例中,存儲過程 level_3a 的可執行部分調用存儲過程 level_1a,即沿著祖先層次結構進一步向上調用。(請注意,使用 v_cnt 是為了避免無限循環。)

CREATE OR REPLACE PROCEDURE level_0
IS
    v_cnt           NUMBER(2) := 0;
    PROCEDURE level_1a
    IS
        PROCEDURE level_2a
        IS
            PROCEDURE level_3a
            IS
            BEGIN
                DBMS_OUTPUT.PUT_LINE('........ BLOCK level_3a');
                v_cnt := v_cnt + 1;
                IF v_cnt < 2 THEN
                    level_1a;                  -- Ancestor block called
                END IF;
                DBMS_OUTPUT.PUT_LINE('........ END BLOCK level_3a');
            END level_3a;
        BEGIN
            DBMS_OUTPUT.PUT_LINE('...... BLOCK level_2a');
            level_3a;                          -- Local block called
            DBMS_OUTPUT.PUT_LINE('...... END BLOCK level_2a');
        END level_2a;
    BEGIN
        DBMS_OUTPUT.PUT_LINE('.. BLOCK level_1a');
        level_2a;                              -- Local block called
        DBMS_OUTPUT.PUT_LINE('.. END BLOCK level_1a');
    END level_1a;
BEGIN
    DBMS_OUTPUT.PUT_LINE('BLOCK level_0');
    level_1a;
    DBMS_OUTPUT.PUT_LINE('END BLOCK level_0');
END level_0;

下面是結果輸出:

BEGIN
    level_0;
END;

BLOCK level_0
.. BLOCK level_1a
...... BLOCK level_2a
........ BLOCK level_3a
.. BLOCK level_1a
...... BLOCK level_2a
........ BLOCK level_3a
........ END BLOCK level_3a
...... END BLOCK level_2a
.. END BLOCK level_1a
........ END BLOCK level_3a
...... END BLOCK level_2a
.. END BLOCK level_1a
END BLOCK level_0

調用同輩塊中聲明的子程序

以下示例顯示如何相對于從中調用子程序的本地塊、父塊或其他祖先塊來調用同輩塊中聲明的子程序。

在此示例中,存儲過程 level_1b 的可執行部分調用存儲過程 level_1a(即其同輩塊)。這兩者均是獨立存儲過程 level_0 本地的。

注意,從存儲過程 level_1b 對 level_2a 的調用或等效的 level_1a.level_2a 調用將被注釋掉,因為此調用將會導致錯誤。不允許調用同輩塊 (level_1a) 的后代子程序 (level_2a)。

CREATE OR REPLACE PROCEDURE level_0
IS
    v_cnt     NUMBER(2) := 0;
    PROCEDURE level_1a
    IS
        PROCEDURE level_2a
        IS
        BEGIN
            DBMS_OUTPUT.PUT_LINE('...... BLOCK level_2a');
            DBMS_OUTPUT.PUT_LINE('...... END BLOCK level_2a');
        END level_2a;
    BEGIN
        DBMS_OUTPUT.PUT_LINE('.. BLOCK level_1a');
        DBMS_OUTPUT.PUT_LINE('.. END BLOCK level_1a');
    END level_1a;
    PROCEDURE level_1b
    IS
    BEGIN
        DBMS_OUTPUT.PUT_LINE('.. BLOCK level_1b');
        level_1a;                              -- Sibling block called
--      level_2a;                              -- Error – Descendant of sibling block
--      level_1a.level_2a;                     -- Error - Descendant of sibling block
        DBMS_OUTPUT.PUT_LINE('.. END BLOCK level_1b');
    END level_1b;
BEGIN
    DBMS_OUTPUT.PUT_LINE('BLOCK level_0');
    level_1b;
    DBMS_OUTPUT.PUT_LINE('END BLOCK level_0');
END level_0;

下面是結果輸出:

BEGIN
    level_0;
END;

BLOCK level_0
.. BLOCK level_1b
.. BLOCK level_1a
.. END BLOCK level_1a
.. END BLOCK level_1b
END BLOCK level_0

在以下示例中,存儲過程 level_1a(即存儲過程 level_1b 的同輩,后者存儲過程 level_3b 的祖先)成功調用。

CREATE OR REPLACE PROCEDURE level_0
IS
    PROCEDURE level_1a
    IS
    BEGIN
        DBMS_OUTPUT.PUT_LINE('.. BLOCK level_1a');
        DBMS_OUTPUT.PUT_LINE('.. END BLOCK level_1a');
    END level_1a;
    PROCEDURE level_1b
    IS
        PROCEDURE level_2b
        IS
            PROCEDURE level_3b
            IS
            BEGIN
                DBMS_OUTPUT.PUT_LINE('........ BLOCK level_3b');
                level_1a;                      -- Ancestor's sibling block called
                level_0.level_1a;              -- Qualified ancestor's sibling block
                DBMS_OUTPUT.PUT_LINE('........ END BLOCK level_3b');
            END level_3b;
        BEGIN
            DBMS_OUTPUT.PUT_LINE('...... BLOCK level_2b');
            level_3b;                          -- Local block called
            DBMS_OUTPUT.PUT_LINE('...... END BLOCK level_2b');
        END level_2b;
    BEGIN
        DBMS_OUTPUT.PUT_LINE('.. BLOCK level_1b');
        level_2b;                              -- Local block called
        DBMS_OUTPUT.PUT_LINE('.. END BLOCK level_1b');
    END level_1b;
BEGIN
    DBMS_OUTPUT.PUT_LINE('BLOCK level_0');
    level_1b;
    DBMS_OUTPUT.PUT_LINE('END BLOCK level_0');
END level_0;

下面是結果輸出:

BEGIN
    level_0;
END;

BLOCK level_0
.. BLOCK level_1b
...... BLOCK level_2b
........ BLOCK level_3b
.. BLOCK level_1a
.. END BLOCK level_1a
.. BLOCK level_1a
.. END BLOCK level_1a
........ END BLOCK level_3b
...... END BLOCK level_2b
.. END BLOCK level_1b
END BLOCK level_0