SQL anywhere query error: Not enough values for host variables - c#

I am building a query using ODBC command object in .Net with multiple parameters being passed in. When executing the query against SQL Anywhere, I get the following error. (The same code works against SQL Server).
[System.Data.Odbc.OdbcException] = {"ERROR [07002] [Sybase][ODBC Driver][SQL Anywhere]Not enough values for host variables"}
The command object has the same number of parameters added as the place holders ('?') in the query. Following is a simple query and C# code that fails the test.
C# code to populate the host variables
String queryText= #"DECLARE #loanuseraddress varchar(40), #loanid decimal
Set #loanid = ?
Set #loanuseraddress = ?
select * from loan_assignments where loan_id = #loanid"
OdbcConnection connection = new OdbcConnection(request.ConnectionString);
OdbcCommand command;
command = new OdbcCommand(queryText, connection);
OdbcParameter param1 = new OdbcParameter("#loanid", OdbcType.Decimal);
param1.Value = request.Loan.LoanNumber;
command.Parameters.Add(param1);
OdbcParameter param2 = new OdbcParameter("#loanuseremployer", dbcType.VarChar);
param2.Value = appraisalCompanyUpdate.LoanUserEmployer;
if (param2.Value == null)
param2.Value = DBNull.Value;
command.Parameters.Add(param2);
connection.Open();
OdbcDataReader rows = command.ExecuteReader();

I fixed this by checking for nulls. When you try to pass a null parameter to Sybase, that's the error you get (at least for me). Have a feeling LoanId is null at some point.
Edit After doing a little more research, I think you can also get this error when you try multiple insert / deletes / updates through the Sybase ODBC Connection in .Net. I don't think this is supported and MSDN seems to say it's vendor specific.

