hHey,
I want to set the value of the assignment value in the method of the node_ty. However I get the error that navigation through Ref variables is not allowed or the typ I want to assign is not correct. So I don't really know how to do that. Could you help me with that?
CREATE OR REPLACE TYPE property_ty AS OBJECT(
name VARCHAR2(100)
);
/
CREATE OR REPLACE TYPE assignment_ty AS OBJECT(
value VARCHAR2(100),
property REF property_ty
);
/
CREATE OR REPLACE TYPE property_tty AS TABLE OF property_ty;
/
CREATE OR REPLACE TYPE node_ty AS OBJECT(
x NUMBER,
y NUMBER,
assignment REF assignment_ty,
property property_tty,
MEMBER PROCEDURE set_assignment (name VARCHAR2, value VARCHAR2)
);
/
CREATE OR REPLACE TYPE BODY node_ty AS
MEMBER PROCEDURE set_assignment (name VARCHAR2, value VARCHAR2) AS
prop_not_added EXCEPTION;
prop_exists BOOLEAN := FALSE;
assig_test assignment_ty;
prop property_ty;
BEGIN
FOR i IN 1..self.property.COUNT
LOOP
IF property(i).name = name THEN
prop.name :=name;
assig_test.value :=value;
assig_test.property := prop;
assignment := assig_test;
prop_exists := TRUE;
END IF;
END LOOP;
IF prop_exists = FALSE THEN
RAISE prop_not_added;
END IF;
EXCEPTION
WHEN prop_not_added THEN
DBMS_OUTPUT.PUT_LINE('Property cannot be set');
END;
END;
/
You have to create the necessary setup and then query the data from the setup.I am giving only the setup which is different from yours.The remaining you can keep it as same as your setup
create table t00 of property_ty;
insert into t00 values('san');
create table t0 of assignment_ty;
insert into t0 select 'xxx',REF(p) from t00 p;
CREATE OR REPLACE TYPE node_ty AS OBJECT(
x NUMBER,
y NUMBER,
assignment REF assignment_ty,
property property_tty,
MEMBER PROCEDURE set_assignment (p_name VARCHAR2, p_value VARCHAR2)
);
create or replace TYPE BODY node_ty AS
MEMBER PROCEDURE set_assignment (p_name VARCHAR2, p_value VARCHAR2) AS
prop_not_added EXCEPTION;
prop_exists BOOLEAN := FALSE;
assig_test REF assignment_ty;
prop1 property_ty;
prop REF property_ty;
BEGIN
FOR i IN 1..self.property.COUNT
LOOP
IF property(i).name = p_name THEN
SELECT REF(a) INTO assignment FROM t0 a,t00 b where a.value=p_value and b.name=p_name
and ref(b)=property;
prop_exists := TRUE;
END IF;
END LOOP;
IF prop_exists = FALSE THEN
RAISE prop_not_added;
END IF;
EXCEPTION
WHEN prop_not_added THEN
DBMS_OUTPUT.PUT_LINE('Property cannot be set');
END;
END;
Related
I am working on a package in PL/SQL.
This is my spec:
TYPE outrec IS RECORD(
tw_m_id NUMBER,
tw_m_dealer_id NUMBER,
tw_number NUMBER,
check_uid NUMBER);
TYPE outrecset IS TABLE OF outrec;
FUNCTION report
(
p_watermark IN NUMBER,
p_param IN NUMBER,
p_index IN NUMBER
) RETURN outrecset
PIPELINED;
This is my body:
FUNCTION func
(
p_watermark => p_watermark,
p_param => p_param,
p_index => p_index
)
RETURN outrecset
PIPELINED IS
temp outrec;
BEGIN
before_report(p_watermark => p_watermark,
p_param => p_param,
p_index => p_index);
FOR c_rec IN (SELECT tw_m_id,
tw_m_dealer_id,
tw_number,
package_name.somefunction(tw_number) AS check_uid
FROM table1
JOIN table2 rk ON id1 = rk.id2
WHERE 1 = 1
AND id1 = rk.id2
AND id1 = p_param)
LOOP
temp.tw_m_tw_rechnungskopf_id := c_rec.tw_m_tw_rechnungskopf_id;
temp.tw_m_haendler_id_rechnung := c_rec.tw_m_haendler_id_rechnung;
temp.check_uid := c_rec.check_uid;
PIPE ROW(temp);
END LOOP;
END;
I am trying to get value from package_name.somefunction(tw_number) AS check_uid. The problem is that somefunction returns BOOLEAN value.
When I set check_uid to BOOLEAN I get Error: PLS-00382: expression is of the wrong type because of course SQL doesn't support BOOLEAN. I tried :
CASE
WHEN package_name.somefunction(tw_number) THEN true
else false
END as check_uid
inside SELECT then I get Error: PL/SQL: ORA-00920: invalid relational operator.
Can someone tell me how to do this PL/SQL is not my strongest side :(
EDIT: I can't change somefunction to return for an example varchar2 it needs to stay the way it is
Write a simple wrapper function to convert the PL/SQL BOOLEAN to a NUMBER data type that is valid in SQL and expected by your record type and you can move the function call outside the SQL.
CREATE FUNCTION MAP_BOOLEAN( truthy IN BOOLEAN ) RETURN NUMBER DETERMINISTIC
IS
BEGIN
RETURN CASE truthy
WHEN TRUE THEN 1
WHEN FALSE THEN 0
ELSE NULL
END;
END map_boolean;
/
So your specification would be:
CREATE PACKAGE package_name IS
TYPE outrec IS RECORD(
tw_m_id NUMBER,
tw_m_dealer_id NUMBER,
tw_number NUMBER,
check_uid NUMBER
);
TYPE outrecset IS TABLE OF outrec;
-- Note: This may be in another package but is here for convenience.
FUNCTION somefunction(value IN NUMBER) RETURN BOOLEAN;
FUNCTION report
(
p_watermark IN NUMBER,
p_param IN NUMBER,
p_index IN NUMBER
) RETURN outrecset PIPELINED;
END;
/
and the corresponding body would be:
CREATE PACKAGE BODY package_name IS
FUNCTION somefunction(value IN NUMBER) RETURN BOOLEAN
IS
BEGIN
RETURN TRUE;
END;
PROCEDURE before_report(
p_watermark IN NUMBER,
p_param IN NUMBER,
p_index IN NUMBER
)
IS
BEGIN
NULL;
END;
FUNCTION report(
p_watermark IN NUMBER,
p_param IN NUMBER,
p_index IN NUMBER
) RETURN outrecset PIPELINED
IS
temp outrec;
BEGIN
before_report(
p_watermark => p_watermark,
p_param => p_param,
p_index => p_index
);
FOR c_rec IN (
SELECT tw_m_id,
tw_m_dealer_id,
tw_number
FROM table1
JOIN table2 rk ON id1 = rk.id2
WHERE id1 = p_param
)
LOOP
temp.tw_m_id := c_rec.tw_m_id;
temp.tw_m_dealer_id := c_rec.tw_m_dealer_id;
temp.check_uid := MAP_BOOLEAN(
PACKAGE_NAME.SOMEFUNCTION( c_rec.tw_number )
);
PIPE ROW(temp);
END LOOP;
END;
END;
/
(Note: you also need to update the cursor loop as the values you were selecting did not match the fields of the record.)
db<>fiddle here
Typically you would create an overloaded function in your package that returns 1/0 or Y/N. But since you have no access to the package you can use an inline function in your sql query to do this for you.
create or replace function func (parameter_i VARCHAR2) RETURN BOOLEAN
AS
BEGIN
return true;
END;
/
WITH
FUNCTION func_yn(parameter_i VARCHAR2)
RETURN NUMBER
IS
l_return_value BOOLEAN;
BEGIN
l_return_value :=func(parameter_i => parameter_i);
RETURN CASE l_return_value WHEN TRUE THEN 1 WHEN FALSE THEN 0 END;
END func_yn;
SELECT
func_yn('test')
FROM dual;
here's my code
here's how I defined my procedure with the input and output params and the select I want to make to match the values from a request to input params and if there's a match to data from the table then get the output values.
CREATE OR REPLACE PROCEDURE WS_STOCK_RESERVATION_CATEGORY
(
P_CONTEXT IN VARCHAR2,
P_SERIAL_FLAG IN VARCHAR2,
P_RESERVATION_CODE IN VARCHAR2,
P_SERIAL_NUMBER IN NUMBER ,
P_DEALER_CODE IN VARCHAR2,
P_WAREHOUSE_CODE IN VARCHAR2,
P_ACTION IN VARCHAR2,
P_ORDER_ID IN VARCHAR2,
P_OA_ID IN VARCHAR2,
P_COMPONENT_ID IN VARCHAR2,
P_RESERVATION_ID IN NUMBER,
ITEM_CODE OUT VARCHAR2,
RESERVATION_ID OUT NUMBER,
RETURN_MESSAGE OUT VARCHAR2
) AS
BEGIN
SELECT * FROM TEST362
WHERE P_CONTEXT = P_CONTEXT
AND P_SERIAL_FLAG = P_SERIAL_FLAG
AND P_RESERVATION_CODE = P_RESERVATION_CODE
AND P_SERIAL_NUMBER = P_SERIAL_NUMBER
AND P_DEALER_CODE =P_DEALER_CODE
AND P_WAREHOUSE_CODE = P_WAREHOUSE_CODE
AND P_ACTION = P_ACTION
AND P_ORDER_ID = P_ORDER_ID
AND P_OA_ID = P_OA_ID
AND P_COMPONENT_ID = P_COMPONENT_ID
AND P_RESERVATION_ID = P_RESERVATION_ID
ITEM_CODE := 'CiprianItemCode';
RESERVATION_ID := 2000;
RETURN_MESSAGE := 'SperSaMeargaWS362';
END WS_STOCK_RESERVATION_CATEGORY;
I don't fully understand the purpose of procedures for PL/SQL object types. From reading online, the impression I got was that functions should not modify the data and should only return a value, instead procedures should be used, however it appears that for object types, changes in a procedure don't propagate outside the local context:
CREATE OR REPLACE TYPE TestType FORCE
AS OBJECT
(
TestValue NUMBER
, CONSTRUCTOR FUNCTION TestType
RETURN SELF AS RESULT
, MEMBER PROCEDURE IncrementValueAsProcedure
, MEMBER FUNCTION IncrementValueAsFunction
RETURN TestType
)
/
CREATE OR REPLACE TYPE BODY TestType
AS
CONSTRUCTOR FUNCTION TestType
RETURN SELF AS RESULT
IS
BEGIN
SELF.TestValue := 0;
RETURN;
END;
MEMBER PROCEDURE IncrementValueAsProcedure
IS
MyType TestType := SELF;
BEGIN
MyType.TestValue := TestValue + 1;
END;
MEMBER FUNCTION IncrementValueAsFunction
RETURN TestType
IS
MyType TestType := SELF;
BEGIN
MyType.TestValue := TestValue + 1;
RETURN MyType;
END;
END;
/
DECLARE
MyTest TestType;
BEGIN
MyTest := TestType();
DBMS_OUTPUT.PUT_LINE(MyTest.TestValue);
MyTest.IncrementValueAsProcedure();
DBMS_OUTPUT.PUT_LINE(MyTest.TestValue);
MyTest := MyTest.IncrementValueAsFunction();
DBMS_OUTPUT.PUT_LINE(MyTest.TestValue);
END;
This gives the output:
0
0
1
So the change in the update procedure hasn't been picked up. What am I doing wrong?
Oh, worked this out.
MEMBER PROCEDURE IncrementValueAsProcedure
IS
MyType TestType := SELF;
BEGIN
MyType.TestValue := TestValue + 1;
END;
This creates a copy of the current object and updates it. The following works as expected:
MEMBER PROCEDURE IncrementValueAsProcedure
IS
BEGIN
TestValue := TestValue + 1;
END;
I am trying to insert data in my table where one of the columns is of Object type, but I am getting the following error:
RA-06553: PLS-306: wrong number or types of arguments in call to 'SET_DATUME'
Table definition is:
CREATE TABLE Trebovanje (
BrDokumenta NUMBER,
Datumi DATUMI_T,
SifRJ NUMBER,
BrRadKnjiz NUMBER,
CONSTRAINT trebovanje_pk PRIMARY KEY (BrDokumenta),
CONSTRAINT treb_rj_fk FOREIGN KEY (SifRJ) REFERENCES RadnaJedinica (SifRJ),
CONSTRAINT trebovanje_zap_fk FOREIGN KEY (BrRadKnjiz) REFERENCES Zaposleni (BrRadKnjiz)
);
DATUMI_T user defined type:
create or replace TYPE DATUMI_T AS OBJECT (
DatPrijema DATE,
DatStampanja DATE,
MEMBER FUNCTION get_dat_prijema RETURN DATE,
MEMBER FUNCTION get_dat_stampanja RETURN DATE,
MEMBER PROCEDURE set_datume(p_dat_prijema DATE, p_dat_stampanja DATE),
MEMBER FUNCTION proveri_dat_stampanja(p_dat_stampanja DATE) RETURN BOOLEAN
);
create or replace TYPE BODY DATUMI_T AS
MEMBER FUNCTION get_dat_prijema RETURN DATE AS
BEGIN
RETURN self.DatPrijema;
END get_dat_prijema;
MEMBER FUNCTION get_dat_stampanja RETURN DATE AS
BEGIN
RETURN self.DatStampanja;
END get_dat_stampanja;
MEMBER PROCEDURE set_datume(p_dat_prijema DATE, p_dat_stampanja DATE) AS
BEGIN
self.DatPrijema := p_dat_prijema;
IF proveri_dat_stampanja(p_dat_stampanja)
THEN
self.DatStampanja := p_dat_stampanja;
ELSE
self.DatStampanja := SYSDATE;
END IF;
END set_datUME;
MEMBER FUNCTION proveri_dat_stampanja(p_dat_stampanja DATE) RETURN BOOLEAN AS
ok BOOLEAN;
BEGIN
ok := self.DatPrijema < p_dat_stampanja;
RETURN ok;
END proveri_dat_stampanja;
END;
And my insert statement:
insert into trebovanje values (
1,
DATUMI_T.set_datume(
TO_DATE('2017-01-02 17:44:33', 'YYYY-MM-DD HH24:MI:SS'),
TO_DATE('2017-01-02 17:55:33', 'YYYY-MM-DD HH24:MI:SS')
),
1,
1234);
Any thoughts on this?
What you seem to need is define a constructor that "hides" the default constructor.
CREATE OR REPLACE TYPE DATUMI_T AS OBJECT
(
DatPrijema DATE,
DatStampanja DATE,
MEMBER FUNCTION get_dat_prijema
RETURN DATE,
MEMBER FUNCTION get_dat_stampanja
RETURN DATE,
MEMBER PROCEDURE set_datume(p_dat_prijema DATE, p_dat_stampanja DATE),
MEMBER FUNCTION proveri_dat_stampanja(p_dat_stampanja DATE)
RETURN BOOLEAN,
/* the declaration of your own constructor ( same name of the type ) */
CONSTRUCTOR FUNCTION DATUMI_T(DatPrijema DATE, DatStampanja DATE)
RETURN SELF AS RESULT
);
create or replace TYPE BODY DATUMI_T AS
MEMBER FUNCTION get_dat_prijema RETURN DATE AS
BEGIN
RETURN self.DatPrijema;
END get_dat_prijema;
MEMBER FUNCTION get_dat_stampanja RETURN DATE AS
BEGIN
RETURN self.DatStampanja;
END get_dat_stampanja;
MEMBER PROCEDURE set_datume(p_dat_prijema DATE, p_dat_stampanja DATE) AS
BEGIN
self.DatPrijema := p_dat_prijema;
IF proveri_dat_stampanja(p_dat_stampanja)
THEN
self.DatStampanja := p_dat_stampanja;
ELSE
self.DatStampanja := SYSDATE;
END IF;
END set_datUME;
/* the body of your constructor */
CONSTRUCTOR FUNCTION DATUMI_T(DatPrijema DATE, DatStampanja DATE)
RETURN SELF AS RESULT as
BEGIN
self.DatPrijema := DatPrijema;
IF proveri_dat_stampanja(DatStampanja)
THEN
self.DatStampanja := DatStampanja;
ELSE
self.DatStampanja := SYSDATE;
END IF;
return;
END DATUMI_T;
MEMBER FUNCTION proveri_dat_stampanja(p_dat_stampanja DATE) RETURN BOOLEAN AS
ok BOOLEAN;
BEGIN
ok := self.DatPrijema < p_dat_stampanja;
RETURN ok;
END proveri_dat_stampanja;
END;
trying to check whether the file I want to read exists or not.
Here are another approaches:
Using BFILE and fileexists function of dbms_lob package:
create or replace function FileExists(
p_DirName in varchar2, -- schema object name
p_FileName in varchar2
) return number
is
l_file_loc bfile;
begin
l_file_loc := bfilename(upper(p_DirName), p_FileName);
return dbms_lob.fileexists(l_file_loc); -- 1 exists; 0 - not exists
end;
Using fgetattr function of utl_file package:
create or replace function FileExists(
p_DirName in varchar2, -- schema object name
p_FileName in varchar2
) return number
is
l_fexists boolean;
l_flen number;
l_bsize number;
l_res number(1);
begin
l_res := 0;
utl_file.fgetattr(upper(p_DirName), p_FileName, l_fexists, l_flen, l_bsize);
if l_fexists
then
l_res := 1;
end if;
return l_res;
end;
Use UTL_FILE.FGETATTR function.
This function is designed specifically for this purpose.
Syntax:
UTL_FILE.FGETATTR(
location IN VARCHAR2,
filename IN VARCHAR2,
fexists OUT BOOLEAN,
file_length OUT NUMBER,
block_size OUT BINARY_INTEGER);
Example:
DECLARE
fexists BOOLEAN;
file_length NUMBER;
block_size BINARY_INTEGER;
BEGIN
UTL_FILE.FGETATTR('MY_ORA_DIRECTORY', 'my_file_name.csv', fexists, file_length, block_size);
IF fexists THEN
-- Do something
-- ...
END IF;
END IF;
Oracle documentation:
https://docs.oracle.com/database/121/ARPLS/u_file.htm#ARPLS70915
One more useful link:
https://www.foxinfotech.in/2018/09/how-to-check-if-file-exists-in-pl-sql.html
Creating a function that checks if a file exists is fairly easy by just trying to open it and catching any exceptions (this example function taken from AskTom)
CREATE OR REPLACE FUNCTION file_exists(p_fname IN VARCHAR2) RETURN BOOLEAN
AS
l_file UTL_FILE.FILE_TYPE;
BEGIN
l_file := UTL_FILE.FOPEN(SUBSTR( p_fname, 1, instr(p_fname,'/',-1) ),
SUBSTR( p_fname, instr( p_fname, '/', -1)+1 ), 'r' );
UTL_FILE.FCLOSE( l_file );
RETURN TRUE;
EXCEPTION
WHEN UTL_FILE.INVALID_PATH THEN RETURN FALSE;
WHEN UTL_FILE.INVALID_OPERATION THEN RETURN FALSE;
END;
/
Then you can just use;
IF ( file_exists( 'MED_LIST_19_OCT_12.csv' ) )
...