Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to view the type of a variable in PL/SQL dynamically?

This link shows how to get a procedure/function variable's type in Oracle: View Type of a variable.

It does so through the function "get_plsql_type_name":

create or replace function get_plsql_type_name
(
    p_object_name varchar2,
    p_name varchar2
) return varchar2 is
    v_type_name varchar2(4000);
begin
    select reference.name into v_type_name
    from user_identifiers declaration
    join user_identifiers reference
        on declaration.usage_id = reference.usage_context_id
        and declaration.object_name = reference.object_name
    where
        declaration.object_name = p_object_name
        and declaration.usage = 'DECLARATION'
        and reference.usage = 'REFERENCE'
        and declaration.name = p_name;

    return v_type_name;
end;
/

alter session set plscope_settings = 'IDENTIFIERS:ALL';

create or replace type my_weird_type is object
(
    a number
);

create or replace procedure test_procedure is
    var1 number;
    var2 integer;
    var3 my_weird_type;
    subtype my_subtype is pls_integer range 42 .. 43;
    var4 my_subtype;
begin
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'VAR1'));
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'VAR2'));
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'VAR3'));
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'VAR4'));
end;
/

begin
    test_procedure;
end;
/

The problem with the above method is that it is static and I need to verify the type of a variable that can be a subtype of the one declared in the procedure/function scope.

Using the above method I get the following.

Create the type and its subtype:

create or replace type my_weird_type is object
(
    a number
) NOT FINAL;

CREATE OR REPLACE TYPE my_weird_subtype UNDER my_weird_type(  
   b number
);
/

Create a table and populates it:

create table test_my_weird_type(
x my_weird_type,
y my_weird_subtype
);

INSERT INTO test_my_weird_type (x,y) VALUES (my_weird_type(100),my_weird_subtype(100,200));
COMMIT;

Function creation (it has two my_weird_type parameters, and sometimes I am going need to use its subtypes):

create or replace function test_procedure (
    inn_type my_weird_type,
    out_subtype my_weird_type
) RETURN number is
    var1 number;
    var2 integer;
begin
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'VAR1'));
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'VAR2'));
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'INN_TYPE'));
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'OUT_SUBTYPE'));

   return 1;
end;
/

The below query:

select test_procedure(x,y) from test_my_weird_type;

Gives the following output:

NUMBER
INTEGER
MY_WEIRD_TYPE
MY_WEIRD_TYPE

However, the right output is:

NUMBER
INTEGER
MY_WEIRD_TYPE
MY_WEIRD_SUBTYPE

The function needs to recognize which subtype is being used, therefore the function "get_plsql_type_name" needs to be improved. Is there a way to do it?

like image 990
Siqueira Avatar asked Oct 31 '17 13:10

Siqueira


2 Answers

You can't test the type based on the function specification but you can test the type of the passed in objects using the IS OF( type ) operator or the SYS_TYPEID function:

SQL Fiddle

Oracle 11g R2 Schema Setup:

CREATE type my_weird_type IS OBJECT
(
  a NUMBER
) NOT FINAL
/

CREATE TYPE my_weird_subtype UNDER my_weird_type
(
   b NUMBER
)
/

CREATE FUNCTION getType(
  i_type my_weird_type
) RETURN VARCHAR2
IS
BEGIN
  IF i_type IS OF( my_weird_subtype ) THEN
    RETURN 'subtype';
  ELSIF i_type IS OF( my_weird_type ) THEN
    RETURN 'type';
  ELSE
    RETURN 'other';
  END IF;
END;
/

CREATE FUNCTION getType2(
  i_type my_weird_type
) RETURN VARCHAR2
IS
  o_type USER_TYPES.TYPE_NAME%TYPE;
BEGIN
  SELECT type_name
  INTO   o_type
  FROM   user_types
  WHERE  typeid = SYS_TYPEID( i_type );

  RETURN o_type;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    RETURN NULL;
END;
/

create table test_my_weird_type(
  value my_weird_type
)
/

INSERT INTO test_my_weird_type (value)
SELECT my_weird_type(1)      FROM DUAL UNION ALL
SELECT my_weird_subtype(2,3) FROM DUAL UNION ALL
SELECT NULL                  FROM DUAL
/

Query 1:

SELECT t.value.a AS a,
       TREAT( t.value AS my_weird_subtype ).b AS b,
       getType( value ),
       getType2( value )
FROM   test_my_weird_type t

Results:

|      A |      B | GETTYPE(VALUE) |  GETTYPE2(VALUE) |
|--------|--------|----------------|------------------|
|      1 | (null) |           type |    MY_WEIRD_TYPE |
|      2 |      3 |        subtype | MY_WEIRD_SUBTYPE |
| (null) | (null) |          other |           (null) |
like image 178
MT0 Avatar answered Sep 25 '22 19:09

MT0


The function needs to recognize wich subtype is beeing used, therefore the function "get_plsql_type_name" needs to be improved. Is there a way to do it?

No. there is no way. USER_IDENTIFIERS displays information about the identifiers in the stored objects like (Packages/Procedure/Function etc) owned by the current user.

Oracle doesnot provide any data dictionary for standalone Object created under SQL scope to identify TYPE and SUBTYPE. You can at max identify them as TYPE.

for example in your case the below one will only return TYPE even thought its a SUBTYPE.

SELECT *
  FROM all_objects
 WHERE object_name = 'MY_WEIRD_SUBTYPE'

Edit:

One other way i can think of is to check if for any Type you pass has a SUPERTYPE. If so then it would imply that the type is a subtype.

You can use a query like:

SELECT 1
  FROM user_types
 WHERE type_name  = 'MY_WEIRD_SUBTYPE'
 and supertype_name is not null;

You can implement this feature in your function to check if its a SUBTYPE or not

like image 29
XING Avatar answered Sep 22 '22 19:09

XING