In SSMS copied string has different behaviour to original string - tsql

I am attempting to semi automate creation of my databases
As part of this I want to add extended properties of column descriptions.
When I try to run sp_sqlexec in my script ( or even just Exec(#mystring) I get an error. However, if while debugging, I copy the dynamic sql string from the watch window and then run sp_sqlexec on the copied string in a seperate window I get no errors and the extended properties are added correctly.
The following script demonstrates the problem:
--Create a table to apply column descriptions to
Create table dbo.table1 (id int, name nvarchar(20));
--Create the table that contains our column descriptions
Create table dbo.column_descs_table (schemaname nvarchar(20), tablename nvarchar(20), columnname nvarchar(20), column_description nvarchar(20))
Insert into column_descs_table (schemaname, tablename, columnname, column_description)
values ('dbo', 'table1', 'id', 'the id column'), ('dbo' , 'table1', 'name', 'the name column');
--Dynamic sql string varaible to hold the commands
Declare #dyn_sql nvarchar(max);
Set #dyn_sql = 'N'''; --Set to opening quote
--now create the string containing commands to add column escriptions
SELECT #dyn_sql = #dyn_sql + N' EXEC sp_addextendedproperty ''''Col Desc'''', ''''' + column_description + N''''', ''''SCHEMA'''', ' + schemaname + N', ''''TABLE'''', ' + tablename + N', ''''COLUMN'''', ' + columnname + N' ;'
FROM dbo.column_descs_table
Set #dyn_sql = #dyn_sql + ''''; --add the closing quote
Print #dyn_sql --If I copy the contents of #dyn_sql here and run seperately it works OK
Exec sp_sqlexec #dyn_sql -- this line causes error
The error I get is
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near ' EXEC sp_addextendedproperty 'Col Desc', 'the id column', 'SCHEMA', dbo, 'TABLE', table1, 'COLUMN', id ; EXEC sp_addextendedprope'.
Yet if I step through the code and copy the contents of #dyn_sql then paste this as follows:
Exec sp_sqlexec N' EXEC sp_addextendedproperty ''Col Desc'', ''the id column'', ''SCHEMA'', dbo, ''TABLE'', table1, ''COLUMN'', id ; EXEC sp_addextendedproperty ''Col Desc'', ''the name column'', ''SCHEMA'', dbo, ''TABLE'', table1, ''COLUMN'', name ;'
Then the above works fine and the column descriptions are added as expected.
Any help on this specific copying problem is much appreciated. I do understand the security issues with dynamic sql ( this script will be removed from the database once my setup is complete)
Thanks in advance
Jude

It looks like it's because your leading N is included within the string to execute; you don't need it at all. In other words, you are ending up with something like this:
exec sp_execsql 'N'' exec sp_addextendedproperty /* etc. */ '''
But it should be like this:
exec sp_execsql N'exec sp_addextendedproperty /* etc. */ '
But why are you even using dynamic SQL here? All values passed to sp_addextendedproperty can be passed as parameters so there is no obvious reason to use dynamic SQL, unless you've simplified something for the question.
Finally, you should be using sp_executesql, it's the preferred way to execute dynamic SQL.

I believe that I have resolved my string copying problem. SQL was detecting double quotes in by concatenated string as empty strings and removing them. A simple example showing the problem and my solution is below:
--Example to Select 'simple string' and then 'concat string' into results sets
DECLARE
#Simplestring nvarchar( max ) = '' ,
#Concatstring nvarchar( max ) = '' ,
#Stringvar nvarchar( 10 ) = 'string';
--The double quotes in next line are the quotemark we want plus a quotemark acting
--as an escape character
--#simplestring will be set to 'Select 'simple string' '
SET #Simplestring = 'Select ''simple string'' ';
--Similarly we need #concatstring to be set to 'Select 'Concat string' '
SET #Concatstring = 'Select '' concat' + #Stringvar + ''; -- this wont work the last
--double quote will be removed
--Add a character that cannot appear in any othe part of the concatenation - I've used *
SET #Concatstring = 'Select '' Concat ' + #Stringvar + '*';
--Now replace the * with a quote mark
SET #Concatstring = REPLACE( #Concatstring , '*' , '''' ); -- This will work
EXEC sp_executesql #Simplestring;
EXEC sp_executesql #Concatstring;
There may be a simpler solution than mine.
Many thanks for the advice on using sp_executesql. I am working on changing my code to use this ( with variables passed in as parametrs).
Jude

Related

How to import multiple CSV files into SQL Server tables?

I am using SQL Server 2017 version, and I want to import multiple .csv files into multiple tables in SQL server.
I found the following script in the net,
--BULK INSERT MULTIPLE FILES From a Folder
--a table to loop thru filenames drop table ALLFILENAMES
CREATE TABLE ALLFILENAMES(WHICHPATH VARCHAR(255),WHICHFILE varchar(255))
--some variables
declare #filename varchar(255),
#path varchar(255),
#sql varchar(8000),
#cmd varchar(1000)
--get the list of files to process:
SET #path = 'C:\Dump\'
SET #cmd = 'dir ' + #path + '*.csv /b'
INSERT INTO ALLFILENAMES(WHICHFILE)
EXEC Master..xp_cmdShell #cmd
UPDATE ALLFILENAMES SET WHICHPATH = #path where WHICHPATH is null
--cursor loop
declare c1 cursor for SELECT WHICHPATH,WHICHFILE FROM ALLFILENAMES where WHICHFILE like '%.csv%'
open c1
fetch next from c1 into #path,#filename
While ##fetch_status <> -1
begin
--bulk insert won't take a variable name, so make a sql and execute it instead:
set #sql = 'BULK INSERT Temp FROM ''' + #path + #filename + ''' '
+ ' WITH (
FIELDTERMINATOR = '','',
ROWTERMINATOR = ''\n'',
FIRSTROW = 2
) '
print #sql
exec (#sql)
fetch next from c1 into #path,#filename
end
close c1
deallocate c1
But the problem is I cannot use the command 'EXEC Master..xp_cmdShell' cause it was disabled by DBA's due to some security reasons, and they are not permitting me to use it. Is there any alternative command that I can use instead of 'xp_cmdShell' in the same script.
In this script near bulk insert command (set #sql = 'BULK INSERT Temp FROM ''' + #path + #filename + ''' '
+ ') I see only one table name 'Test', and how can I mention multiple table names in the Bulk insert command?
Any help please.
It's been a long time since I have had to do this, but this is how I used to do these kinds of things.
DECLARE #intFlag INT
SET #intFlag = 1
WHILE (#intFlag <=100)
BEGIN
PRINT #intFlag
declare #fullpath1 varchar(1000)
select #fullpath1 = '''\\FTP\' + convert(varchar, getdate()- #intFlag , 112) + '_your_file.csv'''
declare #cmd1 nvarchar(1000)
select #cmd1 = 'bulk insert [dbo].[your_table] from ' + #fullpath1 + ' with (FIELDTERMINATOR = ''\t'', FIRSTROW = 5, ROWTERMINATOR=''0x0a'')'
exec (#cmd1)
SET #intFlag = #intFlag + 1
END
GO
As you can tell, this is looping through a bunch of files with dates as file names. The first part of each file name was in this date format: convert(varchar, getdate()- #intFlag , 112)
I'm guessing your files have names that match some specific pattern.
SQl Server has a tool that does this for you. Goto to your SQL Server folder
Open SQL Server Import and Export Wizard.
Choose a Data Source Microsoft Excel
Select the Excel File. And following the steps

Is this vulnerable to SQL injection?

We are using a third party product which references a stored procedure in MSSQL. This stored proc looks something like this:
CREATE PROCEDURE [dbo].[example]
#a nvarchar(255)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #strSQL nvarchar(3000)
SET #strSQL = 'SELECT * FROM test WHERE x = ''1'''
IF IsNull(#a, '') <> ''
SET #strSQL = #strSQL + ' AND a = ''' + #a + ''''
EXEC(#strSQL)
END
This stored proc doesn't actually output its results to the website but I'm still sure that it is vulnerable to SQL injection. I can input t' + 'est and get the same result as I would from inputing test.
We obviously need to get them to change this but I need to demonstrate that it is an issue first. How can I do something like insert a row in to a table by passing SQL in as #a? If I do
'; INSERT INTO blah VALUES('test')
Then I get:
Incorrect syntax near ';'.
yes, it's vulnerable, but by chance you've injected the wrong text, producing a syntax error:
SELECT * FROM test WHERE x = "1" AND a =; INSERT INTO blah VALUES('test')
^--your syntax error
If your injection text had been:
a; INSERT blah blah blah
^---
then you'd have ended up with two valid queries and test in your blah table.
Yes, you can set your # to have an secape character and thus create mutiple Execs ulimately leading to execcmd format C: or other - google SQL injection attacks
However:
Create proc db.eg #a nvarchar(255)
AS
BEGIN
Update Mytable SET Mycol = #a WHERE Condition etc..
END
IS not open to SQL injection as the string goes directly to the table column, it is nt exec'd

escape quotes inside quotes in TSQL string

I'm trying to run the following TSQL statement on Microsoft SQL 2008
DECLARE #tmpMessage nvarchar(max)
SET #tmpMessage = 'select * from openquery(GLive,''select ID from Links WHERE [HREF] LIKE ''test'''')';
exec sp_executesql #tmpMessage
the above code doesnt work because the single quotes before test closes the main quotes around the second select statement select ID from Links ....
and yes i have to put my statement in a string first before executes it because openquery function wont allow me to do somethin like
select * from openquery(GLive,'select ID from Links WHERE [Href] LIKE ''' + #Var + ''''')
any suggesstions would be appreciated.
thanks in advance.
Here is a template that I use whenever I'm dealing with variables in an openquery statement to a linked server:
DECLARE #UniqueId int
, #sql varchar(500)
, #linkedserver varchar(30)
, #statement varchar(600)
SET #UniqueId = 2
SET #linkedserver = 'LINKSERV'
SET #sql = 'SELECT DummyFunction(''''' + CAST(#UniqueId AS VARCHAR(10))+ ''''') FROM DUAL'
SET #statement = 'SELECT * FROM OPENQUERY(' + #linkedserver + ', '
SET #Statement = #Statement + '''' + #SQL + ''')'
EXEC(#Statement)
You might also try out the QUOTENAME command.

Why does concatenating strings in the argument of EXEC sometimes cause a syntax error in T-SQL?

In MS SQL Server Management Studio 2005, running this code
EXEC('SELECT * FROM employees WHERE employeeID = ' + CAST(3 AS VARCHAR))
gives this error: Incorrect syntax near 'CAST'
However, if I do this, it works:
DECLARE #temp VARCHAR(4000)
SET #temp = 'SELECT * FROM employees WHERE employeeID = ' + CAST(3 AS VARCHAR)
EXEC(#temp)
I found an explanation here: T-SQL: Cannot pass concatenated string as argument to stored procedure
According to the accepted answer, EXEC can take a local variable or a value as its argument, but not an expression.
However, if that's the case, why does this work:
DECLARE #temp VARCHAR(4000)
SET #temp = CAST(3 AS VARCHAR)
EXEC('SELECT * FROM employees WHERE employeeID = ' + #temp)
'SELECT * FROM employees WHERE employeeID = ' + #temp sure looks like an expression to me, but the code executes with no errors.
The documentation states that EXEC can take either a string variable, a constant T-SQL string, or combinations/concatenations of both of them.
Your "why does this work" example uses a concatenation of a constant T-SQL string and a string variable, and so is perfectly legal.

TSQL varchar string manipulation

I have a variable which contains the following string: AL,CA,TN,VA,NY
I have no control over what I get in that variable (comes from reporting services)
I need to make it look like this: 'AL','CA','TN','VA','NY'
How do I do this?
declare #x varchar(50) = 'AL,CA,TN,VA,NY'
select '''' + REPLACE(#x, ',', ''',''') + ''''
I ended up doing something very similar that I thought I'd post. (I'll give credit to Mitch however)
This takes care of the middle:
SET #StateList = REPLACE(#StateList, ',', ''',''')
Then quote the edges:
SET #WhereClause1 = #WhereClause1 + 'AND customerState IN (''' + #StateList + ''') '
For a more generic answer, when you don't know what your output will look like exactly, use regular expressions.
This would let you you match on something like [A-Z]{2} and replace it with '$&'.
A commenter suggested this is overkill for this task - agreed, if you can guarantee you will always get a string like that. However, other people find these question pages later with similar, but not exact, problems, so other options are helpful to have.
Don't bother with dynamic sql.
You need to convert the string to a table
so
A,B,C,D
becomes
Value
A
B
C
D
using a function like
http://www.sqlusa.com/bestpractices/training/scripts/splitcommadelimited/
then you can use CROSS APPLY (which is like joining to a table, but a table created by a function) or you can just put it in a table variable and join to that
I want to know y does the following script run in SQL and not in T-SQL
DECLARE #tblName varchar(30)
SET #tblName = CONVERT(VARCHAR(20),GETDATE(),112) + 'Table'
DECLARE #sql nvarchar(4000)
SELECT #sql =
'CREATE TABLE "' + #tblName + '"
(
ID VARCHAR(15),
Name VARCHAR(15)
)'
EXEC(#sql)
go
it gives you the error
Msg 170, Sev 15: Line 1: Incorrect syntax near '20090714Table'. [SQLSTATE 42000]