Fetching Output Parameters from a stored procedure using Eclipse link - jpa

I am calling a stored procedure from JPA EclipseLink. Stored Procedure returns three output variables, One cursor type and other two types. I am able to get the value returned from cursor but not the other two values.
Here is the part of stored procedure(example):
create or replace PROCEDURE PROCEDURE1 (
Variable1 IN VARCHAR2,
Variable12 IN VARCHAR2,
Variable13 IN VARCHAR2,
Variable14 IN VARCHAR2,
p_xml_result OUT SYS_REFCURSOR,
p_errorcode OUT NUMBER,
p_errorMessage OUT VARCHAR2)
IS
-- Declare variable
ERRSQL VARCHAR2 (80);
NOSTRAECCEZIONE EXCEPTION;
ERROR_ONE EXCEPTION;
V_AREA VARCHAR2(40);
BEGIN
SELECT AREA
INTO V_AREA
FROM Employee
WHERE Employee_CODE = P_EmployeeCode;
OPEN p_xml_result FOR
<Some query to get data>
p_errorcode := 0;
p_errorMessage := NULL;
EXCEPTION
WHEN NO_DATA_FOUND THEN
p_errorcode := 100;
p_errorMessage := 'generic error';
WHEN OTHERS THEN
p_errorcode := 102;
p_errorMessage := SQLERRM;
Java Code:
StoredProcedureCall procCall = new StoredProcedureCall();
procCall.setProcedureName("getSpecialOffers");
procCall.addNamedArgument("Variable1","Variable1",String.class);
procCall.addNamedArgument("Variable2", "Variable12", String.class);
procCall.addNamedArgument("Variable13", "Variable13", String.class);
procCall.addNamedArgument("Variable14", "Variable14", String.class);
procCall.addNamedOutputArgument("p_xml_result", "p_xml_result", OracleTypes.CURSOR);
procCall.addNamedOutputArgument("p_errorcode", "p_errorcode", Integer.class);
procCall.addNamedOutputArgument("p_errorMessage", "p_errorMessage", String.class);
Session session = ((JpaEntityManager)_em).getActiveSession();
ValueReadQuery query = new ValueReadQuery();
query.setCall(procCall);
query.addArgument("Variable1");
query.addArgument("Variable12");
query.addArgument("Variable13");
query.addArgument("Variable14");
Vector parameters = new Vector();
parameters.addElement(Variable1);
parameters.addElement(Variable12);
parameters.addElement(Variable13);
parameters.addElement(Variable14);
Object qResult = null;
qResult = session.executeQuery(query,parameters);
Vector vec=(Vector) qResult;
ArrayRecord arrayrec =(ArrayRecord) vec.get(0);
p_xml_result returns four field, which I am able to fetch as arrayrec.get("VariableXYZ"). and arrayrec also has only those four variables present which are returned from cursor. But I want to access the value of p_errorcode in my code in all the conditions. Can you please help me with it?

I got the desired output params by changing ValueReadQuery to DataReadQuery.

Related

How can I query a custom datatype object inside an array of said custom datatype in PL/pgSQL?

Suppose I have:
CREATE TYPE compfoo AS (f1 int, f2 text);
And I create a table foo containing two columns: fooid and fooname, corresponding to the fields of compfoo, later I insert some records 1, aa, 2, bb, 3, cc
Then, I define a PL/pgSQL function (more or less as follows:)
create or replace function foo_query()
returns text
language plpgsql
as $$
declare
r compfoo;
arr compfoo [];
footemp compfoo;
result text;
begin
for r in
select * from foo where fooid = 1 OR fooid = 2
loop
arr := array_append(arr, r);
end loop;
foreach footemp in array arr
loop
select footemp.f1 into result where footemp.f1 = 1;
end loop;
return result;
end;
$$
Where I query first foo by column names and save the results into arr, an array of compfoo. Later, I iterate over arr and try to query the elements by their fieldnames as defined in compfoo.
I don't get an error per se in Postgres but the result of my function is null.
What am I doing wrong?
The RAISE NOTICE should be your best friend. You can print the result of some variables at some points of your code. The basic issue are not well initialized values. The arr variable is initialized by NULL value, and any operation over NULL is NULL again.
Another problem is in select footemp.f1 into result where footemp.f1 = 1; statement. SELECT INTO in Postgres overwrite the target variable by NULL value when an result is empty. In second iteration, the result of this query is empty set, and the result variable is set on NULL.
The most big problem of your example is style of programming. You use ISAM style, and your code can be terrible slow.
Don't use array_append in cycle, when you can use array_agg function in query, and you don't need cycle,
Don't use SELECT INTO when you don't read data from tables,
Don't try to repeat Oracle' pattern BULK COLLECT and FOREACH read over collection. PostgreSQL is not Oracle, uses very different architecture, and this pattern doesn't increase performance (like on Oracle), but probably you will lost some performance.
Your fixed code can looks like:
CREATE OR REPLACE FUNCTION public.foo_query()
RETURNS text
LANGUAGE plpgsql
AS $function$
declare
r compfoo;
arr compfoo [] default '{}'; --<<<
footemp compfoo;
result text;
begin
for r in
select * from foo where fooid = 1 or fooid = 2
loop
arr := array_append(arr, r);
end loop;
foreach footemp in array arr
loop
if footemp.f1 = 1 then --<<<
result := footemp.f1;
end if;
end loop;
return result;
end;
$function$
postgres-# ;
It returns expected result. But it is perfect example how don't write stored procedures. Don't try to replace SQL in stored procedures. All code of this procedure can be replaced just by one query. In the end this code can be very slow on bigger data.

