Dynamic cursor variable in DB2 - db2

I want to open a dynamic cursor variable.
CREATE OR REPLACE PROCEDURE TTT ()
P1: BEGIN
Declare cID char(5) ;
declare c1 cursor for s1 ;
declare stmt varchar(1000) ;
set cid = 'a' ;
Set stmt = 'select * from aaa where a = ?' ;
prePare s1 from stmt ;
open c1 using cid ;
END P1
I get error :
A.TTT - Deploy started.
Create stored procedure returns SQLCODE: -104, SQLSTATE: 42601.
A.TTT: 6: An unexpected token "" was found following "". Expected tokens may include: "".. SQLCODE=-104, SQLSTATE=42601, DRIVER=3.69.56
An unexpected token "" was found following "". Expected tokens may include: "".. SQLCODE=-104, SQLSTATE=42601, DRIVER=3.69.56
A.TTT - Deploy failed.
A.TTT - Roll back completed successfully.

The cause of the error is that the code ignores the documented rules for the order of statements in a compound-SQL block.
One of the rules is that the cursor declaration(s) must appear after all other declared variables and before the start of the SQL PL code block.
So in your question, the order of the variables should be:
Declare cID char(5) ;
declare stmt varchar(1000) ;
declare c1 cursor for s1 ;
For Db2-LUW at current version these rules for compound SQL blocks are here.
To return a resultSet to the caller or the client, remember to add with return to caller or with return to client in the declare c1 cursor line, along with adding dynamic result sets 1 in the procedure definition.
To consume the cursor (i.e. to fetch from it) inside the stored procedure code then you do not need these things.
Your jdbc driver is very old, so plan to upgrade it to a better supported version. You can download the latest versions via this site https://www.ibm.com/support/pages/db2-jdbc-driver-versions-and-downloads
You can also make the error messages more verbose by appending an additional attribute to the connection string URL ;retrieveMessagesFromServerOnGetMessage=true;
This can speed up your ability to solve trivial programming problems when learning.

Related

Calling procedure from PyQt5 using psycopg2 driver PostgreSQL Error 2D000 Invalid transaction termination

