I have an SP that invokes xp_cmdshell to call an .exe. The .exe has logic to avoid requiring user input, but a few still slip through, which cause the SP to hang indefinitely and leave things in a bad state.
Is there a way to add timeout logic to the xp_cmdshell call, so that the .exe call is aborted, gracefully if possible, after 60 seconds (i.e. equivalent of CTRL-Z or CTRL-C in cmd prompt)?
SP
CREATE OR ALTER PROCEDURE RC.USR_SP_REFRESHEXCELFILE (
#SourceFilePath VARCHAR(1000)
, #SourceFileName VARCHAR(255)
, #TargetFilePath VARCHAR(1000)
, #TargetFileName VARCHAR(255)
, #Result INTEGER OUTPUT -- 0 = success, 1 = failure
) AS
BEGIN
-- Declare variables
DECLARE
#ExcelRefreshProgramLocation VARCHAR(1000),
#CommandString VARCHAR(8000)
-- Get exe path
SET #ExcelRefreshProgramLocation = <...some location...>
-- Set command string
SET #CommandString =
#ExcelRefreshProgramLocation + ' ' +
#SourceFilePath + ' "' +
#SourceFileName + '" ' +
#TargetFilePath + ' "' +
#TargetFileName + '"' -- quotes not working with file paths
-- Allow advanced options to be changed
EXECUTE sp_configure 'show advanced options', 1;
RECONFIGURE;
-- Enable command shell
EXECUTE sp_configure 'xp_cmdshell', 1;
RECONFIGURE;
-- Execute command
EXEC #Result = xp_cmdshell #CommandString;
-- Disable command shell
EXECUTE sp_configure 'xp_cmdshell', 0;
RECONFIGURE;
-- Prevent advanced options from being changed
EXECUTE sp_configure 'show advanced options', 0;
RECONFIGURE;
END
Related
I received one requirement from client,if any take the database backup from SQL Server, backup will by default contains the password.
so No One can restore the database without password.
I suggests the TDE encryption solution but he is not agree with this.
anyone one know, Is there any SQL setting for this?
Thanks
Nirav
IDK if there is any built-in option (except TDE). We are using WinRAR for similar purpose :
EXEC sp_configure 'show advanced options', 1
GO
RECONFIGURE
GO
EXEC sp_configure 'xp_cmdshell', 1
GO
RECONFIGURE
GO
DECLARE #fileName varchar(1000)
DECLARE #pathName varchar(1000)
DECLARE #backupName varchar(1000)
DECLARE #backupFullName varchar(1000)
DECLARE #rarName varchar(1000)
DECLARE #compressCommand varchar(1000)
DECLARE #deleteBakCommand varchar(1000)
DECLARE #deleteRarCommand varchar(1000)
DECLARE #moveCommand varchar(1000)
DECLARE #deleteOldRarCommand varchar(1000)
SET #fileName = 'database' + CONVERT(varchar, DATEPART(weekday,getdate()))
set #pathName = 'D:\folder\'
SET #backupName = #fileName + '.bak'
SET #rarName = #fileName + '.rar'
SET #backupFullName =#pathName + #backupName
SET #compressCommand = 'C:\Progra~1\WinRAR\rar a -m5 -pPASSWORD '+ #pathName + #rarName + ' ' + #pathName+#backupName + '"'
SET #deleteBakCommand = 'del ' + #pathName + #backupName
SET #deleteRarCommand = 'del ' + #pathName + #rarName
SET #deleteOldRarCommand = 'del ' + #pathName + 'TARGET\db\' + #rarName
set #moveCommand = 'copy /y ' + #pathName+#rarName + ' ' + #pathName + 'TARGET\db\' + #rarName
EXEC master..xp_cmdshell #deleteBakCommand;
EXEC master..xp_cmdshell #deleteRarCommand;
BACKUP DATABASE DBNAME TO DISK = #backupFullName;
EXEC master..xp_cmdshell #deleteOldRarCommand;
EXEC master..xp_cmdshell #compressCommand;
EXEC master..xp_cmdshell #deleteBakCommand;
EXEC master..xp_cmdshell #moveCommand;
EXEC master..xp_cmdshell #deleteRarCommand;
GO
EXEC sp_configure 'xp_cmdshell', 0
I am trying to create a text file that includes line breaks. I have this code:
DECLARE #Text AS VARCHAR(100)
DECLARE #Cmd AS VARCHAR(100)
SET #Text = 'This is my text line 1 (supposed to be new line character) This is line2'
SET #Cmd ='echo ' + #Text + ' > C:\FileStore\test.txt'
EXECUTE Master.dbo.xp_CmdShell #Cmd, NO_OUTPUT
What should i use to have a new line character on the text file so that the output should be
This is my text line 1
This is line2
UPDATE:
I've tried this as per suggestions
SET #Text = 'This is my text line 1' + char(13) + char(10)+ 'This is line2'
But with this, it won't create the txt file anymore
Thanks
Use CHAR(13) to output a carriage return, or CHAR(13) + CHAR(10) for carriage return plus line feed (for windows)
Windows uses a carriage-return line-feed sequence to indicate a new line, character codes 13 & 10 so:
select 'hello' + char(13) + char(10) + 'world'
(Note than in SSMS grid view you will just see a double space)
it won't create the txt file any more
Ah thats the echo command line not supporting new-lines.
You could
echo This is my text line 1 > "C:\FileStore\test.txt" & echo This is line2 >> "C:\FileStore\test.txt"
Or find a different way that does not involve the command line.
EXEC sp_configure 'show advanced options', 1;
GO
-- To update the currently configured value for advanced options.
RECONFIGURE;
GO
-- To enable the feature.
EXEC sp_configure 'xp_cmdshell', 1;
GO
-- To update the currently configured value for this feature.
RECONFIGURE;
GO
DECLARE #cmd varchar(8000), #var varchar(2000)
SET #cmd = 'echo line1 > "d:\test.txt"'
+ '& echo line2 >> "d:\test.txt"'
+ '& echo line3 >> "d:\test.txt"'
+ '& echo line4 >> "d:\test.txt"'
+ '& echo line5 >> "d:\test.txt"'
EXEC master..xp_cmdshell #cmd;
EXEC sp_configure 'show advanced options', 1;
GO
-- To update the currently configured value for advanced options.
RECONFIGURE;
GO
-- To enable the feature.
EXEC sp_configure 'xp_cmdshell', 0;
GO
-- To update the currently configured value for this feature.
RECONFIGURE;
GO
I've been working around a limitation put in place by a vendor and it is requiring me to write a SQL Server trigger that populates a text file that I then move to a mail pickup folder (it would be so much easier if the vendor would allow me to email directly form SQL.
With that said I can write to text files and move them just fine but what I cant seem to do is insert <html> tags into the text file.
When I try it just leaves that line out. Any help out there?
SET #from = 'FROM: no-reply#domainname.com' ;
set #to = 'TO: keith#domainname.com';
SET #subject = #subjecttext
SET #cmd = '(echo '+#from+' && echo '+#to+' && echo ' +#subject+') >//server02-2010/test/SprintTask.txt';
EXEC master..xp_cmdshell #cmd, no_output;
--set #body = 'more text';
--SET #cmd = '(echo '+#body+') >>//server02-2010/test/SprintTask.txt';
--EXEC master..xp_cmdshell #cmd, no_output;
set #body = 'Content-Type: multipart/mixed;'
SET #cmd = '(echo '+#body+') >>//server02-2010/test/SprintTask.txt';
EXEC master..xp_cmdshell #cmd, no_output;
Set #body = 'boundary="Attached"';
SET #cmd = '(echo '+#body+') >>//server02-2010/test/SprintTask.txt';
EXEC master..xp_cmdshell #cmd, no_output;
Set #body = '--Attached' ;
SET #cmd = '(echo '+#body+') >>//server02-2010/test/SprintTask.txt';
EXEC master..xp_cmdshell #cmd, no_output;
Set #body = 'Content-Type: text/html;charset=UTF-8' ;
SET #cmd = '(echo '+#body+') >>//server02-2010/test/SprintTask.txt';
EXEC master..xp_cmdshell #cmd, no_output;
Set #body = 'Content-Transfer-Encoding: 7bit' ;
SET #cmd = '(echo '+#body+') >>//server02-2010/test/SprintTask.txt';
EXEC master..xp_cmdshell #cmd, no_output;
Set #body = '--Attached--'
SET #cmd = '(echo '+#body+') >>//server02-2010/test/SprintTask.txt';
EXEC master..xp_cmdshell #cmd, no_output;
Set #body = '<b>' + 'THIS IS TEST TEXT';
SET #cmd = '(echo '+#body+') >>//server02-2010/test/SprintTask.txt';
EXEC master..xp_cmdshell #cmd, no_output;
A trigger should be very small and nimble since it executes in the context of the transaction that caused the trigger to fire.
It should most definitely not do any heavy lifting and extensive processing, like writing out to an external file...
I would recommend rethinking your trigger strategy - in your case, I would
write a trigger that outputs the relevant information into a separate "PendingEmails" table (or something like this) - but nothing more
create a separate program (could be a T-SQL stored procedure, or a front-end application written in C# or something) that would then consult that "PendingEmails" table on a scheduled basis, and if needed, would actually send out those e-mails you want to send out - in a separate process, not within the same database transaction
Otherwise, your trigger might end up taking a long time to complete, especially if it might need to wait for an external source to return with an acknowledgement - and this will happen in the context of your actual SQL statement - a sure fire way to kill off any performance your database might have had....
Whenever you would like to write html tags using:
Set #body = '<b>' + 'THIS IS TEST TEXT';
SET #cmd = '(echo '+#body+') >>//server02-2010/test/SprintTask.txt';
EXEC master..xp_cmdshell #cmd, no_output;
you need to understand that CMD.EXE echo ... is actually being performed. Since your #body contains < and > characters, CMD.EXE interpret this as:
redirect input from a file: <
redirect output to a file: >
To avoid this CMD.EXE's interpretation, you need to escape all html tag < and > characters, by placing a ^ (caret character) in front of the redirection characters like:
Set #body = '<b>' + 'THIS IS TEST TEXT';
SET #body = REPLACE(#body, '<', '^<'); -- escape <
SET #body = REPLACE(#body, '>', '^>'); -- escape >
SET #cmd = '(echo '+#body+') >>//server02-2010/test/SprintTask.txt'; -- here, of course, output redirection is needed
EXEC master..xp_cmdshell #cmd, no_output;
Cheers, Paul
I would like to get the internal database version number of a SQL Server database programatically to determine which SQL Server instance to use.
i.e. SQL Server 2008 - Version 661, SQL Server 2008 R2 - Version 665
I have found an article: http://rusanu.com/2011/04/04/how-to-determine-the-database-version-of-an-mdf-file/ that shows the location of the version number as the DWORD value at offset 0×12064 in the .mdf file.
How can I get this information programatically?
Thanks
If the database is already attached:
CREATE TABLE #d
(
ParentObject NVARCHAR(4000),
[Object] NVARCHAR(4000),
Field NVARCHAR(4000),
VALUE NVARCHAR(4000)
);
DECLARE #sql NVARCHAR(MAX) = N'dbcc page(' + RTRIM(DB_ID())
+ ',1,9,3) with tableresults;'
INSERT #d EXEC sp_executesql #sql;
SELECT [Version] = VALUE FROM #d WHERE Field = N'dbi_version';
DROP TABLE #d;
Though if the database is already attached, then it is already at the version of the instance. If the database isn't attached, why not just attach it to the most recent version you have? Just because the database is currently 661 does not mean it has to stay that way. It will work if you attach it to 2008 R2 or 2012 just as well.
Here's a stored procedure I created to read the offline .mdf using OLE Automation and ADO's Stream object. This solution and more context are posted here as well: https://social.technet.microsoft.com/Forums/sqlserver/en-US/3de5b574-0751-44a2-b69f-fa0c20378359/how-to-determine-sql-server-version-of-an-mdf-file?forum=sqlsetupandupgrade
CREATE /*ALTER*/ PROCEDURE spDBVersion
(
#FilePath nvarchar(500) /*Your filepath here EG 'C:\SQLData\somedatabase.mdf'*/
)
AS
DECLARE #HR int,
#objStream int,
#strErrorMessage Varchar(1000),
#Source nvarchar(255),
#Desc nvarchar(255),
#offset bigint,
#binary1 binary,
#binary2 binary
SET NOCOUNT ON
SELECT #strErrorMessage='Creating ADO stream object'
EXEC #HR = sp_OACreate 'ADODB.Stream' , #objStream OUT
IF #HR <> 0 GOTO CLEANUP
SELECT #strErrorMessage='Opening ADO stream'
EXEC #HR = sp_OAMethod #objStream, 'Open'
IF #HR <> 0 GOTO CLEANUP
SELECT #strErrorMessage='Declaring ADO stream type binary'
EXEC #HR = sp_OASetProperty #objStream, 'Type', 1
IF #HR <> 0 GOTO CLEANUP
SELECT #strErrorMessage='Opening File into ADO stream'
EXEC #HR = sp_OAMethod #objStream, 'LoadFromFile', NULL , #FilePath
IF #HR <> 0 GOTO CLEANUP
SELECT #offset = 73828 --9 * 8192 + 96 + 4
SELECT #strErrorMessage='Setting offset to read file'
EXEC #HR = sp_OASetProperty #objStream, 'Position', #offset
IF #HR <> 0 GOTO CLEANUP
SELECT #strErrorMessage = 'Reading Version number from mdf'
EXEC #HR = sp_OAMethod #objstream, 'Read', #binary1 OUT , 1
IF #HR <> 0 GOTO CLEANUP
SELECT #strErrorMessage = 'Reading Version number from mdf'
EXEC #HR = sp_OAMethod #objstream, 'Read', #binary2 OUT , 1
IF #HR <> 0 GOTO CLEANUP
SELECT #binary1 + 256 * #binary2 AS DatabaseVersion
END_ROUTINE:
RETURN
CLEANUP:
BEGIN
EXEC sp_OAGetErrorInfo #objStream,
#Source OUT,
#Desc OUT;
SELECT ErrorInSP='spDBVersion',
HR = convert(varbinary(4),#HR),
Source=#Source,
Description=#Desc,
Process=#strErrorMessage
GOTO END_ROUTINE
END
GO
This Stored Procedure Requires OLE Automation, so you'll want to wrap it in some reconfigure statements assuming OLE is disabled (as it probably should be).
EXEC sp_configure 'show advanced options', 1
RECONFIGURE
GO
EXEC sp_configure 'Ole Automation Procedures', 1
RECONFIGURE
GO
exec spDBVersion 'E:\SQLData\somedatabase.mdf'
GO
EXEC sp_configure 'Ole Automation Procedures', 0
RECONFIGURE
GO
EXEC sp_configure 'show advanced options', 0
RECONFIGURE
GO
How to call a stored procedure from a user defined function in SQL Server 2000
Either you need to modify your stored procedure to be a user defined function or the other way around.
One crude way to achieve what you are looking for is to have your exec statement in a batch script and call that batch script from your function. Something like this:
create function <functionName>
exec master.sys.xp_cmpshell 'C:\storedProc.bat'
....
....
return #return
end
More on xp_cmpshell on MSDN.
You can't call regular stored procs from functions - only other functions or some extended stored procedures. See here for the BOL article (from SQL 2005). Attempting to call a standard stored proc from a UDF will result in the following error...
Msg 557, Level 16, State 2, Line 1
Only functions and some extended stored procedures can be executed from within a function.
I recently had a similar issue. In fact the error message is not properly formatted since sp_executesql is an extended stored procedure as you can check by the following script:
select objectproperty(object_id('sp_executesql'),'IsExtendedProc')
returns 1
Since we can’t use sp_executesql even it’s an XP, I had to find another workaround by using sp_OAMethod. My scenario was how to find the number of rows dynamically in a table according to some criteria (null values in my scenario). Using sp_OAMethod I built the following function:
IF object_id(N'dbo.fc_ContaRegistros_x_Criterio') is not null DROP FUNCTION [dbo].[fc_ContaRegistros_x_Criterio]
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
CREATE FUNCTION dbo.fc_ContaRegistros_x_Criterio (
#str_TBName VARCHAR(100),
#str_Criter VARCHAR(500)
)
RETURNS BIGINT
AS
BEGIN
-- Objetivo : Contar numero de registros de uma determinada tabela de acordo com o critério passado
-- Criação : Josué Monteiro Viana - 09/07/09
/*
Exemplo:
DECLARE #count INT
SET #count = dbo.fc_ContaRegistros_x_Criterio('master.dbo.sysobjects', '')
PRINT #count
SET #count = dbo.fc_ContaRegistros_x_Criterio('crk.dbo.acao', 'where cod_acao is null')
PRINT #count
*/
DECLARE
#int_objSQL INT,
#int_erros INT,
#int_objSelectCountResult INT,
#bint_SelectCount BIGINT,
#sql NVARCHAR(2000)
EXEC #int_erros = sp_OACreate 'SQLDMO.SQLServer', #int_objSQL OUTPUT
EXEC #int_erros = sp_OASetProperty #int_objSQL, 'LoginSecure', TRUE
EXEC #int_erros = sp_OAMethod #int_objSQL, 'Connect', null, '.'
--SET #sql = 'SELECT count(*) FROM ' + #str_TBName + ' WHERE ' + #str_Criter
SET #sql = 'SELECT count(*) FROM ' + #str_TBName + ' ' + #str_Criter
SET #sql = 'ExecuteWithResults("' + #sql + '")'
EXEC #int_erros = sp_OAMethod #int_objSQL, #sql, #int_objSelectCountResult OUTPUT
EXEC #int_erros = sp_OAMethod #int_objSelectCountResult, 'GetRangeString(1, 1)', #bint_SelectCount OUT
EXEC #int_erros = sp_OADestroy #int_objSQL
-- debug info: not valid inside a fc
--if #int_erros <> 0 EXEC sp_OAGetErrorInfo #int_objSQL else print 'ok'
if #int_erros <> 0 SET #bint_SelectCount = #int_erros
RETURN #bint_SelectCount
END
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
I know your case is a little different, but I’m sure you can use this udf as a guideline to help you.
Best wishes,
Josue Monteiro Viana