Oracle: Creating an array of object type in package

oracleplsql

I want to implement a stack data structure in 10gR2.

However, when I try to do:

CREATE or replace TYPE ContainerArray AS VARRAY(25) OF XDB.DBMS_XMLDOM.DOMNODE ;

I get

"schema-level type has illegal reference to XDB.DBMS_XMLDOM."

So I've figured out that's because Oracle will not let you create a schema-level type which references a package-level type.

I can fix that problem by creating the type inside of the package:

TYPE ContainerArray AS VARRAY(25) OF XDB.DBMS_XMLDOM.DOMNODE;

However, then when I try to create my object, I can't reference the package-level type:

CREATE TYPE DOMNode_Stack AS OBJECT ( 
   max_size INTEGER, 
   top      INTEGER,
   position ContainerArray,
   MEMBER PROCEDURE initialize,
   MEMBER FUNCTION full RETURN BOOLEAN,
   MEMBER FUNCTION empty RETURN BOOLEAN,
   MEMBER PROCEDURE push (n IN INTEGER),
   MEMBER PROCEDURE pop (n OUT INTEGER)
);

and I can't create the object type inside of the package because I get

"Object not supported in this context."

Any thoughts on how I can solve this issue?

Best Answer

sql level objects like an OBJECT can only reference SQL types in their attributes.

if you see the package spec:

TYPE DOMNode IS RECORD (id RAW(13));

so you could create an sql array of

SQL> CREATE or replace TYPE ContainerArray AS VARRAY(25) OF raw(13);
  2  /

Type created.

then in your object put

position ContainerArray

which will work. when you reference it, just assign a local variable v_dom xml_dom.domnode; begin v_dom.id := your array index value that you're dealing with at the time;

be advised that on any Oracle upgrade you need to verify that the structure of DOMNODE hasn't changed.

edit: here's a sample:

SQL> set serverout on
SQL> create or replace type containerarray as varray(25) of raw(13);
  2  /

Type created.

SQL> create type domnode_stack as object (
  2     position containerarray,
  3     member procedure initialize,
  4     member procedure print_node_names
  5  );
  6  /

Type created.

SQL> CREATE TYPE body DOMNode_Stack
  2  as
  3    member procedure initialize
  4    is
  5      v_dom dbms_xmldom.DOMNode;
  6      v_doc dbms_xmldom.domdocument;
  7      v_nl  dbms_xmldom.DOMNodeList;
  8    begin
  9      position := containerarray();
 10      v_doc := dbms_xmldom.newDOMDocument(xmltype('<root><foo>test</foo><foo2>test2</foo2></root>')); -- just some summy data.
 11      v_nl  := dbms_xmldom.getElementsByTagName(v_doc, '*');
 12      for idx in 0..dbms_xmldom.getLength(v_nl)-1 loop
 13        v_dom := DBMS_XMLDOM.item(v_nl, idx);
 14        position.extend;
 15        position(position.last) := v_dom.id;
 16      end loop;
 17    end;
 18
 19    member procedure print_node_names
 20    is
 21      v_dom dbms_xmldom.DOMNode;
 22    begin
 23      for idx in 1..position.count
 24      loop
 25        v_dom.id := position(idx);
 26        dbms_output.put_line(dbms_xmldom.getnodename(v_dom)||'='||dbms_xmldom.getnodevalue(dbms_xmldom.getfirstchild(v_dom)));
 27      end loop;
 28    end;
 29  end;
 30  /

Type body created.

SQL> show errors type body DOMNode_Stack
No errors.
SQL> declare
  2    o_domnode DOMNode_Stack := DOMNode_Stack(null);
  3  begin
  4    o_domnode.initialize();
  5    o_domnode.print_node_names();
  6  end;
  7  /
root=
foo=test
foo2=test2

PL/SQL procedure successfully completed.

the important bit being:

to assign from DBMS_XMLDOM to the sql type we take v_dom.id.

13 v_dom := DBMS_XMLDOM.item(v_nl, idx); 14 position.extend; 15 position(position.last) := v_dom.id;

and when reversing:

23 for idx in 1..position.count 24 loop 25 v_dom.id := position(idx);

ie assign the ID part (not the record itself..that would error out) back.