I started with PostgreSQL recently and I'm running into trouble calling a procedure from a PyQt5 client using the psycopg2 driver.
I set a simple procedure with 6 parameters, included a BEGIN and a COMMIT statements and run seamlessly from PgAdmin 4.
When tried to call the procedure from the PyQt5 client a got error code 2D000 with the following message:Invalid transaction termination
CONTEXT: PL/pgSQL function es_edit_text_report(integer,date,integer,integer,character varying,character varying) line 10 at COMMIT
I found several mentions to this error both in stackoverflow and in the web but I could not figure it out why am I getting this results. The closer explanation found may be by #Laurenz Albe on Invalid transaction termination but it looks to me that none of the mentioned conditions apply. Here we have a very simple procedure being called just once from a psycopg2 connection opened from a pool for this single purpose.
Following other responses I just commented the commit on the procedure and then I was able to run it from the client. This places some further questions:
I understand that the transaction is not operative any longer, consequently you would not be able to run complex procedures safely unless this issue is solve. I'll appreciate your thoughts about it.
Procedure Code:
CREATE OR REPLACE PROCEDURE es_edit_text_report(IN _id integer, IN _report_date,
IN _responsibleid integer,
IN _categoryid integer,
IN _description varchar,
IN _location varchar)
AS $$
BEGIN
UPDATE text_maintenance_news
SET report_date = _report_date,
reporterid = _responsibleid::smallint,
categoryid = _categoryid::smallint,
description = _description,
location = _location
WHERE id = _id;
COMMIT;
END; $$
LANGUAGE plpgsql
PyQt5 client code:
#pyqtSlot()
def save(self):
try:
with self.connPool.getconn() as conn:
with conn.cursor() as cur:
if self.mode == OPEN_NEW:
cur.execute('CALL es_load_text_report(%s, %s, %s, %s, %s)',(
self.reportDate.date().toString("yyyy-MM-dd"),
self.comboResponsible.getHiddenData(0),
self.comboCategory.getHiddenData(0),
self.txtDescription.text(),
self.txtLocation.text()))
conn.commit()
else:
cur.execute('CALL es_edit_text_report(%s, %s, %s, %s, %s, %s)',
(self.id,
self.reportDate.date().toString("yyyy-MM-dd"),
self.comboResponsible.getHiddenData(0),
self.comboCategory.getHiddenData(0),
self.txtDescription.text(),
self.txtLocation.text()))
conn.commit()
qry = QSqlQuery(self.db)
qry.exec("SELECT * FROM es_load_text_reports()")
if qry.lastError().type() != 0:
raise DataError('save: qry', qry.lastError().text())
self.tableNews.model().setQuery(qry)
self.clear()
except OSError as e:
QMessageBox.warning(self, 'Save : ', e.args, QMessageBox.Ok)
except DataError as e:
QMessageBox.warning(self, e.source, e.message, QMessageBox.Ok)
except DatabaseError as e:
QMessageBox.warning(self, 'Save', f'{e.pgcode} {e.pgerror}', QMessageBox.Ok)ter code here
Please notice I'm using a QSqlQuery to retrive the updated table data. The reason being is I'm still trying to figure out how to handle psycopg2 list of tuples return data.
UPDATE: So far the only workaround I was able to find is to remove the COMMIT line from the procedure. I'm under the impression that Postgres is transferring the transaction control to the driver. I wonder if this is the proper way to do it. I'll appreciate your comments about it.
With the psycopg2 driver, you have to set autocommit to True for statements that must run outside a transaction. From the docs:
A few commands (e.g. CREATE DATABASE, VACUUM, CALL on stored
procedures using transaction control…) require to be run outside any
transaction: in order to be able to run these commands from Psycopg,
the connection must be in autocommit mode: you can use the autocommit
property.
In your code add this line:
conn.autocommit = True
and then your CALL statement should run.
Here's my MRE
create procedure:
CREATE OR REPLACE PROCEDURE test_procedure(
par1 integer,
par2 integer)
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
COMMIT;
END;
$BODY$;
Python code:
import sys
import psycopg2
print(sys.version) # 3.7.1 and 3.8.2 tested
print(psycopg2.__version__) # 2.8.6 and 2.9.3 tested
conn = psycopg2.connect('host=myhost dbname=mydb user=myuser')
cur = conn.cursor()
conn.autocommit = True # this is the key line. without this, next line will fail
cur.execute('CALL aaa.test_procedure(%s,%s)', (1,1))
conn.commit()

SQL0628N with MODIFIES SQL DATA when creating a table function

