JDBC - Retrieve Boolean output from Oracle Procedure - stored-procedures

I need to invoke an Oracle procedure with one IN parameter with VARCHAR2 and OUT parameter as BOOLEAN data type.
Below is my code using SimpleJdbcCall
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(getTemplate())
.withCatalogName("package_name")
.withProcedureName("proc_name")
.withoutProcedureColumnMetaDataAccess()
.declareParameters(
new SqlParameter ("userName", Types.VARCHAR),
new SqlOutParameter("status", Types.BOOLEAN)
);
Map<String, Object> inParams = new HashMap<String, Object>();
inParams .put("userName", userInput);
Map<String, Object> outputValue= jdbcCall.execute(inParams);
Exception : CallableStatementCallback; uncategorized SQLException for
SQL [{call PACKAGE_NAME.PROC_NAME(?, ?)}]; SQL state [99999];
error code [17004]; Invalid column type: 16; nested exception is
java.sql.SQLException: Invalid column type: 16
After doing a research i found that "JDBC drivers do not support the passing of Boolean parameters to PL/SQL stored procedures"
It has been suggested to wrap the PL/SQL procedure with a second PL/SQL procedure. The main problem is that i am restricted for write access in the db as that is client data. please help me to fix this issue.
Some of the links i referred
https://docs.oracle.com/cd/F49540_01/DOC/java.815/a64685/tips3.htm
https://community.oracle.com/thread/2139408?tstart=0
https://community.oracle.com/thread/887712?tstart=0
https://community.oracle.com/thread/975159?tstart=0
Stored Function - Sending/Receiving Boolean - BD
http://docs.oracle.com/cd/B28359_01/java.111/b31224/apxtblsh.htm#i1005380

From the official Oracle JDBC documentation:
It is not feasible for Oracle JDBC drivers to support calling
arguments or return values of the PL/SQL RECORD, BOOLEAN, or table
with non-scalar element types. [...] As a workaround to PL/SQL RECORD,
BOOLEAN, or non-scalar table types, create container procedures that
handle the data as types supported by JDBC. For example, to wrap a
stored procedure that uses PL/SQL boolean, create a stored procedure
that takes a character or number from JDBC and passes it to the
original procedure as BOOLEAN or, for an output parameter, accepts a
BOOLEAN argument from the original procedure and passes it as a CHAR
or NUMBER to JDBC. Similarly, to wrap a stored procedure that uses
PL/SQL records, create a stored procedure that handles a record in its
individual components, such as CHAR and NUMBER, or in a structured
object type.
This is exactly what you did in your answer, but I wanted to add the documentation as reference.

I fixed the issue by writing a wrapper procedure to process the result of the actual procedure and sending back the result as varchar data type .
If any of you find it as wrong approach or if you a have any easy way to fix this, please do share your comments.
Below is the procedure:
DECLARE
userName VARCHAR2(13);
status BOOLEAN;
result VARCHAR2(13);
BEGIN
userName := ?;
status := NULL;
package_name.proc_name ( userName, status);
BEGIN
IF status THEN result := 'Yes';
ELSIF NOT status THEN result := 'No';
ELSE result := 'NULL';
END IF;
END;
COMMIT;
? := result;
END ;
Also instead of simpleJdbcCall, i used CallableStatement for processing this. Refer below:
try{
String wrapperProc= "DECLARE userName VARCHAR2(13); status BOOLEAN; result VARCHAR2(13); BEGIN userName := ?; status := NULL; "+
"package_name.proc_name ( userName, status ); BEGIN IF status THEN result := 'Yes'; ELSIF NOT status THEN " +
"result := 'No'; ELSE result := 'NULL'; END IF; END; COMMIT; ? := result;END ;";
CallableStatement proc_stmt= null;
proc_stmt = getTemplate().getDataSource().getConnection().prepareCall(wrapperProc);
proc_stmt.setString(1, "userName");
proc_stmt.registerOutParameter(2, Types.VARCHAR);
proc_stmt.execute();
System.out.println("Final Result : "+proc_stmt.getString(2));
} catch(SQLException e){
System.out.println("SQL Exception : "+e.getMessage());
e.printStackTrace();
} catch (Exception e) {
System.out.println("Exception : "+e.getMessage());
e.printStackTrace();
}
~~Suriya

Related

Assigning default value to snowflake stored procedure arguments

