How can I format my string in SQL so that it is interpreted properly in Powershell? - sql

I'm currently working on a simple SQL Script that I want to run from SSMS. What the script is supposed to do is take a database, make a backup of it, and then make a .zip file of that backup.
The problem I am running into is when attempting to zip up the backup. I declared all of my variables at the beginning of the file and I believe that when I attempt to execute #sqlcmd the string is not being read by Powershell properly.
SET #sqlcmd = ('powershell.exe [IO.Compression.ZipFile]::CreateFromDirectory("' + #bkpath + '", "C:\'+ #fileName + '.zip")')
PRINT #sqlcmd
-- this statement returns (powershell.exe [IO.Compression.ZipFile]::CreateFromDirectory("C:\Backup\", "C:\ScriptingTestDB_20170607.zip"))
EXEC xp_cmdshell #sqlcmd
Running this code returns the following output in my results window:
At line:1 char:47
+ [IO.Compression.ZipFile]::CreateFromDirectory(C:\Backup", C:\Scriptin ...
+ ~
Missing ')' in method call.
At line:1 char:56
+ ... le]::CreateFromDirectory(C:\Backup", C:\ScriptingTestDB_20170607.zip)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The string is missing the terminator: ".
At line:1 char:47
+ ... le]::CreateFromDirectory(C:\Backup", C:\ScriptingTestDB_20170607.zip)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unexpected token 'C:\Backup", C:\ScriptingTestDB_20170607.zip)' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingEndParenthesisInMethodCall
NULL
After some testing I think I figured out that the problem lies with how Powershell interprets the SQL string. Because of the many quotes It's possible that even though SQL prints out the string correctly, when Powershell gets ahold of it the format gets messed up so something needs to be changed there, but still not sure what. Does anyone know of any guidelines I can use to find out how the SQL code should be written such that Powershell reads the following:
[IO.Compression.ZipFile]::CreateFromDirectory("C:\Backup\", "C:\ScriptingTestDB_20170607.zip")

You need to escape "-characters, with \. My working script:
declare #sqlcmd varchar(1000), #bkpath nvarchar(max) = 'C:\i1\tmp', #filename nvarchar(max) = 'file'
SET #sqlcmd = 'powershell.exe Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::CreateFromDirectory(\"' + #bkpath + '\", \"C:\i1\'+ #fileName + '.zip\")'
EXEC xp_cmdshell #sqlcmd

Related

execute powershell from SSMS

I am trying to execute as certain xp_cmdshell code:
declare #url varchar(250) = 'https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=439d4b804bc8187953eb36d2a8c26a02'
declare #c varchar(1000) = N'powershell.exe -noprofile -executionpolicy bypass '
+ N'-command (Invoke-WebRequest -Uri '''+#url+'''-UseBasicParsing).content'
print #c
exec xp_cmdshell #c
but this is the error that I get:
The string is missing the terminator: '.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
'appid' is not recognized as an internal or external command,
operable program or batch file
when I try an execute the same thing in powershell I get the expected response(see image)
(Invoke-WebRequest -Uri 'https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=439d4b804bc8187953eb36d2a8c26a02'-UseBasicParsing).content
Powershell
The expected output from the xp_cmdshell should be a json
The error message implies that your command line is being executed via cmd.exe, where & is a metacharacter.
To use & verbatim - as is your intent - either the URL of which & is a part must be enclosed in "..." (cmd.exe only recognizes double quotes) or you must ^-escape the &:
declare #url varchar(250) = 'https://samples.openweathermap.org/data/2.5/weather?q=London,uk^&appid=439d4b804bc8187953eb36d2a8c26a02'
-- ...
When cmd.exe parses the command line, it recognizes ^& as an escaped & to be used verbatim, and removes ^, the escape character, before passing the argument on.

Issue with SSIS Package parameter - dtexec

I am trying to execute SSIS package using command line dtcexe but I am getting error.
Error I get - Description: The package path referenced an object that cannot be found: "\Package.Variables[$Project::p_cityID].Properties[Value]". This occurs when an attempt is made to resolve a package path to an object that cannot be found.
Begin
declare #p_cityId varchar(10) = '%'
declare #p_count varchar(10) = '-1'
declare #query varchar(4000) =
'dtexec /Project C:\SSIS\DUTPackages.ispac /Package pmtCity.dtsx /decrypt pass#123'
+ ' /SET \Package.Variables[$Package::p_cityID].Properties[Value];''' + #p_cityId + ''''
+ ' /SET \Package.Variables[$Package::p_count].Properties[Value];''' + #p_count + ''
exec xp_cmdshell #query
End
So far I have tried using -
Package.Variables[$Project::p_cityID].Properties[Value]
Package.Variables[User::p_cityID].Properties[Value]
But had no luck.
Can anyone point me to right direction and tell me what I am doing wrong?
From MSDN The following example of running a package from the .ispac project file and setting package and project parameters.
/Project c:\project.ispac /Package Package1.dtsx /SET \Package.Variables[$Package::Parameter];1 /SET \Package.Variables[$Project::Parameter];1

bcp outfile not found

I'm trying to run the script below, but it returns null. When I run the DOS command, it generates the file normally.
DECLARE #str VARCHAR(1000)
SET #str = 'bcp "Select * FROM WDG.dbo.Facilidade" queryout "w:\xyzTable.txt" -S "WDG-NOTE24\MSSQLWDG" -T -c -t ; '
EXEC xp_cmdshell #str
GO
I need to return a separate txt file for ';' with query data
Tanks
Given your error in your comments i will follow this link and as i described earlier its an access problem to your folder.
Remember it should be given to your Sql service account user and not your self
BCP unable to open BCP host access
I managed to make it work, I found the command below that tests if it has access to the directory.
For some reason did not accept the old path, so I created another one on another disk and it worked.
EXEC master..xp_cmdshell 'DIR C:\sql'
Very thank's for help.
I have a problem, at the end of my import file, it comes with the text '--END--', and when the bulk insert is going to render, it displays an unexpected end message.
I can put some parameter so that when it finds the text it finishes the import.
declare #sql varchar(max)
set #sql = 'BULK INSERT Temp_Facilite FROM ''' + ##FullPath + '''WITH (FIRSTROW = 2,CODEPAGE = ''RAW'',FIELDTERMINATOR = '';'',ROWTERMINATOR = ''0x0A'',MAXERRORS = 3, KEEPNULLS );'
exec (#sql)

TransactSQL to run another TransactSQL script

I have 10 transact SQL scripts that each create a table and fill it with data.
I am attempting to create 1 master sql script that will run each of the 10 other scripts.
Is there a way with TSQL / TRANSACTSQL for Microsoft SQL Server 2008 to execute another tsql script from within the current tsql script?
This is intended to be run through the SQL Server Management Studio (SSMS).
Thanks!
Try this if you are trying to execute a .sql file in SSMS:
:r C:\Scripts\Script1.sql
:r C:\Scripts\Script2.sql
:r C:\Scripts\Script3.sql
...
note: for this to run turn on sql command mode (Query > SQLCMD Mode)
If these are scripts you run fairly often you might consider dropping them in a stored proc and running them that way...
You can also do it through sqlcmd (which I believe is more common):
sqlcmd -S serverName\instanceName -i C:\Scripts\Script1.sql
Or just use openrowset to read your script into a variable and execute it:
DECLARE #SQL varchar(MAX)
SELECT #SQL = BulkColumn
FROM OPENROWSET
( BULK 'MeinPfad\MeinSkript.sql'
, SINGLE_BLOB ) AS MYTABLE
--PRINT #sql
EXEC (#sql)
I find it useful to define a variable with the path, if I want to execute a set of scripts, say to run a test, something like:
:setvar path "C:\code\branch-qa"
:r $(path)\tables\client.sql
:r $(path)\tables\item.sql
:r $(path)\proc\clientreport.sql
exec clientreport
You can use osql or better yet the newer sqlcmd almost interchangeably. I am using osql in this example only because I happened to have a code sample sitting around but in production I am using sqlcmd. Here is a snipped of code out of a larger procedure I use to run update scripts against databases. They are ordered by major, minor, release, build as I name my scripts using that convention to track releases. You are obviously missing all of my error handing, the parts where I pull available scripts from the database, setup variables, etc but you may still find this snippet useful.
The main part I like about using osql or sqlcmd is that you can run this code in ssms, or in a stored procedure (called on a scheduled basis maybe) or from a batch file. Very flexible.
--Use cursor to run upgrade scripts
DECLARE OSQL_cursor CURSOR
READ_ONLY
FOR SELECT FileName
FROM #Scripts
ORDER BY Major, Minor, Release, Build
OPEN OSQL_cursor
FETCH NEXT FROM OSQL_cursor INTO #name
WHILE (##fetch_status <> -1)
BEGIN
IF ((##fetch_status <> -2) AND (#result = 0))
BEGIN
SET #CommandString = 'osql -S ' + ##ServerName + ' -E -n -b -d ' + #DbName + ' -i "' + #Dir + #name + '"'
EXEC #result = master.dbo.xp_cmdshell #CommandString, NO_OUTPUT
IF (#result = 0)
BEGIN
SET #Seconds = DATEDIFF(s, #LastTime, GETDATE())
SET #Minutes = #Seconds / 60
SET #Seconds = #Seconds - (#Minutes * 60)
PRINT 'Successfully applied ' + #name + ' in ' + cast(#Minutes as varchar)
+ ' minutes ' + cast(#Seconds as varchar) + ' seconds.'
SET #LastTime = GETDATE()
END
ELSE
BEGIN
SET #errMessage = 'Error applying ' + #name + '! The database is in an unknown state and the schema may not match the version.'
SET #errMessage = #errMessage + char(13) + 'To find the error restore the database to version ' + #StartingVersion
SET #errMessage = #errMessage + ', set #UpToVersion = the last version successfully applied, then run ' + #name
SET #errMessage = #errMessage + ' manually in Query Analyzer.'
END
IF #name = (#UpToVersion + '.sql')
GOTO CleanUpCursor --Quit if the final script specified has been run.
END
FETCH ENDT FROM OSQL_cursor INTO #name
END
The simplest way would be to make your scripts stored procedures, and to call (via the EXECUTE command) each procedure in turn from a central procedure. This is ideal if you're going to run the exact same script(s) over and over again (or the same script with different parameters passed in).
If your scripts are .sql (or any kind of text) file, as #Abe Miesller says (upvoted) you can run them from within SSMS via the :r command, when SQLCMD mode is enabled. You would have to know and script the exact file path and name. This cannot be done from within a stored procedure.
A last alternative, usable with "known" file names and necessary for arbitrary file names (say, all files currently loaded in a subfolder) is to leverage the power of extended procedure XP_CMDSHELL. Such solutions can get compelx pretty fast (use it to retrieve list of files, build and execute via xp_cmdshell a string calling SQLCMD for each file in turn, manage results and errors via output files, it goes on and on) so I'd only do this as a last resort.
Assuming you want to keep the 10 scripts in their own individual files, I would say the easiest way to do what you want would be to create a batch file that executes osql.exe to execute the 10 scripts in the order you want.

Escaping command parameters passed to xp_cmdshell to dtexec

I am calling an SSIS package remotely using a stored procedure and a call to xp_cmdshell:
declare #cmd varchar(5000)
set #cmd = '"C:\Program Files (x86)\Microsoft SQL Server\100\DTS\Binn\dtexec.exe" /Rep E /Sql Package /SET \Package.Variables[User::ImportFileName].Value;c:\foo.xlsx'
print #cmd
exec xp_cmdshell #cmd
This works fine, however I can not guarantee the variable value (c:\foo.xslx) is not going to contain spaces so I would like to escape that with quotes like below:
set #cmd = '"C:\Program Files (x86)\Microsoft SQL Server\100\DTS\Binn\dtexec.exe" /Rep E /Sql Package /SET \Package.Variables[User::ImportFileName].Value;"c:\foo.xlsx"'
But by doing this I get the error
'C:\Program' is not recognized as an internal or external command, operable program or batch file.
Both of the above commands work fine if executed within cmd.exe so I am guessing that SQL Server is interpreting my double quotes and changing something, but I can't figure out what.
In a nutshell, put CMD /S /C " at the beginning, and " at the end. In between you can have as many quotes as you like.
Here is how you do it:
declare #cmd varchar(8000)
-- Note you can use CMD builtins and output redirection etc with this technique,
-- as we are going to pass the whole thing to CMD to execute
set #cmd = 'echo "Test" > "c:\my log directory\logfile.txt" 2> "c:\my other directory\err.log" '
declare #retVal int
declare #output table(
ix int identity primary key,
txt varchar(max)
)
-- Magic goes here:
set #cmd = 'CMD /S /C " ' + #cmd + ' " '
insert into #output(txt)
exec #retVal = xp_cmdshell #cmd
insert #output(txt) select '(Exit Code: ' + cast(#retVal as varchar(10))+')'
select * from #output
After looking into this, it appears you have to use the DOS 8.3 notation with xp_cmdshell e.g. c:\progra~1... and you can only have one set of quotes on the arguments.
To get around this limitation, either use the older DOS notation, or put your code in a batch file instead which will run fine.
Source: http://social.msdn.microsoft.com/forums/en-US/sqlintegrationservices/thread/4e7024bb-9362-49ca-99d7-1b74f7178a65