How to get the value of var cast in this query postgresql?

I have the problem when create select insert loop in procedure postgresql. The problem is var cast('1000/1902/003' AS TEXT) always null. How to detect this variabel? i realy need this variabel.
I have try without casting but the parameter always read as integer.
BEGIN
FOR tx IN EXECUTE 'SELECT * FROM T_DataUpload2_FinalDetail WHERE dataupload2fd_id = CAST('|| 1000/1902/003 ||'AS TEXT)'
LOOP
data_cust := tx.DataUpload2FD_DistID;
data_dist := tx.DataUpload2FD_CustID;
RETURN NEXT;
END LOOP;
END;
Why dynamic SQL to begin with?
BEGIN
FOR tx IN SELECT *
FROM T_DataUpload2_FinalDetail
WHERE dataupload2fd_id = '1000/1902/003'
LOOP
data_cust := tx.DataUpload2FD_DistID;
data_dist := tx.DataUpload2FD_CustID;
RETURN NEXT;
END LOOP;
END;
The expression (without quotes) 1000/1902/003 means "1000 divided by 1902 divided by 3" the result would be 0,175... which is rounded to 0 (not null) because all values are integers and thus integer division is used.

Delphi/FireDac + PostgreSQL + stored procedure + Procedure (To call a procedure, use CALL)

We are putting postgreSQL database support on our system, we currently work with Oracle.
We are trying to call a procedure in the database, just as we call in Oracle.
var conBanco := TFDConnection.Create(Self);
try
conBanco.Params.Database := 'Database';
conBanco.Params.UserName := 'UserName';
conBanco.Params.Password := 'Password';
conBanco.Params.Values['Server'] := 'IP';
conBanco.Params.DriverID := 'PG';
conBanco.Open();
var stpBanco := TFDStoredProc.Create(Self);
try
stpBanco.Connection := conBanco;
stpBanco.StoredProcName := 'gerador_padrao';
stpBanco.Prepare;
stpBanco.ParamByName('gerador').Value := 'pessoas';
stpBanco.ExecProc();
ShowMessage(VarToStrDef(stpBanco.ParamByName('parchave').Value, ''));
finally
stpBanco.Free;
end;
finally
conBanco.Free;
end;
However we get the following error:
exception class : Exception
exception message : EPgNativeException: [FireDAC][Phys][PG][libpq]
ERROR: superus.gerador_padrao(gerador => character varying, parchave
=> numeric) is a procedure. To call a procedure, use CALL.
Stored procedure in database:
CREATE OR REPLACE PROCEDURE superus.gerador_padrao
(gerador character varying, INOUT parchave numeric)
LANGUAGE plpgsql
AS $procedure$
begin
--Code
end;
$procedure$
;
The error occurs on the following line:
stpBanco.Prepare;
The above code works perfectly in Oracle, how do I call the procedure in PostgreSQL?
Thank you.
Below we will talk about Delphi 10.2.3, maybe in later versions it's different.
Inside the file FireDAC.Phys.PGMeta.pas has a TFDPhysPgCommandGenerator.GetStoredProcCall method, inside which the procedure text is formed and there is no option to call the procedure via CALL. Moreover, there is no "CALL " text in this entire file. Maybe this is a mistake, or maybe some kind of cunning idea... But that doesn't make it any easier for us.
// function returns void
if not lHasOut then begin
if FCommandKind = skStoredProc then
FCommandKind := skStoredProcNoCrs;
// NULL to avoid problem with execution of a
// void function in binary result format
Result := 'SELECT NULL FROM ';
end
else begin
if lHasCrs and (FCommandKind = skStoredProc) then
FCommandKind := skStoredProcWithCrs
else if lFunc then
lFunc := FCommandKind in [skStoredProc, skStoredProcNoCrs];
if lFunc then
Result := 'SELECT '
else
Result := 'SELECT * FROM ';
end;
In total, here I see 2 options:
Form the procedure call string yourself. I'm sure it's not difficult, although, of course, it's more correct when the component does it.
Instead of a procedure, make a function with the same content, and make your output parameter the result. It is important to remember - the procedure call then must pass through ExecFunc in this case;

Postgres cursor

