How to handle error in ibm_db python package while calling stored procedure? - python-2.7

I'm trying call stored procedure using following code
conn = ibm_db.connect("database","username","password")
sql = "CALL DB2INST1.KPI_VALIDATE()"
stmt = ibm_db.exec_immediate(conn, sql)
But this procedure does not return any rows & It will only return code. Now I need to handle error whether procedure run successfully or not. Could anyone help me how to handle this?
Thanks

For test purposes, I've created a table:
db2 "create table so(c1 int not null primary key)"
and my procedure will simply insert a row into this table - this will allow me to easily force an error with a duplicate key:
db2 "create or replace procedure so_proc(in insert_val int)
language sql
insert into so values(insert_val)"
db2 "call so_proc(1)"
Return Status = 0
db2 "call so_proc(1)"
SQL0803N One or more values in the INSERT statement, UPDATE statement, or
foreign key update caused by a DELETE statement are not valid because the
primary key, unique constraint or unique index identified by "1" constrains
table "DB2V115.SO" from having duplicate values for the index key.
SQLSTATE=23505
now with Python:
conn = ibm_db.connect("DATABASE=SAMPLE;HOSTNAME=localhost;PORT=61115;UID=db2v115;PWD=xxxxx;","","")
stmt = ibm_db.exec_immediate(conn, "CALL SO_PROC(2)")
stmt = ibm_db.exec_immediate(conn, "CALL SO_PROC(2)")
Exception Traceback (most recent call last)
<ipython-input-8-c1f4b252e70a> in <module>
----> 1 stmt = ibm_db.exec_immediate(conn, "CALL SO_PROC(2)")
Exception: [IBM][CLI Driver][DB2/LINUXX8664] SQL0803N One or more values in the INSERT statement, UPDATE statement, or foreign key update caused by a DELETE statement are not valid because the primary key, unique constraint or unique index identified by "1" constrains table "DB2V115.SO" from having duplicate values for the index key. SQLSTATE=23505 SQLCODE=-803
so if a procedure hits an exception then you'll get it, you just need to handle exception Try/Except block:
try:
stmt = ibm_db.exec_immediate(conn, "CALL SO_PROC(2)")
except Exception:
print("Procedure failed with sqlstate {}".format(ibm_db.stmt_error()))
print("Error {}".format(ibm_db.stmt_errormsg()))
Procedure failed with sqlstate 23505
Error [IBM][CLI Driver][DB2/LINUXX8664] SQL0803N One or more values in the INSERT statement, UPDATE statement, or foreign key update caused by a DELETE statement are not valid because the primary key, unique constraint or unique index identified by "1" constrains table "DB2V115.SO" from having duplicate values for the index key. SQLSTATE=23505 SQLCODE=-803
Or you are actually interested with CALL return code/status? E.g.:
create or replace procedure so_proc_v2(in insert_val int)
language sql
if not exists (select 1 from so where c1 = insert_val)
then
insert into so values(insert_val);
return 0;
else
return -1;
end if#
test:
db2 "call so_proc_v2(10)"
Return Status = 0
db2 "call so_proc_v2(10)"
Return Status = -1
then this is a bit tricky. With CLI trace enabled (I have ibm_db installed in my local path so it fetched CLI package there too):
export LD_LIBRARY_PATH=$HOME/.local/lib/python3.7/site-packages/clidriver/lib/
$HOME/.local/lib/python3.7/site-packages/clidriver/bin/db2trc on -cli -f /tmp/cli/trc
<run_code>
$HOME/.local/lib/python3.7/site-packages/clidriver/bin/db2trc off
$HOME/.local/lib/python3.7/site-packages/clidriver/bin/db2trc fmt -cli /tmp/cli.trc /tmp/cli.fmt
trace does show the returns status:
SQLExecute( hStmt=1:8 )
---> Time elapsed - -7.762688E+006 seconds
( Row=1, iPar=1, fCType=SQL_C_LONG, rgbValue=10 )
( return=-1 )
( COMMIT REQUESTED=1 )
( COMMIT REPLY RECEIVED=1 )
but I don't see anywhere in python-ibmdb API a way to fetch it... (e.g. ibm_dbcallproc doesn't have such option). Which means, that unless I'm missing something, you would have to raise an issue on Github to extent the API

