【问题标题】:Metadata regarding PL/SQL package-level record types关于 PL/SQL 包级记录类型的元数据
【发布时间】:2018-09-13 23:57:27
【问题描述】:

假设您有一个定义了 RECORD 类型的 PL/SQL 包:

CREATE OR REPLACE PACKAGE TEST_PACKAGE AS

    TYPE PERSON_RECORD_TYPE IS RECORD
    (
        first_name VARCHAR2(1000),
        last_name  VARCHAR2(1000)
    );

END;

有没有办法获得TEST_PACKAGE.PERSON_RECORD_TYPE 中包含的字段列表?例如,是否有包含此信息的 ALL_* 视图?

我对 schema 级别的记录类型不感兴趣,只对 级别的记录类型感兴趣。

【问题讨论】:

  • 从同一个包中获取字段?
  • @tbone:不一定。我想从不同的包中获取字段。
  • “获得它们”的意思是如何使用它们?你能给出一些你试图在这个其他包中完成的伪代码吗?
  • 你想用这些数据做什么?这是为了协助诸如影响分析之类的开发人员任务吗?还是某种动态编程练习?
  • @APC:我的目标是根据该记录类型中的字段自动生成一些 PL/SQL 代码。

标签: oracle reflection plsql


【解决方案1】:

如果 PERSON_RECORD_TYPE 用作某个过程或函数的参数或结果类型,则可以查询 ALL_ARGUMENTS。那里的信息有点加密(记录和集合的多级封装的层次结构被编码在 POSITION、SEQUENCE 和 DATA_LEVEL 列中),但是它是存在的。

我不认为这样的问题指向错误的架构。对于自动 PLSQL 代码生成,这是完全合法的请求,不幸的是,PLSQL 语言支持非常弱。

