Undefined variable error in EXCEPTION block | BigQuery - google-bigquery

How do I declare a variable that can also be referenced in EXCEPTION block in BigQuery stored procedure.
CREATE PROCEDURE test.my_sp_1()
BEGIN
DECLARE var1 INT64 DEFAULT 1;
SELECT 1/0;
EXCEPTION WHEN ERROR THEN
SELECT var1;
END;
when the sp is invoked, I'm getting below error
Query error: Unrecognized name: var1 at [my-project.test.my_sp_1:5:10]
Is it possible to reference a variable declared above exception block?

BigQuery script seems to support block-level variable scope and EXCEPTION seems to start a new block of statements. If so, you are not accessible to a variable defined in same level of another block scope in the script.
what if must use the defined variable in EXCEPTION block,
If you have to access a pre-defined variable in EXCEPTION block, I think you can declare it in outer block and access it inner EXCEPTION block like below.
CREATE OR REPLACE PROCEDURE fn.my_sp_1()
BEGIN -- outer block
DECLARE var1 INT64 DEFAULT 1; -- a variable defined in outer block
BEGIN -- initiates a new block
SELECT 1/0; -- exception occurs
EXCEPTION WHEN ERROR THEN
SELECT var1; -- you can access a variable defined in outer block scope here.
END;
END;
CALL fn.my_sp_1();
Query results
References
BEGIN..END
BEGIN initiates a block of statements where declared variables exist only until the corresponding END.

Related

IBM plsql - cursor

I am new with PLSQL (IDM db2) and I am trying to create a procedure to find and delete some admin_tasks if they exist.
Explanation:
By running the bellow query1, I can find out the list of tasks in the scheduler:
SELECT * FROM SYSTOOLS.ADMIN_TASK_LIST;
To delete the task, I can run this query2:
call sysproc.admin_task_remove('TASK_NAME', null);
So I want to create a procedure to delete all tasks with the pattern "My_Task_*".
This should delete all the tasks with that pattern and leave the other existent tasks intact. The procedure should handle cases where the admin_tasks doesn't exist, or no admin_tasks were ever created, and should run without throwing any errors.
I have researched and found out that this can be made using cursors.
Can you help me to implement this?
EDIT:
I managed to find this solution:
BEGIN
FOR v1 AS c1 CURSOR FOR
SELECT NAME
FROM SYSTOOLS.ADMIN_TASK_LIST
WHERE NAME LIKE 'My\_task\_%' ESCAPE '\'
DO
call sysproc.admin_task_remove(NAME, null);
END FOR;
END
This seems to work except if the SYSTOOLS.ADMIN_TASK_LIST was not yet defined.
If it's not defined than I get this error when I run the query: If it is not defined I get this error
: [Code: -204, SQL State: 42704]
"SYSTOOLS.ADMIN_TASK_LIST" is an undefined name.. SQLCODE=-204,
SQLSTATE=42704, DRIVER=4.25.1301
So how can I bypass this error? DOing something like first checking if SYSTOOLS.ADMIN_TASK_LIST is defined, if its defined to the above query if not do nothing.
Presuming that your client tool uses '#' as a statement delimiter.
BEGIN
FOR L1 AS
SELECT NAME
FROM SYSTOOLS.ADMIN_TASK_LIST
WHERE NAME LIKE 'My\_task\_%' ESCAPE '\'
DO
CALL ADMIN_TASK_REMOVE (L1.NAME, NULL);
END FOR;
END
Update:
With error handling for non-existing table. The error handler just "eats" the error on non-existing table use.
You must use dynamic sql for that.
BEGIN
DECLARE SQLSTATE CHAR (5);
DECLARE L_NAME VARCHAR (128);
DECLARE C1 CURSOR FOR S1;
DECLARE EXIT HANDLER FOR SQLSTATE '42704' BEGIN END;
PREPARE S1 FROM
'
SELECT NAME
FROM SYSTOOLS.ADMIN_TASK_LIST
WHERE NAME LIKE ''My\_task\_%'' ESCAPE ''\''
';
OPEN C1;
L1:
LOOP
FETCH C1 INTO L_NAME;
IF SQLSTATE = '02000' THEN LEAVE L1; END IF;
CALL ADMIN_TASK_REMOVE (L_NAME, NULL);
END LOOP L1;
CLOSE C1;
END

Variable scoping in BigQuery stored procedure

I am trying to create nested BEGIN..END blocks within the body of BigQuery stored procedure. The code is as follows:
CREATE OR REPLACE PROCEDURE dataset.proc(IN p_var1 INT64, OUT out_param STRING)
BEGIN
DECLARE p_abc INT64;
DECLARE p_bcd INT64;
BEGIN
DECLARE p_abc INT64 DEFAULT 0; //Error Here : re-declaration cannot occur.
WHILE (p_abc <= p_bcd) DO
BEGIN
SET p_abc = p_abc + 1;
END;
END WHILE;
END;
END;
The above stored procedure doesn't compile because of the redeclaration. Unlike in traditional databases, like Netezza or Teradata, I can easily perform such type of variable scoping.
Is there some way to do this on BigQuery or not possible at all?
The documentation says:
It is an error to declare a variable with the same name as a variable declared earlier in the current block or in a containing block.
So I would say it is impossible to create a variable with the same name in the case you described.

