How to pass by reference object instance in PL/SQL? - oracle

I have the following object :
-- A dummy type with one attribute
CREATE OR REPLACE TYPE example_type AS OBJECT
(
value VARCHAR2(50),
CONSTRUCTOR FUNCTION example_type (value_in VARCHAR2) RETURN SELF AS RESULT
);
/
CREATE OR REPLACE TYPE BODY example_type AS
CONSTRUCTOR FUNCTION example_type (value_in VARCHAR2) RETURN SELF AS RESULT IS
BEGIN
SELF.value := value_in;
RETURN;
END;
END;
/
And another table type of the same object :
-- A table of example_type
CREATE OR REPLACE TYPE example_table_type AS TABLE OF example_type;
/
I use these two objects as follows :
declare
Item example_type;
ItemsList example_table_type;
Value1 varchar2(15);
Value2 varchar2(15);
begin
Item := example_type(value_in => 'a');
ItemsList := example_table_type();
ItemsList.extend(1);
ItemsList(ItemsList.count) := Item;
ItemsList(1).value := 'b'; -- Replace the object value with 'b'
Value1 := Item.value; -- This returns 'a'. Why hasn't this value changed?
Value2 := ItemsList(1).value; -- This returns 'b'
end;
When running this code the value of the original object doesn't change (It's still a) While I'm expecting it to change to b.
If I run this code in C# or .Net it will change.
I was wondering why the value remains the same as I expect Oracle to "pass the object by reference".
Does anyone know what am I doing wrong?
Thanks.

I was wondering why the value remains the same as I expect Oracle to "pass the object by reference".
Does anyone know what am I doing wrong ?
Oracle does not "pass by reference".
declare
item1 example_type := example_type(value_in => 'a');
item2 example_type := item1;
begin
item1.value := 'b';
DBMS_OUTPUT.PUT_LINE(
item1.value || ' - ' || item2.value
);
end;
/
Outputs b - a as item2 is a copy of item1 and not a reference to item1.
If you want to create references to objects then store them in a table and use a REF pointer.

Related

How to have a constant field inside a object

to test the code: https://dbfiddle.uk/?rdbms=oracle_21&fiddle=1be9f3d406df287afd874ad5dfc94cfc
I would like to have constant field inside a object. I can do that with a package but I haven't succeeded to do that with an object. Why?
create type a as object(
a integer
) --OK
create type as object(
b constant integer :=1
)
ORA-02302: invalid or missing type name
The CREATE TYPE syntax does not allow it:
object_type_def ::=
datatype ::=
Nowhere does it say that you can use the CONSTANT keyword.
As an alternative, you can create an attribute and set it to a value in a user-defined constructor:
CREATE TYPE test IS OBJECT(
a NUMBER,
pi NUMBER,
CONSTRUCTOR FUNCTION test(
SELF IN OUT NOCOPY test,
a NUMBER
) RETURN SELF AS RESULT
);
with the type body:
CREATE TYPE BODY test IS
CONSTRUCTOR FUNCTION test(
SELF IN OUT NOCOPY test,
a NUMBER
) RETURN SELF AS RESULT
IS
BEGIN
SELF.a := a;
SELF.pi := 3.14159;
RETURN;
END;
END;
/
Then:
SELECT test(1).a, test(1).pi
FROM DUAL;
Outputs:
TEST(1).A
TEST(1).PI
1
3.14159
However, that does not make the value a constant and it can still be set to something else:
DECLARE
v_obj TEST := TEST(1);
BEGIN
v_obj.pi := 4; -- For large circles!
DBMS_OUTPUT.PUT_LINE( v_obj.a || ', ' || v_obj.pi );
END;
/
Outputs:
1, 4
As long as you have a policy of never changing the value then it will be the default from the user-defined constructor; if someone breaks that policy then...
db<>fiddle here

plsql printing a custom object

