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.
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.
PL/SQL has three types of records: table-based, cursor-based, programmer-defined.
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.
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.
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.
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
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.
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 " .)
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.
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
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:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With