【讨论】:

    【解决方案2】:

    Oracle 18c 之后的解决方案

    我认为这在 18c 之前不起作用(如果我错了,请纠正我),但现在我们可以查询 ALL_PLSQL_TYPE_ATTRS 视图:

    SELECT type_name, attr_name, attr_type_name, length
    FROM all_plsql_type_attrs
    WHERE package_name = 'TEST_PACKAGE'
    ORDER BY owner, package_name, type_name, attr_no;
    

    要得到这样的东西:

    TYPE_NAME           ATTR_NAME   ATTR_TYPE_NAME  LENGTH
    ------------------------------------------------------
    PERSON_RECORD_TYPE  FIRST_NAME  VARCHAR2        1000
    PERSON_RECORD_TYPE  LAST_NAME   VARCHAR2        1000
    

    Oracle 18c 之前的解决方案

    jOOQ's code generator 在内部使用以下查询可靠地找到所有包级别 PL/SQL RECORD 类型:

    SELECT
      "x"."TYPE_OWNER",
      "x"."TYPE_NAME",
      "x"."TYPE_SUBNAME","a".subprogram_id,
      "a"."ARGUMENT_NAME" "ATTR_NAME",
      "a"."SEQUENCE" "ATTR_NO",
      "a"."TYPE_OWNER" "ATTR_TYPE_OWNER",
      nvl2("a"."TYPE_SUBNAME", "a"."TYPE_NAME", NULL) "package_name",
      COALESCE("a"."TYPE_SUBNAME", "a"."TYPE_NAME", "a"."DATA_TYPE") "ATTR_TYPE_NAME",
      "a"."DATA_LENGTH" "LENGTH",
      "a"."DATA_PRECISION" "PRECISION",
      "a"."DATA_SCALE" "SCALE"
    FROM "SYS"."ALL_ARGUMENTS" "a"
    JOIN (
      SELECT
        "a"."TYPE_OWNER",
        "a"."TYPE_NAME",
        "a"."TYPE_SUBNAME",
        MIN("a"."OWNER") KEEP (DENSE_RANK FIRST
          ORDER BY "a"."OWNER" ASC, "a"."PACKAGE_NAME" ASC, 
                   "a"."SUBPROGRAM_ID" ASC, "a"."SEQUENCE" ASC) "OWNER",
        MIN("a"."PACKAGE_NAME") KEEP (DENSE_RANK FIRST
          ORDER BY "a"."OWNER" ASC, "a"."PACKAGE_NAME" ASC, 
                   "a"."SUBPROGRAM_ID" ASC, "a"."SEQUENCE" ASC) "PACKAGE_NAME",
        MIN("a"."SUBPROGRAM_ID") KEEP (DENSE_RANK FIRST
          ORDER BY "a"."OWNER" ASC, "a"."PACKAGE_NAME" ASC, 
                   "a"."SUBPROGRAM_ID" ASC, "a"."SEQUENCE" ASC) "SUBPROGRAM_ID",
        MIN("a"."SEQUENCE") KEEP (DENSE_RANK FIRST
          ORDER BY "a"."OWNER" ASC, "a"."PACKAGE_NAME" ASC, 
                   "a"."SUBPROGRAM_ID" ASC, "a"."SEQUENCE" ASC) "SEQUENCE",
        MIN("next_sibling") KEEP (DENSE_RANK FIRST
          ORDER BY "a"."OWNER" ASC, "a"."PACKAGE_NAME" ASC, 
                   "a"."SUBPROGRAM_ID" ASC, "a"."SEQUENCE" ASC) "next_sibling",
        MIN("a"."DATA_LEVEL") KEEP (DENSE_RANK FIRST
          ORDER BY "a"."OWNER" ASC, "a"."PACKAGE_NAME" ASC, 
                   "a"."SUBPROGRAM_ID" ASC, "a"."SEQUENCE" ASC) "DATA_LEVEL"
      FROM (
        SELECT
          lead("a"."SEQUENCE", 1, 99999999) OVER (
            PARTITION BY "a"."OWNER", "a"."PACKAGE_NAME", 
                         "a"."SUBPROGRAM_ID", "a"."DATA_LEVEL"
            ORDER BY "a"."SEQUENCE" ASC
          ) "next_sibling",
          "a"."TYPE_OWNER",
          "a"."TYPE_NAME",
          "a"."TYPE_SUBNAME",
          "a"."OWNER",
          "a"."PACKAGE_NAME",
          "a"."SUBPROGRAM_ID",
          "a"."SEQUENCE",
          "a"."DATA_LEVEL",
          "a"."DATA_TYPE"
        FROM "SYS"."ALL_ARGUMENTS" "a"
        WHERE "a"."OWNER" IN ('TEST')     -- Possibly replace schema here
        ) "a"
      WHERE ("a"."TYPE_OWNER" IN ('TEST') -- Possibly replace schema here
      AND "a"."OWNER"         IN ('TEST') -- Possibly replace schema here
      AND "a"."DATA_TYPE"      = 'PL/SQL RECORD')
      GROUP BY
        "a"."TYPE_OWNER",
        "a"."TYPE_NAME",
        "a"."TYPE_SUBNAME"
      ) "x"
    ON (("a"."OWNER", "a"."PACKAGE_NAME", "a"."SUBPROGRAM_ID")
     = (("x"."OWNER", "x"."PACKAGE_NAME", "x"."SUBPROGRAM_ID"))
    AND "a"."SEQUENCE" BETWEEN "x"."SEQUENCE" AND "next_sibling"
    AND "a"."DATA_LEVEL" = ("x"."DATA_LEVEL" + 1))
    ORDER BY
      "x"."TYPE_OWNER" ASC,
      "x"."TYPE_NAME" ASC,
      "x"."TYPE_SUBNAME" ASC,
      "a"."SEQUENCE" ASC
    

    在您的情况下,结果将类似于:

    TYPE_NAME     TYPE_SUBNAME        ATTR_NAME   ATTR_TYPE_NAME   LENGTH
    ----------------------------------------------------------------------
    TEST_PACKAGE  PERSON_RECORD_TYPE  FIRST_NAME  VARCHAR2         1000
    TEST_PACKAGE  PERSON_RECORD_TYPE  LAST_NAME   VARCHAR2         1000
    

    当前限制:

    • 该查询将仅查找至少在某处被另一种类型和/或过程引用的那些类型。这是从查询中的 ALL_ARGUMENTS 字典视图继承而来的限制。
    • %ROWTYPE 类型未正确返回,因为行类型未从 TYPE_NAME / TYPE_SUBNAME 列中引用。

    更多信息在这里: https://blog.jooq.org/2016/11/08/use-jooq-to-read-write-oracle-plsql-record-types

    【讨论】:

    • ALL_PLSQL_TYPE_ATTRS 实际上早在 12.1.0.2 就可以使用
    • 事实上,根据changelog,它在 12.1.0.1 中可用。
    【解决方案3】:

    这里有一些关于从包代码中检索信息的类似问题。

    Find package global variables from data dictionary

    Get Package Methods and Parameters from Oracle

    我认为这是与第一个类似的问题。您不能通过视图访问这些字段。有 parse source text 解决方案,这很丑陋,或者您可能需要解决此问题。

    无论如何,如果你需要这个,我认为你的架构有问题。

    【讨论】:

    • 作为评论不是比回答更好吗?
    猜你喜欢
    • 2023-03-24
    • 2011-12-23
    • 2023-01-20
    • 1970-01-01
    • 2013-01-05
    • 1970-01-01
    • 2011-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多