I am having issues in printing the custom object.
Here is my complete use case
create or replace type invoice_obt
as object (
invoice_id number
);
/
create type invoices_ntt
as table of invoice_obt;
/
create type customer_with_invoices
as object (
customer_id number,
invoices invoices_ntt
)
declare
l_customer customer_with_invoices;
l_invoices invoices_ntt := invoices_ntt();
begin
l_invoices.extend(3);
l_invoices(1) := invoice_obt(100);
l_invoices(2) := invoice_obt(200);
l_invoices(3) := invoice_obt(200);
l_customer := customer_with_invoices(1,l_invoices);
end;
How do i print out the l_customer using dbms_output please.
When you call customer_with_invoices(), you want to call the constructor, so you need to pass parameters for the object properties, even null if you want.
Also, the extend applies to l_customers.invoices, and not to l_customers.
For example, this works:
declare
l_customers customer_with_invoices := customer_with_invoices(null, null);
begin
l_customers.invoices := invoices_ntt();
l_customers.invoices.extend(1);
l_customers.invoices(1) := new invoice_obt(1);
--
dbms_output.put_line(l_customers.invoices(1).invoice_id);
end;
/
To print the content of the object with dbms_output.put_line , you need to pass it the elements, given that it does not handle objects. In your edited code, this is an example:
for i in 1 .. l_customer.invoices.count loop
dbms_output.put_line('invoice (' || i || ') = ' || l_customer.invoices(i).invoice_id);
end loop;
In a production code, you should better check if objects exist, before trying to use .count or similar.

Get the value of a record type from another package in Oracle PL/SQL

packageA Body
MEMBER FUNCTION getValue (indx IN PLS_INTEGER) RETURN VARCHAR2
IS
colData packageB.vldtnR
BEGIN
colData := packageB.getColumnData(indx);
--I want to output the id and name from the specified index
END;
packageB Header
TYPE vldtnR IS RECORD(
id PLS_INTEGER;
name VARCHAR2(50)
)
packageB Body
TYPE vldtnArryT IS TABLE OF vldtnR INDEX BY PLS_INTEGER;
vldtnArry vldnArryT;
FUNCTION getColumnData(indx IN PLS_INTEGER) IS
BEGIN
IF vldtnArry.EXISTS(indx) = TRUE THEN
RETURN vldtnArry(indx);
END IF;
END;
Code Overview:
vldtnArry pertains to vldArryT (PackageB body)
vldtnArryT pertains to vldtnR (PackageB body)
vldtnR is in PackageB header
Question:
How do I output the id and name of an index in packageA?
In colData you have a record, you can call the fields from the record directly as colData.id and colData.name.
As Maksim said, you have a colData variable which is a record type, so you can refer to the fields as colData.id and colData.name. You want to return both as a single string based on the varchar2 return type and the embedded comment, so you can do:
CREATE PACKAGE BODY packageA AS
FUNCTION getValue (indx IN PLS_INTEGER) RETURN VARCHAR2
IS
colData packageB.vldtnR;
BEGIN
colData := packageB.getColumnData(indx);
return 'Index ' || indx || ' ID ' || colData.id
|| ' name "' || colData.name || '"';
END;
END;
/
Which will return something like this, which you can obviously modify to your desired output:
Index 2 ID 13 name "Thirteen"
The code you posted has numerous other problems, hopefully from retyping it here. SQL Fiddle demo which compiles and lets you see the result for a couple of index values, bases on a manually-populated collection.

Checking if a collection element exists in Oracle