Is it possible to have default values in arguments of Stored procedures of Snowflake. For the below example, I am getting error. Please help
syntax error line 1 at position 53 unexpected ''test''.
create or replace procedure test(arg1 string default 'test')
returns string not null
language sql
as
$$
begin
return arg1;
end;
$$
;
Snowflake's procedures applies polymorphism instead of using default value. This solution is when you do not want to call sp like func1(Null)
For example (sql scripting):
create or replace procedure func1(arg1 varchar, arg2 varchar)
...
create or replace procedure func1(arg1 varchar)
...
call func1(arg1 , 'some-default-value')
...
One option could be providing NULL as value and handle it at the begining of the stored procedure with COALSESCE:
create or replace procedure test(arg1 string)
returns string not null
language sql
as
$$
begin
arg1 := COALESCE(arg1, 'test');
return arg1;
end;
$$;
CALL test(NULL);
-- test
Setting a default value/values as arguments directly in Stored procedures is not available in Snowflake currently
The below link can be referred for the allowed syntax in Stored Procedures
https://docs.snowflake.com/en/sql-reference/sql/create-procedure.html#syntax

TClientDataSet error executing with TParam of blob type (ftBlob)

We are using MSSQL 2012.
Trying to update Client photo with Stored Procedure
spui_SetClientPhoto
int ClientID
VarBinary(Max) Photo
Program runs fine with pure ADO:
ADO.ProcedureName:='spui_SetClientPhoto';
ADO.Parameters.CreateParameter('#ClientsID',ftInteger,pdInput,0,95075);
ADO.Parameters.CreateParameter('#Photo',ftBlob,pdInput,0,NULL);
ADO.Parameters[1].LoadFromFile('C:\Photo.png',ftBlob);
ADO.ExecProc;
But with CDS it cause an error:
Implicit Conversion from datatype Varchar(max) to Varbinary(max) is not allowed.
ADO.ProcedureName:='spui_SetClientPhoto';
cds.SetProvider(ADO);
cds.Params.CreateParam(ftInteger,'#ClientsID',ptInput).AsInteger:=95075;
cds.Params.CreateParam(ftBlob,'#Photo',ptInput).LoadFromFile('C:\Photo.png', ftBlob);
cds.Execute;
e.g. cannot run CDS with Parameters of BLOB type. Any solution for this?
The following works fine for me, with the Picture field type in the AdoQuery and
CDS set to ftGraphic, and the Stored Proc's DDL set to
CREATE PROCEDURE [dbo].[SetClientPhoto](#ClientID int, #Picture Image)
AS
BEGIN
SET NOCOUNT ON;
update table_2
set picture = #Picture
where
ID = #ClientID
END
Code
procedure TForm1.SavePictureViaStoredProc;
var
PrvCommandText,
PrvSql : String;
ID : Integer;
const
scTestImage = 'D:\TestPictures\TestBMP.BMP';
begin
// First, save the text of the AdoQuery's Sql and the CDS's CommandText
PrvCommandText := CDS1.CommandText;
PrvSql := AdoQuery1.SQL.Text;
// Save the iD of the row we want to use
ID := CDS1.FieldByName('ID').AsInteger;
try
// Allow CommandText changes on the DSP
DataSetProvider1.Options := DataSetProvider1.Options + [poAllowCommandText];
CDS1.Close;
// construct a Sql statement to invoke the Stored Proc
CDS1.CommandText := 'exec dbo.SetClientPhoto #ClientID = :' + IntToStr(ID) + ', #Picture = :Picture';
// Set up parameters
CDS1.Params.Clear;
CDS1.Params.CreateParam(ftInteger, '#ClientID', ptInput);
CDS1.Params.CreateParam(ftGraphic, '#Picture', ptInput);
CDS1.Params.ParamByName('#ClientID').Value := ID;
CDS1.Params.ParamByName('#Picture').LoadFromFile(scTestImage, ftGraphic);
AdoQuery1.Close;
AdoQuery1.SQL.Text := '';
CDS1.Execute; // This executes the stored proc
CDS1.Params.Clear;
finally
ADoQuery1.SQL.Text := PrvSql;
CDS1.CommandText := PrvCommandText;
CDS1.Open;
end;
end;
Note: I very rarely store images in databases, and have not yet managed to get this to work with .Jpg and .Png files. I vaguely recall that there is an extra step needed with storing those in a DB without getting a "Stream read error" or "Invalid image" exception, and I'll see if I can remind myself of it later.

PLSQL call procedure from function

I have a function which is called from a select query, below is the function which works perfect. I want to call the procedure below if boolean = 1 that inserts values into the login table:
create or replace FUNCTION isLoggedIn(x IN VARCHAR2, y IN VARCHAR2)
RETURN number IS
boolean number(1) := 0;
BEGIN
SELECT count(*) into boolean
FROM VIEWLOGIN
WHERE username = x AND password = y;
IF boolean = 1 THEN
PROCDURELOGIN
RETURN boolean;
ELSE
RETURN 0;
END IF;
END;
This is my procedure:
create or replace PROCEDURE PROCDURELOGIN
IS
BEGIN
INSERT INTO "SW3"."LOGIN" (LOGINID, MEMBERID)
VALUES (seqLogin.NEXTVAL, '1');
Commit;
END;
Create view VIEWLOGIN
SELECT firstname, surname, username, password
FROM member
But I get the error when I run the query:
Error starting at line : 1 in command -
SELECT firstname, surname, isLoggedIn(username, password)
FROM VIEWLOGIN
WHERE username = 'fionawalshe' AND password = 'qwertyu8'
Error report -
SQL Error: ORA-14551: cannot perform a DML operation inside a query
ORA-06512: at "SW3.PROCDURELOGIN", line 4
ORA-06512: at "SW3.ISLOGGEDIN", line 10
14551. 00000 - "cannot perform a DML operation inside a query "
*Cause: DML operation like insert, update, delete or select-for-update
cannot be performed inside a query or under a PDML slave.
*Action: Ensure that the offending DML operation is not performed or
use an autonomous transaction to perform the DML operation within
the query or PDML slave.
Oracle clearly says in the error message, what is a problem. Try this:
create or replace PROCEDURE PROCDURELOGIN
IS
pragma autonomous_transaction;
BEGIN
INSERT INTO "SW3"."LOGIN" (LOGINID, MEMBERID)
VALUES (seqLogin.NEXTVAL, '1');
Commit;
END;
By the way, I don't like such procedures and recommend not to use them, if possible.

How big is an nvarchar(max) as far as ADO is concerned?

i am trying to use a parameterized query against ADO:
INSERT INTO Foo (Name, Value) VALUES(#name, #value)
In SQL Server the Name column is a varchar type. The Value column is an nvarchar(max).
What size do i pass when creating a parameter when i don't know, or want to specify, the size?
procedure SaveTheThing(Connection: TADOConnection);
var
sql: WideString;
cmd: _Command;
begin
sql := 'INSERT INTO Foo (Name, Value) VALUES(#name, #value)';
cmd := CoCommand.Create;
cmd.Set_ActiveConnection(Connection.ConnectionObject);
cmd.Set_CommandType(adCmdText);
cmd.Set_CommandText(sql);
//and now add the parameters
cmd.Parameters.Append(
cmd.CreateParameter('#name', adVarChar, adParamInput, -1, filename)
);
cmd.Parameters.Append(
cmd.CreateParameter('#value', adVarWChar, adParamInput, -1, GetXmlToWideString)
);
cmd.Execute({out}recordsAffected, EmptyParam, adCmdSomeThatDoesntCauseAnExcetpion or adExecuteNoRecords);
end;
The simple alternative was going to be:
sql := 'INSERT INTO Foo (Name, Value)'#13#10+
'VALUES (+QuotedStr(filename)+', '+QuotedStrW(GetXmlToWideString)+')';
and be done already. But i thought i'd burn a few days trying to make parameterized queries a viable solution, and avoid having to write a QuotedStrW.
You can use the -1 value in the size of a ADO parameter without problems.
Try this sample code , which insert a 2MB string in the Value column
var
sql: WideString;
cmd: _Command;
recordsAffected : OleVariant;
begin
sql := 'INSERT INTO Foo (Name, Value) VALUES(?, ?)';
cmd := CoCommand.Create;
cmd.Set_ActiveConnection(Connection.ConnectionObject);
cmd.Set_CommandType(adCmdText);
cmd.Set_CommandText(sql);
//and now add the parameters
cmd.Parameters.Append(cmd.CreateParameter('#name', adVarChar, adParamInput, -1, 'AfileName'));
cmd.Parameters.Append(cmd.CreateParameter('#value', adVarWChar, adParamInput, -1, StringOfChar('#', 2*1024*1024)));
cmd.Execute({out}recordsAffected, EmptyParam, adExecuteNoRecords);
end;
You can use my answer on your other post:
"Must declare the variable #myvariable" error with ADO parameterized query
to use parameters and parameter by name. Try to avoid using _Command, but use TADOCommand because it is more friendly (and simpler to code too). You can assign parameter to a value by using:
Parameters.ParamByName('xxxx').value := someValue.
Of course someValue data type must correspond to your SQL server column data type definition.

How to parameterize widestrings using TADOCommand parameterized query?

i am trying to use a parameterized query with Delphi TADOCommand:
var
s: WideString;
cmd: TADOCommand;
recordsAffected: OleVariant;
begin
cmd := TADOCommand.Create(nil);
cmd.Connection := Connection;
cmd.CommandText := 'INSERT INTO Sqm(Filename) VALUES(:filename)';
s := AFilename;
cmd.Parameters.ParamByName('filename').Value := s;
cmd.Execute();
The resulting data in the database is complete mangled:
C?:\U?s?er?s?\i??n?.A?V`A?T?O?P?I?A?\A?p?p?D??t??\L?o???l?\A?v?at??r? S?o?f?t?w?är¨? C?r??t?i??n?s?\S?o°f?t?w?r?? Q?u??li?t?y? M??t?r?i?cs?\C??S?-s?q?m?00.x?m?l
i can use a native parameterized ADO Command object. It saves the data correctly:
C̬:\Ȗŝḙr͇s̶\i̜ẵn̥.ÀV̹AͧT̶O̠P̩I̿Ȁ\A͜p̥p̔D͑ẫt̒ā\L̫o͋ɕălͭ\A̼v̼ẵt͈ấr̄ S̫o̖f͎t̻w̵ạr͂ẽ C̾r̮ḛẵt͘iͩo̳n̬s̨\S̪ōf̒t͘w̚âr̿ɇ Qͬüẳlͮi̫tͥy̽ M͘ȇt̨r̟i̻çš\C͍MͥS̚-s̞q̕m͜00.xͤm̧l̝
but it's very fragile and not suitable for production use.
How can i use unicode/WideStrings with TADOCommand in Delphi?
Bonus Chatter
In SQL Server Profiler you can see the SQL being executed:
exec sp_executesql N'INSERT INTO Sqm(Filename) VALUES(#P1)', N'#P1 char(300),#P2 text', 'C?:\Us?er?s?\i?än?.A?V?A?T?O?P?I?À\A?p?p?D?ât?a\L?o?çal¯\A?v?at??r? So?f?t?w?ar?? C?r??á?i?o?n?s?\So¸f"t?w?ar?? Q?u??l?i?ty? M??t?r?i¸?s`\C?M°S?-s?q?m?00.?m¨´l¯ '
Which points out the problem - it's building the WideString parameter as a char(300) value. Make it not broke.
The last i see of my WideString before it goes down the parameter hole is:
ParameterObject.Value := NewValue;
where
NewValue is a variant of type VT_BSTR (aka varOleStr) with the proper value
ParameterObject is a native ADO _Parameter object, with a .Type of 129 (adChar)
Even trying to force the parameter type:
cmd.Parameters.ParamByName('filename').DataType := ftWideString;
cmd.Parameters.ParamByName('filename').Value := s;
doesn't help.
Note: This question is part of a series on how to paramaterize INSERT INTO foo (value) VALUES (%s)
https://stackoverflow.com/questions/10726212/using-wrong-type-with-parameterized-query-causes-error
How big is an nvarchar(max) as far as ADO is concerned?
How to parameterize widestrings using TADOCommand parameterized query?
"Must declare the variable #myvariable" error with ADO parameterized query
How about using
cmd.Parameters.ParamByName('filename').Value := WideStringToUCS4String(s);
By the way, s is declared as widestring. is it necessary to have s as a widestring? How about just
var
s : String;
in System.pas, UCS4String (UCS-4 byte or UTF-32 bits) is declared as:
...
...
UCS4Char = type LongWord;
...
UCS4String = array of UCS4Char;
...
function WideStringToUCS4String(const S: WideString): UCS4String;
...
function UCS4StringToWidestring(const S: UCS4String): WideString;
What data type you stored the filename column as ? Can sql server 2000 handle UTF-32 string?
The answer is that it cannot be done in Delphi (5).
It might be fixed in newer versions of Delphi; but without anyone to test it we won't know.
Q.: How to parameterize widestrings using TADOCommand parameterized query?
A.: You can't. Sorry for the inconvience.

Resources