I'm using Postgres 12 and have written this procedure:
CREATE OR REPLACE PROCEDURE reduceStock(id INTEGER, soldQuantity INTEGER)
LANGUAGE plpgsql AS
$$
BEGIN
UPDATE inventory SET ProductStockAmount = ProductStockAmount - soldQuantity WHERE ProductID = id;
END;
$$;
It works perfectly if I open up psql on the command line and run call reduceStock(1,1);
However, calling it from my Java program as follows:
CallableStatement stmt = conn.prepareCall("{call reduceStock(?, ?)}");
stmt.setInt(1, productID);
stmt.setInt(2, quantity);
stmt.execute();
Gives me the following error:
What I've Tried
running call reduceStock(1,1); from the psql client - works perfectly
dropping the database and starting over to see if some old definition was cached - didn't work
different capitalisations, spacings of call
Any ideas would be appreciated
You need to remove the curly braces, which are the JDBC escape for calling a procedure. But because Postgres has it's own call command, they are not needed (and collides with the JDBC escape).
CallableStatement stmt = conn.prepareCall("call reducestock(?, ?)");
The curly braces around the procedure inocation ({call reduceStock(?, ?)}) mean that this is not native SQL, but rather JDBC syntax. You can read more about it here: Why do JDBC calls to stored procedures wrap the call in curly brackets?.
So calls like this still have to get translated to the native SQL by the JDBC driver. It happens that the Postgres driver, by default, treats such statements as function calls and translates them to SELECT reduceStock(?, ?) SQL query. This is not how stored procedures shall be called in Postgres. In Postgres a stored procedure call SQL is call reduceStock(?, ?).
One way to make it work would be, like #a_horse_with_no_name wrote in his answer, to remove the curly braces. This makes the statement a native call and because it's a valid Postgres SQL this is going to work. The downside is that it makes it less cross-platform as it will not work for DBs that don't support the call procname() syntax. For example this won't work for Oracle, so if you have to support multiple JDBC drivers, this is the less-preferable way to go.
A better fix would be to hint Postgres JDBC driver to treat this syntax like a stored procedure call rather than a function call and translate it to SQL accordingly. For this purpose the Postgres driver exposes a escapeSyntaxCallMode configuration property (check out the EscapeSyntaxCallMode enum as well):
Specifies how the driver transforms JDBC escape call syntax into underlying SQL, for invoking procedures or functions. (backend >= 11) In escapeSyntaxCallMode=select mode (the default), the driver always uses a SELECT statement (allowing function invocation only). In escapeSyntaxCallMode=callIfNoReturn mode, the driver uses a CALL statement (allowing procedure invocation) if there is no return parameter specified, otherwise the driver uses a SELECT statement. In escapeSyntaxCallMode=call mode, the driver always uses a CALL statement (allowing procedure invocation only).
As you can see all {call something()} statements are treated like function calls by default and always translated to SELECTs. Setting escapeSyntaxCallMode to call will make the driver translate them to call SQL statements instead. The callIfNoReturn option seems most reasonable for most use-cases as it will transform JDBC calls to stored procedure calls if no return parameter has been specified and as function calls otherwise.
You can find an example of using this setting in Postgres docs (Chapter 6. Calling Stored Functions and Procedures):
// set up a connection
String url = "jdbc:postgresql://localhost/test";
Properties props = new Properties();
// ... other properties ...
// Ensure EscapeSyntaxCallmode property set to support procedures if no return value
props.setProperty("escapeSyntaxCallMode", "callIfNoReturn");
Connection con = DriverManager.getConnection(url, props);
// Setup procedure to call.
Statement stmt = con.createStatement();
stmt.execute("CREATE TEMP TABLE temp_val ( some_val bigint )");
stmt.execute("CREATE OR REPLACE PROCEDURE commitproc(a INOUT bigint) AS '"
+ " BEGIN "
+ " INSERT INTO temp_val values(a); "
+ " COMMIT; "
+ " END;' LANGUAGE plpgsql");
stmt.close();
// As of v11, we must be outside a transaction for procedures with transactions to work.
con.setAutoCommit(true);
// Procedure call with transaction
CallableStatement proc = con.prepareCall("{call commitproc( ? )}");
proc.setInt(1, 100);
proc.execute(); proc.close();>
-- https://jdbc.postgresql.org/documentation/head/callproc.html#call-procedure-example
Related
I am using snowflake-jdbc-3.13.8.jar and jdk1.8.0_151 to implement snowflake stored procedure which has an OUT parameter. I am getting
net.snowflake.client.jdbc SnowflakeLoggedFeatureNotSupportedException
on the line where registerOutParameter is called.
‛‛‛
CallableStatement st = connection.prepareCall("call sp_poc(?)"); //Call proc
st.registerOutParameter(1,java.sql.Types.VARCHAR); //exception here
ResultSet rs= st.executeQuery(); //execute st
‛‛‛
Below is the procedure created on snowflake db
CREATE or Replace PROCEDURE sp_poc()
RETURNS VARCHAR
LANGUAGE javaScript
AS
$$
var rs="test";
return rs;
$$;
You cannot use the output of a Snowflake stored procedure directly. If you want to get its return output, you need to run another query. This is true whether it's running from the web UI or through a driver or connector:
call sp_poc();
select * from table(result_scan(last_query_id()));
If you don't want to depend on the last_query_id() function, you can capture the query ID in the JDBC call. You can refer to the getQueryId() method of the Snowflake JDBC driver here:
https://docs.snowflake.com/en/user-guide/jdbc-api.html#interface-snowflakecallablestatement
If you capture the query ID that way, you can then get the output results of the SP for up to 24 hours after its completion using:
select * from table(result_scan(?));
Where the bind variable is the query ID returned from the getQueryId() JDBC method.
While executing Sybase procedure from JDBC, I am getting below error:
Execute cursor is declared on a procedure which contains a non-SELECT
or a SELECT with COMPUTE clause. for the declaration of this cursor to
be legal it should have a single select statement without a compute
clause
I am using JCONN4 sybase jar. Does sybase has such restrictions on procedure to not have select statement with compute clause?
Also I searched in Sybase documentation but couldn't get proper answer.
http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.39996_1250/html/svrtsg/svrtsg348.htm
I cannot post the procedure here, but I can post the sample
create proc sample (#value_date datetime = null) as
begin
if #value_date is null
select #value_date = some_column from some_table
select a,b,c,d into #ad
from
table_a where a='something'
select a,b,c,d case when a=0 then 0 else b/a
from #ad
end
The above procedure is called using sybStatemt.executeQuery function
Looks like its Sybase bug. steps to reproduce the issue
Create a procedure having select with compute clause as i described above
write jdbc program and use belew method
statement.setFetchSize(1000);
Execute the program and you will see the error
now the question is does Sybase really has these kind of restrictions or it is specific to their Driver only and we can say its driver issue ?
You must use CallableStatement when calling store procedure
If you execute a stored procedure in a CallableStatement object that represents parameter values as question marks, you get better performance than if you use both question marks and literal values for parameters. Also, if you mix literals and question marks, you cannot use output parameters with a stored procedure.
The following example creates sp_stmt as a CallableStatement object for executing the stored procedure MyProc:
CallableStatement sp_stmt = conn.prepareCall( "{call MyProc(?,?)}");
The two parameters in MyProc are represented as question marks. You can register one or both of them as output parameters using the registerOutParameter methods in the CallableStatement interface.
In the following example, sp_stmt2 is a CallableStatement object for executing the stored procedure MyProc2.
CallableStatement sp_stmt2 = conn.prepareCall( {"call MyProc2(?,'javelin')}");
Run your sp from sybase command prompt.
If it gives result it should work with sybase driver.
I have used ado.net driver in c# it can run similar queries
https://marketplace.visualstudio.com/items?itemName=CDATASOFTWARE.SybaseADONETProvider
Your Sp looks simple. But i think your sp had some runtime issue.
I think this line
if #value_date is null
select #value_date = some_column from some_table
should be
if #value_date is null
select #value_date = some_column from some_table where col1='kkk' so that only
one value comes
I have a stored procedure in a postgres database. I'm using the postgres JDBC driver to execute a stored procedure, and I do not care about the return type, and can't execute the query. It's indicating that there's a syntax error near the name of the function.
In procedures that return rows, I've been able to do this via a PreparedStatement and setting the parameters, like:
PreparedStatement prepared = connection.prepareStatement("SELECT * FROM NonQueryProcedure(?)");
prepared.setInt(1, 999);
// ....
ResulSet resultSet = prepared.executeQuery();
However, I can't seem to get this to work for an "update" stored procedure where I don't care about the return type. I've tried using connection.prepareStatement() and prepareCall(), and also tried executing it with statement.execute(), .executeUpdate(), and .executeQuery(), without success.
How can I execute a stored procedure where I don't care about the return type?
As PostgreSQL has no "real" procedures, functions are simply executed using a SELECT statement:
statement.execute("select NonQueryProcedure(?)");
Note that inside a PL/pgSQL function, you can use the perform statement to call such a function. But this is not available outside of a PL/pgSQL block.
Without the actual syntax error, I can't say for sure, but try this:
"SELECT * FROM \"getData\"(?)"
CamelCase/PascalCase is a BAD idea in any SQL database. Either it folds it to a single case and all you see is AMASSOFUNREADABLELETTERS or it requires quoting and you will have to forevermore type "aMassofLettersAndQuotesAndShiftKeysAndMyFingersHurt" anytime you want to avoid a syntax error.
I'm having issues with stored procedures that have a lot of insert statements when using Sybase's jdbc driver. After 50-60 inserts, the stored procedure stops execution and returns. See code below.
I'm using Sybase Anywhere 10 and their jconn2.jar, but have also tried jconn3.jar.
java code:
String sp = "sp_test";
Statement stmt = con.createStatement();
stmt.execute(sp);
stmt.close();
stored procedure:
create procedure dba.sp_test()
as
begin
declare #lnCount integer
select #lnCount = 1
while (#lnCount <= 1000 )
begin
insert into tableTest (pk) values (#lnCount)
select #lnCount = #lnCount + 1
end
end
After 58 inserts the procedure returns. Doing a select count(*) from tableTest afterward returns a count of 58. There are not SQLExceptions being thrown. I tried putting a begin/commit transaction around the insert, but it didn't make a difference. I've also tried the jodbc driver and it works fine, but I'm not able to use this as a solution because I've had other issues with it.
Using executeUpdate solved the problem:
String sp = "sp_test";
Statement stmt = con.createStatement();
stmt.executeUpdate(sp);
stmt.close();
I believe if you insert con.commit() immediately after stmt.execute() would work too.
StringBuffer sql = new StringBuffer("{ call ? := mailmerge_package.getLetters(?, ?, ?)}");
I know it's like an sql statement but theres no such thing as 'call' in SQL.
Can someone explain to me what it means and how does it come to be understood by Java
EDIT:
import oracle.jdbc.driver.OracleTypes;
//omitted code
CallableStatement cs = null;
ResultSet rs = null;
StringBuffer sql = new StringBuffer("{ call ? := mailmerge_package.getLetters(?, ?, ?)}");
try {
cs = conn.prepareCall(sql.toString());
cs.registerOutParameter(1, OracleTypes.CURSOR);
DAOUtils.setLong(cs, 3, checklistAnsMastId);
DAOUtils.setLong(cs, 2, workEntityId);
cs.setLong(4, patientId);
DAOUtils.setLong(cs, 5, encounterId);
cs.setString(6, encounterType);
cs.execute();
rs = (ResultSet)cs.getObject(1);
This looks like SQL you can pass to Oracle.
If so then this could be used to call an Oracle function mailmerge_package.getLetters which returns a value. That value is parsed by JDBC and the Db layer to replace the first ?, this can be read into a Java variable. the Oracle function takes 3 parameters (the 3 ? in parentheses)
{call <<procedure name>>}
is a SQL escape sequence. Basically, since different databases have different syntax for how to call a user-defined procedure and different databases have different built-in functions for various things like common date/ time functions, JDBC drivers implement a number of escape sequences where the driver translates a generic specification (i.e. {call <<procedure name>>}) and expands that to be the database-specific syntax. There are various other escape sequences for things like outer joins, date literals, and string functions that can be useful if you're trying to write database agnostic code.
FYI, these escape sequences were originally defined in the ODBC API and then adopted by JDBC so you may find more documentation related to ODBC than JDBC.
It is call to an stored procedure of a database. But which database, I can't tell from this code.