DB2 Logging in stored procedure - db2

Is it possible to create log outputs in a specific mode like debug or info within a stored procedure?
I just know only awared about the cmd DBMS_OUTPUT.PUT_LINE. But I need it with specification of the log level.

--#SET TERMINATOR #
CREATE TABLE LOG (TS TIMESTAMP NOT NULL, MSG VARCHAR (100))#
CREATE OR REPLACE PROCEDURE LOGGER (P_MSG VARCHAR (100))
AUTONOMOUS
BEGIN
INSERT INTO LOG (TS, MSG) VALUES (GENERATE_UNIQUE()::TIMESTAMP, P_MSG);
END#
CREATE TABLE TEST (I INT)#
CREATE OR REPLACE TRIGGER TEST_AIR
AFTER INSERT ON TEST
REFERENCING NEW AS N
FOR EACH ROW
BEGIN ATOMIC
CALL LOGGER ('Start of insertion: ' || N.I);
CALL DBMS_ALERT.SLEEP (3);
CALL LOGGER ('End of inserion: ' || N.I);
END#
INSERT INTO TEST VALUES 1, 2#
SELECT * FROM LOG ORDER BY TS#
TS
MSG
2023-02-13-17.41.57.098209
Start of insertion: 1
2023-02-13-17.42.00.115693
End of inserion: 1
2023-02-13-17.42.00.137199
Start of insertion: 2
2023-02-13-17.42.03.163761
End of inserion: 2

Related

Trying to use temporary table in IBM DB2 and facing issues

I am getting the following error while creating a stored procedure for testing purpose:
SQL Error [42601]: An unexpected token "DECLARE GLOBAL TEMPORARY TABLE
SESSION" was found following "RSOR WITH RETURN FOR". Expected tokens may include: "".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.21.29
Code:
CREATE OR REPLACE PROCEDURE Test ( IN GE_OutPutType SMALLINT)
----------------------------------------------------------------------------------------------------
DYNAMIC RESULT SETS 1 LANGUAGE SQL
BEGIN
DECLARE C CURSOR WITH RETURN FOR DECLARE GLOBAL TEMPORARY TABLE
SESSION.TEMP (DATE CHAR(10) NOT NULL,
SALARY DECIMAL(9,
2) ,
COMM DECIMAL(9,
2));
INSERT
INTO
SESSION.TEMP (DATE,
SALARY,
COMM) SELECT
VARCHAR_FORMAT(CURRENT_DATE,
'MM/DD/YYYY'),
10.2,
11.5
FROM
sysibm.sysdummy1
IF GE_OutPutType = 1
BEGIN
SELECT
*
FROM
TEMP
ELSEIF GE_OutPutType = 2 SELECT
'HEADER' CONCAT SPACE(1979) CONCAT 'H'
FROM
sysibm.sysdummy1
END OPEN C;
END
Your syntax is not valid.
You must declare your temporary table independently of your cursor.
You cannot combine these in a single statement.
Use dynamic-SQL features to achieve what you need.
Use instead the format:
Declare c1 cursor with return to caller for Statement1
and
set v_cursor_text = 'select ... from session.temp ; `
then use
prepare Statement1 from v_cursor_text;
and before you exit the stored procedure you need to leave the cursor opened:
open c1;
Do study the Db2 online documentation to learn more about these features.
Here is a small fragment of your procedure showing what I mean:
CREATE OR REPLACE PROCEDURE mytest ( IN GE_OutPutType SMALLINT)
DYNAMIC RESULT SETS 1
LANGUAGE SQL
specific mytest
BEGIN
DECLARE v_cursor_text varchar(1024);
DECLARE C1 CURSOR WITH RETURN FOR Statement1;
DECLARE GLOBAL TEMPORARY TABLE SESSION.TEMP (
DATE CHAR(10) NOT NULL,
SALARY DECIMAL(9,
2) ,
COMM DECIMAL(9,
2))
with replace on commit preserve rows not logged;
INSERT INTO SESSION.TEMP (DATE, SALARY, COMM)
SELECT VARCHAR_FORMAT(CURRENT_DATE, 'MM/DD/YYYY'),
10.2,
11.5
FROM sysibm.sysdummy1 ;
if GE_OutPutType = 1
then
set v_cursor_text = 'select * from session.temp';
end if;
if GE_OutPutType = 2
then
set v_cursor_text = 'select ''header'' concat space(1979) concat ''H'' from sysibm.sysdummy1';
end if;
prepare Statement1 from v_cursor_text;
open c1;
END#

