Search code examples
oracle-databaseplsqlforward-declarationhoisting

Define a record type in PL/SQL block that references a collection of itself


How to define a record type in PL/SQL anonymous block that contains a property that is a collection of itself? Look at the following example:

DECLARE
    type t_item is record (
        name varchar2(64),
        children t_items              -- referencing t_items type
    );
    type t_items is table of t_item;  -- referencing t_item type

BEGIN
    -- script code
END

PL/SQL has no type hoisting so Oracle engine raises an exception:

PLS-00498: illegal use of a type before its declaration

How to define a record t_item that contains a table of t_item in its property children?


Solution

  • You can use objects defined in the SQL Scope using inheritance:

    SQL Fiddle

    Oracle 11g R2 Schema Setup:

    CREATE TYPE abstract_item IS OBJECT (
      name VARCHAR2(64)
    ) NOT FINAL NOT INSTANTIABLE
    /
    
    CREATE TYPE t_items IS TABLE OF abstract_item
    /
    
    CREATE TYPE t_item UNDER abstract_item (
      children t_items
    ) INSTANTIABLE
    /
    

    Query 1:

    SELECT t_item(
             '1',
             t_items(
               t_item( '1.1', t_items() ),
               t_item(
                 '1.2',
                 t_items(
                   t_item( '1.2.1', null )
                 )
               ),
               t_item( '1.3', null )
             )
           )
    FROM   DUAL
    

    Results: (SQLFiddle doesn't display it nicely - but it runs without errors)

    | T_ITEM('1',T_ITEMS(T_ITEM('1.1',T_ITEMS()),T_ITEM('1.2',T_ITEMS(T_ITEM('1.2.1',NULL))),T_ITEM('1.3',NULL))) |
    |-------------------------------------------------------------------------------------------------------------|
    |                                                                                  oracle.sql.STRUCT@2a094aab |
    

    You could use a similar declaration in PL/SQL:

    DECLARE
      items t_item;
    BEGIN
      items = t_item( 'Item Name', t_items( /* ... */ ) );
    END;
    /