DB2 LUW - Get Error Line in Stored Procedure - stored-procedures

I'm trying to determine the line in a stored procedure or the last SQL-statement which is causing an error. As a workaround I'm using temporary variables which I manually set to determine in which part of my stored procedure an error occurs.
See the following:
-- Create an ErrorLog table
Create Table SCHEMA.ErrorLog_lrc_test
(
ErrSQLCODE Integer ,
Codepart Char(1),
Type Char(1) ,
MsgText VarChar(1024));
CREATE OR REPLACE PROCEDURE SCHEMA.test_error(IN divisor INT)
LANGUAGE SQL
BEGIN
-- Define variables
DECLARE codepart_var Char(1);
DECLARE test_INT INT;
-- Define sqlcode
DECLARE SQLCODE INTEGER;
--Define Error-Handler
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
INSERT INTO SCHEMA.ErrorLog_lrc_test(ErrSQLCODE, Codepart, TYPE, MsgText)
VALUES(SQLCODE, codepart_var, 'E', SYSPROC.SQLERRM(SQLCODE));
END;
--Define Warning-Handler
DECLARE CONTINUE HANDLER FOR SQLWARNING, NOT FOUND
BEGIN
INSERT INTO SCHEMA.ErrorLog_lrc_test(ErrSQLCODE, Codepart, TYPE, MsgText)
VALUES(SQLCODE, codepart_var, 'W', SYSPROC.SQLERRM(SQLCODE));
END;
-- Set temporary variable to 'a' to get part of code where error occured
SET codepart_var = 'a';
-- Create Error
sELECT 1/divisor into test_INT
FROM SYSIBM.SYSDUMMY1;
SET codepart_var = 'b';
-- Create Error
sELECT 1/divisor into test_INT
FROM SYSIBM.SYSDUMMY1;
SET codepart_var = 'c';
-- Create Not Found (Sqlcode 100)
INSERT INTO SCHEMA.ErrorLog_lrc_test
SELECT NULL, NULL, NULL, NULL FROM "SYSIBM".SYSDUMMY1
WHERE 1 = 0 ;
END
call SCHEMA.test_error(0);
SELECT *
FROM SCHEMA.ErrorLog_lrc_test;
I get the following:
ERRSQLCODE
CODEPART
TYPE
MSGTEXT
-801
a
E
SQL0801N Division by zero was attempted.
-801
b
E
SQL0801N Division by zero was attempted.
100
c
W
SQL0100W No row was found for FETCH, UPDATE or DELETE; or the result of a query is an empty table.
So I am able to get the part of the code where an error or warning occurs, but it would be better to get the line or the SQL statement as I don't want to specify every part of the code with a temporary variable.
I already found this SQLCA --> sqlerrd(3): "...If an error is encountered during the compilation of an SQL routine, trigger, or dynamic compound SQL (inlined or compiled) statement, sqlerrd(3) contains the line number where the error was encountered". For now I didn't manage to make use of SQLCA variables. I don't know how to implement them in DB2 LUW in a stored procedure.
Is there another/better way to log the specific line or SQL-statement in a stored procedure which is causing an error?
My DB2 version is 10.5.0.
Thank you!

If your Db2-server platform is Linux/Unix/Windows, and you are using a recent version, then consider using DBMS_UTILITY.FORMAT_ERROR_BACKTRACE which may help you.
Documentation here. The documentation includes a worked example.
When using this for stored procedures or routines, it is wise to always create those routines with a meaningful specific name with the SPECIFIC clause on the create or replace statement. Otherwise the routine will have a system generated name which will not be meaningful to users when it appears in the output of DBMS_UTILITY.FORMAT_ERROR_BACKTRACE. There are other reasons you should always use a specific name for your routines.
The SQLCA is for calling programs (i.e. the program that calls the stored procedure).

Related

How to call a procedure with a rowtype literal as parameter in PL/SQL?

Lets say I have a table and a procedure that accepts one argument of the tables rowtype:
CREATE TABLE t (a NUMBER, b NUMBER);
CREATE PROCEDURE p (x t%ROWTYPE) IS
BEGIN
NULL;
END;
Can I call that procedure using a rowtype literal, that is without explicitly creating a rowtype variable (or at least not explicitly listing and assigning every field of it)? The two following approaches both generate the below error:
p(1, 2);
p((1, 2));
PLS-00306: wrong number or types of arguments in call to 'P'
You could also construct the record from a cursor loop:
for r in (
select 1, 2 from dual
)
loop
p(r);
end loop;
Unfortunately PL/SQL records are just simple structures and don't come with constructors as object types do. (I wish they did.)
This is not an optimal solution, since it (a) requires the creation of a variable and (b) isn't very pretty. But at least it works:
DECLARE
x t%ROWTYPE;
BEGIN
SELECT 1, 2 INTO x FROM dual;
p(x);
END;
I am still interested in better solutions.

Oracle 11g - Passing boolean to stored procedure