Related

QSqlQuery row affected result

SHA256 sha256;
std::string s = sha256(pass.toStdString());
QString myquery = "declare #identifier nvarchar(100) = NEWID()\
declare #user_name nvarchar(50) = '"+user+"'\
declare #pass_word nvarchar(100) = '"+QString::fromStdString(s)+"'\
declare #hint nvarchar(50) = '"+hint+"'\
if NOT exists(select * from user_table where (userid=#identifier or username = #user_name))\
insert into user_table (username,password,password_salt,userid) values(#user_name,#pass_word,#hint,#identifier)";
qDebug()<<myquery;
openSqlConnection();
QSqlQuery q3;
q3.exec(myquery);
After executing this query, I should see result which will be (1 row(s) affected) if executed successfully.
If username already exists in the database, the result will be
Command(s) completed successfully.
To see the result from the select I use q3.next().
How do I know that my query has been executed successfully?
How do I know that my query has been executed successfully?
You have a number of options here. You could check either:
The returned value of QSqlQuery::exec
Returns true and sets the query state to active if the query was successful; otherwise returns false.
for example:
if (!q3.exec(myquery))
\\ show error
The status of the query, i.e. QSqlQuery::isActive
An active QSqlQuery is one that has been exec()'d successfully but not yet finished with.
for example:
q3.exec(myquery);
if (!q3.isActive())
\\ show error
The type of QSqlQuery::lastError
QSqlError::NoError 0 No error occurred.
for example:
q3.exec(myquery);
if (q3.lastError().type() != QSqlError::NoError)
\\ show error
If you pass the check (the one you have selected), you could process your query further, e.g. to see if the username already exists and how many rows have been affected.

Oracle APEX: Error Message in field raises Error Message

I have the following code:
DECLARE
i NUMBER;
BEGIN
SELECT COUNT(*) INTO i FROM apex_collections where collection_name = 'COLLECTION';
if i = 0 then
apex_error.add_error(
p_message => 'Invalid Excel file!'
, p_display_location => apex_error.c_inline_with_field
, p_page_item_name => 'P3_BROWSE_EXCEL'
);
end if;
END;
All it does is check if a collection exists. If not, it should raise an error.
I am calling this from a Process, so an Alert or something similar is no alternative.
Instead of showing the correct error message next to the P3_BROWSE_EXCEL - item, it shows this error on top of the page:
1 error has occurred
Error: Not found
I am 100% sure P3_BROWSE_EXCEL exists on that page, so why?
Why don't you use simple SQL validation? Validation Type - Rows returned, the query
SELECT 1 FROM apex_collections where collection_name = 'COLLECTION';
Display location - Inline with field, Associated Item - you can choose an item from a list with 100% guarantee that it exists.

Sqlite3: Using try: except: to establish db table & default data set - kosher?

I have written up the following initializing routine for an sqlite3 db table, but something tells me this is a) fragile, and/or b) Just a Bad Idea(tm).
The notion is that if the table is not present or it has not been initiailized, the try blocks will fault and the data will be created. In initial testing this works - although I am not seeing the defaults printed to console when the script runs: I get an empty tuple printed. Examining the database using the sqlite shell I see the data is present.
But that niggling feeling lingers that there is something very wrong with this approach. Thoughts? Opinions? Advice?
import sqlite3 as lite
import sys
def insert_defaults():
conn = lite.connect('db.sqlite')
with conn:
cur = conn.cursor()
defaults = (
('mykey','value for key one'),
('anotherkey','value for key two')
)
cur.executemany("INSERT INTO Settings(key,value) VALUES ( ?, ? )", defaults)
def initialize():
conn = lite.connect('db.sqlite')
settings = ()
try:
conn.row_factory = lite.Row
cur = conn.cursor()
cur.execute("SELECT * FROM Settings")
if cur.rowcount < 1:
insert_defaults()
cur.execute("SELECT * FROM Settings")
settings = cur.fetchall()
except lite.Error, e:
print "Error: %s" % e.args[0]
print "Trying to create missing table"
try:
cur.execute( "DROP TABLE IF EXISTS Settings" )
cur.execute("CREATE TABLE IF NOT EXISTS Settings (id INTEGER PRIMARY KEY, key TEXT NOT NULL, value TEXT)")
insert_defaults()
except lite.Error, e:
if conn:
conn.rollback()
print "Error: %s" % e.args[0]
sys.exit(1)
finally:
if conn:
conn.close()
return settings
if __name__ == "__main__":
print initialize()
Erik
Relying on exceptions to detect that the table does not exist is not very reliable because there could be other errors that have nothing to do with what you want to check.
The easiest way to ensure that the table is created is to just execute CREATE TABLE IF NOT EXISTS ... when your program is started; this will be ignored if the table already exists.
To check for some records existing, using a SELECT is fine.
However, if you have a UNIQUE or PRIMARY KEY constraint on the key column, you could just execute INSERT OR IGNORE INTO Settings....
You should not use a separate connection in insert_defaults(); this will lead to problems if you don't get your transaction commits correct.

