I am trying to use ALTER USER query for Oracle database using OracleCommand in C# in the following code. It creates the query if the values for Username and password are not empty strings. But I get an error "ORA-01036: illegal variable name/number" when ExecuteNonQuery() is executed.
string updateQuery = "ALTER USER :user IDENTIFIED BY :password";
connection = new OracleConnection(LoginPage.connectionString);
connection.Open();
OracleCommand cmd = new OracleCommand(updateQuery, connection);
cmd.Connection = connection;
for(int i=0;i<usersList.Count;i++)
{
if (!(selectedUsersArray[i].Equals("")) && !passwordArray[i].Equals(""))
{
OracleParameter userName = new OracleParameter();
userName.ParameterName = "user";
userName.Value = selectedUsersArray[i];
OracleParameter passwd = new OracleParameter();
passwd.ParameterName = "password";
passwd.Value = passwordArray[i];
cmd.Parameters.Add(userName);
cmd.Parameters.Add(passwd);
cmd.Prepare();
cmd.ExecuteNonQuery();
}
}
Could you please suggest what is wrong with my implementation?.
The root cause
In Oracle you have three kinds of SQL statements (and additionally there are PL/SQL blocks):
Statements in the Data Definiton Language (DDL). These statements modify the structure of the database. They begin usually with the verbs "ALTER" or "CREATE"
Statements in the Data Modification Langugage (DML). There statements modify the content inside of tables, leaving the structure of each table unmodified. These statements usually begin with "INSERT", "MERGE" or "DELETE".
Statements in what I call "query language" (there seems to be no canonical name for these). This statements start with the verb "SELECT".
Bind variables in Oracle are only allowed in some special places in DML and query statements. You are trying to use bind variables in a places where they are not allowed. Hence the error.
Solution
Build your statement without bind variables. Build the complete query string instead using string concatenation.
If you want to sanitize the input before concatenating the string, use the DBMS_ASSERT package.
Background
Bind variables can only be used when Oracle can build a query plan without knowing the value of the variable. For DDL statements, there is no query plan. Hence bind variables are not allowed.
In DML and query statements, bind variables are only allowed, when they are used inside a tuple (regarding the underlying set theory), i.e. when the value will be compared with the value in a table or when the value will be inserted in a table. They are not allowed to change the structure of the execution plan (e.g. to change the target table or to change the number of comparisons).
Just for others getting this error and looking for info on it, it is also thrown if you happen to pass a binding parameter and then never use it. I couldn't really find that stated clearly anywhere but had to prove it through trial and error.
I just spent several days checking parameters because I have to pass 60 to a stored procedure. It turns out that the one of the variable names (which I load into a list and pass to the Oracle Write method I created) had a space in the name at the end. When comparing to the variables in the stored procedure they were the same, but in the editor I used to compare them, I didnt notice the extra space. Drove me crazy for the last 4 days trying everything I could find, and changing even the .net Oracle driver. Just wanted to throw that out here so it can help someone else. We tend to concentrate on the characters and ignore the spaces. . .
You defined one oracleCommand but used it in 'for'.
it means you are adding parameter with the same name to one OracleCommand.
you should use cmd.Parameters.clear() to refresh your parameters.
for(int i=0;i<usersList.Count;i++)
{
if (!(selectedUsersArray[i].Equals("")) && !passwordArray[i].Equals(""))
{
cmd.Parameters.clear();//Add this line
OracleParameter userName = new OracleParameter();
userName.ParameterName = "user";
userName.Value = selectedUsersArray[i];
OracleParameter passwd = new OracleParameter();
passwd.ParameterName = "password";
passwd.Value = passwordArray[i];
cmd.Parameters.Add(userName);
cmd.Parameters.Add(passwd);
cmd.Prepare();
cmd.ExecuteNonQuery();
}
}
The Oracle error ORA-01036 means that the query uses an undefined variable somewhere. From the query we can determine which variables are in use, namely all that start with #. However, if you're inputting this into an advanced query, it's important to confirm that all variables have a matching input parameter, including the same case as in the variable name, if your Oracle database is Case Sensitive.
This error happens when you are also missing cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new OracleParameter("GUSERID ", OracleType.VarChar)).Value = userId;
I was having eight parameters and one was with space at the end as shown in the above code for "GUSERID ".Removed the space and everything started working .
I was having the same problem in an application that I was maintaining, among all the adjustments to prepare the environment, I also spent almost an hour banging my head with this error "ORA-01036: illegal variable name / number" until I found out that the application connection was pointed to an outdated database, so the application passed two more parameters to the outdated database procedure causing the error.
You cannot pass user/table name to pl/sql with a parameter. You can create a procedure and build sql and then execute immediately to achieve that.
I have faced same problem ... For the problem is like this, I am calling the PRC inside cpp program and my PRC taking 4 arguments but while calling I used only 1 arguments so this error came for me.
Begin Example_PRC(:1); End; // this cause the problem
Begin Example_PRC(:1,:2,:3,:4); End; // this is the solution
I had the same issue today when using Python module cx_Oracle. In my case, the root cause was an invalid variable name.
Example: SELECT * FROM DATA WHERE KEY IN (:_0, :_1, ...)
When I changed _0 to var0, it worked fine.
From this blog post, I found these rules for variable names:
Must start with a letter
Maximum size is limited to 30 letters
Cannot contain whitespace characters
Can contain dollar sign ('$'), underscore ('_') and hash sign ('#')
Is case-insensitive
I had the same problem, was learning connection to oracledb.
previous code-
SELECTALLCANDIDATES = "Select * from candidate_master";
data= await connection.execute(SELECTALLCANDIDATES, {autoCommit:true})
Removed the {autoCommit:true} and it started working fine.Correct code-
SELECTALLCANDIDATES = "Select * from candidate_master";
data= await connection.execute(SELECTALLCANDIDATES)
Still don't know why but it works.
Check your named variables match, I had the same problem, I had a spelling mistake/typo in one of my parameters
Related
I've got an error when i'm using MySql Stored Procedure and
Data Access Layer (MVC)have a method used to read data from database using MySqlDataAdapter and MySqlParameter and DataTable to read data called storedatai was tested this method before in a login controller and it's work as well : the main error here when i need to store last accountId using MySql last_insert_id() function but it always returns null value i tried to Convert it to int then add 1 in every time form opening , but that is useless because it's returns an error
System.ArgumentException: 'Parameter 'accountId' not found in the collection.'
here is the method in Data Access Layer that i use it to store data from database
public DataTable storeData(string trChannel,MySqlParameter[] list_OF)
{
InitializeDb();// Database configration method
MySqlCommand Transmeter = new MySqlCommand
{
CommandType = CommandType.StoredProcedure,
CommandText = trChannel,
Connection = dbcon // dbcon its Connection string comming from DAL
};
if (list_OF != null)
{
Transmeter.Parameters.AddRange(list_OF);
}
MySqlDataAdapter massenger = new MySqlDataAdapter(Transmeter);
DataTable _mainContainer = new DataTable();
massenger.Fill(_mainContainer);
disConnect();
return _mainContainer;
}
The stored procedure that i used it like this after i tried to use last_insert_id() and i failed so i change the sp to this and its work and give me result when i call it inside MySQL Server
CREATE DEFINER=`root`#`localhost` PROCEDURE `getAccountId`(out accountId int(5))BEGIN select Max(acotId)from accounts;set #accountId=last_insert_id(acotId); END
In this case the server going throw an error
Unknown "acotId" in fields list
but i don't need to fix it because it's give me a result , so when i called this procedure inside application using a method that return a DataTable value give the first error that i wrote it before System.ArgumentException: 'Parameter 'accountId' not found in the collection.' for the record i got more than five methods contains the same error most of them with input or output parameter and most of them without ..
public DataTable getAcotId()
{
DataTable pdbContainer = new DataTable();
pdbContainer = _socket.storeData("getAccountId",null);//_socket it's a link to data access layer
return pdbContainer;
}
I tried also method like this with a parameter and it's also didn't works
public DataTable getCusttId()
{
MySqlParameter[] parCut = new MySqlParameter[1];
parCut[0] = new MySqlParameter("?custId", MySqlDbType.Int16, 5) ;
parCut[0].Direction = ParameterDirection.Output;
DataTable pdbContainer = new DataTable();
pdbContainer = _socket.storeData("getCustomerId", parCut);
return pdbContainer;
}
Finally i want to convert the result from this methods to int and i think about using this code
int customerFinalId = getCustId.Rows.Fields[i].Feilds<int>("custId")
That is all guys and i'm dire to need help immediately for more necessary
Thank you a lot for helping me ..
You got MySQL, that is a problem for this problem. It is very common that you need to figure out "the Primary Key of the thing you just inserted". Usually to update the (G)UI.
If this was SQL, the OUTPUT clause would be your friend. This thing alone is worth its memory footprint in gold. But MySQL does not have any equivalent Syntax. That means you have to do it the hard way.
When figuring this value out, it is very important to guard against race conditions. The reliable way is to replace the implicit Transactions and Table locks of the DML statement with a explicit one that covers both the DML statement and the DQL Satement (SELECT) that follows.
Unfortunatley I am not that solid in MySQL Syntax, so somebody else will have to give you exact code.
There are multiple issues with your code
You NEED to provide all the parameters your SP expects or else it will just error out. Since you do not have any try and catch in your SP your entire query will bail on first error.
#accountId is a sql variable and you need to declare that if you want to use it
last_insert_id takes no parameter unless you want to set it explicitly which destroys the whole purpose of it in your example.
The answer here is the second method of getCusttId replacing the ? by #, which works for me.
I am struggling with proper parameter passing to a MySQL query. In MySQL workbench, my query works fine, but not in the C# code. I assume it is due to wrong parameter passing.
That's why I'd like to see what precisely do I pass to the cmd.ExecuteScalar() method. But I can't figure out how to determine the cmd string.
In debugger I only get query with formal parameters, not passed ones. And even by using cmd.ToString() I get this nonsense:
MySql.Data.MySqlClient.MySqlCommand.
Here is my code:
string timeStampStr = timeStamp.ToString("yyyy-MM-dd hh:mm:ss");
...
MySqlCommand cmd = new MySqlCommand("SELECT COUNT(*) FROM plc WHERE plc.last_communication < #timeThreshold AND plc.id = #plcId", _conn);
cmd.Parameters.AddWithValue("#timeThreshold", timeStampStr); // Is this correct ? timeStampStr is a string
cmd.Parameters.AddWithValue("#plcId", plcId);
object result = cmd.ExecuteScalar();
Thank you !
Your best bet is probably to enable the query log on MySQL and use that to profile what was sent to the database engine.
This is because the application code doesn't actually replace the placeholders with the parameter values, the database engine does. The application code invokes the parameterized query and supplies the parameters simultaneously. (As a bit of a side-effect, this allows database engines to cache execution plans for parameterized queries much more effectively, since the query itself doesn't change. This provides a slight performance improvement when using parameterized queries over concatenated values.)
And even by using cmd.ToString() I get this nonsence: MySql.Data.MySqlClient.MySqlCommand.
That's not nonsense, that's the name of the class on which you're calling .ToString(). The default behavior of .ToString() for reference types is to return the name of the type, unless you override it.
I'm currently evaluating Oracle's ODP.NET DataProvider and I ran into a problem that popped up in one of our testcases: When the same command text is executed with different parameter types, the parameter type of the first executed command is used in all following commands.
Take for example the following code:
const int sampleInt32 = 1234567890;
const string sampleNvarchar = "someTestString";
const string sqlCommandtext = "SELECT :PARAM PARAM FROM DUAL";
using (OracleConnection connection = new OracleConnection(builder.ConnectionString))
{
connection.Open();
//Test 1 - Int 32
using (OracleCommand commandInt32 = connection.CreateCommand())
{
commandInt32.CommandText = sqlCommandtext;
commandInt32.Parameters.Add("PARAM", OracleDbType.Int32, sampleInt32, ParameterDirection.Input);
using (IDataReader reader = commandInt32.ExecuteReader())
{
while (reader.Read())
{
int resultInt32 = (int)reader.GetDecimal(0);
Assert.AreEqual(sampleInt32, resultInt32);
}
}
}
//Test 2 - NVarchar
using (OracleCommand commandNVarchar = connection.CreateCommand())
{
commandNVarchar.CommandText = sqlCommandtext;
commandNVarchar.Parameters.Add("PARAM", OracleDbType.NVarchar2, sampleNvarchar, ParameterDirection.Input);
using (IDataReader reader = commandNVarchar.ExecuteReader())
{
while (reader.Read())
{
string resultNVarchar = reader.GetString(0);
Assert.AreEqual(sampleNvarchar, resultNVarchar);
}
}
}
}
If commandInt32 is executed before commandNVarchar, execution of commandNVarchar fails with ORA-01722 - Invalid number. If the order is switched so commandNVarchar is executed first, it fails with "Specified cast is not valid" on reader.GetDecimal.
So far I've tried setting StatementCacheSize=0; Pooling=false; StatementCachePurge=true as ConnectionString parameters but I can't get this to work.
Is there anything I'm missing or are there any other options worth trying?
EDIT: Maybe some background on why this is needed/required: We don't use ODP or any other Dataprovider directly in our application (or at least: we're on our way to reach this goal), there's an DataLayer in between that performs database/provider specific optimiziations and monitoring of connection health,...
In this Layer for example StoredProcedures can be called, having the option of parameter type tuning. Some of our procedures have Clobs as Parameter types, as sometimes the value can be longer than x characters, but most likely it will be shorter.
So before executing via ExecuteNonQuery with ArrayBindCount set to y, parameter values are checked if Clob can be passed as varchar (Nclob as Nvarchar). "Rebinding" reduces the time to execute 2500 records from about 500ms to 200ms at the cost of losing a few ms checking string lengths. And this rebinding can only be done if the parameter type can be changed. Without this option we would need to execute it as Clob everytime, taking the performance hit.
To my understanding, parameter binding is unsupported in a SELECT list. I was so surprised that this worked at all that I had to run your code to see it with my own eyes. I believe that for the client to allow that SQL statement to execute at all is a bug.
Regardless, I inserted the following line between the test cases to get them both to work:
connection.PurgeStatementCache();
However, this only seems to work with the Managed Client (I've tried it with version 4.112.3.60). The regular client still fails as you describe.
Two things. When used as connection string parameters, the configuration variables need to have spaces, ie
Statement Cache Size=0;
The format you are using can be used directly in the config though:
http://docs.oracle.com/html/E10927_01/featConfig.htm#CJABCACG
http://docs.oracle.com/html/E10927_01/featOraCommand.htm#CIHCAFIG
You could use that same configuration section to enable tracing - comparing the traces might give you an idea of what is happening.
I believe PurgeStatementCache (not sure StatementCachePurge exists) is a runtime command, ie
connection.PurgeStatementCache
Metadata Pooling = false;
Our application is using Oracle 12c with ODP.Net Managed Provider
When using OracleCommandBuilder.DeriveParameters() we were always seeing the same parameters return from the stored procedure despite adding/ removing/ updating parameters. We would only see the changes after restarting the IIS process.
The only solution that worked was setting Metadata Pooling = false; in the Oracle connection string
We had no success with the following which have been mentioned here or on Oracle's forums:
connection.PurgeStatementCache();
Statement Cache Size=0;
Pooling = false;
What version of Oracle are you connecting to? This may be a bind variable peaking (or lack thereof) issue. The feature was introduced in 9i but had some issues all the way thru 10. You could try executing the following to see if you can reproduce the problem without ODP.net:
var param varchar2(255)
exec :param:='TEST';
select :param FROM DUAL;
change the type on "param" from varchar2 to number and change the value and reexecute to see what happens.
You could also try executing the command under a different connection instead of a shared one.
In the end, you could simply rename the bind variable in the statement, relative to the type (ie :paramNum or :paramString). The name you give the the parameter on the .net side is irrelevant unless cmd.BindByName is set to true. By default it is false, and variables are bound in the order they are added.
I was debugging a database operation code and I found that proper UPDATE was never happening though the code never failed as such. This is the code:
condb.Open();
OleDbCommand dbcom = new OleDbCommand("UPDATE Word SET word=?,sentence=?,mp3=? WHERE id=? AND exercise_id=?", condb);
dbcom.Parameters.AddWithValue("id", wd.ID);
dbcom.Parameters.AddWithValue("exercise_id", wd.ExID);
dbcom.Parameters.AddWithValue("word", wd.Name);
dbcom.Parameters.AddWithValue("sentence", wd.Sentence);
dbcom.Parameters.AddWithValue("mp3", wd.Mp3);
But after some tweaking this worked:
condb.Open();
OleDbCommand dbcom = new OleDbCommand("UPDATE Word SET word=?,sentence=?,mp3=? WHERE id=? AND exercise_id=?", condb);
dbcom.Parameters.AddWithValue("word", wd.Name);
dbcom.Parameters.AddWithValue("sentence", wd.Sentence);
dbcom.Parameters.AddWithValue("mp3", wd.Mp3);
dbcom.Parameters.AddWithValue("id", wd.ID);
dbcom.Parameters.AddWithValue("exercise_id", wd.ExID);
Why is it so important that the parameters in WHERE clause has to be given the last in case of OleDb connection? Having worked with MySQL previously, I could (and usually do) write parameters of WHERE clause first because that's more logical to me.
Is parameter order important when querying database in general? Some performance concern or something?
Is there a specific order to be maintained in case of other databases like DB2, Sqlite etc?
Update: I got rid of ? and included proper names with and without #. The order is really important. In both cases only when WHERE clause parameters was mentioned last, actual update happened. To make matter worse, in complex queries, its hard to know ourselves which order is Access expecting, and in all situations where order is changed, the query doesnt do its intended duty with no warning/error!!
Within Access, an ADODB.Command object ignores parameter names. In fact I can refer to a parameter using a bogus name (which doesn't even exist in the SQL statement) and ADO doesn't care. All it seems to care about is that you supply parameter values in the exact same order as those parameters appear in the SQL statement. BTW, that is also what happens if I build the SQL statement with ? place-holders instead of named parameters.
While I realize that your question is about c# and OleDbCommand, it looks to me like Dot.Net's OleDbCommand may be operating the same as Access' ADODB.Command. Unfortunately, I don't know Dot.Net ... but that is my hunch. :-)
The order is important because of the use of ? placeholders in the command string.
If you want to list the parameters in any order, it's best to use named parameters, such as #word, #sentence, etc.
condb.Open();
OleDbCommand dbcom = new OleDbCommand("UPDATE Word SET word=#word,sentence=#sentence,mp3=#mp3 WHERE id=#id AND exercise_id=#exercise_id", condb);
dbcom.Parameters.AddWithValue("#id", wd.ID);
dbcom.Parameters.AddWithValue("#exercise_id", wd.ExID);
dbcom.Parameters.AddWithValue("#word", wd.Name);
dbcom.Parameters.AddWithValue("#sentence", wd.Sentence);
dbcom.Parameters.AddWithValue("#mp3", wd.Mp3);
I have been doing some tests with using OleDbCommand and its parameters collection against an Access DB. The ordering of parameters is of course necessary, since this is a limitation of the OLE DB .NET provider. But there is a problem that you can encounter when using question marks as place holders.
Say you have a query ("stored procedure") in your Access DB that looks like this, very simplified here:
parameters
prmFirstNumber Long,
prmSecondNumber Long;
select
fullName
from
tblPersons
where
numberOfCars < prmFirstNumber And
numberOfPets < prmSecondNumber And
numberOfBooks beteween prmFirstNumber And prmSecondNumber
Here you see that simply changing to question marks would break the query.
I have found though, as a solution to this, that you can actually use names for parameters. So you can let the query above remain as it is. You just have to use the same order when you run the query. Like in this case, you first add the parameter prmFirstNumber and then prmSecondNumber, and then you run the query.
When reusing parameters, i.e. executing a query more than once and setting new values for the parameters each time, one must call the prepare method of the command object right after having defined the parameters. There are some details there that need to be fulfilled too, look at the documentation for "prepare". Not calling prepare causes strange behaviour without error messages which can corrupt your database or cause wrong information to be presented to users.
I can add also that when queries are stored in the Access DB with parameters specified, like in my example above, then the ordering of the parameters is unambiguously defined by the parameters-section.
I also made a routine, "retrieveDeclaredJetParametersInOrder", which automatically populates an OleDbCommand object with those named parameters, in the correct order. So my code can look like this:
Dim cmd As New OleDbCommand("qryInAccessDB", Conn)
cmd.CommandType = CommandType.StoredProcedure
Conn.Open()
retrieveDeclaredJetParametersInOrder(cmd)
cmd.Parameters("prmOneOfTheParametersPerhapsTheLastOneDeclared").Value = 1
cmd.Parameters("prmAnotherone").Value = 20
cmd.Parameters("prmYetAnotherPerhapsTheFirstOneDeclared").Value = 300
cmd.ExecuteNonQuery()
Conn.Close()
So, as you see, I can handle it as if parameters are named, and never have to bother with their ordering.
The retrieveDeclaredJetParametersInOrder of course adds extra time to execution, since it involves an extra call to the DB, where it retrieves the SQL-text and then parses out the parameter names and types.
So there are a lot of questions regarding this method on SO, but none seem to answer my question. I firstly had an issue with the connectionstring (although it seems to work in other areas of my code correctly). This was resolved easily.
Now the issue is with a simple SELECT query via a OLEDBCommand (Text) that keeps popping up the following error?
"SELECT [Opportunity#],[BidManager],[Prob %],[Opportunity_Stage].[Opportunity_Status],[Term],[Sign Date] FROM [Sheet1$];"
No value given for one or more required parameters.
but their are no parameters????
Checked and double checked the columns names, but to no avail. Also tried removing the special characters from the column names, but still the same exception.
I'd suggest trying to run SELECT * FROM [Sheet1$] that way you'll know pretty certain if it's a column issue or an issue with the rest of the code.
Also, I can't check at the very moment, but are you sure there should be a ; after the select statement, isn't that just when you want to execute multiple statements?
OleDbCommand requires two parameters, the command string you are trying to execute and the OleDbConnection itself. Are you calling it correctly?
Dim myConnection As New OleDbConnection(myConnString)
OleDbCommand myCommand = new OleDbCommand(mySelectQuery,myConnection);
I've had this error with an OleDb command when my column names were incorrect.
"No value given for one or more required parameters."
I assume that the database interprets any columns names that it cannot find as parameters