I have a need to test a stored procedure involving arrays that are written in PL/SQL. I have no way of seeing the PL/SQL contents (shop rules) but need to call the proc. I'd like to know how to call the following proc that invovles 3 arrays of integers directly from toad. The method signature looks like this.
Procedure persistChanges(myKey in NUMBER, arrayOfIntsFirst numberTableType, arrayOfIntsSecond numberTableType, arrayOfIntsThird numberTableType).
How can I call a PL/SQL proc in TOAD where I can hard-code the values for the paramters to test the proc? I'm told there's no way this could be done in Toad. Much appreciated!!!
I have to believe there's a way to poplate these variables but just not sure how to go about doing so...
DECLARE
myKey NUMBER;
arrayOfIntsFirst PL/SQL TABLE;
arrayOfIntsSecond PL/SQL TABLE;
arrayOfIntsThird PL/SQL TABLE;
BEGIN
myKey := NULL;
-- arrayOfIntsFirst := NULL; Modify the code to initialize this parameter
-- arrayOfIntsSecond := NULL; Modify the code to initialize this parameter
-- arrayOfIntsThird := NULL; Modify the code to initialize this parameter
MY_SCHEMA.PKG_MYPACKAGE.PERSISTCHANGES ( myKey, arrayOfIntsFirst,
arrayOfIntsSecond, arrayOfIntsThird );
COMMIT;
END;
Try something like:
declare
l_nums t_num_tab;
begin
l_nums := t_num_tab();
l_nums.extend(2);
l_nums(1) := 56;
l_nums(2) := 42;
for i in l_nums.first .. l_nums.last
loop
dbms_output.put_line( 'Number is: ' || l_nums(i) );
end loop;
end;
And Toad is just a development environment, it won't limit you from writing an anonymous block like above. The t_num_tab is defined as a table of numbers:
CREATE OR REPLACE TYPE t_num_tab as table of number;
You don't even need to formally extend the collection first and assign values. You can initialize in one step:
declare
l_nums t_num_tab;
begin
l_nums := t_num_tab(23,89,152);
...
end;
Read more here. Hope that helps.
Related
I need to test a stored procedure that has several subprocedures in the is block. I have copied everything to a test window, however, putting these subprocedures in the declare block does not seem to work. When I attempt to call them from the begin block, I get an error saying that they have not been declared. I haven't changed anything other than moving the contents of is to declare. What can I do?
You can declare procedure, however:
1) You declare it without 'create or replace' keywords
2) If you declare any variables in the same block, all procedures and functions must be declared after the last variable declaration.
So, for example, this:
declare
some_text varchar2(10) := 'SOME_TEXT';
procedure print_4 is
x number := 4;
begin
dbms_output.put_line(x);
end print_4;
begin
print_4;
end;
is a valid pl/sql block, while this:
declare
procedure print_4 is
x number := 4;
begin
dbms_output.put_line(x);
end print_4;
some_text varchar2(10) := 'SOME_TEXT';
begin
print_4;
end;
is not.
If you post your code we might find some other errors.
I am new to plsql. I am trying to put two scripts under the same package. These scripts deal with arrays. How do I pass an array into the procedure? If I am to declare the array, do I do it in the specification or the body? I am trying this right now but it doesn't work.
CREATE PACKAGE cop_cow_script AS
PROCEDURE COP_COW_DATALOAD_V2(arr_claims VARRAY(15000) OF VARCHAR2(10), arr_sql VARRAY(500) OF VARCHAR2(1000));
END cop_cow_script;
As you see I want to pass in those two arrays.
You need to declare types in the package specification and use them as parameter types in the procedure declaration:
CREATE OR REPLACE PACKAGE cop_cow_script AS
TYPE arr_claims_t IS VARRAY(15000) OF VARCHAR2(10);
TYPE arr_sql_t IS VARRAY(500) OF VARCHAR2(1000);
PROCEDURE COP_COW_DATALOAD_V2(arr_claims arr_claims_t, arr_sql arr_sql_t);
END cop_cow_script;
/
EDIT - below is an example of the package body - the procedure loops through elements of it's first parameter and prints them using DBMS_OUTPUT.PUT_LINE
PROCEDURE COP_COW_DATALOAD_V2(arr_claims arr_claims_t, arr_sql arr_sql_t)
IS
BEGIN
FOR i IN arr_claims.FIRST .. arr_claims.LAST
LOOP
DBMS_OUTPUT.PUT_LINE( arr_claims( i ) );
END LOOP;
END;
END cop_cow_script;
/
An then you can initialize them and pass to the procedure invocation for example in this way (this is an anonymous block that declares two variables, initializes them and invokes the procedure passing both parameters to it:
DECLARE
my_array1 cop_cow_script.arr_claims_t := cop_cow_script.arr_claims_t();
my_array2 cop_cow_script.arr_sql_t := cop_cow_script.arr_sql_t();
BEGIN
my_array1.extend;
my_array1( 1 ) := 'string 1';
my_array2.extend;
my_array2( 1 ) := 'string 2';
cop_cow_script.COP_COW_DATALOAD_V2( my_array1, my_array2 );
END;
/
First off, I'm hard-pressed to imagine a situation where you'd actually want to use a PL/SQL varray rather than a nested table or an associative array. There really isn't a benefit to declaring a fixed size collection.
Whatever sort of collection type you want, you'd need to declare the collection type either in the package specification or at the SQL level (depending on the type of collection) separate from the procedure specification
CREATE PACKAGE cop_cow_script AS
TYPE arr_claims IS varray(15000) of varchar(10);
TYPE arr_sql IS varray(500) of varchar(1000);
PROCEDURE COP_COW_DATALOAD_V2(p_claims arr_claims,
p_sql arr_sql);
END cop_cow_script;
I am new to PL/SQL. I have created a pipelined function inside a package which takes as its parameter input an array of numbers (nested table).
But I am having trouble trying to run it via an sql query. Please see below
my input array
CREATE OR REPLACE TYPE num_array is TABLE of number;
my function declaration
CREATE OR REPLACE PACKAGE "my_pack" as
TYPE myRecord is RECORD(column_a NUMBER);
TYPE myTable IS TABLE of myRecord;
FUNCTION My_Function(inp_param num_array) return myTable PIPELINED;
end my_pack;
my function definition
CREATE OR REPLACE PACKAGE BODY "my_pack" as
FUNCTION My_Function(inp_param num_array) return myTable PIPELINED as
rec myRecord;
BEGIN
FOR i in 1..inp_param.count LOOP
FOR e IN
(
SELECT column_a FROM table_a where id=inp_param(i)
)
LOOP
rec.column_a := e.column_a;
PIPE ROW (rec);
END LOOP;
END LOOP;
RETURN;
END;
end my_pack;
Here is the latest code I've tried running from toad. But it doesn't work
declare
myarray num_array;
qrySQL varchar2(4000);
begin
myarray := num_array(6341,6468);
qrySQL := 'select * from TABLE(my_pack.My_Function(:myarray))';
execute immediate qrySQL;
end;
So my question is how can I feed an array into this pipelined function from either TOAD or SQL Developer. An example would be really handy.
Thanks
The error is fairly clear, you have a bind variable that you haven't assigned anything to. You need to pass your actual array with:
qrySQL := 'select * from TABLE(my_pack.My_Function(:myarray))';
execute immediate qrySQL using myarray;
It's maybe more useful, if you want to call it from PL/SQL, to use static SQL as a cursor:
set serveroutput on
declare
myarray num_array;
begin
myarray := num_array(6341,6468);
for r in (select * from TABLE(my_pack.My_Function(myarray))) loop
dbms_output.put_line(r.column_a);
end loop;
end;
/
Or just query it statically as a test, for fixed values:
select * from TABLE(my_pack.My_Function(num_array(6341,6468)));
SQL Fiddle with some minor tweaks to the function to remove errors I think came from editing to post.
I'm trying to port oracle stored procedures (plsql) to a postgresql function (pl/pgsql).
In oracle I can define a stored procedure with IN and OUT parameters.
CREATE OR REPLACE PROCEDURE MY_TEST(foo IN NUMBER,
bar OUT NUMBER)
IS
BEGIN
bar := 1
END
This will store a value of 1 in the variable that is passed to the stored procedure.
I can call it as follows:
DECLARE
outValue NUMBER ;
BEGIN
Exec MY_TEST(10, outValue);
DBMS_OUTPUT.PUT_LINE('Value Returned Is : '||outValue) ;
END ;
In Postgresql (pl/pgsql) I can define a function like this one:
CREATE OR REPLACE FUNCTION MY_TEST(foo IN NUMBER,
bar OUT NUMBER)
BEGIN
bar := 1
END;
$body$
LANGUAGE PLPGSQL;
However I can not use the out parameter the same way as I could in oracle.
In postgresql the OUT parameter defines the return value.
In oracle stored procedures don't have return values, but instead write the output into the variable that is passed in the call
Is there something I overlooked, that would permit me to use pl/pgsql functions in a similar way as the stored procedure is used in the example above?
Any hints are greatly appreciated.
In PostgreSQL, PL/pgSQL or SQL functions take parameter values and return values.
They do not take pointers or references - so that the value of the referenced address could be manipulated.
You could do something like that in theory with a C-language function, where you can pass values by reference. However, de facto, you cannot. The manual warns:
Warning
Never modify the contents of a pass-by-reference input value. If you
do so you are likely to corrupt on-disk data, since the pointer you
are given might point directly into a disk buffer. The sole exception to this rule is explained in Section 35.10.
In short: what you are trying to do is impossible in PostgreSQL.
You don't need an OUT parameter for Postgres:
CREATE OR REPLACE FUNCTION MY_TEST(foo IN integer)
returns integer
as
$body$
BEGIN
return 1;
END;
$body$
LANGUAGE PLPGSQL;
Then use it like this:
DO $body$
DECLARE
result integer;
begin
result := my_test(42);
raise notice 'Return value is: %', result;
end;
$body$
Take a read of this.
http://www.postgresonline.com/journal/archives/129-Use-of-OUT-and-INOUT-Parameters.html
In summary given the following stored function and the anonymous block to test this is one approach that the article will show.
CREATE OR REPLACE FUNCTION fn_plpgsqltestout(param_subject text,
OUT subject_scramble text, OUT subject_char text)
AS
$$
BEGIN
subject_scramble := substring($1, 1,CAST(random()*length($1) As integer));
subject_char := substring($1, 1,1);
END;
$$
LANGUAGE 'plpgsql' VOLATILE;
DO $body$
DECLARE
o_subject_scramble TEXT;
o_subject_char TEXT;
begin
--Option1
SELECT (fn_plpgsqltestout('This is a test subject')).* INTO o_subject_scramble,o_subject_char;
--Option2
SELECT (fn_plpgsqltestout('This is a test subject')).subject_scramble INTO o_subject_scramble;
SELECT (fn_plpgsqltestout('This is a test subject')).subject_char INTO o_subject_char;
--Option3
o_subject_scramble := (fn_plpgsqltestout('This is a test subject')).subject_scramble;
o_subject_char := (fn_plpgsqltestout('This is a test subject')).subject_char;
raise notice 'Return value is: %', o_subject_scramble;
raise notice 'Return value is: %', o_subject_char;
end;
$body$
I have a function that takes as one of it's arguments a VARRAY of pl/sql Objects. How do I execute this stored procedure and bind the resultset that it returns to a data grid in TOAD for Oracle?
After some searching around, I found the answer to my own problem. Say your varray type was called varchar_pair_array and the objects stored in this array were called varchar_pair_object. varchar_pair_object is a simple object that has two varchars as it's members.
Here is the code for executing a proc that takes in a varray of varchar_pair_object (s):
DECLARE
RetVal SYS_REFCURSOR;
a_simplevalue VARCHAR2(200);
another_simplevalue VARCHAR2(200);
my_array_of_varchar_pairs VARCHAR_PAIR_ARRAY; -- assume varchar_pair_array is defined somewhere else
my_obj VARCHAR_PAIR_OBJECT; -- assume varchar_pair_object is defined somewhere else
my_other_obj VARCHAR_PAIR_OBJECT;
BEGIN
a_simplevalue := 'hello';
another_simplevalue := 'there';
my_obj := VARCHAR_PAIR_OBJECT('nice to meet you', 'greetings');
my_other_obj := VARCHAR_PAIR_OBJECT('goodbye', 'ciao');
my_array_of_varchar_pairs := VARCHAR_PAIR_ARRAY();
my_array_of_varchar_pairs.EXTEND(2); -- this should be the number of objects you plan to put into the array
my_array_of_varchar_pairs(1) := my_obj;
my_array_of_varchar_pairs(2) := my_other_obj;
RetVal := my_function ( a_simplevalue, another_simplevalue, my_array_of_varchar_pairs); -- assuming your array takes two varchars and one array of VARCHAR_PAIR_OBJECT (s)
:to_grid := RetVal;
END;
Copy paste this code in TOAD's sql editor and change it to adapt to your function and types and hit F9. TOAD will ask you the type of the :to_grid variable. Select cursor (assuming your function returns a ref cursor) and hit enter. TOAD will bind the result set to a data grid.
Links that helped me:
http://www.smart-soft.co.uk/Oracle/oracle-plsql-tutorial-part-11.htm (good tutorial on collections)
http://download.oracle.com/docs/cd/B10501_01/appdev.920/a96624/10_objs.htm#1972 (especially useful in this case is the section on declaring and initializing objects)
With very little change the same can be done with a procedure.