I am trying to encapsulate the functionality from this sample code here, inside a Table-Function.
I can run the sample alone without any problem.
But when I create a table function, just with a single call to OPEN_CURSOR , I receive SQL0577N
CREATE FUNCTION ROW_CHECKSUM
( IN sSchema VARCHAR(128) ,
IN sTable VARCHAR(128) ,
IN sColumnList VARCHAR(1024) ,
IN sWhere VARCHAR(1023),
IN iRows INTEGER
)
RETURNS TABLE (ROW_PK_VALUES VARCHAR(3000), CHECKSUM INTEGER )
LANGUAGE SQL
SPECIFIC ROW_CHECKSUM
--NO EXTERNAL ACTION
--MODIFIES SQL DATA
--NOT DETERMINISTIC
BEGIN
DECLARE iCheckSum INTEGER ;
DECLARE sKyes VARCHAR(1024) ;
DECLARE iCursor INTEGER;
DECLARE sQuery VARCHAR(32000) ;
SET sQuery = 'SELECT ' || sColumnList || ' FROM "' || sSchema || '"."' || sTable || '" WHERE ' || sWhere || ' FETCH FIRST ' || TO_CHAR(iRows) || ' ONLY' ;
CALL DBMS_SQL.OPEN_CURSOR(iCursor);
--CALL DBMS_SQL.PARSE(iCursor, sQuery, DBMS_SQL.native) ;
--PIPE (sKeys, iCheckSum) ;
--PIPE ('abcd', 1234) ;
RETURN ;
END
----
SQL0577N User defined routine "DB2ADMIN.ROW_CHECKSUM" (specific name "")
attempted to modify data but was not defined as MODIFIES SQL DATA. LINE
NUMBER=33. SQLSTATE=38002
it seems, OPEN_CURSOR demands to have the MODIFY SQL DATA specified.. ok.. let's go!
But, when I specify it, then I get the following error, instead:
SQL0628N Multiple or conflicting keywords involving the "MODIFIES SQL DATA"
clause are present. LINE NUMBER=33. SQLSTATE=42613
The error details for -628 error is too generic and does not help me to determine what's really going on here.
I need to perform dynamic SQL queries using DBMS_SQL module, and return the result set using PIPE , like this other sample here.
I have been reading spread documentations the entire day.. and so far was not able to determine exactly what rule I am violating.
Also, found some inconsistencies on documentation, which I don't understand:
This page, says:
SQL table functions cannot contain compiled compound statements.
While, the Rules from RETURN statement says the opposite, and matches with PIPE sample code:
In an SQL table function using a compound SQL (compiled) statement, an expression, NULL, or fullselectcannot be specified. Rows are returned from the function using the PIPE statement and the RETURN statement is required as the last statement to execute when the function exits (SQLSTATE 2F005).
Appreciate any help!
Look at the note about the MODIFIES SQL DATA in the CREATE FUNCTION statement description:
4 Valid only for compiled scalar function definition and an inlined
table function definition.
But you can't use PIPE in an inlined function.
So, you want to use different functionalities, which can't be used together.
The inconsistency you found in the documentation is not related to you problem.

How to store the result of a select query into a variable(IBM DB2)?

I am trying to save the result of a query into a variable. I am using IBM DB2, but I can only store the result if I am declaring the variable inside a procedure.
My code is:
DECLARE #myvar INTEGER;
SET #myvar = (SELECT MAX(ID) FROM S0SCSQMS.S0SRPTCNAME);
and I receive the following errors:
For the first line:"SQL0104N An unexpected token "INTEGER" was found following "DECLARE #myvar ". Expected tokens may include: "END-OF-STATEMENT". LINE NUMBER=1. SQLSTATE=42601"
The error code does not tell me much. I looked for it on the IBM documentation.
Looking forward for an answer.
Thank you.
try this (work on iseries db2 v7r1)
CREATE OR REPLACE VARIABLE myvar INTEGER ;
SET myvar = (SELECT max( id_xp_dossier) FROM cilgprod.xp_dossier);
DROP VARIABLE myvar;

I can create a stored procure with invalid user defined function names in it

I just noticed that I could alter my stored procedure code with a misspelled user defined function in it.
I noticed that at 1st time I execute the SP.
Is there any way to get a compile error when an SP include an invalid user-defined function name in it?
At compile time? No.
You can, however, use some of SQL's dependency objects (if using MS SQL) to find problems just after deployment, or as part of your beta testing. Aaron Bertran has a pretty nice article rounding up the options, depending upon the version of SQL Server.
Here is an example using SQL Server 2008 sys object called sql_expression_dependencies
CREATE FUNCTION dbo.scalarTest
(
#input1 INT,
#input2 INT
)
RETURNS INT
AS
BEGIN
-- Declare the return variable here
DECLARE #ResultVar int
-- Add the T-SQL statements to compute the return value here
SELECT #ResultVar = #input1 * #input2
-- Return the result of the function
RETURN #ResultVar
END
GO
--Fn Works!
SELECT dbo.ScalarTest(2,2)
GO
CREATE PROCEDURE dbo.procTest
AS
BEGIN
SELECT TOP 1 dbo.scalarTest(3, 3) as procResult
FROM sys.objects
END
GO
--Sproc Works!
EXEC dbo.procTest
GO
--Remove a dependency needed by our sproc
DROP FUNCTION dbo.scalarTest
GO
--Does anything have a broken dependency? YES
SELECT OBJECT_NAME(referencing_id) AS referencing_entity_name,
referenced_entity_name, *
FROM sys.sql_expression_dependencies
WHERE referenced_id IS NULL --dependency is missing
GO
--Does it work? No
EXEC dbo.procTest
GO