I create a simple type:
create or replace TYPE SIMPLE_TYPE AS OBJECT (ID NUMBER(38), NAME VARCHAR2(20));
Simple test:
DECLARE
TYPE ObjectList IS TABLE OF SIMPLE_TYPE;
tmp SIMPLE_TYPE := SIMPLE_TYPE(1, 'a');
o ObjectList := new ObjectList(SIMPLE_TYPE(2, 'a'), SIMPLE_TYPE(3, 'a'));
BEGIN
IF tmp.EXISTS(tmp) THEN
dbms_output.put_line('OK, exists.');
END IF;
END;
I get an exception: PLS-00302: component 'EXISTS' must be declared
But this example work:
DECLARE
TYPE NumList IS TABLE OF INTEGER;
n NumList := NumList(1,3,5,7);
BEGIN
n.DELETE(2);
IF n.EXISTS(1) THEN
dbms_output.put_line('OK, element #1 exists.');
END IF;
IF n.EXISTS(3) = FALSE THEN
dbms_output.put_line('OK, element #2 has been deleted.');
END IF;
IF n.EXISTS(99) = FALSE THEN
dbms_output.put_line('OK, element #99 does not exist at all.');
END IF;
END;
Is it possible to implement EXISTS method in SIMPLE_TYPE type?
As the documentation states, EXISTS() tests for the existence of a numbered entry in a collection. That is, array.exists(3) asserts that the third element of array is populated.
What you are trying to do in your first example is test whether the instance tmp matches an element in ObjectList. From 10g onwards we can do this using the MEMBER OF syntax. Unfortunately, in order to make that work we have to declare a MAP method, which is rather clunky and would get rather annoying if the object has a lot of attributes.
SQL> create or replace type simple_type as object
2 ( id number
3 , name varchar2(30)
4 , map member function compare return varchar2);
5 /
Type created.
SQL>
SQL> create or replace type body simple_type as
2 map member function compare return varchar2
3 is
4 return_value integer;
5 begin
6 return to_char(id, '0000000')||name;
7 end compare;
8 end;
9 /
Type body created.
SQL>
Running the example...
SQL> set serveroutput on size unlimited
SQL>
SQL> declare
2 type objectlist is table of simple_type;
3 tmp simple_type := simple_type(1, 'a');
4 o objectlist := new objectlist(simple_type(2, 'a'), simple_type(3, 'a'));
5 begin
6 if tmp MEMBER OF o then
7 dbms_output.put_line('ok, exists.');
8 else
9 dbms_output.put_line('search me');
10 end if;
11 end;
12 /
search me
PL/SQL procedure successfully completed.
SQL>
tmp SIMPLE_TYPEE := SIMPLE_TYPE(1, 'a');
…
IF tmp.EXISTS(tmp) THEN
You declare tmp as SIMPLE_TYPE, not ObjectList.
SIMPLE_TYPE is scalar type, not a collection.
Probably you wanted to check o.EXISTS instead (which is an ObjectList)?
Update:
EXISTS when applied to a collection takes an integer index as an argument and checks if the element with this index exists (not its value).
To check that SIMPLE_TYPE(1, 'a') exists in your table, you should so the following:
Create ObjectList in a dictionary:
CREATE TYPE ObjectList IS TABLE OF SIMPLE_TYPE;
Issue the SELECT query:
DECLARE
tmp SIMPLE_TYPE := SIMPLE_TYPE(1, 'a');
o ObjectList := new ObjectList(SIMPLE_TYPE(2, 'a'), SIMPLE_TYPE(3, 'a'));
myid INT;
BEGIN
SELECT 1
INTO myid
FROM TABLE(o) q
WHERE SIMPLE_TYPE(q.id, q.name) = tmp
AND rownum = 1;
IF (myid = 1) THEN
dbms_output.put_line('OK, exists.');
END IF;
END;

Oracle PL/SQL: How to print a table type

I am trying to print an table type for debugging purposes, but don't know how. I tried the following two methods, neither of which work:
dbms_output.put_line (V_TEMP_TABTYPE(1));
dbms_output.put_line (V_TEMP_TABTYPE);
The error generated is: PLS-00306: wrong number or types of arguments in call to.
So, how can I print the contents of a table type? Or is there a different way to display the contents?
The table_type and the type it references are::
create or replace TYPE MY_TYPE IS OBJECT( MyString Varchar(20)
, counter Number(9) );
create or replace TYPE MY_TABTYPE AS TABLE OF MY_TYPE;
Oracle has objects but it's ... different. Not exactly sure with your question if you want to see the values of the properties or if you want to actually see the type.
CREATE OR REPLACE TYPE MY_TYPE IS OBJECT (
MyString Varchar(20)
, counter Number(9)
);
Now run some code for it.
DECLARE
myType MY_TYPE;
BEGIN
myType := MY_TYPE('ABC123',0);
-- To see the values reference the properties
DBMS_OUTPUT.PUT_LINE(myType.mystring);
-- To see they TYPE of the OBJECT
DBMS_OUTPUT.PUT_LINE(SYS.ANYDATA.CONVERTOBJECT(myType).getTypeName());
END;
Of course you can create methods on the object to return information for you a bit easier.
CREATE OR REPLACE TYPE MY_TYPE IS OBJECT (
MyString Varchar(20)
, counter Number(9)
, MEMBER FUNCTION getType RETURN VARCHAR2
, MEMBER FUNCTION toString RETURN VARCHAR2
)
/
CREATE OR REPLACE TYPE BODY MY_TYPE
AS
MEMBER FUNCTION getTYPE RETURN VARCHAR2 IS
BEGIN
RETURN SYS.ANYDATA.CONVERTOBJECT(SELF).getTypeName();
END;
MEMBER FUNCTION toString RETURN VARCHAR2 IS
BEGIN
RETURN 'MY_TYPE('||self.mystring||','||self.counter||')';
END;
END;
/
You can call the functions on the object now makes it easier to read imo.
DECLARE
mytype MY_TYPE;
BEGIN
mytype := MY_TYPE('AGAIN','0');
DBMS_OUTPUT.PUT_LINE(mytype.toString);
DBMS_OUTPUT.PUT_LINE(mytype.getType);
END;
dbms_output.put_line(v_temp_tabtype(i).myString);

Resources