DBMS_DESCRIBE

在复杂的数据库应用开发或迁移过程中,您可能需要在运行时动态获取PL/SQL函数或存储过程的详细定义,例如参数名称、数据类型和模式等。PolarDB PostgreSQL版(兼容Oracle)提供的DBMS_DESCRIBE内置包能够满足此类需求,它允许您通过程序化方式获取指定PL/SQL对象的元数据信息,并将其存入PL/SQL集合变量中,从而支持动态代码分析、开发调试工具或进行自动化验证。

功能简介

DBMS_DESCRIBE是一个高度兼容Oracle的内置包,其核心功能由DESCRIBE_PROCEDURE存储过程提供。当您调用此过程并传入一个PL/SQL对象(如函数或存储过程)的名称时,它会返回该对象所有参数的详细描述信息。

这些返回的信息被存储在多个OUT参数中,这些参数是基于VARCHAR2_TABLENUMBER_TABLE两种预定义集合类型(关联数组)的变量。通过遍历这些集合,您可以在PL/SQL代码中访问每个参数的名称、位置、模式(IN/OUT/IN OUT)、数据类型代码等属性。

  • 核心过程:DBMS_DESCRIBE.DESCRIBE_PROCEDURE

  • 输出数据类型:

    • TYPE VARCHAR2_TABLE IS TABLE OF VARCHAR2(30) INDEX BY BINARY_INTEGER;:用于存储字符串类型的参数属性,如参数名。

    • TYPE NUMBER_TABLE IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;:用于存储数字类型的参数属性,如参数位置、模式、数据类型代码等。

优势

  • 运行时代码分析:允许在PL/SQL程序中动态检查其他函数或过程的接口定义,无需访问静态数据字典。

  • 提升开发效率:可用于构建通用的开发、调试或验证工具,自动化处理与过程调用相关的任务。

适用范围

您的PolarDB PostgreSQL版(兼容Oracle)集群的修订版本需为2.0.14.17.36.0及以上

说明

您可在控制台查看内核小版本号,也可以通过SHOW polardb_version;语句查看。如未满足内核小版本要求,请升级内核小版本

DESCRIBE_PROCEDURE过程参数

DBMS_DESCRIBE.DESCRIBE_PROCEDURE(
    object_name     IN     VARCHAR2,
    reserved1       IN     VARCHAR2,
    reserved2       IN     VARCHAR2,
    overload        OUT    NUMBER_TABLE,
    position        OUT    NUMBER_TABLE,
    level           OUT    NUMBER_TABLE,
    argument_name   OUT    VARCHAR2_TABLE,
    datatype        OUT    NUMBER_TABLE,
    default_value   OUT    NUMBER_TABLE,
    in_out          OUT    NUMBER_TABLE,
    length          OUT    NUMBER_TABLE,
    precision       OUT    NUMBER_TABLE,
    scale           OUT    NUMBER_TABLE,
    radix           OUT    NUMBER_TABLE,
    spare           OUT    NUMBER_TABLE
);

参数

类型

描述

object_name

VARCHAR2

必填。要描述的函数或存储过程的名称。

  • Oracle将对象名转为大写不同,PolarDB在查找时会将此参数值转为全小写。

  • 支持procpackage.procschema.package.proc格式。

  • 当前不支持通过同义词(Synonym)指定对象。

reserved1

VARCHAR2

保留参数,必须设置为NULL或空字符串。

reserved2

VARCHAR2

保留参数,必须设置为NULL或空字符串。

overload

NUMBER_TABLE

过程的唯一编号,用于区分重载。

说明

此参数在PolarDB中暂未实现,返回值无实际意义。

position

NUMBER_TABLE

参数在列表中的位置。0代表函数的返回值。

level

NUMBER_TABLE

复合类型(如RECORD)的层级。

说明

此参数在PolarDB中暂未实现,返回值无实际意义。

argument_name

VARCHAR2_TABLE

函数或存储过程的参数名称。

datatype

NUMBER_TABLE

参数的数据类型代码。

说明

PolarDB返回的是内部数据类型的OID,而非Oracle的类型代码。

default_value

NUMBER_TABLE

参数是否有默认值。

说明

此参数在PolarDB中暂未实现,返回值无实际意义。

in_out

NUMBER_TABLE

参数模式:0表示IN1表示OUT2表示IN OUT

length

NUMBER_TABLE

字符串类型的长度约束。

说明

此参数在PolarDB中暂未实现,返回值无实际意义。

precision

NUMBER_TABLE

NUMBER类型的精度。

说明