Transaction in SP avoid correct answer

I have an SP that work very well when called from SSMS. but when I call it from my application
that written in native C++ and use ODBC for connecting to database, the operation return no error but actually do nothing in the database.
My SP read some values from some temporary tables and either insert them in database or update them.
I had a transaction in SP that guard all the code of SP, I hardly debug my SP and find that function will return in first insert or update and so do nothing. So I remove that transaction and function partly worked, I mean it add some of the items but leave some of them there without adding them to the database.
here is a skeleton of my SP:
--BEGIN TRANSACTION
DECLARE #id bigint, #name nvarchar(50)
DELETE FROM MyTable WHERE NOT( id IN (SELECT id from #MyTable) )
DECLARE cur1 CURSOR FOR SELECT id, name FROM #MyTable
OPEN cur1
WHILE 1 != 0
BEGIN
FETCH cur1 INTO #id, #name
IF ##FETCH_STATUS != 0 BREAK;
UPDATE MyTable SET [Name]=#name WHERE [id]=#id
IF ##ROWCOUNT = 0
INSERT INTO MyTable ( ID, Name ) VALUES ( #id, #name )
END
CLOSE cur1
DEALLOCATE cur1
--COMMIT TRANSACTION
Is it possible you have an implicit transaction started in ODBC that needs an explicit COMMIT to end (after the call to the SP)? SSMS generally uses autocommit mode.
I solve my problem by adding SET NOCOUNT ON to start of my SP, so I think when SQL return multiple result set as a result of executing my SQL, ODBC close or cancel the command upon receiving first result set.

How to catch Unique constraint error in POSTGRESQL

If I create a table with a unique contraint, for example:
CREATE TABLE distributors (
did integer,
name varchar(40) UNIQUE
);
What would happen if I try to enter an entry with the name that already exists. I tried to do so and it just quit without displaying any error message. Is there a way to check whether a new entry was actually inserted?
If the insert failed than there should be error code set somewhere, readable by some method of the interface you're using - more details are definitely in documentation to your access library/module.
Alternatively you can change your insert to:
INSERT INTO distributors (did, name) values ( ... ) RETURNING did;
And if it didn't return anything - there has been error.
If you try to insert record with name that already exists, you'll receive error message like this:
ERROR: duplicate key value violates unique constraint "distributors_name_key"
DETAIL: Key (name)=(aaa) already exists.
and record will not be inserted.
If you do it from allplcation level, the exception will be thrown with a message similar to this. It's up to the programmer how to handle this exception.
If your ID field is autogenerating (SERIAL or BIGSERIAL), and you insert just name, if you insert name which already exists, ID sequence will increase by 1, even if you didn't insert any record.
To avoid that you can make "SELECT" query before INSERT to check, it the record already exists. Possible to do all in one transaction, in pseudo-code:
BEGIN TRANSACTION;
int records = SELECT name FROM table WHERE name = 'aaa' FOR UPDATE; //FOR UPDATE to lock the row from being read by other user until transaction finishes.
if (records == 0)
INSERT INTO table VALUES (1, 'aaa');
else
MessageBox.Show("Record already exists");
COMMIT TRANSACCTION;