How to call Sybase stored procedure with named params using JDBC - sql

I have a stored procedure in Sybase which I can invoke from my favourite SQL client like this:
exec getFooBar #Foo='FOO', #Bar='BAR'
It returns a table of results, so its like a query. It has actually many parameters, but I only want to call it with Foo and sometimes with Foo and Bar specified.
Sybase is ASE 15.0, I am using jConnect 6.0.5.
I can invoke this using a PreparedCall if I specify only the first parameter:
CallableStatement cs = conn.prepareCall("{call getFooBar(?) }");
cs.setString(1,"FOO");
However I can't use the #Foo notation to pass my params:
conn.prepareCall("{call getFooBar #Foo='FOO' }");
conn.prepareCall("call getFooBar #Foo='FOO' ");
conn.prepareCall("{call getFooBar #Foo=? }"); // + setString(1,'FOO')
In all these cases I get exception from the DB telling me that the Foo parameter is expected:
com.sybase.jdbc3.jdbc.SybSQLException: Procedure getFooBar expects
parameter #Foo, which was not supplied
Ideally I'd like to do this with Spring JdbcTemplate or SimpleJdbcCall, but with those I could not even get to the point where I could with plain old JDBC.
So to summarize, I'd like to avoid ending up with:
{ call getFooBar(?,null,null,null,null,null,?,null,null) }
Is that possible using JDBC or better Spring?

Not the most efficient solution, but, have you tried using the plain Statement itself with EXEC.
eg.
String mySql = "EXEC getFooBar #Foo='FOO', #Bar='BAR'";
ResultSet rs = conn.getConnection().createStatement().executeQuery(mySql);

Related

Groovy Sql Execute Statement won't accept closures

I have a statement:
sqlInstance.execute(executeString){
dummy, realList->
debug("Real LIst: "+realList)
}
which fails with 'Invalid column type'
But:
def bool = sqlInstance.execute(executeString)
works. If I print bool, it prints as 'true'.
For reference:
executeString = "select distinct channel_id from guide_sched"
For some reason, the closure isn't working for the execute method in groovy's Sql, although I've checked the documentation and it's supposed to.
It looks like the first environment I was testing on ran Groovy 2.4 and the second runs Groovy 2.1. The execute statement I was using didn't exist until after 2.1
Instead, I used the .rows() function to return a GroovyRowResult which I parsed for the information I needed, instead of accessing it directly in the .execute() closure.

Manually sanitize SQL parameters

