SQL procedure executes first line then stops - sql-server

I am running an SQL procedure that has to update a certain table. When I run the procedure it says successfully completed, yet the records are not updated when I try to debug it, it run only the line SET ANSI ON, then it gives the successful message. I am using SQL server 2012
Am I missing something, is there anything I need to add? See my code here:
USE [CADDe_ProdCopy]
GO
/****** Object: StoredProcedure [dbo].[sp_sms_X203] Script Date: 2015/09/03 08:28:15 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[sp_sms_X203]
as
declare #lCount int
set #lCount = (select count(*) from tbl_X203_SMS where SMSSent = 0 )
if #lCount > 0
begin
DECLARE #cSMSeMail varchar(100)
declare #cSMSType varchar(10)
declare #cSMSSent int
declare #cRA varchar(10)
declare #cWizard varchar(7)
declare #cCName varchar(26)
declare #cContact varchar(30)
declare #cUsed_KM int
declare #cAmount_Due decimal(18, 2)
declare #cSMSMessage varchar(160)
declare #cvblf varchar(1)
declare #cCheckInDt datetime
declare #cCheckOutDt datetime
declare #err int
set #cvblf = '|'
declare lcursor CURSOR FOR
Select SMSType, RA, CName, Contact, Used_KM, Amount_Due, eMail, [CheckInDateTime] ,[CheckOutDateTime]
From tbl_X203_SMS WHERE SMSSent = 0
open lcursor
fetch next from lcursor into #cSMSType, #cRA, #cCName, #cContact, #cUsed_KM, #cAmount_Due, #cSMSeMail, #cCheckInDt, #cCheckOutDt
while ##FETCH_STATUS = 0
begin
--SET #cContact = '+27834115771'
--SET #cSMSeMail = 'amangelsdorf#avis.co.za'
-- Check that the date of the checkin is within same day
if rtrim(ltrim(#cSMSType)) = 'CheckIn'
begin
if datediff(day,#cCheckInDt,getdate()) = 0
begin
SET #cSMSMessage = left('Thank you '+ #cCName +' for renting with AVIS.',160)
SET #cSMSMessage = left( #cSMSMessage + ' RA#' + #cRA + 'Retrieve your invoice at http://www.avis.co.za/inv' ,160)
--if #cAmount_Due > 0
-- SET #cSMSMessage = left( #cSMSMessage + #cvbLf + 'AMT:R ' + cast(#cAmount_Due as varchar),160)
exec sp_sms_xml_post #cContact, #cSMSMessage, #cSMSeMail
end
end
-- Check that the date of the checkout is within same day
if rtrim(ltrim(#cSMSType)) = 'CheckOut'
begin
if datediff(day,#cCheckOutDt,getdate()) = 0
begin
--SET #cSMSMessage = left( 'Thank you for choosing AVIS.' + #cvbLf + 'For any assistance contact the AVIS Careline on Tel: 0800001669' ,160)
SET #cSMSMessage = left( 'Thank you for choosing AVIS. ' + #cvbLf + 'Kindly contact 0800001669 for any roadside or emergency assistance.' ,160)
exec sp_sms_xml_post #cContact, #cSMSMessage, #cSMSeMail
end
end
set #err = ##error
if #err = 0
begin
--print 'no error'
update tbl_X203_SMS set SMSSent = 1 where SMSType = #cSMSType and RA = #cRA
end
fetch next from lcursor into #cSMSType, #cRA, #cCName, #cContact, #cUsed_KM, #cAmount_Due, #cSMSeMail, #cCheckInDt, #cCheckOutDt
end
close lcursor
deallocate lcursor
end
`

You check your count value in
set #lCount = (select count(*) from tbl_X203_SMS where SMSSent = 0 )
if #lCount > 0
because may be you are getting value as 0 so it is not going inside the if condition, you can use print(#lCount ) before if and execute stored procedure from sql server.

The code that you have shown is the code to create / alter a stored procedure and won't execute it, hence the Successfully Compiled response.
In order to execute this procedure you will need to use the exec statement:
exec [dbo].[sp_sms_X203]

Related

Will all transaction in this stored procedure be rolled back

I have created a stored procedure (shown below) in SQL Server and tried to include a Rollback Transaction as I need to have a "stored procedure that has a transaction around it, so that if/when it fails all inserts will be rolled back."
I am unsure if this work or not, or will work, I cannot test yet as only developing locally, but wondered if someone wouldn't mind looking over the Rollback Transaction part of the stored procedure and advise if on the right path?
USE AutomatedTesting
GO
ALTER PROCEDURE [dbo].[spInsertTestCases]
(#AddedTFS INT,
#Scenario NVARCHAR(500),
#TargetTableName NVARCHAR(100),
#TargetFieldName NVARCHAR(100),
#ExpectedResult NVARCHAR(100),
#TargetTableDBName NVARCHAR(100),
#TargetTableSchema NVARCHAR(100),
#TargetFieldIsDateTime NVARCHAR(1),
#TestCaseIdentifiers dbo.TestCaseIdentifiers READONLY ) -- can only be READONLY. meaning you cannot amend the param
/* #TestCaseIdentifiers var will be prepopulated
TestDataIdentifiersNEW is a custom data type which has fields (TestSequence ColumnName ColumnValue IsAlphaNumeric)
so stored procedure is called like:
EXEC [dbo].[spTest_UserDefinedDatatype] 'param1','param2' #temp_testdata
#temp_testdata will already be defined and popualted(INSERT INTO ) before exec to add in 1 to many rows.
for example:
ColumnName ColumnValue
PATIENTID 123456
SOURCESYS PAS
in simple terms above EXEC is:
EXEC [dbo].[spTest_UserDefinedDatatype] 'param1','param2' 'PATIENTID 123456'
'SOURCESYS PAS'
*/
AS
BEGIN TRY
BEGIN TRANSACTION
BEGIN
--DECLARE #TableNameUpdate SYSNAME = #TargetTableName
--DECLARE #CDI SYSNAME = REPLACE(#TargetTableName,'CDO','CDI') -- so if targettablename param is CDO then swap it to CDI. why?
DECLARE #sql VARCHAR(MAX) = ' INSERT INTO [dbo].[TestCasesIdentifier] ([TestCaseId], [TestCaseSequence], [FieldName], [FieldValue], [AlphaNumeric]) VALUES '
DECLARE #i INT = 1
DECLARE #TableNameUpdate SYSNAME = #TargetTableName
DECLARE #CDI SYSNAME = REPLACE(#TargetTableName,'CDO','CDI')
DECLARE #ColName SYSNAME
DECLARE #Ret NVARCHAR(256)
DECLARE #sql2 NVARCHAR(MAX)
DECLARE #TestCaseID INT = -1 --does this need default variable?
DECLARE #ErrorCode INT = ##error
DECLARE #TestSequence INT
DECLARE #ColumnName VARCHAR(100)
DECLARE #ColumnValue VARCHAR(100)
DECLARE #IsAlphaNumeric BIT
DECLARE #TableTestSequence INT = ISNULL((SELECT MAX([TableTestSequence]) + 1 FROM TestCases WHERE #TargetTableName = [TargetTableName]), 1)
-- INSERT into TestCases. 1 record
-- An assumption that a number of fields will have defaults on them - if not, extra fields will need adding
INSERT INTO [dbo].[TestCases] ([AddedTFS], [TableTestSequence], [Scenario],
[TargetTableName], [TargetFieldName], [ExpectedResult],
[TargetTableDBName], [TargetTableSchema], [TargetFieldIsDateTime])
VALUES (#AddedTFS, -- AddedTFS (The TFS Number of the Development carried out)
ISNULL((SELECT MAX([TableTestSequence]) + 1 -- TableTestSequence (Generates the next Sequence Number for a Table)
FROM TestCases -- if table doesnt exist in TestCases then sets to 1
WHERE #TargetTableName = [TargetTableName]), 1),
#Scenario, -- Scenario (A description of the scenario use GIVEN and WHERE)
#TargetTableName, -- TargetTableName (References the Target Table entered at the top of this SQL - SET #TableName = 'CDO_APC_ELECTIVE_ADMISSION_LIST')
#TargetFieldName, -- TargetFieldName (The Field in which we want to test)
#ExpectedResult, -- ExpectedResult (The expected output/result of the field in which we want to test)
#TargetTableDBName, -- The DB to be used
#TargetTableSchema, -- the schema to be used
#TargetFieldIsDateTime) ---- 1 = Yes, 0 = No (Is Target field a datetime field)
-- Grab the identity value just generated by the last statement and the last error code generated
-- in order to reference TestCases PK when adding to TestCaseIdentifiers
SELECT #TestCaseID = SCOPE_IDENTITY(), #ErrorCode = ##error
IF #ErrorCode = 0 --OR #TestCaseID <> -1 -- #ErrorCode <> 0 if error then back out testcases INSERT? surely should use BEGIN/ROLLBACK tran
--IF #ErrorCode = 0 OR #TestCaseID <> -1
-- If there was no error creating the TestCase record, create the records for the WHERE clause
BEGIN
/*
rollback insert if no matching records
rollback insert if SQL returns more than 1 record
return error message to user
*/
SELECT
ic.index_column_id, c.name
INTO #tmp
FROM sys.indexes i
JOIN sys.index_columns ic ON i.object_id = ic.object_id
AND i.index_id = ic.index_id
JOIN sys.columns c ON c.column_id = ic.column_id
AND c.object_id = ic.object_id
JOIN sys.tables t ON c.object_id = t.object_id
WHERE t.name = #CDI
AND i.is_primary_key = 1
IF (SELECT COUNT(*) FROM #TestCaseIdentifiers) = 0
--IF #PKValues IS NULL
BEGIN
WHILE #i <= (SELECT COUNT(*) FROM #tmp)
BEGIN
SELECT #ColName = [name]
FROM #tmp
WHERE index_column_id = #i
-- if #expectedvalue IS NULL
SET #sql2 = 'SELECT TOP 1 #RetvalOut = ' + QUOTENAME(#ColName) + ' FROM ' + QUOTENAME(#CDI) + ' ORDER BY NEWID()'
-- else
-- SET #sql2 = ''
EXECUTE sp_executesql #command = #sql2, #ParmDefinition = N'#RetvalOut NVARCHAR(MAX) OUTPUT', #retvalOut = #Ret OUTPUT
SET #sql += '(' + CONVERT(VARCHAR(100),#TestCaseID) + ',' + CONVERT(VARCHAR(10),#i) + ',''' + #ColName + ''',''' + #Ret + ''',1),'
SET #i+=1
SELECT #sql = REVERSE(SUBSTRING(REVERSE(#sql),2,8000))
PRINT #sql
EXEC #sql
END
END
ELSE
BEGIN
--PRINT 'got here'
DECLARE csr_TestCaseIdentifierInsert CURSOR FOR
SELECT [TestSequence],[ColumnName],[ColumnValue],[IsAlphaNumeric]
FROM #TestCaseIdentifiers
ORDER BY [TestSequence]
OPEN csr_TestCaseIdentifierInsert
FETCH NEXT FROM csr_TestCaseIdentifierInsert INTO #TestSequence, #ColumnName, #ColumnValue, #IsAlphaNumeric
WHILE ##fetch_status = 0
BEGIN
INSERT INTO [dbo].[TestCasesIdentifier]
([TestCaseId],
[TestCaseSequence],
[FieldName],
[FieldValue],
[AlphaNumeric])
VALUES
(#TestCaseID, #TestSequence, #ColumnName, #ColumnValue,#IsAlphaNumeric)
FETCH NEXT FROM csr_TestCaseIdentifierInsert INTO #TestSequence, #ColumnName, #ColumnValue, #IsAlphaNumeric
END
CLOSE csr_TestCaseIdentifierInsert
DEALLOCATE csr_TestCaseIdentifierInsert
END -- loop to add records to testcasesidentifier
END
END
COMMIT
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRAN
DECLARE #ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE()
DECLARE #ErrorSeverity INT = ERROR_SEVERITY()
DECLARE #ErrorState INT = ERROR_STATE()
-- Use RAISERROR inside the CATCH block to return error
-- information about the original error that caused
-- execution to jump to the CATCH block.
RAISERROR (#ErrorMessage, #ErrorSeverity, #ErrorState);
END CATCH
You are almost there. I usually wrap the stored proc code within a BEGIN..END block as well, then next comes the most important part: you must add SET XACT_ABORT ON; before your TRY..CATCH and BEGIN TRAN, as SQL Server defaults the XACT_ABORT to OFF. Otherwise not everything will be rolled back.
Example setup:
CREATE PROCEDURE dbo.uspMyTestProc
AS
BEGIN
SET NOCOUNT, XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
-- Do your magic stuff here before committing...
COMMIT;
END TRY
BEGIN CATCH
IF ##trancount > 0
ROLLBACK TRANSACTION;
-- Add extra error logging here if you want...
END CATCH;
END;
GO
Also, if you want to add a possible stacktrace if you are using nested procedures et cetera you might want to consider using a generic error handler à la Erland Sommerskog. We adapted this approach completely. See for more details How to handle Transaction in Nested procedure in SQL server?

Pass a table name to stored procedure as a parameter

This is my stored procedure, this is working:
/*
#CRM_Ref - This is the only required input port for this SP
#North_Ref - Output port, returns the North_Ref for a valid CRM_Ref input
#Output_Message - Output port, Returns the output of the SP, either 'Success' or 'North_Ref not found'
*/
ALTER PROCEDURE [dbo].sp_Get_North_Reference
#CRM_Ref NVARCHAR(255),
#North_Ref VARCHAR(255) OUTPUT,
#Output_Message VARCHAR(255) OUTPUT
AS
DECLARE #var_North_Ref VARCHAR(255); -- Variable used to store the North_Ref
DECLARE #var_Output_Message VARCHAR(255); -- Variable to carry the Output_Message
DECLARE #COUNTER INT; -- Counter for the amount of times the while loop should run
SET #COUNTER = 100;
-- Loop will run 10 times with a 10 second delay between each loop
WHILE #COUNTER >= 1
BEGIN
SET #var_North_Ref = (SELECT TOP 1 North_Ref FROM DEV.dbo.Address__ADDRESS WHERE CRM_Ref = #CRM_Ref ORDER BY PUBLICATION_INSTANCE_DATE DESC)
IF #var_North_Ref IS NULL
BEGIN
SET #COUNTER = #COUNTER - 1; -- Counter is decremented by 1
SET #var_Output_Message = 'North_Ref not found';
WAITFOR DELAY '00:00:10'; -- Wait is triggered if no North_Ref is found
END
ELSE
BEGIN
SET #COUNTER = 0; -- Counter is set to 0 to end the while loop
SET #var_Output_Message = 'Success';
END
END
SET #Output_Message = #var_Output_Message; -- Format Output_Message
SET #North_Ref = #var_North_Ref; -- Format North_Ref
;
GO
I would like to add another parameter into this stored procedure (#TableName VARCHAR(255)) which I want to pass to the SELECT statement.
So I would like something like:
SELECT TOP 1 North_Ref
FROM #Table_Name
WHERE CRM_Ref = #CRM_Ref
ORDER BY PUBLICATION_INSTANCE_DATE DESC
I have tried doing this as it is above but I am getting errors as I don't think you can use parameters as a table name in stored procedures
how about try this concept :
CREATE TABLE #tempTable(abc int);
declare #strSQL nvarchar(255)
SET #strSQL = 'insert into #tempTable select 123'
EXEC sp_executesql #strSQL
declare #abc int
select top 1 #abc = abc from #tempTable
drop table #tempTable
select #abc

Searching for records in Microsoft SQL Server

Is it possible to search for records in Microsoft SQL Server manager?
I mean something like in VS pressing Ctrl-F and searching by word?
There is no way in searching for objects in SQL Server management studio. There are views and tables that can be used to search for objects like:
SELECT * FROM sys.tables WHERE name LIKE '%...%'
To search for tables.
If you mean, searching for data within a table, I need to learn T-SQL.
There are non-free 3rd party tools (ex. Redgate's SQL Search), but I use the proc below. You have to create the proc in every [master] database so it's available server-wide, but you can pass in a search term and an optional database name (otherwise it searches through object definitions in all databases):
USE [master]
GO
/****** Object: StoredProcedure [dbo].[sp_FindTextOnServer] Script Date: 10/6/2017 3:39:19 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/*-----------------------------------------------------------
This procedure will search all or a specified database objects
for the supplied text and return a table with the values
Examples:
EXEC sp_FindTextOnServer 'Billing', 'fcsCore'
EXEC sp_FindTextOnServer 'vJurisdiction'
-----------------------------------------------------------*/
ALTER PROCEDURE [dbo].[sp_FindTextOnServer]
#text VARCHAR(40),
#searchDB VARCHAR(100) = NULL
AS
DECLARE #DisplayText VARCHAR(100),
#sSql VARCHAR(1000),
#line VARCHAR(300),
#char CHAR,
#lineNo INTEGER,
#counter INTEGER,
#AddedRecord BIT,
#dbObjectType VARCHAR(100),
#dbObject VARCHAR(100),
#ObjectBody VARCHAR(7000),
#dbName VARCHAR(100)
SET #DisplayText = #Text
SET #text = '%' + #text + '%'
SET #AddedRecord = 0
CREATE TABLE #SearchResults
(
DBName VARCHAR(100) NOT NULL,
ObjectType VARCHAR(100) NOT NULL,
ObjectName VARCHAR(100) NOT NULL,
Line INT NOT NULL,
Reference VARCHAR(7000) NOT NULL
)
CREATE TABLE #tempSysComments
(
DBName VARCHAR(100),
DBObjectType VARCHAR(100),
DBObject VARCHAR(100),
TextString text
)
--Populate a table with the search results from all databases on the server that include the searched text
SET #sSql = 'USE [?]
SELECT
''?'' AS DBName,
LOWER(REPLACE(o.type_desc, ''_'', '' '')) + '' ('' + RTRIM(type) + '')'' AS DBObjectType,
OBJECT_NAME(sm.object_id) AS DBObject,
CAST(sm.definition AS VARCHAR(7000)) AS TextString
FROM sys.sql_modules AS sm
JOIN sys.objects AS o
ON sm.object_id = o.object_id
WHERE CAST(sm.definition AS VARCHAR(7000)) LIKE ''' + #text + '''
ORDER BY o.type_desc, OBJECT_NAME(sm.object_id)'
IF (#searchDB IS NULL)
BEGIN
INSERT INTO #tempSysComments
EXEC sp_MSFOREachDB #sSql
END
ELSE
BEGIN
SET #sSql = REPLACE(#sSql, '?', #searchDB)
INSERT INTO #tempSysComments
EXEC (#sSql)
END
DECLARE codeCursor CURSOR
FOR
SELECT DBName, DBObjectType, DBObject, TextString
FROM #tempSysComments
WHERE DBName IS NOT NULL
OPEN codeCursor
FETCH NEXT FROM codeCursor INTO #dbName, #dbObjectType, #dbObject, #ObjectBody
IF ##FETCH_STATUS <> 0
BEGIN
PRINT 'Text ''' + #DisplayText + ''' was not found in objects on server ' + ##SERVERNAME
-- Close and release code cursor.
CLOSE codeCursor
DEALLOCATE codeCursor
RETURN
END
-- Search each object within code cursor.
WHILE ##FETCH_STATUS = 0
BEGIN
SET #lineNo = 0
SET #counter = 1
-- Process each line.
WHILE (#counter <> Len(#ObjectBody))
BEGIN
SET #char = SUBSTRING(#ObjectBody, #counter,1)
-- Check for line breaks.
IF (#char = CHAR(13))
BEGIN
SET #lineNo = #lineNo + 1
-- Check if we found the specified text.
IF (PATINDEX(#text, #line) <> 0)
BEGIN
SET #AddedRecord = 1
INSERT #SearchResults
SELECT #dbName, #dbObjectType, #dbObject, #lineNo, LEFT(RTRIM(LTRIM(#line)), 7000)
END
SET #line = ''
END
SET #line = #line + #char
SET #counter = #counter + 1
END
IF (#AddedRecord = 0)
INSERT #SearchResults
SELECT #dbName, #dbObjectType, #dbObject, 0, SUBSTRING(#ObjectBody, 1, 7000)
SET #AddedRecord = 0
FETCH NEXT FROM codeCursor INTO #dbName, #dbObjectType, #dbObject, #ObjectBody
END
-- Close and release cursor.
CLOSE codeCursor
DEALLOCATE codeCursor
-- Return the info.
SELECT DISTINCT DBName, ObjectType, ObjectName, Line, RTRIM(LTRIM(REPLACE(REPLACE(Reference, CHAR(9), ' '), CHAR(13)+CHAR(10), ' '))) AS Reference
FROM #SearchResults
ORDER BY DBName, ObjectType, ObjectName, Line
-- Cleanup.
DROP TABLE #SearchResults
DROP TABLE #tempSysComments
RETURN

MSSQL stored procedure call from ADO - not running properly

I have sp in MSSQL server - code below. When I run it from job, or SSMS it runs OK. But I need to run it from VB6 app with ADODB.
My VB6 code:
Dim cmd As New ADODB.Command
cmd.ActiveConnection = CNN
cmd.CommandTimeout = 180
cmd.CommandText = "dbbackup"
cmd.CommandType = ADODB.CommandTypeEnum.adCmdStoredProc
cmd.Execute(, , ADODB.ConnectOptionEnum.adAsyncConnect)
Problem is: When database backup is almost done - about 90+%, cmd.State changes from Executing to Closed and VB6 code continue in executing (to this moment it waits for sp to complete). But there is a lot of code after backup which never run this way(old backup delete,...). I realized that “Last database backup” property on MSSQL database was not set and in table msdb.dbo.backupset there are no rows for my backup. But there si good restorable backup on HDD.
When i stops program for 5 minutes in debug, sp runs properly to end and everything is OK. This backup code is last code in app run and after it ends program closes all connections and exits. I added wait to VB6 code and it helps on some servers, but many other servers still has same problem.
I think main question is why MSSQL server returns control flow to VB6 code and sp is not completed yet.
Thanks
sp code:
PROCEDURE [dbo].[dbBackup]
AS
BEGIN
SET NOCOUNT ON;
EXEC sp_configure 'show advanced options', 1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell', 1
RECONFIGURE
If OBJECT_ID('tempdb..#DBName','u') IS NULL
Create Table #DBName
(
ID int identity (1,1) ,
Name varchar(128) not null ,
RetentionPeriod int null,
BackupPath varchar(255) default(''),
DBSize float default(0)
)
If OBJECT_ID('tempdb..#ExistingBackups', 'u') IS NULL
Create Table #ExistingBackups
(
Name varchar(128) ,
ID int identity (1,1)
)
Declare #Path varchar(255)
Declare #sql varchar(1000)
Declare #Name varchar(128)
Declare #RetentionPeriod int
Declare #LastBackupToKeep varchar(8)
Declare #ID int
Declare #MaxID int
Declare #eName varchar(255)
Declare #eMaxID int
Declare #eID int
Declare #eTimeStamp varchar(20)
Declare #errMsg nvarchar(2048)
Declare #errCount int; set #errCount = 0;
Declare #freeSpace bigint
Declare #pageSize float
Declare #dbSize bigint
Declare #procDate datetime
Declare #Sklad char(3)
Declare #backupName as varchar(255)
Select #pageSize = v.low / 1024 From master..spt_values v (noLock) Where v.number = 1 And v.[type] = 'E'
Select Top 1 #sklad = sklad_id From dbo.pohyb (noLock) Where Convert(int, sklad_id) > 500
Set #procDate = GETDATE()
Truncate Table #DBName
Insert Into #DBName (Name, RetentionPeriod, BackupPath)
Select DBName, BackupsToStore, BackupPath
From dbo.databaseBackup (noLock)
Where runBackup = 1
Select #MaxID = max(ID), #ID = 0 From #DBName
While #ID < #MaxID
Begin
Select #ID = min(ID) From #DBName Where ID > #ID
Select #Name = Name, #RetentionPeriod = RetentionPeriod, #Path = BackupPath
From #DBName
Where ID = #ID
If SUBSTRING(#Path, Len(#Path), 1) <> '\' Set #Path = #Path + '\'
Set #sql = 'Update #DBName Set DBSize= (Select Round(Sum(size) *' + CONVERT(varchar, #pageSize) + '/1024, 0) From ' + #Name + '.dbo.sysfiles (noLock)) Where Name = ''' + #Name + ''''
Exec (#sql)
Select #dbSize = DBSize From #DBName
--Exec #freeSpace = dbo.getDiskFreeSpace #drive = #Path
--If #freeSpace > #dbSize
--Begin
Set #eTimeStamp = REPLACE(REPLACE(CONVERT(varchar, #procDate, 113), ' ', '_'), ':', '-')
Set #sql = #Path + #Name + '_' + #eTimeStamp + '.bak'
Set #errMsg = 'OK'
Begin Try
SET #backupName = 'Objednavky backup by job ' + CONVERT(varchar, GETDATE(), 104) + ' ' + CONVERT(varchar, GETDATE(), 108);
Backup Database #Name To Disk = #sql
WITH NAME = #backupName;
-------mazanie backupu begin
Truncate Table #ExistingBackups
Set #sql = 'dir /B /OD ' + #Path + #Name + '_*.bak'
Insert #ExistingBackups Exec master..xp_cmdshell #sql
If Exists (Select 1 From #ExistingBackups Where PATINDEX('%File Not Found%', Name) > 0)
Truncate Table #ExistingBackups
Delete From #ExistingBackups Where Name IS NULL
Select #eID = 0
Select #eMaxID = Max(ID) - #RetentionPeriod From #ExistingBackups
While #eID < #eMaxID
Begin
Select #eID = Min(ID) From #ExistingBackups Where ID > #eID
Select #eName = Name From #ExistingBackups Where ID = #eID
Set #sql = 'del ' + #Path + #eName
Exec master..xp_cmdshell #sql
End
Truncate Table #ExistingBackups
-------mazanie backupu end
End Try
Begin Catch
Set #errMsg = #errMsg + '||' + CONVERT(varchar,ERROR_MESSAGE())
Set #errCount = #errCount + 1;
End Catch
--End
--Else
--Set #errMsg = 'Pln? disk (Vo?n? miesto: ' + CONVERT(varchar, #freeSpace) + ' MB, potrebn? aspo?: ' + CONVERT(varchar, #dbSize) + ' MB)'
Insert Into [dbo].[databaseBackup_log] ([Sklad_id], [DBName], [BackupDate], [Status]) Values (#Sklad, #Name, #procDate, Ltrim(Rtrim(CONVERT(varchar,#errMsg))))
End
Drop Table #DBName
Drop Table #ExistingBackups
IF #errCount > 0 BEGIN
RAISERROR (#errMsg, 16, 2) WITH SETERROR
END
RETURN 0;
END

How to encrypt all existing stored procedures of a database

Is there any possibility to encrypt all existing stored procedures of a SQL Server 2008 database AFTER they have been created via an SQLCMD script?
The reason I want to do this is the following:
I'd like to develop the stored procedures without encryption so I can easily click on "Modify" in SQL Server Management Studio to check their contents.
However, for the deployment I'd like to encrypt them so I thought that maybe I could write a script which encrypts them only after they're created. For dev systems I simply wouldn't run the script while on end-user systems the script would be run.
You might want to check Encrypting all the Stored Procedures of a Database :
If you ever decide that you need to protect your SQL Stored
Procedures, and thought encrypting was a good idea, BE VERY CAREFUL!!!
Encrypting Database stored procedures SHOULD NOT be done without
having backup files or some sort of Source Control for the stored
procedures. The reason I say this is because, once they are encrypted,
there is no turning around. (Yes, there are third party tools that
will decrypt your code, but Why go through that trouble.)
This trick is something I developed because my company needed to host the application on a different server, and we were concerned
about our code being compromised. So, to deliver the database, we
decided to encrypt all out stored procedures. Having over a hundred
procedures written, I didn't want to open each procedure and paste
'WITH ENCRYPTION' in each and every stored procedure. (For those of
you who do not know how to encrypt, refer How Do I Protect My Stored
Procedure Code[^]). So I decided to make my own little C# application
that did the same.
This application is a console application made
using Visual Studio 2005 and SQL server 2005. The input parameters are
database name, Server address, database username and password. Once
you are able to provide these details, you are ready to have all your
stored procedures encrypted.
I have put the code of my application
here as is. For this code to work, you will need to add an
"Microsft.SQlserver.SMO" refrence to the application, so that the
classes such as "Database" and "StoredProcedure" are accessible.
BEFORE YOU DO THIS, TAKE A BACKUP!!!!!!!
//Connect to the local, default instance of SQL Server.
string DB = "";
ServerConnection objServerCOnnection = new ServerConnection();
objServerCOnnection.LoginSecure = false;
Console.WriteLine("Enter name or IP Address of the Database Server.");
objServerCOnnection.ServerInstance = Console.ReadLine();
Console.WriteLine("Enter name of the Database");
DB = Console.ReadLine();
Console.WriteLine("Enter user id");
objServerCOnnection.Login = Console.ReadLine();
Console.WriteLine("Enter Password");
objServerCOnnection.Password = Console.ReadLine();
Console.WriteLine(" ");
Server srv = new Server();
try // Check to see if server connection details are ok.
{
srv = new Server(objServerCOnnection);
if (srv == null)
{
Console.WriteLine("Server details entered are wrong,"
+ " Please restart the application");
Console.ReadLine();
System.Environment.Exit(System.Environment.ExitCode);
}
}
catch
{
Console.WriteLine("Server details entered are wrong,"
+ " Please restart the application");
Console.ReadLine();
System.Environment.Exit(System.Environment.ExitCode);
}
Database db = new Database();
try // Check to see if database exists.
{
db = srv.Databases[DB];
if (db == null)
{
Console.WriteLine("Database does not exist on the current server,"
+ " Please restart the application");
Console.ReadLine();
System.Environment.Exit(System.Environment.ExitCode);
}
}
catch
{
Console.WriteLine("Database does not exist on the current server,"
+ " Please restart the application");
Console.ReadLine();
System.Environment.Exit(System.Environment.ExitCode);
}
string allSP = "";
for (int i = 0; i < db.StoredProcedures.Count; i++)
{
//Define a StoredProcedure object variable by supplying the parent database
//and name arguments in the constructor.
StoredProcedure sp;
sp = new StoredProcedure();
sp = db.StoredProcedures[i];
if (!sp.IsSystemObject)// Exclude System stored procedures
{
if (!sp.IsEncrypted) // Exclude already encrypted stored procedures
{
string text = "";// = sp.TextBody;
sp.TextMode = false;
sp.IsEncrypted = true;
sp.TextMode = true;
sp.Alter();
Console.WriteLine(sp.Name); // display name of the encrypted SP.
sp = null;
text = null;
}
}
}
I have the same problem.
My solution is to put "-- WITH ENCRYPTION" in all of my stored procedures. This version is used by developers and stored in source control.
I then use a tool (like sed) in my build to replace "-- WITH ENCRYPTION" with "WITH ENCRYPTION" on the files before I send them to be installed.
For a pure SQL solution you could use REPLACE.
WITH ENCRYPTION means that the code behind the proc is not stored in the SysComments table.
You could write a script that does a exec sp_helptext 'MyProcName' and gets the contents into a VarChar (MAX) so it can hold multiline / large procedures easily and then modifiy the procedure from it's original state
CREATE MyProcName AS
SELECT SecretColumns From TopSecretTable
change CREATE to ALTER and AS surrounded by space or tab or newline (good place to use Regular Expressions) to WITH ENCRYPTION AS
ALTER MyProcName WITH ENCRYPTION AS
SELECT SecretColumns From TopSecretTable
This will hide all code for the stored proc on the production server.
You can put this in a LOOP or a CURSOR (not really a set based operation IMHO) for all objects of a specific type and/or naming convention that you want to encrypt, and run it every time you deploy.
I would recommend creating the sproc in a multi-line string variable and then inserting or altering it using sp_executesql. The only annoying downside to this approach is doubling of single quotes for strings.
DECLARE #action varchar(max);
SET #action = 'CREATE'; /* or "ALTER" */
DECLARE #withEncryption varchar(max);
SET #withEncryption = ''; /* or "WITH ENCRYPTION" */
DECLARE #sql varchar(max);
SET #sql = #action + ' PROCEDURE dbo.Something'
(
....
) ' + #withEncryption +
' AS
BEGIN
DECLARE #bob varchar(10);
SET #bob = ''Bob'';
....
END;
';
EXEC sp_executesql #statement = #sql;
[Note the whitespace around the variables.]
All of my scripts use this method, which works well once you get used to the quote doubling thing.
I also use a batch file to call the script, and SQLCMD-mode command line variables to select various behaviours, which makes it repeatable and easy to test.
Use This Query which Encrypt All Procedures in database
CREATE TABLE #backup
(
id BIGINT IDENTITY(1, 1),
sptext NVARCHAR(MAX) NOT NULL,
spname NVARCHAR(100) NOT NULL,
encrypttext NVARCHAR(MAX) NULL,
encryptstatus BIT NOT NULL
DEFAULT ( 0 )
)
DECLARE #sptexttable TABLE
(
id BIGINT IDENTITY(1, 1),
sptext NVARCHAR(MAX),
spname NVARCHAR(100)
)
INSERT INTO #sptexttable ( sptext, spname )
SELECT [text],
[name]
FROM syscomments
JOIN sysobjects ON syscomments.id = sysobjects.id
AND sysobjects.xtype = 'p'
DECLARE #sptext NVARCHAR(MAX)
DECLARE #spname NVARCHAR(100)
DECLARE #counter INT
SET #counter = 1
WHILE #counter <= ( SELECT MAX(id)
FROM #sptexttable
)
BEGIN
BEGIN TRY
INSERT INTO #backup ( sptext, spname )
SELECT sptext,
spname
FROM #sptexttable
WHERE id = #counter
END TRY
BEGIN CATCH
END CATCH
IF NOT EXISTS ( SELECT [name]
FROM sysobjects
WHERE [name] = 'ce_LastIndexOf'
AND xtype = 'FN' )
BEGIN
EXEC
( 'CREATE FUNCTION ce_LastIndexOf
(
#strValue VARCHAR(4000),
#strChar VARCHAR(50)
)
RETURNS INT
AS BEGIN
DECLARE #index INT
SET #index = 0
WHILE CHARINDEX(#strChar, #strValue) > 0
BEGIN
SET #index = #index
+ CASE WHEN CHARINDEX(#strChar, #strValue) > 1
THEN ( LEN(#strValue) - LEN(SUBSTRING(#strValue,
CHARINDEX(#strChar, #strValue)
+ LEN(#strChar),
LEN(#strValue))) )
ELSE 1
END
SET #strValue = SUBSTRING(#strValue,
CHARINDEX(#strChar, #strValue)
+ LEN(#strChar), LEN(#strValue))
END
RETURN #index
END'
)
END
DECLARE #tempproc NVARCHAR(MAX)
DECLARE #procindex INT
DECLARE #beginindex INT
DECLARE #header NVARCHAR(MAX)
DECLARE #asindex INT
DECLARE #replacetext NVARCHAR(MAX)
SET #tempproc = ( SELECT sptext
FROM #sptexttable
WHERE id = #counter
)
IF ( SELECT CHARINDEX('CREATE PROC', UPPER(#tempproc))
) > 0
BEGIN
BEGIN TRY
SELECT #procindex = CHARINDEX('PROC', UPPER(#tempproc))
PRINT #procindex
SELECT #beginindex = CHARINDEX('BEGIN', UPPER(#tempproc))
PRINT #beginindex
SELECT #header = SUBSTRING(#tempproc, #procindex,
#beginindex - #procindex)
SELECT #asindex = ( SELECT dbo.ce_lastindexof(#header, 'AS')
- 2
)
SELECT #replacetext = STUFF(#header, #asindex, 10,
CHAR(13) + 'WITH ENCRYPTION'
+ CHAR(13) + 'AS' + CHAR(13))
SET #tempproc = REPLACE(#tempproc, #header, #replacetext)
END TRY
BEGIN CATCH
END CATCH
END
UPDATE #sptexttable
SET sptext = #tempproc
WHERE id = #counter
--PLAY HERE TO M AKE SURE ALL PROCS ARE ALTERED
UPDATE #sptexttable
SET sptext = ( SELECT REPLACE(sptext, 'CREATE PROC',
'ALTER PROC')
FROM #sptexttable
WHERE id = #counter
)
WHERE id = #counter
SELECT #sptext = sptext,
#spname = spname
FROM #sptexttable
WHERE id = #counter
BEGIN TRY
EXEC ( #sptext
)
UPDATE #backup
SET encrypttext = #sptext,
encryptstatus = 1
WHERE id = #counter
END TRY
BEGIN CATCH
PRINT 'the stored procedure ' + #spname
+ ' cannot be encrypted automatically'
END CATCH
SET #counter = #counter + 1
END
SELECT *
FROM #backup
I wrote a cursor, steps through and encrypts most objects.
DECLARE cur_ENCRYPT_ANTHING CURSOR READ_ONLY
FOR
SELECT STUFF(src.definition,
CASE WHEN CHARINDEX('AS' + CHAR(13),src.definition,1) = 0
THEN CASE WHEN CHARINDEX('AS ' + CHAR(13),src.definition,1) = 0 THEN CHARINDEX('AS ',src.definition,1)
ELSE CHARINDEX('AS ' + CHAR(13),src.definition,1)
END
ELSE CHARINDEX('AS' + CHAR(13),src.definition,1)
END,3,'WITH ENCRYPTION AS' + CHAR(13))
FROM (SELECT o.name
, STUFF(RIGHT(sm.definition,LEN(sm.definition) - CHARINDEX('CREATE ',sm.definition,1) + 1),1,6,'ALTER') AS definition
FROM sys.sql_modules AS sm
JOIN sys.objects AS o ON sm.object_id = o.object_id
WHERE CAST(CASE WHEN sm.definition IS NULL THEN 1
ELSE 0
END AS BIT) = 0
AND type <> 'TR'
) AS src
DECLARE #VLS NVARCHAR(MAX)
OPEN cur_ENCRYPT_ANTHING
FETCH NEXT FROM cur_ENCRYPT_ANTHING INTO #VLS
WHILE (##fetch_status <> -1)
BEGIN
IF (##fetch_status <> -2)
BEGIN
BEGIN TRY
EXEC (#VLS)
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
PRINT ''
PRINT #VLS
END CATCH
END
FETCH NEXT FROM cur_ENCRYPT_ANTHING INTO #VLS
END
CLOSE cur_ENCRYPT_ANTHING
DEALLOCATE cur_ENCRYPT_ANTHING
I have made an update to one of the above answers by removing the dependency on the initial Begin Tag. I had a situation where not all my stored procedures had BEGIN and END.
I used the AS clause instead and also used a case sensitive version of the charindex (by adding a collation)
Its not a perfect solution but helped in getting more of my stored procedures encrypted.
Here is my updated code:
IF OBJECT_ID('tempdb..#backup', 'U') IS NOT NULL
BEGIN
DROP TABLE #backup
END
CREATE TABLE #backup
(
id BIGINT IDENTITY(1, 1),
sptext NVARCHAR(MAX) NOT NULL,
spname NVARCHAR(100) NOT NULL,
encrypttext NVARCHAR(MAX) NULL,
encryptstatus BIT NOT NULL
DEFAULT ( 0 )
)
DECLARE #sptexttable TABLE
(
id BIGINT IDENTITY(1, 1),
sptext NVARCHAR(MAX),
spname NVARCHAR(100)
)
INSERT INTO #sptexttable ( sptext, spname )
SELECT [text],
[name]
FROM syscomments
JOIN sysobjects ON syscomments.id = sysobjects.id
AND sysobjects.xtype = 'p'
DECLARE #sptext NVARCHAR(MAX)
DECLARE #spname NVARCHAR(100)
DECLARE #counter INT
SET #counter = 1
WHILE #counter <= ( SELECT MAX(id)
FROM #sptexttable
)
BEGIN
BEGIN TRY
INSERT INTO #backup ( sptext, spname )
SELECT sptext,
spname
FROM #sptexttable
WHERE id = #counter
END TRY
BEGIN CATCH
END CATCH
IF NOT EXISTS ( SELECT [name]
FROM sysobjects
WHERE [name] = 'CaseSensitiveIndex'
AND xtype = 'FN' )
BEGIN
   
EXEC (
'CREATE FUNCTION dbo.CaseSensitiveIndex(#source nvarchar(max), #pattern VARCHAR(50))
RETURNS int
BEGIN
return CHARINDEX(#pattern COLLATE Latin1_General_CS_AS, #source COLLATE Latin1_General_CS_AS)
END; '
)
end
IF NOT EXISTS ( SELECT [name]
FROM sysobjects
WHERE [name] = 'ce_LastIndexOf'
AND xtype = 'FN' )
BEGIN
    
EXEC
( 'CREATE FUNCTION ce_LastIndexOf
    (#strValue VARCHAR(max),
    #strChar VARCHAR(50))
RETURNS INT
AS
BEGIN
DECLARE #index INT
    
SET #index = 0
WHILE CHARINDEX(#strChar, #strValue) > 0
    BEGIN
        SET #index = #index + CASE WHEN CHARINDEX(#strChar, #strValue) > 1
                     THEN
                        (LEN(#strValue) - LEN(SUBSTRING(#strValue,CHARINDEX(#strChar, #strValue) + LEN(#strChar),LEN(#strValue))))
                     ELSE
                        1
                     END
        SET #strValue = SUBSTRING(#strValue,CHARINDEX(#strChar, #strValue) + len(#strChar),LEN(#strValue))    
    END
    RETURN #index
END'
)
END
DECLARE #tempproc NVARCHAR(MAX)
DECLARE #procindex INT
DECLARE #beginindex INT
DECLARE #header NVARCHAR(MAX)
DECLARE #asindex INT
DECLARE #replacetext NVARCHAR(MAX)
SET #tempproc = ( SELECT sptext
FROM #sptexttable
WHERE id = #counter
)
IF ( SELECT CHARINDEX('CREATE PROC', UPPER(#tempproc))
) > 0
BEGIN
BEGIN TRY
SELECT #procindex = CHARINDEX('PROC', UPPER(#tempproc))
PRINT #procindex
SELECT #beginindex=(select dbo.CaseSensitiveIndex(#tempproc, 'AS'))
if(#beginindex=0) begin set #beginindex=( SELECT dbo.ce_lastindexof(#tempproc, 'AS'))end
SELECT #header = SUBSTRING(#tempproc, #procindex,
#beginindex )
SELECT #asindex = ( SELECT dbo.ce_lastindexof(#header, 'AS')
- 2
)
SELECT #replacetext = STUFF(#header, #asindex, 3,
CHAR(13) + 'WITH ENCRYPTION'
+ CHAR(13) + 'AS' + CHAR(13))
SET #tempproc = REPLACE(#tempproc, #header, #replacetext)
                    
END TRY
BEGIN CATCH
END CATCH
    
END
UPDATE #sptexttable
SET sptext = #tempproc
WHERE id = #counter
--PLAY HERE TO MAKE SURE ALL PROCS ARE ALTERED
UPDATE #sptexttable
SET sptext = ( SELECT REPLACE(sptext, 'CREATE PROC',
'ALTER PROC')
FROM #sptexttable
WHERE id = #counter
)
WHERE id = #counter
SELECT #sptext = sptext,
#spname = spname
FROM #sptexttable
WHERE id = #counter
BEGIN TRY
EXEC ( #sptext)
UPDATE #backup
SET encrypttext = #sptext,
encryptstatus = 1
WHERE id = #counter
END TRY
BEGIN CATCH
PRINT 'the stored procedure ' + #spname
+ ' cannot be encrypted automatically'
END CATCH
SET #counter = #counter + 1
END
SELECT *
FROM #backup where encryptstatus =0
1) I export Create code for SP and functions. Keep it backed up. for example D:\SP2.sql"
2) this transact SQL code, generate the script to delete existing sP & Functions
SELECT 'DROP PROCEDURE [' + SCHEMA_NAME(p.schema_id) + '].[' + p.NAME + ']' as A
FROM sys.procedures p
union
SELECT 'DROP FUNCTION ' + [name]
FROM sysobjects WHERE [type] IN (N'FN', N'IF', N'TF', N'FS', N'FT') AND category = 0
order by a
3) This Poweshell code
replace
AS
BEGIN
by
WITH ENCRYPTION
AS
BEGIN
The code
$File = "D:\SP2.sql"
$File2 = $File.Replace("SP2.sql","SP-WithEncrypt.sql")
$sortie=""
$SP = get-content -path $file
echo $SP.Count
For ($i = 0 ; $i -le $SP.Count)
{ if ($sp[$i] -eq "AS" -and $sp[$i+1] -eq "BEGIN")
{ $AEcrire = "`nWITH ENCRYPTION `n AS `n BEGIN"
$i+=1
}
else
{$AEcrire =$sp[$i]
}
$sortie += "`n$AEcrire"
$i+=1
$SP.Count-$i
}
$sortie| out-file $File2
Would be faster with a .replace( ,), but problem with End of lines...
4) run the SP-WithEncrypt.sql in SSMS

Resources