Profile ADO.NET statements without access to SQL Server Profiler - c#

Consider a block of code like this:
using (SqlConnection c = new SqlConnection("{connection string}"))
{
c.Open();
using (SqlCommand cmd = new SqlCommand("INSERT INTO Table (Field1, Field2) VALUES (#Field1, #Field2)", c))
{
cmd.Parameters.AddWithValue("#Field1", "some value");
cmd.Parameters.AddWithValue("#Field2", 10);
cmd.ExecuteNonQuery();
}
}
I would like to be able to see the actual statement sent to the server, but the kicker is I don't have access to SQL Server Profiler via our Network/SQL administrators. Is there any way I can get at the actual statement submitted to the server?

There are a range of tools that do exactly this. If you are in asp.net, you may find MiniProfiler a good start (you can use it on other platforms, but the UI tooling is stronger on asp.net). The main change involved here would be to move from SqlCommand etc to use c.CreateCommand, since it works by wrapping the connection (decorator pattern) - while it is still a DbConnection, the outermost object is not a SqlConnecion any more. I have to say, though - in this particular example you already know the exact command sent to the server. It is useful for finding the surprises, though. If that doesn't suit, "Hibernating Rhinos" offer a range of profiling tools, and many Orpheus general-purpose profiling tools will include ado.net tools; the main reason I mention MiniProfiler is that is is free, readily available, and very low impact (it is what we wrote to profile stackoverflow.com - and we leave it "on" 24x7)

Related

Getting Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near [duplicate]

This question already has answers here:
Using Prepared Statement in C# with Mysql
(2 answers)
Closed 12 months ago.
I am getting this error message:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near ''D:\AirMaintStorage\' where id=1' at line 1
I run this in WorkBench and it works correctly but from my code in my app I get the error above. Here is my code:
string SQL = "Update Company_Table set Company_Table_Default_Storage='";
SQL += ra.EscFunction(DefaultStorage);
SQL += "' where id=";
SQL += cp.id.ToString();
MySql.Data.MySqlClient.MySqlConnection conn = new MySql.Data.MySqlClient.MySqlConnection (ra.conn_String1);
MySql.Data.MySqlClient.MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand(SQL, conn);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
The query is:
Update Company_Table set Company_Table_Default_Storage='D:\\AirMaintStorage\\' where id=1
ra is a utilities class that holds the connection information and escfunction escapes apostrophes in a string. cmp.cp.id is the id field of my company table.
I expect there's a bug in EscFunction(). You should remove that function from your code base completely; it's entirely the wrong way to approach the issue. It is not correct to sanitize your database inputs!
Rather, the only correct approach is to QUARANTINE your database inputs using parameterized queries, as demonstrated below:
string SQL = "
UPDATE Company_Table
SET Company_Table_Default_Storage= #DefaultStorage
WHERE ID= #ID";
using (var conn = new MySqlConnection(ra.conn_String1))
using (var cmd = new MySqlCommand(SQL, conn))
{
cmd.Parameters.AddWithValue("#DefaultStorage", DefaultStorage);
cmd.Parameters.AddWithValue("#ID", cp.id);
conn.Open();
cmd.ExecuteNonQuery();
} // No need to even call conn.Close(); The using block takes care of it.
This is different than "sanitizing", because the quarantined values are never merged back into the SQL command text, even on the server.
Also note the use of using blocks to manage the connection lifetime.
Finally, I want to address this comment:
I have been trying to move to a more multi-platform environment so I have started moving my app to MySQL.
I'm getting outside the realms of the both the question scope and the fact-based verifiable-answers we like to write for Stack Exchange here, and more into an expression of my personal opinion on a specific technology. Nevertheless, I didn't want to leave that comment alone.
I do get wanting to support a broader set of database or hosting technologies, but you should know the MySql product spent many years (from ~2005 to ~2018) almost completely stagnant. It has fallen significantly behind other options and is the least standards compliant of the major products in the space. The good news is it seems to be progressing again, but right now if I needed to move an app to a new open source DB today I'd pick Postgresql instead, and it wouldn't be a close decision.
Of course, your use case may be different, but I think MySql has a reputation as the default option that no longer reflects that actual state of the technologies, and hasn't for some time now. At the same time, SQL Server is now perfectly happy to run in linux, meaning it's not required to switch to MySql or anything else in order to enable multiplatform hosting.

Insert SQL Statement into SQL Server column

