Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Metadata regarding PL/SQL package-level record types

Suppose you have a PL/SQL package with a RECORD type defined:

CREATE OR REPLACE PACKAGE TEST_PACKAGE AS

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

END;

Is there any way to obtain a list of fields contained within TEST_PACKAGE.PERSON_RECORD_TYPE? For example, are there any ALL_* views with this information?

I am not interested in schema-level record types, only package-level record types.

like image 489
Adam Paynter Avatar asked Jan 10 '12 13:01

Adam Paynter


People also ask

What is metadata in Plsql?

Oracle Database provides information about all of the tables, views, columns, and procedures in a database. This information about information is known as metadata. It is stored in two locations: data dictionary tables (accessed via built-in views) and a metadata registry.

What are the types of PL SQL records?

PL/SQL has three types of records: table-based, cursor-based, programmer-defined.

Which object provides metadata about API?

The Metadata API makes use of XML (Extensible Markup Language) and XSLT (Extensible Stylesheet Language Transformation). The Metadata API represents object metadata as XML because it is a universal format that can be easily parsed and transformed.

What are the components of package in PL SQL?

Packages consist of two main components: the package specification and the package body. The package specification is the public interface, comprising the elements that can be referenced outside of the package. A package specification is created by executing the CREATE PACKAGE statement.

What are packages in PL/SQL?

Packages are schema objects that groups logically related PL/SQL types, variables, and subprograms. A package will have two mandatory parts −. Package specification. Package body or definition.

What is record type in PL/SQL?

Oracle PL/SQL Records Type with Examples What is Record Type? A Record type is a complex data type which allows the programmer to create a new data type with the desired column structure. It groups one or more column to form a new data type

What is a composite data type in PL/SQL?

PL/SQL lets you define two kinds of composite data types: collection and record. A composite data type stores values that have internal components. You can pass entire composite variables to subprograms as parameters, and you can access internal components of composite variables individually. Internal components can be either scalar or composite.

What is a local type in PL/SQL?

A collection type defined in a PL/SQL block is a local type. It is available only in the block, and is stored in the database only if the block is in a standalone or package subprogram. (Standalone and package subprograms are explained in " Nested, Package, and Standalone Subprograms " .)


2 Answers

If PERSON_RECORD_TYPE is used as argument or result type of some procedure or function, you can query ALL_ARGUMENTS. The information is little bit encrypted there (the hierarchy of multilevel encapsulation of records and collections is encoded in POSITION,SEQUENCE and DATA_LEVEL columns), however it is present.

I don't think such a question points to wrong architecture. For automatic PLSQL code generation this is completely legitimate request, unfortunately with very weak PLSQL language support.

like image 138
Tomáš Záluský Avatar answered Nov 01 '22 20:11

Tomáš Záluský


Solution after Oracle 18c

I think this didn't work prior to 18c (correct me if I'm wrong), but now we can query the ALL_PLSQL_TYPE_ATTRS view:

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;

To get something like this:

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

Solution prior to Oracle 18c

jOOQ's code generator internally uses the following query to reliably find all package level PL/SQL RECORD types:

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

In your case, the result will be something like:

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

Current limitations:

  • The query will find only those types that are referenced by at least one other type and/or procedure somewhere. This is a limitation inherited from the ALL_ARGUMENTS dictionary view in the query.
  • %ROWTYPE types are not returned correctly because the row type is not referenced from the TYPE_NAME / TYPE_SUBNAME columns.

More information here: https://blog.jooq.org/2016/11/08/use-jooq-to-read-write-oracle-plsql-record-types

like image 45
Lukas Eder Avatar answered Nov 01 '22 20:11

Lukas Eder