I'm using Oracle 11.g and Apex 4.2.
I have an Oracle package (body and spec) that works fine with a stub of test data. My next step is to call this package from an Apex screen. I'd like to pass in two variables:
mail_event_prim_key - a number
mail_list_name - a varchar2(1024)
I have two questions.
1) How do I construct the package to receive two IN-only values from Apex? No values need to be returned to Apex.
2) How do I call this from Apex. I assume I'd use a PL/SQL function to do so.
My current package spec is:
create or replace PACKAGE "PKG_MAIL_SEND" as
PROCEDURE proc_mail_send;
PROCEDURE proc_job_mail_send;
PROCEDURE proc_kill_job_mail;
end PKG__MAIL_SEND;
A portion of my package body is:
create or replace PACKAGE BODY "PKG_MAIL_SEND" as
PROCEDURE proc_parish_mail_send
is
mail_event_prim_key number;
mail_list_name varchar2(1024);
crlf varchar2(2) := CHR(10) || CHR(13);
l_html clob;
l_html_header varchar2(1024);
l_html_body clob;
l_html_footer varchar2(1024);
l_html_total clob;
l_addresses DBMS_SQL.varchar2_table;
l_from varchar2(1024);
l_to varchar2(2048);
l_subject varchar2(2048);
l_smtp_hostname varchar2(2048);
l_smtp_port varchar2(2048);
l_smtp_username varchar2(2048);
l_smtp_password varchar2(2048);
BEGIN
-- Initialize Variables
mail_event_prim_key := '1';
mail_list_name := 'My List Name';
Select lu_value into l_smtp_hostname from hymn_lookup where lu_type = 'smtp_hostname';
-- Lots more code here
End proc_mail_send;
-- Code for PROCEDURE proc_job_mail_send;
-- Code for PROCEDURE proc_kill_job_mail;
end PKG_MAIL_SEND;
Thanks for looking at this.
To receive 2 values in package you obviously need procedure with 2 parameters. For example for your package:
create or replace PACKAGE "PKG_MAIL_SEND" as
-- let this procedure have 2 parameters:
PROCEDURE proc_mail_send(p_address in varchar2, p_topic in varchar2);
PROCEDURE proc_job_mail_send;
PROCEDURE proc_kill_job_mail;
end PKG__MAIL_SEND;
First way
After that click on Create page (or Create region), select type Form, then Form on Procedure. Pass all steps of wizard. After that you will have a region with fields for every parameters of procedure and button to run procedure.
Second way
Create page process. Write in source of process anonymous block, call your procedure from that block (here you need to create manually all things that apex creates automatically in first way - process, items, buttons, etc.).
Third way
Call procedure from dynamic action (it is bit more harder way if you are new to APEX).
Forth way
Call using ajax (procedure will be called inside application process). It is the most hard way, it is used rarely.
Related
First off, I'm a DBA that dabbles in PL/SQL Programming. I have some knowledge, but some is most certainly lacking.
CREATE OR REPLACE PROCEDURE TRIGGER_PRC (P_TRGNAME IN VARCHAR2, P_STATUS IN VARCHAR2)
AS ....
I'd like to allow the P_STATUS parameter only be allowed values of 'E' or 'D' for enabling or disabling a trigger. I've done some searching, but can't seem to find the solution for this. Any help is greatly appreciated!
Thanks!
Jeremy
You could use an IF to check if the values is either E or D. If not, raise an error using raise_application_error:
CREATE OR REPLACE PROCEDURE TRIGGER_PRC (P_TRGNAME IN VARCHAR2, P_STATUS IN VARCHAR2)
AS
begin
if P_STATUS not in ('E', 'D') then
raise_application_error(-20001, 'Invalid P_STATUS value - ' || P_STATUS);
end if;
. . .
. . .
end;
/
I'd rather take the following approach:
CREATE OR REPLACE PROCEDURE ENABLE_TRIGGER_PRC (P_TRGNAME IN VARCHAR2)
AS ....
BEGIN
-- Enable the trigger P_TRGNAME here
END;
CREATE OR REPLACE PROCEDURE DISABLE_TRIGGER_PRC (P_TRGNAME IN VARCHAR2)
AS ....
BEGIN
-- Disable the trigger P_TRGNAME here
END;
You can make the second to call the first etc., but you should always have the IF statement (as GurV mentioned), to validate it once inside the Procedure. In the future, you might want to add drop as well. I suggest you put those into a package, so that they are all consolidated there, having one procedure to execute the actual statement, say in execute immediate, so that all the other procedures can reuse the same code.
Cheers
A solution would also be to use a Boolean, since you have only two choices.
CREATE OR REPLACE PROCEDURE TRIGGER_PRC (P_TRGNAME IN VARCHAR2, P_STATUS IN BOOLEAN)
AS ....
From this interesting article, it is advise to create a separate function to validate your input if you want some kind of ENUM in PLSQL (externalize the if GurV suggests).
So what is a guy to do?
If you want to use enum in a table, use a check constraint.
If you want to use enum in a stored procedure, write a separate procedure to validate the input.
What you could also do, if more than 2 values:
If you can afford to store global variables in a package and also advise your developers to look for constants in a defined package (GUI like PLSQL Developer or alike make it very easy to use):
CREATE OR REPLACE PACKAGE global_vars IS
P_STATUS_enable CONSTANT varchar2(2) := 'E';
P_STATUS_disable CONSTANT varchar2(2) := 'D';
P_STATUS_drop CONSTANT varchar2(2) := 'Dr';
end global_vars;
/
create or replace procedure TRIGGER_PRC (P_TRGNAME IN VARCHAR2, P_STATUS IN varchar2)
AS
begin
if P_STATUS = global_vars.P_STATUS_enable then
-- do something
dbms_output.put_line('ENABLE');
elsif P_STATUS = global_vars.P_STATUS_disable then
-- p_status = P_STATUS_disable
dbms_output.put_line('DISABLE');
elsif P_STATUS = global_vars.P_STATUS_drop then
-- do other stuff
dbms_output.put_line('DROP?');
end if;
end TRIGGER_PRC;
/
begin
TRIGGER_PRC ('TRIG', global_vars.P_STATUS_enable);
end;
/
I am creating Stored Procedure in Oracle and one of them is created permanently and another one is temporarily created and vanished after serving its purpose.How it is working please give your guidance when to use and how it is created.
---- This is not created in DB, just temporarily created and vanished
DECLARE name varchar2(10);
PROCEDURE printVal (name varchar2) IS
BEGIN
dbms_output.put_line ('name:' || name);
END;
BEGIN
name := 'Joe';
printVal(name);
END;
/
---- This is created in DB and permanently available
create PROCEDURE printVal (name varchar2) IS
BEGIN
dbms_output.put_line ('name:' || name);
END;
To understand, Dividing your sql in two parts.
Stored Procedure:
Stored procedures are stored in database.
We can call stored procedures any time after creation.
Stored procedures also supports input output parameters.
Anonymous Block:
These are unnamed pl/sql blocks.
Anonymous blocks are not stored in database.
Cannot pass paramters
---------- Stored Procedure Start--------
DECLARE name varchar2(10);
PROCEDURE printVal (name varchar2) IS
BEGIN
dbms_output.put_line ('name:' || name);
END;
--------- Stored Procedure End-----------
----------anonymous block Start----------
BEGIN
name := 'Joe';
printVal(name);
END;
/
----------anonymous block end ------------
Well, clearly there is different syntax -- the first one is an anonymous block, and the second creates a stored procedure. The expected behaviour is exactly what you observe, and covered by Oracle PL/SQL documentation. https://docs.oracle.com/cloud/latest/db112/LNPLS/overview.htm#LNPLS141
i'm kind of new to Oracle Pl\SQL. I was just trying to create a simple Package with a procedure that returns a set of object id's; the code is as follows:
--Package Spec
CREATE OR REPLACE PACKAGE TEST IS
--GET OBJECT ID'S FROM CONTROL TABLE
PROCEDURE get_object_id_control(p_obj_id OUT abc_table%ROWTYPE);
END;
--Package Body
PROCEDURE get_object_id_control(p_obj_id OUT abc_table%ROWTYPE) AS
BEGIN
SELECT object_id
INTO p_obj_id
FROM abc_table
WHERE fec_proc IS NULL;
END;
I get Error: PL/SQL: ORA-00913: too many values. Is this the correct way for returning multiple values of same data type, or is there a better approach. Thanks in advance.
You can create a custom table type and set the out parameter of the procedure to that type.
CREATE TABLE ABC_TABLE(ID varchar2(100));
create or replace type abc_tab is table of varchar2(100);
/
CREATE OR REPLACE PACKAGE TEST IS
PROCEDURE get_object_id_control(p_obj_id OUT abc_tab);
END;
/
CREATE OR REPLACE PACKAGE BODY TEST IS
PROCEDURE get_object_id_control(p_obj_id OUT abc_tab) AS
BEGIN
SELECT id
bulk collect INTO p_obj_id
FROM abc_table;
END;
END;
/
Then you can call it like so:
declare
v abc_tab;
begin
TEST.get_object_id_control(p_obj_id => v);
for i in v.first..v.last loop
dbms_output.put_line(v(i));
end loop;
end;
/
Similar to GurV's answer (since he beat me by like 30 seconds...), you can use a PL/SQL object type as well. You do not need the CREATE TYPE statement if you don't need to reference the type in SQL.
--Package Spec
CREATE OR REPLACE PACKAGE TEST AS
TYPE id_table_type IS TABLE OF NUMBER;
--GET OBJECT ID'S FROM CONTROL TABLE
PROCEDURE get_object_id_control(p_obj_id_list OUT id_table_type);
END;
--Package Body
CREATE OR REPLACE PACKAGE BODY TEST AS
PROCEDURE get_object_id_control(p_obj_id_list OUT id_table_type) AS
BEGIN
SELECT object_id
BULK COLLECT INTO p_obj_id_list
FROM abc_table
WHERE fec_proc IS NULL;
END;
END;
To use it:
DECLARE
l_id_list test.id_table_type;
BEGIN
test.get_object_id_control (p_obj_id_list => l_id_list);
FOR i IN l_id_list.FIRST .. l_id_list.LAST LOOP
DBMS_OUTPUT.put_line (l_id_list (i));
END LOOP;
END;
I'm stuck with the passing Dates as an array parameters from the Oracle Apex page into package. Package contains one procedure with an array of type of dates. So what I want to do is to pass a simple dates into it from the Apex page, pl/sql block. Here is my code so far:
create or replace PACKAGE PK_NAME AS
TYPE DATES_ARRAY_TYPE IS VARRAY(100) OF DATE;
PROCEDURE PASS_DATES (
DATES DATES_ARRAY_TYPE
);
END PK_NAME;
create or replace PACKAGE BODY PK_NAME AS
PROCEDURE PASS_DATES (
DATES DATES_ARRAY_TYPE
) AS
BEGIN
for i in 1..DATES.count loop
HTP.P(DATES(i));
end loop;
END;
END PASS_DATES;
END PK_NAME;
Simple as that. And I call this procedure from the Apex page pl/sql block:
PK_NAME.PASS_DATES (
DATES => '15-JAN-15', '16-JAN-15', '17-JAN-15'
);
However, it doesn't work, every time I'm trying to save it, it gives me an error:
•ORA-06550: line 3, column 25: PLS-00312: a positional parameter association may not follow a named association ORA-06550: line 2, column 1: PL/SQL: Statement ignored
What is wrong with it or what have I missed ?
https://docs.oracle.com/cd/A97630_01/appdev.920/a96624/05_colls.htm
you must init constructor DATES_ARRAY_TYPE()
i think it must look like this
create TYPE DATES_ARRAY_TYPE IS VARRAY(100) OF DATE;
create or replace procedure test_case( p_dates DATES_ARRAY_TYPE) is
begin
dbms_output.put_line(p_dates(1));
end;
declare
a DATES_ARRAY_TYPE;
begin
a := DATES_ARRAY_TYPE(sysdate, sysdate + 1,to_date('1.01.2016','dd.mm.yyyy'));
test_case(a);
end;
also if you want to use TYPE in PACKAGE PK_NAME (not global) you must use object like PK_NAME.DATES_ARRAY_TYPE in your code.
ok, lets go in your case:
1. create package and body:
https://gyazo.com/789b875ce47852e859c395c2021f9cd4
create or replace PACKAGE PCK AS
-- your type in pck
TYPE DATES_ARRAY_TYPE IS VARRAY(100) OF DATE;
procedure test_case(p_dates DATES_ARRAY_TYPE);
END PCK;
create or replace PACKAGE body PCK AS
procedure test_case(p_dates DATES_ARRAY_TYPE) IS
BEGIN
--here just raise second element in array for DEMO
raise_application_error('-20000',p_dates(2) );
END;
END PCK;
2.create page and button and after submit process:
https://gyazo.com/755f6e089db0a6a8ea058567d2b3384b
declare
asd PCK.DATES_ARRAY_TYPE := PCK.DATES_ARRAY_TYPE('31-JUL-15', '01-AUG-15', '02-AUG-13', '03-AUG-13');
begin
pck.test_case(asd);
end;
after button that submit page i get this:
https://gyazo.com/b894fc6b9b6dd28964ba2e6548244bc8
I have a pkg that I use to keep report oriented code CMS_REPORTS.
I added a procedure to return a ref cursor and the pkg compiles fine, but fails when I call the proc to test it with:
ORA-04063: package body "CMS.CMS_REPORTS" has errors
ORA-06508: PL/SQL: could not find program unit being called: "CMS.CMS_REPORTS"
I've removed the orig proc and replaced it with this to keep things simple - same problem.
The proc is this:
procedure test_ref_cur(p_testno in number,
p_cur in out ref_cur) as
begin
open p_cur for
select p_testno + 1 from dual;
end test_ref_cur;
I have defined the ref cursor in the pkg spec like this:
type ref_cur is ref cursor;
procedure test_ref_cur(p_testno in number,
p_cur in out ref_cur);
I've tried all sorts of combinations of using ref cursor and sys_refcursor and all bring up the same error. If I remove the proc from the pkg, it works fine.
I'm beginning to think it's a system issue?
Has anyone else had this problem?
Regards
Dave
Hard to tell what is the issue here, since it doesn't look like we are seeing the relevant code.
So here are some thing I recommend to double check:
package and package body are there and are actually compiled without an exception
you are in the schema/user that contains package and package body.
There are no other objects with the same name, that might hide your package/package body
the procedure you try to call is present in package and package body.
remove all code from package + package body except a single trivial procedure and check if that works.
If you've done all that update the question with the results.
In order to achieve what you want you have to use SYS_REFCURSOR:
create procedure test_ref_cur(p_testno in number,
p_cur in out SYS_REFCURSOR) as
begin
open p_cur for
select p_testno + 1 from dual;
end test_ref_cur;
-- PROCEDURE TEST_REF_CUR compiled
... and example:
DECLARE
l_sysrc SYS_REFCURSOR;
l_num NUMBER;
procedure test_ref_cur(p_testno in number,
p_cur in out SYS_REFCURSOR) as
begin
open p_cur for
select p_testno + 1 from dual;
end test_ref_cur;
BEGIN
test_ref_cur(1, l_sysrc);
FETCH l_sysrc INTO l_num;
DBMS_OUTPUT.PUT_LINE(l_num);
END;
-- Result:
-- 2
Since Oracle 7.3 the REF CURSOR type has been available to allow
recordsets to be returned from stored procedures and functions. Oracle
9i introduced the predefined SYS_REFCURSOR type, meaning we no longer
have to define our own REF CURSOR types.
Source: http://www.oracle-base.com/articles/misc/using-ref-cursors-to-return-recordsets.php