I've inherited an application with a lot of ADO work in it, but the insert/update helper method that was written returns void. We've also been experiencing a lot of issues with data updates/inserts not actually happening. My goal is to update all of them to check rows affected and depending on the results, act accordingly, but for the time being of finding what may be causing the issue, I wanted to log SQL statements that are called against the server and the number of rows affected by the statement.
This is the statement I'm attempting:
SqlCommand com = new SqlCommand(String.Format("'INSERT INTO
SqlUpdateInsertHistory(Statement, AffectedRows) VALUES (''{0}'', {1});'",
statement.Replace("'", "''"), rows), con);
but it seems to constantly break somewhere in the sql that is being passed in (some cases on single quotes, but I imagine there are other characters that could cause it as well.
Is there a safe way to prep a statement string to be inserted?
I just can't rightly propose a solution to this question without totally modifying what you're doing. You're currently wide open to SQL Injection. Even if this is a local application, practice how you want to play.
using (SqlCommand com = new SqlCommand("INSERT INTO SqlUpdateInsertHistory(Statement, AffectedRows) VALUES (#Statement, #AffectedRows)", con))
{
com.Parameters.AddWithValue("#Statement", statement);
com.Parameters.AddWithValue("#AffectedRows", rows);
com.ExecuteNonQuery();
}
Have you tried SQL Server Profiler? It's already been written and logs queries, etc.
Someone else tried this and got a lot of decent answers here.

how to get exec string for sql management studio from .net code

I am debugging code someone else wrote that calls a lot of stored procedures (sql server 2008 r2) from C# code. The C# code looks like this
SqlCommand sqlCommand = new SqlCommand(strSP, ConnectionOpen());
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.CommandTimeout = intTimeOut;
//System.Data.SqlClient.SqlParameter[] prmSQL
if (prmSQL != null)
{
while (i < prmSQL.Length)
{
sqlCommand.Parameters.Add(prmSQL[i]);
i = i + 1;
}
}
SqlDataReader sqlReader = sqlCommand.ExecuteReader();
For debugging my stored procedures I really need the string that sql management studio needs which is like
exec sp_name param one, param two (with quotes if needed for strings and dates..)
The sql command object does not provide this string via some property. The only way I know is to run the sql profiler on sql server and grab the string. Unfortunately the DBA's do not like this since they say running the profiler impacts performance. Is there any addin or code snippet you guys use to get the sp exec string from c# code ? Whats the best way to get this string ? Thanks
You could use a tool like mvc-mini-profiler available on NuGet (note: the name is misleading; it isn't limited to MVC). Minor clarification - since it wraps the connection, you would need to use the abstract DbConnection rather than SqlConnection, and then you just tweak the one line of code (probably in a utility class somewhere) that creates your connection, i.e. instead of:
var conn = new SqlConnection(someString);
return conn;
you might use:
var conn = new SqlConnection(someString);
return new StackExchange.Profiling.Data.ProfiledDbConnection(
conn, MiniProfiler.Current);
There's a couple of other steps to enable it (all shown on the site page), but it literally takes 2 minutes to add to an MVC application. The output is that it monitors, in real time, for enabled users (developers etc), all the activity. We use it 24x7 on stackoverflow/stackexchange (meaning: we made very sure it didn't impact performance). A live demo is available on https://data.stackexchange.com/ - just log in, and the profiling data is visible top-left. It automatically presents the data in a form runnable from SSMS, because that is how we often use it - so: it presents parameters as though they were variable declarations / initializations.
It also plays nicely with ORMs such as LINQ-to-SQL and dapper-dot-net (and many others).
Rep is too low (still a noob to StackOverflow)to comment so I'm posting this as an answer. My apologies. However, you might consider looking at SMO. SMO is a .NET object model that can be used to interact with SQL Server. Using SMO you can get a reference to a specific Stored Procedure
and then enumerate it's parameters.
That might help you get started.
In order to construct the EXEC command, you will need to know the parameter names used by the procedure. I believe you can find them by using the GetDbSchemaTable method, whcih will retrieve stored procedure SQL (I have done this using MS-Access/OLEDB and am assuming it works the same for MS-SQL/SqlClient):
using (conn == new OleDb.OleDbConnection(DBConnection)) {
conn.Open();
DataTable DBObject = conn.GetOleDbSchemaTable(OleDb.OleDbSchemaGuid.Procedures, null);
}
The column named "PROCEDURE_DEFINITION" contains the procedure's SQL and hopefully the parameter list.
You may also want to have a look at Obtaining Schema Information from a Database.
HTH

ODBC Command does not provide the count of inserted records

I am using an ODBC connnection to connect to a database. When I execute OdbcCommand.ExecuteNonQuery with an insert statement, the records are inserted into the table, but the method returns 0.
ExecuteNonQuery returns the number of records affected. It's working fine in case of Delete and Update but not working in case of Insert.
query = "Insert into table1 (field1, field2) values (1,2)";
OdbcConnection = _remoteconn = new OdbcConnection(constring);
OdbcCommand cmd = new OdbcCommand(query, _remoteconn);
recordsAffected = cmd.ExecuteNonQuery();
ODBC is not a driver - it is a wrapper around a driver. The answer to this question is going to depend on the underlying ODBC driver that you are using, which would typically be specified in the Connection String.
The MSDN documentation is really more of an expectation or a suggestion, but the ODBC interface cannot force the driver to return a particular result. It may be that the driver has some optimizations or settings which are interfering with your results, not unlike SQL Servers NO COUNT setting (which would override a driver's attempts to report affected rows, etc).
See wikipedia for a more thorough explanation of how ODBC works:
http://en.wikipedia.org/wiki/ODBC
"Because different technologies have different capabilities, most ODBC drivers do not implement all functionality defined in the ODBC standard. Some drivers offer extra functionality not defined by the standard."
If you tell us which driver you are using, it may help to find a solution.
EDIT
To the best of my knowledge, the ODBC API function in question is the SQLRowCount function, as defined here:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms711835(v=vs.85).aspx
According to the ODBC standard, this function must be implemented for a driver to be considered ODBC-compliant. (That is not to say that the function will return the expected or correct result, just that it's there).
According the Transoft documentation (page 67) I've been able to dig up the SQLRowCount function was implemented. There is no mention of this function in any way being disabled or of any required configuration.
Given the above, I would contact the vendor directly. It would appear that there is an error in their implementation or an undocumented "feature".
I had a similar problem and it was because the ODBC Driver for the database in question had a different version from the database.
Check the version in the database with:
select ##version
and in the ODBC (at least for windows xp) at:
Control Panel -> Administrative Tools -> Microsoft ODBC Source
Administrator -> Drivers -> here check the column version of your
database
If they are different, that might be the problem!
Try
ExecuteReader
instead of
ExecuteNonQuery
and get the value with a select in the SQL sentence
using System;
using System.Data;
using System.Data.Odbc;
class myOdbc
{
static void Main()
{
OdbcConnection myOdbcCommandConnection = new OdbcConnection("...");
myOdbcCommandConnection.Open();
OdbcCommand myOdbcCommand = myOdbcCommandConnection.CreateCommand();
myOdbcCommand.CommandText = "INSERT INTO table1 (field1, field2) VALUES (?, ?)";
myOdbcCommand.Parameters.Add("#field1", OdbcType.Int);
myOdbcCommand.Parameters.Add("#field2", OdbcType.Int);
myOdbcCommand.Parameters["#field1"].Value = 1;
myOdbcCommand.Parameters["#field2"].Value = 2;
Console.WriteLine("Number of Rows Affected is: {0}", myOdbcCommand.ExecuteNonQuery());
myOdbcCommandConnection.Close();
Console.ReadKey();
}
}
MSDN for OdbcCommand states the following:
For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command.
Can you post more code ? You're most certainly losing track of the number you get, or no records are being inserted (!!)
UPDATE
Or, as correctly pointed out in comments, the ODBC driver itself is either bugged or simply not providing this functionality (you're not given the count of records you'd expect).

.NET/C# check if SQL query modifies database and if not execute

I know how to execute queries from C# but I want to provide a dropdown list in which people can write a query and it will execute and populate the list.
A problem is that I want to forbid all queries that modify the database in any way. I have not managed to find a way to do this and I did my best with google.
The solution I can think of is that I will scan the query for INSERT, DELETE, UPDATE and only allow SELECT statements. However, I want to be able to allow users to call stored procedures as well. This means I need to get the body of the stored procedure and scan it before I execute it. How do I download a stored procedure then?
If anyone knows a way to only execute read only queries do share please! I have the feeling scanning the text for INSERT, DELETE, UPDATE doesn't prevent SQL injections.
The easiest way to do this might be to offload this job to the database. Just make sure that the database user that will be running the queries has read-access only. Then, any queries that do anything other than SELECT will fail, and you can report that failure back to the users.
If you don't go this route, the complexity becomes quite enormous, since you basically have to be prepared to parse an arbitrary SQL statement, not to mention arbitrary sequences of SQL statements if you allow stored procs to be run.
Even then, take care to ensure that you aren't leaking sensitive data through your queries. Directly input queries from site users can be dangerous if you're not careful. Even if you are, allowing these queries on anything but a specifically constructed sandbox database is a "whoops, I accidentally changed the user's permissions" away from becoming a security nightmare.
Another option is to write a "query creator" page, where users can pick the table and columns they'd like to see. You can then a) only show tables and columns that are appropriate for a given user (possibly based on user roles etc.) and b) generate the SQL yourself, preferably using a parameterized query.
Update: As Yahia points out, if the user has execute privilege (so that they can execute stored procs,) then the permissions of the procedure itself are honoured. Given that, it might be better to not allow arbitrary stored proc execution, but rather offer the users a list of procedures that are known to be safe. That will probably be difficult to maintain and error-prone, though, so disallowing stored procs altogether might be best.
How about creating a user account on the database server which only has select (read-only) rights?
Perhaps you could set up a SQL user with read-only access to the database and issue the command using that user? Then you can catch the errors when/if they happen.
It seems to me that it's going to be very difficult and error-prone to try to parse the query to figure out if it modifies the database.
You can't parse SQL like that reliably.
Use permissions to
Allow only SELECT on tables and views
No permissions on stored procedures that change data (An end user by default won't be able to see stored procedure definition)
Best is to not allow users to enter SQL and use only prepared/parameterized queries...
The next best way to prevent that is to use a restricted user with pure read access
The above two can be combined...
BEWARE
To execute a Stored Procedure the user must have execute privilege... IF the Stored Procedure modifies data then this would happen without an error messages even with a restricted user since the permission to modify is granted to the Stored Procedure!
IF you absolutely must allow users to enter SQL and can't restrict the login then you would need to use a SQL parser - for example this...
As to how to download the body of a Stored Procedure - this is dependent on the DB you use (SQL Server, Oracle etc.).
EDIT:
Another option are so-called "Database Firewall" - you connect instead of directly to the DB to the Firewall... in the Firewall you configure several things like time-based restrictions (when specific users/statement are/art not allowed), SQL-based statement (which are allowed...), quantity-based restrictions (like you can get 100 records, but are not able to download the whole table/DB...) etc.
There are commercial and opensource DB Firewalls out there - though these are by nature very dependent on the DB you use etc.
Examples:
Oracle Firewall - works with Oracle / SQL Server / DB2 etc.
SecureSphere - several including Oracle / SQL Server / DB2 etc.
GreenSQL - opensource version support Postgres + MySQL, commercial MS SQL Server
Don't forget about things that are even worse than INSERT, UPDATE, and DELETE. Like TRUNCATE...that's some bad stuff.
i think SQL Trigger is the best way what you want to do.
Your first move should be to create a DB user for this specific task with only the needed permissions (basically SELECT only), and with the rights to see only the tables you need them to see (so they cannot SELECT sys tables or your users table).
More generally, it seems like a bad idea to let users execute code directly on your database. Even if you protect it against data modification, they will still be able to make ugly-looking joins to make your db run slow, for instance.
Maybe whichever language your programming the UI with, you could try to look online for a custom control that allows filtering on a database. Google it...
this is not perfect but might be what you want, this allows the keyword to appear if its a part of a bigger alphanumeric string:
public static bool ValidateQuery(string query)
{
return !ValidateRegex("delete", query) && !ValidateRegex("exec", query) && !ValidateRegex("insert", query) && !ValidateRegex("alter", query) &&
!ValidateRegex("create", query) && !ValidateRegex("drop", query) && !ValidateRegex("truncate", query);
}
public static bool ValidateRegex(string term, string query)
{
// this regex finds all keywords {0} that are not leading or trailing by alphanumeric
return new Regex(string.Format("([^0-9a-z]{0}[^0-9a-z])|(^{0}[^0-9a-z])", term), RegexOptions.IgnoreCase).IsMatch(query);
}
you can see how it works here: regexstorm
see regex cheat sheet: cheatsheet1, cheatsheet2
notice this is not perfect since it might block a query with one of the keywords as a quote, but if you write the queries and its just a precaution then this might do the trick.
you can also take a different approach, try the query, and if it affects the database do a rollback:
public static bool IsDbAffected(string query, string conn, List<SqlParameter> parameters = null)
{
var response = false;
using (var sqlConnection = new SqlConnection(conn))
{
sqlConnection.Open();
using (var transaction = sqlConnection.BeginTransaction("Test Transaction"))
using (var command = new SqlCommand(query, sqlConnection, transaction))
{
command.Connection = sqlConnection;
command.CommandType = CommandType.Text;
command.CommandText = query;
if (parameters != null)
command.Parameters.AddRange(parameters.ToArray());
// ExecuteNonQuery() does not return data at all: only the number of rows affected by an insert, update, or delete.
if (command.ExecuteNonQuery() > 0)
{
transaction.Rollback("Test Transaction");
response = true;
}
transaction.Dispose();
command.Dispose();
}
}
return response;
}
you can also combine the two.

Categories

Resources