I'm getting the error "syntax error (missing operator) in query expression" using this code:
x = "John's";
OleDbCommand commandRdd = new OleDbCommand("Select rdd_tbl.Customer, rdd_tbl.Balance From rdd_tbl WHERE rdd_tbl.Customer ='" + x + "'", con);
OleDbDataReader readerRdd = commandRdd.ExecuteReader();
I know the problem in John >'< s symbol, but i don't know how to make it a part of the name .
You really never want to do it by replacing any special character, while it may work with simple cases there can be other reserved characters that may require handling. Also, the escape sequences are DB specific, so different engines may need different treatments (I assume the multi-DB thing just because you used the generic OleDb classes instead of a more specific family).
Escaping special chars is a good solution for scripts and quick and dirty code, but never do that in a real life program, as there is a better option.
The real solution are parameters. You just send the query text with a parameter placeholder instead of the real value and then supply the value afterwards, before running. A quick implementation could be something like this:
x = "John's";
OleDbCommand commandRdd = new OleDbCommand("Select rdd_tbl.Customer, rdd_tbl.Balance From rdd_tbl WHERE rdd_tbl.Customer = #customer", con);
commandRdd.Parameters.Add(new OleDbParameter("#customer", OleDbType.VarChar, 100, ParameterDirection.Input, false, 0, 0, null, DataRowVersion.Default, x));
OleDbDataReader readerRdd = commandRdd.ExecuteReader();
A few things to notice. First, the query text contains rdd_tbl.Customer = #customer, a fixed text independent of the actual value you want to submit. then it comes the parameter declaration, where the name, data type and actual value are specified. Be sure to fill all those details and match them to the underlying DB structure. The rest of the code is exactly the same.
This also has the added benefit of improving performance. Because of the query text being exactly the same, the query can often be cached and reused afterwards if submitted again, even if the parameter value changes (those values are not part of the query itself). If you need to run the same query a few times in a row with different values, this makes possible to use prepared statements, which again are precompiled queries that run subsequently with different values. Those possible optimizations are all allowed by the API, but the actual results will vary depending the DB engine.
Escape ' with a \. Like so.
x = "John\'s";
OleDbCommand commandRdd = new OleDbCommand("Select rdd_tbl.Customer, rdd_tbl.Balance From rdd_tbl WHERE rdd_tbl.Customer ='" + x + "'", con);
OleDbDataReader readerRdd = commandRdd.ExecuteReader();
Also, use SQL Parameters like #Plutonix has suggested, else you are vulnerable to SQL Injection / "Bobby Tables"
Related
I have seen a few questions asked about this kind of thing already, but whenever I try anything it just doesn't work with this exact instance. I have a remote SQL SERVER database set up, and it has over 2000 rows.
This is my C#:
command.Connection = connectionString;
command.CommandType = CommandType.Text;
command.CommandText = "SELECT IconUrl FROM Items WHERE DefIndex IN " + iconSqlString;
connection.Open();
string iconString = command.ExecuteScalar().ToString();
connection.Close();
I do have the SqlConnection set up and all that, I just don't think it is necessary to be included.
iconSqlString is in the format of (32, 126, 68). The string has over 150 items and is different each time. I need to return the IconUrl for all items in the string. The current query for some reason only returns on IconUrl, for the item with the value of 35 in the 51st position (out of 196). I know that the iconString is in the format of a string, but it is only for testing purposes, I was expecting it to have returned all the results, separated by commas or something else. I wish to return all the values possible to an array, to be called later with something like sqlItem[5]. How would I do this?
ExecuteScalar is for single result, you will need to use ExecuteReader instead.
Also as a side note, you should never use variables to perform a query because of an attack called SQLI or SQL Injection. Here is a link with some examples on how to execute parameterized queries.
I was reminding myself of the syntax for parameterized queries (C# OleDB library), and the first couple of examples I stumbled across were straight-forward, but I noticed something about the syntax:
sqlCommand.CommandText = "SELECT * FROM Customer WHERE Name LIKE #Name;";
sqlCommand.Parameters.AddWithValue("#Name", "%" + searchString + "%");
What I noticed was the SQL format for identifying what will be replaced with the parameters was different from what I have been typically using. I typically use a colon and square brackets:
sqlCommand.CommandText = "SELECT * FROM Customer WHERE [Name] LIKE :Name;";
sqlCommand.Parameters.AddWithValue(":Name", "%" + searchString + "%");
Now, the two examples I was looking at with the '#' were from sql-server examples, where I normally only work with Access and Oracle. But I saw other access examples using the '#' as well.
Does it really make any difference, or is it just a style thing? I recall that (using access at least) the engine ignores the parameter names and just inserts the parameters in the order that they are listed.
I note from the MSDN that:
The OLE DB.NET Framework Data Provider uses positional parameters that are marked with a question mark (?) instead of named parameters.
This reinforces my view that the parameter name / syntax doesn't matter and is ignored. However, even if this is correct, is there a good habit to get into that provides more robustness for other frameworks etc?
Obviously parameterizing queries is important to help prevent you from sql-injection. Different databases use different constructs to use parameters. For example SQL-Server uses the "#" as a place-holder for a parameter but DOES allow for parameters to be named... likewise, others may use ":" also as a named-place-holder. However, with OleDB, it uses a single "?" as the place-holder and is NOT named. Therefore, you must add your parameters in exact order as they represent in your query command.
Also, I have found instances when using NAMED parameters, that if you have a column and parameter by the same, it might not work as intended as the database might resolve itself by the column -- which is not the intended purpose. Rename the parameter with some prefix (or suffix) to help clarify. Such example might be..
sqlCommand.CommandText = "update MyCustomerTable set email = #email where customerID = #customerID";
sqlCommand.Parameters.AddWithValue("email", "someValue#anywhere.com");
sqlCommand.Parameters.AddWithValue("customerID", someIDValue );
Also note.. you don't actually include the "#" or ":" for named parameters. The engines will know how to handle them.
all looks good, but to prevent ambiguity if the "parameter" is not found and it falls back on the column name, try..
sqlCommand.CommandText = "update MyCustomerTable set email = #parmEmail where customerID = #parmCustomerID";
sqlCommand.Parameters.AddWithValue("parmEmail", "someValue#anywhere.com");
sqlCommand.Parameters.AddWithValue("parmCustomerID", someIDValue );
This way there is no confusion.
Now, back to the "?" place-holder. If you have a single parameter value that is applied multiple times, you need to add the parameter for each instance. If named parameters are allowed, you might get away with..
sqlCommand.CommandText =
#"update SomeTable
set Rate1 = Rate1 * #newRateFactor,
Rate2 = Rate2 * #newRateFactor";
sqlCommand.Parameters.AddWithValue("newRateFactor", 1.15);
Notice only a single named-parameter is required... but with the "?", you have to add it EACH TIME
sqlCommand.CommandText =
#"update SomeTable
set Rate1 = Rate1 * ?,
Rate2 = Rate2 * ?";
sqlCommand.Parameters.AddWithValue("ratePlaceHolder1", 1.15);
sqlCommand.Parameters.AddWithValue("ratePlaceHolder2", 1.15);
Doing things like sql inserts and updates can also get tricky when you have a bunch of parameters for all column names. You can still give a parameter NAME value, but it must be in the same ordinal position within the query for execution.
This reinforces my view that the parameter name / syntax doesn't matter and is ignored.
That is certainly not true. The parameter prefix you are using does matter, but ODBC is a little more relax on that since it has to support a lot of platforms. Never ever assume though you can just throw in any garbage, because it will bite you.
You indeed have to use the ? for parameters in OLE DB. The names you give when supplying the parameters are ignored, the order is all that matters there.
I was looking up how to insert into my database via sql and I noticed the way I had seen a person do an sql statement was different from the way I had done it and and now I'm wondering which way is better.
An example of what I had done in a previous (select) statement.
SqlConnection conn = new SqlConnection(Variables.Default.sqlConString);
conn.Open();
string builtCmd = Variables.Default.returnUserNameSql1 + usersInput + Variables.Default.returnUsernameSql2;
SqlCommand cmd = new SqlCommand(builtCmd, conn);
usersInput is a string.
Variables.Default.returnUserNameSql1 = SELECT [Username] from [dbo].[LoginDetails] WHERE [Username] = '
returnUsernameSql2 = '
What I have seen online (not my query):
cmd.CommandText = "INSERT INTO klant(klant_id,naam,voornaam) VALUES(#param1,#param2,#param3)";
cmd.Parameters.Add(new SqlParameter("#param1", klantId));
cmd.Parameters.Add(new SqlParameter("#param2", klantNaam));
cmd.Parameters.Add(new SqlParameter("#param3", klantVoornaam));
Is the use of the Parameters function (?) better? If so in what way?
Thanks for your time.
I modified my original query thanks to the help of some of the comments here. I'll post it if anyone's interested:
using (SqlConnection conn = new SqlConnection(Variables.Default.sqlConString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(Variables.Default.returnUserNameSql, conn))
{
cmd.Parameters.Add(new SqlParameter(Variables.Default.param1, usersInput));
SqlDataReader reader = cmd.ExecuteReader();
usernameTaken = reader.Read();
cmd.Dispose();
}
conn.Close();
}
Look up "SQL Injection attack" on google. Bobby Tables says hello. And then realize that your way is not bad, it is a security nightmare because everyone with access to your program can execute whatever SQL he wants.
SQL Injection is certainly an important reason not to use string concatenation, but there are a few others:
string delimiters - you'd need to include string delimiters in your SQL statement, and if the values you concatenate include them as well, you'll likely get syntax errors. With parameters you don't need string delimiters, and values with apostro[phes or quotes don't affect the SQL syntax.
string conversion of values - you'd need to convert all non-string values (numbers, dates, etc.) to strings, and ensure that their string represenatations are exactly reversible by the server. This is especially problematic for dates since the same string can represent two different dates depending on the culture of the server. With parameters, the values are passed without translation, so there's no risk that the server misinterprets values.
pre-compilation - with concatenation, the server must reanalyze each query to determine the "best" plan. With parameters, the server can re-use a cached plan since the actual query has been issued before, just with different parameters. This doesn't mean that queries will always perform faster, and in some cases can actually cause bad plans to be used, but it is a consideration if you issue millions of queries that only differ in parameter values.
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
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.