I am trying to create a stored procedure with boolean input parameter:-
Step 1: I have created a table like below
CREATE TABLE STOREBOOL ( BOOLVAL NUMBER(1));
Step 2: I created a stored procedure
Based on the following link: http://docs.oracle.com/cd/F49540_01/DOC/java.815/a64685/tips3.htm#1005343 created a procedure
CREATE OR REPLACE PROCEDURE boolProc(x boolean)
AS
BEGIN
INSERT INTO storebool("boolval") VALUES(x);
COMMIT;
END;
Output is:-
Warning: Procedure created with compilation errors.
Similarly created a second procedure:
CREATE OR REPLACE PROCEDURE boolWrap(x int)
AS
BEGIN
IF (x=1) THEN
boolProc(TRUE);
ELSE
boolProc(FALSE);
END IF;
END;
Output is:-
Warning: Procedure created with compilation errors.
Step 3: Executing the code
BEGIN
boolWrap(1);
END;
/
It is showing the following error:
boolWrap(1);
*
ERROR at line 2:
ORA-06550: line 2, column 1
PLS-00905: object SCOTT.BOO
ORA-06550: line 2, column 1
PL/SQL: Statement ignored
How do i run it properly?
Did you notice the "created with compilation errors" messages? You can see what the errors were immediately after you see that with show errors, or by querying the user_errors view.
In the first procedure you're dong this:
INSERT INTO storebool("boolval") VALUES(x);
... which has two problems: (a) you don't have a column called "boolval"; it was created unquoted as BOOLVAL so either refer to it unquoted, or quoted but in uppercase as it appears in the data dictionary; and (b) you're trying to set a number column to a boolean value. Oracle SQL doesn't have a boolean type, but there is no implicit conversion available. You need to define what number represents TRUE, and what represents FALSE, and then insert that number instead of the boolean x argument. So you'd see errors like:
4/46 PLS-00382: expression is of wrong type
4/28 PL/SQL: ORA-00904: "boolval": invalid identifier
The boolWrap can't compile because boolProc isn't valid.
Your boolProc and boolWrap procedures are reversed really; you want the 'real' procedure to take a number argument, and the wrapper to take a boolean and convert that to a number to call the real one.
This might make more sense:
CREATE TABLE STOREBOOL (BOOLVAL NUMBER(1),
CONSTRAINT BOOLCHECK CHECK (BOOLVAL IN (0,1))
);
CREATE OR REPLACE PROCEDURE boolProc(p_bool_num number)
AS
BEGIN
INSERT INTO storebool(boolval) VALUES (p_bool_num);
END;
/
CREATE OR REPLACE PROCEDURE boolWrap(p_bool boolean)
AS
l_bool_num number;
BEGIN
IF p_bool THEN
boolProc(1);
ELSE
boolProc(0);
END IF;
END;
/
Then you can call boolProc with a number argument, or boolWrap with a boolean argument:
BEGIN
boolProc(1);
END;
/
BEGIN
boolWrap(false);
END;
/
select * from storebool;
BOOLVAL
----------
1
0

Difference of execution in SQL Developer and SQL *Plus

