# 嵌套的子程序
在PL/SQL块内创建的子程序，支持在匿名块、存储过程、函数、包内的存储过程及函数中，声明并创建子存储过程或子函数。
#### 注意事项
- 在ORA兼容性数据库下使用。
- 最大嵌套层数限制通过GUC参数max_subpro_nested_layers控制（默认值为3，取值范围0\~100）。若嵌套子程序中含有匿名块，匿名块不计入层数，但匿名块内嵌套子程序计入总层数。
- 嵌套子程序不支持重载、不支持使用SETOF。
- 嵌套子程序内不支持定义为自治事务，但可调用含有自治事务的存储过程或函数。
- 子函数（FUNCTION）不支持直接调用且必须包含返回值，子存储过程（PROCEDURE）不支持在表达式中调用。
- 嵌套子程序不支持PERFORM调用，动态语句中不能包含嵌套子程序。
- 当前嵌套子程序的修饰符支持如下，其余修饰符暂不支持。
  ```
  {IMMUTABLE  | STABLE  | VOLATILE }  
  {CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT }
  ```
  
- 仅支持一个限定符引用嵌套子程序或嵌套子程序的变量。
- 当子函数（FUNCTION）返回值类型为函数自定义的record类型时，无法通过subfunc().col的方式访问子函数返回值的列属性，执行时会报错。
- 嵌套子程序的声明必须位于声明部分的最后（在其他变量、游标、类型等声明完成之后再声明嵌套子程序）。
- 嵌套子程序只能在声明的函数或存储过程内部调用，外部不可使用。
- 嵌套子程序不支持debugger打断点，但支持step单步调试。
- 其余注意事项同存储过程及函数一致。
 
#### 语法格式
- 创建子存储过程语法格式：
  ```
  PROCEDURE procedure_name [ (parameters) ]
      [{IMMUTABLE  | STABLE  | VOLATILE }
       | {CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT }]
      { IS | AS }
      [ declarations ]
  BEGIN
      plsql_body
  END;
  ```
  
- 创建子函数语法：
  ```
  FUNCTION function_name  [ (parameters) ] RETURN rettype     
      [{IMMUTABLE  | STABLE  | VOLATILE }
       | {CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT }]
      { IS | AS }
      [ declarations ]
  BEGIN
      plsql_body
  END;
  ```
  在declarations部分可再定义下层的嵌套子程序。
  
- 示例：
  ```
  -- 创建一个存储过程
  CREATE OR REPLACE PROCEDURE proc_test() AS
      -- 声明并定义一个子存储过程
      PROCEDURE proc_sub() IS
      BEGIN
          dbe_output.put_line('this is subprogram');
      END;
  BEGIN
      dbe_output.put_line('this is a procedure');
      -- 执行块内调用子存储过程 
      proc_sub();
  END;
  /
  -- 外部调用存储过程
  BEGIN
      proc_test;
  END;
  /
  -- 输出结果
  this is a procedure
  this is subprogram
  ANONYMOUS BLOCK EXECUTE
  ```
  
 
#### 嵌套子程序的声明及定义规则
- 嵌套子程序不可重复声明，不可重复定义，不支持重载。
- 嵌套子程序的标识符不得与变量名，关键字同名。
- 支持先声明后定义，所有先声明的子程序必须在后续声明块中找到其定义。
 
#### 嵌套子程序的调用规则
- 嵌套子程序可调用自身实现递归调用。
- 嵌套子程序可调用上层作用域声明的子程序。
- 嵌套子程序可调用本地声明的下层子程序，但不可访问下层子程序内部的嵌套子程序。
- 嵌套子程序可调用相同作用域中先于自身声明的子程序。
示例：
```
-- 调用自身
CREATE OR REPLACE PROCEDURE proc_test(var1 int) AS
    PROCEDURE proc_sub(var2 int) IS
    BEGIN
        dbe_output.put_line('var = ' || var2);
        IF var2 > 1 THEN
            proc_sub(var2 - 1);
        END IF;
    END;
BEGIN
    proc_sub(var1);
END;
/
BEGIN
    proc_test(3);
END;
/
--输出结果
var = 3
var = 2
var = 1
--调用上层子程序
CREATE OR REPLACE PROCEDURE proc_test(var1 int) AS
    PROCEDURE procsub_1(var2 int) IS
    BEGIN
 proc_test(var2 - 1);
    END;
BEGIN
    dbe_output.put_line('proc_test var1 = ' || var1);
 IF var1 > 1 THEN
       procsub_1(var1);
 END IF;
END;
/
BEGIN
    proc_test(3);
END;
/
-- 输出结果
proc_test var1 = 3
proc_test var1 = 2
proc_test var1 = 1
-- 调用本地声明的下层子程序
CREATE OR REPLACE PROCEDURE proc_test() AS
    PROCEDURE proc_sub1 IS
        procedure proc_sub2 IS
        BEGIN
            dbe_output.put_line('--this is subprogram2 begin');
            dbe_output.put_line('--this is subprogram2 end');
        END;
    BEGIN
 dbe_output.put_line('this is subprogram1 begin');
 proc_sub2();
 dbe_output.put_line('this is subprogram1 end');
    END;
BEGIN
    dbe_output.put_line('this is a procedure begin');
    proc_sub1();
    dbe_output.put_line('this is a procedure end');
END;
/
BEGIN
    proc_test;
END;
/
-- 输出结果
this is a procedure begin
this is subprogram1 begin
--this is subprogram2 begin
--this is subprogram2 end
this is subprogram1 end
this is a procedure end
```
#### 嵌套子程序的变量
变量类型包括：基础类型，record类型、table of类型、cursor类型、varray类型等其它PL/SQL支持的变量类型。
- 可访问的变量：
  - 自身声明的变量。
  
  - 上层子程序声明的变量。
   
- 变量的访问规则：
  - 如果变量不带限定符，首先将在本程序内查找变量。如果变量名不存在，则向上层查找变量，以此类推。
  
  - 如果变量带有限定符，将在限定符的区域查找。目前仅支持一个限定符的使用。
   
 
