Executing PostgreSQL Stored Procedure using Spring Data - JdbcTemplate - postgresql

I am trying to call a PostgreSQL Stored Procedure from Spring Data JdbcTemplate. The following are the error and code block. Appreciate if any one can help.
Stored procedure
CREATE or replace PROCEDURE getRecord (
IN in_id INTEGER,
OUT out_name VARCHAR(20),
OUT out_age INTEGER)
language plpgsql
as $$
BEGIN
SELECT name, age
INTO out_name, out_age
FROM Student where id = in_id;
END
$$
Springboot Code
SimpleJdbcCall simpleJdbcCall;
dataSource = jdbcTemplate.getDataSource();
simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate).withProcedureName("getrecord");
SqlParameterSource in = new MapSqlParameterSource().addValue("in_id",24);
try {
Map<String, Object> out = simpleJdbcCall.execute(in);
if (out != null){
System.out.println("A record found");
}
else
{
System.out.println("No record found");
}
}
catch (Exception e){
System.out.println(e.getMessage());
}
Error
CallableStatementCallback; bad SQL grammar [{call getrecord(?, ?, ?)}]; nested exception is org.postgresql.util.PSQLException: ERROR: getrecord(integer) is a procedure
Hint: To call a procedure, use CALL.
Position: 15
Note:
The stored procedure is having three parameters - one IN and two Out Parameters.
After going through few tutorials, I had observed that, only in parameter is being passed to the stored procedure call rather than all 3 parameters, because only the first parameter is IN and the rest of two are OUT parameters.
For example:
https://www.tutorialspoint.com/springjdbc/springjdbc_stored_procedure.htm
https://mkyong.com/spring-boot/spring-boot-jdbc-stored-procedure-examples/

Related

Unable to call a PostgreSQL function using callable statement or prepared statement

I have created a PostgreSQL function which is tested on the backend side and it works as expected. However, when I am trying to call it through the Scala module it says that the function doesn't exist.
Function:
create or replace function testing.compareData(ab integer, b json, tablename varchar) RETURNS void as $$
DECLARE
actualTableName varchar := tablename;
histTableName varchar:= actualTableName ||'_hist';
job_id Integer:=0;
begin --<<<< HERE
set search_path to testing; -- Set the schema name
execute 'SELECT id FROM '||actualTableName||' WHERE id =$1' into job_id using ab;
-- if there is data for id in the table then perform below operations
if job_id is not null then
execute FORMAT('INSERT INTO %I select * from %I where id = $1',histTableName,actualTableName) USING ab;
execute FORMAT('DELETE FROM %I where id = $1',actualTableName) USING ab;
EXECUTE FORMAT('INSERT INTO %I values($1,$2)',actualTableName) USING ab,b;
-- if id is not present then create a new record in the actualTable
ELSE
EXECUTE FORMAT('INSERT INTO %I values($1,$2)',actualTableName) USING ab,b;
END IF;
END; --<<<< END HERE
$$ LANGUAGE plpgsql;
Callable Statement Way:
def callingStoredProcedure(message: String, id: Integer, resourceType: String): Unit = {
val connectionUrl: String = ReadingConfig.postgreDBDetails().get("url").getOrElse("None")
var conn: Connection = null
var callableStatement: CallableStatement = null
try {
conn = DriverManager.getConnection(connectionUrl)
callableStatement = conn.prepareCall("{ call testing.compareData( ?,?,? ) }")
callableStatement.setString(1, message)
callableStatement.setInt(2, id)
callableStatement.setString(3, resourceType)
callableStatement.execute()
} catch {
case up: Exception =>
throw up
} finally {
conn.close()
}
}
Prepared Statement way:
def callDataCompareAndInsertFunction(message: String, id: Integer, resourceType: String): Unit = {
val connectionUrl: String = ReadingConfig.postgreDBDetails().get("url").getOrElse("None")
var pstmt: PreparedStatement = null
var conn: Connection = null
try {
conn = DriverManager.getConnection(connectionUrl)
pstmt = conn.prepareStatement("select testing.compareData(?,?,?)")
pstmt.setInt(1, id)
pstmt.setString(2, message)
pstmt.setString(3, resourceType)
pstmt.executeQuery()
}
catch {
case e: Exception => throw e
}
finally {
conn.close()
}
}
Here, testing is my schema under which the function is created. When ran using both the ways it throws below error:
Exception in thread "main" org.postgresql.util.PSQLException: ERROR: function testing.comparedata(character varying, integer, character varying) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Well your first parameter isn't a string, so calling setString(1, ...) will result in the error you quoted in your question.
Your second parameter is declared as json so you can't directly pass a String value there as well. The following should work (given the function definition):
pstmt = conn.prepareStatement("select testing.compareData(?,cast(? as json),?)")
pstmt.setInt(1, id)
pstmt.setString(2, message)
pstmt.setString(3, resourceType)
You might also need to use pstmt.execute() instead of executeQuery() as your function doesn't return anything.

