I want to use LINQ to call stored procedure in this way but stored procedure I want to call contain SQL string that executed by
EXEC sp_executesql #strSQL
In this way Visual Studio does not generate result class. To resolve this problem I execute SQL string by
INSERT INTO #Temp EXEC sp_executesql #strSQL
SELECT * from #Temp
Maybe it is not the best way but it works. The problem is that it sometimes return items in wrong sequence. How to fix it?
Use an Order By to specify the sort order
Related
I need to add the schema value dynamically to the query. I was trying to construct it the way we usually use values but realised it does not work with the schema names the same way.
This was what I was trying to do
sql = "SELECT Name FROM [#dbo].[Members]";
...
command.Parameters.Add("#dbo", SqlDbType.VarChar).Value = "dbo";
I know I can construct the query by directly adding the variable in the query like this:
sql = $#"SELECT Name FROM [{parameter}].[Members]";
But I wanted to disallow any kind of SQL Injection so want to go ahead using the parameterized query as above.
Can anyone help with a possible idea to implement this?
TIA
The schema is not a parameter. Query parameters are equivalent to function parameters in eg C#. They're used to pass values. In SQL, the table and columns are equivalent to C# types and properties. You can't specify them by name. The schema in a SQL query is similar to the Namespace in C#. The table is equivalent to a Type. In C#, just because Sales.Record and Diagnostics.Record have the same type name doesn't mean the two types can be used the same way.
The question doesn't explain why the schema name is passed dynamically. It's almost certain there are easier, more efficient and safer ways to query similar tables in multiple schemas, but the solution would depend on the actual problem.
There are some techniques that can be used to make such a dynamic query safe if not efficient. I'd really, really try to avoid treating the schema as a value though.
Using QUOTENAME
One option, is to use QUOTENAME in a T-SQL script to construct a dynamic query. At least this way a syntax error will be thrown if the schema and table names are wrong:
sql = #"declare #sql nvarchar(max)='SELECT Name FROM ' + QUOTENAME(#dbo) + '.[Members]';
select #sql;
exec sp_executesql #sql;";
...
command.Parameters.Add("#dbo", SqlDbType.NVarChar,100).Value = "dbo";
QUOTENAME will convert something like sys].schemas; PRINT ''x''; -- to [[sys]].schemas; PRINT 'x'; --]. This will result in an error :
declare #sql nvarchar(max)= 'select * from [' +quotename('sys].schemas; PRINT ''x''; --')
select #sql
exec sp_executesql #sql;
--------
select * from [[sys]].schemas; PRINT 'x'; --]
Invalid object name '[sys].schemas; PRINT 'x'; --'.
It's too easy to make quoting mistakes with such scripts. This could be extracted into a stored procedure :
CREATE PROCEDURE GetMemberNameBySchema
#dbo nvarchar(100)
as
declare #sql nvarchar(max)='SELECT Name FROM ' + QUOTENAME(#dbo) + '.[Members]';
exec sp_executesql #sql;
Verify the Schema name
Query sys.schema to ensue the schema is correct before constructing the query. Let's say you're using Dapper (so I don't have to write all the ADO.NET code) :
var schema="dbo";
var isValid=connection.ExecuteScalar<bool?>(
"select 1 from sys.schema where name=#name",
new {name=schema});
//isValid will be null if nothing is found
if(isValid ==true)
{
var names=connection.Query($"SELECT Name FROM [{schema}].[Members]");
...
}
This is safe to do because the first query ensured the schema name is valid.
You can only pass parameters to dynamic query not object names as far as I know. You can do something like below:
Update after seeing Panagiotis' solution. we can take out the exists part outside. I am no expert in C# but something similar we want to do:
DECLARE #Table NVARCHAR(100);
DECLARE #Schema NVARCHAR(50);
SET #Schema = 'dbo'; --This part will come from C#
SET #Table = 'Tablename'; --This part will come from C#
DECLARE #sql NVARCHAR(MAX);
IF EXISTS(SELECT * FROM sys.tables WHERE name = #Table and schema_id = SCHEMA_ID(#Schema))
BEGIN
SET #sql = N'SELECT TOP 1 * FROM '+ QUOTENAME(#Schema) + '.' + QUOTENAME(#Table) +';';
PRINT #sql;
EXEC sp_executesql #sql;
END
ELSE
RAISERROR('Table or Schema doesn''t exist.',16,1);
In the IF EXISTS you need to pass the table name and schema name as parameter to dynamic query.
In the actual query you need to make a concatenation of table name and schema name.
This will prevent sql injection as well and if the table is not there then raise an error.
In my project I have defined a stored procedure with a example code below:
CREATE PROCEDURE [dbo].[Stored]
#ParameterA AS varchar(128),
#ParameterB AS varchar(128),
#ParameterC AS varchar(400)
AS
BEGIN
DECLARE #query AS Varchar(MAX)
SET #query = 'SELECT *
FROM Table
WHERE A = '''+ #ParameterA + ''
IF #ParameterB = 'B'
BEGIN
SET #query = #query + ' AND C=''' + #ParameterC + ''
END
EXECUTE sp_executesql #query
END
I call this procedure with Entity Framework through the following code:
DBContext.Database.SqlQuery<Object>("Stored",
new SqlParameter("#p0", Param0),
new SqlParameter("#p1", Param1),
new SqlParameter("#p2", Param2)).ToList();
If I call a stored procedure with the string below, I generate a SQL injection:
Param2 = "ABC' ; DROP TABLE Table2"
How can I prevent this with Entity Framework?
You cannot
The underlying SQL procedure is faulty and a security nightmare. There is no way you can repair that on the layer on top of it. You are doing the best you can in EntityFramework, but it's still unsafe. You need to repair the problem (SQL proc) and not apply band aids to the layer using it.
sp_executesql seems to be a good starting point for a procedure that needs to have dynamic SQL and bind parameters.
you are creating a dynamic query, where you are concatenating parameters. this is causing issue.
do not use dynamic query, or validate parameters (if it contains any keywords or characters)
you can also rewrite your query into IF-ELSE structure on basis of parameters, so you do not need dynamic query.
I have to configure a system which provides me with an area to input SQL statements.
It is important to notice that we cannot modify the system we are configuring.
I believe the system was built in C# (.net for sure but C# is a guess).
Any way, I'm trying to create a script that would:
create a temporary table
create a temporary procedure (which inserts into the table created)
call the temporary procedure 4 times
Read the temp table as a response to the system's call.
Something like:
CREATE Procedure #myTempProcedure(
#param1 nvarchar(max)
) as
begin
insert #tempTable (col1, col2) select aCol, bCol from table2 where col2 = #param1;
end;
CREATE TABLE #tempTable
(col1 nvarchar(512),
(col2 nvarchar(512));
EXEC #myTempProcedure N'val1';
EXEC #myTempProcedure N'val2';
EXEC #myTempProcedure N'val3';
EXEC #myTempProcedure N'val4';
select col1, col2 from #tempTable;
The system is very likely executing my script via C# SqlCommand.ExecuteReader() method. As I can simulate the problem in a simple C# application I created.
The problem is that when executing this script, the system (or SQL Server) assumes the procedure body to be the entire script and seems to disregard my ; in line 6 of the example above. My intention with this ; was to flag the end of the procedure creation.
Executing this script in Management studio requires a GO to be placed in line 7 of the example above, otherwise the same problem reported by the system would happen in Management Studio.
Is there a GO equivalent I could use in this script to get it to work??
Or is there a better way to script this??
I have a background in Oracle, and I'm still leaning SQL server usual tricks... The System accepts multiple commands apart from the create procedure here, So I'm inclined to believe there is a SQL Server trick I could use here.
Thank you in advance!
The problem is that syntactically there is no way to create a procedure and then do something after it in the same batch. The compiler doesn't know where it ends, and things like semi-colon don't fix it (because semi-colon only terminates a statement, not a batch).
Using dynamic SQL, (and fixing one syntax error) this works:
EXEC('
CREATE Procedure ##myTempProcedure(
#param1 nvarchar(max)
) as
begin
insert #tempTable (col1, col2) select aCol, bCol from table2 where col2 = #param1;
end;
');
CREATE TABLE #tempTable
(
col1 nvarchar(512),
col2 nvarchar(512)
);
EXEC ##myTempProcedure N'val1';
EXEC ##myTempProcedure N'val2';
EXEC ##myTempProcedure N'val3';
EXEC ##myTempProcedure N'val4';
select col1, col2 from #tempTable;
EXEC('DROP PROC ##myTempProcedure;');
1) please look at the rights on the server you many have some issues with if you cannot change or add anything to the system.
ie., Create procedure statements.
2) you could do a small exercise
open a connection object using the SqlConnection()
keep the connection open till you execute all you statements
ie., a) create your #table
b) execute your insert statement.
c) select * from your #table
this should get you back the data you are intending to get back from your temp table note i skipped the entire proc here.
Instead of creating stored procedure, you can execute sql statements delimited by semi colon. You can execute multiple statements this way. Also if you want to create a temp table and load it with data, you can use the same connection with multiple sql commands.
Given that the proc definition doesn't change, and that there is no real harm in the proc existing beyond the end of this particular process, it could just as easily be a regular (i.e. non-temporary) Stored Procedure that just happens to exist in tempdb. The benefit of using a regular Stored Procedure created in tempdb is that you do not need to worry about potential name collisions when using global temporary stored procedures. The script simply needs to ensure that the stored procedure exists. But there is no need to remove the Stored Procedure manually or have it automatically cleaned up.
The following code is adapted from #RBarryYoung's answer:
IF (OBJECT_ID(N'tempdb.dbo.myTempProcedure') IS NULL)
BEGIN
USE [tempdb];
EXEC('
CREATE PROCEDURE dbo.myTempProcedure(
#param1 NVARCHAR(MAX)
) AS
BEGIN
INSERT INTO #tempTable (col1, col2)
SELECT aCol, bCol
FROM table2
WHERE col2 = #param1;
END;
');
END;
CREATE TABLE #tempTable
(
col1 NVARCHAR(512),
col2 NVARCHAR(512)
);
EXEC tempdb.dbo.myTempProcedure N'val1';
EXEC tempdb.dbo.myTempProcedure N'val2';
EXEC tempdb.dbo.myTempProcedure N'val3';
EXEC tempdb.dbo.myTempProcedure N'val4';
SELECT col1, col2 FROM #tempTable;
The only difference here is that a non-temporary Stored Procedure does not execute in the context of the current database, but instead, like any other non-temporary Stored Procedure, runs within the context of the database where it exists, which in this case is tempdb. So the table that is being selected from (i.e. table2) needs to be fully-qualified. This means that if the proc needs to run in multiple databases and reference objects local to each of them, then this approach is probably not an option.
I am new to SQL Server, I am trying to insert records into table using a stored procedure as shown below.
I want a suggestion that is using the below stored procedure. Also:
can I prevent SQL injection?
is it the right way?
Correct me if I miss anything in below procedure which leads to SQL injection.
Create PROCEDURE [dbo].[spInsertParamTable]
#CmpyCode nvarchar(50),
#Code nvarchar(50),
#DisplayCode nvarchar(50),
#TotalDigit int,
#Nos bigint,
#IdentitY int OUTPUT
AS
BEGIN
INSERT tblParamTable (CmpyCode, Code, DisplayCode, TotalDigit, Nos)
VALUES (#CmpyCode, #Code, #DisplayCode, #TotalDigit, #Nos)
END
SELECT #Identity = SCOPE_IDENTITY();
RETURN #Identity
SQL Injection specifically refers to injecting SQL code into an existing SQL query that's built up via string concatenation and executed dynamically. It is almost always of the form:
#dynamicSQL = "select * from sensitivetable where field = " + #injectableParameter
sp_executesql #dynamicSQL
For this particular stored procedure, the worst an attacker could do is insert unhelpful values into your tblParamTable.
However, if these values are then used in a dynamically-built query later on, then this merely becomes a second-order attack: insert values on page 1, see results of dynamic query on page 2. (I only mention this since your table is named tblParamTable, suggesting it might contain parameters for later re-use.)
Can I prevent SQL injection?
You already are - there is no way to "inject" code into your SQL statement since you're using parameters.
Is it the right way?
Well, there's not one "right" way - but I don't see anything seriously wrong with what you're doing. A few suggestions:
You don't need to RETURN your output parameter value. Setting it is enough.
You have the last SELECT outside of the BEGIN/END block, which isn't hurting anything but for consistency you should put everything inside BEGIN/END (or leave them out altogether).
create procedure pro_training2
as
begin
create view Tab1view As
select * from tab1
end
Is this possible to create a view in the procure?
You can do this using dynamic SQL, e.g.
CREATE PROCEDURE dbo.pro_training2
AS
BEGIN
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'CREATE VIEW dbo.Tab1View AS SELECT <columns> FROM dbo.tab1;';
EXEC sp_executesql #sql;
END
GO
But this is a serious code smell.
You have tagged with MySQL though the above is not MySQL syntax.
Anyway, if you did mean for MySQL: yes, it is possible to issue CREATE VIEW from within a procedure, but using a different syntax:
CREATE PROCEDURE pro_training2()
MODIFIES SQL DATA
BEGIN
create view Tab1view As select * from tab1;
END
Use sp_executesql and specify the Database name:
EXEC myDb1..sp_executesql N'CREATE VIEW Tab1view As select * from tab1'