Wrapper stored procedure to create publication for transactional replication - sql-server

I have created a wrapper stored procedure to create publication for transactional replication for SQL Server 2008 Standard Edition SP3. But when I execute the procedure I get the following error. The column "file_exists" is not user defined. This error doesn't make any sense to me. This works on Dev environment but same code doesn't work on test environment. Dev and Test are identical as far as I can tell. I also tried to explicitly set options, made them 5496 (SELECT ##OPTIONS). Any help greatly appreciated.
-- error
Msg 50000, Level 16, State 1, Procedure CreatePublicationForDB, Line 161
Invalid column name 'file_exists'.
-- Begin Script
CREATE DATABASE TestPublication
GO
USE TestPublication
CREATE TABLE Orders(
OrderID INT PRIMARY KEY,
CustomerID INT,
ProductID INT,
UpdatedAt DATETIME,
UpdatedBy DATETIME
)
GO
CREATE TABLE Products(
ProductID INT PRIMARY KEY,
ProductName VARCHAR(100)
)
GO
CREATE VIEW V_Order
AS
SELECT o.OrderID,o.CustomerID, p.ProductName
FROM Orders o
JOIN Products p
ON o.ProductID = p.ProductID
GO
CREATE SCHEMA repl
GO
CREATE TABLE repl.ReplicationTables
(
DBName sys.sysname NOT NULL DEFAULT('TestPublication'),
SchemaOwner sys.sysname NOT NULL DEFAULT('dbo'),
TableName sys.sysname NOT NULL
)
GO
INSERT INTO repl.ReplicationTables (tablename)
VALUES('Orders'),('Products'),('V_Order')
GO
USE TestPublication
GO
CREATE PROCEDURE CreatePublicationForDB( #databaseName sysname, #publicationName sysname = #databaseName, #allow_initialize_from_backup NVARCHAR(5) = 'true')
AS
BEGIN
BEGIN TRY
SET ANSI_WARNINGS ON
SET ANSI_PADDING ON
SET ANSI_NULLS ON
SET ARITHABORT ON
SET QUOTED_IDENTIFIER ON
SET ANSI_NULL_DFLT_ON ON
SET CONCAT_NULL_YIELDS_NULL ON
DECLARE #sp_replicationdboption varchar(MAX) = ' USE '+#databaseName +';',
#sp_addpulication VARCHAR(MAX) = ' USE '+#databaseName +';',
#sp_addpublication_snapshot VARCHAR(MAX) = ' USE '+#databaseName +';',
#sp_addarticle VARCHAR(MAX) = ' USE '+#databaseName +';',
#sp_startpublication_snapshot VARCHAR(MAX) = ' USE '+#databaseName +';'
DECLARE #allow_anonymous NVARCHAR(5) = CASE WHEN #allow_initialize_from_backup = 'false' OR #allow_initialize_from_backup IS NULL THEN 'true' ELSE 'false' END
DECLARE #immediate_sync NVARCHAR(5) = #allow_anonymous, #publisher sysname = ##SERVERNAME
-- set up database publication
SET #sp_replicationdboption += '
exec sp_replicationdboption #dbname = N'''+#databaseName+ ''',
#optname = N''publish'',
#value = N''true'''
-- Publication
SET #sp_addpulication += '
exec sp_addpublication #publication = N'''+#publicationName+ ''',
#description = N''Transactional publication of database '+#databaseName+' from Publisher '+#publisher+''',
#sync_method = N''concurrent'',
#retention = 0,
#allow_push = N''true'',
#allow_pull = N''true'',
#allow_anonymous = N'''+#allow_anonymous+ ''' ,
#enabled_for_internet = N''false'',
#snapshot_in_defaultfolder = N''true'',
#compress_snapshot = N''false'',
#ftp_port = 21,
#ftp_login = N''anonymous'',
#allow_subscription_copy = N''false'',
#add_to_active_directory = N''false'',
#repl_freq = N''continuous'',
#status = N''active'',
#independent_agent = N''true'',
#immediate_sync = N'''+#immediate_sync+ ''' ,
#allow_sync_tran = N''false'',
#autogen_sync_procs = N''false'',
#allow_queued_tran = N''false'',
#allow_dts = N''false'',
#replicate_ddl = 1,
#allow_initialize_from_backup = N'''+COALESCE(#allow_initialize_from_backup, 'false')+ ''' ,
#enabled_for_p2p = N''false'',
#enabled_for_het_sub = N''false'''
IF #allow_initialize_from_backup = 'false'
BEGIN
-- publication snapshot
SET #sp_addpublication_snapshot +='
exec sp_addpublication_snapshot #publication = N'''+#publicationName+ ''',
#frequency_type = 1,
#frequency_interval = 0,
#frequency_relative_interval = 0,
#frequency_recurrence_factor = 0,
#frequency_subday = 0,
#frequency_subday_interval = 0,
#active_start_time_of_day = 0,
#active_end_time_of_day = 235959,
#active_start_date = 0,
#active_end_date = 0,
#job_login = null,
#job_password = null,
#publisher_security_mode = 1'
SET #sp_startpublication_snapshot+=' exec sys.sp_startpublication_snapshot #publication = N'''+#publicationName+ ''''
END
-- Articles
IF OBJECT_ID('tempdb..#t') IS NULL
BEGIN
PRINT 'creating temp table t'
CREATE TABLE #t (NAME sysname,objectid INT, sch_owner sysname NULL, article sysname NOT NULL, isIndexed BIT NULL, IsSchemaBound BIT NULL, TYPE CHAR(2) NULL)
END
INSERT INTO #t(NAME,objectid, sch_owner,isIndexed,IsSchemaBound, TYPE,article)
EXEC('
USE '+#databaseName + '
SELECT f.Name, f.object_id,f.sch, f.IsIndexed,f.IsSchemaBound, f.type,CASE WHEN ROW_NUMBER() OVER (PARTITION BY f.name ORDER BY f.sch) > 1 THEN f.name + CAST((ROW_NUMBER() OVER (PARTITION BY f.name ORDER BY f.sch) - 1) AS VARCHAR(2)) ELSE f.name END AS Article
FROM(
SELECT t.Name, t.object_id,t.sch, IsIndexed,IsSchemaBound, type
FROM
(SELECT DBName, SchemaOwner, TableName
FROM TestPublication.repl.ReplicationTables
GROUP BY DBName, SchemaOwner, TableName )rt JOIN
(SELECT o.Name, o.object_id,s.name AS sch, objectproperty(o.object_id, ''IsIndexed'') AS IsIndexed,objectproperty(o.object_id, ''IsSchemaBound'') AS IsSchemaBound, o.type
FROM
sys.objects o
JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE o.type IN (''U'',''V'')
AND ObjectProperty(o.object_id, ''IsMSShipped'') = 0
AND (ObjectProperty(o.object_id, ''TableHasPrimaryKey'') = 1 OR ObjectProperty(o.object_id, ''TableHasPrimaryKey'') IS NULL)
) t ON rt.tablename = t.name AND rt.SchemaOwner = t.sch
WHERE rt.DBName = '''+#databaseName + '''
) f'
)
SELECT #sp_addarticle +=
'exec sp_addarticle
#publication = N''' +#databaseName +
''', #article = N''' +t.article+
''', #source_owner = N''' +t.sch_owner +
''', #source_object = N''' + t.NAME +
''', #type = N''' +
CASE WHEN t.type = 'U' THEN 'logbased'
WHEN t.type = 'V' AND (IsIndexed = 1 OR IsSchemaBound = 1 )THEN 'indexed view schema only'
WHEN t.type = 'V' AND IsIndexed = 0 THEN 'view schema only' END
+''', #description = null,#creation_script = null,#pre_creation_cmd = N''drop'',
#schema_option = '+
CASE WHEN t.type = 'U' THEN '0x000000000803509F'
WHEN t.type = 'V' THEN '0x0000000008000001' END+
',#destination_table = N'''+t.Name+
''',#destination_owner = N'''+t.sch_owner+''''+
CASE WHEN t.TYPE = 'U' THEN
', #identityrangemanagementoption = N''manual'',#vertical_partition = N''false'',
#ins_cmd = N''CALL sp_MSins_'+t.sch_owner+''+t.Name+
''', #del_cmd = N''CALL sp_MSdel_'+t.sch_owner+''+t.Name+''',
#upd_cmd = N''SCALL sp_MSupd_'+t.sch_owner+''+t.Name+''''
ELSE ''
END
+';'
FROM #t t
PRINT 'Now running sp_replicationdboption'
PRINT #sp_replicationdboption
EXEC(#sp_replicationdboption)
PRINT 'Now running sp_addpulication'
PRINT #sp_addpulication
EXEC(#sp_addpulication)
IF #allow_initialize_from_backup = 'false'
BEGIN
PRINT 'Now running sp_addpulication_snapshot and starting snapshot'
PRINT #sp_addpublication_snapshot
EXEC(#sp_addpublication_snapshot)
EXEC(#sp_startpublication_snapshot)
END
PRINT 'Now running sp_addarticles'
PRINT #sp_addarticle
EXEC(#sp_addarticle)
-- exec sp_droppublication #publication = N'Products'
END TRY
BEGIN CATCH
IF ##trancount > 0
ROLLBACK
DECLARE #ERROR_SEVERITY INT, #ERROR_STATE INT, #ERROR_MESSAGE NVARCHAR(4000)
SELECT #ERROR_SEVERITY = ERROR_SEVERITY(), #ERROR_STATE = ERROR_STATE(), #ERROR_MESSAGE = ERROR_MESSAGE()
RAISERROR(#ERROR_MESSAGE, #ERROR_SEVERITY, #ERROR_STATE)
END CATCH
END
GO
and finally execute
EXEC CreatePublicationForDB 'TestPublication'
-- drop replication in case you want to run the above again.
exec TestPublication.dbo.sp_droppublication #publication = 'TestPublication'
exec TestPublication.dbo.sp_replicationdboption #dbname = 'TestPublication', #optname = 'publish', #value = 'false'
-- cleanup database
DROP DATABASE TestPublication

Sorry, old post I know, but I ran into a very similar situation and got around it. I haven't found a workable solution anywhere else. I'm including my experience to help others.
Summary of my situation is that I have an msbuild process running a series of scripts from a manifest file (text file with a series of script names) within a transaction. The scripts that were intended to create and configure agent jobs always died with the same error. (invalid column name "file_exists")
Procs used in my failing script(s):
msdb.dbo.sp_add_category
msdb.dbo.sp_add_job
msdb.dbo.sp_add_jobstep
msdb.dbo.sp_update_job
msdb.dbo.sp_add_jobschedule
msdb.dbo.sp_add_jobserver
Commenting out the call to msdb.dbo.sp_add_jobstep allowed the script to complete.
I do have SQL command variables in the form #database_name=N'$(DatabaseName)' in the script as well. This led to some misdirected efforts to try to use escape macros as mentioned in the "Important" note under [ #command= ] 'command' on the documentation for sp_add_jobstep.
https://msdn.microsoft.com/en-us/library/ms187358.aspx
If I run the build up to, but not including, the job creation script and start over, it succeeds.
What I found, after much trial and error, was that, even though the procs are qualified as being in [MSDB], unless I actually included a USE statement, the process fails. By beginning the script with "USE [MSDB] GO" and then switching back to my build target (i.e. USE [MyBuildDB] GO), I no longer get the error.
My agent jobs create as expected, and several scripts within the build run without error.

It's hard to say what the problem is without seeing all of the code and having the schema. I'd recommend not creating a wrapper to create publication(s). The create publication scripts can be saved and executed on demand when needed - eliminating the need for a wrapper.

Related

A list of read to file / write to file keywords for SQL Server

We're migrating a SQL Server and as part of preparing for this we need to know about any file access it will be doing to paths that may not exist any more.
There is a large amount of T-SQL we would need to go through to search for any reads / writes to file paths. I'm thinking I could search all of our T-SQL for keywords that do those operations to find potential problems.
Two example keywords I can think of are
OPENROWSET
QUERYOUT
Is there any others?
You want a list of keywords that might be in code somewhere that hard-codes a defunct path? Well, the list is very long... bulk insert, xp_cmdshell, sys.dm_os_file_exists, sp_OACreate, xp_fileexist, create database, alter database, restore database, backup database, ... enumerating this and somehow being sure you got them all will be difficult. It will certainly be easier to find all of the hard-coded paths (assuming you know the list of paths that have been removed) than to find all the keywords that might be involved in referencing them.
If you have removed a mapped drive called X:\, for example, it is much easier to search all procedures / job steps / etc. for %X:\% than it is to find all the places where you use BULK INSERT and then check each one of those to see if they reference X:\.
In either case, I put together this handy search procedure that searches code for a specific string. You can certainly use it to find all instances of BULK INSERT, and all instances of xp_cmdshell, etc. But again it will be a lot easier to search for the explicit paths / drive letters / network shares, and even use that as an opportunity to change the way your code references paths that can change (e.g. put them in config or in a table, instead of hard-coding them anywhere).
CREATE PROCEDURE dbo.FindStringInModules
#search_string nvarchar(4000),
#database_list nvarchar(max) = NULL,
#case_sensitive bit = 0,
#search_jobs bit = 0,
#search_job_and_step_names bit = 0,
#search_schema_names bit = 0,
#search_object_names bit = 0,
#search_column_names bit = 0,
#search_parameter_names bit = 0,
#search_system_objects bit = 0,
#search_system_databases bit = 0,
#search_everything bit = 0,
#debug bit = 0
AS
BEGIN
SET NOCOUNT ON;
IF #search_everything = 1
BEGIN
SELECT
#search_jobs = 1,
#search_job_and_step_names = 1,
#search_object_names = 1,
#search_schema_names = 1,
#search_column_names = 1,
#search_parameter_names = 1,
#search_system_objects = 1,
#search_system_databases = 1;
END
DECLARE #sql nvarchar(max),
#template nvarchar(max),
#exec nvarchar(1024),
#all_text nvarchar(128),
#coll_text nvarchar(128);
SELECT #sql = N'',
#template = N'',
#all_text = CASE #search_system_objects
WHEN 1 THEN N'all_' ELSE N'' END,
#coll_text = CASE #case_sensitive
WHEN 1 THEN N'Latin1_General_100_CS_AS_SC'
WHEN 0 THEN N'Latin1_General_100_CI_AS_SC'
END;
CREATE TABLE #o
(
[database] nvarchar(130),
[schema] nvarchar(130),
[object] nvarchar(130),
[type] nvarchar(130),
create_date datetime,
modify_date datetime,
column_name nvarchar(130),
param_name nvarchar(130),
definition xml
);
SET #search_string = N'%' + #search_string + N'%';
SET #template = N'
SELECT [database] = DB_NAME(),
[schema] = s.name,
[object] = o.name,
[type] = o.type_desc,
o.create_date,
o.modify_date,
[column_name] = $col$,
[param_name] = $param$,
definition = CONVERT(xml, ''<?query -- '' + QUOTENAME(DB_NAME())
+ CHAR(13) + CHAR(10) + OBJECT_DEFINITION(o.object_id)
+ CHAR(13) + CHAR(10) + ''--'' + CHAR(63) + ''>'')
FROM sys.$all$objects AS o
INNER JOIN sys.schemas AS s
ON o.[schema_id] = s.[schema_id]';
SET #sql = #sql + REPLACE(REPLACE(#template, N'$col$', N'NULL'), N'$param$', N'NULL')
+ N'
' + N' WHERE OBJECT_DEFINITION(o.[object_id]) COLLATE $coll$
' + N' LIKE #s COLLATE $coll$';
SET #sql = #sql + CASE #search_schema_names WHEN 1 THEN N'
OR s.name COLLATE $coll$
LIKE #s COLLATE $coll$' ELSE N'' END;
SET #sql = #sql + CASE #search_object_names WHEN 1 THEN N'
OR o.name COLLATE $coll$
LIKE #s COLLATE $coll$' ELSE N'' END;
SET #sql = #sql + CASE #search_column_names WHEN 1 THEN N';
' + REPLACE(REPLACE(#template, N'$col$', N'c.name'),N'$param$',N'NULL')
+ N'
INNER JOIN sys.$all$columns AS c ON o.[object_id] = c.[object_id]
AND c.name COLLATE $coll$
LIKE #s COLLATE $coll$;' ELSE N'' END;
SET #sql = #sql + CASE #search_parameter_names WHEN 1 THEN N';
' + REPLACE(REPLACE(#template, N'$col$', N'NULL'),N'$param$',N'p.name')
+ N'
INNER JOIN sys.$all$parameters AS p ON o.[object_id] = p.[object_id]
AND p.name COLLATE $coll$
LIKE #s COLLATE $coll$;' ELSE N'' END;
SET #sql = REPLACE(REPLACE(#sql, N'$coll$', #coll_text), N'$all$', #all_text);
DECLARE #db sysname, #c cursor;
SET #c = cursor FORWARD_ONLY STATIC READ_ONLY FOR
SELECT QUOTENAME(name) FROM sys.databases AS d
LEFT OUTER JOIN dbo.fn_split(#database_list, N',') AS s ON 1 = 1
WHERE
(
LOWER(d.name) = LOWER(LTRIM(RTRIM(s.value)))
OR NULLIF(RTRIM(#database_list), N'') IS NULL
)
AND d.database_id >= CASE #search_system_databases
WHEN 1 THEN 1 ELSE 5 END
AND d.database_id < 32767
AND d.state = 0;
OPEN #c;
FETCH NEXT FROM #c INTO #db;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #exec = #db + N'.sys.sp_executesql';
IF #debug = 1
BEGIN
RAISERROR(N'Running dynamic SQL on %s:', 1, 0, #db);
PRINT #sql;
END
ELSE
BEGIN
INSERT #o
(
[database],
[schema],
[object],
[type],
create_date,
modify_date,
column_name,
param_name,
definition
)
EXEC #exec #sql, N'#s nvarchar(4000)', #s = #search_string;
END
FETCH NEXT FROM #c INTO #db;
END
IF #debug = 0
BEGIN
SELECT [database],
[schema],
[object],
[type],
create_date,
modify_date,
column_name,
param_name,
definition
FROM #o
ORDER BY [database], [schema], [object], [column_name], [param_name];
END
/* jobs */
IF #search_jobs = 1
BEGIN
SET #template = N'SELECT
job_name = j.name,
s.step_id,
s.step_name,
j.date_created,
j.date_modified,
[command_with_use] = CONVERT(xml, N''<?query -- ''
+ QUOTENAME(s.database_name)
+ CHAR(13) + CHAR(10) + s.[command]
+ CHAR(13) + CHAR(10) + ''--'' + CHAR(63) + ''>'')
FROM msdb.dbo.sysjobs AS j
INNER JOIN msdb.dbo.sysjobsteps AS s
ON j.job_id = s.job_id
WHERE s.command COLLATE $coll$
LIKE #s COLLATE $coll$'
+ CASE #search_job_and_step_names WHEN 1 THEN
N' OR j.name COLLATE $coll$
LIKE #s COLLATE $coll$
OR s.step_name COLLATE $coll$
LIKE #s COLLATE $coll$'
ELSE N'' END
+ N' ORDER BY j.name, s.step_id;';
SET #sql = REPLACE(#template, N'$coll$', #coll_text);
IF #debug = 1
BEGIN
PRINT N'Running this for jobs:';
PRINT #sql;
END
ELSE
BEGIN
EXEC sys.sp_executesql #sql, N'#s nvarchar(4000)', #s = #search_string;
END
END
END
GO

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

Wipe out all data in a scheme, leave structure intact

We're going to be going through a period of testing on a product soon. This product is a web application with a SQL Server 2008R2 backend.
Our database has several schemas within it (Customer, DataEntry, and a few others).
I have found ways to wipe all data in a database without breaking referential integrity or the data structures, which is close to what we're looking to do. The problem I'm finding is that we actually need a bunch of the data from some of the tables. Essentially, we only want to wipe the Customers schema.
We have a script written which will load in the test data for customers, but is there a way to change the techniques in my linked article to target only a specific schema? Is there a better way to clear all data in a schema?
A common scenario for me as well. I usually write what I call a reset script, deleting all data form the target tables in the order necessary to prevent referential errors, and then reseed the primary keys.
DELETE FROM < table 1 >
DELETE FROM < table 2 >
... etc ...
DBCC CHECKIDENT (< table 1 >, RESEED, 0)
DBCC CHECKIDENT (< table 2 >, RESEED, 0)
... etc ...
EDIT
To more fully answer the original question. to leave data in specific tables you would need to modify the block of code that does the deleting / truncating, and also modify the code that reseeds the idents in a similar way.
EXEC sp_MSForEachTable '
IF object_id(''?'') != < table name > AND object_id(''?'') != < table name > AND ... etc ...
BEGIN
IF OBJECTPROPERTY(object_id(''?''), ''TableHasForeignRef'') = 1
DELETE FROM ?
ELSE
TRUNCATE TABLE ?
END
'
GO
Just set the #schemaID to the name of the schema you wish to blow away and it should do the rest. If you end up with a FK dependency loop it will break and tell you what to do...
Declare #schemaID Nvarchar(256)
Set #schemaID = 'Schema' -- Set this to the name of the schema you wish to blow away
If Object_ID('tempdb..#tables') Is Not Null Drop Table #tables
Create Table #tables (tID Int, SchemaName Nvarchar(256), TableName Nvarchar(256))
Insert #tables
Select Row_Number() Over (Order By s.name, so.name), s.name, so.name
From sysobjects so
Join sys.schemas s
On so.uid = s.schema_id
Where so.xtype = 'u'
And s.name = #schemaID
Declare #SQL Nvarchar(Max),
#schema Nvarchar(256),
#table Nvarchar(256),
#iter Int = 1,
#loopCatch Int = 0
While Exists (Select 1
From #tables)
Begin
Select #schema = SchemaName,
#table = TableName
From #tables
Where tID = #iter
If Exists (Select 1
From sysobjects o
Join sys.schemas s1
On o.uid = s1.schema_id
Join sysforeignkeys fk
On o.id = fk.rkeyid
Join sysobjects o2
On fk.fkeyid = o2.id
Join sys.schemas s2
On o2.uid = s2.schema_id
Join #tables t
On o2.name = t.TableName Collate Database_Default
And s2.name = t.SchemaName Collate Database_Default
Where o.name = #table
And s1.name = #schema)
Begin
Update t
Set tID = (Select Max(tID) From #tables) + 1
From #tables t
Where tableName = #table
And schemaName = #schema
Set #iter = #iter + 1
End
Else
Begin
Set #Sql = 'Truncate Table [' + #schema + '].[' + #table + ']'
Begin Try
Exec sp_executeSQL #SQL;
Delete t
From #tables t
Where tableName = #table
And schemaName = #schema
Set #iter = #iter + 1
End Try
Begin Catch
Print #SQL
Update t
Set tID = (Select Max(tID) From #tables) + 1
From #tables t
Where tableName = #table
And schemaName = #schema
Set #iter = #iter + 1
Set #loopCatch = #loopCatch + 1;
If #loopCatch > 5
Begin
Select 'WARNING: Endless FK redundancy loop. Drop the constraints and these tables, truncate and reapply constraints manually'
Union All
Select '[' + SchemaName + '].[' + TableName + ']'
From #tables;
Break;
End
End Catch
End
End
This is parameterized on database and schema. If no schema is supplied, it will clear all data in the specified database.
Handles tables with foreign key references appropriately by disabling constraints. If the procedure fails, which it shouldn't normally do, ensure that you run it successfully after fixing the cause of the problem, which should ensure constraint checking goes back to normal.
This will not handle foreign key references correctly if you have foreign keys between schemas, however, it could be fairly easily amended to handle this.
create procedure [removeData] (#database_name sysname, #schema_name sysname = null)
as
set nocount on
create table #tables (
TableName varchar(900) not null primary key,
HasFKRef bit not null
);
declare #sql nvarchar(4000),
#table_name varchar(900);
if (db_id(#database_name) is null)
raiserror ('You must at least specify the database name', 16, 1);
set #sql = 'select ''['' + TABLE_CATALOG + ''].['' + TABLE_SCHEMA + ''].['' + TABLE_NAME + '']'' as TableName, (case when exists(select * from [' + #database_name + '].INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc inner join [' + #database_name + '].INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc on rc.UNIQUE_CONSTRAINT_CATALOG = tc.CONSTRAINT_CATALOG and rc.UNIQUE_CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA and rc.UNIQUE_CONSTRAINT_NAME = tc.CONSTRAINT_NAME where tc.TABLE_NAME = t.TABLE_NAME) then 1 else 0 end) as HasFKRef
from [' + #database_name + '].INFORMATION_SCHEMA.TABLES t
where TABLE_TYPE = ''BASE TABLE'' and TABLE_SCHEMA = isnull(#schema_name, TABLE_SCHEMA)';
insert into #tables
exec sp_executesql #sql, N'#schema_name sysname', #schema_name;
declare #curse cursor
set #curse = cursor fast_forward for
select sql from (
select 'alter table ' + TableName + ' nocheck constraint all' as sql, 1 as sort
from #tables
union all
select 'truncate table ' + TableName, 2 as sort
from #tables
where HasFKRef = 0
union all
select 'delete from ' + TableName, 3 as sort
from #tables
where HasFKRef = 1
union all
select 'alter table ' + TableName + ' with check check constraint all', 4 as sort
from #tables
) t
order by sort, sql
open #curse
fetch next from #curse into #sql
while (##fetch_status = 0)
begin
exec (#sql)
fetch next from #curse into #sql
end
close #curse
GO

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