How can I assign a value to a variable declared outside the exception in bigquery?

I am using a stored procedure and I need to assign the value to a variable declared outside the exception, the idea is to be able to call another stored procedure with additional variables when an error occurs
BEGIN
DECLARE v_error string;
SELECT 1/0; --division by zero: 1 / 0
EXCEPTION WHEN ERROR THEN
SET v_error = CONCAT('execution error: ',##error.message, ' Query: ',##error.statement_text);
END;
the result is:
Undeclared variable: v_error at [5:5]
If you want to bring any value from the main block into the EXCEPTION block, then DECLARE outside the BEGIN block.
DECLARE kk INT64;
BEGIN
SET kk = (SELECT MAX(i) FROM `fh-bigquery.public_dump.numbers_255`);
SELECT 1/0;
EXCEPTION WHEN ERROR THEN
SELECT kk;
END;

Copying value to actual parameter

I have a pckage plch_pkg as follows
create type plch_number_nt is table of number;
create or replace package plch_pkg
authid definer
is
g_numbers plch_numbers_nt:=plch_numbers_nt(1);
procedure empty_the_nt_fail(numbers_io IN OUT plch_numbers_nt);
procedure empty_the_nt(numbers_io IN OUT plch_numbers_nt);
end plch_pkg;
create or replace package body plch_pkg
is
procedure empty_the_nt_fail(numbers_io in out plch_numbers_nt)
is
begin
dbms_output.put_line('empty_the_nt_fail');
numbers_io.delete;
raise program_error;
end empty_the_nt_fail;
procedure empty_the_nt(numbers_io in out plch_numbers_nt)
is
begin
dbms_output.put_line('empty_the_nt');
numbers_io.delete;
end empty_the_nt;
end plch_pkg;
Later I created this procedure.
create or replace procedure plch_proc
authid definer
is
l_numbers plch_numbers_nt := plch_numbers_nt(1);
procedure show_numbers
is
begin
dbms_output.put_line('count='||l_numbers.count);
end;
begin
plch_pkg.empty_the_nt_fail(l_numbers);
show_numbers;
exception
when others then
show_numbers;
end plch_proc;
Now when i call this procedure as follows
declare
begin
plch_proc;
end;
I get the output as count=0.
This is my understanding. My procedure plch_proc the empty_the_nt_fail procedure in the plch_pkg.After deleting all the elements from numbers_io nested table,it raises an error.
The oracle documentation says that "Only when the procedure has finished without exception is the result value copied back to the formal parameter".
This copying of numbers_io to my actual parameter l_numbers should not happen as the error occured. But when I called the procedure plch_proc,it printed the count as 0,which means that formal parameter value was copied to actual parameter. Could somebody please explain
I found the explaination for copying actual parameters and formal parameters here
https://docs.oracle.com/cd/B10500_01/appdev.920/a96624/08_subs.htm#895
https://docs.oracle.com/cd/B13789_01/appdev.101/b10807/08_subs.htm#i23202
http://www.dba-oracle.com/t_pl_sql_nocopy_data_structures.htm

sql oracle procedure IN OUT parameter, how to execute it

my procedure looks like this:
create or replace procedure odcitaj_surovinu_zo_skladu
(
v_id_suroviny IN surovina.id_suroviny%TYPE,
odcitaj IN OUT number
)
as
begin
...//some code here
odcitaj:=odcitaj-22;
...//some code here
end;
Procedure compiled w/o errors. I'm trying to execute it as:
execute odcitaj_surovinu_zo_skladu(1,200);
But it gives error, that '200' can't be used as target of assigment.
So how to execute it? Does ODCITAJ even need to be IN OUT? Cause i know that, if it was just IN , then it would act as constant and i won't be able to assign it anything
As Dmitry Bychenko said, you have to use a variable as the target of an OUT or IN OUT parameter, you can't provide a constant. Your parameter does need to be IN OUT since you're modifying it in the procedure. You can either use an anonymous block:
declare
l_odcitaj number;
begin
l_odcitaj := 200;
odcitaj_surovinu_zo_skladu(1, l_odcitaj);
-- do something with the updated value of l_odcitaj
end;
/
If you want to use the SQL*Plus/SQL Developer execute shorthand wrapper for an anonymous block you can declare a bind variable instead:
variable l_odcitaj number;
exec :l_odcitaj := 200;
exec odcitaj_surovinu_zo_skladu(1, :l_odcitaj);
Notice that the variable name has a colon in front when it is set and when the procedure is called, because it is a bind variable.
If you want you can then use that updated bind variable in other calls, or print it's post-procedure value:
print l_odcitaj
If the updated value - from odcitaj:=odcitaj-22; - doesn't need to be returned and is only used inside the procedure, you could declare the argument as IN and have a local variable which you set from the argument and then manipulate and use in the procedure.
create or replace procedure odcitaj_surovinu_zo_skladu
(
v_id_suroviny IN surovina.id_suroviny%TYPE,
v_odcitaj IN number
)
as
l_odcitaj number;
begin
l_odcitaj := v_odcitaj;
...//some code here
l_odcitaj:=l_odcitaj-22;
...//some code here
end;
/
You could then call the procedure with constant values. It just depends whether the caller needs to know the modified value.