Concatenated String Not Working in SQL - c#

C# ... this works
string sql = "SELECT * FROM STATEMENTS WHERE [idTrip] = '2015Q15'";
command.CommandText = sql;
But when I try to replace the '2015Q15' with a variable as follows, it does not work
string sql = "SELECT * FROM STATEMENTS WHERE [idTrip] = '" + myVariable + "'";
command.CommandText = sql;
When I run through line by line, I can see that the str sql looks fine but it does not select any records

Try this:
command.CommandText = "SELECT * FROM STATEMENTS WHERE [idTrip] = #idTrip";
command.Parameters.AddWithValue("#idTrip", myVariable);

Aside from the danger for SQL injection...
Do you have checked for leading or trailing white spaces in myVariable? Use .Trim() on myVariable to rule this out. I assume you have checked the content of myVariable to be correct otherwise?
If still no results are returned: Trace the SQL that is actually arriving at the server with the SQL Server profiler. Capture the command, execute it in SQL Server Management Studio to make sure it executes & yields the expected results.
Is your database configured to be case-sensitive? Could this be the reason? If the letter casing in your myVariable content is not exactly the same as in your table it could have this effect also.

Related

Syntax Error, but Copy/Pasted Query Text Works in SSMS

I am having an issue generating a SQL query using C#. To troubleshoot, I made the button that executes the query also display the query text in a textbox on the form. What's perplexing is that I get an error saying "Incorrect syntax near 'IF'" when the program tries to execute the query, but if I copy/paste the query from the textbox to SSMS it works fine.
The variable that stores the query looks like:
string myQuery = #"
SELECT DISTINCT filter.id_column INTO #temp1
FROM MasterDB.dbo.filter filter
LEFT JOIN ClientDB.dbo.codes codetable
ON filter.id_column=codetable.id_column
WHERE codetable.name IS NULL
DECLARE #code_id1 INT;
SET #code_id1 = (SELECT MAX(code_num) FROM ClientDB.dbo.codes)+1
EXEC('ALTER TABLE #temp1 ADD tempID INT IDENTITY(' + #code_id1 + ',1)')
GO
IF (SELECT COUNT(*) FROM #temp1)>0
BEGIN
DECLARE #code_id2 INT;
SET #code_id2 = (SELECT MAX(tempID) FROM #temp1)+1
UPDATE ClientDB.dbo.track
SET next=#code_id2 WHERE [trackname]='account'
END";
The C# code to populate the textbox with the query text and then run the query looks like:
using (SqlConnection myConnection = new SqlConnection(HostConnStr))
using (SqlCommand myCommand = myConnection.CreateCommand())
{
myCommand.CommandText = myQuery;
this.textBox1.Text = myCommand.CommandText;
myConnection.Open();
try { myCommand.ExecuteNonQuery(); }
catch (SqlException s) { MessageBox.Show(s.ToString()); }
myConnection.Close();
}
Does anyone know why the query text can be copied to SSMS and run fine, but throws a SQL exception when executed from C#? And how do I make the query run?
Critique on the query design will be appreciated, but I am more concerned with simply getting the query to execute since it does what I need it to do as-is.
EDIT: This may be a duplicate (I was thrown off by the error being near 'IF' when it appears that 'GO' is the problem, so my searches were in the wrong direction. However, I am still not sure that the answers provided in similar questions will work since I am under the impression that splitting the query into multiple commands will fail due to the later part of the query referencing a temporary table in the earlier part (will the temporary table not become unavailable after the first command is finished?).
It's the GO statement. You can replace it with ; in most instances.
In TSQL it's OK to have multiple statements separated by GO. In the ADO.NET version you can't do this.
The way to do this would be spilt the string on the GO and execute each independently. Such as this example,
string scriptText = #"...."
//split the script on "GO" commands
string[] splitter = new string[] { "\r\nGO\r\n" };
string[] commandTexts = scriptText.Split(splitter, StringSplitOptions.RemoveEmptyEntries);
foreach (string commandText in commandTexts)
{
//execute commandText
}

Debugging a connection between VS in C# and SQL Server

Below is a piece of code I have been working on for the past couple of days:
SqlConnection connectMOBILE = new SqlConnection("Server=OMADB03;Database=MOBILE;Trusted_Connection=True;");
string masterErrorString;
connectMOBILE.Open();
string stringIncorrectPassword = string.Concat(
"SELECT SERVICE_ID, RESPONSE_DATA, DATE_ENTERED",
"FROM WS_TRANSACTION",
"WHERE SERVICE_ID = 'GETUSERTOKENLOGIN'");
SqlCommand commandIncorrectPassword = connectMOBILE.CreateCommand();
commandIncorrectPassword.CommandText = stringIncorrectPassword;
SqlDataReader reader = commandIncorrectPassword.ExecuteReader();
while (reader.Read())
{
masterErrorString = reader.ToString();
BOAssistant.WriteLine(masterErrorString);
}
This code is using a class called BOAssistant that works like Console.WriteLine but instead writes to a log file.
What this code should be doing is collecting the results from my query and placing them in my log file, but when I run this program I get the following error message:
System.Data.SqlClient.SqlException (0x80131904): Incorrect syntax near 'SERVICE_ID'.
There are about 20+ more lines but this is the one that stands out the most. This is my first time writing a program that connects Visual Studio and SQL Server so I am wondering if it is something wrong in the code or I am missing something in my code to establish a stronger connection? What is in the code now is a result of research I have done on the internet. Also when I run the query in SQL Server it works so I know the syntax for query is correct.
When you combine your string you get incorrect SQL, because spaces are missing. string.Concat creates you this query:
SELECT SERVICE_ID, RESPONSE_DATA, DATE_ENTEREDFROM WS_TRANSACTIONWHERE SERVICE_ID = 'GETUSERTOKENLOGIN'
which obviously has some missing spaces.
Instead, update your query with spaces:
string stringIncorrectPassword = string.Concat(
"SELECT SERVICE_ID, RESPONSE_DATA, DATE_ENTERED ", // added space
"FROM WS_TRANSACTION ", // added space
"WHERE SERVICE_ID = 'GETUSERTOKENLOGIN'");

How can I see the SQL sent to the database after the parameters have replaced their placeholders?

The first MessageBox.Show() below simply shows me the exact same thing as const string SQL_GET_VENDOR_ITEMS, which seems fine to me, but I'm getting, "There was an error parsing the query. [Token line number, Token line offset,, Token in error,,]"
Is there a way to spy on the contents of the SQL after parameters have been added; it should then be something like: "SELECT ItemID, PackSize FROM VendorItems WHERE VendorID = 'TEST' AND VendorItemID = '852963'
Here's the pertinent code:
const string SQL_GET_VENDOR_ITEMS = "SELECT ItemID, PackSize " +
"FROM VendorItems " +
"WHERE VendorID = #VendorID AND VendorItemID = #VendorItemID";
string retVal = string.Empty;
checkConnection();
SqlCeCommand vendorCMD = objCon.CreateCommand();
try
{
vendorCMD.CommandText = SQL_GET_VENDOR_ITEMS;
vendorCMD.Parameters.Add("#VendorID", SqlDbType.NVarChar, 10).Value = VendorID;
vendorCMD.Parameters.Add("#VendorItemID", SqlDbType.NVarChar, 19).Value = VendorItemID;
MessageBox.Show(string.Format("Made it up to vendorCMD.ExecuteReader() with sql {0}", vendorCMD.CommandText));
. . .
vendorReader.Close();
}
catch (SqlCeException sqlceex)
{
MessageBox.Show(string.Format("SqlCeException in GetValsForVendorAndItem == {0}", sqlceex.Message));//TODO: Remove
}
finally
{
vendorCMD.Dispose();
}
return retVal;
. . .
but I can almost guarantee that won't work in my VS2003/.NET 1.0 world
ahhh... version - see MSDN:
The .NET Compact Framework data provider for SQL Server CE does not support named parameters for passing parameters to an SQL statement called by a SqlCeCommand when CommandType is set to Text. You must use the question mark (?) placeholder. For example: SELECT * FROM Customers WHERE CustomerID = ?
Since you are on CE, your options are limited, but there are some suggestions for how to peek into the database: Profiler for Sql CE
If you were on normal SQL Server, you might consider using SQL Profiler. You'd be able to see what is getting executed against the database.

how to prevent an SQL Injection Attack?

Currently, I am creating an SQL Query by doing something like
string SQLQuery = "SELECT * FROM table WHERE ";
foreach(word in allTheseWords)
{
SQLQuery = SQLQuery + " column1 = '" + word + "' AND";
}
I understand that this can lead to an SQL Injection attack. I don't know how to pass an array as a parameter
where report in #allTheseWords
===========
I am using SQL Server 2012
Unfortunately, you cannot pass an array as a parameter without adding a user-defined type for table-valued parameters. The simplest way around this restriction is to create individually named parameters for each element of the array in a loop, and then bind the values to each of these elements:
string SQLQuery = "SELECT * FROM table WHERE column1 in (";
for(int i = 0 ; i != words.Count ; i++) {
if (i != 0) SQLQuery += ",";
SQLQuery += "#word"+i;
}
...
for(int i = 0 ; i != words.Count ; i++) {
command.Parameters.Add("#word"+i, DbType.String).Value = words[i];
}
You can also create a temporary table, insert individual words in it, and then do a query that inner-joins with the temp table of words.
Here is the recommendation from Microsoft:
Use Code Analysis to detect areas in your Visual Studio projects that are prone to sql injection;
Refer to the article on how to reduce risk of attack:
On short they talk about:
using a stored procedure.
using a parameterized command string.
validating the user input for both type and content before you build the command string.
Btw, you can enable static analysis as part of your build process and configure it so that when a security rule is broken, the build also breaks. Great way to make sure your team writes secure code!
Using ADO you can do it with the help of params
SqlConnection Con = new SqlConnection(conString);
SqlCommand Com = new SqlCommand();
string SQLQuery = "SELECT * FROM table WHERE ";
int i=1;
foreach(word in words)
{
Com.Parameters.Add("#word"+i.ToString(),SqlDbType.Text).Value = word;
SQLQuery = SQLQuery + " column1 = '#word"+i.ToString()+"' AND ";
i++;
}
Com.CommandText =SQLQuery;
For SQL Server, you'd use a Table-Valued Parameter. SQL has one structure that represents a collection of multiple items of the same type. It's called a table. It doesn't have arrays.
Of course, your supposed updated query:
where report in #allTheseWords
Isn't equivalent to your original query, but may be closer to the intent. In the query constructed using AND, you're saying that the same column, in the same row has to be equal to multiple different words. Unless all of the words are equal, this will never return any rows. The updated query answers whether any of the words match, rather than all.
You need to use prepared statements. The way those are handled is that you write your query and put placeholders for the values you want to use. Here's an example:
SELECT * FROM table WHERE column1 = #word
You then have to go through a prepare phase where the SQL engine knows it will need to bind parameters to the query. You can then execute the query. The SQL engine should know when and how to interpret the parameters you bind to your query.
Here's some code to do that:
SqlCommand command = new SqlCommand(null, rConn);
// Create and prepare an SQL statement.
command.CommandText = "SELECT * FROM table WHERE column1 = #word";
command.Parameters.Add ("#word", word);
command.Prepare();
command.ExecuteNonQuery();
I combine the use of params with HtmlEncoding(to get rid of special characters where not needed). Give that a shot.
using (SqlConnection conn = new SqlConnection(conString))
{
string sql = "SELECT * FROM table WHERE id = #id";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.paramaters.AddWithValue("#id", System.Net.WebUtility.HtmlEncode(id));
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
}
}
}

Save executed Sql inside table

I am trying to log down sql that execute.
I have a function call LogGenerateReport(String Sql) that will do a insert process to save the data in a database.
The Problem i face is about the SQl ''.
For example:
INSERT INTO TABLE(Sql)
VALUE('SELECT * FROM Sales WHERE SalesID = 'ABC123';')
Its return me error and i know what happened because of the quote.
I try again inside my database where i open a new query and paste above sql and made some modification on it such as.
INSERT INTO TABLE(Sql)
VALUE('SELECT * FROM Sales WHERE SalesID = ''' + 'ABC123' + ''';')
Its return me expected result.
Output:
|Sql |
|SELECT * FROM Sales WHERE SalesID = 'ABC123';|
But back on my .aspx.cs page i have a string builder that store the executed query and before it executed, it need to save the query first.
For example:
System.Text.StringBuilder str = new System.Text.StringBuilder();
str.append("SELECT * FROM Sales WHERE SalesID = 'ABC123';");
api.LogGenerateReport(Convert.tostring(str));
Its return me error as like above because of the quote.
I try to figure it out to overcome this and my idea is
String TempSql = Convert.tostring(str);
TempSql.Replace("'","+'''");
I wont work because of the + symbol is at different position.
Is there any way to overcome this?
To succesfully log any and all SQL queries regardless of their content, you need to apply parameterized commands in the following way:
using(var command = new SqlCommand(someSqlConnection))
{
command.CommandText = "INSERT INTO TABLE(Sql) VALUE(#Sql)";
command.Parameters.AddWithValue("#Sql", "<any string>");
command.ExecuteNonQuery();
}
That way you can avoid escaping anything with NON-STANDARD methods, and protect your code from SQL injection attacks.
cmd.CommandType = CommandType.Text;
cmd.CommandText = #"SELECT * FROM Sales WHERE SalesID = #Param";
cmd.Parameters.Add("#Param", SqlDbType.Varchar).Value = 'ABC';
Try this out and using paramterised one is highly recommended and to some extent handling sql injection as well as these sorts of problems
Why can't you just replace all single quotes within the statement with double single qoutes?
This request works fine:
INSERT INTO TABLE(Sql)
VALUE('SELECT * FROM Sales WHERE SalesID = ''ABC123'';')
And the code:
String TempSql = Convert.tostring(str);
TempSql.Replace("'","''");
My guess is your api.LogGenerateReport isn't escaping single quotes when inserting the row into the database. You should fix your insert statement using paramaters.
To get it to "work" as is, you need to escape your single quotes with two single quotes. Try changing:
"SELECT * FROM Sales WHERE SalesID = 'ABC123';"
to
"SELECT * FROM Sales WHERE SalesID = ''ABC123'';" <-- these are 2 single quotes, no double quotes
Good luck.

Categories