此参数在PolarDB中暂未实现,返回值无实际意义。

scale

NUMBER_TABLE

NUMBER类型的标度。

说明

此参数在PolarDB中暂未实现,返回值无实际意义。

radix

NUMBER_TABLE

NUMBER类型的基数。

说明

此参数在PolarDB中暂未实现,返回值无实际意义。

spare

NUMBER_TABLE

保留字段。

说明

此参数在PolarDB中暂未实现,返回值无实际意义。

使用说明

以下示例将演示如何使用DBMS_DESCRIBE.DESCRIBE_PROCEDURE来获取不同类型PL/SQL对象的参数信息。

数据准备

在执行示例前,请先连接到您的PolarDB PostgreSQL版(兼容Oracle)集群并创建以下测试用的存储过程和包。

-- 1. 创建一个独立的存储过程
CREATE OR REPLACE PROCEDURE standalone_proc(
    p_varchar IN VARCHAR2,
    p_number OUT NUMBER,
    p_date IN OUT DATE
) AS
BEGIN
    NULL;
END standalone_proc;
/

-- 2. 创建一个包
CREATE OR REPLACE PACKAGE emp_pkg1 AS
    FUNCTION get_employee_name(p_emp_id IN NUMBER) RETURN VARCHAR2;
END emp_pkg1;
/
CREATE OR REPLACE PACKAGE BODY emp_pkg1 AS
    FUNCTION get_employee_name(p_emp_id IN NUMBER) RETURN VARCHAR2 IS
    BEGIN
        RETURN NULL;
    END get_employee_name;
END emp_pkg1;
/

-- 3. 创建一个Schema及带Schema的包
CREATE SCHEMA emp_schema;
CREATE OR REPLACE PACKAGE emp_schema.emp_pkg2 AS
    FUNCTION get_employee_name(p_emp_id NUMBER) RETURN VARCHAR2;
END emp_pkg2;
/
CREATE OR REPLACE PACKAGE BODY emp_schema.emp_pkg2 AS
    FUNCTION get_employee_name(p_emp_id NUMBER) RETURN VARCHAR2 IS
    BEGIN
        RETURN NULL;
    END get_employee_name;
END emp_pkg2;
/

示例1:描述一个独立的存储过程

此示例演示如何获取一个不属于任何包的独立存储过程standalone_proc的参数信息。

DECLARE
    -- 声明用于接收输出的集合变量
    v_overload     DBMS_DESCRIBE.NUMBER_TABLE;
    v_position     DBMS_DESCRIBE.NUMBER_TABLE;
    v_level        DBMS_DESCRIBE.NUMBER_TABLE;
    v_arg_name     DBMS_DESCRIBE.VARCHAR2_TABLE;
    v_datatype     DBMS_DESCRIBE.NUMBER_TABLE;
    v_default_val  DBMS_DESCRIBE.NUMBER_TABLE;
    v_in_out       DBMS_DESCRIBE.NUMBER_TABLE;
    v_length       DBMS_DESCRIBE.NUMBER_TABLE;
    v_precision    DBMS_DESCRIBE.NUMBER_TABLE;
    v_scale        DBMS_DESCRIBE.NUMBER_TABLE;
    v_radix        DBMS_DESCRIBE.NUMBER_TABLE;
    v_spare        DBMS_DESCRIBE.NUMBER_TABLE;
BEGIN
    -- 调用过程获取参数信息
    DBMS_DESCRIBE.DESCRIBE_PROCEDURE(
        object_name   => 'standalone_proc',
        reserved1     => NULL,
        reserved2     => NULL,
        overload      => v_overload,
        position      => v_position,
        level         => v_level,
        argument_name => v_arg_name,
        datatype      => v_datatype,
        default_value => v_default_val,
        in_out        => v_in_out,
        length        => v_length,
        precision     => v_precision,
        scale         => v_scale,
        radix         => v_radix,
        spare         => v_spare);

    -- 格式化并打印结果
    DBMS_OUTPUT.PUT_LINE('Position  Name         Mode');
    DBMS_OUTPUT.PUT_LINE('--------  -----------  ----');
    FOR i IN 1..v_arg_name.COUNT LOOP
        DBMS_OUTPUT.PUT_LINE(
            RPAD(v_position(i), 10) ||
            RPAD(NVL(v_arg_name(i), 'N/A'), 13) ||
            v_in_out(i)
        );
    END LOOP;
END;
/

预期输出:

Position  Name         Mode
--------  -----------  ----
1         p_varchar    0
2         p_number     1
3         p_date       2

示例2:描述包内的函数