The following code runs good as expected in SQL Developer without any errors, but when I run the code in SQL*Plus it is throwing an error.
declare
v_num1 integer := '&a';
v_num2 integer := '&b';
v_result number;
begin
v_result := v_num1 / v_num2;
dbms_output.put_line ('v_result: '||v_result);
end;
The error raised is
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 2
Please point out where the error I made.
Your SQL*Plus session has substitution variables turned off, so the '&a' is being treated as a literal string, and it isn't prompting you for the value. In a simplified version:
set define off
declare
i integer := to_number('&a');
begin
null;
end;
/
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 2
To fix it, turn substitution variables back on:
set define &
Then it works:
declare
i integer := to_number('&a');
begin
null;
end;
/
Enter value for a:
If I enter 1:
PL/SQL procedure successfully completed.
You don't need the quotes, or the to_number(), unless you're using group seperators (and then you'd need to supply a format model too). You can just do:
declare
i integer := &a;
begin
null;
end;
/
As you have it you're doing an unnecessary implicit string-to-number conversion.
If you haven't explicitly turned them off, then you probably have a site or user profile that is doing the set define off, among other configuration settings. it's probably doing that for a reason, but if you currently only have a site profile then you could create your own user profile to override that. Just be aware of the effect of anything you change; you may have other scripts that rely on it being off, e.g. if you have data that has ampersands and it's been disabled to prevent issues interpreting those.

PL/SQL anonymous block to test a procedure

pL/SQL PROCEDURE
This is the code for a procedure that takes the term,lineno,component name,student id,score as input and processes the student score.
The procedure should add the score into the scores table if there are no exceptions.
CODE
CREATE OR REPLACE PROCEDURE score_details
(aterm IN scores.Term%type,
alineno IN scores.Lineno%type,
acompname IN scores.Compname%type,
asid IN scores.sid%type,
apoints IN scores.points%type)
AS sterm scores.Term%type;
slineno scores.Lineno%type;
scompname scores.compname%type;
ssid scores.sid%type;
spoints scores.points%type;
BEGIN
SELECT term,lineno,compname,sid,points
INTO sterm,slineno,scompname,ssid,spoints
FROM scores
WHERE aterm=term AND alineno=Lineno AND acompname=compname AND asid=sid AND apoints=points;
EXCEPTION
when no_data_found THEN
dbms_output.put_line('Invalid details');
ANONYMOUS BLOCK
The below is the code for anonymous block to test the above procedure.
I'm not able to get the correct result.Please help me with the code.
ACCEPT prompt 'pterm','plineno','pcompname','psid','ppoints'
DECLARE pterm scores.Term%type;
plineno scores.Lineno%type;
pcompname scores.Compname%type;
psid scores.sid%type;
ppoints scores.points%type;
BEGIN
score_details(pterm,plineno,pcompname,psid,ppoints);
END
I'm going to assume that you've intentionally missed out most of the body of your stored procedure, as it fetches some values out of a table into some local variables and then does nothing with these values.
I'm guessing that you're wondering why the values you entered in the ACCEPT line didn't make it into the stored procedure call.
ACCEPT is a SQL*Plus statement that you can use to set substitution variables. The following example creates a substitution variable named colour and displays the result. The line Yellow was typed in by me:
SQL> ACCEPT &colour
Yellow
SQL> PROMPT Your favourite colour is &colour
Your favourite colour is Yellow
You can also provide a prompt to the user to clarify what you're asking for:
SQL> ACCEPT colour PROMPT 'Enter a colour > '
Enter a colour > Yellow
and you can also use substitution variables in SQL:
SQL> select '&colour' from dual;
old 1: select '&colour' from dual
new 1: select 'Yellow' from dual
'YELLO
------
Yellow
It seems there's two things not quite right with your PL/SQL block at the moment:
Your ACCEPT statement isn't working. You haven't quite got the order of the parts of it right (if used, PROMPT must come after the variable name). Also, I don't think you can set more than one substitution variable in a single ACCEPT.
You're not using the substitution variables that contain the values entered.
I'd therefore imagine that you'd want the call to your PL/SQL block to look a bit more like the following:
ACCEPT pterm PROMPT 'Enter a term > '
ACCEPT plineno PROMPT 'Enter a line number > '
-- and similarly for the others.
DECLARE
pterm scores.Term%type := '&pterm';
plineno scores.Lineno%type := '&plineno';
pcompname scores.Compname%type := '&pcompname';
psid scores.sid%type := '&psid';
ppoints scores.points%type := '&ppoints';
BEGIN
score_details(pterm,plineno,pcompname,psid,ppoints);
END;

Undeclared identifier:

I have an EXCEL sheets witgh various
C:\Documents and Settings\delfi\MilkDataMgr\FtsExcel.pas(2056):Undeclared identifier: smrBgm167GallonsGrosssDA'
procedure convertXSLToDelfi(fuel: TFtsFuelTypes; const prefix: string);
var
ColumnNameMap : TStrings;
i : Integer;
other : string;
begin
{ ColumnNameMap := TStrings; }
ColumnNameMap := TStringList.Create;
ColumnNameMap.Values['smrBgm229GallonsGross']:=' smrBgm167GallonsGrosssDA';
i := ColumnNameMap.IndexOfName(smrBgm229GallonsGross);
if i >= 0 then
smrBgm229GallonsGross:= ColumnNameMap.Values[smrBgm229GallonsGross]
else
smrBgm229GallonsGross:= smrBgm229GallonsGross;
end;
a detailed issue is in this link, i folowed the solution offered there
how to create Delphi 4 structure to map column names in XLS to column names in SQL
I am just picking up threads kindly help me out.
In the code I gave you, notice that the value inside the brackets for the Values property was quoted. It's a string. Maybe you meant to have "smrBgm229GallonsGross" in quotes, too, like this:
ColumnNameMap.Values['smrBgm229GallonsGross'] := 'smrBgm167GallonsGrosssDA';
In your code, the compiler complains that it doesn't recognize the identifier smrBgm229GallonsGross. Look at your code. Have you declared such an identifier? If not, then you can't expect the compiler to know what you're asking of it, and the error message makes perfect sense.
(If you were using Perl, the compiler might have known what you wanted. There are certain situations where a so-called "bareword" will be interpreted as a string literal rather than an identifier. But that's Perl, not Delphi.)
So far, I've only been looking at the first line of code that mentions smrBgm229GallonsGross. However, there are five more lines of code, where you look up the index of the name and then assign values to a variable, and those will need a proper declaration of a variable. In my example, I used the made-up variable name ColumnName to represent the name of whatever input column you happen to be processing at the time. I assumed you would be iterating over a list of columns in a loop, so you would do the same thing for each column, taking a value from your Excel spreadsheet and transferring it into a corresponding column in the database, which was represented by the also-made-up variable name RealColumnName.

Resources