"Insufficient host variables" can also mean something else but it's applicable to the OP:
one of the other causes could be that you have a set of declared variables different from the set your SQL statement is using.
E.g. this could be a typo, or you could have copied in SQL from Visual Studio that was used to fill a dataset table using parameters (like :parm) but in doing so you forgot to declare it (as #parm) in your stored proc or begin/end block.

Related

How to Select an out parameter from a MySql Procedure in .net

Assume we have a stored procedure like so
CREATE PROCEDURE CopyValue(IN src INT, OUT dest INT)
BEGIN
SET dest = src;
END
I want to call this from a .net app (assume connection etc created successfully)
var sql = "call CopyValue(100, #destValue); select #destValue as Results;";
The string in the above statement works perfectly well when called in MySql Workbench.
However this - obviously - fails with "MySqlException: Parameter '#destValue' must be defined" when executed on a MySqlCommand object in .net
How do I arrange this statement so I can capture an output parameter from an existing procedure?
NB: I'm running against MySql 5.6, which I can't upgrade at this time.
NB Calling the procedure directly with CommandType.StoredProcedure goes against company guidelines.
By default, user-defined variables aren't allowed in SQL statements by MySQL Connector/NET. You can relax this restriction by adding AllowUserVariables=true; to your connection string. No modifications to your SQL or how you're executing the MySqlCommand should be necessary.
For information about why this is the default, you can read the research on this MySqlConnector issue (which also has the same default behaviour, but a much better error message that will tell you how to solve the problem): https://github.com/mysql-net/MySqlConnector/issues/194
A colleague (who wishes to remain anonymous) has answered this perfectly. Essentially put backticks ` after the # and at the end of the variable name e.g.
#`MyParam`
A fully working example.
static void Main(string[] args)
{
using var con = new MySql.Data.MySqlClient.MySqlConnection("Data Source=localhost; User Id=...;Password=...;Initial Catalog=...");
con.Open();
using var cmd = con.CreateCommand();
cmd.CommandText = "call CopyValue2(100, #`v2`); select #`v2` as Results;";
using var reader = cmd.ExecuteReader();
if (reader.Read())
Console.WriteLine($"Copied Value {reader.GetInt64(0)}");
}
Thanks OG :)

How do I call a stored procedure with unconventional parameters?

I'm attempting to integrate Red Gate's SQLBackup Pro software into my in-house backup software, written in C#. The natural way to do this is via their Extended Stored Procedure. The problems is that it's called in a format I've never seen before:
master..sqlbackup '-SQL "BACKUP DATABASE pubs TO DISK = [C:\Backups\pubs.sqb]"'
This works just fine when run via SSMS. Where I run into trouble is trying to call it from C# (using .NET 4 and Dapper Dot Net).
My first attempt doesn't work because it interprets the entire cmd string as the name of the stored procedure and throws the error "Cannot find stored procedure ''":
var cmd = "master..sqlbackup '-SQL \"BACKUP DATABASE pubs TO DISK = [C:\\Backups\\pubs.sqb]\"'";
connection.Execute(cmd, commandType: CommandType.StoredProcedure, commandTimeout: 0);
My second attempt returns immediately and appears (to C#) to succeed, but no backup is actually taken (this also sucks for parameterization):
var cmd = "master..sqlbackup";
var p = new DynamicParameters();
p.Add("", "'-SQL \"BACKUP DATABASE pubs TO DISK = [C:\\Backups\\pubs.sqb]\"'");
connection.Execute(cmd, p, commandType: CommandType.StoredProcedure, commandTimeout: 0);
My third attempt also appears to succeed, but no backup is actually taken:
var cmd = "master..sqlbackup '-SQL \"BACKUP DATABASE pubs TO DISK = [C:\\Backups\\pubs.sqb]\"'";
connection.Execute(cmd, commandTimeout: 0);
What am I missing?
UPDATE 1:
I overlooked the Red Gate documentation that says the stored proc won't actually raise a SQL error, it just returns errors in an output table. Slick. This might explain why I was getting silent failures in the second and third tests above: some underlying problem and they're not collecting the output to show why.
Here's where I am now:
var cmd = "master..sqlbackup";
var p = new DynamicParameters();
p.Add("", "'-SQL \"BACKUP DATABASE pubs TO DISK = [C:\\Backups\\pubs.sqb]\"'");
p.Add("#exitcode", DbType.Int32, direction: ParameterDirection.Output);
p.Add("#sqlerrorcode", DbType.Int32, direction: ParameterDirection.Output);
connection.Execute(cmd, p, commandType: CommandType.StoredProcedure, commandTimeout: 0);
When I run this and check those output parameters, I get Exit Code 870:
No command passed to SQL Backup.
The command is empty.
So it's not seeing the empty-named paramater.
Update 2:
Capturing the above in a trace shows that the empty parameter string ends up replaced with #Parameter1= which explains why the stored procedure doesn't see it.
Your first attempt looks almost right. What I notice is that you failed to escape the backslashes. For this kind of thing, it's often easier to use the # prefix to disable escaping for the string. Also, you want to prepend with exec and make it CommandType.Text:
EDIT: fixed my own escaping bugs here
var cmd = #"exec 'master..sqlbackup -SQL ""BACKUP DATABASE pubs TO DISK = [C:\Backups\pubs.sqb]""'";
connection.Execute(cmd, commandType: CommandType.Text, commandTimeout: 0);
It's gross and not at all what I wanted, but this is what I've got working now:
var cmd = String.Format(#"
DECLARE #exitcode int;
DECLARE #sqlerrorcode int;
EXEC master..sqlbackup '-SQL \"BACKUP DATABASE [{0}] TO DISK = [{1}])\"', #exitcode OUTPUT, #sqlerrorcode OUTPUT;
IF (#exitcode >= 500) OR (#sqlerrorcode <> 0)
BEGIN
RAISERROR('SQLBackup failed with exitcode %d and sqlerrorcode %d ', 16, 1, #exitcode, #sqlerrorcode)
END
", myDbName, myBkpPath);
connection.Execute(cmd, commandTimeout: 0);
This executes the backup and actually returns failure status, which exposed an underlying issue that caused the failure part of the silent failures.
Before it runs, myDbName is checked against a list of known databases to ensure it exists and myBkpPath is generated by my code, so I'm not worried about injections. It's just...well, look at that. Hideous.
Have you tried creating a typical stored procedure that calls the extended stored procedure, and calling that from code? It looks like you have only a few parameters to deal with here.
You had several problems in your tests.
In the first one you set the CommandType.StoredProcedure. You should have set it to CommandType.Text so that it would be smart enough to just pass that string along to be exec'd.
In the subsequent examples, you didn't actually give the parameter a name. Go look at their SqlBackup procedure and see what the parameter names are. Then use it. Otherwise, nothing is going to be assigned to it.

C#: DB2 test available connection first

I have a C# .NET program running an ETL which connects to a DB2 database. Sometimes this database is down, so I'd like to do a health check at the beginning of the application to see if the database is available, without actually calling any stored procedures or pushing any data. Here's an example of the code I'm using now:
OdbcConnection myODBCConnection = new OdbcConnection("DSN=DB2AA;UID=ABCD;PWD=1234;");
OdbcCommand myODBCCommand = new OdbcCommand();
myODBCCommand.CommandType = CommandType.StoredProcedure;
myODBCCommand.CommandText = "{CALL SYSPROC.ABC001(?, ?)}";
myODBCCommand.Parameters.Add("INPUT", OdbcType.VarChar, 500);
myODBCCommand.Parameters["INPUT"] = myString
myODBCCommand.Connection = myODBCConnection
myODBCConnection.Open();
OdbcTransaction myTrans;
myTrans = myODBCConnection.BeginTransaction();
myODBCCommand.Transaction = myTrans;
myTrans.Commit();
myODBCCommand.ExecuteNonQuery();
myODBCConnection.Close();
What's the best way to test this connection without actually pushing any data?
You can simply run some innoccuous select query to check to see if the db is available.
You can try to do something as simple as:
Select 1
Or
Select getdate()
Those simple queries don't even touch any tables but will return only if the rdbms is running.
Note: those examples are for sql server but might work for db2. I haven't had to do a live check on a db2 yet though the similar concept should be doable.
Note 2: after a closer look at your code, all you should really have/need to do is check for success of your odbc connection's .Open() call.

executing Oracle stored procedures using ADO (c#)

Im trying to call a Oracle stored proc from a C# application using the following code
Connection conn = new Connection();
Recordset rs = new Recordset();
conn.Open("Provider=MSDAORA;User Id=username;Password=password;Data Source=DB;", null, null, 0); ;
rs.Open("sproc 'abc', 'xyz'", conn, ADODB.CursorTypeEnum.adOpenStatic, ADODB.LockTypeEnum.adLockReadOnly, -1);
where abc and xyz are input parameters..
However, I get "invalid SQL statement exception" when I try to run it..
Is there any other way to execute a oracle stored proc. I can execute MSSQL stored procs or normal Oracle queries in the same way described above..
I even tried using createparameter, but that didn't help either
Thanks,
Sam
Grab the Oracle ODP.NET tools: http://www.oracle.com/technology/software/tech/windows/odpnet/index.html
They are what I use to interact with our Oracle database from my ASP.NET application
Check here for an example of calling an Oracle stored procedure in C#.
Basically, with the package:
// Create oracle command object for the stored procedure
OracleCommand cmd = new OracleCommand("HR_DATA.GETCURSORS", conn);
cmd.CommandType = CommandType.StoredProcedure;
// Enter a parameter for the procedure
OracleParameter dep_id = new OracleParameter();
dep_id.OracleDbType = OracleDbType.Decimal;
dep_id.Direction = ParameterDirection.Input;
dep_id.Value = 60;
cmd.Parameters.Add(dep_id);
// Add more parameters ...
// Execute the stored procedure
Here's a link to the API documentation
Nevermind.. Apparently I was missing brackets around input parameters...
Thanks,
Sam

SQL 'Execute As' Login Command and Linq to SQL

I am trying to execute a sql query as another login using the 'Execute As' command. I am using Linq to SQL, so I've generated a Data Context class and I am using the ExecuteQuery method to run the 'Execute As' SQL command. I then call a Linq to SQL command that is successful. However, every subsequent query fails with the following error:
A severe error occurred on the current command. The results, if any, should be discarded.
Here is the code snippet that I have tried:
SummaryDataContext summary = new SummaryDataContext();
summary.ExecuteQuery<CustomPostResult>(#"Execute as Login='Titan\Administrator'");
var test = summary.Customers.First();
var test2 = summary.Products.ToList();
No matter what query I run on the second query I receive the error message from above. Any help would be appreciated.
I managed to get around this issue in my application by executing the query using ADO.NET classes.
SqlCommand cmd = new SqlCommand("EXECUTE AS USER = 'operator'");
cmd.Connection = dc.Connection as SqlConnection;
cmd.Connection.Open();
cmd.ExecuteNonQuery();
// do the rest of the queries using linq to sql
You may have already ruled this out, but one possible work around would be to simply create the data context with a different connection string.
To edit the connection string, you can set the DataContext.Connection.ConnectionString property. I've done it before in the partial method OnCreated(), which gets called when the data context gets created. I haven't tested but I think you could also do:
YourDataContext dc = new YourDataContext();
dc.Connection.ConnectionString = "connection string here";
Here's an article that describes this as well - http://www.mha.dk/post/Setting-DataContext-Connection-String-at-runtime.aspx
I was having a similar issue and by looking at ruskey's answer I was able to Execute as User but noticed that I was getting errors when running other queries after that. It was due to the missing Revert. So for anyone having a similar issue this is how the code looks like.
SqlCommand cmd = new SqlCommand("EXECUTE AS USER = 'domain\\user';");
OSSDBDataContext dc = new OSSDBDataContext();
cmd.Connection = dc.Connection as SqlConnection;
cmd.Connection.Open();
cmd.ExecuteNonQuery();
//Execute stored procedure code goes here
SqlCommand cmd2 = new SqlCommand("REVERT;");
cmd2.Connection = dc.Connection as SqlConnection;
cmd2.ExecuteNonQuery();

Categories