通过指定子程序的名称和任何实参,调用子程序,这与调用独立存储过程或函数的方式相同。

可以使用一个或多个限定符来调用子程序,也可以不使用限定符来调用子程序,这些限定符是父级子程序的名称或带标记的匿名块,这些块构成了从中声明子程序的祖先层次结构。

该调用指定为点分隔的限定符列表,以子程序名称及其任何参数结尾,如下所示:

[[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