Good morning guys,
do you know an easy way to automatically generate an Oracle package.procedure call?
After have defined the package
Create Or Replace Package PKG1
As
Procedure PRC1
(
P_VAL1_I In NUMBER,
P_Return_Set_O Out Sys_Refcursor,
);
End;
I'd like to generate via a script the call
DECLARE
var_P_RETURN_SET_O SYS_REFCURSOR;
BEGIN
PKG1.PRC1(P_VAL1_I => 0, P_Return_Set_O => var_P_RETURN_SET_O );
END;
Of Course I'll replace later the input parameter(s).
Any idea/suggestion?
Here the answer to myself .. was not that difficult .. simply run a pl/sql that build the procedure call .
Declare
l_Package varchar2(255) :=upper('xxxxxxxxx');
l_Proc varchar2(255) :=upper('xxxxxxxxxxxx');
Begin
Dbms_Output.Put_Line('DECLARE');
For Var_Out In (
select ObjArg.Argument_Name,Data_type
FROM sys.user_objects UsrObj
Inner Join sys.user_arguments ObjArg On UsrObj.object_id= ObjArg.object_id
Where UsrObj.object_name = l_Package
And ObjArg.ObjecT_Name= l_Proc
AND UsrObj.object_type = 'PACKAGE'
And ObjArg.In_Out='OUT'
)
Loop
Dbms_Output.Put_Line('L_'||Var_Out.Argument_Name ||' '||Var_Out.Data_type||';');
end Loop;
Dbms_Output.Put_Line('BEGIN');
Dbms_Output.Put_Line(l_Package||'.'||l_Proc);
Dbms_Output.Put_Line('(');
For PArams In (
select ObjArg.Argument_Name,Data_type,ObjArg.In_Out
FROM sys.user_objects UsrObj
Inner Join sys.user_arguments ObjArg On UsrObj.object_id= ObjArg.object_id
Where UsrObj.object_name = l_Package
And ObjArg.ObjecT_Name= l_Proc
AND UsrObj.object_type = 'PACKAGE'
)
Loop
IF PArams.In_Out='OUT' Then
Dbms_Output.Put_Line(PArams.Argument_Name || '=> L_'||PArams.Argument_Name);
ELSE
Dbms_Output.Put_Line(PArams.Argument_Name || '=> xxx');
End If;
end Loop;
Dbms_Output.Put_Line(');');
Dbms_Output.Put_Line('END');
End;
Result ..
DECLARE
L_P_RETURN_SET_O REF CURSOR;
L_P_EXECUTION_STATUS_O NUMBER;
BEGIN
PKG.PROC
(
P_PARAM_I=> xxx
P_RETURN_SET_O=> L_P_RETURN_SET_O
);
END;
The code is not perfect ..but it works ...
Did you try:
variable rc refcursor;
exec PKG1.PRC1(0, :rc);
print rc;
You can create job which will call your procedure on specified intervals.
For instance:
BEGIN
DBMS_SCHEDULER.CREATE_JOB
(
job_name => 'JOB_NAME'
,start_date => SYSDATE
,repeat_interval => 'FREQ=DAILY;BYHOUR=05;BYMINUTE=00;BYSECOND=00'
,end_date => NULL
,job_class => 'DEFAULT_JOB_CLASS'
,job_type => 'PLSQL_BLOCK'
,job_action => 'BEGIN
PKG1.PRC1(P_VAL1_I => 0, P_Return_Set_O => var_P_RETURN_SET_O );
END;
,comments => 'JOB_NAME'
);
DBMS_SCHEDULER.ENABLE(name => 'JOB_NAME');
END;
This job will run daily at 5 a.m .
I hope it will solve your problem!
Related
If was ok when i create a function and add a policy in Oracle. Error happen when i want to select a table (OBJECT_NAME). It seems like error happen in the select into variable, but i dont know how to fix it (I make sure that select into 1 variable only and the same type as variable)
CREATE OR REPLACE FUNCTION TC6_NHANVIEN(P_SCHEMA VARCHAR2, P_OBJ VARCHAR2)
RETURN VARCHAR2
AS
USERCHECK VARCHAR2(100);
VAITROCHECK VARCHAR(100);
BEGIN
USERCHECK := SYS_CONTEXT('USERENV', 'SESSION_USER');
VAITROCHECK :='';
IF (USER LIKE 'NV%') THEN --i want to check a variable. Error maybe right here
SELECT VAITRO INTO VAITROCHECK
FROM SYSTEM.NHANVIEN
WHERE MANV=USER;
END IF;
IF (USERCHECK LIKE 'NV%'AND VAITROCHECK!='THANHTRA') THEN
RETURN 'MANV = '''||USERCHECK||'''';
ELSE
RETURN '1=1';
END IF;
END;
BEGIN
dbms_rls.add_policy(
OBJECT_SCHEMA => 'SYSTEM',
OBJECT_NAME => 'NHANVIEN',
POLICY_NAME => 'PC_2',
POLICY_FUNCTION => 'TC6_NHANVIEN',
STATEMENT_TYPES => 'SELECT,UPDATE',
UPDATE_CHECK => TRUE
);
END;
grant select on SYSTEM.NHANVIEN to NV001;
select * from SYSTEM.NHANVIEN --LOG IN AS NV001
Is it possible delete from apex collection where C003 = 31
The DELETE_MEMBERS procedure is what are you looking for. To find out more about it please check the official Oracle documentation.
You can do something like:
APEX_COLLECTION.DELETE_MEMBERS
(
p_collection_name => 'collection_name',
p_attr_number => 3,
p_attr_value => '31'
);
Or use the sequence ID.
DECLARE
CURSOR c_temp IS
SELECT
seq_id
FROM APEX_COLLECTIONS
WHERE collection_name = 'collection_name'
AND c003 = '31';
BEGIN
FOR r_temp IN c_temp LOOP
apex_collection.delete_member
(
p_collection_name => 'collection_name',
p_seq => r_temp.seq_id
);
END LOOP;
END;
I have a collection (associative array or nested table) of NUMBER variables. I want to initiate a job which invokes a stored procedure which in turn receives this very custom type and does something for each element. Now my job takes a program where I set arguments. How do I set the argument of my custom data type (associative array or table of Number) into the scheduler program? Or any alternative ways? Thanks in advance
Custom types can be passed to scheduler programs using ANYDATA. But the process is complicated, so it might be easier to create a new job with a hard-coded PL/SQL block instead of re-using a program with arguments.
--Create a nested table of NUMBERs.
create or replace type number_nt is table of number;
--Create a procedure that accepts a nested table of numbers.
create or replace procedure test_procedure(p_numbers in number_nt) is
begin
if p_numbers is not null then
for i in 1 .. p_numbers.count loop
dbms_output.put_line(p_numbers(i));
end loop;
end if;
end;
/
--Create a PROGRAM to run the stored procedure.
begin
dbms_scheduler.create_program
(
program_name => 'TEST_PROGRAM',
program_type => 'STORED_PROCEDURE',
program_action => 'TEST_PROCEDURE',
number_of_arguments => 1
);
end;
/
--Define the argument that will be passed into the stored procedure and enable the program.
begin
dbms_scheduler.define_anydata_argument
(
program_name => 'TEST_PROGRAM',
argument_position => 1,
argument_type => 'SYS.ANYDATA',
default_value => null
);
dbms_scheduler.enable('TEST_PROGRAM');
end;
/
--Create the nested table of numbers, convert it into an ANYDATA, create a job,
--pass the ANYDATA to the job, and then enable the job.
declare
v_numbers number_nt := number_nt(1,2,3);
v_anydata anydata;
begin
v_anydata := anydata.convertCollection(v_numbers);
dbms_scheduler.create_job
(
job_name => 'TEST_JOB',
program_name => 'TEST_PROGRAM',
enabled => false
);
dbms_scheduler.set_job_anydata_value
(
job_name => 'TEST_JOB',
argument_position => 1,
argument_value => v_anydata
);
dbms_scheduler.enable('TEST_JOB');
end;
/
--Check the results.
--STATUS = "SUCCEEDED", OUTPUT = "1 2 3"
select status, output
from dba_scheduler_job_run_details where job_name = 'TEST_JOB';
I'm currently trying to implement a similar version of oracle's APEX_MAIL package. I have everything working, but I can't make the job work unless I modify it.
The job APEX_MAIL uses is called ORACLE_APEX_MAIL_QUEUE
BEGIN
DBMS_SCHEDULER.set_attribute( name => '"APEX_040000"."ORACLE_APEX_MAIL_QUEUE"', attribute => 'job_action', value => 'APEX_040000.WWV_FLOW_MAIL.PUSH_QUEUE');
DBMS_SCHEDULER.set_attribute( name => '"APEX_040000"."ORACLE_APEX_MAIL_QUEUE"', attribute => 'number_of_arguments', value => '2');
DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(
job_name => '"APEX_040000"."ORACLE_APEX_MAIL_QUEUE"',
argument_position => 1,
argument_value => '');
DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(
job_name => '"APEX_040000"."ORACLE_APEX_MAIL_QUEUE"',
argument_position => 2,
argument_value => '');
END;
/
So I go to the package to see what the code does. I'm was assuming push queue would send out emails in the queue. Instead, it calls the same job again!
PROCEDURE PUSH_QUEUE( P_SMTP_HOSTNAME IN VARCHAR2 DEFAULT NULL,
P_SMTP_PORTNO IN VARCHAR2 DEFAULT NULL )
IS
BEGIN
PUSH_QUEUE_BACKGROUND;
END PUSH_QUEUE;
PROCEDURE PUSH_QUEUE_BACKGROUND
IS
BEGIN
SYS.DBMS_SCHEDULER.RUN_JOB( JOB_NAME => 'ORACLE_APEX_MAIL_QUEUE', USE_CURRENT_SESSION => FALSE );
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE <> -27478 THEN
RAISE;
END IF;
END PUSH_QUEUE_BACKGROUND;
So basically this job does nothing, but I switch it to call PUSH_QUEUE_IMMEDIATE which does what I think it should do.
PROCEDURE PUSH_QUEUE_IMMEDIATE( P_FORCE_YN IN VARCHAR2 DEFAULT 'N')
IS
L_STATUS NUMBER;
L_LOCK_HDL VARCHAR2(128);
E_DB_SHUTDOWN EXCEPTION;
PRAGMA EXCEPTION_INIT(E_DB_SHUTDOWN, -1089);
BEGIN
WWV_FLOW_DEBUG.ENABLE_DBMS_OUTPUT;
SYS.DBMS_LOCK.ALLOCATE_UNIQUE( LOCKNAME => 'APEX_MAIL_QUEUE_LOCK', LOCKHANDLE => L_LOCK_HDL);
L_STATUS := SYS.DBMS_LOCK.REQUEST( LOCKHANDLE => L_LOCK_HDL,
LOCKMODE => SYS.DBMS_LOCK.X_MODE,
TIMEOUT => 0,
RELEASE_ON_COMMIT => FALSE );
WWV_FLOW_DEBUG.INFO('APEX Mail Lock status: ' || L_STATUS );
IF L_STATUS = 0 THEN
FOR C1 IN ( SELECT ID, MAIL_SEND_COUNT, LAST_UPDATED_ON
FROM WWV_FLOW_MAIL_QUEUE
ORDER BY MAIL_SEND_COUNT, LAST_UPDATED_ON) LOOP
BEGIN
WWV_FLOW_DEBUG.INFO( 'Pushing email: ' || C1.ID );
IF (C1.MAIL_SEND_COUNT = 0) OR (NVL(P_FORCE_YN,'N') = 'Y') OR
(C1.MAIL_SEND_COUNT > 0 AND (POWER(2,C1.MAIL_SEND_COUNT)/(60*24) + C1.LAST_UPDATED_ON) < SYSDATE) THEN
BACKGROUND( P_ID => C1.ID );
END IF;
WWV_FLOW_DEBUG.INFO( 'Pushed email: ' || C1.ID );
EXCEPTION
WHEN OTHERS THEN
WWV_FLOW_DEBUG.LOG_EXCEPTION;
IF L_LOCK_HDL IS NOT NULL THEN
L_STATUS := SYS.DBMS_LOCK.RELEASE( L_LOCK_HDL );
WWV_FLOW_DEBUG.INFO('APEX Mail released lock' );
END IF;
END;
END LOOP;
END IF;
IF L_LOCK_HDL IS NOT NULL THEN
L_STATUS := SYS.DBMS_LOCK.RELEASE( L_LOCK_HDL );
WWV_FLOW_DEBUG.INFO('APEX Mail released lock' );
END IF;
EXCEPTION WHEN E_DB_SHUTDOWN THEN
NULL;
END PUSH_QUEUE_IMMEDIATE;
I'm trying to copy APEX_MAIL to a point, but if I do, I won't have a working job. Can anyone point out if APEX_MAIL changes what the job does after an application setting change or any other change?
Thanks in advance!
APEX_MAIL.PUSH_QUEUE is usable in your own code to send your mail (in the queue) out immediate. The job normally calls PUSH_QUEUE_IMMEDIATE. I don't know if your setting ever was a bug in the installation or something wrong on your site.
Thus fact, it calls PUSH_QUEUE_IMMEDIATE in a separate session as APEX_040000 job.
Since everyone can request an immediate send of all the jobs in the queue, it makes sure via SYS.DBMS_LOCK.REQUEST only one session will actually do the transmit.
I've created a new function called GET_FILENAME inside my Oracle APEX database. I'd like to call this function from within a APEX PL/SQL code block and pass it the filename. Below is the function I created in my APEX DB with SQL Dev.
create or replace function get_filename
(p_path IN VARCHAR2)
RETURN varchar2
IS
v_file VARCHAR2(100);
BEGIN
-- Parse string for UNIX system
IF INSTR(p_path,'/') > 0 THEN
v_file := SUBSTR(p_path,(INSTR(p_path,'/',-1,1)+1),length(p_path));
-- Parse string for Windows system
ELSIF INSTR(p_path,'\') > 0 THEN
v_file := SUBSTR(p_path,(INSTR(p_path,'\',-1,1)+1),length(p_path));
-- If no slashes were found, return the original string
ELSE
v_file := p_path;
END IF;
RETURN v_file;
END;
APEX side of things....
Below is the code I'd like to put in when calling the process.
TO_DATE(SUBSTR(GET_FILENAME(file_date),21,8),'YYYY-MM-DD')
Below is the apex process code and the comments are where I'd like to put it in.
BEGIN
APEX_COLLECTION.ADD_MEMBER
(
p_collection_name => 'PARSE_COL_HEAD',
p_c001 => 'C031',
p_c002 => 'FILE_DATE');
FOR UPLOAD_ROW IN (SELECT SEQ_ID FROM APEX_COLLECTIONS
WHERE COLLECTION_NAME = 'SPREADSHEET_CONTENT')
LOOP
APEX_COLLECTION.UPDATE_MEMBER_ATTRIBUTE (
p_collection_name => 'SPREADSHEET_CONTENT',
p_seq => UPLOAD_ROW.SEQ_ID,
p_attr_number => '31',
p_attr_value => :P25_FILE_NAME -- I want to call the process here
);
END LOOP;
END;
I don't normally answer my own question. But this was easier than I thought :). Below is the code I used to get this working....
BEGIN
APEX_COLLECTION.ADD_MEMBER
(
p_collection_name => 'PARSE_COL_HEAD',
p_c001 => 'C031',
p_c002 => 'FILE_DATE');
FOR UPLOAD_ROW IN (SELECT SEQ_ID FROM APEX_COLLECTIONS
WHERE COLLECTION_NAME = 'SPREADSHEET_CONTENT')
LOOP
APEX_COLLECTION.UPDATE_MEMBER_ATTRIBUTE (
p_collection_name => 'SPREADSHEET_CONTENT',
p_seq => UPLOAD_ROW.SEQ_ID,
p_attr_number => '31',
p_attr_value => TO_DATE(SUBSTR(GET_FILENAME(:P25_FILE_NAME),21,8),'YYYY-MM-DD')
);
END LOOP;
END;