.


:




:

































 

 

 

 


Processing an ANYDATA value




I'll now show you what all this means, first with a program intended to return a string version of any variable's contents. For now, this program deals only with numbers, strings, dates, objects, and REFs, but you could extend it to almost any other datatype.

/* File on web: printany.fun */ 1 CREATE OR REPLACE FUNCTION printany (adata IN ANYDATA) 2 RETURN VARCHAR2 3 AS 4 aType ANYTYPE; 5 retval VARCHAR2(32767); 6 result_code PLS_INTEGER; 7 BEGIN 8 CASE adata.GetType(aType) 9 WHEN DBMS_TYPES.TYPECODE_NUMBER THEN 10 RETURN 'NUMBER: ' || TO_CHAR(adata.AccessNumber); 11 WHEN DBMS_TYPES.TYPECODE_VARCHAR2 THEN 12 RETURN 'VARCHAR2: ' || adata.AccessVarchar2; 13 WHEN DBMS_TYPES.TYPECODE_CHAR THEN 14 RETURN 'CHAR: ' || RTRIM(adata.AccessChar); 15 WHEN DBMS_TYPES.TYPECODE_DATE THEN 16 RETURN 'DATE: ' || TO_CHAR(adata.AccessDate, 'YYYY-MM-DD hh24:mi:ss'); 17 WHEN DBMS_TYPES.TYPECODE_OBJECT THEN 18 EXECUTE IMMEDIATE 'DECLARE ' || 19 ' myobj ' || adata.GetTypeName || '; ' || 20 ' myad anydata:=:ad; ' || 21 'BEGIN ' || 22 ':res:= myad.GetObject(myobj); ' || 23 ':ret:= myobj.print(); ' || 24 'END;' 25 USING IN adata, OUT result_code, OUT retval; 26 retval:= adata.GetTypeName || ': ' || retval; 27 WHEN DBMS_TYPES.TYPECODE_REF THEN 28 EXECUTE IMMEDIATE 'DECLARE ' || 29 ' myref ' || adata.GetTypeName || '; ' || 30 ' myobj ' || SUBSTR(adata.GetTypeName, 31 INSTR(adata.GetTypeName, ' ')) || '; ' || 32 ' myad anydata:=:ad; ' || 33 'BEGIN ' || 34 ':res:= myad.GetREF(myref); ' || 35 ' UTL_REF.SELECT_OBJECT(myref, myobj);' || 36 ':ret:= myobj.print(); ' || 37 'END;' 38 USING IN adata, OUT result_code, OUT retval; 39 retval:= adata.GetTypeName || ': ' || retval; 40 ELSE 41 retval:= '<data of type ' || adata.GetTypeName ||'>'; 42 END CASE; 43 44 RETURN retval; 45 46 EXCEPTION 47 WHEN OTHERS 48 THEN 49 IF INSTR(SQLERRM, 'component ''PRINT'' must be declared') > 0 50 THEN 51 RETURN adata.GetTypeName || ': <no print() function>'; 52 ELSE 53 RETURN 'Error: ' || SQLERRM; 54 END IF; 55 END;

Here are just a few highlights.

 

Line 1

This function receives a single argument of type ANYDATA. In order to call the function, you must convert your variable to an ANYDATA; for example:

DBMS_OUTPUT.PUT_LINE(printany(ANYDATA.ConvertNumber(3.14159)));

Explicit conversions are sort of the dark side of ANYDATA.

 

Line 5

In cases where I need a temporary variable to hold the result, I assume that 32K will be big enough. Remember that PL/SQL dynamically allocates memory for large VARCHAR2s, so it won't be a memory pig unless required.

 

Line 6

The value of result_code (see lines 25 and 38) is irrelevant for the operations in this example, but is required by the ANYDATA API.

 

Line 8

The ANYDATA type includes a method called GetType that returns a code corresponding to the datatype. Here is its specification:

MEMBER FUNCTION ANYDATA.GetType (OUT NOCOPY ANYTYPE) RETURN typecode_integer ;

To use this method, though, you have to declare an ANYTYPE variable into which Oracle will store detailed information about the type that you've encapsulated.

 

Lines 9, 11, 13, 15, 17, 27

These expressions rely on the constants that Oracle provides in the built-in package DBMS_TYPES.

 

Lines 10, 12, 14, 16

These statements use the ANYDATA.ConvertNNN member functions introduced in Oracle9i Release 2. In Release 1, you can use the GetNNN member procedures for a similar result, although they require the use of a temporary local variable.

 

Lines 18-25

To get an object to print itself without doing a lot of data dictionary contortions, this little dynamic anonymous block will construct an object of the correct type and invoke its print() member method.

 

Lines 28-38

The point of this is to dereference the pointer and return the referenced object's content. Well, it will work if there's a print().

 

Lines 49-51

In the event that I'm trying to print an object with no print member method, the compiler will return an error at runtime that I can detect in this fashion. In this case the code will just punt and return a generic message.

Let's take a look at some simple invocations and what this returns:

DECLARE achar CHAR(20):= 'fixed-length string'; abook book_t:= NEW book_t(id => 12345, title => 'my book', pages => 100); sref REF serial_t; asub subject_t:= subject_t('The World', NULL);BEGIN DBMS_OUTPUT.PUT_LINE(printany(ANYDATA.ConvertNumber(3.141592654))); DBMS_OUTPUT.PUT_LINE(printany(ANYDATA.ConvertChar(achar))); DBMS_OUTPUT.PUT_LINE(printany(ANYDATA.ConvertObject(abook))); DBMS_OUTPUT.PUT_LINE(printany(ANYDATA.ConvertObject(asub))); SELECT TREAT(REF(c) AS REF serial_t) INTO sref FROM catalog_items c WHERE title = 'Time'; DBMS_OUTPUT.PUT_LINE(printany(ANYDATA.ConvertRef(sref)));END;

This yields:

NUMBER: 3.141592654CHAR: fixed-length stringSCOTT.BOOK_T: id=12345; title=my book; publication_date=; isbn=; pages=100SCOTT.SUBJECT_T: <no print() function>REF SCOTT.SERIAL_T: id=10004; title=Time; publication_date=; issn=0040-781X;open_or_closed=Open

As you can see, using ANYDATA is not as convenient as true inheritance hierarchies because ANYDATA requires explicit conversions. On the other hand, it does make possible the creation of a table column or object attribute that will hold any type of data.[10]

[10] As of this writing, it is impossible to store in a table an ANYDATA encapsulating an object that has evolved or that is part of a type hierarchy.





:


: 2015-10-01; !; : 638 |


:

:

, .
==> ...

1613 - | 1534 -


© 2015-2024 lektsii.org - -

: 0.026 .