Error: DB2 Stored procedure with the error A value is not compatible with the data type of its assignment target. Target name is "C1"

While creating the stored procedure I am getting the error
"A value is not compatible with the data type of its assignment target. Target name is "C1".. SQLCODE=-408, SQLSTATE=42821, DRIVER=4.19.56"
Help me to create a stored procedure with cursor variable
CREATE OR REPLACE TYPE DE_ROW_T as row ( C1 INTEGER,C2 VARCHAR(100),C2 CHAR(1));
CREATE OR REPLACE TYPE C_ID_CURSOR_T as DE_ROW_T CURSOR;
CREATE OR REPLACE PROCEDURE "SP_CURTEST"
LANGUAGE SQL
BEGIN
DECLARE V_ROW DE_ROW_T;
DECLARE C1 C_ID_CURSOR_T;
SET C1 = CURSOR FOR
SELECT DISTINCT
COL1,COL2,COL3
FROM
MYTABLE
WHERE COL1=101;
OPEN C1;
FETCH C1 INTO V_ROW;
END;
Your table DDL must match the row type in datatype and lengths, otherwise you will get exceptions trying to fit the wrong data into your rowtype variable.
The following script shows how you can see the stored-procedure operating with Db2-LUW v11.5.4.0 on Linux, via printing out the progress as debugging tool. You can also debug stored procedures with other GUI tools, including IBM Data Studio.
--#SET TERMINATOR ;
CREATE OR REPLACE TYPE DE_ROW_T as row ( C1 INTEGER,C2 VARCHAR(100),C3 CHAR(1));
CREATE OR REPLACE TYPE C_ID_CURSOR_T as DE_ROW_T CURSOR;
drop table if exists mytable;
create table mytable(col1 integer, col2 varchar(100), col3 char(1));
insert into mytable(col1,col2,col3) values(101,'abcd','a');
--#SET TERMINATOR #
set serveroutput on#
CREATE OR REPLACE PROCEDURE "SP_CURTEST"
LANGUAGE SQL
specific curtest
BEGIN
DECLARE V_ROW DE_ROW_T;
DECLARE C1 C_ID_CURSOR_T;
SET C1 = CURSOR FOR SELECT DISTINCT COL1,COL2,COL3 FROM MYTABLE WHERE COL1=101;
call dbms_output.put_line('opening c1');
OPEN C1;
call dbms_output.put_line('opened c1');
FETCH C1 INTO V_ROW;
call dbms_output.put_line('fetched c1: '||varchar(v_row.c1)||' , '||trim(v_row.c2)||' , '||v_row.c3);
END
#
call sp_curtest()#
IF you run the above script via the db2 CLP at the command line, it will show the output below:
$ db2 -tvf sp_so_8.sql
CREATE OR REPLACE TYPE DE_ROW_T as row ( C1 INTEGER,C2 VARCHAR(100),C3 CHAR(1))
DB20000I The SQL command completed successfully.
CREATE OR REPLACE TYPE C_ID_CURSOR_T as DE_ROW_T CURSOR
DB20000I The SQL command completed successfully.
drop table if exists mytable
DB20000I The SQL command completed successfully.
create table mytable(col1 integer, col2 varchar(100), col3 char(1))
DB20000I The SQL command completed successfully.
insert into mytable(col1,col2,col3) values(101,'abcd','a')
DB20000I The SQL command completed successfully.
set serveroutput on
DB20000I The SET SERVEROUTPUT command completed successfully.
CREATE OR REPLACE PROCEDURE "SP_CURTEST"
LANGUAGE SQL
specific curtest
BEGIN
DECLARE V_ROW DE_ROW_T;
DECLARE C1 C_ID_CURSOR_T;
SET C1 = CURSOR FOR SELECT DISTINCT COL1,COL2,COL3 FROM MYTABLE WHERE COL1=101;
call dbms_output.put_line('opening c1');
OPEN C1;
call dbms_output.put_line('opened c1');
FETCH C1 INTO V_ROW;
call dbms_output.put_line('fetched c1: '||varchar(v_row.c1)||' , '||trim(v_row.c2)||' , '||v_row.c3);
END
DB20000I The SQL command completed successfully.
call sp_curtest()
Return Status = 0
opening c1
opened c1
fetched c1: 101 , abcd , a
$

