How can I Pass a Table Name to SqlCommand? - c#

I am trying to pass a table name as a parameter to my query through SqlCommand but it doesn't seems to be working.
Here is my code;
SqlConnection con = new SqlConnection( "server=.;user=sa;password=12345;database=employee" );
con.Open( );
SqlCommand cmd = new SqlCommand( "drop table #tbName" , con );
cmd.Parameters.AddWithValue( "#tbName" , "SampleTable" );
cmd.ExecuteNonQuery( );
con.Close( );

SqlCommand.Parameters are supported for Data manipulation language operations not Data definition language operations.
Even if you use DML, you can't parameterize your table names or column names etc.. You can parameterize only your values.
Data manipulation language =
SELECT ... FROM ... WHERE ...
INSERT INTO ... VALUES ...
UPDATE ... SET ... WHERE ...
DELETE FROM ... WHERE ...
Data definition language =
CREATE TABLE ...
DROP TABLE ... ;
ALTER TABLE ... ADD ... INTEGER;
You can't use DROP statement with parameters.
If you really have to use drop statement, you might need to use string concatenation on your SqlCommand. (Be aware about SQL Injection) You might need to take a look at the term called Dynamic SQL
Also use using statement to dispose your SqlConnection and SqlCommand like;
using(SqlConnection con = new SqlConnection(ConnectionString))
using(SqlCommand cmd = con.CreateCommand())
{
cmd.CommandText = "drop table " + "SampleTable";
con.Open()
cmd.ExecuteNonQuery();
}

