I have a serious problem of formatting queries, it may result in SQL injection too, I saw some similar qstns, but not sure how could i use it in C# as I am new to it. I use c#,Odbc command
I have 3 strings like
qry ="select description from TableA" , qryOrder = " order by description" , qryAppend = " where ID = '{0}' order by description\", _selectedPlantID" provided _selectedId is another variable, Now I want to use these variables to form diff queries at different scenarios, for eg, qry + qry order , or qry + qryAppend.
Since _selectedPlantId is also needed, I use string.Format as :
_cmd.CommandText = "string.Format(\"" + qry + qryAppend + ")";
But its not working. any solution ?
Error is SQL syntax error with quotes
thanks in advance !!
Simply put, this should make it work. You'll need two variables (bool), I'll explain later why:
var shouldOrder = true;
var shouldAppend = true;
_cmd.CommandText = String.Format(
"{0} {1} {2}",
qry,
shouldOrder ? qryOrder : String.Empty,
shouldAppend ? qryAppend : String.Empty
);
These two variables (shouldOrder and shouldAppend) will help you with the "diff queries at different scenarios" as you've said.
Providing these variables with true or false will change what text goes into the String.Format and will change query accordingly.
So, if you use shouldOrder = false; the query command won't get the order part. Setting shouldAppend = false; will avoid including the extra part (append) into the SQL command.
Now, be careful!
This won't solve your SQL injection problem. I've just shown a quick fix.
To avoid SQL injections, you'll have to change your SQL command and you cannot use String.Format anymore.
To understand how to do that, take a look into DGibbs comment.
Related
I am attempting to convert query below which executes the update statement based on the boolean value. i am adding the warehouse based on an if statement, but i am not quiet sure how to write this with StringBuilder. I am quiet new to C#.
string query = value ? "UPDATE Warehouse SET usen = 'T' WHERE warehouse='01'" : "UPDATE Warehouse SET use = 'F' WHERE warehouse='01'";
I attempted the following:
StringBuilder query = new StringBuilder();
query.Append(value ? "UPDATE Warehouse SET usen = 'T' WHERE warehouse='");
if (warehouse.Equals("T"))
query.Append(TWarehouse + "'");
else if (warehouse.Equals("V"))
query.Append(VWarehouse + "'");
query.Append(: "UPDATE Warehouse SET usen = 'F' WHERE warehouse);
This did not work. Clearly I am doing something wrong. Can anyone help me figure this out.
There are better ways than constructing a query in a string and executing it against the database.
As #AlexeiLevenkov suggest, you should use parameterized queries. See How to Execute a Parameterized Query.
Also the StringBuilder is recommended for long strings and more complex situations.
From StringBuilder documentation:
"Although the StringBuilder class generally offers better performance
than the String class, you should not automatically replace String
with StringBuilder whenever you want to manipulate strings.
Performance depends on the size of the string, the amount of memory to
be allocated for the new string, the system on which your app is
executing, and the type of operation. You should be prepared to test
your app to determine whether StringBuilder actually offers a
significant performance improvement. "
In your case you can use string.Format and have a more readable code, like:
string format = "UPDATE Warehouse SET use = '{0}' WHERE warehouse='01'";
string query = string.Format(format, value ? "T" : "F");
query.Append(value ? "UPDATE Warehouse SET usen = 'T' WHERE warehouse='");
This isn't valid C#.
https://msdn.microsoft.com/en-us/library/ty67wk28.aspx
I'd recommend reading up on the conditional operator. Look at the first line in your question (I've reformatted it a bit)
string query = value
? "UPDATE Warehouse SET usen = 'T' WHERE warehouse='01'"
: "UPDATE Warehouse SET use = 'F' WHERE warehouse='01'";
What this does is conditionally assign a value to the query variable. It'll assign "UPDATE Warehouse SET usen = 'T' WHERE warehouse='01'" if value == true, or it'll assign "UPDATE Warehouse SET use = 'F' WHERE warehouse='01'" if value == false.
Is this what you try to do?
StringBuilder query = new StringBuilder();
query.Append("UPDATE Warehouse SET usen = '");
query.Append(value? "T" : "F");
query.Append("' WHERE warehouse='");
if(warehouse.Equals("T"))
{
query.Append(TWarehouse);
}
else if(warehouse.Equals("V"))
{
query.Append(VWarehouse);
}
else
{
query.Append("YourDefaultValue");
}
query.Append("'");
As Carlos said, your way of writing the code is wrong.
I m working on a project in which I have to use Access Database out of all queries it keeps showing up this Exception
System.Data.OleDb.OleDbException was unhandled
Message = Syntax error in UPDATE statement.
Source = Microsoft Office Access Database Engine
ErrorCode= - 2147217900
In the following query
public static string updateDailyBalance = "UPDATE DailyBalance SET [{0}] = {1} WHERE [CustomerID] = {2} & [PurchaseMonth] = {3}";
and in the following source code
for (int i = 0; i < dsCustomerBal.Tables[0].Rows.Count; i++)
{
//{0} = Day, {1} = BalancePoints, {2} = CustomerID, {3} = yyyyMM
string strQ = Constants.updateDailyBalance;
strQ = strQ.Replace("{0}", l.ToString());
strQ = strQ.Replace("{1}", dsCustomerBal.Tables[0].Rows[i]["Bal"].ToString());
strQ = strQ.Replace("{2}", dsCustomerBal.Tables[0].Rows[i]["CustomerID"].ToString());
strQ = strQ.Replace("{3}", _LastUpdatedDate.ToString("yyyyMM"));
Database db1 = DatabaseFactory.CreateDatabase("Deltin");
DbCommand dbComm1 = db1.GetSqlStringCommand(strQ);
dbComm1.CommandTimeout = 0;
int j = db.ExecuteNonQuery(dbComm1);
}
in the line
int j = db.ExecuteNonQuery(dbComm1);
There might be other things can be wrong but first thing I see, there is no & operator in WHERE clause.
I think you need to use it like;
WHERE [CustomerID] = {2} AND [PurchaseMonth] = {3}
But more important, instead of string replacing and concatenation, you should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
Define your column values (not names) as a parameter, add them in your for loop as a OleDbParameterCollection, execute your query and clear your parameters with OleDbParameterCollection.Clear() before next iteration.
As always when doing this type of games - check the full output. The query you send is not the query you have in your code an dcopy/pasting that would really show you the error.
THere is no & in SQL. Which results in a syntax error.
Also learn about .NET base classes. Besides parameters being the answer to avoid SQL injection - your string manipulation is ignorant towards String.Format which would allow you to replace all the replace commands with ONE run. Funny enough as you already use the string.format syntax in your sql string.
That said: This is an invitation to abuse your database due to gross neglect. Read up on SQL Injections and use parameters.
Hello everyone I solved my question it had to do with null entries in the table that is why update was giving syntax error .Thanks for the tip on SQL injection .Cheers!
This question already has answers here:
Parameterize an SQL IN clause
(41 answers)
Closed 9 years ago.
In my application written in C# , I am writing a SQL query. Following is the query
SELECT [Resource No_] where [Resource No_] In (#resources)
#resources is user input parameters having one or more that one strings.
My query is failing without showing an error
According to me, query is failing because in #resources parameter following is being passed
"'123,'124','125'"
(there are 2 inverted commas in the beginning and at the end and that is failing my query).
[Resource No_] is of type NVARCHAR in the database.
After Googling, I have found some help on this topic but all are applicable when [Resource No_] is of type Integer
While I don't agree with the selected answer (or many of the tricky answers) for the "duplicate question", here is an answer to it which shows an approach very similar with my following recommendation.
(I've voted to close this question as a duplicate, because there are such answers, even if buried.)
Only one SQL value can be bound to any given placeholder.
While there ways to send all the data as "one value", I'd recommend creating the placeholders dynamically: it's simple, clean, and will work reliably in most cases.
Consider this:
ICollection<string> resources = GetResources();
if (!resources.Any()) {
// "[Resource No_] IN ()" doesn't make sense
throw new Exception("Whoops, have to use different query!");
}
// If there is 1 resource, the result would be "#res0" ..
// If there were 3 resources, the result would be "#res0,#res1,#res2" .. etc
var resourceParams = string.Join(",",
resources.Select((r, i) => "#res" + i));
// This is NOT vulnerable to classic SQL Injection because resourceParams
// does NOT contain user data; only the parameter names.
// However, a large number of items in resources could result in degenerate
// or "too many parameter" queries so limit guards should be used.
var sql = string.Format("SELECT [Resource No_] where [Resource No_] In ({0})",
resourceParams);
var cmd = conn.CreateCommand();
cmd.CommandText = sql;
// Assign values to placeholders, using the same naming scheme.
// Parameters prevent SQL Injection (accidental or malicious).
int i = 0;
foreach (var r in resources) {
cmd.Parameters.AddWithValue("#res" + i, r);
i++;
}
Use a user defined table type to accept your parameter, then a JOIN clause in your select to limit the results set. See http://social.msdn.microsoft.com/Forums/en-US/2f466c93-43cd-436d-8a7c-e458feae71a0/how-to-use-user-defined-table-types
Do something like
resources.Aggregate(r1, r2 => r1 + "', '" + r2 + "'")
and pass the list in one string.
Suppose that I want to create an SQL SELECT statement dynamically with reflection on primary key. I search in the table for primary keys and then, I make the statement.
Problem is, I don't know the type of fields that compose the primary key before getting them. So, if it's a string or date, I must add quotation marks but not if it's an int.
Atm, I am doing like that :
var type = field.GetType().Name;
if (type.ToLower().StartsWith("string") || type.ToLower().StartsWith("date"))
{
field = "\"" + field + "\"";
} else if (type.ToLower().StartsWith("char"))
{
field = "\'" + field + "\'";
}
With this code, I can handle some SQL types but there are a lot more.
My problem is that it's combined with LinQ. I got a DataContext object and a generic type table from the context. And context.ExecuteQuery only allows parameters to be passed has values. I also tried with Dynamic LinQ but I got the same problem
Does anyone know a better solution?
That is simply the wrong way to write SQL. Parameterize it and all these problems evaporate (as do problems with "which date format to use", etc. And of course the biggie: SQL injection.
Then it just becomes a case of adding #whatever into the TSQL, and using cmd.Parameters.AddWithValue("whatever", field) (or similar).
Update (from comments): since you mention you are using DataContext.ExecuteQuery, this becomes easier: that method is fully parameterized using the string.Format convention, i.e.
object field = ...;
var obj = db.ExecuteQuery<SomeType>(
"select * from SomeTable where Id = {0}", field).ToList(); // or Single etc
No string conversions necessary.
(the last parameter is a params object[], so you can either pass multiple discreet terms, or you can populate an object[] and pass that, if the number of terms is not fixed at compile-time; each term in the array maps by (zero-based) index to the {0}, {1}, {2}... etc token in the query)
Have you tried with parameters? For instance if you are using SQLServer as a database and you want to do this query:
"SELECT * FROM someTable WHERE id = " + field;
Then you should use sometething like this:
"SELECT * FROM someTable WHERE id = #field"
and add parameter to your command:
SqlParameter param1 = new SqlParameter("#field", field);
command.Parameters.Add(param1);
EDIT: Watch out that for different database providers the syntax for the SQL query is different, the same for the Access would be
"SELECT * FROM someTable WHERE id = ?";
command.Parameters.AddWithValue("field", field);
How to make this code properly? I am not satisfied with this code, I'm lost.
I give you a simple example but the query is more complexe.
Thanks in advance.
string aValue;
string queryA;
string queryB;
string finalQuery;
string queryA = #"SELECT column1 FROM table1 WHERE column1=";
queryA += aValue;
string queryB = #"SELECT column1, column2,"
if (aValue == "all"){
queryB += #"column3";
}
queryB += #"FROM table1 WHERE column1=";
queryB += #"'" +aValue+ "'";
private void exportExcel(){
// change the value with a dropdownlist
if (ddlType.selectedIndex(1))
aValue = "typeA";
else if(ddlType.selectedIndex(2))
aValue = "typeB";
else
aValue = "all";
// select the query
if (aValue == "typeA")
finalQuery = queryA;
else if (aValue == "typeB")
finalQuery = queryB;
ExecQUery(finalQuery);
}
In both Java and C# (and pretty much any other platform) you should definitely not include the values directly in the SQL. That's opening up the way to SQL injection attacks, and also makes dealing with formatting for dates, times and numbers tricky.
Instead, you should use parameterized SQL, specifying the values in the parameters. How you do that varies between Java and C#, but the principle is the same.
Another approach on both platforms is to use an ORM of some description rather than building queries by hand. For example, in .NET you might want to use a LINQ provider of some description, and in Java you might want to use something like Hibernate. Either way you get to express your queries at a higher level of abstraction than just the raw SQL.
It's hard to give much more concrete advice without knowing what platform you're really using (or database) and without a real query to look at.
One small change you can do is set value attribute of dropdownlist to typeA,TypeB, etc.. and get rid of the initial if conditions and variables.
eg:
if(ddlType.selectedValue.toString()=="typeA")
finalQuery = queryA;
if(ddlType.selectedValue.toString()=="typeB")
finalQuery = queryB;
I usually load it from a resource file. This gives you some freedom to change the queries (this in case you don't need to generate it dynamically with if blocks). In source code I use formatting ending my line with a comment line in order to avoid my IDE to concatenate or put it all in one like like:
String sql = "select " + //
" * " + //
"from "+ //
" employee " + //
"where " + //
" salary > :minSal " + //
" and startDate > :minStartDate";
And in case of conditional part I just add it with a if block. But for where statements I just add one default "1=1" in order to proceed with additional limitations, so if there is no additional limitations the query will still be valid. Suppose both where statements in the SQL bellow were added conditionally:
String sql = "select " + //
" * " + //
"from "+ //
" employee " + //
"where 1 = 1 ";
Until here you have your base SQL, valid, that means if not condition is added it will still be valid.
Suppose you will add salary limitation just in case if it is informed:
if (salary != null) {
sql += "and salary > :minSalary";
parameters.put("minSalary", salary);
}
As you can see in the same condition I add a new expression to my SQL and a parameter to a map that will be used later in execute to set the parameters to the query, that avoids you to create a second if statement just to set this parameter.
Another approach that you could take is build the entire SQL and before the execution ask for the prepared statement which parameters it needs as input and provide them. In java you can do it with:
http://download.oracle.com/javase/1.4.2/docs/api/java/sql/PreparedStatement.html#getParameterMetaData%28%29
I know that is not the case but if ORM is used, is common to have Builders for queries and this turns this task much easier, for example in Hibernate you could have something like:
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50)
.list();
As it is documented at:
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/querycriteria.html
That means you could do this:
Criteria c = sess.createCriteria(Cat.class)
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50);
if (name != null) {
c.add( Restrictions.like("name", name);
}
List cats = c.list();