Retrieve data from PostgreSQL Database using ADO.Net "System.Data.Odbc" (VB.Net)

Though I have been using SQL Server, Oracle from last decade, I have been asked to
do some research on PostgreSQL and after some initial investigation it is evident that I am now stuck on retrieving data from the PostgreSQL database using Function.
Using following piece of code to retrieve the data and getting error
('ERROR [26000] ERROR: prepared statement "mytabletest" does not exist;
'Error while executing the query)
Code Snippets
Dim oDBCommand As DbCommand = GetDBCommand(oConnectionType, "mytabletest", CommandType.StoredProcedure)
Dim dstResults As DataSet = GetDataSet(ConnectionTypes.ODBC, oDBCommand)
Public Function GetDataReader(dbType As ConnectionTypes, command As DbCommand) As DbDataReader
Try
Dim oConnection As DbConnection = GetDBConnection(dbType)
Dim oDBTransaction As DbTransaction = oConnection.BeginTransaction
command.Connection = oConnection
command.Transaction = oDBTransaction
'GETTING ERROR ON FOLLOWING LINE
'ERROR [26000] ERROR: prepared statement "mytabletest" does not exist;
'Error while executing the query
return command.ExecuteReader()
Catch ex As Exception
Throw ex
Finally
End Try
Return Nothing
End Function
Environement I am currently working on is following:-
32 Bit Machine.
Visual Studio 2010 + SP1
ODBC Prodiver: PostgreSQL Unicode 9.01.02.00
ADO.Net (System.Data.Odbc)
Please note that I am open to any suggestions i.e. if I am completely doing it wrong
OR partially etc. Please feel free to write.
In order to make it easier for you to create a same environment, please use following table/function definition.
--- Simple table to make things easier to understand. <br>
CREATE TABLE mytable
(
messagetypeid integer NOT NULL,
messagetype character varying(100) NOT NULL
)
-- Function to retrieve data. <br>
CREATE OR REPLACE FUNCTION mytabletest() <br>
RETURNS SETOF refcursor AS $$
DECLARE
ref1 refcursor;
BEGIN
OPEN ref1 FOR SELECT * FROM mytable;
RETURN NEXT ref1;
END;
$$ LANGUAGE plpgsql;
Please Note:
If I use <br>
Dim oDBCommand As DbCommand = GetDBCommand(oConnectionType, "SELECT * FROM mytable", CommandType.Text)
then system manages to retrieve information from the datbase without any issue, however, as I mentioned as soon we use "Function" it throws an exception.
During my failed efforts to search any solution from the internet someone mentioned that Table should be created with the lower case it so just for the sake of it I recreated with the lower case, however, problem persists.
I am unfamiliar with .net but I suspect you meant something more like:
GetDBCommand(oConnectionType, "SELECT myfunc()", CommandType.Text)
Or in the case of SETOF functions etc..
GetDBCommand(oConnectionType, "SELECT * FROM myfunc()", CommandType.Text)
PostgreSQL does not have 'stored procedures' per-ce. It does have functions and I believe that the client/server protocol has a method for preparing statements that can then be executed multiple times with different variables (to save on the cost of parsing the SQL), but this should be exposed via your client library.