I would like to run a script like:
CREATE LOGIN [me] WITH PASSWORD = #0
and run it like:
var createUserCommand = conn.CreateCommand();
createUserCommand.CommandText = script;
createUserCommand.Parameters.AddWithValue("#0", passwordDecrypted);
However, this throws:
A first chance exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll
Additional information: Incorrect syntax near '#0'.
From what I read online (nowhere official, just from SO answers/comments) it is not possible to use SQL parameters with DDL statements. Link to official docs for this are welcome!
OK. I do need this parametrized. As I see it, there are 2 options:
I manually sanitize (.Replace("'", "''") => how can I do this best?
I call into .NET to sanitize for me. However I assume this is not sanitized within ADO.NET, but just past to SQL Server, and sanitized there...
What would be the best approach?
All you need to escape is the single ' to 2 x '
commandText = string.Format("CREATE LOGIN [me] WITH PASSWORD = '{0}'", pass.Replace("'", "''"));
An alternative would be to create a stored procedure with a password parameter and use it to build a CREATE LOGIN string which you then sp_executeSQL.

Dapper: Procedure or function has too many arguments specified

While using Dapper to call a stored procedure, I'm receiving the following error:
Procedure or function has too many arguments specified
I'm using DynamicParameters to add a list of simple parameters to the query.
The parameter code looks like this:
var parameters = new DynamicParameters();
parameters.Add(p.Name, p.Value, direction: p.Mode);
The query code looks like this:
var result = _connection.Query<T>(
string.Format("{0}.{1}", request.SchemaName, request.StoredProcedureName),
parameters,
commandType: CommandType.StoredProcedure,
transaction: _transaction);
The executing sql in the profiler shows as following:
exec dbo.storedProcedureName #ParameterNames1=N'ParameterName',#ParameterNames2=N'ParameterName',#RemoveUnused=1
#ParameterNames1 is not at all how the parameter is called. Actually, the names are being passed in as the values (N'ParameterName'). The #RemoveUnused parameter seems completely random to me, as it does not occur in the calling code at all.
The full code for this can be found here: GitHub project at lines 61 and 228.
Edit: I've found that the issue is caused by calling the same procedure twice, with different result sets. So the first time I'm calling it with Query, the second time with Query. Why Dapper is having trouble with this scenario is still a mystery.
I recently came across this issue and this appears to be caused by the following:
Your stored procedure can return multiple datasets (maybe based on a
condition parameter).
You are calling the stored procedure using
Query<T>() instead of QueryMultiple() and then mapping datasets
via Read<T>.
We recently upgraded from an old version of Dapper to v1.4 in order to support Table Variable Parameters and we started experiencing this behaviour as a direct result of the upgrade.
Solution:
Convert your Query<T> based code to a QueryMultiple implementation.
I simply can't reproduce this:
public void SO25069578_DynamicParams_Procs()
{
var parameters = new DynamicParameters();
parameters.Add("foo", "bar");
try { connection.Execute("drop proc SO25069578"); } catch { }
connection.Execute("create proc SO25069578 #foo nvarchar(max) as select #foo as [X]");
var tran = connection.BeginTransaction(); // gist used transaction; behaves the same either way, though
var row = connection.Query<HazX>("SO25069578", parameters,
commandType: CommandType.StoredProcedure, transaction: tran).Single();
tran.Rollback();
row.X.IsEqualTo("bar");
}
public class HazX
{
public string X { get; set; }
}
works fine. There is a RemoveUnused property on DynamicParameters, bit: when using dynamic parameters, that shouldn't be added. I've even tried using the template based constructor:
parameters = new DynamicParameters(parameters);
but again: this works fine. Is it possible that you're using a really, really old version of dapper? What version are you using?
I realize this is an old thread. I'm however using the latest version of the Nuget package (1.60.6) and encountered this problem recently.
To reproduce this, you'll need a Stored Procedure that based on an input parameter could return 1 or 2 (more than 1) resultset. In the code, I use 2 different extension methods to call it, too (QueryMultipleAsync sets the parameter to 1 or true and QueryAsync which sets it to 0 or false). If your test ends up calling the SP to return multiple resultset first, subsequent calls that need 1 resultset will fail with this error.
The only way I managed to solve this was to break down the SP into 2 so they have different names.
For reference, here is how I call the SP:
var data = await sqlConnection.QueryAsync<T>(
StoredProcedureName,
parms,
transaction: null,
commandTimeout: null,
commandType: CommandType.StoredProcedure)
and
var data = await sqlConnection.QueryMultipleAsync(
StoredProcedureName,
param: p,
commandType: CommandType.StoredProcedure)
.Map<Type1, Type2, long>
(
o1 => o1.Id,
o2 => o2.FkId ?? 0,
(o1, o2) => { o1.Children = o2.ToList(); }
);
This Dapper issue is caused by the Read method for reading datasets after a QueryMultiple.
In this case Dapper caches the parameters and if you call the same stored procedure with the same parameters using a Dapper Query method, it will fail.
To solve the problem, change the call to QueryMultiple method from like this:
var reader = conn.QueryMultiple (spName, pars, commandType: CommandType.StoredProcedure);
to this:
var cmd = new CommandDefinition (spName, pars, commandType: CommandType.StoredProcedure, flags: CommandFlags.NoCache);
var reader = conn.QueryMultiple (cmd);
Recently hit this problem caused by calling the same procedure twice using different Dapper methods.
The first call to the same SQL stored procedure was via .QueryMultiple. calling the same procedure with parameters again using .QuerySingleOrDefault resulted in the parameters being ParameterNames1 and RemoveUnused mentioned in the origninal question.

SQL Parameter not found

The error:
Procedure or Function '' expects parameter '#Param1' which was not supplied.
An excerpt of the stored procedure:
I have a stored procedure on a SQL Server 2012. The procedure looks something like this...
SELECT *
FROM Orders
WHERE Orders.CustomerID = #param1 AND
Orders.CustomerJoinDate = #param2
I call it from my code {Using Visual Studio 2008} like so...
The calling method in Visual Studios:
First I create an array of parameters I'm going to pass...
Dim Param() As SqlClient.SqlParameter =
New SqlClient.SqlParameter()
{
New SqlClient.SqlParameter("#Param1", Me.cmbFilter1.Text),
New SqlClient.SqlParameter("#Param2", Me.cmbFilter2.Text)
}
Then I loop through the parameters and add them to a command to be executed by a datareader.
The mSQLCmd is set to call the stored procedure described above...
mSQLCmd.Parameters.Clear()
mSQLCmd.CommandText = SQLCmd
For Each sParam As SqlClient.SqlParameter In Param
mSQLCmd.Parameters.AddWithValue(sParam.ParameterName, sParam.Value)
Next
Error occurs when:
I try to run mSQL.ExecuteReader() Can someone point me in the right direction on this? I've verified that each parameter is included in the Param() with the correct values.
I've also tested the stored procedure on SQL Server and verified when the two necessary parameters are provided it works correctly. Something is wrong on the vb side.
If you're calling a stored procedure, you need to set the CommandType of the SqlCommand accordingly!
mSQLCmd.CommandText = SQLCmd
// add this line!
mSQLCmd.CommandType = CommandType.StoredProcedure
Otherwise the name of the stored procedure you're trying to call is interpreted as a SQL command you're trying to execute.

How do you retrieve the return value of a DB2 SQL sproc using Perl DBI?

I need to retrieve the value returned by a DB2 sproc that I have written. The sproc returns the number of rows in a table and is used by the calling process to decide whether or not to update other data.
I have looked at several similar questions on SO but they refer to the use of out parameters instead of using the sproc's return value, for example:
Perl Dbi and stored procedures
I am using a standard DBI connection to the database with both RaiseError and PrintError enabled.
$sql_stmt = "call MY_TABLE_SPACE.MY_SPROC('2011-10-31')";
$sth = $dbh->prepare($sql_stmt)
or die "Unable to prepare SQL '$sql_stmt': $rps_met_dbh->errstr";
$rsp = 0;
$rsp = $sth->execute();
unless($rsp) {
print(STDERR "Unable to execute sproc: $rps_met_dbh->errstr\n");
}
print(STDERR "$?\n");
I have tried looking at $h->err for both the statement handle and the db handle.
I would really prefer communicating the number of rows via a return code rather than using SQLSTATE mechanism if I can.
Edit:
I have finished up using a dedicated out parameter to communicate the number of rows updated as follows:
$sql_stmt = "call MY_TABLE_SPACE.MY_SPROC('2011-10-31')";
$sth = $dbh->prepare($sql_stmt)
or die "Unable to prepare SQL '$sql_stmt': $rps_met_dbh->errstr";
$sth = $dbh->bind_param_inout(1, $rows_updated, 128)
or die "Unable to prepare SQL '$sql_stmt': $rps_met_dbh->errstr";
$rows_updated = 0;
$rsp = 0;
$rsp = $sth->execute();
unless($rsp) {
print(STDERR "Unable to execute sproc: $rps_met_dbh->errstr\n");
}
print(STDERR "$rows_updated\n");
Edit 2:
And now thinking about this further I have realised that I should apply the PragProg principle of "Tell. Don't Ask." That is, I shouldn't call the sproc. then have it give me back a number before I decide whether or not to call the anopther sproc, i.e. "Ask".
I should just call the first sproc. and have it decide whether it should call the other sproc or not, i.e. "Tell" and let it decide.
What is wrong with using an output parameter in your procedure. I've not got a working DB2 lying around right now or I'd provide an example but when I was using it I'm sure you can define output parameters in procedures and bind them with bind_param_inout. I cannot remember if a DB2 procedure can return a value (like a function) but if it can them using "? = call MY_TABLE_SPACE.MY_SPROC('2011-10-31')" would allow you to bind the output return value. If this doesn't work you could use a DB2 function which definitely can return a value. However, at the end of the day the way you get data out of a procedure/function is to bind output parameters - that is just the way it is.
I've no idea what you mean by "using SQLSTATE". I've also no idea what you mean by looking at $h->err as that is only set if the procedure fails or you cannot call the procedure (SQL error etc).