User Soner Gönül pointed out why it doesn't work, nevertheless you can write stored procedure yourself.
CREATE PROCEDURE dbo.procdroptable
#TABLENAME SYSNAME
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = 'DROP TABLE dbo.' + QUOTENAME(#TABLENAME) + '';
EXEC sp_executesql #SQL;
END
GO
Code from this question.

Related

How to fix SQL Injection Issue of truncation of table

Below is the line of code where I truncate table records. The table value is coming from the front end. In my Veracode scan, it is showing SQL injection. How can I avoid this? I cannot create a stored procedure as the connection string is dynamic where I need to truncate this table. Is there another approach?
SqlCommand cmd = connection.CreateCommand();
cmd.Transaction = transaction;
cmd.CommandText = "TRUNCATE TABLE " + tablename;
cmd.ExecuteNonQuery();
You need dynamic sql:
string sql = #"
DECLARE #SQL nvarchar(150);
SELECT #SQL = 'truncate table ' + quotename(table_name) + ';'
FROM information_schema.tables
WHERE table_name = #table;
EXEC(#SQL);";
using (var connection = new SqlConnection("connection string here"))
using (var cmd = new SqlCommand(sql, connection))
{
cmd.Transaction = transaction;
cmd.Parameters.Add("#table", SqlDbType.NVarChar, 128).Value = tablename;
connection.Open();
cmd.ExecuteNonQuery();
}
This is one of very few times dynamic SQL makes things more secure, rather than less. Even better, if you also maintain a special table in this database listing other tables users are allowed to truncate, and use that rather than information_schema to validate the name. The idea of letting users just truncate anything is kind of scary.
Parametrized or not, you can make it only a little more secured in this case. Never totally secured. For this you need
create table TruncMapping in DB where you store
id guid
statement varchar(300)
your data will look like
SOME-GUID-XXX-YYY, 'TRUNCATE TABLE TBL1'
In your front end use a listbox or combobox with text/value like "Customer Data"/"SOME-GUID-XXX-YYY"
In your code use ExecuteScalar to execute Select statement from TruncMapping where id = #1 , where id will be parameterized GUID from combo value
Execute your truncate command using ExecuteNonQuery as you do now but with a retrieved string from previous call.
Your scan tool will most likely choke. If it is still thinking code is unsafe, you can safely point this as false positive because what you execute is coming from your secured DB. Potential attacker has no way to sabotage your "non-tuncatable tables" because they are not listed in TruncMapping tables.
You've just created multi-layered defense against sql injection.
here is one way to hide it from scanning tools
private const string _sql = "VFJVTkNBVEUgVEFCTEU=";
. . . .
var temp = new { t = tablename };
cmd.CommandText =
Encoding.ASCII.GetString(Convert.FromBase64String(_sql)) + temp.t.PadLeft(temp.t.Length + 1);
security by obscurity

Using SQL parameters to DROP INDEX in C#

I'm trying to follow best practice (and also remove Visual Studio Code Analysis warnings) by using parameters when dropping a SQL Server index.
Not using parameters works fine:
string tableName = "dbo.TableName";
SqlCommand sqlCommand = new SqlCommand("DROP INDEX Blah ON " + tableName);
sqlCommand.Connection = sqlConnection;
sqlCommand.ExecuteNonQuery();
However, when I try to use a parameter I get an error
Incorrect syntax near '#TableName'.
Code:
string tableName = "dbo.TableName";
SqlCommand sqlCommand = new SqlCommand("DROP INDEX Blah ON #TableName");
sqlCommand.Parameters.Add(new SqlParameter("TableName", tableName));
sqlCommand.Connection = sqlConnection;
sqlCommand.ExecuteNonQuery();
What am I doing wrong?
You are doing nothing wrong. Parameters cannot be used to replace identifiers -- column names/aliases, table names/aliases, schema names, and database names. They also cannot be used to replace function names or operators or keywords.
That is a long list. They can be used to replace constants in the query.
I guess the way to remember this is that the parameterized query can be pre-compiled. In order to compile a query, all object references need to be resolved -- so the values cannot be provided by parameters.
You have already solved the problem by putting the table in the string. You can use quotename() to help protect against injection (see here).
DROP INDEX is a DDL statement, most DDL statements don't accept parameterized values. The best you can do is use dynamically constructed SQL and escape the table name using QUOTENAME
string tableName = "dbo.TableName";
string sql = #"
declare #sql nvarchar(500)
set #sql = N'DROP INDEX Blah ON ' + QUOTENAME(#TableName)
exec sp_executesql #sql
";
SqlCommand sqlCommand = new SqlCommand("");
sqlCommand.Parameters.Add("#TableName", SqlDbType.NVarChar, 50).Value = tableName;
sqlCommand.Connection = sqlConnection;
sqlCommand.ExecuteNonQuery();
I also updated your code to use the more "normal" way to add a parameter, explicitly setting the datatype of the parameter.

define per connection variables for sql server

How can I achieve something like following
SqlConnection connection = new SqlConnection("CNStr");
SqlCommand Command = connection.CreateCommand();
command.CommandText = "DECLARE #find varchar(30) = 'Test'; ";
command.ExecuteNonQuery();
command.CommandText = "select * from Some_View_Which_Has_Used_That_Variable_In_ItsWhere_Condition"
var result = command.ExecuteNonQuery();
command.Dispose();
connection.Dispose();
I want to declare some per connection variable, and then use them everywhere, in views, functions and so on.
How can I achieve this goal without using temp table and contextId ?
Thanks in advance
You can not use DECLARE in your sql statement.
It is not even valid Data Manipulation Language statement. It doesn't even effect your Some_View_Which_Has_Used_That_Variable_In_ItsWhere_Condition at all.
That's why it looks pointless to me. If you really want to declare #find, you can write in your view as manual.
And use using statement to dispose your SqlConnection and SqlCommand.
For example;
using(SqlConnection con = new SqlConnection(CNStr))
using(SqlCommand command = con.CreateCommand())
{
//
}
The only obvious per-connection object that persists for longer than a single batch (i.e. your ExecuteXXX calls) would be a temp table:
command.CommandText = "CREATE TABLE #Settings (find varchar(30) not null);
INSERT INTO #Settings (find) VALUES ('Test')";
command.ExecuteNonQuery();
command.CommandText = "select * from Some_View where
SomeColumn in (select find from #Settings)"
var result = command.ExecuteReader();
All variables (whether scalar or table-valued) have lifetimes that end at the end of their respective batches.
A few other things can be set at the connection/session level (e.g. CONTEXT_INFO) but they tend to have fixed, limited data types.

How do I pass a GUID value into an SqlCommand object SQL INSERT statement?

I have an SQL Server database table created by deploying the following description in a .dbproj project:
CREATE TABLE [dbo].[Tasks]
(
TaskId uniqueidentifier primary key,
State int not null,
)
and I want to insert a row into that table with the following code:
using( SqlTransaction transaction = connection.BeginTransaction() ) {
using( SqlCommand command = connection.CreateCommand() ) {
command.CommandText = "INSERT INTO Tasks VALUES( \"" +
Guid.NewGuid().ToString() + "\", 0)";
command.Transaction = transaction;
command.ExecuteNonQuery();
transaction.Commit();
}
}
when ExecuteNonQuery() runs an exeption is thrown saying
The name [the string representation of the GUID I passed] is not permitted in this context.
What's up? I did the same to insert data into an SQLite table previously and it worked. How do I pass a GUID into an SQL INSERT statement?
Use a parameterized query, like so:
command.CommandText = "INSERT INTO Tasks VALUES( #id, 0)";
command.Parameters.Add( "#id", SqlDbType.UniqueIdentifier, 16 ).Value = value;
This way, the database driver formats the value for you. This is a good practice that will also help protect your database from SQL Injection attacks.
Alternatively, you could let the database generate the guid for you:
command.CommandText = "INSERT INTO Tasks VALUES( NEWID(), 0)";

getting sql statement behind view

I have a c# application (2008) that gets data from sql server (2005).
I have a view in sql server that prepares data for display, something like this (simplified):
select Places.Name as [Location], Parts.Name as [Part Name]
from Places inner join Parts
on Places.Id=Parts.Location
I have to filter this with "where" statement that is built in code and is like:
where (Places.Id=1 or Places.Id=15) and
(Parts.Id=56 or Parts.Id=8 or Parts.Id=32)
I can of course keep the basic select statement in my code, but i likw to have things defined only in one place :) and the question is if there is any way to get the select statement behind the view in sql server? Or to get the contents of stored procedure?
Thanks a lot!
Take a look at Information Schema View, you may find your solution.
Using the information schema views as jani suggested is one option.
Another is using the sp_helptext system stored procedure. sp_helptext YourView or sp_helptext YourStoredProcedure gets you the entire object definition.
You can find more information about the at sp_helptext system stored procedure here.
If you want a stored procedure to execute your query (and combining your basic query string, with your where clause), you can accomplish this by using the following code:
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string selectCommand = "EXEC sp_YourStoredProcedure #whereClause";
SqlCommand command = new SqlCommand(selectCommand, connection);
command.Parameters.Add("#whereClause", System.Data.SqlDbType.NVarChar);
command.Parameters["#whereClause"] = whereClause;
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.NextResult())
{
string location = reader.GetString(0);
string partName = reader.GetString(1);
// do something
}
}
connection.Close();
}
Edit: Example of dynamic stored procedure:
CREATE PROCEDURE sp_YourStoredProcedure
(
#whereClause NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #sql AS NVARCHAR(MAX)
SET #sql = N'
select Places.Name as [Location], Parts.Name as [Part Name]
from Places inner join Parts
on Places.Id=Parts.Location '
+ #whereClause
EXEC sp_executesql #sql
END

Categories

Resources