此示例演示如何通过package_name.procedure_name的格式,获取包emp_pkg1内函数get_employee_name的参数信息。

DECLARE
    v_arg_name     DBMS_DESCRIBE.VARCHAR2_TABLE;
    v_position     DBMS_DESCRIBE.NUMBER_TABLE;
    v_in_out       DBMS_DESCRIBE.NUMBER_TABLE;
    -- 省略其他变量声明以保持简洁
    v_overload     DBMS_DESCRIBE.NUMBER_TABLE;
    v_level        DBMS_DESCRIBE.NUMBER_TABLE;
    v_datatype     DBMS_DESCRIBE.NUMBER_TABLE;
    v_default_val  DBMS_DESCRIBE.NUMBER_TABLE;
    v_length       DBMS_DESCRIBE.NUMBER_TABLE;
    v_precision    DBMS_DESCRIBE.NUMBER_TABLE;
    v_scale        DBMS_DESCRIBE.NUMBER_TABLE;
    v_radix        DBMS_DESCRIBE.NUMBER_TABLE;
    v_spare        DBMS_DESCRIBE.NUMBER_TABLE;
BEGIN
    DBMS_DESCRIBE.DESCRIBE_PROCEDURE(
        object_name   => 'emp_pkg1.get_employee_name',
        reserved1     => NULL,
        reserved2     => NULL,
        overload      => v_overload,
        position      => v_position,
        level         => v_level,
        argument_name => v_arg_name,
        datatype      => v_datatype,
        default_value => v_default_val,
        in_out        => v_in_out,
        length        => v_length,
        precision     => v_precision,
        scale         => v_scale,
        radix         => v_radix,
        spare         => v_spare
    );

    DBMS_OUTPUT.PUT_LINE('Position  Name           Mode');
    DBMS_OUTPUT.PUT_LINE('--------  -------------  ----');
    FOR i IN 1..v_arg_name.COUNT LOOP
        DBMS_OUTPUT.PUT_LINE(
            RPAD(v_position(i), 10) ||
            RPAD(NVL(v_arg_name(i), 'RETURN_VALUE'), 15) || -- 位置0是返回值
            v_in_out(i)
        );
    END LOOP;
END;
/

预期输出:

Position  Name           Mode
--------  -------------  ----
1         p_emp_id       

示例3:描述带Schema的包内函数

此示例演示如何通过schema_name.package_name.procedure_name的格式,获取特定Schema下包内函数的参数信息。

DECLARE
    v_arg_name     DBMS_DESCRIBE.VARCHAR2_TABLE;
    v_position     DBMS_DESCRIBE.NUMBER_TABLE;
    v_in_out       DBMS_DESCRIBE.NUMBER_TABLE;
    -- 省略其他变量声明以保持简洁
    v_overload     DBMS_DESCRIBE.NUMBER_TABLE;
    v_level        DBMS_DESCRIBE.NUMBER_TABLE;
    v_datatype     DBMS_DESCRIBE.NUMBER_TABLE;
    v_default_val  DBMS_DESCRIBE.NUMBER_TABLE;
    v_length       DBMS_DESCRIBE.NUMBER_TABLE;
    v_precision    DBMS_DESCRIBE.NUMBER_TABLE;
    v_scale        DBMS_DESCRIBE.NUMBER_TABLE;
    v_radix        DBMS_DESCRIBE.NUMBER_TABLE;
    v_spare        DBMS_DESCRIBE.NUMBER_TABLE;
BEGIN
    DBMS_DESCRIBE.DESCRIBE_PROCEDURE(
        object_name   => 'emp_schema.emp_pkg2.get_employee_name',
        reserved1     => NULL,
        reserved2     => NULL,
        overload      => v_overload,
        position      => v_position,
        level         => v_level,
        argument_name => v_arg_name,
        datatype      => v_datatype,
        default_value => v_default_val,
        in_out        => v_in_out,
        length        => v_length,
        precision     => v_precision,
        scale         => v_scale,
        radix         => v_radix,
        spare         => v_spare
    );

    DBMS_OUTPUT.PUT_LINE('Position  Name           Mode');
    DBMS_OUTPUT.PUT_LINE('--------  -------------  ----');
    FOR i IN 1..v_arg_name.COUNT LOOP
        DBMS_OUTPUT.PUT_LINE(
            RPAD(v_position(i), 10) ||
            RPAD(NVL(v_arg_name(i), 'RETURN_VALUE'), 15) ||
            v_in_out(i)
        );
    END LOOP;
END;
/

预期输出:

Position  Name           Mode
--------  -------------  ----
1         p_emp_id