I created Composite Type
Create Type TestDetailReportType1 As
(
sName text,
cDetailsTimeStamp timestamp,
number text,
dropdi text,
queue text,
agent text,
status int,
reference int
)
I created a cursor which i expect to return a list of composite type ...but no records is returned when i execute select * from TestdetailsCursortest11("abc")
but the query written within the function when executed directly returns 31 row...i am new to postgress so i fail to understand which place i am going wrong while making this function ,really appreciate any guidance at the front.
Note->I specifically want to write a cursor in this scenario...I was successfully able to get result when the function was returning table.
CREATE OR REPLACE FUNCTION public.TestdetailsCursortest11(
hgname text)
RETURNS SETOF TestDetailReportType1
LANGUAGE 'plpgsql'
AS $TestdetailsCursortest11$
DECLARE
cDetailcursor refcursor;
cDetailtEvent RECORD; -- variable to store agent event.
cDetail callDetailReportType1;
BEGIN
OPEN cDetailcursor FOR
select tblUsers.UserName,tblCallEvent.StateCreateDate,tblCallRegister.Cli,tblCallRegister.DDI,tblhuntGroup.name,
tblUsers.Extension,
tblCallEvent.StateID,
tblCallRegister.CallID
from tblCallRegister
inner join tblCallEvent on tblCallRegister.callregisterid= tblCallEvent.callregisterid
inner join tblUsers on tblUsers.userid=tblCallEvent.agentid
inner join tblhuntGroup on tblhuntGroup.HGID=tblCallEvent.HGID
where name=hgname;
FETCH NEXT FROM callDetailcursor INTO callDetailtEvent;
callDetail.sName=callDetailtEvent.UserName;
callDetail.cDetailsTimeStamp=callDetailtEvent.StateCreateDate;
callDetail.number =callDetailtEvent.Cli;
IF callDetailtEvent.StateID = 19
THEN
callDetail.dropdi=callDetailtEvent.DDI;
ELSE
callDetail.dropdi=callDetailtEvent.DDI+1;
END IF;
callDetail.queue=callDetailtEvent.name;
callDetail.agent=callDetailtEvent.Extension;
callDetail.status =callDetailtEvent.StateID;
callDetail.reference=callDetailtEvent.CallID;
RETURN;
CLOSE callDetailcursor;
END;
$TestdetailsCursortest11$;
In a set returning function (a.k.a. table function) you use RETURN not to return a result, but to exit the function.
You use RETURN NEXT <value>; to return a result row. So your function should look similar to this:
DECLARE
cDetail callDetailReportType1;
cDetailtEvent RECORD;
BEGIN
FOR cDetailtEvent IN
SELECT ...
LOOP
cDetail.field1 := ...;
cDetail.field2 := ...;
/* return the next result row */
RETURN NEXT cDetail;
END LOOP;
/*
* This is optional; dropping out from the end
* of a function is an implicit RETURN
*/
RETURN;
END;
The way your function is written it will alwazs return an empty result because there is no RETURN NEXT <value>;.

How to write this pl/pgsql function?

I want to write a stored procedure that returns a 'flattened' object. By 'flattening', I am essentially selecting a set of rows, and returning specific fields in the rows, into the data returned from the function.
The code below explains what I am trying to do
CREATE TABLE user (id int, school_id int, name varchar(32));
CREATE TYPE my_type (user1_id int, user1_name varchar(32), user2_id int, user2_name varchar(32));
CREATE OR REPLACE FUNCTION get_two_users_from_school(schoolid int)
RETURNS my_type AS $$
DECLARE
result my_type
temp_result user
BEGIN
-- for purpose of this question assume 2 rows returned
SELECT id, name INTO temp_result FROM user where school_id = schoolid LIMIT 2;
-- Will the (pseudo)code below work?:
result.user1_id := temp_result[0].id ;
result.user1_name := temp_result[0].name ;
result.user2_id := temp_result[1].id ;
result.user2_name := temp_result[1].name ;
return result ;
END
$$ language plpgsql
I have two questions:
Am I using the correct data type for variable temp_result
Am I accessing the rows correctly (using array indexing)?
Am I accessing the rows correctly (using array indexing)?
No, you need to loop through the result using a cursor which is nicely explained in the manual:
http://www.postgresql.org/docs/current/static/plpgsql-control-structures.html#PLPGSQL-RECORDS-ITERATING
Something like this should work:
DECLARE
temp_result RECORD;
row_counter integer;
BEGIN
row_counter := 1;
FOR temp_result IN SELECT id, name FROM user where school_id = schoolid LIMIT 2 LOOP
IF row_counter = 1 THEN
result.user1_id := temp_result.id;
result.user1_name = temp_result.name;
END IF;
IF row_counter = 2 THEN
result.user2_id := temp_result.id;
result.user2_name = temp_result.name;
END IF;
row_counter := row_counter + 1;
END LOOP;
return result;
END;
Btw: having a table named "user" is not a really good idea as user is a reserved word and might cause some problems in the long run.