Question for SAP DBTech JDBC when trigger is execute

I would like to ask what is this error and how do I fix for it?
SAP DBTech JDBC: [7]: feature not supported: trigger execution with transition variable is not supported with piecewise lob writing.
Situation:
Read from a file that more than 100kb. Then insert into SAP HANA database.
The column to be inserted is BLOB type.
This table have a trigger which will insert the same information into another table (For tracking purpose).
If without this trigger, it able to insert data without any error.
I cannot give the actual code due to confidential. So I create a sample below.
I'm create a hdbtrigger file and compile it using SAP HANA Database Module in SAP Web IDE to create the table, trigger, etc.
TRIGGER "M_IMG_T_T"
AFTER INSERT OR UPDATE OR DELETE
ON "M_IMG_T"
REFERENCING NEW ROW new_row, OLD ROW old_row
FOR EACH ROW
BEGIN
DECLARE upd_kbn VARCHAR(1) := 'U';
IF :new_row."ID_PK" IS NULL THEN
upd_kbn = 'D';
ELSEIF :old_row."ID_PK" IS NULL THEN
upd_kbn = 'I';
END IF;
IF :upd_kbn = 'I' OR :upd_kbn = 'U' THEN
INSERT INTO "M_IMG_T_H" VALUES(
:new_row."ID_PK",
:new_row."I_IMG_FILE_DATA"
);
ELSE
INSERT INTO "M_IMG_T_H" VALUES(
:new_row."ID_PK",
:new_row."I_IMG_FILE_DATA"
);
END IF;
END
When I export catalog of the table, I will get this. (I just copy insert part)
CREATE TRIGGER "M_IMG_T_T_I" AFTER INSERT ON "M_IMG_T" REFERENCING NEW ROW NEW_ROW FOR EACH ROW
BEGIN
DECLARE UPD_CLS VARCHAR(2) := 'I';
INSERT INTO "M_IMG_T_H" (
"I_ID",
"I_IMG_FILE_DATA"
) VALUES(
:NEW_ROW."I_ID",
:NEW_ROW."I_IMG_FILE_DATA"
);
END

Ingres stored procedure to delete multiple records from table

I am creating a SP in Ingres to delete multiple records from single table by comma separated id's, but it is not working. Though when I execute it in a separate query (without storedprocedure) then it is deleting records.
create procedure sptest
(
In Ids varchar(300)
)
AS
BEGIN
Delete from "ingres".mytable where request_id IN (:Ids);
END
Requested rows should be deleted from table
The input is a varchar so in effect what you have in the delete statement is something like:
delete from mytable where request_id in ('1,2,3,4');
Inside a database procedure you can't run "execute immediate", so you can't build a delete string without the quotes and execute it dynamically (though this might be an option to you if your calling program has "execute immediate" available).
To process the IN list within a database procedure I think you'll need to loop through the input string and delete for each value...
eg:
set session authorization ingres;
drop table if exists mytable;
create table mytable(request_id integer);
insert into mytable values(1),(2),(5),(10);
drop procedure if exists sptest;
create procedure sptest
(
In Ids varchar(300)
)
AS
declare msg = varchar(300) not null;
eno = integer not null;
rc = integer not null;
pos = integer not null;
n = varchar(300);
BEGIN
while (length(:Ids) > 0)
do
pos = locate(:Ids, ',');
n = left(:Ids, :pos-1);
Ids = shift(:Ids, -1 * pos);
msg = 'Removing ' + :n;
message :msg;
Delete from "ingres".mytable where request_id = integer(:n);
select iierrornumber, iirowcount into :eno, :rc;
msg = 'Error number '+varchar(:eno) + ' rowcount ' + varchar(:rc);
message :msg;
endwhile;
END;
execute procedure sptest('1,5,10');
select * from mytable;

