PL/SQL Attributes in User-Defined Types

In the previous post I wrote about non-persistable types – a feature that was added in Oracle 18c and provides a declarative way to prohibit the use of a schema-level user-defined type in the data model and to limit it to the code only.
Oracle 21c adds another enhancement, and allows to use unique PL/SQL predefined types (i.e., BOOLEAN, PLS_INTEGER/BINARY_INTEGER and their predefined subtypes) as attributes of non-persistable schema-level types.

To demonstrate it I’ll change COLOR_T – the type I used in the examples in the previous post. Let’s declare the r, g and b attributes as BINARY_INTEGER instead of NUMBER(3), and add a BOOLEAN attribute – is_favorite.
Trying to do it for a persistable type (in version 21 or before) fails with the PLS-00530 error:

SQL> create type color_t as object (
  2    r binary_integer,
  3    g binary_integer,
  4    b binary_integer,
  5    is_favorite boolean,
  6    member function hex_code return varchar2
  7  )
  8  /

Warning: Type created with compilation errors.

SQL> sho err
Errors for TYPE COLOR_T:

LINE/COL ERROR
-------- -----------------------------------------------------------------
2/5      PLS-00530: Illegal type used for object type attribute:
         'BINARY_INTEGER'.

3/5      PLS-00530: Illegal type used for object type attribute:
         'BINARY_INTEGER'.

4/5      PLS-00530: Illegal type used for object type attribute:
         'BINARY_INTEGER'.

5/15     PLS-00530: Illegal type used for object type attribute:
         'BOOLEAN'.

And we’ll get the same error if we try to do it for a non-persistable type in versions before 21c:

19c> create type color_t as object (
  2    r binary_integer,
  3    g binary_integer,
  4    b binary_integer,
  5    is_favorite boolean,
  6    member function hex_code return varchar2
  7  )
  8  not persistable
  9  /

Warning: Type created with compilation errors.

19c> show err
Errors for TYPE COLOR_T:

LINE/COL ERROR
-------- -----------------------------------------------------------------
2/5      PLS-00530: Illegal type used for object type attribute:
         'BINARY_INTEGER'.

3/5      PLS-00530: Illegal type used for object type attribute:
         'BINARY_INTEGER'.

4/5      PLS-00530: Illegal type used for object type attribute:
         'BINARY_INTEGER'.

5/15     PLS-00530: Illegal type used for object type attribute:
         'BOOLEAN'.

In Oracle 21c it succeeds:

21c> create type color_t as object (
  2    r binary_integer,
  3    g binary_integer,
  4    b binary_integer,
  5    is_favorite boolean,
  6    member function hex_code return varchar2
  7  )
  8  not persistable
  9  /

Type created.

21c> create type body color_t as
  2    member function hex_code return varchar2
  3    is
  4    begin
  5      return '#' || to_char(self.r, 'fm0x') ||
  6                    to_char(self.g, 'fm0x') ||
  7                    to_char(self.b, 'fm0x');
  8    end hex_code;
  9  end color_t;
 10  /

Type body created.

Using Non-Persistable Types

Non-persistable types, in general, can be used in both PL/SQL and SQL.
But if the type contains unique PL/SQL attributes its usage is restricted to PL/SQL only.

21c> create function f (i_color in color_t) return varchar2 as
  2  begin
  3      if i_color.is_favorite then
  4          return i_color.hex_code;
  5      else
  6          return 'I do not like this color';
  7      end if;
  8  end;
  9  /

Function created.

21c> declare
  2      l_yellow color_t := color_t(255, 255, 0, false);
  3      l_navy   color_t := color_t(0, 0, 128, true);
  4  begin
  5      dbms_output.put_line('Yellow: ' || f(l_yellow));
  6      dbms_output.put_line('Navy:   ' || f(l_navy));
  7  end;
  8  /
Yellow: I do not like this color
Navy:   #000080

PL/SQL procedure successfully completed.

If we try to use it in a SQL statement, we get the ORA-39972 error:

21c> select count(*) from color_tt();
select count(*) from color_tt()
                     *
ERROR at line 1:
ORA-39972: cannot use an object type with PL/SQL unique attributes in a SQL statement

Using such types in SQL is not supported even where unique PL/SQL types are allowed:

21c> declare
  2      l_yellow color_t := color_t(255, 255, 0, false);
  3      l_text varchar2(100);
  4  begin
  5      select f(l_yellow) into l_text from dual;
  6      dbms_output.put_line('Yellow: ' || l_text);
  7  end f;
  8  /
    select f(l_yellow) into l_text from dual;
           *
ERROR at line 5:
ORA-06550: line 5, column 12:
PL/SQL: ORA-39972: cannot use an object type with PL/SQL unique attributes in a SQL statement
ORA-06550: line 5, column 5:
PL/SQL: SQL Statement ignored

Compare the previous example with the following one – showing that binding unique PL/SQL types to SQL is allowed (as of Oracle 12.1).

21c> create or replace function f (i_bool in boolean) return varchar2 as
  2  begin
  3      return case i_bool when true then 'Yes' else 'No' end;
  4  end f;
  5  /

Function created.

21c> declare
  2      l_true boolean := true;
  3      l_text varchar2(100);
  4  begin
  5      select f(l_true)
  6      into   l_text
  7      from   dual;
  8      dbms_output.put_line('Do I like this color? ' || l_text);
  9  end;
 10  /
Do I like this color? Yes

PL/SQL procedure successfully completed.

Can any predefined PL/SQL type be used?

All the predefined PL/SQL types and subtypes can be used when creating non-persistable collection types (i.e., varrays and nested tables).
However, when creating non-persistable object types, we cannot use subtypes that are restricted with the NOT NULL constraint (like NATURALN or SIMPLE_INTEGER, for example).
The reason is that it is impossible to define attributes of object types as NOT NULL.

21c> create type simple_int_tt as
  2    table of (simple_integer)
  3    not persistable
  4  /

Type created.

21c> create type object_t as object (
  2    a binary_integer,
  3    b  simple_integer,
  4    c boolean
  5  )
  6  not persistable
  7  /

Warning: Type created with compilation errors.

21c> show err
Errors for TYPE OBJECT_T:

LINE/COL ERROR
-------- -----------------------------------------------------------------
3/3      PLS-00218: a variable declared NOT NULL must have an
         initialization assignment

I think the error message here is a bit misleading, because there is no way to define an initialization assignment

Leave a Reply

Your email address will not be published. Required fields are marked *