Postgresql 11 Create or Replace Procedure GetMultipleResultSets(INOUT ref1 refcursor, INOUT ref2 refcursor); Automatic cursor deferencing using Npgsql

Requirement:
How to get back the multiple refcursor data from a postgresql 11 procedure (not a function) without using the fetch statement using npgsql 4.0 from ado.net.
Here is the sample which i have tried:
Postgresql Procedure:
CREATE OR REPLACE PROCEDURE public.GetMultipleResultSets(
INOUT ref1 refcursor,
INOUT ref2 refcursor)
LANGUAGE 'plpgsql'
AS $BODY$
begin
open ref1 for
select * from public."tblTestTable1";
open ref2 for
select * from public."tblTestTable2";
end;
$BODY$;
C# Code using Npgsql 4.0:
public DataSet ReturnAsDataSet(string procedureName)
{
this.dataSet = new DataSet();
OpenConnection();
NpgsqlTransaction objTransaction = this.Connection.BeginTransaction();
NpgsqlDataAdapter adapter = new NpgsqlDataAdapter();
NpgsqlCommand command = this.Connection.CreateCommand();
try
{
NpgsqlParameter refCursorParam1 = new NpgsqlParameter("#ref1", NpgsqlTypes.NpgsqlDbType.Refcursor);
refCursorParam1.Direction = ParameterDirection.InputOutput;
refCursorParam1.Value = "ref1";
command.Parameters.Add(refCursorParam1);
refCursorParam2 = new NpgsqlParameter("#ref2", NpgsqlTypes.NpgsqlDbType.Refcursor);
refCursorParam2.Direction = ParameterDirection.InputOutput;
refCursorParam2.Value = "ref2";
command.Parameters.Add(refCursorParam2);
command.CommandText = "call " + procedureName + "(#ref1, #ref2)";
command.Transaction = objTransaction;
adapter.SelectCommand = command;
adapter.Fill(dataSet);
objTransaction.Commit();
}
catch (NpgsqlException ex)
{
if (objTransaction != null)
objTransaction.Rollback();
throw new Exception(ex.Message);
}
finally
{
CloseConnection();
command.Dispose();
objTransaction.Dispose();
}
return this.dataSet;
}
This code will return a table having the "ref1", "ref2" as the columns and "ref1" and "ref2" as the values inside it as follows:
enter image description here
But I need the actual result sets returned from the procedure.
How can I achieve it without manually fetching those refcursor data.
I mean without using "fetch all ref" statement how can we retrieve the data by executing either ExecuteReader() or adapter.Fill() methods as above.
Is there any automatic cursor dereferencing available in npgsql?
Please provide the answer if anyone knows.
Thanks for your help in advance.
This is currently not done for you by Npgsql, this issue tracks it. You can see this long discussions on the pros and cons of this. At the moment you'll have to call FETCH on the cursors yourself.

42809 Error On Executing PostgreSQL Stored Procedure From Asp.Net C# Application

I am using PostgreSQL pgadmin4 (4.16v) with ASP.NET application. I have created a procedure as defined below:
CREATE OR REPLACE PROCEDURE public.usp_bind(
)
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
select district_id,district_name from district_master order by district_name;
END;
$BODY$;
From asp.net application I have called above procedure, code as below:
NpgsqlConnection conn = new NpgsqlConnection();
NpgsqlDataAdapter da = new NpgsqlDataAdapter();
NpgsqlCommand cmd = new NpgsqlCommand();
DataSet ds = new DataSet();
public string dbconstr = dbConnectstr.Connectionstring();
public DataSet getDatafunction(string procedure_, [Optional] string flag_)
{
using (conn = new NpgsqlConnection(dbconstr))
{
//conn.Open();
using (da = new NpgsqlDataAdapter())
{
da.SelectCommand.CommandType = CommandType.StoredProcedure;
da.SelectCommand.CommandText = "CALL usp_bind";
da.SelectCommand.Connection = conn;
using (ds = new DataSet())
{
da.Fill(ds);
}
}
//conn.Close();
}
return ds;
}
It's giving me an error as - 42809: 'usp_bind' is a procedure.
I would have called it using a CALL method too but did't worked. What is the exact way to call a procedure from ASP.NET application?
Don't set CommandType.StoredProcedure on your command.
Unfortunately, stored procedures are new, and CommandType.StoredProcedure was already used to invoke functions, and changing that would be a major breaking change at this point.

How to pass table object type as IN param to Stored Proc using EclipseLink 2.6 JPA