Load contents from a CSV file into a PostgreSQL table

Below is a description of the procedure I went through to try and load data from a file into a PostgreSQL 8.0 database running on a Linux RedHat 7.2 host.
Now, my issue is that the FOR EVERY ROW trigger is getting called and the procedure is executing.
What I'd like it to do, however, is have it check the appropriate row of my table once I have given in the filename and decide based on the contents of the record whether to do a DUMP BULK DATA or a DUMP WHOLE CSV FILE only once (on the trigger).
Please help me solve this issue...
My logfile.tmp is as follows:
27/Apr/2013:17:03:42 +0530#192.168.1.3#16#0##$http://localhost/images/
banner-left.jpg##$10.1ff.ff.ff#-#Y#-
27/Apr/2013:17:03:42 +0530#192.168.1.3#16#0##$http://localhost/images/
banner-left.jpg##$10.ff.ff.2ff05#-#Y#-
The COPY command I am using:
/usr/local/pgsql/bin/psql localhost -d d1 -U u1 -tc "COPY tblaccesslog ( accesstime, clientip, username, request,bytes, urlpath, url, contenttype, issite, webcatname) FROM 'logfile.tmp' WITH DELIMITER AS '#';" >> /tmp/parselog.log 2>&1
The trigger (insert_accesslog_trigger) in question:
insert_accesslog_trigger BEFORE INSERT ON tblaccesslog FOR EACH ROW EXECUTE PROCEDURE accesslog_insert_trigger()
and finally the trigger function (accesslog_insert_trigger()) being used:
accesslog_insert_trigger()
DECLARE
tablemaxtuples NUMERIC(10);
tableno NUMERIC(10);
newtable TEXT;
query TEXT;
tablecount NUMERIC(10);
min_limit NUMERIC(10);
max_limit NUMERIC(10);
BEGIN
tablemaxtuples := 100000;
tableno := ( NEW.id - ( NEW.id % tablemaxtuples ) ) / tablemaxtuples +1;
newtable := 'tblaccesslog'||to_char(CURRENT_DATE,'YYYYMMDD')||'_child_'||tableno;
SELECT trim(count(tablename)) INTO tablecount FROM pg_tables WHERE tablename=newtable ;
IF tablecount = 0
THEN
min_limit := (tableno-1)*tablemaxtuples;
max_limit := min_limit + tablemaxtuples;
query := 'CREATE TABLE '||newtable||'( PRIMARY KEY (id),CHECK ( id >= '||min_limit||' AND id <'||max_limit||' ) ) INHERITS (tblaccesslog)';
EXECUTE query;
END IF;
query := 'INSERT INTO '|| newtable ||' ( id, username, clientip, url, accesstime, requestbytes, contenttype, issite, urlpath, webcatname ) VALUES ('||NEW.id||','''||NEW.username||''','''||NEW.clientip||''','''||NEW.url||''','''||NEW.accesstime||''','''||NEW.requestbytes||''','''||NEW.contenttype||''','''||NEW.issite||''','''|| replace(NEW.urlpath,'\'','') ||''','''||NEW.webcatname||''')';
EXECUTE query;
RETURN NULL;
END;
The PostgreSQL documentation overview of triggers makes clear that there is no type of trigger that suits your requirements: a FOR EACH ROW trigger will, as its name says, be executed once for each row, and as the manual page states "Statement-level triggers do not currently have any way to examine the individual row(s) modified by the statement."
However, what you can do instead is put your actual COPY command inside a function. The function could COPY TO a temporary table and then perform the appropriate steps to determine where it should go from there.
Then your copy command (which I'm guessing is in a cron job or similar) would just run SELECT bulk_insert_access_log(); rather than the long line currently listed.