Can someone help me figure out on how to pass table object type as IN parameter to a stored procedure using EclipseLink JPA?
Here is my stored procedure:
create or replace TYPE FORMS_DTLS_TYPE FORCE as OBJECT
(ITEM_ID NUMBER, STUDY_ID NUMBER, ITEMTYPE VARCHAR2(50),ITEM_TITLE VARCHAR2(50));
create or replace TYPE FORMS_DTLS_COLL AS TABLE OF FORMS_DTLS_TYPE;
PROCEDURE createForm(pi_form_coll IN FORMS_DTLS_COLL,
x_return_status OUT NOCOPY VARCHAR2)
IS
BEGIN
x_return_status := 'SUCCESS';
FOR i in pi_form_coll.FIRST..pi_form_coll.LAST
LOOP
INSERT INTO TEST_FORMS ("ITEM_ID", "STUDY_ID", "ITEM_TYPE", "ITEM_TITLE")
VALUES (pi_form_coll(i).ITEM_ID, pi_form_coll(i).STUDY_ID,pi_form_coll(i).ITEMTYPE, 'TITLE');
COMMIT;
END LOOP;
END createForm;
Defining the Stored Procedure using #NamedPLSQLStoredProcedureQuery:
#Entity
#NamedPLSQLStoredProcedureQueries( {
#NamedPLSQLStoredProcedureQuery(name = "TestForms.forms", procedureName = "FORMS_DTLS_PKG.createForm",
parameters = {
#PLSQLParameter(name = "pi_form_dtls", direction = Direction.IN, databaseType = "FORMS_DTLS_COLL"),
#PLSQLParameter(name = "x_return_status", direction = Direction.OUT, databaseType = "VARCHAR_TYPE")
}
)
})
#Struct(name="FORMS_DTLS_TYPE", fields={"ITEM_ID", "STUDY_ID", "ITEMTYPE", "ITEM_TITLE"})
#PLSQLTable(
name="FORMS_DTLS_COLL",
compatibleType="FORMS_DTLS_TYPE"
)
public class TestForms implements Serializable {
...
}
Invoking the stored procedure:
public String createTestForm0() throws ServiceException {
Query query = em.createNamedQuery("TestForms.forms");
TestForms forms = new TestForms();
List<TestForms> formList = new ArrayList();
forms.setItemId(BigDecimal.valueOf(5));
forms.setStudyId(BigInteger.valueOf(99));// stuId);
forms.setItemType("New");
forms.setItemTitle("New title");
formList.add(forms);
query.setParameter("pi_form_dtls", formList);
String result = (String)query.getSingleResult();
return result;
}
I'm getting the following error:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.4.v20160829-44060b6): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Fail to construct descriptor: Unable to resolve type: "ORACLE.FORMS_DTLS_TYPE"
Error Code: 17060
Call:
DECLARE
pi_form_dtlsTARGET FORMS_DTLS_COLL;
pi_form_dtlsCOMPAT FORMS_DTLS_TYPE := :1;
x_return_statusTARGET VARCHAR(255);
FUNCTION EL_SQL2PL_1(aSqlItem FORMS_DTLS_TYPE)
RETURN FORMS_DTLS_COLL IS
aPlsqlItem FORMS_DTLS_COLL;
BEGIN
IF aSqlItem.COUNT > 0 THEN
FOR I IN 1..aSqlItem.COUNT LOOP
aPlsqlItem(I) := aSqlItem(I);
END LOOP;
END IF;
RETURN aPlsqlItem;
END EL_SQL2PL_1;
BEGIN
pi_form_dtlsTARGET := EL_SQL2PL_1(pi_form_dtlsCOMPAT);
FORMS_DTLS_PKG.createForm2(pi_form_dtls=>pi_form_dtlsTARGET, x_return_status=>x_return_statusTARGET);
:2 := x_return_statusTARGET;
END;
bind => [:1 => [oracle.hsgbu.eclinical.designer.dto.TestFormDto#424d058a], x_return_status => :2]
Query: DataReadQuery(name="TestForms.forms" )

What data type should be set in registerOutParameter() when returning record

I created following procedure in GP 4.3.7.
CREATE OR REPLACE FUNCTION test.mytest(param varchar)
RETURNS SETOF record
LANGUAGE sql
AS
$body$
SELECT * from test.mytable;
$body$
VOLATILE;
What datatype should I set when register output param in following java code:
CallableStatement cstmt = conn.prepareCall("{call test.mytest(?, ?)}");
cstmt.setString(1, "abc");
cstmt.registerOutParameter(2, Types.VARCHAR);
cstmt.execute();
I tried many data types as the param in registerOutParameter(). However, I always received the exception:
A CallableStatement was executed with an invalid number of parameters
Is there anyone can give me some advice?
Thanks very much!
https://jdbc.postgresql.org/documentation/91/callproc.html#callproc-resultset-setof
"Functions that return data as a set should not be called via the CallableStatement interface, but instead should use the normal Statement or PreparedStatement interfaces."
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from test.mytest(1, 'abc')");
while (rs.next())
{
//do something here
}
rs.close();
stmt.close();