/**********************************************************************/
/* MSDB8TO9.SQL                                                       */
/*                                                                    */
/* Upgrades 7.x, 8.x and 9.0 to 10.0 and drops all obsolete 8.x       */
/*                                                                    */
/*
** Copyright Microsoft, Inc. 1994 - 2008
** All Rights Reserved.
*/
/**********************************************************************/

PRINT '----------------------------------------'
PRINT 'Starting execution of PREINSTMSDB100.SQL'
PRINT '----------------------------------------'

use msdb
go
-- Check that we're in msdb
IF (DB_NAME() <> N'msdb')
  RAISERROR('A problem was encountered accessing msdb. upgrade script terminating.', 20, 127) WITH LOG
go

CHECKPOINT
go
--set compatibily level to 100
sp_dbcmptlevel @dbname = 'msdb',  @new_cmptlevel = '100'

go

-- Allow updates to system catalogs so that we can fully manipulate our system objects
EXECUTE master.dbo.sp_configure N'allow updates', 1
go
RECONFIGURE WITH OVERRIDE
go

/**************************************************************/
/* Record time of start of creates                            */
/**************************************************************/
SELECT start = getdate() INTO #InstMsdb
go

--preserve existing object permnission during upgrade
--create perms table
IF (NOT OBJECT_ID(N'dbo.upgrade_perms', 'U') IS NULL)
BEGIN
  DROP TABLE dbo.upgrade_perms
END
-- upgrade_perms is filled with current permission of objects in MSDB
-- the structure of table is:
-- state_desc = GRANT|DENY
-- permission_name = SELECT|EXECUTE|UPDATE ...
-- object_name = grantor name
-- grantee_name = grantee name

CREATE TABLE dbo.upgrade_perms(state_desc nvarchar(60), permission_name sysname, object_name sysname, grantee_name sysname)
CREATE INDEX indnc ON dbo.upgrade_perms(object_name)
DECLARE @state_desc			  nvarchar(60)
DECLARE @permission_name	sysname
DECLARE @object_name		  sysname
DECLARE @grantee_name		  sysname 

DECLARE perms_cursor CURSOR LOCAL FOR 
	SELECT state_desc, permission_name, OBJECT_NAME(major_id), USER_NAME(grantee_principal_id) from msdb.sys.database_permissions 
  WHERE state_desc IS NOT NULL AND 
  permission_name IS NOT NULL AND 
  OBJECT_NAME(major_id) IS NOT NULL AND 
  USER_NAME(grantee_principal_id) IS NOT NULL 	

OPEN perms_cursor
   FETCH NEXT FROM perms_cursor INTO @state_desc, @permission_name, @object_name, @grantee_name
   WHILE (@@fetch_status = 0)
   BEGIN
      INSERT dbo.upgrade_perms(state_desc, permission_name, object_name, grantee_name)
      VALUES(@state_desc, @permission_name, @object_name, @grantee_name) 
      FETCH NEXT FROM perms_cursor INTO @state_desc, @permission_name, @object_name, @grantee_name
   END
DEALLOCATE perms_cursor
go


------------------------VIEWS UPGRADE---------------------------------------


------------------------TABLE UPGRADE---------------------------------------
--create an populate sysoriginatingservers
use msdb
go

IF (NOT EXISTS (SELECT *   --just a safe belt, this table shouldn't be in 8.x
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sysoriginatingservers')
              AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysoriginatingservers...'
  CREATE TABLE dbo.sysoriginatingservers
  (
    -- There is only a single MSX server record in this table (originating_server_id = 1)
    -- 0 is generated by sysoriginatingservers_view and indicates the local server
    originating_server_id	INT 		CONSTRAINT CK_originating_server_id_MustBe_1 CHECK (originating_server_id = 1) 
									    DEFAULT (1) UNIQUE CLUSTERED,				
    originating_server    sysname		NOT NULL UNIQUE NONCLUSTERED,
    --Mark this record as a MSX server entry
    master_server         bit			CONSTRAINT CK_master_server_MustBe_1 CHECK (master_server = 1) 
									    DEFAULT (1)	
  )
END
go

IF (NOT EXISTS (SELECT t.name FROM msdb.sys.all_columns c JOIN msdb.sys.all_objects t 
                ON c.object_id = t.object_id 
                WHERE c.name = 'originating_server_id' and t.name = 'sysjobs' and t.type = 'U'))
BEGIN                
  PRINT ''
  PRINT 'Adding column originating_server_id to table sysjobs...'
  --add new column 9.0 originating_server_id
  ALTER TABLE sysjobs WITH NOCHECK 
  ADD originating_server_id INT NULL
END
go

DECLARE @MSXServerName        sysname
DECLARE @LocalServerName      sysname
DECLARE @UpdateOrgServerTSQL  nvarchar(MAX)

SELECT @LocalServerName = UPPER(CONVERT(sysname, SERVERPROPERTY('servername')))

EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                        N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                        N'MSXServerName',
                                        @MSXServerName OUTPUT,
                                        N'no_output'

SELECT @MSXServerName = LTRIM(RTRIM(UPPER(@MSXServerName)))
IF (@MSXServerName = '') SELECT @MSXServerName = NULL

IF (@MSXServerName IS NOT NULL)
BEGIN
  IF (NOT EXISTS( SELECT * FROM dbo.sysoriginatingservers 
                  WHERE originating_server_id = 1 AND originating_server = @MSXServerName
                  AND master_server = 1))
    BEGIN
      PRINT ''
      PRINT 'Populate table sysoriginatingservers...'
      INSERT INTO sysoriginatingservers( originating_server_id, originating_server,  master_server) 
      VALUES(1, @MSXServerName, 1)
    END  
END

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sysjobs')
              AND (type = 'U')))
BEGIN
  IF (EXISTS (SELECT t.name FROM msdb.sys.all_columns c JOIN msdb.sys.all_objects t 
                  ON c.object_id = t.object_id 
                  WHERE c.name = 'originating_server' and t.name = 'sysjobs' and t.type = 'U'))
  BEGIN                

    PRINT ''
    PRINT 'Populate new column originating_server_id of table sysjobs...'
    --set this column based on the value of 8.0 only column originating_server 
	  --if MSX server is NULL we come up with server name that cannot exit, a generated GUID
    SELECT @UpdateOrgServerTSQL = 
    '
    UPDATE sysjobs SET originating_server_id = 
    CASE UPPER(originating_server) 
      WHEN ''' + @LocalServerName + ''' THEN 0 --local_server_id 
      WHEN ''' + ISNULL(@MSXServerName, CONVERT(sysname, NEWID()))   + ''' THEN 1 --msx_server_id 
      ELSE 0 --7.0 (local) or bad data 
    END
    '
    EXECUTE( @UpdateOrgServerTSQL)
    
    PRINT ''
    PRINT 'Drop column originating_server of table sysjobs...'
    --drop 8.0 column originating_server
    DROP INDEX sysjobs.nc2
    ALTER TABLE sysjobs DROP COLUMN originating_server
  END
END  
go

--normalize 8.0 sysjobschedules into 9.0 sysschedules and sysjobschedules
IF NOT EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sysschedules')
              AND (type = 'U'))
BEGIN
--create first sysschedules table  
	PRINT ''
	PRINT 'Creating table sysschedules...'

	CREATE TABLE dbo.sysschedules
	(
	schedule_id            INT IDENTITY     PRIMARY KEY CLUSTERED,
	schedule_uid           UNIQUEIDENTIFIER NOT NULL,
	originating_server_id  INT              NOT NULL, 
	name                   sysname          NOT NULL,
	owner_sid				       varbinary(85)	  NOT NULL, 
	enabled                INT              NOT NULL,
	freq_type              INT              NOT NULL,
	freq_interval          INT              NOT NULL,
	freq_subday_type       INT              NOT NULL,
	freq_subday_interval   INT              NOT NULL,
	freq_relative_interval INT              NOT NULL,
	freq_recurrence_factor INT              NOT NULL,
	active_start_date      INT              NOT NULL,
	active_end_date        INT              NOT NULL,
	active_start_time      INT              NOT NULL,
	active_end_time        INT              NOT NULL,
	date_created           DATETIME         NOT NULL  DEFAULT (GETDATE()),
	date_modified          DATETIME         NOT NULL  DEFAULT (GETDATE()),
	version_number         INT              NOT NULL  DEFAULT (1)
	)
-- CREATE UNIQUE CLUSTERED INDEX clust ON sysschedules(job_id, name)
-- CREATE UNIQUE NONCLUSTERED INDEX nc1 ON sysschedules(schedule_id)
END
go
--a system object cannot be renamed, turn off marking system object trace to alloe renaming of temp_sysjobschedules
dbcc traceoff(1717, -1) 

go
-- create temp cross 9.0 table temp_sysjobschedules
IF EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'temp_sysjobschedules')
              AND (type = 'U'))
BEGIN
  DROP TABLE dbo.temp_sysjobschedules
END
go

PRINT ''
PRINT 'Creating table temp_sysjobschedules'	
CREATE TABLE dbo.temp_sysjobschedules
	(
	schedule_id		         INT					    REFERENCES dbo.sysschedules(schedule_id),
	job_id                 UNIQUEIDENTIFIER REFERENCES dbo.sysjobs(job_id),
	next_run_date          INT              NOT NULL   DEFAULT 0,
	next_run_time          INT              NOT NULL   DEFAULT 0
	)
go

DECLARE @dynamicSQL nvarchar(4000)
IF (EXISTS (SELECT t.name FROM msdb.sys.all_columns c JOIN msdb.sys.all_objects t 
                ON c.object_id = t.object_id 
                WHERE c.name = 'name' and t.name = 'sysjobschedules' and t.type = 'U'))
BEGIN	
  PRINT ''
  PRINT 'Moving schedule data ...'	
	SET IDENTITY_INSERT dbo.sysschedules ON
  SELECT @dynamicSQL =
  '
	INSERT INTO dbo.sysschedules
	(
		schedule_id            ,
		schedule_uid           ,
		originating_server_id  ,
		name                   ,
		owner_sid				       ,
		enabled                ,
		freq_type              ,
		freq_interval          ,
		freq_subday_type       ,
		freq_subday_interval   ,
		freq_relative_interval ,
		freq_recurrence_factor ,
		active_start_date      ,
		active_end_date        ,
		active_start_time      ,
		active_end_time        ,
		date_created           
	)
	SELECT
		js.schedule_id            ,
		NEWID()                   ,
		0                         , --local server. TO DO make sure local server = 0
		js.name                   ,      
		j.owner_sid               ,      
		js.enabled                   ,
		js.freq_type              ,
		js.freq_interval          ,
		js.freq_subday_type       ,
		js.freq_subday_interval   ,
		js.freq_relative_interval ,
		js.freq_recurrence_factor ,
		js.active_start_date      ,
		js.active_end_date        ,
		js.active_start_time      ,
		js.active_end_time        ,
		js.date_created           
	FROM
  dbo.sysjobs j JOIN dbo.sysjobschedules js ON j.job_id = js.job_id
	  
	INSERT INTO dbo.temp_sysjobschedules
	(
		schedule_id		        ,
		job_id                ,
		next_run_date         ,
		next_run_time         
	)
	SELECT
		js.schedule_id          ,
		js.job_id               ,
		js.next_run_date        ,
		js.next_run_time          
	FROM dbo.sysjobs j JOIN dbo.sysjobschedules js ON j.job_id = js.job_id
'
  EXECUTE (@dynamicSQL)

	SET IDENTITY_INSERT dbo.sysschedules OFF

	IF (EXISTS (SELECT *
				FROM msdb.dbo.syscolumns
				WHERE (id = OBJECT_ID(N'sysjobschedules'))
				AND (name = N'date_created')
				AND (cdefault <> 0)))
	  EXECUTE sp_unbindefault N'sysjobschedules.date_created'

	DROP TABLE dbo.sysjobschedules
	EXECUTE sp_rename 'temp_sysjobschedules', 'sysjobschedules'
	EXECUTE (N'CREATE UNIQUE CLUSTERED INDEX clust ON dbo.sysjobschedules(job_id, schedule_id)')
	PRINT ''
	PRINT 'Updating schedules done'
END	
go

--just a safe belt
IF EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'temp_sysjobschedules')
              AND (type = 'U'))
BEGIN
  DROP TABLE dbo.temp_sysjobschedules
END
go

--alter only if command column is not already nvarchar(max)
IF (NOT EXISTS (SELECT c.* FROM msdb.sys.all_columns c JOIN msdb.sys.all_objects t 
                ON c.object_id = t.object_id 
                WHERE (c.name = 'proxy_id' OR c.name = 'step_uid') AND t.name = 'sysjobsteps' AND t.type = 'U'))
BEGIN
  PRINT ''
  PRINT 'Adding proxy_id, step_uid  columns to sysjobsteps table'  
  ALTER TABLE sysjobsteps ADD
        proxy_id INT NULL,
        step_uid UNIQUEIDENTIFIER NULL
END
go

--rename DTS subsystem to SSIS
IF (OBJECT_ID('dbo.sysjobsteps', 'U') IS NOT NULL)
BEGIN
    UPDATE dbo.sysjobsteps SET subsystem = N'SSIS' WHERE UPPER(subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DTS'
END
go

--to be safer populate sysjobsteps with guids, otherwise step table logs are not possible
EXECUTE (N'UPDATE sysjobsteps SET step_uid = NEWID() WHERE step_uid IS NULL')
go

--if there is no index for step_uid, create it, so step table logs can reference it
IF NOT EXISTS (SELECT name FROM sys.indexes WHERE name = N'nc2' and object_id = object_id(N'[dbo].[sysjobsteps]') )
BEGIN
  EXECUTE (N'CREATE UNIQUE NONCLUSTERED INDEX nc2 ON sysjobsteps(step_uid)')
END
go


--alter sysdownloadlist table
PRINT ''
PRINT 'Alter table sysdownloadlist...'
ALTER TABLE sysdownloadlist ALTER COLUMN source_server sysname
ALTER TABLE sysdownloadlist ALTER COLUMN target_server sysname
go

--alter sysjobhistory  table
PRINT ''
PRINT 'Alter table sysjobhistory...'
ALTER TABLE sysjobhistory ALTER COLUMN server sysname
go

IF EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE name = N'sysjobhistory' and type = 'U')
BEGIN
  UPDATE  msdb.dbo.sysjobhistory
  SET     server = CONVERT(sysname, SERVERPROPERTY('servername'))
  WHERE   UPPER(server) = '(LOCAL)'
END
go

--alter systargetservers table
PRINT ''
PRINT 'Alter table systargetservers...'
ALTER TABLE systargetservers  ALTER COLUMN server_name sysname
go

/**********************************************************************/
/* Saving the syssubsystems related user info                         */
/**********************************************************************/

-- Capture current state of syssubsystems.max_worker_threads in temp table
PRINT 'Check if syssubsystems table exists...'
IF (OBJECT_ID(N'[dbo].[syssubsystems]', 'U') IS NOT NULL)
BEGIN

    IF (OBJECT_ID('tempdb..#syssubsystems_temp', 'U') IS NOT NULL)
    BEGIN
        PRINT 'Dropping existing temp table tempdb..#syssubsystems_temp...'
    DROP TABLE #syssubsystems_temp
    END

    SELECT subsystem, max_worker_threads
    INTO #syssubsystems_temp
    FROM [dbo].[syssubsystems]

END

GO

/**********************************************************************/
/* DONE - Saving the syssubsystems related user info                  */
/**********************************************************************/

--drop syssubsystems table if it exists( to support update from IDW(n) to RTM)
--it will recreated later with the updated schema
IF (OBJECT_ID('dbo.syssubsystems', 'U') IS NOT NULL)
BEGIN
  DROP TABLE dbo.syssubsystems
END

GO

--drop column logshipping from sysdbmaintplans table
--alter only if command column is not already nvarchar(max)
IF (EXISTS (SELECT c.* FROM msdb.sys.all_columns c JOIN msdb.sys.all_objects t 
                ON c.object_id = t.object_id 
                WHERE (c.name = 'logshipping') AND t.name = 'sysdbmaintplans' AND t.type = 'U'))
BEGIN
  ALTER TABLE sysdbmaintplans DROP COLUMN logshipping
END
go

--make sure 
--it will recreated later with the updated schema
IF (OBJECT_ID('dbo.sysjobstepslogs', 'U') IS NOT NULL)
BEGIN
  ALTER TABLE dbo.sysjobstepslogs  ALTER COLUMN log_size bigint
END

-- sysproxylogin upgrade
-- sysproxylogin upgrade
BEGIN TRY
	IF EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='principal_id' and id =
			(SELECT OBJECT_ID(N'dbo.sysproxylogin', 'U')))
	BEGIN
		-- convert data from principal_id to sid
		exec sp_executesql N'
		DECLARE @sid varbinary(85)
		DECLARE @principal_id int 

		DECLARE principal_sid_cursor CURSOR LOCAL 
		FOR
		SELECT distinct principal_id
		FROM dbo.sysproxylogin WHERE (sid IS NULL) AND (flags = 2)

		OPEN principal_sid_cursor 
		FETCH NEXT FROM principal_sid_cursor INTO @principal_id
		WHILE (@@fetch_status = 0)
		BEGIN
		    SELECT @sid=sid FROM msdb.sys.database_principals WHERE principal_id=@principal_id

			IF @sid IS NOT NULL -- principal_id is valid
			BEGIN
				UPDATE dbo.sysproxylogin
				SET sid = @sid
				WHERE principal_id = @principal_id
			END
			ELSE
			BEGIN
				DELETE FROM dbo.sysproxylogin
				WHERE principal_id = @principal_id
			END
			FETCH NEXT FROM principal_sid_cursor INTO @principal_id
		END
		CLOSE principal_sid_cursor 
		DEALLOCATE principal_sid_cursor
		'

		-- remove obsolete column
		DROP INDEX sysproxylogin.clust
		ALTER TABLE dbo.sysproxylogin DROP COLUMN principal_id
        CREATE UNIQUE CLUSTERED    INDEX clust ON sysproxylogin(proxy_id, sid, flags)
	END
END TRY
BEGIN CATCH
	print 'There was a problem upgrading sysproxylogin table.'
	print 'Unable to map existing principal_id values to sid column'
	print 'Error State: ' + convert(varchar(10),ERROR_STATE() )
END CATCH

GO

/**************************************************************/
/* Mark system objects                                        */
/**************************************************************/
declare  @start datetime
		,@name  sysname
select @start = start from #InstMsdb
declare newsysobjs cursor for select name from sys.objects where schema_id = 1 and create_date >= @start
open newsysobjs
fetch next from newsysobjs into @name
while @@fetch_status = 0
begin
	Exec sp_MS_marksystemobject @name
	fetch next from newsysobjs into @name
end
deallocate newsysobjs
drop table #InstMsdb
go

EXECUTE master.dbo.sp_configure N'allow updates', 0
go
RECONFIGURE WITH OVERRIDE
go


/**************************************************************/
/* DMF Pre-upgrade steps                                      */
/**************************************************************/

PRINT 'DMF pre-upgrade steps...'
--
-- >>> CTP5 -> CTP6 Upgrade
--
-- The check is based on presense of ObjectSet tables
--  We also check if principal DMF objects is there
--  if it's not we either upgrade from Yukon
--  or there is nothing to upgrade anyway
IF (OBJECT_ID('[dbo].[syspolicy_policies_internal]', 'U') IS NOT NULL)
	AND (OBJECT_ID('[dbo].[syspolicy_object_sets_internal]', 'U') IS NULL)
BEGIN
	BEGIN TRY
		-- Open transaction - we don't want to delete tables if we don't have a copy of data
		BEGIN TRANSACTION
		-- Create upgrade marker
		CREATE TABLE dbo.dmf_upgrade (id int)
	
		-- STORE DATA
		SELECT * INTO msdb.dbo.tmp_syspolicy_target_sets_internal FROM syspolicy_target_sets_internal 
		SELECT * INTO msdb.dbo.tmp_syspolicy_target_set_levels_internal FROM syspolicy_target_set_levels_internal 
		SELECT * INTO msdb.dbo.tmp_syspolicy_policies_internal FROM syspolicy_policies_internal 
		SELECT * INTO msdb.dbo.tmp_syspolicy_system_health_state_internal FROM syspolicy_system_health_state_internal 
		SELECT * INTO msdb.dbo.tmp_syspolicy_policy_execution_history_internal FROM syspolicy_policy_execution_history_internal 
		SELECT * INTO msdb.dbo.tmp_syspolicy_policy_execution_history_details_internal FROM syspolicy_policy_execution_history_details_internal 
	
		-- Delete policies to invoke the trigger that removes dependent objects (jobs, if any).
		DELETE [dbo].[syspolicy_policies_internal]
		-- T-SQL Policy jobs are now gone, we must nullify job_id to not violate the foreign key constraint
		UPDATE msdb.dbo.tmp_syspolicy_policies_internal SET job_id = NULL
		
		-- DROP TABLES (WITH DEPENDENCIES)	
		IF (OBJECT_ID('[dbo].[syspolicy_target_set_levels_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[syspolicy_target_set_levels_internal]
		IF (OBJECT_ID('[dbo].[syspolicy_target_sets_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[syspolicy_target_sets_internal]
		IF (OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[syspolicy_policy_execution_history_details_internal]
		IF (OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[syspolicy_policy_execution_history_internal]
		IF (OBJECT_ID('[dbo].[syspolicy_system_health_state_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[syspolicy_system_health_state_internal]
		IF (OBJECT_ID('[dbo].[syspolicy_policies]', 'V') IS NOT NULL) DROP VIEW [dbo].[syspolicy_policies]
		IF (OBJECT_ID('[dbo].[syspolicy_policies_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[syspolicy_policies_internal]
	
		COMMIT TRANSACTION
	END TRY
	BEGIN CATCH
            IF (XACT_STATE() <> 0)
            BEGIN
                ROLLBACK TRANSACTION;
            END

            DECLARE @ErrorMessage   NVARCHAR(4000);
            DECLARE @ErrorSeverity  INT;
            DECLARE @ErrorState     INT;
            DECLARE @ErrorNumber    INT;
            DECLARE @ErrorLine      INT;
            DECLARE @ErrorProcedure NVARCHAR(200);
            SELECT @ErrorLine = ERROR_LINE(),
                   @ErrorSeverity = ERROR_SEVERITY(),
                   @ErrorState = ERROR_STATE(),
                   @ErrorNumber = ERROR_NUMBER(),
                   @ErrorMessage = ERROR_MESSAGE(),
                   @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');
            PRINT 'ERROR: DMF CTP5 to CTP6 upgrade tasks failed' 
            RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
	END CATCH;
END
GO
--
-- <<< CTP5 - CTP6 Upgrade
--

--
-- >>> CTP6 - RTM Upgrade
--
IF (OBJECT_ID('[dbo].[syspolicy_policies_internal]', 'U') IS NOT NULL)
BEGIN
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policies_internal]') AND name='object_set_id' AND is_nullable=0)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policies_internal] ALTER COLUMN object_set_id int NULL
		END
END
GO
IF (OBJECT_ID('[dbo].[syspolicy_system_health_state_internal]', 'U') IS NOT NULL)
BEGIN
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_system_health_state_internal]') AND name='target_query_expression_with_id' AND is_nullable=1)
		BEGIN
		IF EXISTS (SELECT * FROM sys.indexes WHERE name = N'IX_syspolicy_system_health_state_internal_target_query_expression_with_id')
			DROP INDEX IX_syspolicy_system_health_state_internal_target_query_expression_with_id ON [dbo].[syspolicy_system_health_state_internal]
		ALTER TABLE [dbo].[syspolicy_system_health_state_internal] ALTER COLUMN target_query_expression_with_id nvarchar(400) NOT NULL
		CREATE INDEX IX_syspolicy_system_health_state_internal_target_query_expression_with_id ON
			[dbo].[syspolicy_system_health_state_internal](target_query_expression_with_id)
		END
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_system_health_state_internal]') AND name='target_query_expression' AND is_nullable=1)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_system_health_state_internal] ALTER COLUMN target_query_expression nvarchar(max) NOT NULL
		END
END
GO
IF (OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]', 'U') IS NOT NULL)
BEGIN
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='policy_id' AND is_nullable=1)
		  BEGIN
		  IF EXISTS (SELECT * FROM sys.indexes WHERE name = N'IX_syspolicy_policy_execution_history_internal_end_date_policy_id')
	  		DROP INDEX IX_syspolicy_policy_execution_history_internal_end_date_policy_id ON [dbo].[syspolicy_policy_execution_history_internal]
		  IF EXISTS (SELECT * FROM sys.indexes WHERE name = N'IX_syspolicy_policy_execution_history_internal_policy_id')
			DROP INDEX IX_syspolicy_policy_execution_history_internal_policy_id ON [dbo].[syspolicy_policy_execution_history_internal]

		  DECLARE @fk_name sysname 
		  SELECT @fk_name = k.name from sys.foreign_keys k
			join sys.foreign_key_columns kc on k.object_id = kc.constraint_object_id
			join sys.columns c on k.parent_object_id = c.object_id AND kc.parent_column_id = c.column_id
			where k.parent_object_id=OBJECT_ID('syspolicy_policy_execution_history_internal')
			and c.name = 'policy_id'	  
		  IF @fk_name IS NOT NULL
			BEGIN
			DECLARE @drop_cmd nvarchar(512)
			SELECT @drop_cmd = 'ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] DROP CONSTRAINT ' + QUOTENAME(@fk_name) 
			EXECUTE (@drop_cmd)
			END
	      END
END
GO
IF (OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]', 'U') IS NOT NULL)
BEGIN
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='policy_id' AND is_nullable=1)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ALTER COLUMN policy_id int NOT NULL
		CREATE INDEX IX_syspolicy_policy_execution_history_internal_end_date_policy_id 
			ON [dbo].[syspolicy_policy_execution_history_internal](policy_id, end_date);
		CREATE INDEX IX_syspolicy_policy_execution_history_internal_policy_id 
			ON [dbo].[syspolicy_policy_execution_history_internal](policy_id);
		END
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='start_date' AND is_nullable=1)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ALTER COLUMN [start_date] datetime NOT NULL
		END
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='result' AND is_nullable=1)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ALTER COLUMN result bit NOT NULL
		END
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='is_full_run' AND is_nullable=1)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ALTER COLUMN is_full_run bit NOT NULL
		END
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='exception_message' AND is_nullable=0)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ALTER COLUMN exception_message nvarchar(max) NULL
		END
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='exception' AND is_nullable=0)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ALTER COLUMN exception nvarchar(max) NULL
		END
END
GO
IF (OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]', 'U') IS NOT NULL)
BEGIN
	  IF NOT EXISTS (SELECT * from sys.foreign_keys k
			join sys.foreign_key_columns kc on k.object_id = kc.constraint_object_id
			join sys.columns c on k.parent_object_id = c.object_id AND kc.parent_column_id = c.column_id
			where k.parent_object_id=OBJECT_ID('syspolicy_policy_execution_history_internal')
			and c.name = 'policy_id')	  
	  BEGIN
	  ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ADD FOREIGN KEY (policy_id) REFERENCES [syspolicy_policies_internal]
	  END
END
GO
IF (OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]', 'U') IS NOT NULL)
BEGIN
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='history_id' AND is_nullable=1)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN history_id bigint NOT NULL
		END
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='target_query_expression_with_id' AND is_nullable=1)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN target_query_expression_with_id nvarchar(4000) NOT NULL
		END
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='target_query_expression' AND is_nullable=1)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN target_query_expression nvarchar(4000) NOT NULL
		END
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='execution_date' AND is_nullable=1)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN execution_date datetime NOT NULL
		END
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='result' AND is_nullable=1)
		BEGIN
		IF EXISTS (SELECT * FROM sys.indexes WHERE name = N'IX_syspolicy_policy_execution_history_details_internal_result')
		  DROP INDEX IX_syspolicy_policy_execution_history_details_internal_result ON [dbo].[syspolicy_policy_execution_history_details_internal] 
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN result bit NOT NULL
		END
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='result_detail' AND is_nullable=0)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN result_detail nvarchar(max) NULL
		END
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='exception_message' AND is_nullable=0)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN exception_message nvarchar(max) NULL
		END
	  IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='exception' AND is_nullable=0)
		BEGIN
		ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN exception nvarchar(max) NULL
		END
END
GO
--
-- <<< CTP6 - RTM Upgrade
--

--
-- >>> Katmai RTM - KJ CTP2 Upgrade
--
-- If there is no 'is_system' column in the following tables, add it
--
-- Need to take care of Shiloh/Yukon upgrade - no DMF objects exist
-- Only check for policies, assuming we either have all DMF tables or none
-- If installation is corrupted (there is policies table, but no conditions table) upgrade fails
--
IF (OBJECT_ID('[dbo].[syspolicy_policies_internal]', 'U') IS NOT NULL)
BEGIN
	IF NOT EXISTS (SELECT * FROM sys.columns WHERE name = 'is_system' AND object_id = OBJECT_ID('[dbo].[syspolicy_policies_internal]', 'U'))
	  ALTER TABLE syspolicy_policies_internal ADD is_system bit NOT NULL DEFAULT (0)
	IF NOT EXISTS (SELECT * FROM sys.columns WHERE name = 'is_system' AND object_id = OBJECT_ID('[dbo].[syspolicy_conditions_internal]', 'U'))
	  ALTER TABLE syspolicy_conditions_internal ADD is_system bit NOT NULL DEFAULT (0)
	IF NOT EXISTS (SELECT * FROM sys.columns WHERE name = 'is_system' AND object_id = OBJECT_ID('[dbo].[syspolicy_object_sets_internal]', 'U'))
	  ALTER TABLE syspolicy_object_sets_internal ADD is_system bit NOT NULL DEFAULT (0)
END
GO

-- Fix indexes on syspolicy_policy_execution_history_details_internal
--
IF (OBJECT_ID('[dbo].[syspolicy_policies_internal]', 'U') IS NOT NULL)
BEGIN
	IF EXISTS (SELECT * FROM sys.indexes WHERE name = N'IX_syspolicy_policy_execution_history_details_internal_result')
	  DROP INDEX IX_syspolicy_policy_execution_history_details_internal_result ON [dbo].[syspolicy_policy_execution_history_details_internal] 
	  
	IF NOT EXISTS (SELECT * FROM sys.key_constraints WHERE name = N'PK_syspolicy_policy_execution_history_details_id')
	  BEGIN
	  DECLARE @ix_name sysname 
	  SELECT @ix_name = name FROM sys.key_constraints 
		WHERE parent_object_id = OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]', 'U') AND type = 'PK' AND is_system_named = 1
	  IF @ix_name IS NOT NULL
		BEGIN
		DECLARE @drop_cmd nvarchar(512)
		SELECT @drop_cmd = 'ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] DROP CONSTRAINT ' + QUOTENAME(@ix_name) 
		EXECUTE (@drop_cmd)
		END
	  ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] 
		ADD CONSTRAINT [PK_syspolicy_policy_execution_history_details_id] PRIMARY KEY CLUSTERED(history_id, detail_id)
	  END
END
GO
--
-- <<< Katmai RTM - KJ CTP2 Upgrade
--


/**************************************************************/
/* End of DMF Pre-upgrade steps                               */
/**************************************************************/


/**********************************************************************/
/* DC Pre-upgrade steps                                               */
/**********************************************************************/
GO
PRINT 'DC pre-upgrade steps...';

-- Capture current state of DataCollector in temp table
-- and re-enable collector after script upgrade
-- #304027 - Data Collection is disabled when upgrading SQL Server 2008 
--           service pack upgrade or hotfix upgrade

PRINT 'Check if Data collector config table exists...'
IF (OBJECT_ID(N'[dbo].[syscollector_config_store_internal]', 'U') IS NOT NULL)
BEGIN

    IF (OBJECT_ID('tempdb..#data_collector_status', 'U') IS NOT NULL)
    BEGIN
        PRINT 'Dropping existing temp table tempdb..#data_collector_status...'
	DROP TABLE #data_collector_status
    END

    DECLARE @collector_enabled int;

    SELECT @collector_enabled = ISNULL(CONVERT(int, parameter_value),0)
    FROM [dbo].[syscollector_config_store_internal]
    WHERE parameter_name = 'CollectorEnabled'

    PRINT 'Data Collector state before upgrade: ' + CONVERT(varchar, @collector_enabled)

    SELECT @collector_enabled AS data_collector_old_status
    INTO #data_collector_status
END

--
-- CTP6->CTP6 Refresh
--

-- Drop the parent_log_id->log_id self reference
IF ((OBJECT_ID ('dbo.syscollector_execution_log_internal') IS NOT NULL) 
	AND (OBJECT_ID('dbo.FK_syscollector_execution_log_parent_log_id', 'F') IS NOT NULL))
BEGIN
    PRINT 'Dropping [FK_syscollector_execution_log_parent_log_id]';
    ALTER TABLE [dbo].[syscollector_execution_log_internal] DROP CONSTRAINT [FK_syscollector_execution_log_parent_log_id];
END

--
-- >>> CTP5 -> CTP6 Upgrade
--
-- Change int log_id columns to bigint 
IF (OBJECT_ID ('dbo.syscollector_execution_log_internal') IS NOT NULL) AND EXISTS (
    SELECT * 
    FROM sys.columns AS c
    INNER JOIN sys.types AS t ON c.system_type_id = t.system_type_id
    WHERE [object_id] = OBJECT_ID ('dbo.syscollector_execution_log_internal')
        AND c.name = 'log_id' AND t.name = 'int'
    )
BEGIN
    BEGIN TRY
        PRINT 'Starting log_id int -> bigint conversion'
        BEGIN TRANSACTION PreInstMsdb100_DCUpgrade
        -- Drop PK/FK constaints referencing log_id columns so we can change the data types of these columns
        PRINT 'Dropping [FK_syscollector_execution_stats_log_id]';
        ALTER TABLE [dbo].[syscollector_execution_stats_internal] DROP CONSTRAINT [FK_syscollector_execution_stats_log_id];
        PRINT 'Dropping [PK_syscollector_execution_stats]';
        ALTER TABLE [dbo].[syscollector_execution_stats_internal] DROP CONSTRAINT [PK_syscollector_execution_stats]; 
        PRINT 'Dropping [PK_syscollector_execution_log]';
        ALTER TABLE [dbo].[syscollector_execution_log_internal] DROP CONSTRAINT [PK_syscollector_execution_log];
        
        -- Truncate the DC log table to avoid causing unnecessary delays during CTP upgrade
        PRINT 'Truncating [syscollector_execution_log_internal]...';
        TRUNCATE TABLE [dbo].[syscollector_execution_log_internal];
        -- log_id values stored in syscollector_execution_stats will no longer be valid after we have truncated the log table
        PRINT 'Truncating [syscollector_execution_stats_internal]...';
        TRUNCATE TABLE [dbo].[syscollector_execution_stats_internal];
        
        -- Change the data type of all log_id columns
        PRINT 'Changing log_id column datatypes...';
        PRINT '   syscollector_execution_stats_internal.log_id';
        ALTER TABLE [dbo].[syscollector_execution_stats_internal] ALTER COLUMN [log_id] bigint NOT NULL;
        PRINT '   syscollector_execution_log_internal.log_id';
        ALTER TABLE [dbo].[syscollector_execution_log_internal] ALTER COLUMN [log_id] bigint NOT NULL; 
        PRINT '   syscollector_execution_log_internal.parent_log_id';
        ALTER TABLE [dbo].[syscollector_execution_log_internal] ALTER COLUMN [parent_log_id] bigint NULL;
    END TRY
    BEGIN CATCH
        IF (XACT_STATE() <> 0)
        BEGIN
            ROLLBACK TRANSACTION;
        END

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');
        PRINT 'ERROR: DC CTP5 to CTP6 upgrade tasks failed' 
        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
    END CATCH;
END
GO

-- Re-create the PK/FK constraints that we dropped in order to modify column datatypes
IF (OBJECT_ID ('dbo.syscollector_execution_log_internal') IS NOT NULL) AND NOT EXISTS (SELECT * FROM sys.objects WHERE name = 'PK_syscollector_execution_log' AND [schema_id] = SCHEMA_ID ('dbo'))
BEGIN
    BEGIN TRY
        PRINT 'Creating PK_syscollector_execution_log...';
        ALTER TABLE [dbo].[syscollector_execution_log_internal] ADD CONSTRAINT [PK_syscollector_execution_log] 
        PRIMARY KEY CLUSTERED (log_id ASC);
    END TRY
    BEGIN CATCH
        -- If we're still in the transaction started by the prior batch, roll it back so that it's 
        -- as if we never dropped any constraints
        PRINT 'ERROR: DC CTP5 to CTP6 upgrade tasks failed to create [PK_syscollector_execution_log] ' 
        IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
    END CATCH;
END
GO
IF (OBJECT_ID ('dbo.syscollector_execution_stats_internal') IS NOT NULL) AND NOT EXISTS (SELECT * FROM sys.objects WHERE name = 'PK_syscollector_execution_stats' AND [schema_id] = SCHEMA_ID ('dbo'))
BEGIN
    BEGIN TRY
        PRINT 'Creating PK_syscollector_execution_stats...';
        ALTER TABLE [dbo].[syscollector_execution_stats_internal] ADD CONSTRAINT [PK_syscollector_execution_stats] 
        PRIMARY KEY CLUSTERED (log_id ASC, task_name ASC, log_time DESC);
    END TRY
    BEGIN CATCH
        PRINT 'ERROR: DC CTP5 to CTP6 upgrade tasks failed to create [PK_syscollector_execution_stats] ' 
        IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
    END CATCH;
END
GO
IF (OBJECT_ID ('dbo.syscollector_execution_stats_internal') IS NOT NULL) AND NOT EXISTS (SELECT * FROM sys.objects WHERE name = 'FK_syscollector_execution_stats_log_id' AND [schema_id] = SCHEMA_ID ('dbo'))
BEGIN
    BEGIN TRY
        PRINT 'Creating FK_syscollector_execution_stats_log_id...';
        ALTER TABLE [dbo].[syscollector_execution_stats_internal] ADD CONSTRAINT [FK_syscollector_execution_stats_log_id] 
        FOREIGN KEY (log_id) REFERENCES [dbo].[syscollector_execution_log_internal] (log_id)
            ON DELETE CASCADE;
    END TRY
    BEGIN CATCH
        PRINT 'ERROR: DC CTP5 to CTP6 upgrade tasks failed to create [FK_syscollector_execution_stats_log_id] ' 
        IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
    END CATCH;
END
GO

IF (@@TRANCOUNT > 0) COMMIT TRANSACTION;
GO
IF (OBJECT_ID ('dbo.syscollector_execution_log_internal') IS NOT NULL)
BEGIN
    EXEC sp_refreshview 'dbo.syscollector_execution_log'
    EXEC sp_refreshview 'dbo.syscollector_execution_stats'
END
GO
--
-- <<< CTP5 - CTP6 Upgrade
--

--
-- >>> Delete auto-generated T-SQL packages stored in msdb. 
--

IF OBJECT_ID ('dbo.syscollector_tsql_query_collector') IS NOT NULL
BEGIN
    RAISERROR ('Deleting cached auto-generated T-SQL Data Collection packages from msdb...', 0, 1) WITH NOWAIT

    /*
    ** We have a row in [syscollector_tsql_query_collector] for each T-SQL collection item, and 
    ** each item has a collection and an upload package. There's a DELETE trigger on this table 
    ** that will take care of deleting the packages from sysssispackages for us.  The packages 
    ** will be re-generated automatically the next time their parent collection set's collection 
    ** package runs. 
    */ 
    DELETE FROM syscollector_tsql_query_collector
END
--
-- <<< Delete auto-generated T-SQL packages stored in msdb. 
--
GO

PRINT 'End of DC pre-upgrade steps.';

/**********************************************************************/
/* End of DC Pre-upgrade steps                                        */
/**********************************************************************/

/**********************************************************************/
/* DAC Pre-upgrade steps                                               */
/**********************************************************************/

RAISERROR('Starting DAC pre-upgrade steps ...', 0, 1) WITH NOWAIT;

--
-- SQL Server 2008 R2 : CTP2->CTP3 upgrade 
-- 1. sysdac_history table has two extra columns that we added in CTP3 (required, comments).
--    It has been decided not to preserve CTP2 history data  i.e. on an upgrade from CTP2->CTP3, deploy/uninstall logs are cleared. 
--
-- 2. {sysdac_packages_internal, sysdac_packages, sysdac_parts_internal, sysdac_parts} are deleted. They are replaced with new artifacts in CTP3.
--

IF (OBJECT_ID('[dbo].[sysdac_history_internal]', 'U') IS NOT NULL)
BEGIN
    IF NOT EXISTS(SELECT 1
	            FROM sys.columns 
	            WHERE (name = 'comments' OR name = 'required') AND  
	                    object_id = OBJECT_ID('[dbo].[sysdac_history_internal]', 'U')
	                    )						
    BEGIN
        DROP TABLE [dbo].[sysdac_history_internal]
    END
END
	
IF (OBJECT_ID('[dbo].[sysdac_parts]', 'V') IS NOT NULL)
    DROP VIEW [dbo].[sysdac_parts]	

IF (OBJECT_ID('[dbo].[sysdac_packages]', 'V') IS NOT NULL)
    DROP VIEW [dbo].[sysdac_packages]

IF (OBJECT_ID('[dbo].[sysdac_parts_internal]', 'U') IS NOT NULL)
    DROP TABLE [dbo].[sysdac_parts_internal]

IF (OBJECT_ID('[dbo].[sysdac_packages_internal]', 'U') IS NOT NULL)
    DROP TABLE [dbo].[sysdac_packages_internal]

/**********************************************************************/
/* End of DAC Pre-upgrade steps                                        */
/**********************************************************************/

GO


PRINT ''
PRINT '----------------------------------------'
PRINT 'Execution of PREINSTMSDB100.SQL complete'
PRINT '----------------------------------------'
go

/**********************************************************************/
/* INSTMSDB.SQL                                                       */
/*                                                                    */
/* Installs the tables, triggers and stored procedures necessary for  */
/* supporting local (and multi-server) jobs, alerts, operators, and   */
/* backup history.  These objects are used by SQL SMO, SQL Management */
/* Studio, SQLServerAgent, DMF, Data Collector and Synthesis          */
/*                                                                    */
/* Also contains SSIS (Data Transformation Services) tables and       */
/* stored procedures for local SQL Server storage of SSIS Packages.   */
/*                                                                    */
/*                                                                    */
/* Copyright Microsoft, Inc. 1994 - 2008                              */
/* All Rights Reserved.                                               */
/*                                                                    */
/**********************************************************************/

PRINT '----------------------------------'
PRINT 'Starting execution of INSTMSDB.SQL'
PRINT '----------------------------------'
go


--this version of instmsdb should be executed only against 10.0 servers
IF (@@microsoftversion / 0x01000000) <> 10
BEGIN
      RAISERROR('This version of instmsdb.sql should only be executed against 10.0 servers.', 20, 127) WITH LOG 
END
go

-- disable the event collection for policies while running this script
IF EXISTS (SELECT * FROM sys.server_triggers WHERE name = N'syspolicy_server_trigger')
    DISABLE TRIGGER [syspolicy_server_trigger] ON ALL SERVER 
GO

IF EXISTS (SELECT * FROM sys.service_queues where name = N'syspolicy_event_queue')
    ALTER QUEUE [syspolicy_event_queue] WITH ACTIVATION (STATUS = OFF)
GO

/*********************************************************************/
/* Create auxilary procedure to enable OBD (Off By Default component */
/*********************************************************************/
CREATE PROCEDURE #sp_enable_component     
   @comp_name     sysname, 
   @advopt_old_value    INT OUT, 
   @comp_old_value   INT OUT 
AS
   BEGIN
   SELECT @advopt_old_value=cast(value_in_use as int) from sys.configurations where name = 'show advanced options';
   SELECT @comp_old_value=cast(value_in_use as int) from sys.configurations where name = @comp_name; 
   EXEC sp_configure 'show advanced options',1;
   RECONFIGURE WITH OVERRIDE;
   EXEC sp_configure @comp_name, 1; 
   RECONFIGURE WITH OVERRIDE;
   END
go

CREATE PROCEDURE #sp_restore_component_state 
   @comp_name     sysname, 
   @advopt_old_value    INT, 
   @comp_old_value   INT 
AS
   BEGIN
   EXEC sp_configure @comp_name, @comp_old_value; 
   RECONFIGURE WITH OVERRIDE;
   EXEC sp_configure 'show advanced options',@advopt_old_value;
   RECONFIGURE WITH OVERRIDE;
   END
go

-- Explicitly set the options that the server stores with the object in sysobjects.status
-- so that it doesn't matter if the script is run using a DBLib or ODBC based client.
SET QUOTED_IDENTIFIER OFF -- We don't use quoted identifiers
SET ANSI_NULLS ON         -- We don't want (NULL = NULL) == TRUE
go
SET ANSI_PADDING ON       -- Set so that trailing zeros aren't trimmed off sysjobs.owner_login_sid
go

-- Allow updates to system catalogs so that all our SP's inherit full DML capability on
-- system objects and so that we can exercise full DDL control on our system objects
EXECUTE master.dbo.sp_configure N'allow updates', 1
go
RECONFIGURE WITH OVERRIDE
go

/**************************************************************/
/*                                                            */
/*      D  A  T  A  B  A  S  E    C  R  E  A  T  I  O  N      */
/*                                                            */
/**************************************************************/

IF (NOT EXISTS (SELECT name
                FROM master.dbo.sysdatabases
                WHERE (name = N'msdb')))
BEGIN
  PRINT 'Creating the msdb database...'
END
go

USE master
go

SET NOCOUNT ON

-- NOTE: It is important that this script can be re-run WITHOUT causing loss of data, hence
--       we only create the database if it missing (if the database already exists we test
--       that it has enough free space and if not we expand both the device and the database).
DECLARE @model_db_size    INT
DECLARE @msdb_db_size     INT
DECLARE @sz_msdb_db_size  VARCHAR(10)
DECLARE @device_directory NVARCHAR(520)
DECLARE @page_size        INT
DECLARE @size             INT
DECLARE @free_db_space    FLOAT

SELECT @page_size = 8

IF (NOT EXISTS (SELECT name
                FROM master.dbo.sysdatabases
                WHERE (name = N'msdb')))
BEGIN
  -- Make sure that we create [the data portion of] MSDB to be at least as large as
  -- the MODEL database
  SELECT @model_db_size = (SUM(size) * @page_size)
  FROM model.dbo.sysfiles

  IF (@model_db_size > 3072) -- 3 is the minimum required size for MSDB (in megabytes)
    SELECT @msdb_db_size = @model_db_size
  ELSE
    SELECT @msdb_db_size = 3072

  SELECT @device_directory = SUBSTRING(filename, 1, CHARINDEX(N'master.mdf', LOWER(filename)) - 1)
  FROM master.dbo.sysaltfiles
  WHERE (name = N'master')

  -- Drop any existing MSDBData / MSDBLog file(s)
  DECLARE   @advopt_old_value    INT 
  DECLARE   @comp_old_value   INT
  EXECUTE #sp_enable_component 'xp_cmdshell', @advopt_old_value out, @comp_old_value out
  EXECUTE(N'EXECUTE master.dbo.xp_cmdshell N''DEL ' + @device_directory + N'MSDBData.mdf'', no_output')
  EXECUTE(N'EXECUTE master.dbo.xp_cmdshell N''DEL ' + @device_directory + N'MSDBLog.ldf'', no_output')
  EXECUTE #sp_restore_component_state 'xp_cmdshell', @advopt_old_value, @comp_old_value

  -- Create the database
  PRINT ''
  PRINT 'Creating MSDB database...'
  SELECT @sz_msdb_db_size = RTRIM(LTRIM(CONVERT(VARCHAR, @msdb_db_size)))
  EXECUTE (N'CREATE DATABASE msdb ON (NAME = N''MSDBData'', FILENAME = N''' + @device_directory + N'MSDBData.mdf'', SIZE = ' + @sz_msdb_db_size + N'KB, MAXSIZE = UNLIMITED, FILEGROWTH = 10%)
                         LOG ON (NAME = N''MSDBLog'',  FILENAME = N''' + @device_directory + N'MSDBLog.ldf'',  SIZE = 512KB, MAXSIZE = UNLIMITED, FILEGROWTH = 10%)')
  EXECUTE (N'ALTER DATABASE msdb SET DB_CHAINING ON')
  PRINT ''
END
ELSE
BEGIN
  PRINT 'Checking the size of MSDB...'

  DBCC UPDATEUSAGE(N'msdb') WITH NO_INFOMSGS

  -- Make sure that MSDBLog has unlimited growth
  ALTER DATABASE msdb MODIFY FILE (NAME = N'MSDBLog', MAXSIZE = 2TB)

  -- Determine amount of free space in msdb. We need at least 2MB free.
  SELECT @free_db_space = ((((SELECT SUM(size)
                              FROM msdb.dbo.sysfiles
                              WHERE status & 0x8040 = 0) -
                             (SELECT SUM(reserved)
                              FROM msdb.dbo.sysindexes
                              WHERE indid IN (0, 1, 255))) * @page_size) / 1024.0)

  IF (@free_db_space < 2)
  BEGIN
    DECLARE @logical_file_name sysname
    DECLARE @os_file_name      NVARCHAR(255)
    DECLARE @size_as_char      VARCHAR(10)
   
    SELECT @logical_file_name = name,
           @os_file_name = filename,
           @size_as_char = CONVERT(VARCHAR(10), size*8 + 2048) -- size column in sysaltfiles is in number of 8KB pages
    FROM master.dbo.sysaltfiles
    WHERE (name = N'MSDBData')
    set @os_file_name = QUOTENAME(@os_file_name,'''')
    PRINT 'Attempting to expand the msdb database...'
    EXECUTE(N'ALTER DATABASE msdb MODIFY FILE (NAME = N''' + @logical_file_name + N''',
                                               FILENAME = N' + @os_file_name + N',
                                               SIZE =' + @size_as_char + N'KB)')    
    IF (@@error <> 0)
      RAISERROR('Unable to expand the msdb database. INSTMSDB.SQL terminating.', 20, 127) WITH LOG
  END
  PRINT ''
END

EXECUTE (N'ALTER DATABASE msdb SET TRUSTWORTHY ON')

go

-- truncate log on checkpoint
ALTER DATABASE msdb 
SET RECOVERY SIMPLE
go

USE msdb
go

-- Check that we're in msdb
IF (DB_NAME() <> N'msdb')
  RAISERROR('A problem was encountered accessing msdb. INSTMSDB.SQL terminating.', 20, 127) WITH LOG
go

-- Add the guest user
IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysusers
                WHERE (name = N'guest')
                  AND (hasdbaccess = 1)))
BEGIN
  PRINT ''
  EXECUTE sys.sp_adduser N'guest'
END
go

CHECKPOINT
go

/**************************************************************/
/* Record time of start of creates                            */
/**************************************************************/
SELECT start = getdate() INTO #InstMsdb
go

/**************************************************************/
/*                                                            */
/*              T  A  B  L  E     D  R  O  P  S               */
/*                                                            */
/**************************************************************/

SET NOCOUNT ON

DECLARE @build_number   INT
SELECT @build_number = @@microsoftversion & 0xffff

-- Do any necessary changes based on build number here



/**************************************************************/
/* drop certificate signature from Agent signed sps           */
/**************************************************************/

BEGIN TRANSACTION
declare @sp sysname
declare @exec_str nvarchar(1024)
declare ms_crs_sps cursor global for select object_name(crypts.major_id) 
   from sys.crypt_properties crypts, sys.certificates certs
   where crypts.thumbprint = certs.thumbprint
   and crypts.class = 1
   and certs.name = '##MS_AgentSigningCertificate##'
open ms_crs_sps
fetch next from ms_crs_sps into @sp
while @@fetch_status = 0
begin
   if exists(select * from sys.objects where name = @sp)
   begin
      print 'Dropping signature from: ' + @sp
      set @exec_str = N'drop signature from ' + quotename(@sp) + N' by certificate [##MS_AgentSigningCertificate##]'
      Execute(@exec_str)
      if (@@error <> 0)
      begin
         declare @err_str nvarchar(1024)
         set @err_str = 'Cannot drop signature from ' + quotename(@sp) + '. Terminating.'
         close ms_crs_sps
         deallocate ms_crs_sps
         ROLLBACK TRANSACTION
         RAISERROR(@err_str, 20, 127) WITH LOG
         return
      end
   end
   fetch next from ms_crs_sps into @sp
end
close ms_crs_sps
deallocate ms_crs_sps
COMMIT TRANSACTION
go


---------------------------------------------------------------
-- Replication Datatype mapping tables
---------------------------------------------------------------

-- REVIEW - Is this needed? What does this do?
exec sys.sp_MSrepl_dropdatatypemappings
go

CHECKPOINT
go

/**************************************************************/
/*                                                            */
/*                     D  E  F  A  U  L  T  S                 */
/*                                                            */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'default_sdl_error_message')
                  AND (type = 'D')))
  EXECUTE('CREATE DEFAULT default_sdl_error_message AS NULL')
go
IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'default_current_date')
                  AND (type = 'D')))
  EXECUTE('CREATE DEFAULT default_current_date AS GETDATE()')
go
IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'default_zero')
                  AND (type = 'D')))
  EXECUTE('CREATE DEFAULT default_zero AS 0')
go
IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'default_one')
                  AND (type = 'D')))
  EXECUTE('CREATE DEFAULT default_one AS 1')
go

/**************************************************************/
/*                                                            */
/*                       T  A  B  L  E  S                     */
/*                                                            */
/**************************************************************/

/**************************************************************/
/* SYSPROXIES                                              */
/**************************************************************/
IF (OBJECT_ID(N'dbo.sysproxies', 'U') IS NULL)
BEGIN
   PRINT ''
   PRINT 'Creating table sysproxies...'
   CREATE TABLE dbo.sysproxies
   (
      proxy_id             INT               IDENTITY,   --used to identify a proxy
      name                  sysname       NOT NULL,   --friendly name of a proxy
    credential_id         INT           NOT NULL,
      enabled                TINYINT       NOT NULL,  
      description           NVARCHAR(512) NULL,     --nvarchar(512)  
    user_sid              VARBINARY(85) NOT NULL,  --sid of proxy NT user
    credential_date_created  DATETIME   NOT NULL   --the date the associated credential has been created   
   )
   CREATE UNIQUE CLUSTERED    INDEX clust ON sysproxies(proxy_id)
   CREATE UNIQUE NONCLUSTERED INDEX nc1   ON sysproxies(name) 
END
go

IF (OBJECT_ID(N'dbo.syssubsystems', 'U') IS NULL)
BEGIN
   PRINT ''
   PRINT 'Creating table syssubsystems...'
   CREATE TABLE dbo.syssubsystems
   (
      subsystem_id       INT         NOT NULL,                       -- used to identify the subsystem 
      subsystem          NVARCHAR(40)  COLLATE database_default NOT NULL,    -- Name of subsystem
      description_id     INT         NULL,                           -- Message number of description string. These messages are stored in sysmessages table for localization purposes. Same story as for Shiloh
      subsystem_dll      NVARCHAR(255) COLLATE database_default NULL,        -- Store full path of the name of subsystem dll  subsystem are always installed in the binn folder of an instance
      agent_exe          NVARCHAR(255) COLLATE database_default NULL,        -- Full path to the executable that use the subsystem
      start_entry_point  NVARCHAR(30)  COLLATE database_default NULL,        -- Function called when initializing subsystem
      event_entry_point  NVARCHAR(30)  COLLATE database_default NULL,        -- Function called when executing a subsystem step 
      stop_entry_point   NVARCHAR(30)  COLLATE database_default NULL,        -- Function called when unloading a subsystem 
      max_worker_threads INT           NULL                                  -- Number of maximum concurrent steps for a subsystem 
   )
   CREATE UNIQUE CLUSTERED    INDEX clust ON syssubsystems(subsystem_id)
   CREATE UNIQUE NONCLUSTERED INDEX nc1   ON syssubsystems(subsystem) 
END
go

IF (OBJECT_ID(N'dbo.sysproxysubsystem', 'U') IS NULL)
BEGIN
   PRINT ''
   PRINT 'Creating table sysproxysubsystem...'

   CREATE TABLE dbo.sysproxysubsystem
   (
      subsystem_id INT           NOT NULL,  --  used to identify the subsystem 
      proxy_id     INT           NOT NULL,  --  used to identify the proxy 
   )
   CREATE UNIQUE CLUSTERED    INDEX clust ON sysproxysubsystem(subsystem_id, proxy_id)
END
go

IF (OBJECT_ID(N'dbo.sysproxylogin', 'U') IS NULL)
BEGIN
   PRINT ''
   PRINT 'Creating table sysproxylogin...'

   CREATE TABLE dbo.sysproxylogin
   (
      proxy_id     INT           NOT NULL,  --used to identify the proxy 
    sid          VARBINARY(85) NULL,       --keep logins, fixed server roles or msdb database roles
      flags        INT           DEFAULT 0 NOT NULL   -- tells is member_id is login = 0, server fixed role, msdb role. 
   )
   CREATE UNIQUE CLUSTERED    INDEX clust ON sysproxylogin(proxy_id, sid, flags)
END
go

PRINT ''
PRINT 'Creating view sysproxyloginsubsystem_view...'
go
IF (NOT OBJECT_ID(N'dbo.sysproxyloginsubsystem_view', 'V') IS NULL)
  DROP VIEW sysproxyloginsubsystem_view
go
CREATE VIEW sysproxyloginsubsystem_view
AS
SELECT ps.subsystem_id AS subsystem_id, pl.proxy_id AS proxy_id, pl.sid AS sid, pl.flags AS flags
FROM sysproxylogin pl JOIN sysproxysubsystem ps ON pl.proxy_id = ps.proxy_id
go

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sqlagent_info')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sqlagent_info...'

  CREATE TABLE sqlagent_info
  (
  attribute sysname       NOT NULL,
  value     NVARCHAR(512) NOT NULL
  )
END
go

/**************************************************************/
/* SYSDOWNLOADLIST                                            */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysdownloadlist')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysdownloadlist...'

  CREATE TABLE sysdownloadlist
  (
  instance_id         INT IDENTITY     NOT NULL,
  source_server       sysname          NOT NULL,
  operation_code      TINYINT          NOT NULL,
  object_type         TINYINT          NOT NULL,
  object_id           UNIQUEIDENTIFIER NOT NULL,
  target_server       sysname        NOT NULL,
  error_message       NVARCHAR(1024)   NULL,
  date_posted         DATETIME         NOT NULL,
  date_downloaded     DATETIME         NULL,
  status              TINYINT          NOT NULL,
  deleted_object_name sysname          NULL
  )

  EXECUTE sys.sp_bindefault default_sdl_error_message, N'sysdownloadlist.error_message'
  EXECUTE sys.sp_bindefault default_current_date,      N'sysdownloadlist.date_posted'
  EXECUTE sys.sp_bindefault default_zero,              N'sysdownloadlist.status'

  CREATE UNIQUE CLUSTERED INDEX clust ON sysdownloadlist(instance_id)
  CREATE NONCLUSTERED     INDEX nc1   ON sysdownloadlist(target_server)
  CREATE NONCLUSTERED     INDEX nc2   ON sysdownloadlist(object_id)
END
go

/**************************************************************/
/* SYSJOBHISTORY                                              */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysjobhistory')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysjobhistory...'

  CREATE TABLE sysjobhistory
  (
  instance_id          INT IDENTITY     NOT NULL,
  job_id               UNIQUEIDENTIFIER NOT NULL,
  step_id              INT              NOT NULL,
  step_name            sysname          NOT NULL,
  sql_message_id       INT              NOT NULL,
  sql_severity         INT              NOT NULL,
  message              NVARCHAR(4000)   NULL,
  run_status           INT              NOT NULL,
  run_date             INT              NOT NULL,
  run_time             INT              NOT NULL,
  run_duration         INT              NOT NULL,
  operator_id_emailed  INT              NOT NULL,
  operator_id_netsent  INT              NOT NULL,
  operator_id_paged    INT              NOT NULL,
  retries_attempted    INT              NOT NULL,
  server               sysname          NOT NULL
  )

  CREATE UNIQUE CLUSTERED INDEX clust ON sysjobhistory(instance_id)
  CREATE NONCLUSTERED     INDEX nc1   ON sysjobhistory(job_id)
END
ELSE
BEGIN
  ALTER TABLE sysjobhistory ALTER COLUMN
    message NVARCHAR(4000)   NULL
END
go


/**************************************************************/
/* sysoriginatingservers                                      */
/**************************************************************/
IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysoriginatingservers')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysoriginatingservers...'

CREATE TABLE dbo.sysoriginatingservers
(
  -- There is only a single MSX server record in this table (originating_server_id = 1)
  -- 0 is generated by sysoriginatingservers_view and indicates the local server
  originating_server_id INT      CONSTRAINT CK_originating_server_id_MustBe_1 CHECK (originating_server_id = 1) 
                             DEFAULT (1) UNIQUE CLUSTERED,           
  originating_server    sysname     NOT NULL UNIQUE NONCLUSTERED,
  --Mark this record as a MSX server entry
  master_server         bit         CONSTRAINT CK_master_server_MustBe_1 CHECK (master_server = 1) 
                             DEFAULT (1)  
)

END
go


/**************************************************************/
/* trig_sysoriginatingservers_delete                          */
/**************************************************************/
PRINT ''
PRINT 'Creating trigger trig_sysoriginatingservers_delete...'

IF NOT OBJECT_ID('dbo.trig_sysoriginatingservers_delete', 'TR') IS NULL
    DROP TRIGGER dbo.trig_sysoriginatingservers_delete
GO
CREATE TRIGGER dbo.trig_sysoriginatingservers_delete
ON dbo.sysoriginatingservers
FOR DELETE
AS
BEGIN
  SET NOCOUNT ON
  -- Only a single MSX server entry can exist in this table. ie. originating_server_id = 1 and master_server = 1. 
  IF((EXISTS (SELECT *
           FROM deleted AS d
                JOIN dbo.sysjobs AS j ON d.originating_server_id = j.originating_server_id)) OR
    (EXISTS (SELECT *
           FROM deleted AS d
                JOIN dbo.sysschedules AS s ON d.originating_server_id = s.originating_server_id)))
  BEGIN
    RAISERROR(14380, -1, -1)
   ROLLBACK TRANSACTION
    RETURN
  END
END
go


/**************************************************************/
/* sysoriginatingservers_view                                 */
/**************************************************************/
PRINT ''
PRINT 'Creating view sysoriginatingservers_view...'
GO
IF (NOT OBJECT_ID(N'dbo.sysoriginatingservers_view', 'V') IS NULL)
  DROP VIEW sysoriginatingservers_view
GO
CREATE VIEW dbo.sysoriginatingservers_view(originating_server_id, originating_server, master_server)
AS 
   SELECT
      0 AS originating_server_id, 
      UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) AS originating_server,
      0 AS master_server
   UNION
   SELECT 
      originating_server_id,
      originating_server,
      master_server
   FROM
      dbo.sysoriginatingservers
GO

/**************************************************************/
/* SYSJOBS                                                    */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysjobs')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysjobs...'

  CREATE TABLE sysjobs
  (
  job_id                     UNIQUEIDENTIFIER NOT NULL,
  originating_server_id      INT              NOT NULL, -- REFERENCE enforced by trig_sysjobs_insert_update
  name                       sysname          NOT NULL,
  enabled                    TINYINT          NOT NULL,
  description                NVARCHAR(512)    NULL,
  start_step_id              INT              NOT NULL,
  category_id                INT              NOT NULL,
  owner_sid                  VARBINARY(85)    NOT NULL,
  notify_level_eventlog      INT              NOT NULL,
  notify_level_email         INT              NOT NULL,
  notify_level_netsend       INT              NOT NULL,
  notify_level_page          INT              NOT NULL,
  notify_email_operator_id   INT              NOT NULL,
  notify_netsend_operator_id INT              NOT NULL,
  notify_page_operator_id    INT              NOT NULL,
  delete_level               INT              NOT NULL,
  date_created               DATETIME         NOT NULL,
  date_modified              DATETIME         NOT NULL,
  version_number             INT              NOT NULL
  )

  CREATE UNIQUE CLUSTERED INDEX clust ON sysjobs(job_id)
  CREATE NONCLUSTERED     INDEX nc1   ON sysjobs(name) -- NOTE: This is deliberately non-unique
  CREATE NONCLUSTERED     INDEX nc3   ON sysjobs(category_id)
  CREATE NONCLUSTERED     INDEX nc4   ON sysjobs(owner_sid)
END
go

/**************************************************************/
/* trig_sysjobs_insert_update                                 */
/**************************************************************/

PRINT ''
PRINT 'Creating trigger trig_sysjobs_insert_update...'

IF NOT OBJECT_ID('dbo.trig_sysjobs_insert_update', 'TR') IS NULL
    DROP TRIGGER dbo.trig_sysjobs_insert_update
GO
CREATE TRIGGER dbo.trig_sysjobs_insert_update
ON dbo.sysjobs
FOR INSERT, UPDATE
AS
BEGIN
  SET NOCOUNT ON
  -- Disallow the insert or update if the originating_server_id isn't in sysoriginatingservers_view.  
  IF (EXISTS (SELECT *
            FROM inserted
           WHERE inserted.originating_server_id NOT IN 
                    (SELECT v.originating_server_id 
                     FROM sysoriginatingservers_view AS v)))
  BEGIN
   RAISERROR(14379, -1, -1, 'dbo.sysjobs')
   ROLLBACK TRANSACTION
    RETURN
  END
END
go

/**************************************************************/
/* SYSJOBSERVERS                                              */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysjobservers')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysjobservers...'

  CREATE TABLE sysjobservers
  (
  job_id               UNIQUEIDENTIFIER NOT NULL,
  server_id            INT              NOT NULL,
  last_run_outcome     TINYINT          NOT NULL,
  last_outcome_message NVARCHAR(4000)   NULL,
  last_run_date        INT              NOT NULL,
  last_run_time        INT              NOT NULL,
  last_run_duration    INT              NOT NULL
  )

  CREATE CLUSTERED    INDEX clust ON sysjobservers(job_id)
  CREATE NONCLUSTERED INDEX nc1   ON sysjobservers(server_id)
END
ELSE
BEGIN
  ALTER TABLE sysjobservers ALTER COLUMN
    last_outcome_message NVARCHAR(4000)   NULL
END
go

/**************************************************************/
/* SYSJOBS_VIEW                                               */
/**************************************************************/

PRINT ''
PRINT 'Creating view sysjobs_view...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sysjobs_view')
              AND (type = 'V')))
  DROP VIEW sysjobs_view
go
CREATE VIEW sysjobs_view
AS
SELECT jobs.job_id,
       svr.originating_server,
       jobs.name,
       jobs.enabled,
       jobs.description,
       jobs.start_step_id,
       jobs.category_id,
       jobs.owner_sid,
       jobs.notify_level_eventlog,
       jobs.notify_level_email,
       jobs.notify_level_netsend,
       jobs.notify_level_page,
       jobs.notify_email_operator_id,
       jobs.notify_netsend_operator_id,
       jobs.notify_page_operator_id,
       jobs.delete_level,
       jobs.date_created,
       jobs.date_modified,
       jobs.version_number,
       jobs.originating_server_id,
       svr.master_server
FROM msdb.dbo.sysjobs as jobs
  JOIN msdb.dbo.sysoriginatingservers_view as svr
    ON jobs.originating_server_id = svr.originating_server_id
  --LEFT JOIN msdb.dbo.sysjobservers js ON jobs.job_id = js.job_id
WHERE (owner_sid = SUSER_SID())
   OR (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)
   OR (ISNULL(IS_MEMBER(N'SQLAgentReaderRole'), 0) = 1)
   OR ( (ISNULL(IS_MEMBER(N'TargetServersRole'), 0) = 1) AND
        (EXISTS(SELECT * FROM msdb.dbo.sysjobservers js 
         WHERE js.server_id <> 0 AND js.job_id = jobs.job_id))) -- filter out local jobs  
go



IF (OBJECT_ID(N'dbo.syssessions', 'U') IS NULL)
BEGIN
   PRINT ''
   PRINT 'Creating table syssessions...'

   CREATE TABLE dbo.syssessions
   (
      session_id                INT  IDENTITY PRIMARY KEY,       
    agent_start_date          DATETIME NOT NULL    
   )
   CREATE UNIQUE NONCLUSTERED INDEX nonclust ON syssessions(agent_start_date)
END
go

IF (OBJECT_ID(N'dbo.sysjobactivity', 'U') IS NULL)
BEGIN
   PRINT ''
   PRINT 'Creating table sysjobactivity...'

   CREATE TABLE dbo.sysjobactivity
   (
      session_id                INT              NOT NULL REFERENCES syssessions(session_id),           
      job_id                    UNIQUEIDENTIFIER NOT NULL REFERENCES  sysjobs(job_id) ON DELETE CASCADE,           
    run_requested_date        DATETIME         NULL,
    run_requested_source      sysname          NULL,
    queued_date               DATETIME         NULL,
    start_execution_date      DATETIME         NULL,
    last_executed_step_id     INT              NULL,
    last_executed_step_date   DATETIME         NULL,
    stop_execution_date       DATETIME         NULL,
    job_history_id            INT              NULL,      --keeps a reference to the record in sysjobhistory for detailed job information
    next_scheduled_run_date   DATETIME         NULL
   )
   CREATE UNIQUE CLUSTERED    INDEX clust ON sysjobactivity(session_id, job_id)
END
go


/**************************************************************/
/* SYSJOBSTEPS                                                */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysjobsteps')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysjobsteps...'

  CREATE TABLE sysjobsteps
  (
  job_id                UNIQUEIDENTIFIER NOT NULL,
  step_id               INT              NOT NULL,
  step_name             sysname          NOT NULL,
  subsystem             NVARCHAR(40)    NOT NULL,
  command               NVARCHAR(max)    NULL,
  flags                 INT              NOT NULL,
  additional_parameters NTEXT            NULL,
  cmdexec_success_code  INT              NOT NULL,
  on_success_action     TINYINT          NOT NULL,
  on_success_step_id    INT              NOT NULL,
  on_fail_action        TINYINT          NOT NULL,
  on_fail_step_id       INT              NOT NULL,
  server                sysname          NULL,      -- Used only by replication and OLAP
  database_name         sysname          NULL,
  database_user_name    sysname          NULL,
  retry_attempts        INT              NOT NULL,
  retry_interval        INT              NOT NULL,
  os_run_priority       INT              NOT NULL,  -- NOTE: Cannot use TINYINT because we need a signed number
  output_file_name      NVARCHAR(200)    NULL,
  last_run_outcome      INT              NOT NULL,
  last_run_duration     INT              NOT NULL,
  last_run_retries      INT              NOT NULL,
  last_run_date         INT              NOT NULL,
  last_run_time         INT              NOT NULL,
  proxy_id              INT              NULL,
  step_uid              UNIQUEIDENTIFIER NULL
  )

  CREATE UNIQUE CLUSTERED INDEX clust ON sysjobsteps(job_id, step_id)
  CREATE UNIQUE NONCLUSTERED INDEX nc1 ON sysjobsteps(job_id, step_name)
  CREATE UNIQUE NONCLUSTERED INDEX nc2 ON sysjobsteps(step_uid)

END
go


/**************************************************************/
/* SYSJOBSTEPSLOGS                                                */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysjobstepslogs')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysjobstepslogs...'

  CREATE TABLE sysjobstepslogs
  (
  log_id                INT IDENTITY (1,1) PRIMARY KEY NOT NULL,
  log                   NVARCHAR(max)    NOT NULL,
  date_created          DATETIME         NOT NULL DEFAULT getdate(),
  date_modified         DATETIME         NOT NULL DEFAULT getdate(),
  log_size              bigint           NOT NULL ,
  step_uid              UNIQUEIDENTIFIER NOT NULL REFERENCES  sysjobsteps(step_uid) ON DELETE CASCADE 
  )
END
go


/**************************************************************/
/* SYSSCHEDULES                                               */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysschedules')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysschedules...'

  CREATE TABLE sysschedules
  (
  schedule_id            INT IDENTITY     PRIMARY KEY CLUSTERED,
  schedule_uid           UNIQUEIDENTIFIER NOT NULL,
  originating_server_id  INT              NOT NULL, -- REFERENCE enforced by trig_sysschedules_insert_update
  name                   sysname          NOT NULL,
  owner_sid              varbinary(85)    NOT NULL, 
  enabled                INT              NOT NULL,
  freq_type              INT              NOT NULL,
  freq_interval          INT              NOT NULL,
  freq_subday_type       INT              NOT NULL,
  freq_subday_interval   INT              NOT NULL,
  freq_relative_interval INT              NOT NULL,
  freq_recurrence_factor INT              NOT NULL,
  active_start_date      INT              NOT NULL,
  active_end_date        INT              NOT NULL,
  active_start_time      INT              NOT NULL,
  active_end_time        INT              NOT NULL,
  date_created           DATETIME         NOT NULL  DEFAULT (GETDATE()),
  date_modified          DATETIME         NOT NULL  DEFAULT (GETDATE()),
  version_number         INT              NOT NULL  DEFAULT (1)
  )

  -- CREATE UNIQUE CLUSTERED INDEX clust ON sysschedules(job_id, name)
END
go

/**************************************************************/
/* trig_sysschedules_insert_update                            */
/**************************************************************/

PRINT ''
PRINT 'Creating trigger trig_sysschedules_insert_update...'

IF NOT OBJECT_ID('dbo.trig_sysschedules_insert_update', 'TR') IS NULL
    DROP TRIGGER dbo.trig_sysschedules_insert_update
GO
CREATE TRIGGER dbo.trig_sysschedules_insert_update
ON dbo.sysschedules
FOR INSERT, UPDATE
AS
BEGIN
  SET NOCOUNT ON
  -- Disallow the insert or update if the originating_server_id isn't in sysoriginatingservers_view. 
  IF (EXISTS (SELECT *
            FROM inserted
           WHERE inserted.originating_server_id NOT IN 
                    (SELECT v.originating_server_id 
                     FROM sysoriginatingservers_view AS v)))
  BEGIN
   RAISERROR(14379, -1, -1, 'dbo.sysschedules')
   ROLLBACK TRANSACTION
    RETURN
  END
END
go

/**************************************************************/
/* SYSSCHEDULES_LOCALSERVER_VIEW                              */
/**************************************************************/

PRINT ''
PRINT 'Creating view sysschedules_localserver_view...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sysschedules_localserver_view')
              AND (type = 'V')))
  DROP VIEW sysschedules_localserver_view
go
CREATE VIEW sysschedules_localserver_view
AS
SELECT sched.schedule_id,
       sched.schedule_uid,
       sched.originating_server_id,
       sched.name,
       sched.owner_sid,
       sched.enabled,
       sched.freq_type,
       sched.freq_interval,
       sched.freq_subday_type,
       sched.freq_subday_interval,
       sched.freq_relative_interval,
       sched.freq_recurrence_factor,
       sched.active_start_date,
       sched.active_end_date,
       sched.active_start_time,
       sched.active_end_time,
       sched.date_created,
       sched.date_modified,
       sched.version_number,
       svr.originating_server,
       svr.master_server
FROM msdb.dbo.sysschedules as sched
    JOIN msdb.dbo.sysoriginatingservers_view as svr
    ON sched.originating_server_id = svr.originating_server_id
WHERE (svr.master_server = 0)
  AND ( (sched.owner_sid = SUSER_SID())
        OR (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)
      OR (ISNULL(IS_MEMBER(N'SQLAgentReaderRole'), 0) = 1)
      )
go


/**************************************************************/
/* SYSJOBSCHEDULES                                            */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysjobschedules')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysjobschedules...'

  CREATE TABLE sysjobschedules
  (
  schedule_id            INT              REFERENCES sysschedules(schedule_id),
  job_id                 UNIQUEIDENTIFIER REFERENCES sysjobs(job_id),
  next_run_date          INT              NOT NULL DEFAULT 0,
  next_run_time          INT              NOT NULL DEFAULT 0
  )

  CREATE UNIQUE CLUSTERED INDEX clust ON sysjobschedules(job_id, schedule_id)
  CREATE NONCLUSTERED INDEX [NC_sysjobschedules_schedule_id] ON sysjobschedules(schedule_id)
END
go


/**************************************************************/
/* SYSCATEGORIES                                              */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'syscategories')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table syscategories...'

  CREATE TABLE syscategories
  (
  category_id    INT IDENTITY NOT NULL,
  category_class INT          NOT NULL, -- 1 = Job, 2 = Alert, 3 = Operator
  category_type  TINYINT      NOT NULL, -- 1 = Local, 2 = Multi-Server [Only relevant if class is 1; otherwise, 3 (None)]
  name           sysname      NOT NULL
  )

  CREATE UNIQUE CLUSTERED INDEX clust ON syscategories(name, category_class)
END
go

-- Install standard [permanent] categories (reserved ID range is 0 - 99)
SET IDENTITY_INSERT msdb.dbo.syscategories ON

DELETE FROM msdb.dbo.syscategories
WHERE (category_id < 100)

-- Core categories
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 0, 1, 1, N'[Uncategorized (Local)]')        -- Local default
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 1, 1, 1, N'Jobs from MSX')                  -- All jobs downloaded from the MSX are placed in this category
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 2, 1, 2, N'[Uncategorized (Multi-Server)]') -- Multi-server default
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 3, 1, 1, N'Database Maintenance')           -- Default for all jobs created by the Maintenance Plan Wizard
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 5, 1, 1, N'Full-Text')                      -- Default for all jobs created by the Index Server
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 6, 1, 1, N'Log Shipping')                   -- Default for Log Shipping jobs
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 7, 1, 1, N'Database Engine Tuning Advisor') -- Default for DTA jobs
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 8, 1, 1, N'Data Collector')                   -- Default for all jobs created by the Data Collector
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (98, 2, 3, N'[Uncategorized]')                -- Alert default
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (99, 3, 3, N'[Uncategorized]')                -- Operator default

-- Replication categories
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (10, 1, 1, N'REPL-Distribution')
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (11, 1, 1, N'REPL-Distribution Cleanup')
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (12, 1, 1, N'REPL-History Cleanup')
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (13, 1, 1, N'REPL-LogReader')
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (14, 1, 1, N'REPL-Merge')
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (15, 1, 1, N'REPL-Snapshot')
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (16, 1, 1, N'REPL-Checkup')
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (17, 1, 1, N'REPL-Subscription Cleanup')
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (18, 1, 1, N'REPL-Alert Response')
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (19, 1, 1, N'REPL-QueueReader')
INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (20, 2, 3, N'Replication')

SET IDENTITY_INSERT msdb.dbo.syscategories OFF
go

/**************************************************************/
/* SYSTARGETSERVERS                                           */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'systargetservers')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table systargetservers...'

  CREATE TABLE systargetservers
  (
  server_id               INT IDENTITY  NOT NULL,
  server_name             sysname       NOT NULL,
  location                NVARCHAR(200) NULL,
  time_zone_adjustment    INT           NOT NULL,  -- The offset from GMT in minutes (set by sp_msx_enlist)
  enlist_date             DATETIME      NOT NULL,
  last_poll_date          DATETIME      NOT NULL,
  status                  INT           NOT NULL,  -- 1 = Normal, 2 = Offline, 4 = Blocked
  local_time_at_last_poll DATETIME      NOT NULL,  -- The local time at the target server as-of the last time it polled the MSX
  enlisted_by_nt_user     NVARCHAR(100) NOT NULL,
  poll_interval           INT           NOT NULL   -- The MSX polling interval (in seconds)
  )

  EXECUTE sys.sp_bindefault default_current_date, N'systargetservers.enlist_date'
  EXECUTE sys.sp_bindefault default_current_date, N'systargetservers.last_poll_date'
  EXECUTE sys.sp_bindefault default_one,          N'systargetservers.status'

  CREATE UNIQUE CLUSTERED    INDEX clust ON systargetservers(server_id)
  CREATE UNIQUE NONCLUSTERED INDEX nc1   ON systargetservers(server_name)
END
go

/**************************************************************/
/* SYSTARGETSERVERS_VIEW                                      */
/**************************************************************/

PRINT ''
PRINT 'Creating view systargetservers_view...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'systargetservers_view')
              AND (type = 'V')))
  DROP VIEW systargetservers_view
go
CREATE VIEW systargetservers_view
AS
SELECT server_id,
       server_name,
       enlist_date,
       last_poll_date
FROM msdb.dbo.systargetservers
UNION
SELECT 0,
       CONVERT(sysname, SERVERPROPERTY('ServerName')),
       CONVERT(DATETIME, N'19981113', 112),
       CONVERT(DATETIME, N'19981113', 112)
go

/**************************************************************/
/* SYSTARGETSERVERGROUPS                                      */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'systargetservergroups')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table systargetservergroups...'

  CREATE TABLE systargetservergroups
  (
  servergroup_id INT IDENTITY NOT NULL,
  name           sysname      NOT NULL
  )

  CREATE UNIQUE CLUSTERED INDEX clust ON systargetservergroups(name)
END
go

/**************************************************************/
/* SYSTARGETSERVERGROUPMEMBERS                                */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'systargetservergroupmembers')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table systargetservergroupmembers...'

  CREATE TABLE systargetservergroupmembers
  (
  servergroup_id INT NOT NULL,
  server_id      INT NOT NULL
  )

  CREATE UNIQUE CLUSTERED INDEX clust ON systargetservergroupmembers(servergroup_id, server_id)
  CREATE NONCLUSTERED     INDEX nc1   ON systargetservergroupmembers(server_id)
END
go

/**************************************************************/
/* SYSALERTS                                                  */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysalerts')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysalerts...'

  CREATE TABLE sysalerts
  (
  id                        INT IDENTITY     NOT NULL,
  name                      sysname          NOT NULL, -- Was length 60 in 6.x
  event_source              NVARCHAR(100)    NOT NULL,
  event_category_id         INT              NULL,
  event_id                  INT              NULL,
  message_id                INT              NOT NULL, -- Was NULL in 6.x
  severity                  INT              NOT NULL, -- Was NULL in 6.x
  enabled                   TINYINT          NOT NULL,
  delay_between_responses   INT              NOT NULL,
  last_occurrence_date      INT              NOT NULL, -- Was NULL in 6.x
  last_occurrence_time      INT              NOT NULL, -- Was NULL in 6.x
  last_response_date        INT              NOT NULL, -- Was NULL in 6.x
  last_response_time        INT              NOT NULL, -- Was NULL in 6.x
  notification_message      NVARCHAR(512)    NULL,
  include_event_description TINYINT          NOT NULL,
  database_name             NVARCHAR(512)    NULL,
  event_description_keyword NVARCHAR(100)    NULL,
  occurrence_count          INT              NOT NULL,
  count_reset_date          INT              NOT NULL, -- Was NULL in 6.x
  count_reset_time          INT              NOT NULL, -- Was NULL in 6.x
  job_id                    UNIQUEIDENTIFIER NOT NULL, -- Was NULL in 6.x
  has_notification          INT              NOT NULL, -- New for 7.0
  flags                     INT              NOT NULL, -- Was NULL in 6.x
  performance_condition     NVARCHAR(512)    NULL,
  category_id               INT              NOT NULL  -- New for 7.0
  )

  CREATE UNIQUE CLUSTERED INDEX ByName ON sysalerts(name)
  CREATE UNIQUE INDEX ByID ON sysalerts(id)
END
go

/**************************************************************/
/* sysalerts_performance_counters_view                        */
/**************************************************************/
PRINT ''
PRINT 'Creating view sysalerts_performance_counters_view...'
go
IF (NOT OBJECT_ID(N'dbo.sysalerts_performance_counters_view', 'V') IS NULL)
  DROP VIEW sysalerts_performance_counters_view
go

CREATE VIEW sysalerts_performance_counters_view
AS
    SELECT  RTRIM(pc.object_name) AS 'object_name', 
            RTRIM(pc.counter_name) AS 'counter_name', 
            CASE WHEN pc.instance_name IS NULL
                THEN NULL
                ELSE RTRIM(pc.instance_name)
            END AS 'instance_name',
            pc.cntr_value,
            pc.cntr_type,
            SERVERPROPERTY('ServerName') AS 'server_name'
    FROM sys.dm_os_performance_counters pc
GO

/**************************************************************/
/* SYSOPERATORS                                               */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysoperators')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysoperators...'

  CREATE TABLE sysoperators
  (
  id                        INT IDENTITY  NOT NULL,
  name                      sysname       NOT NULL, -- Was length 50 in 6.x
  enabled                   TINYINT       NOT NULL,
  email_address             NVARCHAR(100) NULL,
  last_email_date           INT           NOT NULL, -- Was NULL in 6.x
  last_email_time           INT           NOT NULL, -- Was NULL in 6.x
  pager_address             NVARCHAR(100) NULL,
  last_pager_date           INT           NOT NULL, -- Was NULL in 6.x
  last_pager_time           INT           NOT NULL, -- Was NULL in 6.x
  weekday_pager_start_time  INT           NOT NULL,
  weekday_pager_end_time    INT           NOT NULL,
  saturday_pager_start_time INT           NOT NULL,
  saturday_pager_end_time   INT           NOT NULL,
  sunday_pager_start_time   INT           NOT NULL,
  sunday_pager_end_time     INT           NOT NULL,
  pager_days                TINYINT       NOT NULL,
  netsend_address           NVARCHAR(100) NULL,     -- New for 7.0
  last_netsend_date         INT           NOT NULL, -- New for 7.0
  last_netsend_time         INT           NOT NULL, -- New for 7.0
  category_id               INT           NOT NULL  -- New for 7.0
  )

  CREATE UNIQUE CLUSTERED INDEX ByName ON sysoperators(name)
  CREATE UNIQUE INDEX ByID ON sysoperators(id)
END
go

/**************************************************************/
/* SYSNOTIFICATIONS                                           */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysnotifications')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysnotifications...'

  CREATE TABLE sysnotifications
  (
  alert_id             INT      NOT NULL,
  operator_id          INT      NOT NULL,
  notification_method  TINYINT  NOT NULL
  )

  CREATE UNIQUE CLUSTERED INDEX ByAlertIDAndOperatorID ON sysnotifications(alert_id, operator_id)
END
go



/**************************************************************/
/*                                                            */
/*  M  A  I  N  T  E  N  A  N  C  E    P  L  A  N  S          */
/*                                                            */
/**************************************************************/

/**************************************************************/
/* sysmaintplan_subplans                                      */
/**************************************************************/
IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysmaintplan_subplans')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysmaintplan_subplans...'

    -- This table stores the DTS package associated with the maintenance plan
    -- It also stored metadata about the maintenance plan such as its name, description etc
    CREATE TABLE sysmaintplan_subplans
    (
        subplan_id          UNIQUEIDENTIFIER    NOT NULL 
            CONSTRAINT [PK_sysmaintplan_subplan] PRIMARY KEY CLUSTERED,
        subplan_name        sysname             NOT NULL,
        subplan_description NVARCHAR(512)       NULL,
        plan_id             UNIQUEIDENTIFIER    NOT NULL,
        job_id              UNIQUEIDENTIFIER    NOT NULL 
            CONSTRAINT FK_subplan_job_id
            FOREIGN KEY (job_id) REFERENCES sysjobs(job_id),
        msx_job_id          UNIQUEIDENTIFIER DEFAULT NULL NULL
            CONSTRAINT FK_subplan_msx_job_id
            FOREIGN KEY (msx_job_id) REFERENCES sysjobs(job_id),
        schedule_id         INT                 NULL 
            CONSTRAINT FK_subplan_schedule_id 
            FOREIGN KEY (schedule_id) REFERENCES sysschedules(schedule_id),
        msx_plan bit DEFAULT 0 NOT NULL
    )
END
go

/**************************************************************/
/* sysmaintplan_log                                           */
/**************************************************************/
IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysmaintplan_log')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysmaintplan_log...'

    -- This table stores the maintenance plan log info 
    CREATE TABLE sysmaintplan_log
    (
        task_detail_id  UNIQUEIDENTIFIER    NOT NULL 
            CONSTRAINT [PK_sysmaintplan_taskdetail_id] PRIMARY KEY CLUSTERED,
        plan_id         UNIQUEIDENTIFIER    NULL,
        subplan_id      UNIQUEIDENTIFIER    NULL 
            CONSTRAINT [FK_sysmaintplan_log_subplan_id] 
            FOREIGN KEY (subplan_id) REFERENCES sysmaintplan_subplans(subplan_id),
        start_time      DATETIME            NULL,
        end_time        DATETIME            NULL,
        succeeded       BIT                 NULL,
        logged_remotely bit not null default (0),
        source_server_name nvarchar (128) NULL,
        plan_name nvarchar (128) NULL,
        subplan_name nvarchar (128) NULL
    )
END
go


/**************************************************************/
/* sysmaintplan_logdetail                                     */
/**************************************************************/
IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysmaintplan_logdetail')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysmaintplan_logdetail...'

    -- This table stores the maintenance plan log details 
    CREATE TABLE sysmaintplan_logdetail
    (
        task_detail_id  UNIQUEIDENTIFIER NOT NULL 
            CONSTRAINT [FK_sysmaintplan_log_detail_task_id] 
            FOREIGN KEY (task_detail_id) REFERENCES sysmaintplan_log(task_detail_id)
            ON DELETE CASCADE,
        line1           NVARCHAR(256)   NOT NULL,
        line2           NVARCHAR(256)   NULL,
        line3           NVARCHAR(256)   NULL,
        line4           NVARCHAR(256)   NULL,
        line5           NVARCHAR(256)   NULL,
        server_name     sysname         NOT NULL,
        start_time      DATETIME        NULL,
        end_time        DATETIME        NULL,
        error_number    INT             NULL,
        error_message   NVARCHAR(max)   NULL,
        command         NVARCHAR(max)   NULL,
        succeeded       BIT             NULL
    )
END
go


/**************************************************************/
/* SYSTASKIDS                                                 */
/*                                                            */
/* This table provides a mapping between new GUID job ID's    */
/* and 6.x INT task ID's.                                     */
/* Entries are made in this table for all existing 6.x tasks  */
/* and for all new tasks added using the 7.0 version of       */
/* sp_addtask.                                                */
/* Callers of the 7.0 version of sp_helptask will ONLY see    */
/* tasks [jobs] that have a corresponding entry in this table */
/* [IE. Jobs created with sp_add_job will not be returned].   */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'systaskids')
                  AND (type = 'U')))
BEGIN
  CREATE TABLE systaskids
  (
  task_id INT IDENTITY     NOT NULL,
  job_id  UNIQUEIDENTIFIER NOT NULL
  )

  CREATE CLUSTERED INDEX clust ON systaskids(job_id)
END
go

/**************************************************************/
/* SYSCACHEDCREDENTIALS                                       */
/*                                                            */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'syscachedcredentials')
                  AND (type = 'U')))
BEGIN
  CREATE TABLE syscachedcredentials
  (
  login_name          sysname      COLLATE database_default NOT NULL PRIMARY KEY,
  has_server_access   BIT          NOT NULL DEFAULT 0,
  is_sysadmin_member  BIT          NOT NULL DEFAULT 0,
  cachedate           DATETIME     NOT NULL DEFAULT getdate()
  )
END
go

---------------------------------------------------------------
-- Replication Datatype mapping tables
---------------------------------------------------------------

exec sys.sp_MSrepl_createdatatypemappings
go


CHECKPOINT
go

/**************************************************************/
/*                                                            */
/*        C  O  R  E     P  R  O  C  E  D  U  R  E  S         */
/*                                                            */
/**************************************************************/

PRINT ''
PRINT 'Creating function SQLAGENT_SUSER_SNAME ...'
IF (NOT OBJECT_ID(N'dbo.SQLAGENT_SUSER_SNAME', 'FN') IS NULL)
  DROP FUNCTION dbo.SQLAGENT_SUSER_SNAME
go

CREATE FUNCTION dbo.SQLAGENT_SUSER_SNAME(@user_sid VARBINARY(85)) RETURNS sysname
AS
BEGIN
  DECLARE @ret sysname
  IF @user_sid = 0xFFFFFFFF
    SELECT @ret = N'$(SQLAgentAccount)'
  ELSE
    SELECT @ret = SUSER_SNAME(@user_sid)
  RETURN @ret
END
go

PRINT ''
PRINT 'Creating function SQLAGENT_SUSER_SID ...'
IF (NOT OBJECT_ID(N'dbo.SQLAGENT_SUSER_SID', 'FN') IS NULL)
  DROP FUNCTION dbo.SQLAGENT_SUSER_SID
go

CREATE FUNCTION dbo.SQLAGENT_SUSER_SID(@user_name sysname) RETURNS VARBINARY(85)
AS
BEGIN
  DECLARE @ret VARBINARY(85)
  IF @user_name = N'$(SQLAgentAccount)'
    SELECT @ret = 0xFFFFFFFF
  ELSE
    SELECT @ret = SUSER_SID(@user_name, 0)
  RETURN @ret
END
go

-----------------------------------------------------------
-- get_principal_id : retrieves principal_id for a given sid
--
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.get_principal_id', 'FN') IS NULL
    DROP FUNCTION dbo.get_principal_id
GO

CREATE FUNCTION dbo.get_principal_id(@principal_sid varbinary(85))
RETURNS int
AS
BEGIN
    DECLARE @principal_id int
    SELECT @principal_id=principal_id FROM msdb.sys.database_principals WHERE sid=@principal_sid
    RETURN @principal_id
END
GO

-----------------------------------------------------------
-- get_principal_sid : retrieves principal sid from principal_id 
--
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.get_principal_sid', 'FN') IS NULL
    DROP FUNCTION dbo.get_principal_sid
GO

CREATE FUNCTION dbo.get_principal_sid(@principal_id int)
RETURNS varbinary(85)
AS
BEGIN
    DECLARE @principal_sid varbinary(85)
    SELECT @principal_sid=sid FROM msdb.sys.database_principals WHERE principal_id=@principal_id
    RETURN @principal_sid
END
GO
/**************************************************************/
/* SP_SQLAGENT_IS_SRVROLEMEMBER                               */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure SP_SQLAGENT_IS_SRVROLEMEMBER...'
IF (NOT OBJECT_ID(N'dbo.sp_sqlagent_is_srvrolemember', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_sqlagent_is_srvrolemember
go

CREATE PROCEDURE sp_sqlagent_is_srvrolemember
   @role_name sysname, @login_name sysname
AS
BEGIN
  DECLARE @is_member        INT
  SET NOCOUNT ON
  
  IF @role_name IS NULL OR @login_name IS NULL
    RETURN(0)
  
  SELECT @is_member = 0
  --IS_SRVROLEMEMBER works only if the login to be tested is provisioned with sqlserver
  if( @login_name = SUSER_SNAME())
    SELECT @is_member = IS_SRVROLEMEMBER(@role_name)
  else
    SELECT @is_member = IS_SRVROLEMEMBER(@role_name, @login_name)
    
  
  --try to impersonate. A try catch is used because we can have @name as NT groups also
  IF @is_member IS NULL
  BEGIN
    BEGIN TRY
      if( is_srvrolemember('sysadmin') = 1)
      begin
      EXECUTE AS LOGIN = @login_name -- impersonate 
        SELECT @is_member = IS_SRVROLEMEMBER(@role_name)  -- check role membership 
      REVERT -- revert back
      end
    END TRY
    BEGIN CATCH
      SELECT @is_member = 0
    END CATCH
  END
 
  RETURN ISNULL(@is_member,0)
END
go

/**************************************************************/
/* SP_VERIFY_CATEGORY_IDENTIFIERS                                */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_category_identifiers...'
IF (NOT OBJECT_ID(N'dbo.sp_verify_category_identifiers', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_verify_category_identifiers
go

CREATE PROCEDURE sp_verify_category_identifiers
   @name_of_name_parameter [varchar](60),
   @name_of_id_parameter [varchar](60),
   @category_name [sysname] OUTPUT,
   @category_id [INT] OUTPUT
AS
BEGIN
  DECLARE @retval         INT
  DECLARE @category_id_as_char NVARCHAR(36)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter))
  SELECT @name_of_id_parameter   = LTRIM(RTRIM(@name_of_id_parameter))
  SELECT @category_name          = LTRIM(RTRIM(@category_name))

  IF (@category_name = N'') SELECT @category_name = NULL

  IF ((@category_name IS NOT NULL) AND (@category_id IS NOT NULL))
  BEGIN
    RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter)
    RETURN(1) -- Failure
  END

  -- Check category id
  IF (@category_id IS NOT NULL)
  BEGIN
    SELECT @category_name = name
    FROM msdb.dbo.syscategories
    WHERE (category_id = @category_id)
    IF (@category_name IS NULL)
    BEGIN
     SELECT @category_id_as_char = CONVERT(nvarchar(36), @category_id)
      RAISERROR(14262, -1, -1, '@category_id', @category_id_as_char)
      RETURN(1) -- Failure
    END
  END
  ELSE
  -- Check category name
  IF (@category_name IS NOT NULL)
  BEGIN
    -- The name is not ambiguous, so get the corresponding category_id (if the job exists)
    SELECT @category_id = category_id
    FROM msdb.dbo.syscategories
    WHERE (name = @category_name)
    IF (@category_id IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@category_name', @category_name)
      RETURN(1) -- Failure
    END
  END

  RETURN(0) -- Success
END
go

PRINT ''
PRINT 'Creating function agent_datetime...'
IF (NOT OBJECT_ID(N'dbo.agent_datetime', 'FN') IS NULL)
  DROP FUNCTION dbo.agent_datetime
go

CREATE FUNCTION agent_datetime(@date int, @time int)
RETURNS DATETIME
AS
BEGIN
 RETURN
  (
    CONVERT(DATETIME,
          CONVERT(NVARCHAR(4),@date / 10000) + N'-' + 
          CONVERT(NVARCHAR(2),(@date % 10000)/100)  + N'-' +
          CONVERT(NVARCHAR(2),@date % 100) + N' ' +        
          CONVERT(NVARCHAR(2),@time / 10000) + N':' +        
          CONVERT(NVARCHAR(2),(@time % 10000)/100) + N':' +        
          CONVERT(NVARCHAR(2),@time % 100),
    120)
  )
END
go

/**************************************************************/
/* SP_VERIFY_PROXY_IDENTIFIERS                                */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_proxy_identifiers...'
IF (NOT OBJECT_ID(N'dbo.sp_verify_proxy_identifiers', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_verify_proxy_identifiers
go

CREATE PROCEDURE sp_verify_proxy_identifiers
   @name_of_name_parameter [varchar](60),
   @name_of_id_parameter [varchar](60),
   @proxy_name [sysname] OUTPUT,
   @proxy_id [INT] OUTPUT
AS
BEGIN
  DECLARE @retval         INT
  DECLARE @proxy_id_as_char NVARCHAR(36)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter))
  SELECT @name_of_id_parameter   = LTRIM(RTRIM(@name_of_id_parameter))
  SELECT @proxy_name             = LTRIM(RTRIM(@proxy_name))

  IF (@proxy_name = N'') SELECT @proxy_name = NULL

  IF ((@proxy_name IS NULL)     AND (@proxy_id IS NULL)) OR
     ((@proxy_name IS NOT NULL) AND (@proxy_id IS NOT NULL))
  BEGIN
    RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter)
    RETURN(1) -- Failure
  END

  -- Check proxy id
  IF (@proxy_id IS NOT NULL)
  BEGIN
    SELECT @proxy_name = name
    FROM msdb.dbo.sysproxies
    WHERE (proxy_id = @proxy_id)
    IF (@proxy_name IS NULL)
    BEGIN
     SELECT @proxy_id_as_char = CONVERT(nvarchar(36), @proxy_id)
      RAISERROR(14262, -1, -1, '@proxy_id', @proxy_id_as_char)
      RETURN(1) -- Failure
    END
  END
  ELSE
  -- Check proxy name
  IF (@proxy_name IS NOT NULL)
  BEGIN
    -- The name is not ambiguous, so get the corresponding proxy_id (if the job exists)
    SELECT @proxy_id = proxy_id
    FROM msdb.dbo.sysproxies
    WHERE (name = @proxy_name)
    IF (@proxy_id IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@proxy_name', @proxy_name)
      RETURN(1) -- Failure
    END
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_VERIFY_CREDENTIAL_IDENTIFIERS                           */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_verify_credential_identifiers...'
IF (NOT OBJECT_ID(N'dbo.sp_verify_credential_identifiers', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_verify_credential_identifiers
go

CREATE PROCEDURE sp_verify_credential_identifiers
   @name_of_name_parameter [varchar](60),
   @name_of_id_parameter [varchar](60),
   @credential_name [sysname] OUTPUT,
   @credential_id [INT] OUTPUT
AS
BEGIN
  DECLARE @retval         INT
  DECLARE @credential_id_as_char NVARCHAR(36)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter))
  SELECT @name_of_id_parameter   = LTRIM(RTRIM(@name_of_id_parameter))
  SELECT @credential_name        = LTRIM(RTRIM(@credential_name))

  IF (@credential_name = N'') SELECT @credential_name = NULL

  IF ((@credential_name IS NULL)     AND (@credential_id IS NULL)) OR
     ((@credential_name IS NOT NULL) AND (@credential_id IS NOT NULL))
  BEGIN
    RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter)
    RETURN(1) -- Failure
  END

  -- Check credential_id
  IF (@credential_id IS NOT NULL)
  BEGIN
    SELECT @credential_name = name
    FROM master.sys.credentials
    WHERE (credential_id = @credential_id)
    IF (@credential_name IS NULL)
    BEGIN
     SELECT @credential_id_as_char = CONVERT(nvarchar(36), @credential_id)
      RAISERROR(14262, -1, -1, '@credential_id', @credential_id_as_char)
      RETURN(1) -- Failure
    END
  END
  ELSE
  -- Check proxy name
  IF (@credential_name IS NOT NULL)
  BEGIN
    -- The name is not ambiguous, so get the corresponding credential_id (if the job exists)
    SELECT @credential_id = credential_id
    FROM master.sys.credentials
    WHERE (name = @credential_name)
    IF (@credential_id IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@credential_name', @credential_name)
      RETURN(1) -- Failure
    END
  END

  RETURN(0) -- Success
END
go


/**************************************************************/
/* sp_verify_subsystems                                       */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_subsystems...'
IF (NOT OBJECT_ID(N'dbo.sp_verify_subsystems', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_verify_subsystems
go

CREATE PROCEDURE dbo.sp_verify_subsystems
   @syssubsytems_refresh_needed BIT = 0
AS
BEGIN
  SET NOCOUNT ON
   
  DECLARE @retval         INT
  DECLARE @InstRootPath nvarchar(512)
  DECLARE @VersionRootPath nvarchar(512)
  DECLARE @ComRootPath nvarchar(512)
  DECLARE @DtsRootPath nvarchar(512)
  DECLARE @SQLPSPath nvarchar(512)
  DECLARE @DTExec nvarchar(512)
  DECLARE @DTExecExists INT
  DECLARE @ToolsPath nvarchar(512)

  IF ( (@syssubsytems_refresh_needed=1) OR (NOT EXISTS(select * from syssubsystems)) )
  BEGIN
     EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @InstRootPath OUTPUT
     IF @InstRootPath IS NULL
     BEGIN
       RAISERROR(14658, -1, -1) WITH LOG
       RETURN (1)
     END
     SELECT @InstRootPath = @InstRootPath + N'\binn\'

     EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\Microsoft Sql Server\100', N'VerSpecificRootDir', @VersionRootPath OUTPUT
     IF @VersionRootPath IS NULL
     BEGIN
       RAISERROR(14659, -1, -1) WITH LOG
       RETURN(1)
     END

     EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\Microsoft SQL Server\100\SSIS\Setup\DTSPath', N'', @DtsRootPath OUTPUT, N'no_output'
     IF (@DtsRootPath IS NOT NULL)
     BEGIN
       SELECT @DtsRootPath  = @DtsRootPath  + N'Binn\'
       SELECT @DTExec = @DtsRootPath + N'DTExec.exe'
       CREATE TABLE #t (file_exists int, is_directory int, parent_directory_exists int)
       INSERT #t EXEC xp_fileexist @DTExec
       SELECT TOP 1 @DTExecExists=file_exists from #t
       DROP TABLE #t
       IF ((@DTExecExists IS NULL) OR (@DTExecExists = 0))
         SET @DtsRootPath = NULL
     END

     SELECT @ComRootPath  = @VersionRootPath  + N'COM\'

     create table #Platform(ID int,  Name  sysname, Internal_Value int NULL, Value nvarchar(512))
     insert #Platform exec master.dbo.xp_msver 'Platform'
     if EXISTS(select * from #Platform where Value like '%64%')
     EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Wow6432Node\Microsoft\Microsoft Sql Server\100\Tools\ClientSetup', N'SQLPath', @ToolsPath OUTPUT
  else
     EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\Microsoft Sql Server\100\Tools\ClientSetup', N'SQLPath', @ToolsPath OUTPUT
     drop table #Platform
     SELECT @SQLPSPath  = @ToolsPath  + N'\Binn\SQLPS.exe'
     
     -- Procedure must start its own transaction if we don't have one already.
     DECLARE @TranCounter INT;
     SET @TranCounter = @@TRANCOUNT;
     IF @TranCounter = 0
     BEGIN
        BEGIN TRANSACTION;
     END

     -- Obtain processor count to determine maximum number of threads per subsystem
     DECLARE @xp_results TABLE
     (
     id              INT           NOT NULL,
     name            NVARCHAR(30)  COLLATE database_default NOT NULL,
     internal_value  INT           NULL,
     character_value NVARCHAR(212) COLLATE database_default NULL
     )
     INSERT INTO @xp_results
     EXECUTE master.dbo.xp_msver

     DECLARE @processor_count INT
     SELECT @processor_count = internal_value from @xp_results where id=16 -- ProcessorCount

     -- Modify database.
     BEGIN TRY

       --create subsystems
       --TSQL subsystem
       IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'TSQL')
       INSERT syssubsystems
       VALUES
       (
          1, N'TSQL',14556, FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), 20 * @processor_count
       )
       --ActiveScripting subsystem
       IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'ActiveScripting')
       INSERT syssubsystems
       VALUES
       (
          2, N'ActiveScripting',  14555, @InstRootPath + N'SQLATXSS.DLL',NULL,N'ActiveScriptStart',N'ActiveScriptEvent',N'ActiveScriptStop', 10 * @processor_count
       )

       --CmdExec subsystem
       IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'CmdExec')
       INSERT syssubsystems
       VALUES
       (
          3, N'CmdExec', 14550, @InstRootPath + N'SQLCMDSS.DLL',NULL,N'CmdExecStart',N'CmdEvent',N'CmdExecStop', 10 * @processor_count
       )

       --Snapshot subsystem
       IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'Snapshot')
       INSERT syssubsystems
       VALUES
       (
          4, N'Snapshot',   14551, @InstRootPath + N'SQLREPSS.DLL', @ComRootPath + N'SNAPSHOT.EXE', N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count
       )

       --LogReader subsystem
       IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'LogReader')
       INSERT syssubsystems
       VALUES
       (
          5, N'LogReader',  14552, @InstRootPath + N'SQLREPSS.DLL', @ComRootPath + N'logread.exe',N'ReplStart',N'ReplEvent',N'ReplStop',25 * @processor_count
       )

       --Distribution subsystem
       IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'Distribution')
       INSERT syssubsystems
       VALUES
       (
          6, N'Distribution',  14553, @InstRootPath + N'SQLREPSS.DLL', @ComRootPath + N'DISTRIB.EXE',N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count
       )

       --Merge subsystem
       IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'Merge')
       INSERT syssubsystems
       VALUES
       (
          7, N'Merge',   14554, @InstRootPath + N'SQLREPSS.DLL',@ComRootPath + N'REPLMERG.EXE',N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count
       )

       --QueueReader subsystem
       IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'QueueReader')
       INSERT syssubsystems
       VALUES
       (
          8, N'QueueReader',   14581, @InstRootPath + N'SQLREPSS.dll',@ComRootPath + N'qrdrsvc.exe',N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count
       )

       --ANALYSISQUERY subsystem
       IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'ANALYSISQUERY')
       INSERT syssubsystems
       VALUES
       (
          9, N'ANALYSISQUERY', 14513, @InstRootPath + N'SQLOLAPSS.DLL',NULL,N'OlapStart',N'OlapQueryEvent',N'OlapStop',100 * @processor_count
       )

       --ANALYSISCOMMAND subsystem
       IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'ANALYSISCOMMAND')
       INSERT syssubsystems
       VALUES
       (
          10, N'ANALYSISCOMMAND', 14514, @InstRootPath + N'SQLOLAPSS.DLL',NULL,N'OlapStart',N'OlapCommandEvent',N'OlapStop',100 * @processor_count
       )

       IF(@DtsRootPath IS NOT NULL)
       BEGIN
          --DTS subsystem
          IF (NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'SSIS') )
             INSERT syssubsystems
             VALUES
             (
                11, N'SSIS', 14538, @InstRootPath + N'SQLDTSSS.DLL',@DtsRootPath + N'DTExec.exe',N'DtsStart',N'DtsEvent',N'DtsStop',100 * @processor_count
             )
          ELSE
             UPDATE syssubsystems SET agent_exe = @DtsRootPath + N'DTExec.exe' WHERE subsystem = N'SSIS'
       END
       ELSE
       BEGIN
          IF EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'SSIS')
            DELETE FROM syssubsystems WHERE subsystem = N'SSIS' 
       END
       
       --PowerShell subsystem     
	   IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'PowerShell')
	   INSERT syssubsystems
	   VALUES
	   (
		  12, N'PowerShell', 14698, @InstRootPath + N'SQLPOWERSHELLSS.DLL', @SQLPSPath, N'PowerShellStart',N'PowerShellEvent',N'PowerShellStop',2
	   )
	   

   END TRY
   BEGIN CATCH

       DECLARE @ErrorMessage NVARCHAR(400)
       DECLARE @ErrorSeverity INT
       DECLARE @ErrorState INT

       SELECT @ErrorMessage = ERROR_MESSAGE()
       SELECT @ErrorSeverity = ERROR_SEVERITY()
       SELECT @ErrorState = ERROR_STATE()

       -- Roll back the transaction that we started if we are not nested
       IF @TranCounter = 0
       BEGIN
         ROLLBACK TRANSACTION;
       END
       -- if we are nested inside another transaction just raise the 
       -- error and let the outer transaction do the rollback
       RAISERROR (@ErrorMessage, -- Message text.
                   @ErrorSeverity, -- Severity.
                   @ErrorState -- State.
                   )
       RETURN (1)                  
     END CATCH
  END --(NOT EXISTS(select * from syssubsystems))
  
  -- commit the transaction we started
  IF @TranCounter = 0
  BEGIN
    COMMIT TRANSACTION;
  END
  
  RETURN(0) -- Success
END
go


/**************************************************************/
/* sp_verify_subsystem_identifiers                            */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_subsystem_identifiers...'
IF (NOT OBJECT_ID(N'dbo.sp_verify_subsystem_identifiers', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_verify_subsystem_identifiers
go

CREATE PROCEDURE dbo.sp_verify_subsystem_identifiers
   @name_of_name_parameter [varchar](60),
   @name_of_id_parameter [varchar](60),
   @subsystem_name [sysname] OUTPUT,
   @subsystem_id [INT] OUTPUT
AS
BEGIN
  DECLARE @retval         INT
  DECLARE @subsystem_id_as_char NVARCHAR(36)

  SET NOCOUNT ON

  -- this call will populate subsystems table if necessary
  EXEC @retval = msdb.dbo.sp_verify_subsystems
  IF @retval <> 0
     RETURN(@retval)

  -- Remove any leading/trailing spaces from parameters
  SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter))
  SELECT @name_of_id_parameter   = LTRIM(RTRIM(@name_of_id_parameter))
  SELECT @subsystem_name         = LTRIM(RTRIM(@subsystem_name))

  IF (@subsystem_name = N'') SELECT @subsystem_name = NULL

  IF ((@subsystem_name IS NULL)     AND (@subsystem_id IS NULL)) OR
     ((@subsystem_name IS NOT NULL) AND (@subsystem_id IS NOT NULL))
  BEGIN
    RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter)
    RETURN(1) -- Failure
  END

  -- Check subsystem_id
  IF (@subsystem_id IS NOT NULL)
  BEGIN
    SELECT @subsystem_name = subsystem
    FROM msdb.dbo.syssubsystems
    WHERE (subsystem_id = @subsystem_id)
    IF (@subsystem_name IS NULL)
    BEGIN
     SELECT @subsystem_id_as_char = CONVERT(nvarchar(36), @subsystem_id)
      RAISERROR(14262, -1, -1, '@subsystem_id', @subsystem_id_as_char)
      RETURN(1) -- Failure
    END
  END
  ELSE
  -- Check subsystem name
  IF (@subsystem_name IS NOT NULL)
  BEGIN
    -- Make sure Dts is translated into new subsystem's name SSIS
    IF UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS) = N'DTS'
    BEGIN
      SET @subsystem_name = N'SSIS'
    END

    -- The name is not ambiguous, so get the corresponding subsystem_id (if the subsystem exists)
    SELECT @subsystem_id = subsystem_id
    FROM msdb.dbo.syssubsystems
    WHERE (UPPER(subsystem collate SQL_Latin1_General_CP1_CS_AS) = UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS))
    IF (@subsystem_id IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@subsystem_name', @subsystem_name)
      RETURN(1) -- Failure
    END
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* sp_verify_login_identifiers                                */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_login_identifiers...'
IF (NOT OBJECT_ID(N'dbo.sp_verify_login_identifiers', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_verify_login_identifiers
go

CREATE PROCEDURE dbo.sp_verify_login_identifiers
   @login_name [nvarchar](256),
   @fixed_server_role [nvarchar](256),
   @msdb_role [nvarchar](256),
   @name [nvarchar](256) OUTPUT,
  @sid  varbinary(85)   OUTPUT,
   @flags   INT OUTPUT
AS
BEGIN
   DECLARE @retval         INT
    DECLARE @raise_error    bit
   SET NOCOUNT ON

   SELECT @flags = -1, @raise_error = 0
  SELECT @sid = NULL

  IF @login_name IS NOT NULL 
   BEGIN
      --check validity
      --use the new optional parameter of SUSER_SID to have a case insensitive comparation for NT users
    SELECT @sid = SUSER_SID(@login_name, 0)
      IF @sid IS NULL
      BEGIN
         RAISERROR(14520, -1, -1, @login_name)
         RETURN(1) -- Failure    
      END
      SELECT @name = @login_name, @flags = 0
   END
  
   IF COALESCE(@login_name, @fixed_server_role, @msdb_role) IS NULL
   BEGIN
      RAISERROR(14519, -1, -1)
      RETURN(1) -- Failure    
   END

  IF @fixed_server_role IS NOT NULL  AND @flags <> -1
      SELECT @raise_error = 1
   ELSE IF @fixed_server_role IS NOT NULL
   --check validity
   BEGIN
      -- IS_SRVROLEMEMBER return NULL for an invalid server role
      IF ISNULL(IS_SRVROLEMEMBER(@fixed_server_role), -1) = -1
      BEGIN
         RAISERROR(14521, -1, -1, @fixed_server_role)
         RETURN(1) -- Failure    
      END   
      SELECT @name = @fixed_server_role, @flags = 1
    SELECT @sid = SUSER_SID(@fixed_server_role)
   END
   
  IF @msdb_role IS NOT NULL  AND @flags <> -1
      SELECT @raise_error = 1
   ELSE IF @msdb_role IS NOT NULL
   BEGIN
      --check the correctness of msdb role
      IF ISNULL(IS_MEMBER(@msdb_role), -1) = -1 
      BEGIN
         RAISERROR(14522, -1, -1, @msdb_role)
         RETURN(1) -- Failure    
      END      
      SELECT @sid = sid from sys.database_principals
      WHERE  UPPER(@msdb_role collate SQL_Latin1_General_CP1_CS_AS) = UPPER(name collate SQL_Latin1_General_CP1_CS_AS)
    AND type = 'R'
    IF @sid IS NULL
      BEGIN
         RAISERROR(14522, -1, -1, @msdb_role)
         RETURN(1) -- Failure    
      END      
      SELECT @name = @msdb_role, @flags = 2
   END

   IF    @raise_error = 1
   BEGIN
      RAISERROR(14519, -1, -1)
      RETURN(1) -- Failure    
   END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_VERIFY_PROXY                                               */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_proxy...'
IF (NOT OBJECT_ID(N'dbo.sp_verify_proxy', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_verify_proxy
go

CREATE PROCEDURE dbo.sp_verify_proxy
   @proxy_id [INT] = NULL,
   @proxy_name [sysname],
   @enabled [tinyint],
   @description [nvarchar](512) = NULL
AS
BEGIN
  DECLARE @return_code INT
  SET NOCOUNT ON

  -- Check if the NewName is unique
  IF (EXISTS ( SELECT *
               FROM msdb.dbo.sysproxies
               WHERE (name = @proxy_name) AND
            proxy_id <> ISNULL(@proxy_id,0) ))
  BEGIN
    RAISERROR(14261, 16, 1, '@name', @proxy_name)
    RETURN(1) -- Failure
  END

  -- Enabled must be 0 or 1
  IF (@enabled NOT IN (0, 1))
  BEGIN
    RAISERROR(14266, 16, 1, '@enabled', '0, 1')
    RETURN(1) -- Failure
  END
  
  RETURN(0)
END
go

/**************************************************************/
/* SP_ADD_PROXY                                               */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_proxy...'
IF (NOT OBJECT_ID(N'dbo.sp_add_proxy', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_add_proxy
go

CREATE PROCEDURE dbo.sp_add_proxy
   @proxy_name [sysname],
   @enabled [tinyint] = 1,
   @description [nvarchar](512) = NULL,
   @credential_name [sysname] = NULL,
  @credential_id [INT] = NULL,
   @proxy_id [int] = NULL OUTPUT
AS
BEGIN
  DECLARE @retval INT
  DECLARE @full_name NVARCHAR(257) --two sysnames + \
  DECLARE @user_sid VARBINARY(85)
  DECLARE @cred_date_time DATETIME
  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @proxy_name                = LTRIM(RTRIM(@proxy_name))
  SELECT @description               = LTRIM(RTRIM(@description))

  IF @proxy_name  = ''  SELECT @proxy_name  = NULL
  IF @description = ''  SELECT @description = NULL

  EXECUTE @retval = sp_verify_proxy NULL,
                                    @proxy_name,
                           @enabled,
                           @description

  IF (@retval <> 0)
     RETURN(1) -- Failure

   EXECUTE @retval = sp_verify_credential_identifiers '@credential_name',
                                                  '@credential_id',
                                                   @credential_name OUTPUT,
                                                   @credential_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure
  -- warn if the user_domain\user_name does not exist
  SELECT @full_name = credential_identity, @cred_date_time = create_date from master.sys.credentials 
  WHERE  credential_id = @credential_id
  --force case insensitive comparation for NT users
  SELECT @user_sid = SUSER_SID(@full_name,0)
  IF @user_sid IS NULL
  BEGIN
    RAISERROR(14529, -1, -1, @full_name)
    RETURN(1)
  END

  -- Finally, do the actual INSERT
  INSERT INTO msdb.dbo.sysproxies
   (
      name,
    credential_id,
      enabled,
      description,
    user_sid,
    credential_date_created
   )
   VALUES
   (
      @proxy_name,
    @credential_id,
      @enabled,
      @description,
    @user_sid,
    @cred_date_time
   )
   
   --get newly created proxy_id;
   SELECT @proxy_id = SCOPE_IDENTITY()
END
go

/**************************************************************/
/* SP_DELETE_PROXY                                               */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_proxy...'
IF (NOT OBJECT_ID(N'dbo.sp_delete_proxy', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_delete_proxy
go

CREATE PROCEDURE dbo.sp_delete_proxy
   @proxy_id      int = NULL,
   @proxy_name    sysname = NULL
   -- must specify only one of above parameters to identify the proxy
AS
BEGIN
   DECLARE @retval   INT
   SET NOCOUNT ON
    
   EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name',
                                                  '@proxy_id',
                                                   @proxy_name OUTPUT,
                                                   @proxy_id   OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure

   --no jobsteps should use this proxy
   IF EXISTS (SELECT * FROM sysjobsteps 
            WHERE @proxy_id = proxy_id)
   BEGIN
      RAISERROR(14518, -1, -1, @proxy_id)
      RETURN(1) -- Failure
   END

    BEGIN TRANSACTION
      --delete any association between subsystems and this proxy 
      DELETE sysproxysubsystem
      WHERE  proxy_id = @proxy_id
       
      --delete any association between logins and this proxy 
      DELETE sysproxylogin
      WHERE  proxy_id = @proxy_id

      -- delete the entry in sysproxies table
      DELETE sysproxies
      WHERE proxy_id = @proxy_id

    COMMIT
   RETURN(0)
END
go


/**************************************************************/
/* SP_UPDATE_PROXY                                            */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_update_proxy...'
IF (NOT OBJECT_ID(N'dbo.sp_update_proxy', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_update_proxy
go

CREATE PROCEDURE dbo.sp_update_proxy
   @proxy_id [int] = NULL,
   @proxy_name [sysname] = NULL, 
   -- must specify only one of above parameter identify the proxy
   @credential_name [sysname] = NULL,
   @credential_id [INT] = NULL,
   @new_name [sysname] = NULL,
   @enabled [tinyint] = NULL,
   @description [nvarchar](512) = NULL
AS
BEGIN
   DECLARE  @x_new_name [sysname] 
   DECLARE  @x_credential_id [int] 
   DECLARE  @x_enabled [tinyint] 
   DECLARE @x_description [nvarchar](512)
   DECLARE @x_credential_date_created [datetime]
   DECLARE @user_sid VARBINARY(85)
      DECLARE @full_name [sysname] --two sysnames + \
   DECLARE @retval   INT
   SET NOCOUNT ON
    
   EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name',
                                                  '@proxy_id',
                                                   @proxy_name OUTPUT,
                                                   @proxy_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  IF @credential_id IS NOT NULL OR @credential_name IS NOT NULL
  BEGIN
     EXECUTE @retval = sp_verify_credential_identifiers '@credential_name',
                                                    '@credential_id',
                                                    @credential_name OUTPUT,
                                                    @credential_id   OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END

   -- Remove any leading/trailing spaces from parameters
   SELECT @new_name                = LTRIM(RTRIM(@new_name))
   SELECT @description             = LTRIM(RTRIM(@description))
  -- Turn [nullable] empty string parameters into NULLs
  IF @new_name      = '' SELECT @new_name = NULL
  IF @description    = '' SELECT @description = NULL

  -- Set the x_ (existing) variables
  SELECT    @x_new_name      = name,
    @x_credential_id = credential_id,
      @x_enabled       = enabled,
      @x_description   = description,
    @x_credential_date_created = credential_date_created
   FROM sysproxies
   WHERE proxy_id = @proxy_id

  --get the new date from credential table
  IF  (@credential_id IS NOT NULL)
    SELECT @x_credential_date_created = create_date FROM master.sys.credentials
    WHERE  credential_id = @credential_id
        
    -- Fill out the values for all non-supplied parameters from the existing values
   IF    (@new_name      IS NULL) SELECT   @new_name          =           @x_new_name                          
   IF (@credential_id IS NULL) SELECT   @credential_id     =           @x_credential_id                                    
   IF (@enabled       IS NULL) SELECT   @enabled           =           @x_enabled                                    
   IF (@description   IS NULL) SELECT   @description       =           @x_description            

  -- warn if the user_domain\user_name does not exist
  SELECT @full_name = credential_identity from master.sys.credentials 
  WHERE  credential_id = @credential_id
  
  --force case insensitive comparation for NT users
  SELECT @user_sid = SUSER_SID(@full_name, 0)
  IF @user_sid IS NULL
  BEGIN
    RAISERROR(14529, -1, -1, @full_name)
    RETURN(1)
  END
 
  -- Finally, do the actual UPDATE
  UPDATE msdb.dbo.sysproxies
   SET
   name     =  @new_name,
   credential_id  =  @credential_id,
   user_sid =  @user_sid,
   enabled     =  @enabled,
   description =  @description,
   credential_date_created = @x_credential_date_created  --@x_ is OK in this case
   WHERE proxy_id = @proxy_id
END
go

/**************************************************************/
/* SP_SQLAGENT_IS_MEMBER                                               */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_sqlagent_is_member...'
IF (NOT OBJECT_ID(N'dbo.sp_sqlagent_is_member', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_sqlagent_is_member
go

-- check if a login is member of NT group\database role
--
-- if we specify a NT group SID @login_sid should be NOT NULL
--
-- if a @role_principal_id is specified, a NULL login is allowed
-- in this case we check if the msdb database user associated
-- with the current security context is member of the specified
-- msdb database role (this allows us to verify if a particular
-- msdb database loginless msdb user is member of that msdb role)
CREATE PROCEDURE dbo.sp_sqlagent_is_member
(
  @group_sid VARBINARY(85) = NULL,
   @role_principal_id  INT = NULL, 
   @login_sid VARBINARY(85)
) 
AS
BEGIN
   DECLARE @ret_success  INT
  DECLARE @login        NVARCHAR(256)
   DECLARE @impersonated INT
  DECLARE @group_name   NVARCHAR(256)
   SELECT   @ret_success = 0 --failure  
  SELECT  @impersonated = 0

  IF (@group_sid IS NOT NULL AND @login_sid IS NULL)
    RETURN(0)

  --a sysadmin can check for every user group membership
  IF (@login_sid IS NOT NULL) AND (ISNULL(IS_SRVROLEMEMBER('sysadmin'),0) = 1)
  BEGIN
    --get login name from principal_id
    SELECT @login = SUSER_SNAME(@login_sid)
    
    IF SUSER_SNAME() <> @login
    BEGIN
       --impersonate
        EXECUTE sp_setuserbylogin @login
      SELECT @impersonated = 1
    END
  END
  
  IF @group_sid IS NOT NULL
    SELECT @group_name = SUSER_SNAME(@group_sid)
  ELSE
    SELECT @group_name = USER_NAME(@role_principal_id)
        
   -- return success, if login is member of the group, and failure if group doesnt exist or login is not member of the group
   SELECT  @ret_success = ISNULL(IS_MEMBER(@group_name),0)

   --revert to self
  IF @impersonated = 1
         EXECUTE sp_setuserbylogin

   RETURN @ret_success
END 
go

/**************************************************************/
/* sp_verify_proxy_permissions                          */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_proxy_permissions...'
IF (NOT OBJECT_ID(N'dbo.sp_verify_proxy_permissions', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_verify_proxy_permissions
go
CREATE PROCEDURE dbo.sp_verify_proxy_permissions
   @subsystem_name sysname,
   @proxy_id      INT = NULL,
   @name       NVARCHAR(256) = NULL,
   @raise_error    INT = 1,
   @allow_disable_proxy INT = 0,
   @verify_special_account INT = 0,
   @check_only_read_perm INT = 0
AS 
BEGIN
  DECLARE @retval   INT
  DECLARE @granted_sid VARBINARY(85)
  DECLARE @is_member INT
  DECLARE @is_sysadmin BIT
  DECLARE @flags TINYINT
  DECLARE @enabled TINYINT
  DECLARE @name_sid VARBINARY(85)
  DECLARE @role_from_sid sysname
  DECLARE @name_from_sid sysname
  DECLARE @is_SQLAgentOperatorRole BIT
  DECLARE @check_only_subsystem BIT
  DECLARE proxy_subsystem CURSOR LOCAL
  FOR
    SELECT p.sid, p.flags
    FROM sysproxyloginsubsystem_view p, syssubsystems s
    WHERE p.proxy_id = @proxy_id AND p.subsystem_id = s.subsystem_id
        AND UPPER(s.subsystem collate SQL_Latin1_General_CP1_CS_AS) = 
            UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS)
   
  SET NOCOUNT ON
  SELECT @retval = 1

  IF @proxy_id IS NULL
    RETURN(0)

   -- TSQL subsystem prohibited
  IF (UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS) = N'TSQL')
  BEGIN
    RAISERROR(14517, -1, -1)
    RETURN(1) -- Failure
  END
   
  --check if the date stored inside proxy still exists and match the cred create_date inside proxy
  --otherwise the credential has been tempered from outside
  --if so, disable proxy and continue execution
  --only a sysadmin caller have cross database permissions but
  --when executing by sqlagent this check will be always performed
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) 
  BEGIN
    IF NOT EXISTS(SELECT * FROM sysproxies p JOIN master.sys.credentials c ON p.credential_id = c.credential_id
            WHERE p.proxy_id = @proxy_id AND p.credential_date_created = c.create_date AND enabled=1)
    BEGIN
        UPDATE sysproxies SET enabled=0 WHERE proxy_id = @proxy_id
    END
  END
  
  --if no login has been passed check permission against the caller 
  IF @name IS NULL
    SELECT @name = SUSER_SNAME()  
    
  --check if the proxy is disable and continue or not based on
  --allow_disable_proxy
  --allow creation of a job step with a disabled proxy but
  --sqlagent always call with @allow_disable_proxy = 0
  SELECT @enabled = enabled FROM sysproxies WHERE proxy_id = @proxy_id
  IF (@enabled = 0) AND (@allow_disable_proxy = 0)
  BEGIN
    RAISERROR(14537, -1, -1, @proxy_id)
    RETURN(2) -- Failure
  END

  --we need to check permission only against subsystem in following cases
  --1. @name is sysadmin
  --2. @name is member of SQLAgentOperatorRole and @check_only_read_perm=1
  --3. @verify_special_account =1
  --sysadmin and SQLAgentOperatorRole have permission to view all proxies
  IF (@verify_special_account = 1)
    SET @check_only_subsystem = 1  
  ELSE
  BEGIN
    EXEC @is_sysadmin = sp_sqlagent_is_srvrolemember N'sysadmin', @name 
    IF (@is_sysadmin = 1)
      SET @check_only_subsystem = 1  
    ELSE
    BEGIN
      EXEC @is_SQLAgentOperatorRole = sp_sqlagent_is_srvrolemember N'SQLAgentOperatorRole', @name -- check role membership 
      IF ((@is_SQLAgentOperatorRole = 1) AND (@check_only_read_perm = 1))
        SET @check_only_subsystem = 1 
    END
  END 
  
  IF (@check_only_subsystem = 1)
  BEGIN
    IF NOT EXISTS(SELECT * FROM sysproxysubsystem sp JOIN syssubsystems s ON sp.subsystem_id = s.subsystem_id
                  WHERE proxy_id = @proxy_id  AND UPPER(s.subsystem collate SQL_Latin1_General_CP1_CS_AS) = 
                     UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS))
    BEGIN
      IF (@raise_error <> 0)
      BEGIN
        RAISERROR(14516, -1, -1, @proxy_id, @subsystem_name, @name)
      END         
      RETURN(1) -- Failure     
    END
    RETURN(0)
  END
  
  --get SID from name; we verify if a login has permission to use a certain proxy
  --force case insensitive comparation for NT users
  SELECT @name_sid = SUSER_SID(@name, 0)

  --check first if name has been granted explicit permissions
  IF (@name_sid IS NOT NULL)
  BEGIN
      IF EXISTS(SELECT * FROM sysproxyloginsubsystem_view p, syssubsystems s
        WHERE p.proxy_id = @proxy_id AND p.subsystem_id = s.subsystem_id
            AND UPPER(s.subsystem collate SQL_Latin1_General_CP1_CS_AS) = 
                UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS)
            AND
        p.sid = @name_sid) -- name has been granted explicit permissions
      BEGIN
        RETURN(0)
      END
  END

  OPEN proxy_subsystem
  FETCH NEXT FROM proxy_subsystem INTO @granted_sid, @flags
  WHILE (@@fetch_status = 0 AND @retval = 1)
  BEGIN
    IF @flags = 0 AND @granted_sid IS NOT NULL AND @name_sid IS NOT NULL -- NT GROUP 
    BEGIN
        EXEC @is_member = sp_sqlagent_is_member @group_sid = @granted_sid, @login_sid = @name_sid 
        IF @is_member = 1
          SELECT @retval = 0
    END
    ELSE IF @flags = 2 AND @granted_sid IS NOT NULL -- MSDB role (@name_sid can be null in case of a loginless user member of msdb)
    BEGIN
        DECLARE @principal_id INT
        SET @principal_id = msdb.dbo.get_principal_id(@granted_sid)
        EXEC @is_member = sp_sqlagent_is_member @role_principal_id = @principal_id, @login_sid = @name_sid 
        IF @is_member = 1
          SELECT @retval = 0
    END
    ELSE IF (@flags = 1) AND @granted_sid IS NOT NULL AND @name_sid IS NOT NULL -- FIXED SERVER Roles
    BEGIN   
      -- we have to use impersonation to check for role membership
      SELECT @role_from_sid = SUSER_SNAME(@granted_sid)
      SELECT @name_from_sid = SUSER_SNAME(@name_sid)
      EXEC   @is_member = sp_sqlagent_is_srvrolemember @role_from_sid, @name_from_sid -- check role membership 

      IF @is_member = 1
        SELECT @retval = 0
    END

    IF @retval = 1
    BEGIN
        SELECT @granted_sid = NULL
        FETCH NEXT FROM proxy_subsystem INTO @granted_sid, @flags
    END
  END
  DEALLOCATE proxy_subsystem
  
  IF (@retval = 1 AND @raise_error <> 0)
  BEGIN
    RAISERROR(14516, -1, -1, @proxy_id, @subsystem_name, @name)
    RETURN(1) -- Failure
  END

   --0 is for success
   RETURN @retval
END
go

/**************************************************************/
/* SP_HELP_PROXY                                              */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_proxy...'
IF (NOT OBJECT_ID(N'dbo.sp_help_proxy', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_help_proxy
go

CREATE PROCEDURE dbo.sp_help_proxy
   @proxy_id          int           = NULL,
   @proxy_name      sysname       = NULL,
   @subsystem_name sysname       = NULL,
   @name           nvarchar(256) = NULL
AS
BEGIN
  DECLARE @retval INT
  DECLARE @subsystem_id INT
  DECLARE @cur_subsystem_name NVARCHAR(40)
  DECLARE @current_proxy_id INT
  DECLARE @not_have_permission INT
  DECLARE cur_proxy CURSOR LOCAL
  FOR
    SELECT p.proxy_id, s.subsystem
    FROM sysproxies p, syssubsystems s
    WHERE ISNULL(UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS), 
        UPPER(s.subsystem collate SQL_Latin1_General_CP1_CS_AS) ) = 
        UPPER(s.subsystem collate SQL_Latin1_General_CP1_CS_AS) AND 
        s.subsystem_id <> 1 --last is TSQL subsystem
          
  -- this call will populate subsystems table if necessary
  EXEC @retval = msdb.dbo.sp_verify_subsystems
  IF @retval <> 0
     RETURN(1) --failure
     
  --create temp table with returned rows
  DECLARE @temp_proxy TABLE
   (
      proxy_id             INT  --used to identify a proxy
   )

  SET NOCOUNT ON

  SELECT @subsystem_id = NULL

  -- Remove any leading/trailing spaces from parameters
  SELECT @proxy_name       = LTRIM(RTRIM(@proxy_name))
  IF @proxy_name           = '' SELECT @proxy_name = NULL
  SELECT @subsystem_name   = LTRIM(RTRIM(@subsystem_name))
  IF @proxy_name           = '' SELECT @proxy_name = NULL
  SELECT @name             = LTRIM(RTRIM(@name))
  IF @name                 = '' SELECT @name = NULL

  IF (@proxy_id IS NOT NULL OR @proxy_name IS NOT NULL)
  BEGIN
    EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name',
                                        '@proxy_id',
                                        @proxy_name OUTPUT,
                                        @proxy_id   OUTPUT
    IF (@retval <> 0)
        RETURN(1) -- Failure
  END

  IF @subsystem_name IS NOT NULL 
  BEGIN
    EXECUTE @retval = sp_verify_subsystem_identifiers '@subsystem_name',
                                      '@subsystem_id',
                                      @subsystem_name OUTPUT,
                                      @subsystem_id   OUTPUT
    IF (@retval <> 0)
        RETURN(1) -- Failure
  END
    
  IF (@subsystem_name IS NOT NULL AND @name IS NULL) OR
    (@subsystem_name IS NULL AND @name IS NOT NULL)
  BEGIN
    RAISERROR(14532, -1, -1, '@subsystem_name', '@name')
    RETURN(1) -- Failure
  END   
  
  --only member of sysadmin and SQLAgentOperatorRole roles can see proxies granted to somebody else
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) AND
      (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0))
  BEGIN
    SELECT @name = SUSER_SNAME()
  END

  IF @name IS NOT NULL
  BEGIN
    OPEN cur_proxy
    FETCH NEXT FROM cur_proxy INTO @current_proxy_id, @cur_subsystem_name
    WHILE (@@fetch_status = 0)
    BEGIN
      --verify if supplied user have permission to use the current proxy for specified subsystem
      --disabled proxy should be shown as well
      IF NOT EXISTS(SELECT * FROM @temp_proxy WHERE proxy_id = @current_proxy_id)
      BEGIN
        EXECUTE @not_have_permission = sp_verify_proxy_permissions 
          @subsystem_name = @cur_subsystem_name, 
          @proxy_id = @current_proxy_id, 
          @name = @name, 
          @raise_error = 0, 
          @allow_disable_proxy = 1, 
          @verify_special_account = 0, 
          @check_only_read_perm = 1
        IF (@not_have_permission = 0) -- have permissions
            INSERT @temp_proxy VALUES(@current_proxy_id)
      END
      FETCH NEXT FROM cur_proxy INTO @current_proxy_id, @cur_subsystem_name
    END 
    CLOSE cur_proxy  
    DEALLOCATE cur_proxy
  END
  ELSE
    INSERT @temp_proxy SELECT proxy_id from sysproxies

  -- returns different result sets if caller is admin or not
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR
      (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 1))
  BEGIN
    SELECT p.proxy_id, 
          p.name, 
          c.credential_identity, 
          p.enabled, 
          p.description, 
          p.user_sid, 
          p.credential_id,
          CASE WHEN c.credential_id IS NULL THEN 0 ELSE 1 END as credential_identity_exists
    FROM sysproxies p LEFT JOIN master.sys.credentials c ON p.credential_id = c.credential_id  
                  JOIN @temp_proxy t ON p.proxy_id = t.proxy_id
              WHERE ISNULL(@proxy_id, p.proxy_id) = p.proxy_id 
  END
  ELSE
  BEGIN
    SELECT p.proxy_id, p.name, null as credential_identity, p.enabled, p.description, null as user_sid, p.credential_id, null as credential_identity_exists 
    FROM sysproxies p, @temp_proxy t
    WHERE  ISNULL(@proxy_id, p.proxy_id) = p.proxy_id AND
              p.proxy_id = t.proxy_id
  END

END
go

/**************************************************************/
/* sp_grant_proxy_to_subsystem                             */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_grant_proxy_to_subsystem...'
IF (NOT OBJECT_ID(N'dbo.sp_grant_proxy_to_subsystem', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_grant_proxy_to_subsystem
go
CREATE PROCEDURE dbo.sp_grant_proxy_to_subsystem
   @proxy_id      int = NULL,
   @proxy_name    sysname = NULL,
   -- must specify only one of above parameter to identify the proxy
   @subsystem_id  int = NULL,
   @subsystem_name sysname = NULL
   -- must specify only one of above parameter to identify the subsystem
AS
BEGIN
   DECLARE @retval   INT
   DECLARE @proxy_account sysname
   SET NOCOUNT ON

   -- Remove any leading/trailing spaces from parameters
   SELECT @subsystem_name          = LTRIM(RTRIM(@subsystem_name))
   SELECT @proxy_name              = LTRIM(RTRIM(@proxy_name))

  -- Turn [nullable] empty string parameters into NULLs
  IF @subsystem_name    = '' SELECT @subsystem_name = NULL
  IF @proxy_name         = '' SELECT @proxy_name = NULL
    
   EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name',
                                                  '@proxy_id',
                                                   @proxy_name OUTPUT,
                                                   @proxy_id   OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure

   EXECUTE @retval = sp_verify_subsystem_identifiers '@subsystem_name',
                                                  '@subsystem_id',
                                                   @subsystem_name OUTPUT,
                                                   @subsystem_id   OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure
   

  --TSQL subsystem is prohibited
  IF @subsystem_id = 1
   BEGIN
     RAISERROR(14530, -1, -1)
     RETURN(1) -- Failure
   END

  --check if we already added an user for the pair subsystem-proxy
  IF (EXISTS(SELECT * FROM sysproxysubsystem WHERE subsystem_id = @subsystem_id
               AND proxy_id = @proxy_id))
  BEGIN
     RAISERROR(14531, -1, -1)
     RETURN(1) -- Failure
   END
  
   INSERT INTO sysproxysubsystem
   (  subsystem_id,  proxy_id )
   VALUES
   (  @subsystem_id,    @proxy_id )

END
go

/**************************************************************/
/* sp_grant_login_to_proxy                           */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_grant_login_to_proxy...'
IF (NOT OBJECT_ID(N'dbo.sp_grant_login_to_proxy', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_grant_login_to_proxy
go
CREATE PROCEDURE dbo.sp_grant_login_to_proxy
   @login_name        NVARCHAR(256) = NULL,
   @fixed_server_role NVARCHAR(256) = NULL, 
   @msdb_role         NVARCHAR(256) = NULL, 
   -- must specify only one of above parameter to identify the type of login
   @proxy_id             int           = NULL,
   @proxy_name         sysname       = NULL
   -- must specify only one of above parameter to identify the proxy
AS
BEGIN
  DECLARE @retval   INT
  DECLARE @name nvarchar(256)
  DECLARE @flags INT
  DECLARE @sid VARBINARY(85)
  DECLARE @is_sysadmin BIT
  
  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @proxy_name              = LTRIM(RTRIM(@proxy_name))
  SELECT @fixed_server_role       = LTRIM(RTRIM(@fixed_server_role))
  SELECT @msdb_role               = LTRIM(RTRIM(@msdb_role))

  -- Turn [nullable] empty string parameters into NULLs
  IF @proxy_name         = '' SELECT @proxy_name = NULL
  IF @login_name         = '' SELECT @login_name = NULL
  IF @fixed_server_role  = '' SELECT @fixed_server_role = NULL
  IF @msdb_role          = '' SELECT @msdb_role  = NULL
    
  EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name',
                                                 '@proxy_id',
                                                 @proxy_name OUTPUT,
                                                 @proxy_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  EXECUTE @retval = sp_verify_login_identifiers  @login_name,
                                                 @fixed_server_role,
                                                 @msdb_role,
                                                 @name OUTPUT,
                                                 @sid OUTPUT,
                                                 @flags OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure
    
  -- is login member of sysadmin role?
  SELECT @is_sysadmin = 0
  IF (@login_name IS NOT NULL)
  BEGIN
     EXEC @is_sysadmin = sp_sqlagent_is_srvrolemember N'sysadmin', @login_name -- check role membership 
  END

  IF (@is_sysadmin = 1)
  BEGIN
   -- @name is sysadmin, it cannot granted to proxy
   -- issue a message and do nothing
   RAISERROR(14395, 10, 1, @name)
  END
  ELSE
  BEGIN
   --check if we already added an user for the pair subsystem-proxy
   IF (EXISTS(SELECT * FROM sysproxylogin WHERE proxy_id = @proxy_id 
               AND ISNULL(sid, 0) = ISNULL(@sid,0) 
               AND flags = @flags))
   BEGIN
      RAISERROR(14531, -1, -1)
      RETURN(1) -- Failure
   END

   INSERT INTO sysproxylogin
      (  proxy_id, sid,  flags )
      VALUES
      ( @proxy_id, @sid, @flags)
  END
END
go

/**************************************************************/
/* sp_revoke_login_from_proxy                                         */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_revoke_login_from_proxy...'
IF (NOT OBJECT_ID(N'dbo.sp_revoke_login_from_proxy', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_revoke_login_from_proxy
go

CREATE PROCEDURE dbo.sp_revoke_login_from_proxy
   @name         NVARCHAR(256),
   @proxy_id        INT = NULL,
   @proxy_name    sysname = NULL
   -- must specify only one of above parameter to identify the proxy
AS
BEGIN
   DECLARE @retval   INT
   DECLARE @sid VARBINARY(85)
   DECLARE @is_sysadmin BIT
   
   SET NOCOUNT ON

   -- Remove any leading/trailing spaces from parameters
   SELECT @proxy_name              = LTRIM(RTRIM(@proxy_name))
   SELECT @name                    = LTRIM(RTRIM(@name))

   -- Turn [nullable] empty string parameters into NULLs
   IF @proxy_name         = '' SELECT @proxy_name = NULL
   IF @name               = '' SELECT @name = NULL
    
   EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name',
                                                  '@proxy_id',
                                                   @proxy_name OUTPUT,
                                                   @proxy_id   OUTPUT
   IF (@retval <> 0)
     RETURN(1) -- Failure
    
  -- is login member of sysadmin role?
  SELECT @is_sysadmin = 0
  IF (@name IS NOT NULL)
  BEGIN
    EXEC @is_sysadmin = sp_sqlagent_is_srvrolemember N'sysadmin', @name -- check role membership 
  END

  IF (@is_sysadmin = 1)
  BEGIN
    -- @name is sysadmin, it cannot be revoked from proxy
    -- issue a message and do nothing
    RAISERROR(14395, 10, -1, @name)
  END
  ELSE
  BEGIN  
    --force case insensitive comparation for NT users
    SELECT @sid = SUSER_SID(@name, 0)
    IF @sid IS NULL -- then @name is a MSDB role
       SELECT @sid = sid FROM sys.database_principals
       WHERE  name = @name

    --check parametrs validity
    IF (EXISTS(SELECT * FROM sysproxylogin WHERE
       proxy_id                  = @proxy_id AND
       ISNULL(sid, 0)            = ISNULL(@sid, 0)))
    BEGIN
       DELETE FROM sysproxylogin WHERE
       proxy_id     = @proxy_id AND 
       ISNULL(sid, 0)= ISNULL(@sid, 0) 
    END
    ELSE
    BEGIN
       RAISERROR(14523, -1, -1, @name, @proxy_name)
       RETURN(1) -- Failure       
    END   
  END

  RETURN(0)
END
go

/**************************************************************/
/* sp_revoke_proxy_from_subsystem                                       */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_revoke_proxy_from_subsystem...'
IF (NOT OBJECT_ID(N'dbo.sp_revoke_proxy_from_subsystem', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_revoke_proxy_from_subsystem
go

CREATE PROCEDURE dbo.sp_revoke_proxy_from_subsystem
   @proxy_id          INT = NULL,
   @proxy_name      sysname = NULL,
   -- must specify only one of above parameter to identify the proxyAS
   @subsystem_id    INT = NULL,
   @subsystem_name sysname = NULL
   -- must specify only one of above parameter to identify the subsystem
AS
BEGIN
   DECLARE @retval   INT
   DECLARE @proxy_account sysname
   SET NOCOUNT ON

   -- Remove any leading/trailing spaces from parameters
   SELECT @subsystem_name          = LTRIM(RTRIM(@subsystem_name))
   SELECT @proxy_name              = LTRIM(RTRIM(@proxy_name))

  -- Turn [nullable] empty string parameters into NULLs
  IF @subsystem_name    = '' SELECT @subsystem_name = NULL
  IF @proxy_name         = '' SELECT @proxy_name = NULL
    
   EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name',
                                                  '@proxy_id',
                                                   @proxy_name OUTPUT,
                                                   @proxy_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

   EXECUTE @retval = sp_verify_subsystem_identifiers '@subsystem_name',
                                                  '@subsystem_id',
                                                   @subsystem_name OUTPUT,
                                                   @subsystem_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure
   
   
  --check parametrs validity
   IF (EXISTS(SELECT * FROM sysproxysubsystem WHERE
      subsystem_id = @subsystem_id AND 
      proxy_id     = @proxy_id ))
      BEGIN
        DELETE FROM sysproxysubsystem WHERE
           subsystem_id = @subsystem_id AND 
           proxy_id     = @proxy_id 
      END
   ELSE
      BEGIN
         RAISERROR(14600, -1, -1, @proxy_name, @subsystem_name)
         RETURN(1) -- Failure       
      END   

   RETURN(0)

END
go

/**************************************************************/
/* SP_ENUM_PROXY_FOR_SUBSYSTEM                                         */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_enum_proxy_for_subsystem...'
IF (NOT OBJECT_ID(N'dbo.sp_enum_proxy_for_subsystem', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_enum_proxy_for_subsystem
go

CREATE PROCEDURE sp_enum_proxy_for_subsystem
   @proxy_id      int = NULL,
   @proxy_name    sysname = NULL,
   -- must specify only one of above parameter to identify the proxy or none
   @subsystem_id  int = NULL,
   @subsystem_name sysname = NULL
   -- must specify only one of above parameter to identify the subsystem or none
AS
BEGIN
   DECLARE @retval   INT
   SET NOCOUNT ON

   -- Remove any leading/trailing spaces from parameters
   SELECT @subsystem_name          = LTRIM(RTRIM(@subsystem_name))
   SELECT @proxy_name              = LTRIM(RTRIM(@proxy_name))

  -- Turn [nullable] empty string parameters into NULLs
  IF @subsystem_name    = '' SELECT @subsystem_name = NULL
  IF @proxy_name         = '' SELECT @proxy_name = NULL

   IF @proxy_name IS NOT NULL OR @proxy_id IS NOT NULL
   BEGIN
      EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name',
                                          '@proxy_id',
                                          @proxy_name OUTPUT,
                                          @proxy_id   OUTPUT
      IF (@retval <> 0)
   RETURN(1) -- Failure
   END

   IF @subsystem_name IS NOT NULL OR @subsystem_id IS NOT NULL
   BEGIN
      EXECUTE @retval = sp_verify_subsystem_identifiers '@subsystem_name',
                                       '@subsystem_id',
                                       @subsystem_name OUTPUT,
                                       @subsystem_id   OUTPUT
      IF (@retval <> 0)
      RETURN(1) -- Failure
   END

  SELECT ps.subsystem_id AS subsystem_id, s.subsystem AS subsystem_name, ps.proxy_id AS proxy_id, p.name AS proxy_name
   FROM sysproxysubsystem ps JOIN sysproxies p ON ps.proxy_id = p.proxy_id
  JOIN syssubsystems s ON ps.subsystem_id = s.subsystem_id
   WHERE
        ISNULL(@subsystem_id, ps.subsystem_id) = ps.subsystem_id AND
        ISNULL(@proxy_id,     ps.proxy_id    ) = ps.proxy_id     
END
go

/**************************************************************/
/* sp_enum_login_for_proxy                                           */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_enum_login_for_proxy...'
IF (NOT OBJECT_ID(N'dbo.sp_enum_login_for_proxy', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_enum_login_for_proxy
go

CREATE PROCEDURE sp_enum_login_for_proxy
   @name         NVARCHAR(256) = NULL,
   @proxy_id        INT           = NULL,
   @proxy_name    sysname       = NULL
   -- must specify only one of above parameter to identify the proxy or none
AS
BEGIN
   DECLARE @retval   INT
  DECLARE @sid VARBINARY(85)
   SET NOCOUNT ON

   -- Remove any leading/trailing spaces from parameters
   SELECT @proxy_name              = LTRIM(RTRIM(@proxy_name))
   SELECT @name                    = LTRIM(RTRIM(@name))

  -- Turn [nullable] empty string parameters into NULLs
  IF @proxy_name         = '' SELECT @proxy_name = NULL
  IF @name                 = '' SELECT @name = NULL

   IF @proxy_name IS NOT NULL OR @proxy_id IS NOT NULL
   BEGIN
      EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name',
                                          '@proxy_id',
                                          @proxy_name OUTPUT,
                                          @proxy_id   OUTPUT
     IF (@retval <> 0)
       RETURN(1) -- Failure
   END

   IF (@name IS NOT NULL) AND 
     --force case insensitive comparation for NT users
     (ISNULL(SUSER_SID(@name, 0), 0) = 0) AND
     (ISNULL(IS_SRVROLEMEMBER(@name), -1) = -1) AND
      (ISNULL(IS_MEMBER(@name), -1) = -1)
   BEGIN
            RAISERROR(14520, -1, -1, @name)
            RETURN(1) -- Failure    
   END      

  --force case insensitive comparation for NT users
  SELECT @sid = SUSER_SID(@name, 0)
  IF @sid IS NULL -- then @name is a MSDB role
    SELECT @sid = sid FROM sys.database_principals
    WHERE  name = @name

  SELECT pl.proxy_id AS proxy_id, p.name AS proxy_name, pl.flags as flags, 
  CASE pl.flags
          WHEN  0 THEN SUSER_SNAME(pl.sid)  -- SQLLOGIN, NT USER/GROUP          
          WHEN  1 THEN SUSER_SNAME(pl.sid)  -- SQL fixed server role 
          WHEN  2 THEN USER_NAME(msdb.dbo.get_principal_id(pl.sid)) -- MSDB role
          ELSE         NULL -- should never be the case
  END AS name,  pl.sid AS sid, msdb.dbo.get_principal_id(pl.sid) AS principal_id
   FROM sysproxylogin pl JOIN sysproxies p ON pl.proxy_id = p.proxy_id
   WHERE  
        COALESCE(@proxy_id,     pl.proxy_id,     0 ) = ISNULL(pl.proxy_id, 0)  AND
        COALESCE(@sid,          pl.sid,          0 ) = ISNULL(pl.sid, 0) 
END
go

/**************************************************************/
/* SP_SQLAGENT_GET_STARTUP_INFO                               */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_sqlagent_get_startup_info...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_sqlagent_get_startup_info')
              AND (type = 'P')))
  DROP PROCEDURE sp_sqlagent_get_startup_info
go
CREATE PROCEDURE sp_sqlagent_get_startup_info
AS
BEGIN
  DECLARE @tbu INT
  DECLARE @agentAllowed INT

  SET NOCOUNT ON

  IF (ServerProperty('InstanceName') IS NULL)
  BEGIN
    EXECUTE @tbu = master.dbo.xp_qv '1338198028'
    EXECUTE @agentAllowed = master.dbo.xp_qv '2858542058'
  END
  ELSE
  BEGIN
    DECLARE @instancename NVARCHAR(128)
    SELECT @instancename = CONVERT(NVARCHAR(128), ServerProperty('InstanceName'))
    EXECUTE @tbu = master.dbo.xp_qv '1338198028', @instancename
    EXECUTE @agentAllowed = master.dbo.xp_qv '2858542058', @instancename
  END

  IF (@tbu < 0)
    SELECT @tbu = 0

  IF (@agentAllowed < 0)
    SELECT @agentAllowed = 0

  SELECT 'msdb_70_compatible' = (SELECT CASE WHEN cmptlevel >= 70 THEN 1 ELSE 0 END FROM master.dbo.sysdatabases WHERE (name = 'msdb')),
         'msdb_read_only' = DATABASEPROPERTY('msdb', 'IsReadOnly'),
         'msdb_available' = CASE ISNULL(DATABASEPROPERTY('msdb', 'IsSingleUser'), 0) WHEN 0 THEN 1 ELSE 0 END &
                            CASE ISNULL(DATABASEPROPERTY('msdb', 'IsDboOnly'), 0) WHEN 0 THEN 1 ELSE 0 END &
                            CASE ISNULL(DATABASEPROPERTY('msdb', 'IsNotRecovered'), 0) WHEN 0 THEN 1 ELSE 0 END &
                            CASE ISNULL(DATABASEPROPERTY('msdb', 'IsSuspect'), 0) WHEN 0 THEN 1 ELSE 0 END,
         'case_sensitive_server' = CASE ISNULL((SELECT 1 WHERE 'a' = 'A'), 0)
                                     WHEN 1 THEN 0
                                     ELSE 1
                                   END,
         'max_user_connection' = (SELECT value FROM master.dbo.syscurconfigs WHERE (config = 103)),
         'sql_server_name' = CONVERT(sysname, SERVERPROPERTY('SERVERNAME')),
         'tbu' = ISNULL(@tbu, 0),
         'platform' = PLATFORM(),
         'instance_name' = ISNULL(CONVERT(sysname, SERVERPROPERTY('INSTANCENAME')), 'MSSQLSERVER'),
         'is_clustered' = CONVERT(INT, SERVERPROPERTY('ISCLUSTERED')),
         'agent_allowed' = @agentAllowed

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_SQLAGENT_HAS_SERVER_ACCESS                              */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_sqlagent_has_server_access...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_sqlagent_has_server_access')
              AND (type = 'P')))
  DROP PROCEDURE sp_sqlagent_has_server_access
go
CREATE PROCEDURE sp_sqlagent_has_server_access
  @login_name         sysname = NULL,
  @is_sysadmin_member INT     = NULL OUTPUT
AS
BEGIN
  DECLARE @has_server_access BIT
  DECLARE @is_sysadmin       BIT
  DECLARE @actual_login_name sysname
  DECLARE @cachedate         DATETIME

  SET NOCOUNT ON

  SELECT @cachedate = NULL

  -- remove expired entries from the cache
  DELETE msdb.dbo.syscachedcredentials
  WHERE  DATEDIFF(MINUTE, cachedate, GETDATE()) >= 29

  -- query the cache
  SELECT  @is_sysadmin = is_sysadmin_member,
          @has_server_access = has_server_access,
          @cachedate = cachedate
  FROM    msdb.dbo.syscachedcredentials
  WHERE   login_name = @login_name
  AND     DATEDIFF(MINUTE, cachedate, GETDATE()) < 29

  IF (@cachedate IS NOT NULL)
  BEGIN
    -- no output variable
    IF (@is_sysadmin_member IS NULL)
    BEGIN
      -- Return result row
      SELECT has_server_access = @has_server_access,
             is_sysadmin       = @is_sysadmin,
             actual_login_name = @login_name
      RETURN
    END
    ELSE
    BEGIN
      SELECT @is_sysadmin_member = @is_sysadmin
      RETURN
    END
  END -- select from cache

  -- Set defaults
  SELECT @has_server_access = 0
  SELECT @is_sysadmin = 0
  SELECT @actual_login_name = FORMATMESSAGE(14205)

  IF (@login_name IS NULL)
  BEGIN
    SELECT has_server_access = 1,
           is_sysadmin       = IS_SRVROLEMEMBER(N'sysadmin'),
           actual_login_name = SUSER_SNAME()
    RETURN
  END

  IF (@login_name LIKE '%\%')
  BEGIN
    -- Handle the LocalSystem account ('NT AUTHORITY\SYSTEM') as a special case
    IF (UPPER(@login_name collate SQL_Latin1_General_CP1_CS_AS) = N'NT AUTHORITY\SYSTEM')
    BEGIN
      IF (EXISTS (SELECT *
                  FROM master.dbo.syslogins
                  WHERE (UPPER(loginname collate SQL_Latin1_General_CP1_CS_AS) = N'NT AUTHORITY\SYSTEM')))
      BEGIN
        SELECT @has_server_access = hasaccess,
               @is_sysadmin = sysadmin,
               @actual_login_name = loginname
        FROM master.dbo.syslogins
        WHERE (UPPER(loginname collate SQL_Latin1_General_CP1_CS_AS) = N'NT AUTHORITY\SYSTEM')
      END
      ELSE
      IF (EXISTS (SELECT *
                  FROM master.dbo.syslogins
                  WHERE (UPPER(loginname collate SQL_Latin1_General_CP1_CS_AS) = N'BUILTIN\ADMINISTRATORS')))
      BEGIN
        SELECT @has_server_access = hasaccess,
               @is_sysadmin = sysadmin,
               @actual_login_name = loginname
        FROM master.dbo.syslogins
        WHERE (UPPER(loginname collate SQL_Latin1_General_CP1_CS_AS) = N'BUILTIN\ADMINISTRATORS')
      END
    END
    ELSE
    BEGIN
      -- Check if the NT login has been explicitly denied access
      IF (EXISTS (SELECT *
                  FROM master.dbo.syslogins
                  WHERE (loginname = @login_name)
                    AND (denylogin = 1)))
      BEGIN
        SELECT @has_server_access = 0,
               @is_sysadmin = sysadmin,
               @actual_login_name = loginname
        FROM master.dbo.syslogins
        WHERE (loginname = @login_name)
      END
      ELSE
      BEGIN
        -- declare table variable for storing results
        DECLARE @xp_results TABLE
        (
        account_name      sysname      COLLATE database_default NOT NULL PRIMARY KEY,
        type              NVARCHAR(10) COLLATE database_default NOT NULL,
        privilege         NVARCHAR(10) COLLATE database_default NOT NULL,
        mapped_login_name sysname      COLLATE database_default NOT NULL,
        permission_path   sysname      COLLATE database_default NULL
        )

        -- Call xp_logininfo to determine server access
        INSERT INTO @xp_results
        EXECUTE master.dbo.xp_logininfo @login_name

        SELECT @has_server_access = CASE COUNT(*)
                                      WHEN 0 THEN 0
                                      ELSE 1
                                    END
        FROM @xp_results
        SELECT @actual_login_name = mapped_login_name,
               @is_sysadmin = CASE UPPER(privilege collate SQL_Latin1_General_CP1_CS_AS)
                                WHEN 'ADMIN' THEN 1
                                ELSE 0
                             END
        FROM @xp_results
      END
    END
      
    -- Only cache the NT logins to approximate the behavior of Sql Server and Windows (see bug 323287)
    -- update the cache only if something is found
    IF  (UPPER(@actual_login_name collate SQL_Latin1_General_CP1_CS_AS) <> '(UNKNOWN)')
    BEGIN
      -- Procedure starts its own transaction.
      BEGIN TRANSACTION;

      -- Modify database.
      -- use a try catch login to prevent any error when trying 
      -- to insert/update syscachedcredentials table
      -- no need to fail since the job owner has been validated
      BEGIN TRY      
        IF EXISTS (SELECT * FROM msdb.dbo.syscachedcredentials WITH (TABLOCKX) WHERE login_name = @login_name)
        BEGIN
          UPDATE msdb.dbo.syscachedcredentials
          SET    has_server_access = @has_server_access,
                is_sysadmin_member = @is_sysadmin,
               cachedate = GETDATE()
          WHERE  login_name = @login_name
        END
        ELSE
        BEGIN
          INSERT INTO msdb.dbo.syscachedcredentials(login_name, has_server_access, is_sysadmin_member) 
          VALUES(@login_name, @has_server_access, @is_sysadmin)
        END
        END TRY
        BEGIN CATCH
          -- If an error occurred we want to ignore it
        END CATCH

        -- The procedure must commit the transaction it started.
        COMMIT TRANSACTION;  
    END
      
  END
  ELSE
  BEGIN
    -- Standard login
    IF (EXISTS (SELECT *
                FROM master.dbo.syslogins
                WHERE (loginname = @login_name)))
    BEGIN
      SELECT @has_server_access = hasaccess,
             @is_sysadmin = sysadmin,
             @actual_login_name = loginname
      FROM master.dbo.syslogins
      WHERE (loginname = @login_name)
    END
  END

  IF (@is_sysadmin_member IS NULL)
    -- Return result row
    SELECT has_server_access = @has_server_access,
           is_sysadmin       = @is_sysadmin,
           actual_login_name = @actual_login_name
  ELSE
    -- output variable only
    SELECT @is_sysadmin_member = @is_sysadmin
END
go


/**************************************************************/
/* SP_SEM_ADD_MESSAGE [used by SEM only]                      */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_sem_add_message...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_sem_add_message')
              AND (type = 'P')))
  DROP PROCEDURE sp_sem_add_message
go
CREATE PROCEDURE sp_sem_add_message
  @msgnum   INT           = NULL,
  @severity SMALLINT      = NULL,
  @msgtext  NVARCHAR(255) = NULL,
  @lang     sysname       = NULL, -- Message language name
  @with_log VARCHAR(5)    = 'FALSE',
  @replace  VARCHAR(7)    = NULL
AS
BEGIN
  DECLARE @retval        INT
  DECLARE @language_name sysname

  SET NOCOUNT ON

  SET ROWCOUNT 1
  SELECT @language_name = name
  FROM sys.syslanguages
  WHERE msglangid = (SELECT number
                     FROM master.dbo.spt_values
                     WHERE (type = 'LNG')
                       AND (name = @lang))
  SET ROWCOUNT 0

  SELECT @language_name = ISNULL(@language_name, 'us_english')
  EXECUTE @retval = master.dbo.sp_addmessage @msgnum, @severity, @msgtext, @language_name, @with_log, @replace
  RETURN(@retval)
END
go

/**************************************************************/
/* SP_SEM_DROP_MESSAGE [used by SEM only]                     */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_sem_drop_message...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_sem_drop_message')
              AND (type = 'P')))
  DROP PROCEDURE sp_sem_drop_message
go
CREATE PROCEDURE sp_sem_drop_message
  @msgnum int     = NULL,
  @lang   sysname = NULL -- Message language name
AS
BEGIN
  DECLARE @retval        INT
  DECLARE @language_name sysname

  SET NOCOUNT ON

  SET ROWCOUNT 1
  SELECT @language_name = name
  FROM sys.syslanguages
  WHERE msglangid = (SELECT number
                     FROM master.dbo.spt_values
                     WHERE (type = 'LNG')
                       AND (name = @lang))
  SET ROWCOUNT 0

  SELECT @language_name = ISNULL(@language_name, 'us_english')
  EXECUTE @retval = master.dbo.sp_dropmessage @msgnum, @language_name
  RETURN(@retval)
END
go

/**************************************************************/
/* SP_GET_MESSAGE_DESCRIPTION [used by SEM only]              */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_get_message_description...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_get_message_description')
              AND (type = 'P')))
  DROP PROCEDURE sp_get_message_description
go
CREATE PROCEDURE sp_get_message_description
  @error INT
AS
BEGIN
  IF EXISTS (SELECT * FROM master.dbo.sysmessages WHERE (error = @error) AND (msglangid = (SELECT msglangid FROM sys.syslanguages WHERE (langid = @@langid))))
    SELECT description FROM master.dbo.sysmessages WHERE (error = @error) AND (msglangid = (SELECT msglangid FROM sys.syslanguages WHERE (langid = @@langid)))
  ELSE
    SELECT description FROM master.dbo.sysmessages WHERE (error = @error) AND (msglangid = 1033)
END
go

/**************************************************************/
/* SP_SQLAGENT_GET_PERF_COUNTERS                              */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_sqlagent_get_perf_counters...'
GO
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_sqlagent_get_perf_counters')
              AND (type = 'P')))
  DROP PROCEDURE sp_sqlagent_get_perf_counters
GO
CREATE PROCEDURE sp_sqlagent_get_perf_counters
  @all_counters BIT = 0
AS
BEGIN

  SET NOCOUNT ON

  -- 32 bit fraction counter types
  DECLARE @perfTypeRawFraction INT
  DECLARE @perfTypeRawBase     INT

  -- A counter of type PERF_RAW_FRACTION, which is a 32-bit counter value.
  SET @perfTypeRawFraction = 537003008 --  In hex, 0x20020400.

   -- A count of type PERF_RAW_BASE, which is the 32-bit divisor used
   -- when handling PERF_RAW_FRACTION types. This counter type should
   -- not be displayed to the user since it is used for mathematical
   -- operations.
  SET @perfTypeRawBase     = 1073939459 -- In hex, 0x40030403.


  -- 64 bit fraction counter types
  DECLARE @perfTypeLargeRawFraction INT
  DECLARE @perfTypeLargeRawBase     INT

  -- A counter of type PERF_LARGE RAW_FRACTION, which is a 64-bit counter value.
  SET @perfTypeLargeRawFraction = 537003264 --  In hex, 0x20020500.

   -- A count of type PERF_LARGE_RAW_BASE, which is the 64-bit divisor used
   -- when handling PERF_LARGE_RAW_FRACTION types. This counter type should
   -- not be displayed to the user since it is used for mathematical
   -- operations.
  SET @perfTypeLargeRawBase     = 1073939712 -- In hex, 0x40030500.



  IF (@all_counters = 0)
  BEGIN
        SELECT  spi1.object_name,
                spi1.counter_name,
                'instance_name' = CASE spi1.instance_name
                                    WHEN N'' THEN NULL
                                    ELSE spi1.instance_name
                                    END,
                'value' = CASE spi1.cntr_type
                            WHEN @perfTypeRawFraction -- 32 bit fraction
                                THEN CONVERT(FLOAT, spi1.cntr_value) / (SELECT CASE spi2.cntr_value 
                                                                            WHEN 0 THEN 1 
                                                                            ELSE spi2.cntr_value 
                                                                            END
                                                                        FROM sysalerts_performance_counters_view spi2
                                                                        WHERE (RTRIM(spi1.counter_name) + ' ' = SUBSTRING(spi2.counter_name, 1, PATINDEX('% base%', LOWER(spi2.counter_name))))
                                                                        AND spi1.object_name = spi2.object_name
                                                                        AND spi1.server_name = spi2.server_name
                                                                        AND spi1.instance_name = spi2.instance_name
                                                                        AND spi2.cntr_type = @perfTypeRawBase
                                                                        )
                            WHEN @perfTypeLargeRawFraction  -- 64 bit fraction
                                THEN CONVERT(FLOAT, spi1.cntr_value) / (SELECT CASE spi2.cntr_value 
                                                                            WHEN 0 THEN 1 
                                                                            ELSE spi2.cntr_value 
                                                                            END
                                                                        FROM sysalerts_performance_counters_view spi2
                                                                        WHERE (RTRIM(spi1.counter_name) + ' ' = SUBSTRING(spi2.counter_name, 1, PATINDEX('% base%', LOWER(spi2.counter_name))))
                                                                        AND spi1.object_name = spi2.object_name
                                                                        AND spi1.server_name = spi2.server_name
                                                                        AND spi1.instance_name = spi2.instance_name
                                                                        AND spi2.cntr_type = @perfTypeLargeRawBase
                                                                        )
                                ELSE spi1.cntr_value
                            END,
       'type' = spi1.cntr_type,
        spi1.server_name
        FROM sysalerts_performance_counters_view spi1,
        (
                SELECT DISTINCT 
                    SUBSTRING(performance_condition, 
                                1, 
                                CHARINDEX('|', performance_condition, 
                                            PATINDEX('%_|_%', performance_condition) + 2) - 1
                             )
                AS performance_condition_s  
                FROM msdb.dbo.sysalerts
                WHERE performance_condition IS NOT NULL
                AND ISNULL(event_id, 0) <> 8 -- exclude WMI events that reuse performance_condition field
                AND enabled = 1
        ) tmp -- We want to select only those counters that have an enabled performance sysalert
        WHERE spi1.cntr_type <> @perfTypeRawBase      -- ignore 32-bit denominator counter type
        AND spi1.cntr_type <> @perfTypeLargeRawBase      -- ignore 64-bit denominator counter type
        AND tmp.performance_condition_s = (spi1.object_name + '|' + spi1.counter_name)
        OPTION (HASH JOIN, LOOP JOIN) -- Avoid merge join when small number of alerts are defined
  END
  ELSE
  BEGIN
        SELECT  spi1.object_name,
                spi1.counter_name,
                'instance_name' = CASE spi1.instance_name
                                    WHEN N'' THEN NULL
                                    ELSE spi1.instance_name
                                    END,
                'value' = CASE spi1.cntr_type
                            WHEN @perfTypeRawFraction -- 32 bit fraction
                            THEN CONVERT(FLOAT, spi1.cntr_value) / (SELECT CASE spi2.cntr_value 
                                                                        WHEN 0 THEN 1 
                                                                        ELSE spi2.cntr_value 
                                                                        END
                                                                    FROM sysalerts_performance_counters_view spi2
                                                                    WHERE (RTRIM(spi1.counter_name) + ' ' = SUBSTRING(spi2.counter_name, 1, PATINDEX('% base%', LOWER(spi2.counter_name))))
                                                                    AND spi1.object_name = spi2.object_name
                                                                    AND spi1.server_name = spi2.server_name
                                                                    AND spi1.instance_name = spi2.instance_name
                                                                    AND spi2.cntr_type = @perfTypeRawBase
                                                                    )
                            WHEN @perfTypeLargeRawFraction  -- 64 bit fraction
                            THEN CONVERT(FLOAT, spi1.cntr_value) / (SELECT CASE spi2.cntr_value 
                                                                        WHEN 0 THEN 1 
                                                                        ELSE spi2.cntr_value 
                                                                        END
                                                                    FROM sysalerts_performance_counters_view spi2
                                                                    WHERE (RTRIM(spi1.counter_name) + ' ' = SUBSTRING(spi2.counter_name, 1, PATINDEX('% base%', LOWER(spi2.counter_name))))
                                                                    AND spi1.object_name = spi2.object_name
                                                                    AND spi1.server_name = spi2.server_name
                                                                    AND spi1.instance_name = spi2.instance_name
                                                                    AND spi2.cntr_type = @perfTypeLargeRawBase
                                                                    )
                            ELSE spi1.cntr_value
                        END,
                'type' = spi1.cntr_type,
                spi1.server_name
        FROM sysalerts_performance_counters_view spi1
        WHERE spi1.cntr_type <> @perfTypeRawBase      -- ignore 32-bit denominator counter type
        AND spi1.cntr_type <> @perfTypeLargeRawBase -- ignore 64-bit denominator counter type
  END
END

GO


/**************************************************************/
/* SP_SQLAGENT_NOTIFY                                         */
/*                                                            */
/* NOTE: We define this procedure here instead of in the      */
/*      'Support procedures' section because of the many      */
/*       other procedures that reference it.                  */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_sqlagent_notify...'
go

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_sqlagent_notify')
              AND (type = 'P')))
  DROP PROCEDURE sp_sqlagent_notify
go
CREATE PROCEDURE sp_sqlagent_notify
  @op_type     NCHAR(1),                -- One of: J (Job action [refresh or start/stop]),
                                        --         S (Schedule action [refresh only])
                                        --         A (Alert action [refresh only]),
                                        --         G (Re-cache all registry settings),
                                        --         D (Dump job [or job schedule] cache to errorlog)
                                        --         P (Force an immediate poll of the MSX)
                                        --         L (Cycle log file)
                                        --         T (Test WMI parameters (namespace and query))
  @job_id      UNIQUEIDENTIFIER = NULL, -- JobID (for OpTypes 'J', 'S' and 'D')
  @schedule_id INT              = NULL, -- ScheduleID (for OpType 'S')
  @alert_id    INT              = NULL, -- AlertID (for OpType 'A')
  @action_type NCHAR(1)         = NULL, -- For 'J' one of: R (Run - no service check),
                                        --                 S (Start - with service check),
                                        --                 I (Insert),
                                        --                 U (Update),
                                        --                 D (Delete),
                                        --                 C (Stop [Cancel])
                                        -- For 'S' or 'A' one of: I (Insert),
                                        --                        U (Update),
                                        --                        D (Delete)
  @error_flag  INT              = 1,    -- Set to 0 to suppress the error from xp_sqlagent_notify if SQLServer agent is not running
  @wmi_namespace nvarchar(128) = NULL,
  @wmi_query     nvarchar(512) = NULL
AS
BEGIN
  DECLARE @retval         INT
  DECLARE @id_as_char     VARCHAR(10)
  DECLARE @job_id_as_char VARCHAR(36)
  DECLARE @nt_user_name   NVARCHAR(100)

  SET NOCOUNT ON

  SELECT @retval = 0 -- Success

  -- Make sure that we're dealing only with uppercase characters
  SELECT @op_type     = UPPER(@op_type collate SQL_Latin1_General_CP1_CS_AS)
  SELECT @action_type = UPPER(@action_type collate SQL_Latin1_General_CP1_CS_AS)

  -- Verify operation code
  IF (CHARINDEX(@op_type, N'JSAGDPLT') = 0)
  BEGIN
    RAISERROR(14266, -1, -1, '@op_type', 'J, S, A, G, D, P, L, T')
    RETURN(1) -- Failure
  END

  -- Check the job id for those who use it
  IF (CHARINDEX(@op_type, N'JSD') <> 0)
  BEGIN
    IF (NOT ((@op_type = N'D' OR @op_type = N'S') AND (@job_id IS NULL))) -- For 'D' and 'S' job_id is optional
    BEGIN
      IF ((@job_id IS NULL) OR
          ((@action_type <> N'D') AND NOT EXISTS (SELECT *
                                                  FROM msdb.dbo.sysjobs_view
                                                  WHERE (job_id = @job_id))))
      BEGIN
        SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id)
        RAISERROR(14262, -1, -1, '@job_id', @job_id_as_char)
        RETURN(1) -- Failure
      END
    END
  END

  -- Verify 'job' action parameters
  IF (@op_type = N'J')
  BEGIN
    SELECT @alert_id = 0
    IF (@schedule_id IS NULL) SELECT @schedule_id = 0

    -- The schedule_id (if specified) is the start step
    IF ((CHARINDEX(@action_type, N'RS') <> 0) AND (@schedule_id <> 0))
    BEGIN
      IF (NOT EXISTS (SELECT *
                      FROM msdb.dbo.sysjobsteps
                      WHERE (job_id = @job_id)
                        AND (step_id = @schedule_id)))
      BEGIN
        SELECT @id_as_char = ISNULL(CONVERT(VARCHAR, @schedule_id), '(null)')
        RAISERROR(14262, -1, -1, '@schedule_id', @id_as_char)
        RETURN(1) -- Failure
      END
    END
    ELSE
      SELECT @schedule_id = 0

    IF (CHARINDEX(@action_type, N'RSIUDC') = 0)
    BEGIN
      RAISERROR(14266, -1, -1, '@action_type', 'R, S, I, U, D, C')
      RETURN(1) -- Failure
    END
  END

  -- Verify 'schedule' action parameters
  IF (@op_type = N'S')
  BEGIN
    SELECT @alert_id = 0

    IF (CHARINDEX(@action_type, N'IUD') = 0)
    BEGIN
      RAISERROR(14266, -1, -1, '@action_type', 'I, U, D')
      RETURN(1) -- Failure
    END

    IF ((@schedule_id IS NULL) OR
        ((@action_type <> N'D') AND NOT EXISTS (SELECT *
                                                FROM msdb.dbo.sysschedules
                                                WHERE (schedule_id = @schedule_id))))
    BEGIN
      SELECT @id_as_char = ISNULL(CONVERT(VARCHAR, @schedule_id), '(null)')
      RAISERROR(14262, -1, -1, '@schedule_id', @id_as_char)
      RETURN(1) -- Failure
    END
  END

  -- Verify 'alert' action parameters
  IF (@op_type = N'A')
  BEGIN
    SELECT @job_id = 0x00
    SELECT @schedule_id = 0

    IF (CHARINDEX(@action_type, N'IUD') = 0)
    BEGIN
      RAISERROR(14266, -1, -1, '@action_type', 'I, U, D')
      RETURN(1) -- Failure
    END

    IF ((@alert_id IS NULL) OR
        ((@action_type <> N'D') AND NOT EXISTS (SELECT *
                                                FROM msdb.dbo.sysalerts
                                                WHERE (id = @alert_id))))
    BEGIN
      SELECT @id_as_char = ISNULL(CONVERT(VARCHAR, @alert_id), '(null)')
      RAISERROR(14262, -1, -1, '@alert_id', @id_as_char)
      RETURN(1) -- Failure
    END
  END

  -- Verify 'registry', 'job dump' and 'force MSX poll' and 'cycle log' action parameters
  IF (CHARINDEX(@op_type, N'GDPL') <> 0)
  BEGIN
    IF (@op_type <> N'D')
      SELECT @job_id = 0x00
    SELECT @alert_id = 0
    SELECT @schedule_id = 0
    SELECT @action_type = NULL
  END

  -- Parameters are valid, so now check execution permissions...

  -- For anything except a job (or schedule) action the caller must be SysAdmin, DBO, or DB_Owner
  IF (@op_type NOT IN (N'J', N'S'))
  BEGIN
    IF NOT ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR
            (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1) OR
            (UPPER(USER_NAME() collate SQL_Latin1_General_CP1_CS_AS) = N'DBO'))
    BEGIN
      RAISERROR(14260, -1, -1)
      RETURN(1) -- Failure
    END
  END

  -- For a Job Action the caller must be SysAdmin, DBO, DB_Owner, or the job owner
  IF (@op_type = N'J')
  BEGIN
    IF NOT ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR
            (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1) OR
            (UPPER(USER_NAME() collate SQL_Latin1_General_CP1_CS_AS) = N'DBO') OR
            (EXISTS (SELECT *
                     FROM msdb.dbo.sysjobs_view
                     WHERE (job_id = @job_id))))
    BEGIN
      RAISERROR(14252, -1, -1)
      RETURN(1) -- Failure
    END
  END

  --verify WMI parameters
  IF (@op_type = N'T')
  BEGIN
   SELECT @wmi_namespace = LTRIM(RTRIM(@wmi_namespace))
   SELECT @wmi_query = LTRIM(RTRIM(@wmi_query))  
    IF (@wmi_namespace IS NULL) or (@wmi_query IS NULL)
   BEGIN
          RAISERROR(14508, 16, 1)
          RETURN(1) -- Failure      
   END
  END

  -- Ok, let's do it...
  SELECT @nt_user_name = ISNULL(NT_CLIENT(), ISNULL(SUSER_SNAME(), FORMATMESSAGE(14205)))
  EXECUTE @retval = master.dbo.xp_sqlagent_notify @op_type, @job_id, @schedule_id, @alert_id, @action_type, @nt_user_name, @error_flag, @@trancount, @wmi_namespace, @wmi_query

  RETURN(@retval)
END
go

/**************************************************************/
/* SP_IS_SQLAGENT_STARTING                                    */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_is_sqlagent_starting...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_is_sqlagent_starting')
              AND (type = 'P')))
  DROP PROCEDURE sp_is_sqlagent_starting
go
CREATE PROCEDURE sp_is_sqlagent_starting
AS
BEGIN
  DECLARE @retval INT

  SELECT @retval = 0
  EXECUTE master.dbo.xp_sqlagent_is_starting @retval OUTPUT
  IF (@retval = 1)
    RAISERROR(14258, -1, -1)

  RETURN(@retval)
END
go


/**************************************************************/
/* SP_VERIFY_JOB_IDENTIFIERS                                  */
/*                                                            */
/* NOTE: We define this procedure here instead of in the      */
/*      'Support procedures' section because of the many      */
/*       other procedures that reference it.                  */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_job_identifiers...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_job_identifiers')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_job_identifiers
go
CREATE PROCEDURE sp_verify_job_identifiers
  @name_of_name_parameter  VARCHAR(60),             -- Eg. '@job_name'
  @name_of_id_parameter    VARCHAR(60),             -- Eg. '@job_id'
  @job_name                sysname          OUTPUT, -- Eg. 'My Job'
  @job_id                  UNIQUEIDENTIFIER OUTPUT,
  @sqlagent_starting_test  VARCHAR(7) = 'TEST',      -- By default we DO want to test if SQLServerAgent is running (caller should specify 'NO_TEST' if not desired)
  @owner_sid                VARBINARY(85) = NULL OUTPUT  
AS
BEGIN
  DECLARE @retval         INT
  DECLARE @job_id_as_char VARCHAR(36)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter))
  SELECT @name_of_id_parameter   = LTRIM(RTRIM(@name_of_id_parameter))
  SELECT @job_name               = LTRIM(RTRIM(@job_name))

  IF (@job_name = N'') SELECT @job_name = NULL

  IF ((@job_name IS NULL)     AND (@job_id IS NULL)) OR
     ((@job_name IS NOT NULL) AND (@job_id IS NOT NULL))
  BEGIN
    RAISERROR(14294, -1, -1, @name_of_id_parameter, @name_of_name_parameter)
    RETURN(1) -- Failure
  END

  -- Check job id
  IF (@job_id IS NOT NULL)
  BEGIN
    SELECT @job_name = name,
           @owner_sid = owner_sid
    FROM msdb.dbo.sysjobs_view
    WHERE (job_id = @job_id)
    
    -- the view would take care of all the permissions issues.
    IF (@job_name IS NULL) 
    BEGIN
      SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id)
      RAISERROR(14262, -1, -1, '@job_id', @job_id_as_char)
      RETURN(1) -- Failure
    END
  END
  ELSE
  -- Check job name
  IF (@job_name IS NOT NULL)
  BEGIN
    -- Check if the job name is ambiguous
    IF ((SELECT COUNT(*)
         FROM msdb.dbo.sysjobs_view
         WHERE (name = @job_name)) > 1)
    BEGIN
      RAISERROR(14293, -1, -1, @job_name, @name_of_id_parameter, @name_of_name_parameter)
      RETURN(1) -- Failure
    END

    -- The name is not ambiguous, so get the corresponding job_id (if the job exists)
    SELECT @job_id = job_id,
           @owner_sid = owner_sid
    FROM msdb.dbo.sysjobs_view
    WHERE (name = @job_name)
    
    -- the view would take care of all the permissions issues.
    IF (@job_id IS NULL) 
    BEGIN
      RAISERROR(14262, -1, -1, '@job_name', @job_name)
      RETURN(1) -- Failure
    END
  END

  IF (@sqlagent_starting_test = 'TEST')
  BEGIN
    -- Finally, check if SQLServerAgent is in the process of starting and if so prevent the
    -- calling SP from running
    EXECUTE @retval = msdb.dbo.sp_is_sqlagent_starting
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END

  RETURN(0) -- Success
END
go


/**************************************************************/
/* SP_VERIFY_SCHEDULE_IDENTIFIERS                             */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_schedule_identifiers...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_schedule_identifiers')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_schedule_identifiers
go
CREATE PROCEDURE sp_verify_schedule_identifiers
  @name_of_name_parameter   VARCHAR(60),             -- Eg. '@schedule_name'
  @name_of_id_parameter     VARCHAR(60),             -- Eg. '@schedule_id'
  @schedule_name            sysname             OUTPUT, 
  @schedule_id              INT                 OUTPUT,
  @owner_sid                VARBINARY(85)       OUTPUT,
  @orig_server_id           INT                 OUTPUT,
  @job_id_filter            UNIQUEIDENTIFIER    = NULL
AS
BEGIN
  DECLARE @retval         INT
  DECLARE @schedule_id_as_char VARCHAR(36)
  DECLARE @sch_name_count INT

  SET NOCOUNT ON
  
  -- Remove any leading/trailing spaces from parameters
  SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter))
  SELECT @name_of_id_parameter   = LTRIM(RTRIM(@name_of_id_parameter))
  SELECT @schedule_name          = LTRIM(RTRIM(@schedule_name))
  SELECT @sch_name_count         = 0
  

  IF (@schedule_name = N'') SELECT @schedule_name = NULL

  IF ((@schedule_name IS NULL)     AND (@schedule_id IS NULL)) OR
     ((@schedule_name IS NOT NULL) AND (@schedule_id IS NOT NULL))
  BEGIN
    RAISERROR(14373, -1, -1, @name_of_id_parameter, @name_of_name_parameter)
    RETURN(1) -- Failure
  END

  -- Check schedule id
  IF (@schedule_id IS NOT NULL)
  BEGIN
    -- if Agent is calling look in all schedules not just the local server schedules
    if(PROGRAM_NAME() LIKE N'SQLAgent%')
    BEGIN
        -- Look at all schedules
        SELECT @schedule_name   = name,
           @owner_sid           = owner_sid,
           @orig_server_id      = originating_server_id
        FROM msdb.dbo.sysschedules
        WHERE (schedule_id = @schedule_id)
    END
    ELSE
    BEGIN
        --Look at local schedules only
        SELECT @schedule_name   = name,
           @owner_sid           = owner_sid,
           @orig_server_id      = originating_server_id
        FROM msdb.dbo.sysschedules_localserver_view
        WHERE (schedule_id = @schedule_id)
    END

    IF (@schedule_name IS NULL)
    BEGIN
     --If the schedule is from an MSX and a sysadmin is calling report a specific 'MSX' message
      IF(PROGRAM_NAME() NOT LIKE N'SQLAgent%' AND
         ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 AND
         EXISTS(SELECT * 
                FROM msdb.dbo.sysschedules as sched
                  JOIN msdb.dbo.sysoriginatingservers_view as svr
                    ON sched.originating_server_id = svr.originating_server_id
                WHERE (schedule_id = @schedule_id) AND 
                      (svr.master_server = 1)))
     BEGIN
       RAISERROR(14274, -1, -1)
     END
      ELSE  
      BEGIN
        SELECT @schedule_id_as_char = CONVERT(VARCHAR(36), @schedule_id)
        RAISERROR(14262, -1, -1, '@schedule_id', @schedule_id_as_char)
      END

      RETURN(1) -- Failure
    END
  END
  ELSE
  -- Check job name
  IF (@schedule_name IS NOT NULL)
  BEGIN
    -- if a job_id is supplied use it as a filter. This helps with V8 legacy support
    IF(@job_id_filter IS NOT NULL)
    BEGIN
        -- Check if the job name is ambiguous and also get the schedule_id optimistically.
        -- If the name is not ambiguous this gets the corresponding schedule_id (if the schedule exists)
        SELECT @sch_name_count = COUNT(*),
               @schedule_id    = MIN(s.schedule_id),
               @owner_sid      = MIN(owner_sid),
               @orig_server_id = MIN(originating_server_id)
        FROM msdb.dbo.sysschedules_localserver_view as s
          JOIN msdb.dbo.sysjobschedules as js 
            ON s.schedule_id = js.schedule_id
        WHERE (name = @schedule_name) AND
              (js.job_id = @job_id_filter)
    END
    ELSE
    BEGIN
      -- Check if the job name is ambiguous from the count(*) result
        -- If the name is not ambiguous it is safe use the fields returned by the MIN() function
        SELECT @sch_name_count = COUNT(*),
         @schedule_id     = MIN(schedule_id),
            @owner_sid       = MIN(owner_sid),
            @orig_server_id  = MIN(originating_server_id)
        FROM msdb.dbo.sysschedules_localserver_view
        WHERE (name = @schedule_name)
    END

    IF(@sch_name_count > 1)
    BEGIN
        -- ambiguous, user needs to use a schedule_id instead of a schedule_name
        RAISERROR(14371, -1, -1, @schedule_name, @name_of_id_parameter, @name_of_name_parameter)
        RETURN(1) -- Failure
    END

    --schedule_id isn't visible to this user or doesn't exist
    IF (@schedule_id IS NULL)
    BEGIN
      --If the schedule is from an MSX and a sysadmin is calling report a specific 'MSX' message
      IF(PROGRAM_NAME() NOT LIKE N'SQLAgent%' AND
         ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 AND
         EXISTS(SELECT * 
                FROM msdb.dbo.sysschedules as sched
                  JOIN msdb.dbo.sysoriginatingservers_view as svr
                    ON sched.originating_server_id = svr.originating_server_id
                  JOIN msdb.dbo.sysjobschedules as js 
                    ON sched.schedule_id = js.schedule_id
                WHERE (svr.master_server = 1) AND
                      (name = @schedule_name) AND
                      ((@job_id_filter IS NULL) OR (js.job_id = @job_id_filter))))
     BEGIN
       RAISERROR(14274, -1, -1)
     END
      ELSE
      BEGIN
        --If not a MSX schedule raise local error
        RAISERROR(14262, -1, -1, '@schedule_name', @schedule_name)
      END

      RETURN(1) -- Failure
    END
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_VERIFY_JOBPROC_CALLER                                   */
/*                                                            */
/* NOTE: We define this procedure here instead of in the      */
/*      'Support procedures' section because of the many      */
/*       other procedures that reference it.                  */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_jobproc_caller...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_jobproc_caller')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_jobproc_caller
go
CREATE PROCEDURE sp_verify_jobproc_caller
  @job_id       UNIQUEIDENTIFIER,
  @program_name sysname
AS
BEGIN
  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @program_name = LTRIM(RTRIM(@program_name))

  IF (EXISTS (SELECT    *
              FROM      msdb.dbo.sysjobs_view
              WHERE     (job_id = @job_id)
              AND       (master_server = 1) )) -- master_server = 1 filters on MSX jobs in this TSX server
              AND       (PROGRAM_NAME() NOT LIKE @program_name)
  BEGIN
    RAISERROR(14274, -1, -1)
    RETURN(1) -- Failure
  END

  RETURN(0)
END
go

/**************************************************************/
/* SP_DOWNLOADED_ROW_LIMITER                                  */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_downloaded_row_limiter...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_downloaded_row_limiter')
              AND (type = 'P')))
  DROP PROCEDURE dbo.sp_downloaded_row_limiter
go
CREATE PROCEDURE sp_downloaded_row_limiter
  @server_name sysname -- Target server name
AS
BEGIN
  -- This trigger controls how many downloaded (status = 1) sysdownloadlist rows exist
  -- for any given server.  It does NOT control the absolute number of rows in the table.

  DECLARE @current_rows_per_server INT
  DECLARE @max_rows_per_server     INT -- This value comes from the resgistry (DownloadedMaxRows)
  DECLARE @rows_to_delete          INT
  DECLARE @quoted_server_name      NVARCHAR(514) -- enough room to accomodate the quoted name
  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @server_name = LTRIM(RTRIM(@server_name))

  -- Check the server name (if it's bad we fail silently)
  IF (@server_name IS NULL) OR
     (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysdownloadlist
                  WHERE (target_server = @server_name)))
    RETURN(1) -- Failure

  SELECT @max_rows_per_server = 0

  -- Get the max-rows-per-server from the registry
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'DownloadedMaxRows',
                                         @max_rows_per_server OUTPUT,
                                         N'no_output'

  -- Check if we are limiting sysdownloadlist rows
  IF (ISNULL(@max_rows_per_server, -1) = -1)
    RETURN

  -- Check that max_rows_per_server is >= 0
  IF (@max_rows_per_server < -1)
  BEGIN
    -- It isn't, so default to 100 rows
    SELECT @max_rows_per_server = 100
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'DownloadedMaxRows',
                                            N'REG_DWORD',
                                            @max_rows_per_server
  END

  -- Get the number of downloaded rows in sysdownloadlist for the target server in question
  -- NOTE: Determining this [quickly] requires a [non-clustered] index on target_server
  SELECT @current_rows_per_server = COUNT(*)
  FROM msdb.dbo.sysdownloadlist
  WHERE (target_server = @server_name)
    AND (status = 1)

  -- Delete the oldest downloaded row(s) for the target server in question if the new row has
  -- pushed us over the per-server row limit
  SELECT @rows_to_delete = @current_rows_per_server - @max_rows_per_server
  IF (@rows_to_delete > 0)
  BEGIN
    WITH RowsToDelete AS (
      SELECT TOP (@rows_to_delete) *
      FROM msdb.dbo.sysdownloadlist
      WHERE (target_server = @server_name)
        AND (status = 1)
      ORDER BY instance_id
    )
    DELETE FROM RowsToDelete;
  END
END
go

/**************************************************************/
/* SP_POST_MSX_OPERATION                                      */
/*                                                            */
/* NOTE: We define this procedure here instead of in the      */
/*      'Support procedures' section because of the many      */
/*       other procedures that reference it.                  */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_post_msx_operation...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_post_msx_operation')
              AND (type = 'P')))
  DROP PROCEDURE sp_post_msx_operation
go
CREATE PROCEDURE sp_post_msx_operation
  @operation              VARCHAR(64),
  @object_type            VARCHAR(64)       = 'JOB',-- Can be JOB, SERVER or SCHEDULE
  @job_id                 UNIQUEIDENTIFIER  = NULL, -- NOTE: 0x00 means 'ALL' jobs
  @specific_target_server sysname           = NULL,
  @value                  INT               = NULL, -- For polling interval value
  @schedule_uid           UNIQUEIDENTIFIER  = NULL  -- schedule_uid if the @object_type = 'SCHEDULE'
AS
BEGIN
  DECLARE @operation_code            INT
  DECLARE @specific_target_server_id INT
  DECLARE @instructions_posted       INT
  DECLARE @job_id_as_char            VARCHAR(36)
  DECLARE @schedule_uid_as_char      VARCHAR(36)
  DECLARE @msx_time_zone_adjustment  INT
  DECLARE @local_machine_name        sysname
  DECLARE @retval                    INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @operation              = LTRIM(RTRIM(@operation))
  SELECT @object_type            = LTRIM(RTRIM(@object_type))
  SELECT @specific_target_server = LTRIM(RTRIM(@specific_target_server))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@specific_target_server = N'') SELECT @specific_target_server = NULL

  -- Only a sysadmin can do this, but fail silently for a non-sysadmin
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) 
    RETURN(0) -- Success (or more accurately a no-op)

  -- Check operation
  SELECT @operation = UPPER(@operation collate SQL_Latin1_General_CP1_CS_AS)
  SELECT @operation_code = CASE @operation
                             WHEN 'INSERT'    THEN 1
                             WHEN 'UPDATE'    THEN 2
                             WHEN 'DELETE'    THEN 3
                             WHEN 'START'     THEN 4
                             WHEN 'STOP'      THEN 5
                             WHEN 'RE-ENLIST' THEN 6
                             WHEN 'DEFECT'    THEN 7
                             WHEN 'SYNC-TIME' THEN 8
                             WHEN 'SET-POLL'  THEN 9
                             ELSE 0
                           END
  IF (@operation_code = 0)
  BEGIN
    RAISERROR(14266, -1, -1, '@operation_code', 'INSERT, UPDATE, DELETE, START, STOP, RE-ENLIST, DEFECT, SYNC-TIME, SET-POLL')
    RETURN(1) -- Failure
  END

  -- Check object type (in 9.0 only 'JOB', 'SERVER' or 'SCHEDULE'are valid)
  IF ((@object_type <> 'JOB') AND (@object_type <> 'SERVER') AND (@object_type <> 'SCHEDULE'))
  BEGIN
    RAISERROR(14266, -1, -1, '@object_type', 'JOB, SERVER, SCHEDULE')
    RETURN(1) -- Failure
  END

  -- Check that for a object type of JOB a job_id has been supplied
  IF ((@object_type = 'JOB') AND (@job_id IS NULL))
  BEGIN
    RAISERROR(14233, -1, -1)
    RETURN(1) -- Failure
  END
  
    -- Check that for a object type of JOB a job_id has been supplied
  IF ((@object_type = 'SCHEDULE') AND (@schedule_uid IS NULL))
  BEGIN
    RAISERROR(14365, -1, -1)
    RETURN(1) -- Failure
  END

  -- Check polling interval value
  IF (@operation_code = 9) AND ((ISNULL(@value, 0) < 10) OR (ISNULL(@value, 0) > 28800))
  BEGIN
    RAISERROR(14266, -1, -1, '@value', '10..28800')
    RETURN(1) -- Failure
  END

  -- Check specific target server
  IF (@specific_target_server IS NOT NULL)
  BEGIN
    SELECT @specific_target_server = UPPER(@specific_target_server)

    -- Check if the local server is being targeted
    IF (@specific_target_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))))
    BEGIN
      RETURN(0)
    END
    ELSE
    BEGIN
      SELECT @specific_target_server_id = server_id
      FROM msdb.dbo.systargetservers
      WHERE (UPPER(server_name) = @specific_target_server)
      IF (@specific_target_server_id IS NULL)
      BEGIN
        RAISERROR(14262, -1, -1, '@specific_target_server', @specific_target_server)
        RETURN(1) -- Failure
      END
    END
  END

  -- Check that this server is an MSX server
  IF ((SELECT COUNT(*)
       FROM msdb.dbo.systargetservers) = 0)
  BEGIN
    RETURN(0)
  END

  -- Get local machine name
  EXECUTE @retval = master.dbo.xp_getnetname @local_machine_name OUTPUT
  IF (@retval <> 0) OR (@local_machine_name IS NULL)
  BEGIN
    RAISERROR(14225, -1, -1)
    RETURN(1)
  END

  -- Job-specific processing...
  IF (@object_type = 'JOB')
  BEGIN
    -- Validate the job (if supplied)
    IF (@job_id <> CONVERT(UNIQUEIDENTIFIER, 0x00))
    BEGIN
      SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id)

      -- Check if the job exists
      IF (NOT EXISTS (SELECT *
                      FROM msdb.dbo.sysjobs_view
                      WHERE (job_id = @job_id)))
      BEGIN
        RAISERROR(14262, -1, -1, '@job_id', @job_id_as_char)
        RETURN(1) -- Failure
      END

      -- If this is a local job then there's nothing for us to do
      IF (EXISTS (SELECT *
                  FROM msdb.dbo.sysjobservers
                  WHERE (job_id = @job_id)
                    AND (server_id = 0))) -- 0 means local server
      OR (NOT EXISTS (SELECT *
                      FROM msdb.dbo.sysjobservers
                      WHERE (job_id = @job_id)))
      BEGIN
        RETURN(0)
      END
    END

    -- Generate the sysdownloadlist row(s)...
    IF (@operation_code = 1) OR  -- Insert
       (@operation_code = 2) OR  -- Update
       (@operation_code = 3) OR  -- Delete
       (@operation_code = 4) OR  -- Start
       (@operation_code = 5)     -- Stop
    BEGIN
      IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) -- IE. 'ALL'
      BEGIN
        -- All jobs

        -- Handle DELETE as a special case (rather than posting 1 instruction per job we just
        -- post a single instruction that means 'delete all jobs from the MSX')
        IF (@operation_code = 3)
        BEGIN
          INSERT INTO msdb.dbo.sysdownloadlist
                (source_server,
                 operation_code,
                 object_type,
                 object_id,
                 target_server)
          SELECT @local_machine_name,
                 @operation_code,
                 1,                -- 1 means 'JOB'
                 CONVERT(UNIQUEIDENTIFIER, 0x00),
                 sts.server_name
          FROM systargetservers sts
          WHERE ((@specific_target_server_id IS NULL) OR (sts.server_id = @specific_target_server_id))
            AND ((SELECT COUNT(*)
                  FROM msdb.dbo.sysjobservers
                  WHERE (server_id = sts.server_id)) > 0)
          SELECT @instructions_posted = @@rowcount
        END
        ELSE
        BEGIN
          INSERT INTO msdb.dbo.sysdownloadlist
                (source_server,
                 operation_code,
                 object_type,
                 object_id,
                 target_server)
          SELECT @local_machine_name,
                 @operation_code,
                 1,                -- 1 means 'JOB'
                 sjv.job_id,
                 sts.server_name
          FROM sysjobs_view     sjv,
               sysjobservers    sjs,
               systargetservers sts
          WHERE (sjv.job_id = sjs.job_id)
            AND (sjs.server_id = sts.server_id)
            AND (sjs.server_id <> 0) -- We want to exclude local jobs
            AND ((@specific_target_server_id IS NULL) OR (sjs.server_id = @specific_target_server_id))
          SELECT @instructions_posted = @@rowcount
        END
      END
      ELSE
      BEGIN
        -- Specific job (ie. @job_id is not 0x00)
        INSERT INTO msdb.dbo.sysdownloadlist
              (source_server,
               operation_code,
               object_type,
               object_id,
               target_server,
               deleted_object_name)
        SELECT @local_machine_name,
               @operation_code,
               1,                -- 1 means 'JOB'
               sjv.job_id,
               sts.server_name,
               CASE @operation_code WHEN 3 -- Delete
                                      THEN sjv.name
                                      ELSE NULL
                                    END
        FROM sysjobs_view     sjv,
             sysjobservers    sjs,
             systargetservers sts
        WHERE (sjv.job_id = @job_id)
          AND (sjv.job_id = sjs.job_id)
          AND (sjs.server_id = sts.server_id)
          AND (sjs.server_id <> 0) -- We want to exclude local jobs
          AND ((@specific_target_server_id IS NULL) OR (sjs.server_id = @specific_target_server_id))
        SELECT @instructions_posted = @@rowcount
      END
    END
    ELSE
    BEGIN
      RAISERROR(14266, -1, -1, '@operation_code', 'INSERT, UPDATE, DELETE, START, STOP')
      RETURN(1) -- Failure
    END
  END
  
  
  -- SCHEDULE specific processing for INSERT, UPDATE or DELETE schedule operations
  -- All msx jobs that use the specified @schedule_uid will be notified with an Insert operation. 
  -- This will cause agent to reload all schedules for each job. 
  -- This is compatible with the legacy shiloh servers that don't know about reusable schedules
  IF (@object_type = 'SCHEDULE')
  BEGIN
    -- Validate the schedule
    -- Check if the schedule exists
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.sysschedules_localserver_view
                    WHERE (schedule_uid = @schedule_uid)))
    BEGIN
      SELECT @schedule_uid_as_char = CONVERT(VARCHAR(36), @schedule_uid)
      
      RAISERROR(14262, -1, -1, '@schedule_uid', @schedule_uid_as_char)
      RETURN(1) -- Failure
    END

    -- If this schedule is only used locally (no target servers) then there's nothing to do
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.sysschedules    s,
                        msdb.dbo.sysjobschedules  js,
                        msdb.dbo.sysjobs_view     sjv,
                        msdb.dbo.sysjobservers    sjs,
                        msdb.dbo.systargetservers sts
                    WHERE (s.schedule_uid = @schedule_uid)
                    AND (s.schedule_id = js.schedule_id)
                    AND (sjv.job_id = js.job_id)
                    AND (sjv.job_id = sjs.job_id)
                    AND (sjs.server_id = sts.server_id)
                    AND (sjs.server_id <> 0)))                        
    BEGIN
      RETURN(0)
    END

    -- Generate the sysdownloadlist row(s)...
    IF (@operation_code = 1) OR  -- Insert
       (@operation_code = 2) OR  -- Update
       (@operation_code = 3)     -- Delete
    BEGIN
      -- Insert specific schedule into sysdownloadlist 
      -- We need to create a sysdownloadlist JOB INSERT record for each job that runs the schedule
     INSERT INTO msdb.dbo.sysdownloadlist
         (source_server,
          operation_code,
          object_type,
          object_id,
          target_server)
     SELECT @local_machine_name,
          1,             -- 1 means 'Insert'
          1,             -- 1 means 'JOB'
          sjv.job_id,
          sts.server_name
     FROM msdb.dbo.sysschedules     s,
           msdb.dbo.sysjobschedules  js,
           msdb.dbo.sysjobs_view     sjv,
         msdb.dbo.sysjobservers    sjs,
         systargetservers          sts
     WHERE (s.schedule_id = js.schedule_id)
        AND (js.job_id = sjv.job_id)
        AND (sjv.job_id = sjs.job_id)
      AND (sjs.server_id = sts.server_id)
        AND (s.schedule_uid = @schedule_uid)
      AND (sjs.server_id <> 0)            -- We want to exclude local jobs
      AND ((@specific_target_server_id IS NULL) OR (sjs.server_id = @specific_target_server_id))

      SELECT @instructions_posted = @@rowcount


    END
    ELSE
    BEGIN
      RAISERROR(14266, -1, -1, '@operation_code', 'UPDATE, DELETE')
      RETURN(1) -- Failure
    END
  END
  

  -- Server-specific processing...
  IF (@object_type = 'SERVER')
  BEGIN
    -- Generate the sysdownloadlist row(s)...
    IF (@operation_code = 6) OR  -- ReEnlist
       (@operation_code = 7) OR  -- Defect
       (@operation_code = 8) OR  -- Synchronize time (with MSX)
       (@operation_code = 9)     -- Set MSX polling interval (in seconds)
    BEGIN
      IF (@operation_code = 8)
      BEGIN
        EXECUTE master.dbo.xp_regread N'HKEY_LOCAL_MACHINE',
                                      N'SYSTEM\CurrentControlSet\Control\TimeZoneInformation',
                                      N'Bias',
                                      @msx_time_zone_adjustment OUTPUT,
                                      N'no_output'
        SELECT @msx_time_zone_adjustment = -ISNULL(@msx_time_zone_adjustment, 0)
      END

      INSERT INTO msdb.dbo.sysdownloadlist
            (source_server,
             operation_code,
             object_type,
             object_id,
             target_server)
      SELECT @local_machine_name,
             @operation_code,
             2,                  -- 2 means 'SERVER'
             CASE @operation_code
               WHEN 8 THEN CONVERT(UNIQUEIDENTIFIER, CONVERT(BINARY(16), -(@msx_time_zone_adjustment - sts.time_zone_adjustment)))
               WHEN 9 THEN CONVERT(UNIQUEIDENTIFIER, CONVERT(BINARY(16), @value))
               ELSE CONVERT(UNIQUEIDENTIFIER, 0x00)
             END,
             sts.server_name
      FROM systargetservers sts
      WHERE ((@specific_target_server_id IS NULL) OR (sts.server_id = @specific_target_server_id))
      SELECT @instructions_posted = @@rowcount
    END
    ELSE
    BEGIN
      RAISERROR(14266, -1, -1, '@operation_code', 'RE-ENLIST, DEFECT, SYNC-TIME, SET-POLL')
      RETURN(1) -- Failure
    END
  END


  -- Report number of rows inserted
  IF (@object_type = 'JOB') AND
     (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) AND
     (@instructions_posted = 0) AND
     (@specific_target_server_id IS NOT NULL)
    RAISERROR(14231, 0, 1, '@specific_target_server', @specific_target_server)
  ELSE
    RAISERROR(14230, 0, 1, @instructions_posted, @operation)

  -- Delete any [downloaded] instructions that are over the registry-defined limit
  IF (@specific_target_server IS NOT NULL)
    EXECUTE msdb.dbo.sp_downloaded_row_limiter @specific_target_server

  RETURN(0) -- 0 means success
END
go

/**************************************************************/
/* SP_VERIFY_PERFORMANCE_CONDITION                            */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_performance_condition...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_performance_condition')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_performance_condition
go
CREATE PROCEDURE sp_verify_performance_condition
  @performance_condition NVARCHAR(512)
AS
BEGIN
  DECLARE @delimiter_count INT
  DECLARE @temp_str        NVARCHAR(512)
  DECLARE @object_name     sysname
  DECLARE @counter_name    sysname
  DECLARE @instance_name   sysname
  DECLARE @pos             INT

  SET NOCOUNT ON
  
  -- The performance condition must have the format 'object|counter|instance|comparator|value'
  -- NOTE: 'instance' may be empty.
  IF (PATINDEX(N'%_|%_|%|[><=]|[0-9]%', @performance_condition) = 0)
  BEGIN
    RAISERROR(14507, 16, 1)
    RETURN(1) -- Failure
  END

  -- Parse the performance_condition
  SELECT @delimiter_count = 0
  SELECT @temp_str = @performance_condition
  SELECT @pos = CHARINDEX(N'|', @temp_str)
  WHILE (@pos <> 0)
  BEGIN
    SELECT @delimiter_count = @delimiter_count + 1
    IF (@delimiter_count = 1) SELECT @object_name = SUBSTRING(@temp_str, 1, @pos - 1)
    IF (@delimiter_count = 2) SELECT @counter_name = SUBSTRING(@temp_str, 1, @pos - 1)
    IF (@delimiter_count = 3) SELECT @instance_name = SUBSTRING(@temp_str, 1, @pos - 1)
    SELECT @temp_str = SUBSTRING(@temp_str, @pos + 1, (DATALENGTH(@temp_str) / 2) - @pos)
    SELECT @pos = CHARINDEX(N'|', @temp_str)
  END
  IF (@delimiter_count <> 4)
  BEGIN
    RAISERROR(14507, 16, 1)
    RETURN(1) -- Failure
  END

  -- Check the object_name
  IF (NOT EXISTS (SELECT *
                  FROM dbo.sysalerts_performance_counters_view
                  WHERE (object_name = @object_name)))
  BEGIN
    RAISERROR(14262, 16, 1, 'object_name', @object_name)
    RETURN(1) -- Failure
  END

  -- Check the counter_name
  IF (NOT EXISTS (SELECT *
                  FROM dbo.sysalerts_performance_counters_view
                  WHERE (object_name = @object_name)
                    AND (counter_name = @counter_name)))
  BEGIN
    RAISERROR(14262, 16, 1, 'counter_name', @counter_name)
    RETURN(1) -- Failure
  END

  -- Check the instance_name
  IF (@instance_name IS NOT NULL)
  BEGIN
    IF (NOT EXISTS (SELECT *
                    FROM dbo.sysalerts_performance_counters_view
                    WHERE (object_name = @object_name)
                      AND (counter_name = @counter_name)
                      AND (instance_name = @instance_name)))
    BEGIN
      RAISERROR(14262, 16, 1, 'instance_name', @instance_name)
      RETURN(1) -- Failure
    END
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_VERIFY_JOB_DATE                                         */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_job_date...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_job_date')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_job_date
go
CREATE PROCEDURE sp_verify_job_date
  @date           INT,
  @date_name      VARCHAR(60) = 'date',
  @error_severity INT         = -1
AS
BEGIN
  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @date_name = LTRIM(RTRIM(@date_name))

  IF ((ISDATE(CONVERT(VARCHAR, @date)) = 0) OR (@date < 19900101) OR (@date > 99991231))
  BEGIN
    RAISERROR(14266, @error_severity, -1, @date_name, '19900101..99991231')
    RETURN(1) -- Failure
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_VERIFY_JOB_TIME                                         */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_job_time...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_job_time')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_job_time
go

CREATE PROCEDURE sp_verify_job_time
  @time           INT,
  @time_name      VARCHAR(60) = 'time',
  @error_severity INT = -1
AS
BEGIN
  DECLARE @hour      INT
  DECLARE @minute    INT
  DECLARE @second    INT
  DECLARE @part_name NVARCHAR(50)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @time_name = LTRIM(RTRIM(@time_name))

  IF ((@time < 0) OR (@time > 235959))
  BEGIN
    RAISERROR(14266, @error_severity, -1, @time_name, '000000..235959')
    RETURN(1) -- Failure
  END

  SELECT @hour   = (@time / 10000)
  SELECT @minute = (@time % 10000) / 100
  SELECT @second = (@time % 100)

  -- Check hour range
  IF (@hour > 23)
  BEGIN
    SELECT @part_name = FORMATMESSAGE(14218)
    RAISERROR(14287, @error_severity, -1, @time_name, @part_name)
    RETURN(1) -- Failure
  END

  -- Check minute range
  IF (@minute > 59)
  BEGIN
    SELECT @part_name = FORMATMESSAGE(14219)
    RAISERROR(14287, @error_severity, -1, @time_name, @part_name)
    RETURN(1) -- Failure
  END

  -- Check second range
  IF (@second > 59)
  BEGIN
    SELECT @part_name = FORMATMESSAGE(14220)
    RAISERROR(14287, @error_severity, -1, @time_name, @part_name)
    RETURN(1) -- Failure
  END

  RETURN(0) -- Success
END
go


/**************************************************************/
/* SP_VERIFY_ALERT                                            */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_alert...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_alert')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_alert
go
CREATE PROCEDURE sp_verify_alert
  @name                          sysname,
  @message_id                    INT,
  @severity                      INT,
  @enabled                       TINYINT,
  @delay_between_responses       INT,
  @notification_message          NVARCHAR(512),
  @include_event_description_in  TINYINT,
  @database_name                 sysname,
  @event_description_keyword     NVARCHAR(100),
  @job_id                        UNIQUEIDENTIFIER OUTPUT,
  @job_name                      sysname          OUTPUT,
  @occurrence_count              INT,
  @raise_snmp_trap               TINYINT,
  @performance_condition         NVARCHAR(512),
  @category_name                 sysname,
  @category_id                   INT              OUTPUT,
  @count_reset_date              INT,
  @count_reset_time              INT,
  @wmi_namespace      NVARCHAR(512),      -- New for 9.0
  @wmi_query          NVARCHAR(512),      -- New for 9.0
  @event_id        INT     OUTPUT   -- New for 9.0
AS
BEGIN
  DECLARE @retval               INT
  DECLARE @non_alertable_errors VARCHAR(512)
  DECLARE @message_id_as_string VARCHAR(10)
  DECLARE @res_valid_range      NVARCHAR(100)
  DECLARE @alert_no_wmi_check   INT
  DECLARE @job_owner_sid      VARBINARY(85)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name                      = LTRIM(RTRIM(@name))
  SELECT @notification_message      = LTRIM(RTRIM(@notification_message))
  SELECT @database_name             = LTRIM(RTRIM(@database_name))
  SELECT @event_description_keyword = LTRIM(RTRIM(@event_description_keyword))
  SELECT @job_name                  = LTRIM(RTRIM(@job_name))
  SELECT @performance_condition     = LTRIM(RTRIM(@performance_condition))
  SELECT @category_name             = LTRIM(RTRIM(@category_name))
  SELECT @alert_no_wmi_check        = 0
  
  -- Only a sysadmin can do this
  
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Check if the NewName is unique
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysalerts
              WHERE (name = @name)))
  BEGIN
    RAISERROR(14261, 16, 1, '@name', @name)
    RETURN(1) -- Failure
  END

  -- Check if the user has supplied MessageID OR Severity OR Performance-Condition OR WMI namespace/query
  IF ((@performance_condition IS NULL) AND (@message_id = 0) AND (@severity = 0) AND ((@wmi_namespace IS NULL) OR (@wmi_query IS NULL))) OR
     ((@performance_condition IS NOT NULL) AND ((@message_id <> 0) OR (@severity <> 0) OR (@wmi_namespace IS NOT NULL) OR (@wmi_query IS NOT NULL))) OR
     ((@message_id <> 0) AND ((@performance_condition IS NOT NULL) OR (@severity <> 0) OR (@wmi_namespace IS NOT NULL) OR (@wmi_query IS NOT NULL))) OR
     ((@severity <> 0) AND ((@performance_condition IS NOT NULL) OR (@message_id <> 0) OR (@wmi_namespace IS NOT NULL) OR (@wmi_query IS NOT NULL)))
  BEGIN
    RAISERROR(14500, 16, 1)
    RETURN(1) -- Failure
  END

  -- Check the Severity
  IF ((@severity < 0) OR (@severity > 25))
  BEGIN
    RAISERROR(14266, 16, 1, '@severity', '0..25')
    RETURN(1) -- Failure
  END

  -- Check the MessageID
  IF (@message_id <> 0) AND
     (NOT EXISTS (SELECT error
                  FROM master.dbo.sysmessages
                  WHERE (error = @message_id)))
  BEGIN
    SELECT @message_id_as_string = CONVERT(VARCHAR, @message_id)
    RAISERROR(14262, 16, 1, '@message_id', @message_id_as_string)
    RETURN(1) -- Failure
  END

  -- Check if it is legal to set an alert on this MessageID
  DECLARE @TempRetVal TABLE (RetVal INT)
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'NonAlertableErrors',
                                         @non_alertable_errors OUTPUT,
                                         N'no_output'
  IF (ISNULL(@non_alertable_errors, N'NULL') <> N'NULL')
  BEGIN
    DECLARE @message_id_as_char VARCHAR(10)

    SELECT @message_id_as_char = CONVERT(VARCHAR(10), @message_id)
    INSERT INTO @TempRetVal
    EXECUTE ('IF (' + @message_id_as_char + ' IN (' + @non_alertable_errors + ')) SELECT 1')
  END

  IF (EXISTS (SELECT *
              FROM @TempRetVal))
  BEGIN
    RAISERROR(14506, 16, 1, @message_id)
    RETURN(1) -- Failure
  END

  -- Enabled must be 0 or 1
  IF (@enabled NOT IN (0, 1))
  BEGIN
    RAISERROR(14266, 16, 1, '@enabled', '0, 1')
    RETURN(1) -- Failure
  END

  -- DelayBetweenResponses must be > 0
  IF (@delay_between_responses < 0)
  BEGIN
    SELECT @res_valid_range = FORMATMESSAGE(14206)
    RAISERROR(14266, 16, 1, '@delay_between_responses', @res_valid_range)
    RETURN(1) -- Failure
  END

  -- NOTE: We don't check the notification message

  -- Check IncludeEventDescriptionIn
  IF ((@include_event_description_in < 0) OR (@include_event_description_in > 7))
  BEGIN
    SELECT @res_valid_range = FORMATMESSAGE(14208)
    RAISERROR(14266, 16, 1, '@include_event_description_in', @res_valid_range)
    RETURN(1) -- Failure
  END

  -- Check the database name
  IF (@database_name IS NOT NULL) AND (DB_ID(@database_name) IS NULL)
  BEGIN
    RAISERROR(15010, 16, 1, @database_name)
    RETURN(1) -- Failure
  END

  -- NOTE: We don't check the event description keyword

  -- Check JobName/ID
  IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL))
  BEGIN
    -- We use '' as a special value which means 'no job' (we cannot use NULL since this forces
    -- sp_update_alert to use the existing value)
    IF (@job_name = N'')
      SELECT @job_id = 0x00
    ELSE
    BEGIN
      EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                                  '@job_id',
                                                   @job_name OUTPUT,
                                                   @job_id   OUTPUT,
                                       @owner_sid = @job_owner_sid OUTPUT
      IF (@retval <> 0)
        RETURN(1) -- Failure
        
     -- Check permissions beyond what's checked by the sysjobs_view
     -- SQLAgentReaderRole and SQLAgentOperatorRole can see all jobs but
     -- cannot modify them
     IF (@job_owner_sid <> SUSER_SID()                   -- does not own the job
        AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))   -- is not sysadmin
     BEGIN
       RAISERROR(14525, -1, -1); 
       RETURN(1) -- Failure
     END
        
      -- Check that the job is a local job
      IF (NOT EXISTS (SELECT *
                      FROM msdb.dbo.sysjobservers
                      WHERE (job_id = @job_id)
                        AND (server_id = 0)))
      BEGIN
        RAISERROR(14527, -1, -1, @job_name)
        RETURN(1) -- Failure
      END
    END
  END

  -- OccurrenceCount must be > 0
  IF (@occurrence_count < 0)
  BEGIN
    RAISERROR(14266, 16, 1, '@occurrence_count', '0..n')
    RETURN(1) -- Failure
  END

  -- RaiseSNMPTrap must be 0 or 1
  IF (@raise_snmp_trap NOT IN (0, 1))
  BEGIN
    RAISERROR(14266, 16, 1, '@raise_snmp_trap', '0, 1')
    RETURN(1) -- Failure
  END

  -- Check the performance condition (including invalid parameter combinations)
  IF (@performance_condition IS NOT NULL)
  BEGIN
    IF (@database_name IS NOT NULL)
    BEGIN
      RAISERROR(14505, 16, 1, '@database_name')
      RETURN(1) -- Failure
    END

    IF (@event_description_keyword IS NOT NULL)
    BEGIN
      RAISERROR(14505, 16, 1, '@event_description_keyword')
      RETURN(1) -- Failure
    END
    
    IF (@wmi_namespace IS NOT NULL)
    BEGIN
      RAISERROR(14505, 16, 1, '@wmi_namespace')
      RETURN(1) -- Failure
    END

    IF (@wmi_query IS NOT NULL)
    BEGIN
      RAISERROR(14505, 16, 1, '@wmi_query')
      RETURN(1) -- Failure
    END

    -- Verify the performance condition
    EXECUTE @retval = msdb.dbo.sp_verify_performance_condition @performance_condition
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END

  -- Check category name
  IF (@category_name = N'[DEFAULT]')
    SELECT @category_id = 98
  ELSE
  BEGIN
    SELECT @category_id = category_id
    FROM msdb.dbo.syscategories
    WHERE (category_class = 2) -- Alerts
      AND (category_type = 3) -- None
      AND (name = @category_name)
  END
  IF (@category_id IS NULL)
  BEGIN
    RAISERROR(14262, -1, -1, '@category_name', @category_name)
    RETURN(1) -- Failure
  END

  -- Check count reset date
  IF (@count_reset_date <> 0)
  BEGIN
    EXECUTE @retval = msdb.dbo.sp_verify_job_date @count_reset_date, '@count_reset_date'
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END

  -- Check count reset time
  IF (@count_reset_time <> 0)
  BEGIN
    EXECUTE @retval = msdb.dbo.sp_verify_job_time @count_reset_time, '@count_reset_time'
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END

  -- Check WMI parameters. Both must exist
  IF (@wmi_namespace IS NOT NULL)
  BEGIN
    IF (@wmi_query IS NULL)
   BEGIN
      RAISERROR(14509, 16, 1, '@wmi_query') 
     RETURN(1) -- Failure
   END
   
    IF (@database_name IS NOT NULL)
    BEGIN
      RAISERROR(14510, 16, 1, '@database_name') 
      RETURN(1) -- Failure
    END

    IF (@event_description_keyword IS NOT NULL)
    BEGIN
      RAISERROR(14510, 16, 1, '@event_description_keyword')
      RETURN(1) -- Failure
    END

    --do not check WMI properties if a registry setting is present
    EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                           N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                           N'AlertNoWmiCheck',
                                           @alert_no_wmi_check OUTPUT,
                                           'no_output'
    if (@alert_no_wmi_check <> 1)
    BEGIN
      EXECUTE @retval = msdb.dbo.sp_sqlagent_notify @op_type = N'T',
                    @wmi_namespace = @wmi_namespace,
               @wmi_query  = @wmi_query,
               @error_flag = 0
      IF (@retval <> 0)
     BEGIN
       RAISERROR(14511, 16, 1)
         RETURN(1) -- Failure
     END
    END

   -- Set event_id to indicate WMI alert   
    SELECT @event_id = 8
  END
  ELSE IF (@wmi_query IS NOT NULL)
  BEGIN
    RAISERROR(14512, 16, 1, '@wmi_namespace')
    RETURN(1) -- Failure
  END
  
  RETURN(0) -- Success
END
go


/**************************************************************/
/* SP_UPDATE_ALERT                                            */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_update_alert...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_update_alert')
              AND (type = 'P')))
  DROP PROCEDURE sp_update_alert
go
CREATE PROCEDURE sp_update_alert
  @name                         sysname,
  @new_name                     sysname          = NULL,
  @enabled                      TINYINT          = NULL,
  @message_id                   INT              = NULL,
  @severity                     INT              = NULL,
  @delay_between_responses      INT              = NULL,
  @notification_message         NVARCHAR(512)    = NULL,
  @include_event_description_in TINYINT          = NULL, -- 0 = None, 1 = Email, 2 = Pager. 4 = NetSend, 7 = All
  @database_name                sysname          = NULL,
  @event_description_keyword    NVARCHAR(100)    = NULL,
  @job_id                       UNIQUEIDENTIFIER = NULL, -- If provided must NOT also provide job_name
  @job_name                     sysname          = NULL, -- If provided must NOT also provide job_id
  @occurrence_count             INT              = NULL, -- Can only be set to 0
  @count_reset_date             INT              = NULL,
  @count_reset_time             INT              = NULL,
  @last_occurrence_date         INT              = NULL, -- Can only be set to 0
  @last_occurrence_time         INT              = NULL, -- Can only be set to 0
  @last_response_date           INT              = NULL, -- Can only be set to 0
  @last_response_time           INT              = NULL, -- Can only be set to 0
  @raise_snmp_trap              TINYINT          = NULL,
  @performance_condition        NVARCHAR(512)    = NULL, -- New for 7.0
  @category_name                sysname          = NULL, -- New for 7.0
  @wmi_namespace           sysname         = NULL, -- New for 9.0
  @wmi_query               NVARCHAR(512)   = NULL  -- New for 9.0
AS
BEGIN
  DECLARE @x_enabled                   TINYINT
  DECLARE @x_message_id                INT
  DECLARE @x_severity                  INT
  DECLARE @x_delay_between_responses   INT
  DECLARE @x_notification_message      NVARCHAR(512)
  DECLARE @x_include_event_description TINYINT
  DECLARE @x_database_name             sysname
  DECLARE @x_event_description_keyword NVARCHAR(100)
  DECLARE @x_occurrence_count          INT
  DECLARE @x_count_reset_date          INT
  DECLARE @x_count_reset_time          INT
  DECLARE @x_last_occurrence_date      INT
  DECLARE @x_last_occurrence_time      INT
  DECLARE @x_last_response_date        INT
  DECLARE @x_last_response_time        INT
  DECLARE @x_flags                     INT
  DECLARE @x_performance_condition     NVARCHAR(512)
  DECLARE @x_job_id                    UNIQUEIDENTIFIER
  DECLARE @x_category_id               INT
  DECLARE @x_event_id                  INT
  DECLARE @x_wmi_namespace          sysname
  DECLARE @x_wmi_query              NVARCHAR(512)

  DECLARE @include_event_desc_code     TINYINT
  DECLARE @return_code                 INT
  DECLARE @duplicate_name              sysname
  DECLARE @category_id                 INT
  DECLARE @alert_id                    INT
  DECLARE @cached_attribute_modified   INT
  DECLARE @event_id                 INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @new_name                  = LTRIM(RTRIM(@new_name))
  SELECT @job_name                  = LTRIM(RTRIM(@job_name))
  SELECT @notification_message      = LTRIM(RTRIM(@notification_message))
  SELECT @database_name             = LTRIM(RTRIM(@database_name))
  SELECT @event_description_keyword = LTRIM(RTRIM(@event_description_keyword))
  SELECT @performance_condition     = LTRIM(RTRIM(@performance_condition))
  SELECT @category_name             = LTRIM(RTRIM(@category_name))

  -- Are we modifying an attribute which SQLServerAgent caches?
  IF ((@new_name                     IS NOT NULL) OR
      (@enabled                      IS NOT NULL) OR
      (@message_id                   IS NOT NULL) OR
      (@severity                     IS NOT NULL) OR
      (@delay_between_responses      IS NOT NULL) OR
      (@notification_message         IS NOT NULL) OR
      (@include_event_description_in IS NOT NULL) OR
      (@database_name                IS NOT NULL) OR
      (@event_description_keyword    IS NOT NULL) OR
      (@job_id                       IS NOT NULL) OR
      (@job_name                     IS NOT NULL) OR
      (@last_response_date           IS NOT NULL) OR
      (@last_response_time           IS NOT NULL) OR
      (@raise_snmp_trap              IS NOT NULL) OR
      (@performance_condition        IS NOT NULL) OR
      (@wmi_namespace             IS NOT NULL) OR
      (@wmi_query              IS NOT NULL))  
    SELECT @cached_attribute_modified = 1
  ELSE
    SELECT @cached_attribute_modified = 0

  -- Map a job_id of 0 to the real value we use to mean 'no job'
  IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) AND (@job_name IS NULL)
    SELECT @job_name = N''

  -- Only a sysadmin can do this
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1)
  END

  -- Check if SQLServerAgent is in the process of starting
  EXECUTE @return_code = msdb.dbo.sp_is_sqlagent_starting
  IF (@return_code <> 0)
    RETURN(1) -- Failure

  -- Check if this Alert exists
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysalerts
                  WHERE (name = @name)))
  BEGIN
    RAISERROR(14262, 16, 1, '@name', @name)
    RETURN(1)
  END

  -- Certain values (if supplied) may only be updated to 0
  IF (@occurrence_count <> 0)
  BEGIN
    RAISERROR(14266, -1, -1, '@occurrence_count', '0')
    RETURN(1) -- Failure
  END
  IF (@last_occurrence_date <> 0)
  BEGIN
    RAISERROR(14266, -1, -1, '@last_occurrence_date', '0')
    RETURN(1) -- Failure
  END
  IF (@last_occurrence_time <> 0)
  BEGIN
    RAISERROR(14266, -1, -1, '@last_occurrence_time', '0')
    RETURN(1) -- Failure
  END
  IF (@last_response_date <> 0)
  BEGIN
    RAISERROR(14266, -1, -1, '@last_response_date', '0')
    RETURN(1) -- Failure
  END
  IF (@last_response_time <> 0)
  BEGIN
    RAISERROR(14266, -1, -1, '@last_response_time', '0')
    RETURN(1) -- Failure
  END

  -- Get existing (@x_) values
  SELECT @alert_id                    = id,
         @x_enabled                   = enabled,
         @x_message_id                = message_id,
         @x_severity                  = severity,
         @x_delay_between_responses   = delay_between_responses,
         @x_notification_message      = notification_message,
         @x_include_event_description = include_event_description,
         @x_database_name             = database_name,
         @x_event_description_keyword = event_description_keyword,
         @x_occurrence_count          = occurrence_count,
         @x_count_reset_date          = count_reset_date,
         @x_count_reset_time          = count_reset_time,
         @x_job_id                    = job_id,
         @x_last_occurrence_date      = last_occurrence_date,
         @x_last_occurrence_time      = last_occurrence_time,
         @x_last_response_date        = last_response_date,
         @x_last_response_time        = last_response_time,
         @x_flags                     = flags,
         @x_performance_condition     = performance_condition,
         @x_category_id               = category_id,
       @x_event_id              = event_id
  FROM msdb.dbo.sysalerts
  WHERE (name = @name)
  
  SELECT @x_job_id = sjv.job_id
  FROM msdb.dbo.sysalerts    sa,
       msdb.dbo.sysjobs_view sjv
  WHERE (sa.job_id = sjv.job_id)
    AND (sa.name = @name)

  -- Fill out the values for all non-supplied parameters from the existsing values
  IF (@x_event_id = 8)
  BEGIN
   -- WMI alert type
   IF (@wmi_namespace IS NULL) SELECT @wmi_namespace = @x_database_name
   IF (@wmi_query IS NULL) SELECT @wmi_query = @x_performance_condition
  END
  ELSE
  BEGIN
   -- Non-WMI alert type
   IF (@database_name IS NULL) SELECT @database_name = @x_database_name
   IF (@performance_condition IS NULL) SELECT @performance_condition = @x_performance_condition
  END
   
  IF (@enabled                      IS NULL) SELECT @enabled                      = @x_enabled
  IF (@message_id                   IS NULL) SELECT @message_id                   = @x_message_id
  IF (@severity                     IS NULL) SELECT @severity                     = @x_severity
  IF (@delay_between_responses      IS NULL) SELECT @delay_between_responses      = @x_delay_between_responses
  IF (@notification_message         IS NULL) SELECT @notification_message         = @x_notification_message
  IF (@include_event_description_in IS NULL) SELECT @include_event_description_in = @x_include_event_description
  IF (@event_description_keyword    IS NULL) SELECT @event_description_keyword    = @x_event_description_keyword
  IF (@job_id IS NULL) AND (@job_name IS NULL) SELECT @job_id                     = @x_job_id
  IF (@occurrence_count             IS NULL) SELECT @occurrence_count             = @x_occurrence_count
  IF (@count_reset_date             IS NULL) SELECT @count_reset_date             = @x_count_reset_date
  IF (@count_reset_time             IS NULL) SELECT @count_reset_time             = @x_count_reset_time
  IF (@last_occurrence_date         IS NULL) SELECT @last_occurrence_date         = @x_last_occurrence_date
  IF (@last_occurrence_time         IS NULL) SELECT @last_occurrence_time         = @x_last_occurrence_time
  IF (@last_response_date           IS NULL) SELECT @last_response_date           = @x_last_response_date
  IF (@last_response_time           IS NULL) SELECT @last_response_time           = @x_last_response_time
  IF (@raise_snmp_trap              IS NULL) SELECT @raise_snmp_trap              = @x_flags & 0x1
  IF (@category_name                IS NULL) SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = @x_category_id)

  IF (@category_name IS NULL)
  BEGIN
    SELECT @category_name = name
    FROM msdb.dbo.syscategories
    WHERE (category_id = 98)
  END

  -- Turn [nullable] empty string parameters into NULLs
  IF (@new_name                  = N'') SELECT @new_name                  = NULL
  IF (@notification_message      = N'') SELECT @notification_message      = NULL
  IF (@database_name             = N'') SELECT @database_name             = NULL
  IF (@event_description_keyword = N'') SELECT @event_description_keyword = NULL
  IF (@performance_condition     = N'') SELECT @performance_condition     = NULL
  IF (@wmi_namespace        = N'') SELECT @wmi_namespace         = NULL
  IF (@wmi_query            = N'') SELECT @wmi_query             = NULL

  -- Verify the Alert
  IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00))
    SELECT @job_id = NULL
  EXECUTE @return_code = sp_verify_alert @new_name,
                                         @message_id,
                                         @severity,
                                         @enabled,
                                         @delay_between_responses,
                                         @notification_message,
                                         @include_event_description_in,
                                         @database_name,
                                         @event_description_keyword,
                                         @job_id OUTPUT,
                                         @job_name OUTPUT,
                                         @occurrence_count,
                                         @raise_snmp_trap,
                                         @performance_condition,
                                         @category_name,
                                         @category_id OUTPUT,
                                         @count_reset_date,
                                         @count_reset_time,
                                         @wmi_namespace,
                                         @wmi_query,
                                     @event_id OUTPUT
  IF (@return_code <> 0)
    RETURN(1) -- Failure

  -- If the user didn't supply a NewName, use the old one.
  -- NOTE: This must be done AFTER sp_verify_alert.
  IF (@new_name IS NULL)
    SELECT @new_name = @name

  -- Turn the 1st 'flags' bit on or off accordingly
  IF (@raise_snmp_trap = 0)
    SELECT @x_flags = @x_flags & 0xFFFE
  ELSE
    SELECT @x_flags = @x_flags | 0x0001

  -- For WMI alerts replace 
  -- database_name with wmi_namespace and 
  -- performance_conditon with wmi_query
  -- so we can store them in those columns in sysalerts table
  IF (@event_id = 8)
  BEGIN
   SELECT @database_name = @wmi_namespace
   SELECT @performance_condition = @wmi_query
  END

  -- Check if this Alert already exists
  SELECT @duplicate_name = FORMATMESSAGE(14205)
  SELECT @duplicate_name = name
  FROM msdb.dbo.sysalerts
  WHERE ((event_id = 8) AND 
       (ISNULL(performance_condition, N'') = ISNULL(@performance_condition, N'')) AND
       (ISNULL(database_name, N'') = ISNULL(@database_name, N''))) OR
      ((ISNULL(event_id,1) <> 8) AND 
       (ISNULL(performance_condition, N'apples') = ISNULL(@performance_condition, N'oranges'))) OR 
      ((performance_condition IS NULL) AND
         (message_id = @message_id) AND
         (severity = @severity) AND
         (ISNULL(database_name, N'') = ISNULL(@database_name, N'')) AND
         (ISNULL(event_description_keyword, N'') = ISNULL(@event_description_keyword, N'')))
  IF (@duplicate_name <> FORMATMESSAGE(14205) AND @duplicate_name <> @name)
  BEGIN
    RAISERROR(14501, 16, 1, @duplicate_name)
    RETURN(1) -- Failure
  END

  -- Finally, do the actual UPDATE
  UPDATE msdb.dbo.sysalerts
  SET name                        = @new_name,
      message_id                  = @message_id,
      severity                    = @severity,
      enabled                     = @enabled,
      delay_between_responses     = @delay_between_responses,
      notification_message        = @notification_message,
      include_event_description   = @include_event_description_in,
      database_name               = @database_name,
      event_description_keyword   = @event_description_keyword,
      job_id                      = ISNULL(@job_id, CONVERT(UNIQUEIDENTIFIER, 0x00)),
      occurrence_count            = @occurrence_count,
      count_reset_date            = @count_reset_date,
      count_reset_time            = @count_reset_time,
      last_occurrence_date        = @last_occurrence_date,
      last_occurrence_time        = @last_occurrence_time,
      last_response_date          = @last_response_date,
      last_response_time          = @last_response_time,
      flags                       = @x_flags,
      performance_condition       = @performance_condition,
      category_id                 = @category_id,
      event_id               = @event_id
  WHERE (name = @name)

  -- Notify SQLServerAgent of the change
  IF (@cached_attribute_modified = 1)
    EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'A',
                                        @alert_id    = @alert_id,
                                        @action_type = N'U'
  RETURN(0) -- Success
END
go


/**************************************************************/
/* SP_DELETE_JOB_REFERENCES                                   */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_job_references...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_job_references')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_job_references
go
CREATE PROCEDURE sp_delete_job_references
  @notify_sqlagent BIT = 1
AS
BEGIN
  DECLARE @deleted_job_id  UNIQUEIDENTIFIER
  DECLARE @task_id_as_char VARCHAR(10)
  DECLARE @job_is_cached   INT
  DECLARE @alert_name      sysname
  DECLARE @maintplan_plan_id  UNIQUEIDENTIFIER
  DECLARE @maintplan_subplan_id  UNIQUEIDENTIFIER

  -- Keep SQLServerAgent's cache in-sync and cleanup any 'webtask' cross-references to the deleted job(s)
  -- NOTE: The caller must have created a table called #temp_jobs_to_delete of the format
  --       (job_id UNIQUEIDENTIFIER NOT NULL, job_is_cached INT NOT NULL).

  DECLARE sqlagent_notify CURSOR LOCAL
  FOR
  SELECT job_id, job_is_cached
  FROM #temp_jobs_to_delete

  OPEN sqlagent_notify
  FETCH NEXT FROM sqlagent_notify INTO @deleted_job_id, @job_is_cached

  WHILE (@@fetch_status = 0)
  BEGIN
    -- NOTE: We only notify SQLServerAgent if we know the job has been cached
    IF(@job_is_cached = 1 AND @notify_sqlagent = 1)
      EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'J',
                                          @job_id      = @deleted_job_id,
                                          @action_type = N'D'

    IF (EXISTS (SELECT *
                FROM master.dbo.sysobjects
                WHERE (name = N'sp_cleanupwebtask')
                  AND (type = 'P')))
    BEGIN
      SELECT @task_id_as_char = CONVERT(VARCHAR(10), task_id)
      FROM msdb.dbo.systaskids
      WHERE (job_id = @deleted_job_id)
      IF (@task_id_as_char IS NOT NULL)
        EXECUTE ('master.dbo.sp_cleanupwebtask @taskid = ' + @task_id_as_char)
    END

    -- Maintenance plan cleanup for SQL 2005.
    -- If this job came from another server and it runs a subplan of a
    -- maintenance plan, then delete the subplan record. If that was
    -- the last subplan still referencing that plan, delete the plan.
    -- This removes a distributed maintenance plan from a target server
    -- once all of jobs from the master server that used that maintenance
    -- plan are deleted.
    SELECT @maintplan_plan_id = plans.plan_id, @maintplan_subplan_id = plans.subplan_id
    FROM sysmaintplan_subplans plans, sysjobs_view sjv
    WHERE plans.job_id = @deleted_job_id
      AND plans.job_id = sjv.job_id
      AND sjv.master_server = 1 -- This means the job came from the master

    IF (@maintplan_subplan_id is not NULL)
    BEGIN
      EXECUTE sp_maintplan_delete_subplan @subplan_id = @maintplan_subplan_id, @delete_jobs = 0
      IF (NOT EXISTS (SELECT *
                      FROM sysmaintplan_subplans
                      where plan_id = @maintplan_plan_id))
      BEGIN
        DECLARE @plan_name sysname

        SELECT @plan_name = name
          FROM sysmaintplan_plans
          WHERE id = @maintplan_plan_id

        EXECUTE sp_ssis_deletepackage @name = @plan_name, @folderid = '08aa12d5-8f98-4dab-a4fc-980b150a5dc8' -- this is the guid for 'Maintenance Plans'
      END
    END

    FETCH NEXT FROM sqlagent_notify INTO @deleted_job_id, @job_is_cached
  END
  DEALLOCATE sqlagent_notify

  -- Remove systaskid references (must do this AFTER sp_cleanupwebtask stuff)
  DELETE FROM msdb.dbo.systaskids
  WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete)

  -- Remove sysdbmaintplan_jobs references (legacy maintenance plans prior to SQL 2005)
  DELETE FROM msdb.dbo.sysdbmaintplan_jobs
  WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete)

  -- Finally, clean up any dangling references in sysalerts to the deleted job(s)
  DECLARE sysalerts_cleanup CURSOR LOCAL
  FOR
  SELECT name
  FROM msdb.dbo.sysalerts
  WHERE (job_id IN (SELECT job_id FROM #temp_jobs_to_delete))

  OPEN sysalerts_cleanup
  FETCH NEXT FROM sysalerts_cleanup INTO @alert_name
  WHILE (@@fetch_status = 0)
  BEGIN
    EXECUTE msdb.dbo.sp_update_alert @name   = @alert_name,
                                     @job_id = 0x00
    FETCH NEXT FROM sysalerts_cleanup INTO @alert_name
  END
  DEALLOCATE sysalerts_cleanup
END
go

/**************************************************************/
/* SP_DELETE_ALL_MSX_JOBS                                     */
/*                                                            */
/* NOTE: This is a separate procedure because SQLServerAgent  */
/*       needs to call it, as does sp_msx_defect and          */
/*       sp_delete_job.                                       */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_all_msx_jobs...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_all_msx_jobs')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_all_msx_jobs
go
CREATE PROCEDURE sp_delete_all_msx_jobs
  @msx_server   sysname,
  @jobs_deleted INT = NULL OUTPUT
AS
BEGIN
  SET NOCOUNT ON

  -- Change server name to always reflect real servername or servername\instancename
  IF (UPPER(@msx_server collate SQL_Latin1_General_CP1_CS_AS) = '(LOCAL)')
    SELECT @msx_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))

  -- Delete all the jobs that originated from the MSX
  -- Note: This temp table is referenced by msdb.dbo.sp_delete_job_references
  CREATE TABLE #temp_jobs_to_delete (job_id UNIQUEIDENTIFIER NOT NULL, job_is_cached INT NOT NULL, owner_sid VARBINARY(85) NOT NULL)

  -- Table of msx schedules to delete
  DECLARE @temp_schedules_to_delete TABLE (schedule_id INT NOT NULL)  

  -- Non-sysadmins can only delete jobs they own. sysjobs_view returns all jobs
  -- for members of SQLAgentReaderRole and SQLAgentOperatorRole, but they should
  -- not be able to delete those jobs
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1))
  BEGIN
   -- NOTE: The left outer-join here is to handle the [unlikely] case of missing sysjobservers rows
   INSERT INTO #temp_jobs_to_delete
   SELECT sjv.job_id, 
         CASE sjs.server_id WHEN 0 THEN 1 ELSE 0 END,
         sjv.owner_sid
   FROM msdb.dbo.sysjobs_view sjv
      LEFT OUTER JOIN msdb.dbo.sysjobservers sjs ON (sjv.job_id = sjs.job_id)
   WHERE (ISNULL(sjs.server_id, 0) = 0)
      AND (sjv.originating_server = @msx_server)
  END
  ELSE
  BEGIN
   -- NOTE: The left outer-join here is to handle the [unlikely] case of missing sysjobservers rows
   INSERT INTO #temp_jobs_to_delete
   SELECT sjv.job_id, 
         CASE sjs.server_id WHEN 0 THEN 1 ELSE 0 END,
         sjv.owner_sid
   FROM msdb.dbo.sysjobs_view sjv
      LEFT OUTER JOIN msdb.dbo.sysjobservers sjs ON (sjv.job_id = sjs.job_id)
   WHERE (ISNULL(sjs.server_id, 0) = 0)
      AND (sjv.originating_server = @msx_server)
      AND (sjv.owner_sid = SUSER_SID())
  END

  -- Must do this before deleting the job itself since sp_sqlagent_notify does a lookup on sysjobs_view
  EXECUTE msdb.dbo.sp_delete_job_references

  BEGIN TRANSACTION

    --Get the list of schedules to delete, these cant be deleted until the references are deleted in sysjobschedules
    INSERT INTO @temp_schedules_to_delete
    SELECT DISTINCT schedule_id 
    FROM   msdb.dbo.sysschedules
    WHERE (schedule_id IN 
            (SELECT schedule_id
            FROM msdb.dbo.sysjobschedules as js
           JOIN #temp_jobs_to_delete as tjd ON (js.job_id = tjd.job_id)))

    DELETE FROM msdb.dbo.sysjobschedules
    WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete)

    --Now OK to delete the schedule
    DELETE FROM msdb.dbo.sysschedules
    WHERE schedule_id IN 
    (SELECT schedule_id
        FROM @temp_schedules_to_delete)
    
    DELETE FROM msdb.dbo.sysjobservers
    WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete)
    
    DELETE FROM msdb.dbo.sysjobsteps
    WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete)

    DELETE FROM msdb.dbo.sysjobs
    WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete)

    DELETE FROM msdb.dbo.sysjobhistory
    WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete)

   --Finally cleanup any orphaned sysschedules that were downloaded from the MSX
   DELETE msdb.dbo.sysschedules
   FROM msdb.dbo.sysschedules s
      JOIN msdb.dbo.sysoriginatingservers_view os ON (s.originating_server_id = os.originating_server_id)
   WHERE (os.originating_server = @msx_server)

  COMMIT TRANSACTION

  SELECT @jobs_deleted = COUNT(*)
  FROM #temp_jobs_to_delete

  DROP TABLE #temp_jobs_to_delete
END
go


/**************************************************************/
/* SP_GENERATE_TARGET_SERVER_JOB_ASSIGNMENT_SQL               */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_generate_target_server_job_assignment_sql...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_generate_target_server_job_assignment_sql')
              AND (type = 'P')))
  DROP PROCEDURE sp_generate_target_server_job_assignment_sql
go
CREATE PROCEDURE sp_generate_target_server_job_assignment_sql
  @server_name     sysname = NULL, 
  @new_server_name sysname = NULL  -- Use this if the target server computer has been renamed
AS
BEGIN
  SET NOCOUNT ON

  -- Change server name to always reflect real servername or servername\instancename
  IF (@server_name IS NULL) OR (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) = '(LOCAL)')
    SELECT @server_name = CONVERT(sysname, SERVERPROPERTY('ServerName'))
  
  IF (@server_name IS NOT NULL) 
    SELECT @server_name = UPPER(@server_name)

  -- Verify the server name
  IF (@server_name <> UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) AND
     (NOT EXISTS (SELECT *
                  FROM msdb.dbo.systargetservers
                  WHERE (UPPER(server_name) = @server_name)))
  BEGIN
    RAISERROR(14262, 16, 1, '@server_name', @server_name)
    RETURN(1) -- Failure
  END

  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers    sjs,
                   msdb.dbo.systargetservers sts
              WHERE (sjs.server_id = sts.server_id)
                AND (UPPER(sts.server_name) = @server_name)))
  BEGIN
    -- Generate the SQL
    SELECT 'Execute this SQL to re-assign jobs to the target server' =
           'EXECUTE msdb.dbo.sp_add_jobserver @job_id = ''' + CONVERT(VARCHAR(36), sjs.job_id) +
           ''', @server_name = ''' +  ISNULL(@new_server_name, sts.server_name) + ''''
    FROM msdb.dbo.sysjobservers    sjs,
         msdb.dbo.systargetservers sts
    WHERE (sjs.server_id = sts.server_id)
      AND (UPPER(sts.server_name) = @server_name)
  END
  ELSE
    RAISERROR(14548, 10, 1, @server_name)

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_GENERATE_SERVER_DESCRIPTION                             */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_generate_server_description...'
go

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_generate_server_description')
              AND (type = 'P')))
  DROP PROCEDURE sp_generate_server_description
go
CREATE PROCEDURE sp_generate_server_description
  @description NVARCHAR(100) = NULL OUTPUT,
  @result_set  BIT = 0
AS
BEGIN
  SET NOCOUNT ON

  DECLARE @xp_results TABLE
  (
  id              INT           NOT NULL,
  name            NVARCHAR(30)  COLLATE database_default NOT NULL,
  internal_value  INT           NULL,
  character_value NVARCHAR(212) COLLATE database_default NULL
  )
  INSERT INTO @xp_results
  EXECUTE master.dbo.xp_msver

  UPDATE @xp_results
  SET character_value = FORMATMESSAGE(14205)
  WHERE (character_value IS NULL)

  SELECT @description = (SELECT character_value FROM @xp_results WHERE (id = 1)) + N' ' +
                        (SELECT character_value FROM @xp_results WHERE (id = 2)) + N' / Windows ' +
                        (SELECT character_value FROM @xp_results WHERE (id = 15)) + N' / ' +
                        (SELECT character_value FROM @xp_results WHERE (id = 16)) + N' ' +
                        (SELECT CASE character_value
                                  WHEN N'PROCESSOR_INTEL_386'     THEN N'386'
                                  WHEN N'PROCESSOR_INTEL_486'     THEN N'486'
                                  WHEN N'PROCESSOR_INTEL_PENTIUM' THEN N'Pentium'
                                  WHEN N'PROCESSOR_MIPS_R4000'    THEN N'MIPS'
                                  WHEN N'PROCESSOR_ALPHA_21064'   THEN N'Alpha'
                                  ELSE character_value
                                END
                         FROM @xp_results WHERE (id = 18)) + N' CPU(s) / ' +
                        (SELECT CONVERT(NVARCHAR, internal_value) FROM @xp_results WHERE (id = 19)) + N' MB RAM.'
  IF (@result_set = 1)
    SELECT @description
END
go

/**************************************************************/
/* SP_MSX_SET_ACCOUNT                                              */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_msx_set_account...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_msx_set_account')
              AND (type = 'P')))
  DROP PROCEDURE sp_msx_set_account
go
CREATE PROCEDURE sp_msx_set_account
  @credential_name sysname = NULL,
  @credential_id   INT = NULL
AS
BEGIN
  DECLARE @retval INT
  IF @credential_id IS NOT NULL OR @credential_name IS NOT NULL
  BEGIN
     EXECUTE @retval = sp_verify_credential_identifiers  '@credential_name',
                                                        '@credential_id',
                                                        @credential_name OUTPUT,
                                                        @credential_id   OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure
    
    --set credential_id to agent registry
    EXECUTE master.dbo.xp_instance_regwrite  'HKEY_LOCAL_MACHINE',
                                    'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                    'MSXCredentialID',
                                    'REG_DWORD', 
                                    @credential_id
    --set connections to standard
    EXECUTE master.dbo.xp_instance_regwrite  'HKEY_LOCAL_MACHINE',
                                    'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                    'RegularMSXConnections',
                                    'REG_DWORD', 
                                    1
  END
  ELSE
  BEGIN
    --just set connection to integrated
    EXECUTE master.dbo.xp_instance_regwrite  'HKEY_LOCAL_MACHINE',
                                    'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                    'RegularMSXConnections',
                                    'REG_DWORD', 
                                    0
  END
END
go

/**************************************************************/
/* SP_MSX_GET_ACCOUNT                                              */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_msx_get_account...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_msx_get_account')
              AND (type = 'P')))
  DROP PROCEDURE sp_msx_get_account
go
CREATE PROCEDURE sp_msx_get_account
AS
BEGIN
  DECLARE @msx_connection INT
  DECLARE @credential_id  INT
  
  SELECT  @msx_connection  = 0    --integrated connections
  SELECT  @credential_id   = NULL      
  EXECUTE master.dbo.xp_instance_regread  N'HKEY_LOCAL_MACHINE',
                                          N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                          N'RegularMSXConnections',
                                          @msx_connection OUTPUT,
                                          N'no_output'
  IF @msx_connection = 1
  BEGIN
    EXECUTE master.dbo.xp_instance_regread  N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'MSXCredentialID',
                                            @credential_id OUTPUT,
                                            N'no_output'
    SELECT msx_connection = @msx_connection , msx_credential_id = @credential_id, 
           msx_credential_name = sc.name , msx_login_name = sc.credential_identity
    FROM   master.sys.credentials sc
    WHERE  credential_id = @credential_id    
  END
END
go

/**************************************************************/
/* SP_DELETE_OPERATOR                                         */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_operator...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_operator')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_operator
go
CREATE PROCEDURE sp_delete_operator
  @name                 sysname,
  @reassign_to_operator sysname = NULL
AS
BEGIN
  DECLARE @id                         INT
  DECLARE @alert_fail_safe_operator   sysname
  DECLARE @job_id                     UNIQUEIDENTIFIER
  DECLARE @job_id_as_char             VARCHAR(36)
  DECLARE @notify_email_operator_id   INT
  DECLARE @notify_netsend_operator_id INT
  DECLARE @notify_page_operator_id    INT
  DECLARE @reassign_to_id             INT
  DECLARE @cmd                        NVARCHAR(1000)
  DECLARE @current_msx_server         sysname
  DECLARE @reassign_to_escaped        NVARCHAR(256)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name                 = LTRIM(RTRIM(@name))
  SELECT @reassign_to_operator = LTRIM(RTRIM(@reassign_to_operator))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@reassign_to_operator = N'') SELECT @reassign_to_operator = NULL

  -- Only a sysadmin can do this
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Check if this Operator exists
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysoperators
                  WHERE (name = @name)))
  BEGIN
    RAISERROR(14262, 16, 1, '@name', @name)
    RETURN(1) -- Failure
  END

  -- Check if this operator the FailSafe Operator
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'AlertFailSafeOperator',
                                         @alert_fail_safe_operator OUTPUT,
                                         N'no_output'

  -- If it is, we disallow the delete operation
  IF (LTRIM(RTRIM(@alert_fail_safe_operator)) = @name)
  BEGIN
    RAISERROR(14504, 16, 1, @name, @name)
    RETURN(1) -- Failure
  END

  -- Check if this operator is 'MSXOperator'
  IF (@name = N'MSXOperator')
  BEGIN
    DECLARE @server_type VARCHAR(3)

    -- Disallow the delete operation if we're an MSX or a TSX
    EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                           N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                           N'MSXServerName',
                                           @current_msx_server OUTPUT,
                                           N'no_output'
    IF (@current_msx_server IS NOT NULL)
      SELECT @server_type = 'TSX'

    IF ((SELECT COUNT(*)
         FROM msdb.dbo.systargetservers) > 0)
      SELECT @server_type = 'MSX'

    IF (@server_type IS NOT NULL)
    BEGIN
      RAISERROR(14223, 16, 1, 'MSXOperator', @server_type)
      RETURN(1) -- Failure
    END
  END

  -- Convert the Name to it's ID
  SELECT @id = id
  FROM msdb.dbo.sysoperators
  WHERE (name = @name)

  IF (@reassign_to_operator IS NOT NULL)
  BEGIN
    -- On a TSX or standalone server, disallow re-assigning to the MSXOperator
    IF (@reassign_to_operator = N'MSXOperator') AND
       (NOT EXISTS (SELECT *
                    FROM msdb.dbo.systargetservers))
    BEGIN
      RAISERROR(14251, -1, -1, @reassign_to_operator)
      RETURN(1) -- Failure
    END

    SELECT @reassign_to_id = id
    FROM msdb.dbo.sysoperators
    WHERE (name = @reassign_to_operator)

    IF (@reassign_to_id IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@reassign_to_operator', @reassign_to_operator)
      RETURN(1) -- Failure
    END
  END

  -- Double up any single quotes in @reassign_to_operator
  IF (@reassign_to_operator IS NOT NULL)
    SET @reassign_to_escaped  = REPLACE(@reassign_to_operator, N'''', N'''''')

  BEGIN TRANSACTION

    -- Reassign (or delete) any sysnotifications rows that reference this operator
    IF (@reassign_to_operator IS NOT NULL)
    BEGIN
      UPDATE msdb.dbo.sysnotifications
      SET operator_id = @reassign_to_id
      WHERE (operator_id = @id)
        AND (NOT EXISTS (SELECT *
                         FROM msdb.dbo.sysnotifications sn2
                         WHERE (sn2.alert_id = msdb.dbo.sysnotifications.alert_id)
                           AND (sn2.operator_id = @reassign_to_id)))
    END

    DELETE FROM msdb.dbo.sysnotifications
    WHERE (operator_id = @id)

    -- Update any jobs that reference this operator
    DECLARE jobs_referencing_this_operator CURSOR LOCAL
    FOR
    SELECT job_id,
           notify_email_operator_id,
           notify_netsend_operator_id,
           notify_page_operator_id
    FROM msdb.dbo.sysjobs
    WHERE (notify_email_operator_id = @id)
       OR (notify_netsend_operator_id = @id)
       OR (notify_page_operator_id = @id)

    OPEN jobs_referencing_this_operator
    FETCH NEXT FROM jobs_referencing_this_operator INTO @job_id,
                                                        @notify_email_operator_id,
                                                        @notify_netsend_operator_id,
                                                        @notify_page_operator_id
    WHILE (@@fetch_status = 0)
    BEGIN
      SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id)
      SELECT @cmd = N'msdb.dbo.sp_update_job @job_id = ''' + @job_id_as_char + N''', '

      IF (@notify_email_operator_id = @id)
        IF (@reassign_to_operator IS NOT NULL)
          SELECT @cmd = @cmd + N'@notify_email_operator_name = N''' + @reassign_to_escaped  + N''', '
        ELSE
          SELECT @cmd = @cmd + N'@notify_email_operator_name = N'''', @notify_level_email = 0, '

      IF (@notify_netsend_operator_id = @id)
        IF (@reassign_to_operator IS NOT NULL)
          SELECT @cmd = @cmd + N'@notify_netsend_operator_name = N''' + @reassign_to_escaped  + N''', '
        ELSE
          SELECT @cmd = @cmd + N'@notify_netsend_operator_name = N'''', @notify_level_netsend = 0, '

      IF (@notify_page_operator_id = @id)
        IF (@reassign_to_operator IS NOT NULL)
          SELECT @cmd = @cmd + N'@notify_page_operator_name = N''' + @reassign_to_escaped  + N''', '
        ELSE
          SELECT @cmd = @cmd + N'@notify_page_operator_name = N'''', @notify_level_page = 0, '

      SELECT @cmd = SUBSTRING(@cmd, 1, (DATALENGTH(@cmd) / 2) - 2)
      EXECUTE (N'EXECUTE ' + @cmd)

      FETCH NEXT FROM jobs_referencing_this_operator INTO @job_id,
                                                          @notify_email_operator_id,
                                                          @notify_netsend_operator_id,
                                                          @notify_page_operator_id
    END
    DEALLOCATE jobs_referencing_this_operator

    -- Finally, do the actual DELETE
    DELETE FROM msdb.dbo.sysoperators
    WHERE (id = @id)

  COMMIT TRANSACTION

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_MSX_DEFECT                                              */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_msx_defect...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_msx_defect')
              AND (type = 'P')))
  DROP PROCEDURE sp_msx_defect
go
CREATE PROCEDURE sp_msx_defect
  @forced_defection BIT = 0
AS
BEGIN
  DECLARE @current_msx_server sysname
  DECLARE @retval             INT
  DECLARE @jobs_deleted       INT
  DECLARE @polling_interval   INT
  DECLARE @nt_user            NVARCHAR(100)

  SET NOCOUNT ON

  -- Only a sysadmin can do this
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) 
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  SELECT @retval = 0
  SELECT @jobs_deleted = 0

  -- Get the current MSX server name from the registry
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'MSXServerName',
                                         @current_msx_server OUTPUT,
                                         N'no_output'

  SELECT @current_msx_server = UPPER(LTRIM(RTRIM(@current_msx_server)))
  IF ((@current_msx_server IS NULL) OR (@current_msx_server = N''))
  BEGIN
    RAISERROR(14298, -1, -1)
    RETURN(1) -- Failure
  END

  SELECT @nt_user = ISNULL(NT_CLIENT(), ISNULL(SUSER_SNAME(), FORMATMESSAGE(14205)))

  EXECUTE @retval = master.dbo.xp_msx_enlist 1, @current_msx_server, @nt_user

  IF (@retval <> 0) AND (@forced_defection = 0)
    RETURN(1) -- Failure

  -- Clear the MSXServerName registry entry
  EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                          N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                          N'MSXServerName',
                                          N'REG_SZ',
                                          N''

  -- Delete the MSXPollingInterval registry entry
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'MSXPollInterval',
                                         @polling_interval OUTPUT,
                                         N'no_output'
  IF (@polling_interval IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE',
                                                  N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                                  N'MSXPollInterval'

  -- Remove the entry from sqlagent_info
  DELETE FROM msdb.dbo.sqlagent_info
  WHERE (attribute = N'DateEnlisted')

  -- Delete all the jobs that originated from the MSX
  -- NOTE: We can't use sp_delete_job here since sp_delete_job checks if the caller is
  --       SQLServerAgent (only SQLServerAgent can delete non-local jobs).
  EXECUTE msdb.dbo.sp_delete_all_msx_jobs @current_msx_server, @jobs_deleted OUTPUT
  RAISERROR(14227, 0, 1, @current_msx_server, @jobs_deleted)

  -- Now delete the old msx server record
  DELETE msdb.dbo.sysoriginatingservers 
  WHERE (originating_server = @current_msx_server)
    AND (master_server = 1)

  -- If a forced defection was performed, attempt to notify the MSXOperator
  IF (@forced_defection = 1)
  BEGIN
    DECLARE @network_address    NVARCHAR(100)
    DECLARE @command            NVARCHAR(512)
    DECLARE @local_machine_name sysname
    DECLARE @res_warning        NVARCHAR(300)

    SELECT @network_address = netsend_address
    FROM msdb.dbo.sysoperators
    WHERE (name = N'MSXOperator')

    IF (@network_address IS NOT NULL)
    BEGIN
      EXECUTE @retval = master.dbo.xp_getnetname @local_machine_name OUTPUT
      IF (@retval <> 0)
        RETURN(1) -- Failure
      SELECT @res_warning = FORMATMESSAGE(14217)
      SELECT @command = N'NET SEND ' + @network_address + N' ' + @res_warning
      SELECT @command = STUFF(@command, PATINDEX(N'%[%%]s%', @command), 2, NT_CLIENT())
      SELECT @command = STUFF(@command, PATINDEX(N'%[%%]s%', @command), 2, @local_machine_name)
      EXECUTE master.dbo.xp_cmdshell @command, no_output
    END
  END

  -- Delete the 'MSXOperator' (must do this last)
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysoperators
              WHERE (name = N'MSXOperator')))
    EXECUTE msdb.dbo.sp_delete_operator @name = N'MSXOperator'

  RETURN(0) -- 0 means success
END
go

/**************************************************************/
/* SP_MSX_ENLIST                                              */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_msx_enlist...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_msx_enlist')
              AND (type = 'P')))
  DROP PROCEDURE sp_msx_enlist
go
CREATE PROCEDURE sp_msx_enlist
  @msx_server_name sysname,
  @location        NVARCHAR(100) = NULL -- The procedure will supply a default
AS
BEGIN
  DECLARE @current_msx_server       sysname
  DECLARE @local_machine_name       sysname
  DECLARE @msx_originating_server   sysname
  DECLARE @retval                   INT
  DECLARE @time_zone_adjustment     INT
  DECLARE @local_time               NVARCHAR(100)
  DECLARE @nt_user                  NVARCHAR(100)
  DECLARE @poll_interval            INT

  SET NOCOUNT ON

  -- Only a sysadmin can do this
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) 
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Only an NT server can be enlisted
  IF ((PLATFORM() & 0x1) <> 0x1) -- NT
  BEGIN
    RAISERROR(14540, -1, 1)
    RETURN(1) -- Failure
  END

  -- Only SBS, Standard, or Enterprise editions of SQL Server can be enlisted
  IF ((PLATFORM() & 0x100) = 0x100) -- Desktop package
  BEGIN
    RAISERROR(14539, -1, -1)
    RETURN(1) -- Failure
  END

  -- Remove any leading/trailing spaces from parameters
  SELECT @msx_server_name  = UPPER(LTRIM(RTRIM(@msx_server_name)))
  SELECT @location         = LTRIM(RTRIM(@location))
  SELECT @local_machine_name = UPPER(CONVERT(NVARCHAR(30), SERVERPROPERTY('ServerName')))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@location = N'') SELECT @location = NULL

  SELECT @retval = 0

  -- Get the values that we'll need for the [re]enlistment operation (except the local time
  -- which we get right before we call xp_msx_enlist to that it's as accurate as possible)
  SELECT @nt_user = ISNULL(NT_CLIENT(), ISNULL(SUSER_SNAME(), FORMATMESSAGE(14205)))
  EXECUTE master.dbo.xp_regread N'HKEY_LOCAL_MACHINE',
                                N'SYSTEM\CurrentControlSet\Control\TimeZoneInformation',
                                N'Bias',
                                @time_zone_adjustment OUTPUT,
                                N'no_output'
  IF ((PLATFORM() & 0x1) = 0x1) -- NT
    SELECT @time_zone_adjustment = -ISNULL(@time_zone_adjustment, 0)
  ELSE
    SELECT @time_zone_adjustment = -CONVERT(INT, CONVERT(BINARY(2), ISNULL(@time_zone_adjustment, 0)))

  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'MSXPollInterval',
                                         @poll_interval OUTPUT,
                                         N'no_output'
  SELECT @poll_interval = ISNULL(@poll_interval, 60) -- This should be the same as DEF_REG_MSX_POLL_INTERVAL
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'MSXServerName',
                                         @current_msx_server OUTPUT,
                                         N'no_output'
  SELECT @current_msx_server = LTRIM(RTRIM(@current_msx_server))

  -- Check if this machine is an MSX (and therefore cannot be enlisted into another MSX)
  IF (EXISTS (SELECT *
              FROM msdb.dbo.systargetservers))
  BEGIN
   --Get local server/instance name  
    RAISERROR(14299, -1, -1, @local_machine_name)
    RETURN(1) -- Failure
  END

  -- Check if the MSX supplied is the same as the local machine (this is not allowed)
  IF (UPPER(@local_machine_name) = @msx_server_name)
  BEGIN
    RAISERROR(14297, -1, -1)
    RETURN(1) -- Failure
  END

  -- Check if MSDB has be re-installed since we enlisted
  IF (@current_msx_server IS NOT NULL) AND
     (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sqlagent_info
                  WHERE (attribute = 'DateEnlisted')))
  BEGIN
    -- User is tring to [re]enlist after a re-install, so we have to forcefully defect before
    -- we can fully enlist again
    EXECUTE msdb.dbo.sp_msx_defect @forced_defection = 1
    SELECT @current_msx_server = NULL
  END

  -- Check if we are already enlisted, in which case we re-enlist
  IF ((@current_msx_server IS NOT NULL) AND (@current_msx_server <> N''))
  BEGIN
    IF (UPPER(@current_msx_server) = @msx_server_name)
    BEGIN
      -- Update the [existing] enlistment
      SELECT @local_time = CONVERT(NVARCHAR, GETDATE(), 112) + N' ' + CONVERT(NVARCHAR, GETDATE(), 108)
      EXECUTE @retval = master.dbo.xp_msx_enlist 2, @msx_server_name, @nt_user, @location, @time_zone_adjustment, @local_time, @poll_interval
      RETURN(@retval) -- 0 means success
    END
    ELSE
    BEGIN
      RAISERROR(14296, -1, -1, @current_msx_server)
      RETURN(1) -- Failure
    END
  END

  -- If we get this far then we're dealing with a new enlistment...
  

  -- If no location is supplied, generate one (such as we can)
  IF (@location IS NULL)
    EXECUTE msdb.dbo.sp_generate_server_description @location OUTPUT

  SELECT @local_time = CONVERT(NVARCHAR, GETDATE(), 112) + ' ' + CONVERT(NVARCHAR, GETDATE(), 108)
  EXECUTE @retval = master.dbo.xp_msx_enlist 0, @msx_server_name, @nt_user, @location, @time_zone_adjustment, @local_time, @poll_interval

  IF (@retval = 0)
  BEGIN
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'MSXServerName',
                                            N'REG_SZ',
                                            @msx_server_name

    IF (@current_msx_server IS NOT NULL)
      RAISERROR(14228, 0, 1, @current_msx_server, @msx_server_name)
    ELSE
      RAISERROR(14229, 0, 1, @msx_server_name)

    -- Update the sysoriginatingservers table with the msx server name. May need to clean up if it already has an msx entry
    SELECT @msx_originating_server = NULL
    -- Get the msx server name 
    SELECT @msx_originating_server = originating_server 
    FROM msdb.dbo.sysoriginatingservers
    WHERE (master_server = 1)
    
    IF(@msx_originating_server IS NULL)
    BEGIN
        -- Good. No msx server found so just add the new one
        INSERT INTO msdb.dbo.sysoriginatingservers(originating_server, master_server) VALUES (@msx_server_name, 1)
    END
    ELSE
    BEGIN
        -- Found a previous entry. If it isn't the same server we need to clean up any existing msx jobs
        IF(@msx_originating_server != @msx_server_name) 
        BEGIN
            INSERT INTO msdb.dbo.sysoriginatingservers(originating_server, master_server) VALUES (@msx_server_name, 1)
            -- Optimistically try and remove any msx jobs left over from the previous msx enlistment. 
            EXECUTE msdb.dbo.sp_delete_all_msx_jobs @msx_originating_server 
            -- And finally delete the old msx server record
            DELETE msdb.dbo.sysoriginatingservers 
            WHERE (originating_server = @msx_originating_server)
              AND (master_server = 1)
        END
    END

    -- Add entry to sqlagent_info
    INSERT INTO msdb.dbo.sqlagent_info (attribute, value) VALUES ('DateEnlisted', CONVERT(VARCHAR(10), GETDATE(), 112))
  END

  RETURN(@retval) -- 0 means success
END
go

/**************************************************************/
/* SP_DELETE_TARGETSERVER                                     */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_targetserver...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_targetserver')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_targetserver
go
CREATE PROCEDURE sp_delete_targetserver
  @server_name        sysname,
  @clear_downloadlist BIT = 1,
  @post_defection     BIT = 1
AS
BEGIN
  DECLARE @server_id INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name)))

  -- Check server name
  SELECT @server_id = server_id
  FROM msdb.dbo.systargetservers
  WHERE (UPPER(server_name) = @server_name)

  IF (@server_id IS NULL)
  BEGIN
    RAISERROR(14262, -1, -1, '@server_name', @server_name)
    RETURN(1) -- Failure
  END

  BEGIN TRANSACTION

    IF (@clear_downloadlist = 1)
    BEGIN
      DELETE FROM msdb.dbo.sysdownloadlist
      WHERE (target_server = @server_name)
    END

    IF (@post_defection = 1)
    BEGIN
      -- Post a defect instruction to the server
      -- NOTE: We must do this BEFORE deleting the systargetservers row
      EXECUTE msdb.dbo.sp_post_msx_operation 'DEFECT', 'SERVER', 0x00, @server_name
    END

    DELETE FROM msdb.dbo.systargetservers
    WHERE (server_id = @server_id)

    DELETE FROM msdb.dbo.systargetservergroupmembers
    WHERE (server_id = @server_id)

    DELETE FROM msdb.dbo.sysjobservers
    WHERE (server_id = @server_id)

  COMMIT TRANSACTION

  RETURN(@@error) -- 0 means success
END
go


/**************************************************************/
/* SP_ENLIST_TSX                                              */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_enlist_tsx' 
go

IF EXISTS (SELECT name FROM sysobjects 
         WHERE name = 'sp_enlist_tsx' AND type = 'P')
   DROP PROCEDURE sp_enlist_tsx
GO

create proc sp_enlist_tsx
   @Action int,            -- 0 - enlist; 1 - defect; 2 - update
   @ServerName  sysname,      -- tsx server name
   @Location  nvarchar(200),  -- tsx server location
   @TimeZoneAdjustment int,   -- tsx server time zone adjustment
   @LocalTime datetime,    -- tsx server local time
   @NTUserName nvarchar(100), -- name of the user performing the enlistment
   @PollInterval int,          -- polling interval
    @TSX_Version int = 0        -- VersionMajor: ((@TSX_Version / 0x1000000) & 0xff)
                                -- VersionMinor: ((@TSX_Version / 0x10000) & 0xff)
                                -- Build no:      (@TSX_Version & 0xFFFF)
as
begin
   SET NOCOUNT ON

   /* check permissions */
   IF (ISNULL(IS_MEMBER(N'TargetServersRole'), 0) = 0) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0)
   begin
   raiserror(15003,-1,-1, N'TargetServersRole')
   return 1
   end

   --9.0 and above servers set this version param
   if(@TSX_Version is null)
      set @TSX_Version = 0

   --Only check version during enlistment
   if(@Action = 0 AND ((@TSX_Version / 0x1000000) & 0xff) < 9)
   begin
      DECLARE @majorVer int, @minorVer int, @buildNo int
      SELECT @majorVer = ((@@microsoftversion / 0x1000000) & 0xff),
             @minorVer = ((@@microsoftversion / 0x10000) & 0xff),
             @buildNo = (@@microsoftversion & 0xfff)

      raiserror(14306, -1, -1, @majorVer, @minorVer, @buildNo )
      return 12
   end

   /* check input parameters */
   if @ServerName is null
   begin
   raiserror(14043, -1, -1, '@ServerName')
   return 2
   end

   select @ServerName = LTRIM(@ServerName)
   select @ServerName = RTRIM(@ServerName)
   if @ServerName = ''
   begin
   raiserror(21263, -1, -1, '@ServerName')
   return 3
   end

   select @ServerName = UPPER(@ServerName)

   if @Action <> 1 And @Action <> 2
   begin
   /* default action is to enlist */
   select @Action = 0
   end

  if @Action = 0 /* enlisting */
  begin
   /* check input parameters */
   if @NTUserName is null
   begin
      raiserror(14043, -1, -1, '@NTUserName')
      return 4
   end

   select @NTUserName = LTRIM(@NTUserName)
   select @NTUserName = RTRIM(@NTUserName)
   if @NTUserName = ''
   begin
     raiserror(21263, -1, -1, '@NTUserName')
     return 5
   end

   /* check if local server is already configured as TSX machine */
   declare @msx_server_name sysname
   select @msx_server_name = N''

   execute master.dbo.xp_instance_regread 
      N'HKEY_LOCAL_MACHINE',
      N'Software\Microsoft\MSSQLServer\SQLServerAgent',
      N'MSXServerName',
      @msx_server_name OUTPUT

   select @msx_server_name = LTRIM(@msx_server_name)
   select @msx_server_name = RTRIM(@msx_server_name)
   if @msx_server_name <> N''
   begin
      raiserror(14360, -1, -1, @@SERVERNAME)
      return 6
   end

   /* 
   * check that local server is not running a desktop SKU, 
   * i.e. Win9x, Office, or MSDE
   */
   if( PLATFORM() & 0x100 = 0x100 )
   begin
      raiserror(14362, -1, -1)
      return 8
   end

   /* check if we have any MSXOperators defined */
   if not exists (SELECT * FROM msdb.dbo.sysoperators WHERE name = N'MSXOperator')
   begin
      raiserror(14363, -1, -1)
      return 9
   end

   /* all checks have passed, insert new row into systargetservers table */
   INSERT INTO msdb.dbo.systargetservers 
   (
   server_name, 
   location, 
   time_zone_adjustment, 
   enlist_date, 
   last_poll_date, 
   status, 
   local_time_at_last_poll, 
   enlisted_by_nt_user, 
   poll_interval
   ) 
   VALUES 
   (
   @ServerName, 
   @Location, 
   @TimeZoneAdjustment, 
   GETDATE(), 
   GETDATE(), 
   1, 
   @LocalTime, 
   @NTUserName, 
   @PollInterval
   )

   /* delete hanging rows from sysdownloadlist */
   DELETE FROM msdb.dbo.sysdownloadlist 
   WHERE target_server = @ServerName
   end

   if @Action = 2 /* updating existing enlistment */
   begin
   /* check if we have any MSXOperators defined */
   if not exists (SELECT * FROM msdb.dbo.sysoperators WHERE name = N'MSXOperator')
   begin
      raiserror(14363, -1, -1)
      return 10
   end

   /* check if TSX machine is already enlisted */
   If not exists (SELECT * FROM msdb.dbo.systargetservers WHERE UPPER(server_name) = @ServerName)
   begin
      raiserror(14364, -1, -1)
      return 11
   end

   if @Location is null /* don't update the location if it is not supplied */
   begin
      UPDATE msdb.dbo.systargetservers SET 
      time_zone_adjustment = @TimeZoneAdjustment, 
      poll_interval = @PollInterval
      WHERE (UPPER(server_name) = @ServerName)
   end
   else
   begin
      UPDATE msdb.dbo.systargetservers SET 
      location = @Location, 
      time_zone_adjustment = @TimeZoneAdjustment, 
      poll_interval = @PollInterval
      WHERE (UPPER(server_name) = @ServerName)
   end
   end

  if @Action = 1 /* defecting */
  begin
   if (exists (SELECT * FROM msdb.dbo.systargetservers WHERE UPPER(server_name) = @ServerName)) 
   begin
      execute msdb.dbo.sp_delete_targetserver 
         @server_name = @ServerName, 
         @post_defection = 0 
   end
   else
   begin
      DELETE FROM msdb.dbo.sysdownloadlist 
      WHERE (target_server = @ServerName)
   end
  end

  if @Action = 0 Or @Action = 2 /* enlisting or updating existing enlistment */
  begin
   /* select resultset to return to the caller */
   SELECT 
   id,
   name, 
   enabled, 
   email_address, 
   pager_address, 
   netsend_address, 
   weekday_pager_start_time, 
   weekday_pager_end_time, 
   saturday_pager_start_time, 
   saturday_pager_end_time, 
   sunday_pager_start_time, 
   sunday_pager_end_time, 
   pager_days 
   FROM 
   msdb.dbo.sysoperators WHERE (name = N'MSXOperator')
   end
end
go


/**************************************************************/
/* SP_GET_SQLAGENT_PROPERTIES                                 */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_get_sqlagent_properties...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_get_sqlagent_properties')
              AND (type = 'P')))
  DROP PROCEDURE sp_get_sqlagent_properties
go
CREATE PROCEDURE sp_get_sqlagent_properties
AS
BEGIN
  DECLARE @auto_start                  INT
  DECLARE @startup_account             NVARCHAR(100)
  DECLARE @msx_server_name             sysname

  -- Non-SQLDMO exposed properties
  DECLARE @sqlserver_restart           INT
  DECLARE @jobhistory_max_rows         INT
  DECLARE @jobhistory_max_rows_per_job INT
  DECLARE @errorlog_file               NVARCHAR(255)
  DECLARE @errorlogging_level          INT
  DECLARE @error_recipient             NVARCHAR(30)
  DECLARE @monitor_autostart           INT
  DECLARE @local_host_server           sysname
  DECLARE @job_shutdown_timeout        INT
  DECLARE @cmdexec_account             VARBINARY(64)
  DECLARE @regular_connections         INT
  DECLARE @host_login_name             sysname
  DECLARE @host_login_password         VARBINARY(512)
  DECLARE @login_timeout               INT
  DECLARE @idle_cpu_percent            INT
  DECLARE @idle_cpu_duration           INT
  DECLARE @oem_errorlog                INT
  DECLARE @email_profile               NVARCHAR(64)
  DECLARE @email_save_in_sent_folder   INT
  DECLARE @cpu_poller_enabled          INT
  DECLARE @alert_replace_runtime_tokens INT

  SET NOCOUNT ON

  -- NOTE: We return all SQLServerAgent properties at one go for performance reasons

  -- Read the values from the registry
  IF ((PLATFORM() & 0x1) = 0x1) -- NT
  BEGIN
    DECLARE @key NVARCHAR(200)

    SELECT @key = N'SYSTEM\CurrentControlSet\Services\'
    IF (SERVERPROPERTY('INSTANCENAME') IS NOT NULL)
      SELECT @key = @key + N'SQLAgent$' + CONVERT (sysname, SERVERPROPERTY('INSTANCENAME'))
    ELSE
      SELECT @key = @key + N'SQLServerAgent'

    EXECUTE master.dbo.xp_regread N'HKEY_LOCAL_MACHINE',
                                  @key,
                                  N'Start',
                                  @auto_start OUTPUT,
                                  N'no_output'
    EXECUTE master.dbo.xp_regread N'HKEY_LOCAL_MACHINE',
                                  @key,
                                  N'ObjectName',
                                  @startup_account OUTPUT,
                                  N'no_output'
  END
  ELSE
  BEGIN
    SELECT @auto_start = 3 -- Manual start
    SELECT @startup_account = NULL
  END
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'MSXServerName',
                                         @msx_server_name OUTPUT,
                                         N'no_output'

  -- Non-SQLDMO exposed properties
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'RestartSQLServer',
                                         @sqlserver_restart OUTPUT,
                                         N'no_output'
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'JobHistoryMaxRows',
                                         @jobhistory_max_rows OUTPUT,
                                         N'no_output'
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'JobHistoryMaxRowsPerJob',
                                         @jobhistory_max_rows_per_job OUTPUT,
                                         N'no_output'
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'ErrorLogFile',
                                         @errorlog_file OUTPUT,
                                         N'no_output'
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'ErrorLoggingLevel',
                                         @errorlogging_level OUTPUT,
                                         N'no_output'
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'ErrorMonitor',
                                         @error_recipient OUTPUT,
                                         N'no_output'
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'MonitorAutoStart',
                                         @monitor_autostart OUTPUT,
                                         N'no_output'
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'ServerHost',
                                         @local_host_server OUTPUT,
                                         N'no_output'
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'JobShutdownTimeout',
                                         @job_shutdown_timeout OUTPUT,
                                         N'no_output'
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'CmdExecAccount',
                                         @cmdexec_account OUTPUT,
                                         N'no_output'
  SET @regular_connections = 0
  SET @host_login_name = NULL
  SET @host_login_password = NULL

  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'LoginTimeout',
                                         @login_timeout OUTPUT,
                                         N'no_output'
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'IdleCPUPercent',
                                         @idle_cpu_percent OUTPUT,
                                         N'no_output'
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'IdleCPUDuration',
                                         @idle_cpu_duration OUTPUT,
                                         N'no_output'
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'OemErrorLog',
                                         @oem_errorlog OUTPUT,
                                         N'no_output'

  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'EmailProfile',
                                         @email_profile OUTPUT,
                                         N'no_output'
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'EmailSaveSent',
                                         @email_save_in_sent_folder OUTPUT,
                                         N'no_output'

  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'AlertReplaceRuntimeTokens',
                                         @alert_replace_runtime_tokens OUTPUT,
                                         N'no_output'

  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'CoreEngineMask',
                                         @cpu_poller_enabled OUTPUT,
                                         N'no_output'
  IF (@cpu_poller_enabled IS NOT NULL)
    SELECT @cpu_poller_enabled = CASE WHEN (@cpu_poller_enabled & 32) = 32 THEN 0 ELSE 1 END

  -- Return the values to the client
  SELECT auto_start = CASE @auto_start
                        WHEN 2 THEN 1 -- 2 means auto-start
                        WHEN 3 THEN 0 -- 3 means don't auto-start
                        ELSE 0        -- Safety net
                      END,
         msx_server_name = @msx_server_name,
         sqlagent_type = (SELECT CASE
                                    WHEN (COUNT(*) = 0) AND (ISNULL(DATALENGTH(@msx_server_name), 0) = 0) THEN 1 -- Standalone
                                    WHEN (COUNT(*) = 0) AND (ISNULL(DATALENGTH(@msx_server_name), 0) > 0) THEN 2 -- TSX
                                    WHEN (COUNT(*) > 0) AND (ISNULL(DATALENGTH(@msx_server_name), 0) = 0) THEN 3 -- MSX
                                    WHEN (COUNT(*) > 0) AND (ISNULL(DATALENGTH(@msx_server_name), 0) > 0) THEN 0 -- Multi-Level MSX (currently invalid)
                                    ELSE 0 -- Invalid
                                  END
                           FROM msdb.dbo.systargetservers),
         startup_account = @startup_account,

         -- Non-SQLDMO exposed properties
         sqlserver_restart = ISNULL(@sqlserver_restart, 1),
         jobhistory_max_rows = @jobhistory_max_rows,
         jobhistory_max_rows_per_job = @jobhistory_max_rows_per_job,
         errorlog_file = @errorlog_file,
         errorlogging_level = ISNULL(@errorlogging_level, 7),
         error_recipient = @error_recipient,
         monitor_autostart = ISNULL(@monitor_autostart, 0),
         local_host_server = @local_host_server,
         job_shutdown_timeout = ISNULL(@job_shutdown_timeout, 15),
         cmdexec_account = @cmdexec_account,
         regular_connections = ISNULL(@regular_connections, 0),
         host_login_name = @host_login_name,
         host_login_password = @host_login_password,
         login_timeout = ISNULL(@login_timeout, 30),
         idle_cpu_percent = ISNULL(@idle_cpu_percent, 10),
         idle_cpu_duration = ISNULL(@idle_cpu_duration, 600),
         oem_errorlog = ISNULL(@oem_errorlog, 0),
         sysadmin_only = NULL,
         email_profile = @email_profile,
         email_save_in_sent_folder = ISNULL(@email_save_in_sent_folder, 0),
         cpu_poller_enabled = ISNULL(@cpu_poller_enabled, 0),
         alert_replace_runtime_tokens = ISNULL(@alert_replace_runtime_tokens, 0)
END
go

/**************************************************************/
/* SP_SET_SQLAGENT_PROPERTIES                                 */
/**************************************************************/
IF EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE name = N'sp_set_sqlagent_properties' AND type = 'P')
BEGIN
  DROP PROCEDURE dbo.sp_set_sqlagent_properties
END
go

PRINT ''
PRINT 'Create procedure sp_set_sqlagent_properties...'
go

CREATE PROCEDURE dbo.sp_set_sqlagent_properties
  @auto_start                  INT           = NULL, -- 1 or 0
  -- Non-SQLDMO exposed properties
  @sqlserver_restart           INT           = NULL, -- 1 or 0
  @jobhistory_max_rows         INT           = NULL, -- No maximum = -1, otherwise must be > 1
  @jobhistory_max_rows_per_job INT           = NULL, -- 1 to @jobhistory_max_rows
  @errorlog_file               NVARCHAR(255) = NULL, -- Full drive\path\name of errorlog file
  @errorlogging_level          INT           = NULL, -- 1 = error, 2 = warning, 4 = information
  @error_recipient             NVARCHAR(30)  = NULL, -- Network address of error popup recipient
  @monitor_autostart           INT           = NULL, -- 1 or 0
  @local_host_server           sysname      = NULL, -- Alias of local host server
  @job_shutdown_timeout        INT           = NULL, -- 5 to 600 seconds
  @cmdexec_account             VARBINARY(64) = NULL, -- CmdExec account information
  @regular_connections         INT           = NULL, -- obsolete
  @host_login_name             sysname       = NULL, -- obsolete
  @host_login_password         VARBINARY(512) = NULL, -- obsolete
  @login_timeout               INT           = NULL, -- 5 to 45 (seconds)
  @idle_cpu_percent            INT           = NULL, -- 1 to 100
  @idle_cpu_duration           INT           = NULL, -- 20 to 86400 seconds
  @oem_errorlog                INT           = NULL, -- 1 or 0
  @sysadmin_only               INT           = NULL, -- not applicable to Yukon server, for backwards compatibility only
  @email_profile               NVARCHAR(64)  = NULL, -- Email profile name
  @email_save_in_sent_folder   INT           = NULL, -- 1 or 0
  @cpu_poller_enabled          INT           = NULL, -- 1 or 0
  @alert_replace_runtime_tokens INT          = NULL  -- 1 or 0
AS
BEGIN
  -- NOTE: We set all SQLServerAgent properties at one go for performance reasons.
  -- NOTE: You cannot set the value of the properties msx_server_name, is_msx or
  --       startup_account - they are all read only.

  DECLARE @res_valid_range           NVARCHAR(100)
  DECLARE @existing_core_engine_mask INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @errorlog_file     = LTRIM(RTRIM(@errorlog_file))
  SELECT @error_recipient   = LTRIM(RTRIM(@error_recipient))
  SELECT @local_host_server = LTRIM(RTRIM(@local_host_server))
  SELECT @host_login_name   = LTRIM(RTRIM(@host_login_name))
  SELECT @email_profile     = LTRIM(RTRIM(@email_profile))

  -- Make sure values (if supplied) are good
  IF (@auto_start IS NOT NULL)
  BEGIN
    -- NOTE: When setting the the services start value, 2 == auto-start, 3 == Don't auto-start
    SELECT @auto_start = CASE @auto_start
                           WHEN 0 THEN 3
                           WHEN 1 THEN 2
                           ELSE 3 -- Assume non auto-start if passed a junk value
                          END
  END

  -- Non-SQLDMO exposed properties
  IF ((@sqlserver_restart IS NOT NULL) AND (@sqlserver_restart <> 0))
    SELECT @sqlserver_restart = 1

  IF (@jobhistory_max_rows IS NOT NULL)
  BEGIN
    SELECT @res_valid_range = FORMATMESSAGE(14207)
    IF ((@jobhistory_max_rows < -1) OR (@jobhistory_max_rows = 0))
    BEGIN
      RAISERROR(14266, -1, -1, '@jobhistory_max_rows', @res_valid_range)
      RETURN(1) -- Failure
    END
  END
  ELSE
  BEGIN
    EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                           N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                           N'JobHistoryMaxRows',
                                           @jobhistory_max_rows OUTPUT,
                                           N'no_output'
    SELECT @jobhistory_max_rows = ISNULL(@jobhistory_max_rows, -1)
  END

  IF (@jobhistory_max_rows_per_job IS NOT NULL)
  BEGIN
    IF (@jobhistory_max_rows = -1)
      SELECT @jobhistory_max_rows_per_job = 0
    ELSE
    BEGIN
      IF ((@jobhistory_max_rows_per_job < 1) OR (@jobhistory_max_rows_per_job > @jobhistory_max_rows))
      BEGIN
        SELECT @res_valid_range = N'1..' + CONVERT(NVARCHAR, @jobhistory_max_rows)
        RAISERROR(14266, -1, -1, '@jobhistory_max_rows', @res_valid_range)
        RETURN(1) -- Failure
      END
    END
  END

  IF (@errorlogging_level IS NOT NULL) AND ((@errorlogging_level < 1) OR (@errorlogging_level > 7))
  BEGIN
    RAISERROR(14266, -1, -1, '@errorlogging_level', '1..7')
    RETURN(1) -- Failure
  END

  IF (@monitor_autostart IS NOT NULL) AND ((@monitor_autostart < 0) OR (@monitor_autostart > 1))
  BEGIN
    RAISERROR(14266, -1, -1, '@monitor_autostart', '0, 1')
    RETURN(1) -- Failure
  END

  IF (@job_shutdown_timeout IS NOT NULL) AND ((@job_shutdown_timeout < 5) OR (@job_shutdown_timeout > 600))
  BEGIN
    RAISERROR(14266, -1, -1, '@job_shutdown_timeout', '5..600')
    RETURN(1) -- Failure
  END

  IF (@login_timeout IS NOT NULL) AND ((@login_timeout < 5) OR (@login_timeout > 45))
  BEGIN
    RAISERROR(14266, -1, -1, '@login_timeout', '5..45')
    RETURN(1) -- Failure
  END

  IF ((@idle_cpu_percent IS NOT NULL) AND ((@idle_cpu_percent < 1) OR (@idle_cpu_percent > 100)))
  BEGIN
    RAISERROR(14266, -1, -1, '@idle_cpu_percent', '10..100')
    RETURN(1) -- Failure
  END

  IF ((@idle_cpu_duration IS NOT NULL) AND ((@idle_cpu_duration < 20) OR (@idle_cpu_duration > 86400)))
  BEGIN
    RAISERROR(14266, -1, -1, '@idle_cpu_duration', '20..86400')
    RETURN(1) -- Failure
  END

  IF (@oem_errorlog IS NOT NULL) AND ((@oem_errorlog < 0) OR (@oem_errorlog > 1))
  BEGIN
    RAISERROR(14266, -1, -1, '@oem_errorlog', '0, 1')
    RETURN(1) -- Failure
  END

  IF (@sysadmin_only IS NOT NULL)
  BEGIN
    RAISERROR(14378, -1, -1)
    RETURN(1) -- Failure
  END

  IF (@email_save_in_sent_folder IS NOT NULL) AND ((@email_save_in_sent_folder < 0) OR (@email_save_in_sent_folder > 1))
  BEGIN
    RAISERROR(14266, -1, -1, 'email_save_in_sent_folder', '0, 1')
    RETURN(1) -- Failure
  END

  IF (@cpu_poller_enabled IS NOT NULL) AND ((@cpu_poller_enabled < 0) OR (@cpu_poller_enabled > 1))
  BEGIN
    RAISERROR(14266, -1, -1, 'cpu_poller_enabled', '0, 1')
    RETURN(1) -- Failure
  END

  IF (@alert_replace_runtime_tokens IS NOT NULL) AND ((@alert_replace_runtime_tokens < 0) OR (@alert_replace_runtime_tokens > 1))
  BEGIN
    RAISERROR(14266, -1, -1, 'alert_replace_runtime_tokens', '0, 1')
    RETURN(1) -- Failure
  END

  -- Write out the values
  IF (@auto_start IS NOT NULL)
  BEGIN
    IF ((PLATFORM() & 0x1) = 0x1) -- NT
    BEGIN
      DECLARE @key NVARCHAR(200)

      SELECT @key = N'SYSTEM\CurrentControlSet\Services\'
      IF (SERVERPROPERTY('INSTANCENAME') IS NOT NULL)
        SELECT @key = @key + N'SQLAgent$' + CONVERT (sysname, SERVERPROPERTY('INSTANCENAME'))
      ELSE
        SELECT @key = @key + N'SQLServerAgent'

      EXECUTE master.dbo.xp_regwrite N'HKEY_LOCAL_MACHINE',
                                     @key,
                                     N'Start',
                                     N'REG_DWORD',
                                     @auto_start
    END
    ELSE
      RAISERROR(14546, 16, 1, '@auto_start')
  END

  -- Non-SQLDMO exposed properties
  IF (@sqlserver_restart IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'RestartSQLServer',
                                            N'REG_DWORD',
                                            @sqlserver_restart
  IF (@jobhistory_max_rows IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'JobHistoryMaxRows',
                                            N'REG_DWORD',
                                            @jobhistory_max_rows
  IF (@jobhistory_max_rows_per_job IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'JobHistoryMaxRowsPerJob',
                                            N'REG_DWORD',
                                            @jobhistory_max_rows_per_job
  IF (@errorlog_file IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'ErrorLogFile',
                                            N'REG_SZ',
                                            @errorlog_file
  IF (@errorlogging_level IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'ErrorLoggingLevel',
                                            N'REG_DWORD',
                                            @errorlogging_level
  IF (@error_recipient IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'ErrorMonitor',
                                            N'REG_SZ',
                                            @error_recipient
  IF (@monitor_autostart IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'MonitorAutoStart',
                                            N'REG_DWORD',
                                            @monitor_autostart
  IF (@local_host_server IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'ServerHost',
                                            N'REG_SZ',
                                            @local_host_server
  IF (@job_shutdown_timeout IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'JobShutdownTimeout',
                                            N'REG_DWORD',
                                            @job_shutdown_timeout
  IF (@cmdexec_account IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'CmdExecAccount',
                                            N'REG_BINARY',
                                            @cmdexec_account

  IF (@login_timeout IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'LoginTimeout',
                                            N'REG_DWORD',
                                            @login_timeout
  IF (@idle_cpu_percent IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'IdleCPUPercent',
                                            N'REG_DWORD',
                                            @idle_cpu_percent
  IF (@idle_cpu_duration IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'IdleCPUDuration',
                                            N'REG_DWORD',
                                            @idle_cpu_duration
  IF (@oem_errorlog IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'OemErrorLog',
                                            N'REG_DWORD',
                                            @oem_errorlog

  IF (@email_profile IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'EmailProfile',
                                            N'REG_SZ',
                                            @email_profile
  IF (@email_save_in_sent_folder IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'EmailSaveSent',
                                            N'REG_DWORD',
                                            @email_save_in_sent_folder

  IF (@alert_replace_runtime_tokens IS NOT NULL)
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'AlertReplaceRuntimeTokens',
                                            N'REG_DWORD',
                                            @alert_replace_runtime_tokens  
  IF (@cpu_poller_enabled IS NOT NULL)
  BEGIN
    EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                           N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                           N'CoreEngineMask',
                                           @existing_core_engine_mask OUTPUT,
                                           N'no_output'

  

    IF ((@existing_core_engine_mask IS NOT NULL) OR (@cpu_poller_enabled = 1))
    BEGIN
      IF (@cpu_poller_enabled = 1)
        SELECT @cpu_poller_enabled = (ISNULL(@existing_core_engine_mask, 0) & ~32)
      ELSE
        SELECT @cpu_poller_enabled = (ISNULL(@existing_core_engine_mask, 0) | 32)

      IF ((@existing_core_engine_mask IS NOT NULL) AND (@cpu_poller_enabled = 32))
        EXECUTE master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE',
                                                      N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                                      N'CoreEngineMask'
      ELSE
        EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                                N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                                N'CoreEngineMask',
                                                N'REG_DWORD',
                                                @cpu_poller_enabled
    END
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_ADD_TARGETSERVERGROUP                                   */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_targetservergroup...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_targetservergroup')
              AND (type = 'P')))
  DROP PROCEDURE sp_add_targetservergroup
go
CREATE PROCEDURE sp_add_targetservergroup
  @name sysname
AS
BEGIN
  SET NOCOUNT ON

  -- Only a sysadmin can do this
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) 
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Remove any leading/trailing spaces from parameters
  SELECT @name = LTRIM(RTRIM(@name))

  -- Check if the group already exists
  IF (EXISTS (SELECT *
              FROM msdb.dbo.systargetservergroups
              WHERE name = @name))
  BEGIN
    RAISERROR(14261, -1, -1, '@name', @name)
    RETURN(1) -- Failure
  END

  -- Disallow names with commas in them (since sp_apply_job_to_targets parses a comma-separated list of group names)
  IF (@name LIKE N'%,%')
  BEGIN
    RAISERROR(14289, -1, -1, '@name', ',')
    RETURN(1) -- Failure
  END

  INSERT INTO msdb.dbo.systargetservergroups (name)
  VALUES (@name)

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_UPDATE_TARGETSERVERGROUP                                */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_update_targetservergroup...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_update_targetservergroup')
              AND (type = 'P')))
  DROP PROCEDURE sp_update_targetservergroup
go
CREATE PROCEDURE sp_update_targetservergroup
  @name     sysname,
  @new_name sysname
AS
BEGIN
  SET NOCOUNT ON

  -- Only a sysadmin can do this
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) 
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Remove any leading/trailing spaces from parameters
  SELECT @name     = LTRIM(RTRIM(@name))
  SELECT @new_name = LTRIM(RTRIM(@new_name))

  -- Check if the group exists
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.systargetservergroups
                  WHERE (name = @name)))
  BEGIN
    RAISERROR(14262, -1, -1, '@name', @name)
    RETURN(1) -- Failure
  END

  -- Check if a group with the new name already exists
  IF (EXISTS (SELECT *
              FROM msdb.dbo.systargetservergroups
              WHERE (name = @new_name)))
  BEGIN
    RAISERROR(14261, -1, -1, '@new_name', @new_name)
    RETURN(1) -- Failure
  END

  -- Disallow names with commas in them (since sp_apply_job_to_targets parses a comma-separated list of group names)
  IF (@new_name LIKE N'%,%')
  BEGIN
    RAISERROR(14289, -1, -1, '@new_name', ',')
    RETURN(1) -- Failure
  END

  -- Update the group's name
  UPDATE msdb.dbo.systargetservergroups
  SET name = @new_name
  WHERE (name = @name)

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_DELETE_TARGETSERVERGROUP                                */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_targetservergroup...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_targetservergroup')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_targetservergroup
go
CREATE PROCEDURE sp_delete_targetservergroup
  @name sysname
AS
BEGIN
  DECLARE @servergroup_id INT

  SET NOCOUNT ON

  -- Only a sysadmin can do this
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) 
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Remove any leading/trailing spaces from parameters
  SELECT @name = LTRIM(RTRIM(@name))

  -- Check if the group exists
  SELECT @servergroup_id = servergroup_id
  FROM msdb.dbo.systargetservergroups
  WHERE (name = @name)

  IF (@servergroup_id IS NULL)
  BEGIN
    RAISERROR(14262, -1, -1, '@name', @name)
    RETURN(1) -- Failure
  END

  -- Remove the group members
  DELETE FROM msdb.dbo.systargetservergroupmembers
  WHERE (servergroup_id = @servergroup_id)

  -- Remove the group
  DELETE FROM msdb.dbo.systargetservergroups
  WHERE (name = @name)

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_HELP_TARGETSERVERGROUP                                  */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_targetservergroup...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_targetservergroup')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_targetservergroup
go
CREATE PROCEDURE sp_help_targetservergroup
  @name sysname = NULL
AS
BEGIN
  DECLARE @servergroup_id INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name = LTRIM(RTRIM(@name))

  IF (@name IS NULL)
  BEGIN
    -- Show all groups
    SELECT servergroup_id, name
    FROM msdb.dbo.systargetservergroups
    RETURN(@@error) -- 0 means success
  END
  ELSE
  BEGIN
    -- Check if the group exists
    SELECT @servergroup_id = servergroup_id
    FROM msdb.dbo.systargetservergroups
    WHERE (name = @name)

    IF (@servergroup_id IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@name', @name)
      RETURN(1) -- Failure
    END

    -- Return the members of the group
    SELECT sts.server_id,
           sts.server_name
    FROM msdb.dbo.systargetservers sts,
         msdb.dbo.systargetservergroupmembers stsgm
    WHERE (stsgm.servergroup_id = @servergroup_id)
      AND (stsgm.server_id = sts.server_id)

    RETURN(@@error) -- 0 means success
  END
END
go

/**************************************************************/
/* SP_ADD_TARGETSVRGRP_MEMBER                                 */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_targetsvgrp_member...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_targetsvrgrp_member')
              AND (type = 'P')))
  DROP PROCEDURE sp_add_targetsvrgrp_member
go
CREATE PROCEDURE sp_add_targetsvrgrp_member
  @group_name  sysname,
  @server_name sysname
AS
BEGIN
  DECLARE @servergroup_id INT
  DECLARE @server_id      INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @group_name = LTRIM(RTRIM(@group_name))
  SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name)))

  -- Check if the group exists
  SELECT @servergroup_id = servergroup_id
  FROM msdb.dbo.systargetservergroups
  WHERE (name = @group_name)

  IF (@servergroup_id IS NULL)
  BEGIN
    RAISERROR(14262, -1, -1, '@group_name', @group_name)
    RETURN(1) -- Failure
  END

  -- Check if the server exists
  SELECT @server_id = server_id
  FROM msdb.dbo.systargetservers
  WHERE (UPPER(server_name) = @server_name)

  IF (@server_id IS NULL)
  BEGIN
    RAISERROR(14262, -1, -1, '@server_name', @server_name)
    RETURN(1) -- Failure
  END

  -- Check if the server is already in this group
  IF (EXISTS (SELECT *
              FROM msdb.dbo.systargetservergroupmembers
              WHERE (servergroup_id = @servergroup_id)
                AND (server_id = @server_id)))
  BEGIN
    RAISERROR(14263, -1, -1, @server_name, @group_name)
    RETURN(1) -- Failure
  END

  -- Add the row to systargetservergroupmembers
  INSERT INTO msdb.dbo.systargetservergroupmembers
  VALUES (@servergroup_id, @server_id)

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_DELETE_TARGETSVRGRP_MEMBER                              */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_targetsvrgrp_member...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_targetsvrgrp_member')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_targetsvrgrp_member
go
CREATE PROCEDURE sp_delete_targetsvrgrp_member
  @group_name  sysname,
  @server_name sysname
AS
BEGIN
  DECLARE @servergroup_id INT
  DECLARE @server_id      INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @group_name = LTRIM(RTRIM(@group_name))
  SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name)))

  -- Check if the group exists
  SELECT @servergroup_id = servergroup_id
  FROM msdb.dbo.systargetservergroups
  WHERE (name = @group_name)

  IF (@servergroup_id IS NULL)
  BEGIN
    RAISERROR(14262, -1, -1, '@group_name', @group_name)
    RETURN(1) -- Failure
  END

  -- Check if the server exists
  SELECT @server_id = server_id
  FROM msdb.dbo.systargetservers
  WHERE (UPPER(server_name) = @server_name)

  IF (@server_id IS NULL)
  BEGIN
    RAISERROR(14262, -1, -1, '@server_name', @server_name)
    RETURN(1) -- Failure
  END

  -- Check if the server is in the group
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.systargetservergroupmembers
                  WHERE (servergroup_id = @servergroup_id)
                    AND (server_id = @server_id)))
  BEGIN
    RAISERROR(14264, -1, -1, @server_name, @group_name)
    RETURN(1) -- Failure
  END

  -- Delete the row from systargetservergroupmembers
  DELETE FROM msdb.dbo.systargetservergroupmembers
  WHERE (servergroup_id = @servergroup_id)
    AND (server_id = @server_id)

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_VERIFY_CATEGORY                                         */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_category...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_category')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_category
go
CREATE PROCEDURE sp_verify_category
  @class          VARCHAR(8),
  @type           VARCHAR(12)  = NULL, -- Supply NULL only if you don't want it checked
  @name           sysname      = NULL, -- Supply NULL only if you don't want it checked
  @category_class INT OUTPUT,
  @category_type  INT OUTPUT           -- Supply NULL only if you don't want the return value
AS
BEGIN
  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @class = LTRIM(RTRIM(@class))
  SELECT @type  = LTRIM(RTRIM(@type))
  SELECT @name  = LTRIM(RTRIM(@name))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@type = '') SELECT @type = NULL
  IF (@name = N'') SELECT @name = NULL

  -- Check class
  SELECT @class = UPPER(@class collate SQL_Latin1_General_CP1_CS_AS)
  SELECT @category_class = CASE @class
                             WHEN 'JOB'      THEN 1
                             WHEN 'ALERT'    THEN 2
                             WHEN 'OPERATOR' THEN 3
                             ELSE 0
                           END
  IF (@category_class = 0)
  BEGIN
    RAISERROR(14266, -1, -1, '@class', 'JOB, ALERT, OPERATOR')
    RETURN(1) -- Failure
  END

  -- Check name
  IF ((@name IS NOT NULL) AND (@name = N'[DEFAULT]'))
  BEGIN
    RAISERROR(14200, -1, -1, '@name')
    RETURN(1) -- Failure
  END

  -- Check type [optionally]
  IF (@type IS NOT NULL)
  BEGIN
    IF (@class = 'JOB')
    BEGIN
      SELECT @type = UPPER(@type collate SQL_Latin1_General_CP1_CS_AS)
      SELECT @category_type = CASE @type
                                WHEN 'LOCAL'        THEN 1
                                WHEN 'MULTI-SERVER' THEN 2
                                ELSE 0
                              END
      IF (@category_type = 0)
      BEGIN
        RAISERROR(14266, -1, -1, '@type', 'LOCAL, MULTI-SERVER')
        RETURN(1) -- Failure
      END
    END
    ELSE
    BEGIN
      IF (@type <> 'NONE')
      BEGIN
        RAISERROR(14266, -1, -1, '@type', 'NONE')
        RETURN(1) -- Failure
      END
      ELSE
        SELECT @category_type = 3
    END
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_ADD_CATEGORY                                            */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_category...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_category')
              AND (type = 'P')))
  DROP PROCEDURE sp_add_category
go
CREATE PROCEDURE sp_add_category
  @class VARCHAR(8)   = 'JOB',   -- JOB or ALERT or OPERATOR
  @type  VARCHAR(12)  = 'LOCAL', -- LOCAL or MULTI-SERVER (for JOB) or NONE otherwise
  @name  sysname
AS
BEGIN
  DECLARE @retval         INT
  DECLARE @category_type  INT
  DECLARE @category_class INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @class = LTRIM(RTRIM(@class))
  SELECT @type  = LTRIM(RTRIM(@type))
  SELECT @name  = LTRIM(RTRIM(@name))

  EXECUTE @retval = sp_verify_category @class,
                                       @type,
                                       @name,
                                       @category_class OUTPUT,
                                       @category_type  OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check name
  IF (EXISTS (SELECT *
              FROM msdb.dbo.syscategories
              WHERE (category_class = @category_class)
                AND (name = @name)))
  BEGIN
    RAISERROR(14261, -1, -1, '@name', @name)
    RETURN(1) -- Failure
  END

  -- Add the row
  INSERT INTO msdb.dbo.syscategories (category_class, category_type, name)
  VALUES (@category_class, @category_type, @name)

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_UPDATE_CATEGORY                                         */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_update_category...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_update_category')
              AND (type = 'P')))
  DROP PROCEDURE sp_update_category
go
CREATE PROCEDURE sp_update_category
  @class    VARCHAR(8),  -- JOB or ALERT or OPERATOR
  @name     sysname,
  @new_name sysname
AS
BEGIN
  DECLARE @retval         INT
  DECLARE @category_id    INT
  DECLARE @category_class INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @class    = LTRIM(RTRIM(@class))
  SELECT @name     = LTRIM(RTRIM(@name))
  SELECT @new_name = LTRIM(RTRIM(@new_name))

  --turn empy parametrs tu null parameters
  IF @name = ''  SELECT @name = NULL

  EXECUTE @retval = sp_verify_category @class,
                                       NULL,
                                       @new_name,
                                       @category_class OUTPUT,
                                       NULL
  IF (@retval <> 0)
    RETURN(1) -- Failure

  --ID @name not null check id such a category exists
  --check name - it should exist if not null
  IF @name IS NOT NULL AND
     NOT EXISTS(SELECT * FROM msdb.dbo.syscategories WHERE name = @name 
      AND category_class = @category_class)
  BEGIN
      RAISERROR(14526, -1, -1, @name, @category_class)
      RETURN(1) -- Failure
  END  

  -- Check name
  SELECT @category_id = category_id
  FROM msdb.dbo.syscategories
  WHERE (category_class = @category_class)
    AND (name = @new_name)
  IF (@category_id IS NOT NULL)
  BEGIN
    RAISERROR(14261, -1, -1, '@new_name', @new_name)
    RETURN(1) -- Failure
  END

  -- Make sure that we're not updating one of the permanent categories (id's 0 - 99)
  IF (@category_id < 100)
  BEGIN
    RAISERROR(14276, -1, -1, @name, @class)
    RETURN(1) -- Failure
  END

  -- Update the category name
  UPDATE msdb.dbo.syscategories
  SET name = @new_name
  WHERE (category_class = @category_class)
    AND (name = @name)

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_DELETE_CATEGORY                                         */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_category...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_category')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_category
go
CREATE PROCEDURE sp_delete_category
  @class VARCHAR(8),  -- JOB or ALERT or OPERATOR
  @name  sysname
AS
BEGIN
  DECLARE @retval         INT
  DECLARE @category_id    INT
  DECLARE @category_class INT
  DECLARE @category_type  INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @class = LTRIM(RTRIM(@class))
  SELECT @name  = LTRIM(RTRIM(@name))

  EXECUTE @retval = sp_verify_category @class,
                                       NULL,
                                       NULL,
                                       @category_class OUTPUT,
                                       NULL
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check name
  SELECT @category_id = category_id,
         @category_type = category_type
  FROM msdb.dbo.syscategories
  WHERE (category_class = @category_class)
    AND (name = @name)
  IF (@category_id IS NULL)
  BEGIN
    RAISERROR(14262, -1, -1, '@name', @name)
    RETURN(1) -- Failure
  END

  -- Make sure that we're not deleting one of the permanent categories (id's 0 - 99)
  IF (@category_id < 100)
  BEGIN
    RAISERROR(14276, -1, -1, @name, @class)
    RETURN(1) -- Failure
  END

  BEGIN TRANSACTION

    -- Clean-up any Jobs that reference the deleted category
    UPDATE msdb.dbo.sysjobs
    SET category_id = CASE @category_type
                        WHEN 1 THEN 0 -- [Uncategorized (Local)]
                        WHEN 2 THEN 2 -- [Uncategorized (Multi-Server)]
                      END
    WHERE (category_id = @category_id)

    -- Clean-up any Alerts that reference the deleted category
    UPDATE msdb.dbo.sysalerts
    SET category_id = 98
    WHERE (category_id = @category_id)

    -- Clean-up any Operators that reference the deleted category
    UPDATE msdb.dbo.sysoperators
    SET category_id = 99
    WHERE (category_id = @category_id)

    -- Finally, delete the category itself
    DELETE FROM msdb.dbo.syscategories
    WHERE (category_id = @category_id)

  COMMIT TRANSACTION

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_HELP_CATEGORY                                           */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_category...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_category')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_category
go
CREATE PROCEDURE sp_help_category
  @class  VARCHAR(8)   = 'JOB', -- JOB, ALERT or OPERATOR
  @type   VARCHAR(12)  = NULL,  -- LOCAL, MULTI-SERVER, or NONE
  @name   sysname      = NULL,
  @suffix BIT          = 0      -- 0 = no suffix, 1 = add suffix
AS
BEGIN
  DECLARE @retval         INT
  DECLARE @type_in        VARCHAR(12)
  DECLARE @category_type  INT
  DECLARE @category_class INT
  DECLARE @where_clause   NVARCHAR(500)
  DECLARE @cmd            NVARCHAR(max)

  SET NOCOUNT ON

  -- Both name and type can be NULL (this is valid, indeed it is how SQLDMO populates
  -- the JobCategory collection)

  -- Remove any leading/trailing spaces from parameters
  SELECT @class = LTRIM(RTRIM(@class))
  SELECT @type  = LTRIM(RTRIM(@type))
  SELECT @name  = LTRIM(RTRIM(@name))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@type = '') SELECT @type = NULL
  IF (@name = N'') SELECT @name = NULL

  -- Check the type and class
  IF (@class = 'JOB') AND (@type IS NULL)
    SELECT @type_in = 'LOCAL' -- This prevents sp_verify_category from failing
  ELSE
  IF (@class <> 'JOB') AND (@type IS NULL)
    SELECT @type_in = 'NONE'
  ELSE
    SELECT @type_in = @type

  EXECUTE @retval = sp_verify_category @class,
                                       @type_in,
                                       NULL,
                                       @category_class OUTPUT,
                                       @category_type  OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Make sure that 'suffix' is either 0 or 1
  IF (@suffix <> 0)
    SELECT @suffix = 1

  --check name - it should exist if not null
  IF @name IS NOT NULL AND
     NOT EXISTS(SELECT * FROM msdb.dbo.syscategories WHERE name = @name
      AND category_class = @category_class)
  BEGIN
      DECLARE @category_class_string NVARCHAR(25)
      SET @category_class_string = CAST(@category_class AS nvarchar(25))
      RAISERROR(14526, -1, -1, @name, @category_class_string)
      RETURN(1) -- Failure
  END
       

  -- Build the WHERE qualifier
  SELECT @where_clause = N'WHERE (category_class = ' + CONVERT(NVARCHAR, @category_class) + N') '
  IF (@name IS NOT NULL)
    SELECT @where_clause = @where_clause + N'AND (name = N' + QUOTENAME(@name, '''') + N') '
  IF (@type IS NOT NULL)
    SELECT @where_clause = @where_clause + N'AND (category_type = ' + CONVERT(NVARCHAR, @category_type) + N') '

  -- Construct the query
  SELECT @cmd = N'SELECT category_id, '
  IF (@suffix = 1)
  BEGIN
    SELECT @cmd = @cmd + N'''category_type'' = '
    SELECT @cmd = @cmd + N'CASE category_type '
    SELECT @cmd = @cmd + N'WHEN 0 THEN ''NONE'' '
    SELECT @cmd = @cmd + N'WHEN 1 THEN ''LOCAL'' '
    SELECT @cmd = @cmd + N'WHEN 2 THEN ''MULTI-SERVER'' '
    SELECT @cmd = @cmd + N'WHEN 3 THEN ''NONE'' '
    SELECT @cmd = @cmd + N'ELSE FORMATMESSAGE(14205) '
    SELECT @cmd = @cmd + N'END, '
  END
  ELSE
  BEGIN
    SELECT @cmd = @cmd + N'category_type, '
  END
  SELECT @cmd = @cmd + N'name '
  SELECT @cmd = @cmd + N'FROM msdb.dbo.syscategories '

  -- Execute the query
  EXECUTE (@cmd + @where_clause + N'ORDER BY category_type, name')

  RETURN(@@error) -- 0 means success
END
go


/**************************************************************/
/* SP_HELP_TARGETSERVER                                       */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_targetserver...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_targetserver')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_targetserver
go
CREATE PROCEDURE sp_help_targetserver
  @server_name sysname = NULL
AS
BEGIN
  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name)))

  IF (@server_name IS NOT NULL)
  BEGIN
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.systargetservers
                    WHERE (UPPER(server_name) = @server_name)))
    BEGIN
      RAISERROR(14262, -1, -1, '@server_name', @server_name)
      RETURN(1) -- Failure
    END
  END

  DECLARE @unread_instructions TABLE
  (
  target_server       sysname COLLATE database_default,
  unread_instructions INT
  )

  INSERT INTO @unread_instructions
  SELECT target_server, COUNT(*)
  FROM msdb.dbo.sysdownloadlist
  WHERE (status = 0)
  GROUP BY target_server

  SELECT sts.server_id,
         sts.server_name,
         sts.location,
         sts.time_zone_adjustment,
         sts.enlist_date,
         sts.last_poll_date,
        'status' = sts.status |
                   CASE WHEN DATEDIFF(ss, sts.last_poll_date, GETDATE()) > (3 * sts.poll_interval) THEN 0x2 ELSE 0 END |
                   CASE WHEN ((SELECT COUNT(*)
                               FROM msdb.dbo.sysdownloadlist sdl
                               WHERE (sdl.target_server = sts.server_name)
                                 AND (sdl.error_message IS NOT NULL)) > 0) THEN 0x4 ELSE 0 END,
        'unread_instructions' = ISNULL(ui.unread_instructions, 0),
        'local_time' = DATEADD(SS, DATEDIFF(SS, sts.last_poll_date, GETDATE()), sts.local_time_at_last_poll),
        sts.enlisted_by_nt_user,
        sts.poll_interval
  FROM msdb.dbo.systargetservers sts LEFT OUTER JOIN
       @unread_instructions      ui  ON (sts.server_name = ui.target_server)
  WHERE ((@server_name IS NULL) OR (UPPER(sts.server_name) = @server_name))
  ORDER BY server_name

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_RESYNC_TARGETSERVER                                     */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_resync_targetserver...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_resync_targetserver')
              AND (type = 'P')))
  DROP PROCEDURE sp_resync_targetserver
go
CREATE PROCEDURE sp_resync_targetserver
  @server_name sysname
AS
BEGIN
  SET NOCOUNT ON

  -- Only a sysadmin can do this
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) 
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Remove any leading/trailing spaces from parameters
  SELECT @server_name = LTRIM(RTRIM(@server_name))

  IF (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) <> N'ALL')
  BEGIN
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.systargetservers
                    WHERE (UPPER(server_name) = UPPER(@server_name))))
    BEGIN
      RAISERROR(14262, -1, -1, '@server_name', @server_name)
      RETURN(1) -- Failure
    END

    -- We want the target server to:
    -- a) delete all their current MSX jobs, and
    -- b) download all their jobs again.
    -- So we delete all the current instructions and post a new set
    DELETE FROM msdb.dbo.sysdownloadlist
    WHERE (target_server = @server_name)
    EXECUTE msdb.dbo.sp_post_msx_operation 'DELETE', 'JOB', 0x00, @server_name
    EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', 0x00, @server_name
  END
  ELSE
  BEGIN
    -- We want ALL target servers to:
    -- a) delete all their current MSX jobs, and
    -- b) download all their jobs again.
    -- So we delete all the current instructions and post a new set
    TRUNCATE TABLE msdb.dbo.sysdownloadlist
    EXECUTE msdb.dbo.sp_post_msx_operation 'DELETE', 'JOB', 0x00, NULL
    EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', 0x00, NULL
  END

  RETURN(@@error) -- 0 means success
END
go

CHECKPOINT
go

/**************************************************************/
/* SP_PURGE_JOBHISTORY                                        */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_purge_jobhistory...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_purge_jobhistory')
              AND (type = 'P')))
  DROP PROCEDURE sp_purge_jobhistory
go
CREATE PROCEDURE sp_purge_jobhistory
  @job_name     sysname          = NULL,
  @job_id       UNIQUEIDENTIFIER = NULL,
  @oldest_date  DATETIME         = NULL
AS
BEGIN
  DECLARE @rows_affected INT
  DECLARE @total_rows    INT
  DECLARE @datepart      INT
  DECLARE @timepart      INT
  DECLARE @retval        INT
  DECLARE @job_owner_sid VARBINARY(85)

  SET NOCOUNT ON

  IF(@oldest_date IS NOT NULL)
  BEGIN
    SET @datepart = CONVERT(INT, CONVERT(VARCHAR, @oldest_date, 112))
    SET @timepart = (DATEPART(hh, @oldest_date) * 10000) + (DATEPART(mi, @oldest_date) * 100) + (DATEPART(ss, @oldest_date))
  END
  ELSE
  BEGIN
    SET @datepart = 99999999
    SET @timepart = 0
  END

  IF ((@job_name IS NOT NULL) OR (@job_id IS NOT NULL))
  BEGIN
    EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                                '@job_id',
                                                 @job_name OUTPUT,
                                                 @job_id   OUTPUT,
                                                 @owner_sid = @job_owner_sid OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure
      
    -- Check permissions beyond what's checked by the sysjobs_view
    -- SQLAgentReader role that can see all jobs but
    -- cannot purge history of jobs they do not own
    IF (@job_owner_sid <> SUSER_SID()                      -- does not own the job
       AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)       -- is not sysadmin
       AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) <> 1)) -- is not SQLAgentOperatorRole
    BEGIN
     RAISERROR(14392, -1, -1);
     RETURN(1) -- Failure
    END

    -- Delete the histories for this job
    DELETE FROM msdb.dbo.sysjobhistory
    WHERE (job_id = @job_id) AND
          ((run_date < @datepart) OR 
           (run_date <= @datepart AND run_time < @timepart))
    SELECT @rows_affected = @@rowcount
  END
  ELSE
  BEGIN
    -- Only a sysadmin or SQLAgentOperatorRole can do this
   IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)           -- is not sysadmin
       AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) <> 1)) -- is not SQLAgentOperatorRole
    BEGIN
      RAISERROR(14392, -1, -1)
      RETURN(1) -- Failure
    END

    IF(@oldest_date IS NOT NULL)
    BEGIN
        DELETE FROM msdb.dbo.sysjobhistory
        WHERE ((run_date < @datepart) OR 
               (run_date <= @datepart AND run_time < @timepart))
    END
    ELSE
    BEGIN
        DELETE FROM msdb.dbo.sysjobhistory
    END
   
   SELECT @rows_affected = @@rowcount
  END

  RAISERROR(14226, 0, 1, @rows_affected)

  RETURN(0) -- Success
END
go


/**************************************************************/
/* SP_HELP_JOBHISTORY_FULL                                    */
/**************************************************************/
use [msdb]

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_jobhistory_full')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_jobhistory_full
go
CREATE PROCEDURE sp_help_jobhistory_full
               @job_id               UNIQUEIDENTIFIER,
               @job_name             sysname,
               @step_id              INT,
               @sql_message_id       INT,
               @sql_severity         INT,
               @start_run_date       INT,
               @end_run_date         INT,
               @start_run_time       INT,
               @end_run_time         INT,
               @minimum_run_duration INT,
               @run_status           INT,
               @minimum_retries      INT,
               @oldest_first         INT,
               @server               sysname,
               @mode                 VARCHAR(7),
               @order_by             INT,
               @distributed_job_history BIT
AS
IF(@distributed_job_history = 1)
  SELECT null as instance_id, 
     sj.job_id,
     job_name = sj.name,
     null as step_id,
     null as step_name,
     null as sql_message_id,
     null as sql_severity,
     sjh.last_outcome_message as message,
     sjh.last_run_outcome as run_status,
     sjh.last_run_date as run_date,
     sjh.last_run_time as run_time,
    sjh.last_run_duration as run_duration,
     null as operator_emailed,
     null as operator_netsentname,
     null as operator_paged,
     null as retries_attempted,
     sts.server_name as server
  FROM msdb.dbo.sysjobservers                sjh
  JOIN msdb.dbo.systargetservers sts ON (sts.server_id = sjh.server_id)
  JOIN msdb.dbo.sysjobs_view     sj  ON(sj.job_id = sjh.job_id)
  WHERE 
  (@job_id = sjh.job_id)
  AND ((@start_run_date       IS NULL) OR (sjh.last_run_date >= @start_run_date))
  AND ((@end_run_date         IS NULL) OR (sjh.last_run_date <= @end_run_date))
  AND ((@start_run_time       IS NULL) OR (sjh.last_run_time >= @start_run_time))
  AND ((@minimum_run_duration IS NULL) OR (sjh.last_run_duration >= @minimum_run_duration))
  AND ((@run_status           IS NULL) OR (@run_status = sjh.last_run_outcome))
  AND ((@server               IS NULL) OR (sts.server_name = @server))
ELSE
  SELECT sjh.instance_id, -- This is included just for ordering purposes
     sj.job_id,
     job_name = sj.name,
     sjh.step_id,
     sjh.step_name,
     sjh.sql_message_id,
     sjh.sql_severity,
     sjh.message,
     sjh.run_status,
     sjh.run_date,
     sjh.run_time,
     sjh.run_duration,
     operator_emailed = so1.name,
     operator_netsent = so2.name,
     operator_paged = so3.name,
     sjh.retries_attempted,
     sjh.server
  FROM msdb.dbo.sysjobhistory                sjh
     LEFT OUTER JOIN msdb.dbo.sysoperators so1  ON (sjh.operator_id_emailed = so1.id)
     LEFT OUTER JOIN msdb.dbo.sysoperators so2  ON (sjh.operator_id_netsent = so2.id)
     LEFT OUTER JOIN msdb.dbo.sysoperators so3  ON (sjh.operator_id_paged = so3.id),
     msdb.dbo.sysjobs_view sj
  WHERE (sj.job_id = sjh.job_id)
  AND ((@job_id               IS NULL) OR (@job_id = sjh.job_id))
  AND ((@step_id              IS NULL) OR (@step_id = sjh.step_id))
  AND ((@sql_message_id       IS NULL) OR (@sql_message_id = sjh.sql_message_id))
  AND ((@sql_severity         IS NULL) OR (@sql_severity = sjh.sql_severity))
  AND ((@start_run_date       IS NULL) OR (sjh.run_date >= @start_run_date))
  AND ((@end_run_date         IS NULL) OR (sjh.run_date <= @end_run_date))
  AND ((@start_run_time       IS NULL) OR (sjh.run_time >= @start_run_time))
  AND ((@end_run_time         IS NULL) OR (sjh.run_time <= @end_run_time))
  AND ((@minimum_run_duration IS NULL) OR (sjh.run_duration >= @minimum_run_duration))
  AND ((@run_status           IS NULL) OR (@run_status = sjh.run_status))
  AND ((@minimum_retries      IS NULL) OR (sjh.retries_attempted >= @minimum_retries))
  AND ((@server               IS NULL) OR (sjh.server = @server))
  ORDER BY (sjh.instance_id * @order_by)

GO

/**************************************************************/
/* SP_HELP_JOBHISTORY_SUMMARY                                 */
/**************************************************************/
use [msdb]

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_jobhistory_summary')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_jobhistory_summary
go
CREATE PROCEDURE sp_help_jobhistory_summary
               @job_id               UNIQUEIDENTIFIER,
               @job_name             sysname,
               @step_id              INT,
               @sql_message_id       INT,
               @sql_severity         INT,
               @start_run_date       INT,
               @end_run_date         INT,
               @start_run_time       INT,
               @end_run_time         INT,
               @minimum_run_duration INT,
               @run_status           INT,
               @minimum_retries      INT,
               @oldest_first         INT,
               @server               sysname,
               @mode                 VARCHAR(7),
               @order_by             INT,
               @distributed_job_history BIT
AS
-- Summary format: same WHERE clause as for full, just a different SELECT list
IF(@distributed_job_history = 1)
  SELECT sj.job_id,
     job_name = sj.name,
     sjh.last_run_outcome as run_status,
     sjh.last_run_date as run_date,
     sjh.last_run_time as run_time,
     sjh.last_run_duration as run_duration,
     null as operator_emailed,
     null as operator_netsentname,
     null as operator_paged,
     null as retries_attempted,
     sts.server_name as server
  FROM msdb.dbo.sysjobservers                sjh
  JOIN msdb.dbo.systargetservers sts ON (sts.server_id = sjh.server_id)
  JOIN msdb.dbo.sysjobs_view     sj  ON(sj.job_id = sjh.job_id)
  WHERE 
  (@job_id = sjh.job_id)
  AND ((@start_run_date       IS NULL) OR (sjh.last_run_date >= @start_run_date))
  AND ((@end_run_date         IS NULL) OR (sjh.last_run_date <= @end_run_date))
  AND ((@start_run_time       IS NULL) OR (sjh.last_run_time >= @start_run_time))
  AND ((@minimum_run_duration IS NULL) OR (sjh.last_run_duration >= @minimum_run_duration))
  AND ((@run_status           IS NULL) OR (@run_status = sjh.last_run_outcome))
  AND ((@server               IS NULL) OR (sts.server_name = @server))
ELSE
  SELECT sj.job_id,
     job_name = sj.name,
     sjh.run_status,
     sjh.run_date,
     sjh.run_time,
     sjh.run_duration,
     operator_emailed = substring(so1.name, 1, 20),
     operator_netsent = substring(so2.name, 1, 20),
     operator_paged = substring(so3.name, 1, 20),
     sjh.retries_attempted,
     sjh.server
  FROM msdb.dbo.sysjobhistory                sjh
     LEFT OUTER JOIN msdb.dbo.sysoperators so1  ON (sjh.operator_id_emailed = so1.id)
     LEFT OUTER JOIN msdb.dbo.sysoperators so2  ON (sjh.operator_id_netsent = so2.id)
     LEFT OUTER JOIN msdb.dbo.sysoperators so3  ON (sjh.operator_id_paged = so3.id),
     msdb.dbo.sysjobs_view                 sj
  WHERE (sj.job_id = sjh.job_id)
  AND ((@job_id               IS NULL) OR (@job_id = sjh.job_id))
  AND ((@step_id              IS NULL) OR (@step_id = sjh.step_id))
  AND ((@sql_message_id       IS NULL) OR (@sql_message_id = sjh.sql_message_id))
  AND ((@sql_severity         IS NULL) OR (@sql_severity = sjh.sql_severity))
  AND ((@start_run_date       IS NULL) OR (sjh.run_date >= @start_run_date))
  AND ((@end_run_date         IS NULL) OR (sjh.run_date <= @end_run_date))
  AND ((@start_run_time       IS NULL) OR (sjh.run_time >= @start_run_time))
  AND ((@end_run_time         IS NULL) OR (sjh.run_time <= @end_run_time))
  AND ((@minimum_run_duration IS NULL) OR (sjh.run_duration >= @minimum_run_duration))
  AND ((@run_status           IS NULL) OR (@run_status = sjh.run_status))
  AND ((@minimum_retries      IS NULL) OR (sjh.retries_attempted >= @minimum_retries))
  AND ((@server               IS NULL) OR (sjh.server = @server))
  ORDER BY (sjh.instance_id * @order_by)

GO

/**************************************************************/
/* SP_HELP_JOBHISTORY_SEM                                     */
/**************************************************************/
use [msdb]

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_jobhistory_sem')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_jobhistory_sem
go

CREATE PROCEDURE sp_help_jobhistory_sem
               @job_id               UNIQUEIDENTIFIER,
               @job_name             sysname,
               @step_id              INT,
               @sql_message_id       INT,
               @sql_severity         INT,
               @start_run_date       INT,
               @end_run_date         INT,
               @start_run_time       INT,
               @end_run_time         INT,
               @minimum_run_duration INT,
               @run_status           INT,
               @minimum_retries      INT,
               @oldest_first         INT,
               @server               sysname,
               @mode                 VARCHAR(7),
               @order_by             INT,
               @distributed_job_history BIT
AS
-- SQL Enterprise Manager format
IF(@distributed_job_history = 1)
  SELECT sj.job_id,
     null as step_name,
     sjh.last_outcome_message as message,
     sjh.last_run_outcome as run_status,
     sjh.last_run_date as run_date,
     sjh.last_run_time as run_time,
     sjh.last_run_duration as run_duration,
     null as operator_emailed,
     null as operator_netsentname,
     null as operator_paged
  FROM msdb.dbo.sysjobservers                sjh
  JOIN msdb.dbo.systargetservers sts ON (sts.server_id = sjh.server_id)
  JOIN msdb.dbo.sysjobs_view     sj  ON(sj.job_id = sjh.job_id)
  WHERE 
  (@job_id = sjh.job_id)
ELSE
  SELECT sjh.step_id,
     sjh.step_name,
     sjh.message,
     sjh.run_status,
     sjh.run_date,
     sjh.run_time,
     sjh.run_duration,
     operator_emailed = so1.name,
     operator_netsent = so2.name,
     operator_paged = so3.name
  FROM msdb.dbo.sysjobhistory                sjh
     LEFT OUTER JOIN msdb.dbo.sysoperators so1  ON (sjh.operator_id_emailed = so1.id)
     LEFT OUTER JOIN msdb.dbo.sysoperators so2  ON (sjh.operator_id_netsent = so2.id)
     LEFT OUTER JOIN msdb.dbo.sysoperators so3  ON (sjh.operator_id_paged = so3.id),
     msdb.dbo.sysjobs_view                 sj
  WHERE (sj.job_id = sjh.job_id)
  AND (@job_id = sjh.job_id)
  ORDER BY (sjh.instance_id * @order_by)
GO

/**************************************************************/
/* SP_HELP_JOBHISTORY                                         */
/**************************************************************/
use [msdb]

PRINT ''
PRINT 'Creating procedure sp_help_jobhistory...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_jobhistory')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_jobhistory
go
CREATE PROCEDURE [dbo].[sp_help_jobhistory]
  @job_id               UNIQUEIDENTIFIER = NULL,
  @job_name             sysname          = NULL,
  @step_id              INT              = NULL,
  @sql_message_id       INT              = NULL,
  @sql_severity         INT              = NULL,
  @start_run_date       INT              = NULL,     -- YYYYMMDD
  @end_run_date         INT              = NULL,     -- YYYYMMDD
  @start_run_time       INT              = NULL,     -- HHMMSS
  @end_run_time         INT              = NULL,     -- HHMMSS
  @minimum_run_duration INT              = NULL,     -- HHMMSS
  @run_status           INT              = NULL,     -- SQLAGENT_EXEC_X code
  @minimum_retries      INT              = NULL,
  @oldest_first         INT              = 0,        -- Or 1
  @server               sysname          = NULL,
  @mode                 VARCHAR(7)       = 'SUMMARY' -- Or 'FULL' or 'SEM'
AS
BEGIN
  DECLARE @retval   INT
  DECLARE @order_by INT  -- Must be INT since it can be -1

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @server   = LTRIM(RTRIM(@server))
  SELECT @mode     = LTRIM(RTRIM(@mode))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@server = N'')   SELECT @server = NULL

  -- Check job id/name (if supplied)
  IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL))
  BEGIN
    EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                                '@job_id',
                                                 @job_name OUTPUT,
                                                 @job_id   OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END

  -- Check @start_run_date
  IF (@start_run_date IS NOT NULL)
  BEGIN
    EXECUTE @retval = sp_verify_job_date @start_run_date, '@start_run_date'
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END

  -- Check @end_run_date
  IF (@end_run_date IS NOT NULL)
  BEGIN
    EXECUTE @retval = sp_verify_job_date @end_run_date, '@end_run_date'
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END

  -- Check @start_run_time
  EXECUTE @retval = sp_verify_job_time @start_run_time, '@start_run_time'
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check @end_run_time
  EXECUTE @retval = sp_verify_job_time @end_run_time, '@end_run_time'
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check @run_status
  IF ((@run_status < 0) OR (@run_status > 5))
  BEGIN
    RAISERROR(14198, -1, -1, '@run_status', '0..5')
    RETURN(1) -- Failure
  END

  -- Check mode
  SELECT @mode = UPPER(@mode collate SQL_Latin1_General_CP1_CS_AS)
  IF (@mode NOT IN ('SUMMARY', 'FULL', 'SEM'))
  BEGIN
    RAISERROR(14266, -1, -1, '@mode', 'SUMMARY, FULL, SEM')
    RETURN(1) -- Failure
  END

  SELECT @order_by = -1
  IF (@oldest_first = 1)
    SELECT @order_by = 1

  DECLARE @distributed_job_history BIT 
  SET @distributed_job_history = 0
  
  IF (@job_id IS NOT NULL) AND ( EXISTS (SELECT *
                              FROM msdb.dbo.sysjobs       sj,
                                 msdb.dbo.sysjobservers sjs
                              WHERE (sj.job_id = sjs.job_id)
                                 AND (sj.job_id = @job_id)
                                 AND (sjs.server_id <> 0)))
   SET @distributed_job_history = 1

  -- Return history information filtered by the supplied parameters.
  -- Having actual queries in subprocedures allows better query plans because query optimizer sniffs correct parameters
  IF (@mode = 'FULL')
  BEGIN
  -- NOTE: SQLDMO relies on the 'FULL' format; ** DO NOT CHANGE IT **
      EXECUTE sp_help_jobhistory_full
         @job_id,
         @job_name,
         @step_id,
         @sql_message_id,
         @sql_severity,
         @start_run_date,
         @end_run_date,
         @start_run_time,
         @end_run_time,
         @minimum_run_duration,
         @run_status,
         @minimum_retries,
         @oldest_first,
         @server,
         @mode,
         @order_by,
         @distributed_job_history
  END
  ELSE
  IF (@mode = 'SUMMARY')
  BEGIN
    -- Summary format: same WHERE clause as for full, just a different SELECT list
    EXECUTE sp_help_jobhistory_summary
         @job_id,
         @job_name,
         @step_id,
         @sql_message_id,
         @sql_severity,
         @start_run_date,
         @end_run_date,
         @start_run_time,
         @end_run_time,
         @minimum_run_duration,
         @run_status,
         @minimum_retries,
         @oldest_first,
         @server,
         @mode,
         @order_by,
         @distributed_job_history
  END
  ELSE
  IF (@mode = 'SEM')
  BEGIN
    -- SQL Enterprise Manager format
    EXECUTE sp_help_jobhistory_sem
         @job_id,
         @job_name,
         @step_id,
         @sql_message_id,
         @sql_severity,
         @start_run_date,
         @end_run_date,
         @start_run_time,
         @end_run_time,
         @minimum_run_duration,
         @run_status,
         @minimum_retries,
         @oldest_first,
         @server,
         @mode,
         @order_by,
         @distributed_job_history
  END
  RETURN(0) -- Success
END
go


/**************************************************************/
/* SP_ADD_JOBSERVER                                           */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_jobserver...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_jobserver')
              AND (type = 'P')))
  DROP PROCEDURE sp_add_jobserver
go

CREATE PROCEDURE sp_add_jobserver
  @job_id         UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name
  @job_name       sysname          = NULL, -- Must provide either this or job_id
  @server_name    sysname         = NULL, -- if NULL will default to serverproperty('ServerName')
  @automatic_post BIT = 1                  -- Flag for SEM use only
AS
BEGIN
  DECLARE @retval                    INT
  DECLARE @server_id                 INT
  DECLARE @job_type                  VARCHAR(12)
  DECLARE @current_job_category_type VARCHAR(12)
  DECLARE @msx_operator_id           INT
  DECLARE @local_server_name         sysname
  DECLARE @is_sysadmin               INT
  DECLARE @job_owner                 sysname
  DECLARE @owner_sid                 VARBINARY(85)
  DECLARE @owner_name                sysname

  SET NOCOUNT ON

  IF (@server_name IS NULL) OR (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) = N'(LOCAL)')
    SELECT @server_name = CONVERT(sysname, SERVERPROPERTY('ServerName'))

  -- Remove any leading/trailing spaces from parameters
  SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name)))

  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- First, check if the server is the local server
  SELECT @local_server_name = CONVERT(NVARCHAR,SERVERPROPERTY ('SERVERNAME'))

  IF (@server_name = UPPER(@local_server_name))
    SELECT @server_name = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))

  -- For a multi-server job...
  IF (@server_name <> UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))))
  BEGIN
    -- 1) Only sysadmin can add a multi-server job
    IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) 
    BEGIN
       RAISERROR(14398, -1, -1);
       RETURN(1) -- Failure
    END

    -- 2) Job must be owned by sysadmin
    SELECT @owner_sid = owner_sid, @owner_name = dbo.SQLAGENT_SUSER_SNAME(owner_sid)
    FROM msdb.dbo.sysjobs
    WHERE (job_id = @job_id)

    IF @owner_sid = 0xFFFFFFFF
    BEGIN
      SELECT @is_sysadmin = 1
    END
    ELSE
    BEGIN
      SELECT @is_sysadmin = 0
      EXECUTE msdb.dbo.sp_sqlagent_has_server_access @login_name = @owner_name, @is_sysadmin_member = @is_sysadmin OUTPUT
    END
    
    IF (@is_sysadmin = 0)
    BEGIN
      RAISERROR(14544, -1, -1, @owner_name, N'sysadmin')
      RETURN(1) -- Failure
    END

    -- 3) Check if any of the TSQL steps have a non-null database_user_name
    IF (EXISTS (SELECT *
                FROM msdb.dbo.sysjobsteps
                WHERE (job_id = @job_id)
                  AND (subsystem = N'TSQL')
                  AND (database_user_name IS NOT NULL)))
    BEGIN
      RAISERROR(14542, -1, -1, N'database_user_name')
      RETURN(1) -- Failure
    END

    SELECT @server_id = server_id
    FROM msdb.dbo.systargetservers
    WHERE (UPPER(server_name) = @server_name)
    IF (@server_id IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@server_name', @server_name)
      RETURN(1) -- Failure
    END
  END
  ELSE
    SELECT @server_id = 0

  -- Check that this job has not already been targeted at this server
  IF (EXISTS (SELECT *
               FROM msdb.dbo.sysjobservers
               WHERE (job_id = @job_id)
                 AND (server_id = @server_id)))
  BEGIN
    RAISERROR(14269, -1, -1, @job_name, @server_name)
    RETURN(1) -- Failure
  END

  -- Prevent the job from being targeted at both the local AND remote servers
  SELECT @job_type = 'UNKNOWN'
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers
              WHERE (job_id = @job_id)
                AND (server_id = 0)))
    SELECT @job_type = 'LOCAL'
  ELSE
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers
              WHERE (job_id = @job_id)
                AND (server_id <> 0)))
    SELECT @job_type = 'MULTI-SERVER'

  IF ((@server_id = 0) AND (@job_type = 'MULTI-SERVER'))
  BEGIN
    RAISERROR(14290, -1, -1)
    RETURN(1) -- Failure
  END
  IF ((@server_id <> 0) AND (@job_type = 'LOCAL'))
  BEGIN
    RAISERROR(14291, -1, -1)
    RETURN(1) -- Failure
  END

  -- For a multi-server job, check that any notifications are to the MSXOperator
  IF (@job_type = 'MULTI-SERVER')
  BEGIN
    SELECT @msx_operator_id = id
    FROM msdb.dbo.sysoperators
    WHERE (name = N'MSXOperator')

    IF (EXISTS (SELECT *
                FROM msdb.dbo.sysjobs
                WHERE (job_id = @job_id)
                  AND (((notify_email_operator_id <> 0)   AND (notify_email_operator_id <> @msx_operator_id)) OR
                       ((notify_page_operator_id <> 0)    AND (notify_page_operator_id <> @msx_operator_id))  OR
                       ((notify_netsend_operator_id <> 0) AND (notify_netsend_operator_id <> @msx_operator_id)))))
    BEGIN
      RAISERROR(14221, -1, -1, 'MSXOperator')
      RETURN(1) -- Failure
    END
  END

  -- Insert the sysjobservers row
  INSERT INTO msdb.dbo.sysjobservers
         (job_id,
          server_id,
          last_run_outcome,
          last_outcome_message,
          last_run_date,
          last_run_time,
          last_run_duration)
  VALUES (@job_id,
          @server_id,
          5,  -- ie. SQLAGENT_EXEC_UNKNOWN (can't use 0 since this is SQLAGENT_EXEC_FAIL)
          NULL,
          0,
          0,
          0)

  -- Re-categorize the job (if necessary)
  SELECT @current_job_category_type = CASE category_type
                                        WHEN 1 THEN 'LOCAL'
                                        WHEN 2 THEN 'MULTI-SERVER'
                                      END
  FROM msdb.dbo.sysjobs_view  sjv,
       msdb.dbo.syscategories sc
  WHERE (sjv.category_id = sc.category_id)
    AND (sjv.job_id = @job_id)

  IF (@server_id = 0) AND (@current_job_category_type = 'MULTI-SERVER')
  BEGIN
    UPDATE msdb.dbo.sysjobs
    SET category_id = 0 -- [Uncategorized (Local)]
    WHERE (job_id = @job_id)
  END
  IF (@server_id <> 0) AND (@current_job_category_type = 'LOCAL')
  BEGIN
    UPDATE msdb.dbo.sysjobs
    SET category_id = 2 -- [Uncategorized (Multi-Server)]
    WHERE (job_id = @job_id)
  END

  -- Instruct the new server to pick up the job
  IF (@automatic_post = 1)
    EXECUTE @retval = sp_post_msx_operation 'INSERT', 'JOB', @job_id, @server_name

  -- If the job is local, make sure that SQLServerAgent caches it
  IF (@server_id = 0)
  BEGIN
    EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'J',
                                        @job_id      = @job_id,
                                        @action_type = N'I'
  END

  RETURN(@retval) -- 0 means success
END
go

/**************************************************************/
/* SP_DELETE_JOBSERVER                                        */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_jobserver...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_jobserver')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_jobserver
go
CREATE PROCEDURE sp_delete_jobserver
  @job_id      UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name
  @job_name    sysname          = NULL, -- Must provide either this or job_id
  @server_name sysname
AS
BEGIN
  DECLARE @retval             INT
  DECLARE @server_id          INT
  DECLARE @local_machine_name sysname

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @server_name = LTRIM(RTRIM(@server_name))

  IF (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) = '(LOCAL)')
    SELECT @server_name = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))

  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- First, check if the server is the local server
  EXECUTE @retval = master.dbo.xp_getnetname @local_machine_name OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure
  IF (@local_machine_name IS NOT NULL) AND (UPPER(@server_name) = UPPER(@local_machine_name))
    SELECT @server_name = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))

  -- Check server name
  IF (UPPER(@server_name) <> UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))))
  BEGIN
    SELECT @server_id = server_id
    FROM msdb.dbo.systargetservers
    WHERE (UPPER(server_name) = @server_name)
    IF (@server_id IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@server_name', @server_name)
      RETURN(1) -- Failure
    END
  END
  ELSE
    SELECT @server_id = 0

  -- Check that the job is indeed targeted at the server
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysjobservers
                  WHERE (job_id = @job_id)
                    AND (server_id = @server_id)))
  BEGIN
    RAISERROR(14270, -1, -1, @job_name, @server_name)
    RETURN(1) -- Failure
  END

  -- Instruct the deleted server to purge the job
  -- NOTE: We must do this BEFORE we delete the sysjobservers row
  EXECUTE @retval = sp_post_msx_operation 'DELETE', 'JOB', @job_id, @server_name

  -- Delete the sysjobservers row
  DELETE FROM msdb.dbo.sysjobservers
  WHERE (job_id = @job_id)
    AND (server_id = @server_id)

  -- We used to change the category_id to 0 when removing the last job server
  -- from a job. We no longer do this.
--  IF (NOT EXISTS (SELECT *
--                  FROM msdb.dbo.sysjobservers
--                  WHERE (job_id = @job_id)))
--  BEGIN
--    UPDATE msdb.dbo.sysjobs
--    SET category_id = 0 -- [Uncategorized (Local)]
--    WHERE (job_id = @job_id)
--  END

  -- If the job is local, make sure that SQLServerAgent removes it from cache
  IF (@server_id = 0)
  BEGIN
    EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'J',
                                        @job_id      = @job_id,
                                        @action_type = N'D'
  END

  RETURN(@retval) -- 0 means success
END
go

/**************************************************************/
/* SP_HELP_JOBSERVER                                          */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_jobserver...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_jobserver')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_jobserver
go
CREATE PROCEDURE sp_help_jobserver
  @job_id                UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name
  @job_name              sysname          = NULL, -- Must provide either this or job_id
  @show_last_run_details TINYINT          = 0     -- Controls if last-run execution information is part of the result set (1 = yes, 0 = no)
AS
BEGIN
  DECLARE @retval INT

  SET NOCOUNT ON

  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- The show-last-run-details flag must be either 1 or 0
  IF (@show_last_run_details <> 0)
    SELECT @show_last_run_details = 1

  IF (@show_last_run_details = 1)
  BEGIN
    -- List the servers that @job_name has been targeted at (INCLUDING last-run details)
    SELECT stsv.server_id,
           stsv.server_name,
           stsv.enlist_date,
           stsv.last_poll_date,
           sjs.last_run_date,
           sjs.last_run_time,
           sjs.last_run_duration,
           sjs.last_run_outcome,  -- Same as JOB_OUTCOME_CODE (SQLAGENT_EXEC_x)
           sjs.last_outcome_message
    FROM msdb.dbo.sysjobservers         sjs  LEFT OUTER JOIN
         msdb.dbo.systargetservers_view stsv ON (sjs.server_id = stsv.server_id)
    WHERE (sjs.job_id = @job_id)
  END
  ELSE
  BEGIN
    -- List the servers that @job_name has been targeted at (EXCLUDING last-run details)
    SELECT stsv.server_id,
           stsv.server_name,
           stsv.enlist_date,
           stsv.last_poll_date
    FROM msdb.dbo.sysjobservers         sjs  LEFT OUTER JOIN
         msdb.dbo.systargetservers_view stsv ON (sjs.server_id = stsv.server_id)
    WHERE (sjs.job_id = @job_id)
  END

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_HELP_DOWNLOADLIST                                       */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_downloadlist...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_downloadlist')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_downloadlist
go
CREATE PROCEDURE sp_help_downloadlist
  @job_id          UNIQUEIDENTIFIER = NULL, -- If provided must NOT also provide job_name
  @job_name        sysname          = NULL, -- If provided must NOT also provide job_id
  @operation       VARCHAR(64)      = NULL,
  @object_type     VARCHAR(64)      = NULL, -- Only 'JOB' or 'SERVER' are valid in 7.0
  @object_name     sysname          = NULL,
  @target_server   sysname         = NULL,
  @has_error       TINYINT          = NULL, -- NULL or 1
  @status          TINYINT          = NULL,
  @date_posted     DATETIME         = NULL  -- Include all entries made on OR AFTER this date
AS
BEGIN
  DECLARE @retval         INT
  DECLARE @operation_code INT
  DECLARE @object_type_id TINYINT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @operation     = LTRIM(RTRIM(@operation))
  SELECT @object_type   = LTRIM(RTRIM(@object_type))
  SELECT @object_name   = LTRIM(RTRIM(@object_name))
  SELECT @target_server = UPPER(LTRIM(RTRIM(@target_server)))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@operation     = '') SELECT @operation = NULL
  IF (@object_type   = '') SELECT @object_type = NULL
  IF (@object_name   = N'') SELECT @object_name = NULL
  IF (@target_server = N'') SELECT @target_server = NULL

  IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL))
  BEGIN
    EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                                '@job_id',
                                                 @job_name OUTPUT,
                                                 @job_id   OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END

  -- Check operation
  IF (@operation IS NOT NULL)
  BEGIN
    SELECT @operation = UPPER(@operation collate SQL_Latin1_General_CP1_CS_AS)
    SELECT @operation_code = CASE @operation
                               WHEN 'INSERT'    THEN 1
                               WHEN 'UPDATE'    THEN 2
                               WHEN 'DELETE'    THEN 3
                               WHEN 'START'     THEN 4
                               WHEN 'STOP'      THEN 5
                               WHEN 'RE-ENLIST' THEN 6
                               WHEN 'DEFECT'    THEN 7
                               WHEN 'SYNC-TIME' THEN 8
                               WHEN 'SET-POLL'  THEN 9
                               ELSE 0
                             END
    IF (@operation_code = 0)
    BEGIN
      RAISERROR(14266, -1, -1, '@operation_code', 'INSERT, UPDATE, DELETE, START, STOP, RE-ENLIST, DEFECT, SYNC-TIME, SET-POLL')
      RETURN(1) -- Failure
    END
  END

  -- Check object type (in 7.0 only 'JOB' and 'SERVER' are valid)
  IF (@object_type IS NOT NULL)
  BEGIN
    SELECT @object_type = UPPER(@object_type collate SQL_Latin1_General_CP1_CS_AS)
    IF ((@object_type <> 'JOB') AND (@object_type <> 'SERVER'))
    BEGIN
      RAISERROR(14266, -1, -1, '@object_type', 'JOB, SERVER')
      RETURN(1) -- Failure
    END
    ELSE
      SELECT @object_type_id = CASE @object_type
                                 WHEN 'JOB'    THEN 1
                                 WHEN 'SERVER' THEN 2
                                 ELSE 0
                               END
  END

  -- If object-type is supplied then object-name must also be supplied
  IF ((@object_type IS NOT NULL) AND (@object_name IS NULL)) OR
     ((@object_type IS NULL)     AND (@object_name IS NOT NULL))
  BEGIN
    RAISERROR(14272, -1, -1)
    RETURN(1) -- Failure
  END

  -- Check target server
  IF (@target_server IS NOT NULL) AND NOT EXISTS (SELECT *
                                                  FROM msdb.dbo.systargetservers
                                                  WHERE UPPER(server_name) = @target_server)
  BEGIN
    RAISERROR(14262, -1, -1, '@target_server', @target_server)
    RETURN(1) -- Failure
  END

  -- Check has-error
  IF (@has_error IS NOT NULL) AND (@has_error <> 1)
  BEGIN
    RAISERROR(14266, -1, -1, '@has_error', '1, NULL')
    RETURN(1) -- Failure
  END

  -- Check status
  IF (@status IS NOT NULL) AND (@status <> 0) AND (@status <> 1)
  BEGIN
    RAISERROR(14266, -1, -1, '@status', '0, 1')
    RETURN(1) -- Failure
  END

  -- Return the result set
  SELECT sdl.instance_id,
         sdl.source_server,
        'operation_code' = CASE sdl.operation_code
                             WHEN 1 THEN '1 (INSERT)'
                             WHEN 2 THEN '2 (UPDATE)'
                             WHEN 3 THEN '3 (DELETE)'
                             WHEN 4 THEN '4 (START)'
                             WHEN 5 THEN '5 (STOP)'
                             WHEN 6 THEN '6 (RE-ENLIST)'
                             WHEN 7 THEN '7 (DEFECT)'
                             WHEN 8 THEN '8 (SYNC-TIME)'
                             WHEN 9 THEN '9 (SET-POLL)'
                             ELSE CONVERT(VARCHAR, sdl.operation_code) + ' ' + FORMATMESSAGE(14205)
                           END,
        'object_name' = ISNULL(sjv.name, CASE
                                           WHEN (sdl.operation_code >= 1) AND (sdl.operation_code <= 5) AND (sdl.object_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) THEN FORMATMESSAGE(14212) -- '(all jobs)'
                                           WHEN (sdl.operation_code  = 3) AND (sdl.object_id <> CONVERT(UNIQUEIDENTIFIER, 0x00)) THEN sdl.deleted_object_name -- Special case handling for a deleted job
                                           WHEN (sdl.operation_code >= 1) AND (sdl.operation_code <= 5) AND (sdl.object_id <> CONVERT(UNIQUEIDENTIFIER, 0x00)) THEN FORMATMESSAGE(14580) -- 'job' (safety belt: should never appear)
                                           WHEN (sdl.operation_code >= 6) AND (sdl.operation_code <= 9) THEN sdl.target_server
                                           ELSE FORMATMESSAGE(14205)
                                         END),
        'object_id' = ISNULL(sjv.job_id, CASE sdl.object_id
                                           WHEN CONVERT(UNIQUEIDENTIFIER, 0x00) THEN CONVERT(UNIQUEIDENTIFIER, 0x00)
                                           ELSE sdl.object_id
                                         END),
         sdl.target_server,
         sdl.error_message,
         sdl.date_posted,
         sdl.date_downloaded,
         sdl.status
  FROM msdb.dbo.sysdownloadlist sdl LEFT OUTER JOIN
       msdb.dbo.sysjobs_view    sjv ON (sdl.object_id = sjv.job_id)
  WHERE ((@operation_code IS NULL) OR (operation_code = @operation_code))
    AND ((@object_type_id IS NULL) OR (object_type = @object_type_id))
    AND ((@job_id         IS NULL) OR (object_id = @job_id))
    AND ((@target_server  IS NULL) OR (target_server = @target_server))
    AND ((@has_error      IS NULL) OR (DATALENGTH(error_message) >= 1 * @has_error))
    AND ((@status         IS NULL) OR (status = @status))
    AND ((@date_posted    IS NULL) OR (date_posted >= @date_posted))
  ORDER BY sdl.instance_id

  RETURN(@@error) -- 0 means success

END
go

/**************************************************************/
/* SP_ENUM_SQLAGENT_SUBSYSTEMS_INTERNAL                       */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_enum_sqlagent_subsystems_internal...'
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_enum_sqlagent_subsystems_internal')
              AND (type = 'P')))
  DROP PROCEDURE sp_enum_sqlagent_subsystems_internal
go
CREATE PROCEDURE sp_enum_sqlagent_subsystems_internal
   @syssubsytems_refresh_needed BIT = 0
AS
BEGIN
  DECLARE @retval INT
  SET NOCOUNT ON
  -- this call will populate subsystems table if necessary
  EXEC @retval = msdb.dbo.sp_verify_subsystems @syssubsytems_refresh_needed
  IF @retval <> 0
     RETURN(@retval)

  -- Check if replication is installed
  DECLARE @replication_installed INT
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\Replication',
                                         N'IsInstalled',
                                         @replication_installed OUTPUT,
                                         N'no_output'
  SELECT @replication_installed = ISNULL(@replication_installed, 0)

  IF @replication_installed = 0
      SELECT  subsystem,
            description = FORMATMESSAGE(description_id),
            subsystem_dll,
            agent_exe,
            start_entry_point,
            event_entry_point,
            stop_entry_point,
            max_worker_threads,
            subsystem_id
      FROM syssubsystems
      WHERE (subsystem NOT IN (N'Distribution', N'LogReader', N'Merge', N'Snapshot', N'QueueReader'))
      ORDER by subsystem
   ELSE
      SELECT  subsystem,
            description = FORMATMESSAGE(description_id),
            subsystem_dll,
            agent_exe,
            start_entry_point,
            event_entry_point,
            stop_entry_point,
            max_worker_threads,
            subsystem_id
      FROM syssubsystems
      ORDER by subsystem_id
      
  RETURN(0)      
END
go

/**************************************************************/
/* SP_ENUM_SQLAGENT_SUBSYSTEMS                                */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_enum_sqlagent_subsystems...'
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_enum_sqlagent_subsystems')
              AND (type = 'P')))
  DROP PROCEDURE sp_enum_sqlagent_subsystems
go
CREATE PROCEDURE sp_enum_sqlagent_subsystems
AS
BEGIN
  DECLARE @retval         INT
  EXEC @retval = msdb.dbo.sp_enum_sqlagent_subsystems_internal
  RETURN(@retval)
END
go


/**************************************************************/
/* SP_VERIFY_SUBSYSTEM                                        */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_subsystem...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_subsystem')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_subsystem
go
CREATE PROCEDURE sp_verify_subsystem
  @subsystem NVARCHAR(40)
AS
BEGIN
  DECLARE @retval         INT
  SET NOCOUNT ON

  -- this call will populate subsystems table if necessary
  EXEC @retval = msdb.dbo.sp_verify_subsystems
  IF @retval <> 0
     RETURN(@retval)

  -- Remove any leading/trailing spaces from parameters
  SELECT @subsystem = LTRIM(RTRIM(@subsystem))

  -- Make sure Dts is translated into new subsystem's name SSIS
  IF (@subsystem IS NOT NULL AND UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DTS')
  BEGIN
    SET @subsystem = N'SSIS'
  END

  IF EXISTS (SELECT * FROM syssubsystems 
          WHERE  UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) =
                 UPPER(subsystem collate SQL_Latin1_General_CP1_CS_AS))
    RETURN(0) -- Success
  ELSE
  BEGIN
    RAISERROR(14234, -1, -1, '@subsystem', 'sp_enum_sqlagent_subsystems')
    RETURN(1) -- Failure
  END
END
go

/**************************************************************/
/* SP_VERIFY_SCHEDULE                                         */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_schedule...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_schedule')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_schedule
go
CREATE PROCEDURE sp_verify_schedule
  @schedule_id            INT,
  @name                   sysname,
  @enabled                TINYINT,
  @freq_type              INT,          
  @freq_interval          INT OUTPUT,   -- Output because we may set it to 0 if Frequency Type is one-time or auto-start
  @freq_subday_type       INT OUTPUT,   -- As above
  @freq_subday_interval   INT OUTPUT,   -- As above
  @freq_relative_interval INT OUTPUT,   -- As above
  @freq_recurrence_factor INT OUTPUT,   -- As above
  @active_start_date      INT OUTPUT,
  @active_start_time      INT OUTPUT,
  @active_end_date        INT OUTPUT,
  @active_end_time        INT OUTPUT,
  @owner_sid              VARBINARY(85) --Must be a valid sid. Will fail if this is NULL
AS
BEGIN
  DECLARE @return_code             INT
  DECLARE @res_valid_range         NVARCHAR(100)
  DECLARE @reason                  NVARCHAR(200)
  DECLARE @isAdmin                 INT
  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name = LTRIM(RTRIM(@name))

  -- Make sure that NULL input/output parameters - if NULL - are initialized to 0
  SELECT @freq_interval          = ISNULL(@freq_interval, 0)
  SELECT @freq_subday_type       = ISNULL(@freq_subday_type, 0)
  SELECT @freq_subday_interval   = ISNULL(@freq_subday_interval, 0)
  SELECT @freq_relative_interval = ISNULL(@freq_relative_interval, 0)
  SELECT @freq_recurrence_factor = ISNULL(@freq_recurrence_factor, 0)
  SELECT @active_start_date      = ISNULL(@active_start_date, 0)
  SELECT @active_start_time      = ISNULL(@active_start_time, 0)
  SELECT @active_end_date        = ISNULL(@active_end_date, 0)
  SELECT @active_end_time        = ISNULL(@active_end_time, 0)


  -- Check owner 
  IF(ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)
    SELECT @isAdmin = 1
  ELSE
    SELECT @isAdmin = 0


  -- If a non-sa is [illegally] trying to create a schedule for another user then raise an error
  IF ((@isAdmin <> 1) AND 
      (ISNULL(IS_MEMBER('SQLAgentOperatorRole'),0) <> 1 AND @schedule_id IS NULL) AND
      (@owner_sid <> SUSER_SID()))
  BEGIN
     RAISERROR(14366, -1, -1)
     RETURN(1) -- Failure
  END


  -- Now just check that the login id is valid (ie. it exists and isn't an NT group)
  IF (@owner_sid <> 0x010100000000000512000000) AND -- NT AUTHORITY\SYSTEM sid
     (@owner_sid <> 0x010100000000000514000000)     -- NT AUTHORITY\NETWORK SERVICE sid
  BEGIN
     IF (@owner_sid IS NULL) OR (EXISTS (SELECT *
                                      FROM master.dbo.syslogins
                                      WHERE (sid = @owner_sid)
                                      AND (isntgroup <> 0)))
     BEGIN
       -- NOTE: In the following message we quote @owner_login_name instead of @owner_sid
       --       since this is the parameter the user passed to the calling SP (ie. either
       --       sp_add_schedule, sp_add_job and sp_update_job)
       SELECT @res_valid_range = FORMATMESSAGE(14203)
       RAISERROR(14234, -1, -1, '@owner_login_name', @res_valid_range)
       RETURN(1) -- Failure
     END
  END
  
  -- Verify name (we disallow schedules called 'ALL' since this has special meaning in sp_delete_jobschedules)
  IF (UPPER(@name collate SQL_Latin1_General_CP1_CS_AS) = N'ALL')
  BEGIN
    RAISERROR(14200, -1, -1, '@name')
    RETURN(1) -- Failure
  END

  -- Verify enabled state
  IF (@enabled <> 0) AND (@enabled <> 1)
  BEGIN
    RAISERROR(14266, -1, -1, '@enabled', '0, 1')
    RETURN(1) -- Failure
  END

  -- Verify frequency type
  IF (@freq_type = 0x2) -- OnDemand is no longer supported
  BEGIN
    RAISERROR(14295, -1, -1)
    RETURN(1) -- Failure
  END
  IF (@freq_type NOT IN (0x1, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80))
  BEGIN
    RAISERROR(14266, -1, -1, '@freq_type', '1, 4, 8, 16, 32, 64, 128')
    RETURN(1) -- Failure
  END

  -- Verify frequency sub-day type
  IF (@freq_subday_type <> 0) AND (@freq_subday_type NOT IN (0x1, 0x2, 0x4, 0x8))
  BEGIN
    RAISERROR(14266, -1, -1, '@freq_subday_type', '0x1, 0x2, 0x4, 0x8')
    RETURN(1) -- Failure
  END

  -- Default active start/end date/times (if not supplied, or supplied as NULLs or 0)
  IF (@active_start_date = 0)
    SELECT @active_start_date = DATEPART(yy, GETDATE()) * 10000 +
                                DATEPART(mm, GETDATE()) * 100 +
                                DATEPART(dd, GETDATE()) -- This is an ISO format: "yyyymmdd"
  IF (@active_end_date = 0)
    SELECT @active_end_date = 99991231  -- December 31st 9999
  IF (@active_start_time = 0)
    SELECT @active_start_time = 000000  -- 12:00:00 am
  IF (@active_end_time = 0)
    SELECT @active_end_time = 235959    -- 11:59:59 pm

  -- Verify active start/end dates
  IF (@active_end_date = 0)
    SELECT @active_end_date = 99991231

  EXECUTE @return_code = sp_verify_job_date @active_end_date, '@active_end_date'
  IF (@return_code <> 0)
    RETURN(1) -- Failure

  EXECUTE @return_code = sp_verify_job_date @active_start_date, '@active_start_date'
  IF (@return_code <> 0)
    RETURN(1) -- Failure

  IF (@active_end_date < @active_start_date)
  BEGIN
    RAISERROR(14288, -1, -1, '@active_end_date', '@active_start_date')
    RETURN(1) -- Failure
  END

  EXECUTE @return_code = sp_verify_job_time @active_end_time, '@active_end_time'
  IF (@return_code <> 0)
    RETURN(1) -- Failure

  EXECUTE @return_code = sp_verify_job_time @active_start_time, '@active_start_time'
  IF (@return_code <> 0)
    RETURN(1) -- Failure

  -- NOTE: It's valid for active_end_time to be less than active_start_time since in this
  --       case we assume that the user wants the active time zone to span midnight.
  --       But it's not valid for active_start_date and active_end_date to be the same for recurring sec/hour/minute schedules

  IF (@active_start_time = @active_end_time and (@freq_subday_type in (0x2, 0x4, 0x8)))
  BEGIN
    SELECT @res_valid_range = FORMATMESSAGE(14202)
    RAISERROR(14266, -1, -1, '@active_end_time', @res_valid_range)
    RETURN(1) -- Failure
  END

  -- NOTE: The rest of this procedure is a SQL implementation of VerifySchedule in job.c

  IF ((@freq_type = 0x1) OR  -- FREQTYPE_ONETIME
      (@freq_type = 0x40) OR -- FREQTYPE_AUTOSTART
      (@freq_type = 0x80))   -- FREQTYPE_ONIDLE
  BEGIN
    -- Set standard defaults for non-required parameters
    SELECT @freq_interval          = 0
    SELECT @freq_subday_type       = 0
    SELECT @freq_subday_interval   = 0
    SELECT @freq_relative_interval = 0
    SELECT @freq_recurrence_factor = 0

    -- Check that a one-time schedule isn't already in the past
    -- Bug 442883: let the creation of the one-time schedule succeed but leave a disabled schedule
    /*
    IF (@freq_type = 0x1) -- FREQTYPE_ONETIME
    BEGIN
      DECLARE @current_date INT
      DECLARE @current_time INT

      -- This is an ISO format: "yyyymmdd"
      SELECT @current_date = CONVERT(INT, CONVERT(VARCHAR, GETDATE(), 112))
      SELECT @current_time = (DATEPART(hh, GETDATE()) * 10000) + (DATEPART(mi, GETDATE()) * 100) + DATEPART(ss, GETDATE())
      IF (@active_start_date < @current_date) OR ((@active_start_date = @current_date) AND (@active_start_time <= @current_time))
      BEGIN
        SELECT @res_valid_range = '> ' + CONVERT(VARCHAR, @current_date) + ' / ' + CONVERT(VARCHAR, @current_time)
        SELECT @reason = '@active_start_date = ' + CONVERT(VARCHAR, @active_start_date) + ' / @active_start_time = ' + CONVERT(VARCHAR, @active_start_time)
        RAISERROR(14266, -1, -1, @reason, @res_valid_range)
        RETURN(1) -- Failure
      END
    END
    */

    GOTO ExitProc
  END

  -- Safety net: If the sub-day-type is 0 (and we know that the schedule is not a one-time or
  --             auto-start) then set it to 1 (FREQSUBTYPE_ONCE).  If the user wanted something
  --             other than ONCE then they should have explicitly set @freq_subday_type.
  IF (@freq_subday_type = 0)
    SELECT @freq_subday_type = 0x1 -- FREQSUBTYPE_ONCE

  IF ((@freq_subday_type <> 0x1) AND  -- FREQSUBTYPE_ONCE   (see qsched.h)
      (@freq_subday_type <> 0x2) AND  -- FREQSUBTYPE_SECOND (see qsched.h)
      (@freq_subday_type <> 0x4) AND  -- FREQSUBTYPE_MINUTE (see qsched.h)
      (@freq_subday_type <> 0x8))     -- FREQSUBTYPE_HOUR   (see qsched.h)
  BEGIN
    SELECT @reason = FORMATMESSAGE(14266, '@freq_subday_type', '0x1, 0x2, 0x4, 0x8')
    RAISERROR(14278, -1, -1, @reason)
    RETURN(1) -- Failure
  END

  IF ((@freq_subday_type <> 0x1) AND (@freq_subday_interval < 1)) -- FREQSUBTYPE_ONCE and less than 1 interval
     OR
     ((@freq_subday_type = 0x2) AND (@freq_subday_interval < 10)) -- FREQSUBTYPE_SECOND and less than 10 seconds (see MIN_SCHEDULE_GRANULARITY in SqlAgent source code)
  BEGIN
    SELECT @reason = FORMATMESSAGE(14200, '@freq_subday_interval')
    RAISERROR(14278, -1, -1, @reason)
    RETURN(1) -- Failure
  END

  IF (@freq_type = 0x4)      -- FREQTYPE_DAILY
  BEGIN
    SELECT @freq_recurrence_factor = 0
    IF (@freq_interval < 1)
    BEGIN
      SELECT @reason = FORMATMESSAGE(14572)
      RAISERROR(14278, -1, -1, @reason)
      RETURN(1) -- Failure
    END
  END

  IF (@freq_type = 0x8)      -- FREQTYPE_WEEKLY
  BEGIN
    IF (@freq_interval < 1)   OR
       (@freq_interval > 127) -- (2^7)-1 [freq_interval is a bitmap (Sun=1..Sat=64)]
    BEGIN
      SELECT @reason = FORMATMESSAGE(14573)
      RAISERROR(14278, -1, -1, @reason)
      RETURN(1) -- Failure
    END

  END

  IF (@freq_type = 0x10)    -- FREQTYPE_MONTHLY
  BEGIN
    IF (@freq_interval < 1)  OR
       (@freq_interval > 31)
    BEGIN
      SELECT @reason = FORMATMESSAGE(14574)
      RAISERROR(14278, -1, -1, @reason)
      RETURN(1) -- Failure
    END

  END

  IF (@freq_type = 0x20)     -- FREQTYPE_MONTHLYRELATIVE
  BEGIN
    IF (@freq_relative_interval <> 0x01) AND  -- RELINT_1ST
       (@freq_relative_interval <> 0x02) AND  -- RELINT_2ND
       (@freq_relative_interval <> 0x04) AND  -- RELINT_3RD
       (@freq_relative_interval <> 0x08) AND  -- RELINT_4TH
       (@freq_relative_interval <> 0x10)      -- RELINT_LAST
    BEGIN
      SELECT @reason = FORMATMESSAGE(14575)
      RAISERROR(14278, -1, -1, @reason)
      RETURN(1) -- Failure
    END
  END

  IF (@freq_type = 0x20)     -- FREQTYPE_MONTHLYRELATIVE
  BEGIN
    IF (@freq_interval <> 01) AND -- RELATIVE_SUN
       (@freq_interval <> 02) AND -- RELATIVE_MON
       (@freq_interval <> 03) AND -- RELATIVE_TUE
       (@freq_interval <> 04) AND -- RELATIVE_WED
       (@freq_interval <> 05) AND -- RELATIVE_THU
       (@freq_interval <> 06) AND -- RELATIVE_FRI
       (@freq_interval <> 07) AND -- RELATIVE_SAT
       (@freq_interval <> 08) AND -- RELATIVE_DAY
       (@freq_interval <> 09) AND -- RELATIVE_WEEKDAY
       (@freq_interval <> 10)     -- RELATIVE_WEEKENDDAY
    BEGIN
      SELECT @reason = FORMATMESSAGE(14576)
      RAISERROR(14278, -1, -1, @reason)
      RETURN(1) -- Failure
    END
  END

  IF ((@freq_type = 0x08)  OR   -- FREQTYPE_WEEKLY
      (@freq_type = 0x10)  OR   -- FREQTYPE_MONTHLY
      (@freq_type = 0x20)) AND  -- FREQTYPE_MONTHLYRELATIVE
      (@freq_recurrence_factor < 1)
  BEGIN
    SELECT @reason = FORMATMESSAGE(14577)
    RAISERROR(14278, -1, -1, @reason)
    RETURN(1) -- Failure
  END

ExitProc:
  -- If we made it this far the schedule is good
  RETURN(0) -- Success

END
go



/**************************************************************/
/* SP_ADD_SCHEDULE                                            */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_add_schedule...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_schedule')
              AND (type = 'P')))
  DROP PROCEDURE sp_add_schedule
go

CREATE PROCEDURE sp_add_schedule
(
  @schedule_name        sysname,
  @enabled              TINYINT         = 1,            -- Name does not have to be unique
  @freq_type            INT             = 0,
  @freq_interval        INT             = 0,
  @freq_subday_type        INT             = 0,
  @freq_subday_interval    INT             = 0,
  @freq_relative_interval  INT             = 0,
  @freq_recurrence_factor  INT             = 0,
  @active_start_date    INT             = NULL,         -- sp_verify_schedule assigns a default
  @active_end_date         INT             = 99991231,     -- December 31st 9999
  @active_start_time    INT             = 000000,       -- 12:00:00 am
  @active_end_time         INT             = 235959,       -- 11:59:59 pm
  @owner_login_name        sysname         = NULL,
  @schedule_uid             UNIQUEIDENTIFIER= NULL  OUTPUT, -- Used by a TSX machine when inserting a schedule
  @schedule_id              INT             = NULL  OUTPUT,
  @originating_server       sysname        = NULL
)   
AS
BEGIN
  DECLARE @retval           INT
  DECLARE @owner_sid        VARBINARY(85)
  DECLARE @orig_server_id   INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @schedule_name         = LTRIM(RTRIM(@schedule_name)),
         @owner_login_name      = LTRIM(RTRIM(@owner_login_name)),
         @originating_server    = UPPER(LTRIM(RTRIM(@originating_server))),
         @schedule_id           = 0
         
         
   -- If the owner isn't supplied make if the current user
  IF(@owner_login_name IS NULL OR @owner_login_name = '')
  BEGIN
    --Get the current users sid
    SELECT @owner_sid = SUSER_SID()
  END
  ELSE
  BEGIN
    -- Get the sid for @owner_login_name SID
    --force case insensitive comparation for NT users
    SELECT @owner_sid = dbo.SQLAGENT_SUSER_SID(@owner_login_name)
    -- Cannot proceed if @owner_login_name doesn't exist
    IF(@owner_sid IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@owner_login_name', @owner_login_name)
      RETURN(1) -- Failure
    END
  END

  -- Check schedule (frequency and owner) parameters
  EXECUTE @retval = sp_verify_schedule NULL,   -- schedule_id does not exist for the new schedule
                                       @name                    = @schedule_name,
                                       @enabled                 = @enabled,
                                       @freq_type               = @freq_type,
                                       @freq_interval           = @freq_interval            OUTPUT,
                                       @freq_subday_type        = @freq_subday_type         OUTPUT,
                                       @freq_subday_interval    = @freq_subday_interval     OUTPUT,
                                       @freq_relative_interval  = @freq_relative_interval   OUTPUT,
                                       @freq_recurrence_factor  = @freq_recurrence_factor   OUTPUT,
                                       @active_start_date       = @active_start_date        OUTPUT,
                                       @active_start_time       = @active_start_time        OUTPUT,
                                       @active_end_date         = @active_end_date          OUTPUT,
                                       @active_end_time         = @active_end_time          OUTPUT,
                                       @owner_sid               = @owner_sid
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- ignore @originating_server unless SQLAgent is calling
  if((@originating_server IS NULL) OR (@originating_server = N'') OR (PROGRAM_NAME() NOT LIKE N'SQLAgent%'))
  BEGIN
    --Get the local originating_server_id
    SELECT @orig_server_id = originating_server_id 
    FROM msdb.dbo.sysoriginatingservers_view 
    WHERE master_server = 0
  END
  ELSE
  BEGIN
    --Get the MSX originating_server_id. If @originating_server isn't the msx server error out
    SELECT @orig_server_id = originating_server_id 
    FROM msdb.dbo.sysoriginatingservers_view 
    WHERE (originating_server = @originating_server)

    IF (@orig_server_id IS NULL)
    BEGIN
      RAISERROR(14370, -1, -1)
      RETURN(1) -- Failure
    END
  END
  
  IF (@schedule_uid IS NULL)
  BEGIN
    -- Assign the GUID
    SELECT @schedule_uid = NEWID()
  END
  ELSE IF (@schedule_uid <> CONVERT(UNIQUEIDENTIFIER, 0x00))
  BEGIN
    --Try and find the schedule if a @schedule_uid is provided. 
    --A TSX server uses the @schedule_uid to identify a schedule downloaded from the MSX
   SELECT @schedule_id = schedule_id
    FROM msdb.dbo.sysschedules
    WHERE schedule_uid = @schedule_uid

   IF((@schedule_id IS NOT NULL) AND (@schedule_id <> 0))
   BEGIN
      --If found update the fields
      UPDATE msdb.dbo.sysschedules
        SET name              = ISNULL(@schedule_name, name),
            enabled              = ISNULL(@enabled, enabled),
         freq_type            = ISNULL(@freq_type, freq_type),
         freq_interval        = ISNULL(@freq_interval, freq_interval),
         freq_subday_type     = ISNULL(@freq_subday_type, freq_subday_type),
         freq_subday_interval = ISNULL(@freq_subday_interval, freq_subday_interval),
         freq_relative_interval  = ISNULL(@freq_relative_interval, freq_relative_interval),
         freq_recurrence_factor  = ISNULL(@freq_recurrence_factor, freq_recurrence_factor),
         active_start_date    = ISNULL(@active_start_date, active_start_date),
         active_end_date         = ISNULL(@active_end_date, active_end_date),
         active_start_time    = ISNULL(@active_start_time, active_start_time),
         active_end_time         = ISNULL(@active_end_time, active_end_time)
      WHERE schedule_uid = @schedule_uid

      RETURN(@@ERROR)
   END
  END
  
  --MSX not found so add a record to sysschedules
  INSERT INTO msdb.dbo.sysschedules
         (schedule_uid,
          originating_server_id,
          name,
          owner_sid,
          enabled,
          freq_type,
          freq_interval,
          freq_subday_type,
          freq_subday_interval,
          freq_relative_interval,
          freq_recurrence_factor,
          active_start_date,
          active_end_date,
          active_start_time,
          active_end_time)
  select @schedule_uid,
         @orig_server_id, 
         @schedule_name,
         @owner_sid,
         @enabled,
         @freq_type,
         @freq_interval,
         @freq_subday_type,
         @freq_subday_interval,
         @freq_relative_interval,
         @freq_recurrence_factor,
         @active_start_date,
         @active_end_date,
         @active_start_time,
         @active_end_time
          
  SELECT @retval = @@ERROR,
         @schedule_id = @@IDENTITY

  RETURN(@retval) -- 0 means success
END
GO


/**************************************************************/
/* SP_ATTACH_SCHEDULE                                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_attach_schedule ...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_attach_schedule')
              AND (type = 'P')))
  DROP PROCEDURE sp_attach_schedule
go

CREATE PROCEDURE sp_attach_schedule
(
  @job_id               UNIQUEIDENTIFIER    = NULL,     -- Must provide either this or job_name
  @job_name             sysname             = NULL,     -- Must provide either this or job_id
  @schedule_id          INT                 = NULL,     -- Must provide either this or schedule_name
  @schedule_name        sysname             = NULL,     -- Must provide either this or schedule_id
  @automatic_post       BIT                 = 1         -- If 1 will post notifications to all tsx servers to that run this job
)   
AS
BEGIN
  DECLARE @retval           INT
  DECLARE @sched_owner_sid  VARBINARY(85)
  DECLARE @job_owner_sid    VARBINARY(85)

  
  SET NOCOUNT ON

  -- Check that we can uniquely identify the job
  EXECUTE @retval = msdb.dbo.sp_verify_job_identifiers '@job_name',
                                                       '@job_id',
                                                        @job_name                   OUTPUT,
                                                        @job_id                     OUTPUT,
                                                        @owner_sid = @job_owner_sid OUTPUT
    IF (@retval <> 0)
        RETURN(1) -- Failure

  -- Check authority (only SQLServerAgent can add a schedule to a non-local job)
  EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%'
  IF (@retval <> 0)
    RETURN(@retval)
        
  -- Check that we can uniquely identify the schedule
  EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name',
                                                            @name_of_id_parameter   = '@schedule_id',
                                                            @schedule_name          = @schedule_name    OUTPUT,
                                                            @schedule_id            = @schedule_id      OUTPUT,
                                                            @owner_sid              = @sched_owner_sid  OUTPUT,
                                                            @orig_server_id         = NULL
  IF (@retval <> 0)
      RETURN(1) -- Failure     

  --Schedules can only be attached to a job if the job and schedule have the 
  --same owner or the caller is a sysadmin
  IF ((@sched_owner_sid <> @job_owner_sid) AND 
     ((@sched_owner_sid <> SUSER_SID()) OR (@job_owner_sid <> SUSER_SID())) AND
      (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))
  BEGIN
     RAISERROR(14377, -1, -1)
     RETURN(1) -- Failure
  END

  -- If the record doesn't already exist create it
  IF( NOT EXISTS(SELECT *  
                 FROM msdb.dbo.sysjobschedules
                 WHERE (schedule_id = @schedule_id)
                   AND (job_id = @job_id)) )
  BEGIN
    INSERT INTO msdb.dbo.sysjobschedules (schedule_id, job_id)
    SELECT @schedule_id, @job_id
    
    SELECT @retval = @@ERROR

    -- Notify SQLServerAgent of the change, but only if we know the job has been cached
    IF (EXISTS (SELECT *
                FROM msdb.dbo.sysjobservers
                WHERE (job_id = @job_id)
                    AND (server_id = 0)))
    BEGIN
        EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'S',
                                            @job_id      = @job_id,
                                            @schedule_id = @schedule_id,
                                            @action_type = N'I'
    END
    
    -- For a multi-server job, remind the user that they need to call sp_post_msx_operation
    IF (EXISTS (SELECT *
                FROM msdb.dbo.sysjobservers
                WHERE (job_id = @job_id)
                    AND (server_id <> 0)))
      -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines 
      IF (@automatic_post = 1)
        EXECUTE sp_post_msx_operation @operation = 'INSERT', @object_type = 'JOB', @job_id = @job_id
      ELSE
        RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation')

    -- update this job's subplan to point to this schedule
    UPDATE msdb.dbo.sysmaintplan_subplans
      SET schedule_id = @schedule_id
    WHERE (job_id = @job_id)
      AND (schedule_id IS NULL)
  END
  
  RETURN(@retval) -- 0 means success
END
GO


/**************************************************************/
/* SP_DETACH_SCHEDULE                                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_detach_schedule ...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_detach_schedule')
              AND (type = 'P')))
  DROP PROCEDURE sp_detach_schedule
go

CREATE PROCEDURE sp_detach_schedule
(
  @job_id               UNIQUEIDENTIFIER    = NULL,     -- Must provide either this or job_name
  @job_name             sysname             = NULL,     -- Must provide either this or job_id
  @schedule_id          INT                 = NULL,     -- Must provide either this or schedule_name
  @schedule_name        sysname             = NULL,     -- Must provide either this or schedule_id
  @delete_unused_schedule BIT               = 0,        -- Can optionally delete schedule if it isn't referenced.
                                                        -- The default is to keep schedules 
  @automatic_post       BIT                 = 1         -- If 1 will post notifications to all tsx servers to that run this job
)   
AS
BEGIN
  DECLARE @retval   INT
  DECLARE @sched_owner_sid VARBINARY(85)
  DECLARE @job_owner_sid    VARBINARY(85)
  
  SET NOCOUNT ON

  -- Check that we can uniquely identify the job
  EXECUTE @retval = msdb.dbo.sp_verify_job_identifiers '@job_name',
                                                       '@job_id',
                                                        @job_name OUTPUT,
                                                        @job_id   OUTPUT,
                                                        @owner_sid = @job_owner_sid OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check authority (only SQLServerAgent can add a schedule to a non-local job)
  EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%'
  IF (@retval <> 0)
    RETURN(@retval)
        
  -- Check that we can uniquely identify the schedule
  EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name',
                                                            @name_of_id_parameter   = '@schedule_id',
                                                            @schedule_name          = @schedule_name OUTPUT,
                                                            @schedule_id            = @schedule_id   OUTPUT,
                                                            @owner_sid              = @sched_owner_sid OUTPUT,
                                                            @orig_server_id         = NULL,
                                                            @job_id_filter          = @job_id
  IF (@retval <> 0)
      RETURN(1) -- Failure
 
  -- If the record doesn't exist raise an error
  IF( NOT EXISTS(SELECT *  
                 FROM msdb.dbo.sysjobschedules
                 WHERE (schedule_id = @schedule_id)
                   AND (job_id = @job_id)) )
  BEGIN
    RAISERROR(14374, 0, 1, @schedule_name, @job_name)    
    RETURN(1) -- Failure   
  END
  ELSE
  BEGIN
  
    -- Only sysadmin can detach schedules from jobs they do not own
   IF (((@sched_owner_sid <> SUSER_SID()) OR (@job_owner_sid <> SUSER_SID())) AND
        (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))
    BEGIN
      RAISERROR(14391, -1, -1)
      RETURN(1) -- Failure
    END

    DELETE FROM msdb.dbo.sysjobschedules
    WHERE (job_id = @job_id)
      AND (schedule_id = @schedule_id)
    
    SELECT @retval = @@ERROR
    
    --delete the schedule if requested and it isn't referenced
    IF(@retval = 0 AND @delete_unused_schedule = 1)
    BEGIN
        IF(NOT EXISTS(SELECT * 
                      FROM msdb.dbo.sysjobschedules
                      WHERE (schedule_id = @schedule_id)))
        BEGIN
            DELETE FROM msdb.dbo.sysschedules
            WHERE (schedule_id = @schedule_id)
        END
    END

    -- Update the job's version/last-modified information
    UPDATE msdb.dbo.sysjobs
    SET version_number = version_number + 1,
        date_modified = GETDATE()
    WHERE (job_id = @job_id)

    -- Notify SQLServerAgent of the change, but only if we know the job has been cached
    IF (EXISTS (SELECT *
                FROM msdb.dbo.sysjobservers
                WHERE (job_id = @job_id)
                    AND (server_id = 0)))
    BEGIN
        EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'S',
                                            @job_id      = @job_id,
                                            @schedule_id = @schedule_id,
                                            @action_type = N'D'
    END

    -- For a multi-server job, remind the user that they need to call sp_post_msx_operation
    IF (EXISTS (SELECT *
                FROM msdb.dbo.sysjobservers
                WHERE (job_id = @job_id)
                    AND (server_id <> 0)))
      -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines 
      IF (@automatic_post = 1)
        EXECUTE sp_post_msx_operation @operation = 'INSERT', @object_type = 'JOB', @job_id = @job_id
      ELSE
        RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation')
    
    -- set this job's subplan to the first schedule in sysjobschedules or NULL if there is none 
    UPDATE msdb.dbo.sysmaintplan_subplans
    SET schedule_id = (    SELECT TOP(1) schedule_id
                        FROM msdb.dbo.sysjobschedules
                        WHERE (job_id = @job_id) )
    WHERE (job_id = @job_id)
      AND (schedule_id = @schedule_id)
  END
  
  RETURN(@retval) -- 0 means success
END
GO

/**************************************************************/
/* SP_UPDATE_REPLICATION_JOB_PARAMETER                        */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_update_replication_job_parameter...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_update_replication_job_parameter')
              AND (type = 'P')))
  DROP PROCEDURE sp_update_replication_job_parameter
go
CREATE PROCEDURE sp_update_replication_job_parameter
  @job_id        UNIQUEIDENTIFIER,
  @old_freq_type INT,
  @new_freq_type INT
AS
BEGIN
  DECLARE @category_id INT
  DECLARE @pattern     NVARCHAR(50)
  DECLARE @patternidx  INT
  DECLARE @cmdline     NVARCHAR(3200)
  DECLARE @step_id     INT

  SET NOCOUNT ON
  SELECT @pattern = N'%[-/][Cc][Oo][Nn][Tt][Ii][Nn][Uu][Oo][Uu][Ss]%'

  -- Make sure that we are dealing with relevant replication jobs
  SELECT @category_id = category_id
  FROM msdb.dbo.sysjobs
  WHERE (@job_id = job_id)

  -- @category_id = 10 (REPL-Distribution), 13 (REPL-LogReader), 14 (REPL-Merge),
  --  19 (REPL-QueueReader)
  IF @category_id IN (10, 13, 14, 19)
  BEGIN
    -- Adding the -Continuous parameter (non auto-start to auto-start)
    IF ((@old_freq_type <> 0x40) AND (@new_freq_type = 0x40))
    BEGIN
      -- Use a cursor to handle multiple replication agent job steps
      DECLARE step_cursor CURSOR LOCAL FOR
      SELECT command, step_id
      FROM msdb.dbo.sysjobsteps
      WHERE (@job_id = job_id)
        AND (UPPER(subsystem collate SQL_Latin1_General_CP1_CS_AS) IN (N'MERGE', N'LOGREADER', N'DISTRIBUTION', N'QUEUEREADER'))
      OPEN step_cursor
      FETCH step_cursor INTO @cmdline, @step_id

      WHILE (@@FETCH_STATUS <> -1)
      BEGIN
        SELECT @patternidx = PATINDEX(@pattern, @cmdline)
        -- Make sure that the -Continuous parameter has not been specified already
        IF (@patternidx = 0)
        BEGIN
          SELECT @cmdline = @cmdline + N' -Continuous'
          UPDATE msdb.dbo.sysjobsteps
          SET command = @cmdline
          WHERE (@job_id = job_id)
            AND (@step_id = step_id)
        END -- IF (@patternidx = 0)
        FETCH NEXT FROM step_cursor into @cmdline, @step_id
      END -- WHILE (@@FETCH_STATUS <> -1)
      CLOSE step_cursor
      DEALLOCATE step_cursor
    END -- IF ((@old_freq_type...
    -- Removing the -Continuous parameter (auto-start to non auto-start)
    ELSE
    IF ((@old_freq_type = 0x40) AND (@new_freq_type <> 0x40))
    BEGIN
      DECLARE step_cursor CURSOR LOCAL FOR
      SELECT command, step_id
      FROM msdb.dbo.sysjobsteps
      WHERE (@job_id = job_id)
        AND (UPPER(subsystem collate SQL_Latin1_General_CP1_CS_AS) IN (N'MERGE', N'LOGREADER', N'DISTRIBUTION', N'QUEUEREADER'))
      OPEN step_cursor
      FETCH step_cursor INTO @cmdline, @step_id

      WHILE (@@FETCH_STATUS <> -1)
      BEGIN
        SELECT @patternidx = PATINDEX(@pattern, @cmdline)
        IF (@patternidx <> 0)
        BEGIN
          -- Handle multiple instances of -Continuous in the commandline
          WHILE (@patternidx <> 0)
          BEGIN
            SELECT @cmdline = STUFF(@cmdline, @patternidx, 11, N'')
            IF (@patternidx > 1)
            BEGIN
              -- Remove the preceding space if -Continuous does not start at the beginning of the commandline
              SELECT @cmdline = stuff(@cmdline, @patternidx - 1, 1, N'')
            END
            SELECT @patternidx = PATINDEX(@pattern, @cmdline)
          END -- WHILE (@patternidx <> 0)
          UPDATE msdb.dbo.sysjobsteps
          SET command = @cmdline
          WHERE (@job_id = job_id)
            AND (@step_id = step_id)
        END -- IF (@patternidx <> -1)
        FETCH NEXT FROM step_cursor INTO @cmdline, @step_id
      END -- WHILE (@@FETCH_STATUS <> -1)
      CLOSE step_cursor
      DEALLOCATE step_cursor
    END -- ELSE IF ((@old_freq_type = 0x40)...
  END -- IF @category_id IN (10, 13, 14)

  RETURN 0
END
go

/**************************************************************/
/* SP_UPDATE_SCHEDULE                                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_update_schedule ...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_update_schedule')
              AND (type = 'P')))
  DROP PROCEDURE sp_update_schedule
go

CREATE PROCEDURE sp_update_schedule
(
  @schedule_id              INT             = NULL,     -- Must provide either this or schedule_name
  @name                     sysname         = NULL,     -- Must provide either this or schedule_id
  @new_name                 sysname         = NULL,
  @enabled                  TINYINT         = NULL,
  @freq_type                INT             = NULL,
  @freq_interval            INT             = NULL,
  @freq_subday_type         INT             = NULL,
  @freq_subday_interval     INT             = NULL,
  @freq_relative_interval   INT             = NULL,
  @freq_recurrence_factor   INT             = NULL,
  @active_start_date        INT             = NULL, 
  @active_end_date          INT             = NULL,
  @active_start_time        INT             = NULL,
  @active_end_time          INT             = NULL,
  @owner_login_name         sysname         = NULL,
  @automatic_post           BIT             = 1         -- If 1 will post notifications to all tsx servers to 
                                                        -- update all jobs that use this schedule
)
AS
BEGIN
  DECLARE @retval                   INT
  DECLARE @owner_sid                VARBINARY(85)
  DECLARE @cur_owner_sid            VARBINARY(85)
  DECLARE @x_name                   sysname
  DECLARE @enable_only_used         INT

  DECLARE @x_enabled                TINYINT
  DECLARE @x_freq_type              INT
  DECLARE @x_freq_interval          INT
  DECLARE @x_freq_subday_type       INT
  DECLARE @x_freq_subday_interval   INT
  DECLARE @x_freq_relative_interval INT
  DECLARE @x_freq_recurrence_factor INT
  DECLARE @x_active_start_date      INT
  DECLARE @x_active_end_date        INT
  DECLARE @x_active_start_time      INT
  DECLARE @x_active_end_time        INT
  DECLARE @schedule_uid             UNIQUEIDENTIFIER

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name              = LTRIM(RTRIM(@name))
  SELECT @new_name          = LTRIM(RTRIM(@new_name))
  SELECT @owner_login_name  = LTRIM(RTRIM(@owner_login_name))
  -- Turn [nullable] empty string parameters into NULLs
  IF (@new_name = N'') SELECT @new_name = NULL

   -- If the owner is supplied get the sid and check it
  IF(@owner_login_name IS NOT NULL AND @owner_login_name <> '')
  BEGIN
      -- Get the sid for @owner_login_name SID 
      --force case insensitive comparation for NT users
      SELECT @owner_sid = dbo.SQLAGENT_SUSER_SID(@owner_login_name)
    -- Cannot proceed if @owner_login_name doesn't exist
    IF(@owner_sid IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@owner_login_name', @owner_login_name)
      RETURN(1) -- Failure
    END
  END

  -- Check that we can uniquely identify the schedule. This only returns a schedule that is visible to this user
  EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@name',
                                                            @name_of_id_parameter   = '@schedule_id',
                                                            @schedule_name          = @name             OUTPUT,
                                                            @schedule_id            = @schedule_id      OUTPUT,
                                                            @owner_sid              = @cur_owner_sid    OUTPUT,
                                                            @orig_server_id         = NULL
  IF (@retval <> 0)
      RETURN(1) -- Failure   

  -- Is @enable the only parameter used beside jobname and jobid?
  IF ((@enabled                   IS NOT NULL) AND
       (@new_name                 IS NULL) AND
      (@freq_type                 IS NULL) AND
      (@freq_interval             IS NULL) AND
      (@freq_subday_type          IS NULL) AND
      (@freq_subday_interval      IS NULL) AND
      (@freq_relative_interval    IS NULL) AND
      (@freq_recurrence_factor    IS NULL) AND
      (@active_start_date         IS NULL) AND
      (@active_end_date           IS NULL) AND
      (@active_start_time         IS NULL) AND
      (@active_end_time           IS NULL) AND
      (@owner_login_name          IS NULL))
    SELECT @enable_only_used = 1
  ELSE
    SELECT @enable_only_used = 0
      
  -- Non-sysadmins can only update jobs schedules they own. 
  -- Members of SQLAgentReaderRole and SQLAgentOperatorRole can view job schedules, 
  -- but they should not be able to delete them
  IF ((@cur_owner_sid <> SUSER_SID())
       AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'),0) <> 1)
      AND (@enable_only_used <> 1 OR ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) <> 1))
  BEGIN
   RAISERROR(14394, -1, -1)
   RETURN(1) -- Failure
  END
  
  -- If the param @owner_login_name is null or doesn't get resolved by SUSER_SID() set it to the current owner of the schedule
  if(@owner_sid IS NULL)
      SELECT @owner_sid = @cur_owner_sid
       
   -- Set the x_ (existing) variables
  SELECT @x_name                   = name,
         @x_enabled                = enabled,
         @x_freq_type              = freq_type,
         @x_freq_interval          = freq_interval,
         @x_freq_subday_type       = freq_subday_type,
         @x_freq_subday_interval   = freq_subday_interval,
         @x_freq_relative_interval = freq_relative_interval,
         @x_freq_recurrence_factor = freq_recurrence_factor,
         @x_active_start_date      = active_start_date,
         @x_active_end_date        = active_end_date,
         @x_active_start_time      = active_start_time,
         @x_active_end_time        = active_end_time
  FROM msdb.dbo.sysschedules
  WHERE (schedule_id = @schedule_id )     
  
  
    -- Fill out the values for all non-supplied parameters from the existing values
  IF (@new_name               IS NULL) SELECT @new_name               = @x_name
  IF (@enabled                IS NULL) SELECT @enabled                = @x_enabled
  IF (@freq_type              IS NULL) SELECT @freq_type              = @x_freq_type
  IF (@freq_interval          IS NULL) SELECT @freq_interval          = @x_freq_interval
  IF (@freq_subday_type       IS NULL) SELECT @freq_subday_type       = @x_freq_subday_type
  IF (@freq_subday_interval   IS NULL) SELECT @freq_subday_interval   = @x_freq_subday_interval
  IF (@freq_relative_interval IS NULL) SELECT @freq_relative_interval = @x_freq_relative_interval
  IF (@freq_recurrence_factor IS NULL) SELECT @freq_recurrence_factor = @x_freq_recurrence_factor
  IF (@active_start_date      IS NULL) SELECT @active_start_date      = @x_active_start_date
  IF (@active_end_date        IS NULL) SELECT @active_end_date        = @x_active_end_date
  IF (@active_start_time      IS NULL) SELECT @active_start_time      = @x_active_start_time
  IF (@active_end_time        IS NULL) SELECT @active_end_time        = @x_active_end_time
      
  -- Check schedule (frequency and owner) parameters
  EXECUTE @retval = sp_verify_schedule @schedule_id             = @schedule_id,
                                       @name                    = @new_name,
                                       @enabled                 = @enabled,
                                       @freq_type               = @freq_type,
                                       @freq_interval           = @freq_interval            OUTPUT,
                                       @freq_subday_type        = @freq_subday_type         OUTPUT,
                                       @freq_subday_interval    = @freq_subday_interval     OUTPUT,
                                       @freq_relative_interval  = @freq_relative_interval   OUTPUT,
                                       @freq_recurrence_factor  = @freq_recurrence_factor   OUTPUT,
                                       @active_start_date       = @active_start_date        OUTPUT,
                                       @active_start_time       = @active_start_time        OUTPUT,
                                       @active_end_date         = @active_end_date          OUTPUT,
                                       @active_end_time         = @active_end_time          OUTPUT,
                                       @owner_sid               = @owner_sid
  IF (@retval <> 0)
    RETURN(1) -- Failure  

  -- Update the sysschedules table
  UPDATE msdb.dbo.sysschedules
  SET name                   = @new_name,
      owner_sid              = @owner_sid,
      enabled                = @enabled,
      freq_type              = @freq_type,
      freq_interval          = @freq_interval,
      freq_subday_type       = @freq_subday_type,
      freq_subday_interval   = @freq_subday_interval,
      freq_relative_interval = @freq_relative_interval,
      freq_recurrence_factor = @freq_recurrence_factor,
      active_start_date      = @active_start_date,
      active_end_date        = @active_end_date,
      active_start_time      = @active_start_time,
      active_end_time        = @active_end_time,
      date_modified          = GETDATE(),
      version_number         = version_number + 1
  WHERE (schedule_id = @schedule_id)

  SELECT @retval = @@error

 -- update any job that has repl steps
  DECLARE @job_id UNIQUEIDENTIFIER
  DECLARE jobsschedule_cursor CURSOR LOCAL FOR
  SELECT job_id
  FROM msdb.dbo.sysjobschedules
  WHERE (schedule_id = @schedule_id)
  
  IF @x_freq_type <> @freq_type
  BEGIN
    OPEN jobsschedule_cursor
    FETCH NEXT FROM jobsschedule_cursor INTO @job_id

    WHILE (@@FETCH_STATUS = 0)
    BEGIN 
      EXEC  sp_update_replication_job_parameter @job_id = @job_id,
                                                @old_freq_type = @x_freq_type,
                                                @new_freq_type = @freq_type
      FETCH NEXT FROM jobsschedule_cursor INTO @job_id
    END
    CLOSE jobsschedule_cursor
  END
  DEALLOCATE jobsschedule_cursor
  
  -- Notify SQLServerAgent of the change if this is attached to a local job
  IF (EXISTS (SELECT *
                FROM msdb.dbo.sysjobschedules AS jsched 
              JOIN msdb.dbo.sysjobservers AS jsvr
                    ON jsched.job_id = jsvr.job_id
                WHERE (jsched.schedule_id = @schedule_id)
                  AND (jsvr.server_id = 0)) )
  BEGIN 
      EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'S',
                                          @schedule_id = @schedule_id,
                                          @action_type = N'U'              
  END


  -- Instruct the tsx servers to pick up the altered schedule
  IF (@automatic_post = 1)
  BEGIN
      SELECT @schedule_uid = schedule_uid 
      FROM sysschedules 
      WHERE schedule_id = @schedule_id

      IF(NOT @schedule_uid IS NULL)
      BEGIN
          -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines 
          EXECUTE @retval = sp_post_msx_operation @operation = 'INSERT', @object_type = 'SCHEDULE', @schedule_uid = @schedule_uid
      END
  END  

  RETURN(@retval) -- 0 means success
END
GO


/**************************************************************/
/* SP_DELETE_SCHEDULE                                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_delete_schedule ...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_schedule')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_schedule
go

CREATE PROCEDURE sp_delete_schedule
(
  @schedule_id          INT                 = NULL,     -- Must provide either this or schedule_name
  @schedule_name        sysname             = NULL,     -- Must provide either this or schedule_id
  @force_delete         bit                 = 0,
  @automatic_post       BIT                 = 1         -- If 1 will post notifications to all tsx servers to that run this schedule
)   
AS
BEGIN
  DECLARE @retval           INT
  DECLARE @owner_sid        VARBINARY(85)
  DECLARE @job_count        INT
  DECLARE @targ_server_id   INT

  SET NOCOUNT ON
  --Get the owners sid       
  SELECT @job_count = 0

  -- Check that we can uniquely identify the schedule. This only returns a schedule that is visible to this user
  EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name',
                                                            @name_of_id_parameter   = '@schedule_id',
                                                            @schedule_name          = @schedule_name    OUTPUT,
                                                            @schedule_id            = @schedule_id      OUTPUT,
                                                            @owner_sid              = @owner_sid        OUTPUT,
                                                            @orig_server_id         = NULL
  IF (@retval <> 0)
    RETURN(1) -- Failure 

  -- Non-sysadmins can only update jobs schedules they own. 
  -- Members of SQLAgentReaderRole and SQLAgentOperatorRole can view job schedules, 
  -- but they should not be able to delete them
  IF ((@owner_sid <> SUSER_SID()) AND
     (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'),0) <> 1))
  BEGIN
   RAISERROR(14394, -1, -1)
   RETURN(1) -- Failure
  END
    
  --check if there are jobs using this schedule
  SELECT @job_count = count(*)
  FROM sysjobschedules 
  WHERE (schedule_id = @schedule_id)   
  
  -- If we aren't force deleting the schedule make sure no jobs are using it
  IF ((@force_delete = 0) AND (@job_count > 0))
  BEGIN 
    RAISERROR(14372, -1, -1)
    RETURN (1) -- Failure 
  END

  -- Get the one of the terget server_id's. 
  -- Getting MIN(jsvr.server_id) works here because we are only interested in this ID
  -- to determine if the schedule ID is for local jobs or MSX jobs. 
  -- Note, an MSX job can't be run on the local server
  SELECT @targ_server_id = MIN(jsvr.server_id)
  FROM msdb.dbo.sysjobschedules AS jsched 
   JOIN msdb.dbo.sysjobservers AS jsvr
      ON jsched.job_id = jsvr.job_id
  WHERE (jsched.schedule_id = @schedule_id)

  --OK to delete the job - schedule link
  DELETE sysjobschedules 
  WHERE schedule_id = @schedule_id

  --OK to delete the schedule 
  DELETE sysschedules 
  WHERE schedule_id = @schedule_id

  -- @targ_server_id would be null if no jobs use this schedule
  IF (@targ_server_id IS NOT NULL)
  BEGIN
   -- Notify SQLServerAgent of the change but only if it the schedule was used by a local job
   IF (@targ_server_id = 0)
   BEGIN 
      -- Only send a notification if the schedule is force deleted. If it isn't force deleted
      -- a notification would have already been sent while detaching the schedule (sp_detach_schedule)
      IF (@force_delete = 1)
      BEGIN
        EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'S',
                                   @schedule_id = @schedule_id,
                                   @action_type = N'D'
      END                   
   END
   ELSE
   BEGIN
    -- Instruct the tsx servers to pick up the altered schedule
    IF (@automatic_post = 1)
    BEGIN
      DECLARE @schedule_uid UNIQUEIDENTIFIER
      SELECT @schedule_uid = schedule_uid 
      FROM sysschedules 
      WHERE schedule_id = @schedule_id

      IF(NOT @schedule_uid IS NULL)
      BEGIN
        -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines 
        EXECUTE sp_post_msx_operation @operation = 'INSERT', @object_type = 'SCHEDULE', @schedule_uid = @schedule_uid
      END
    END
    ELSE
      RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation')
   END
  END
  
  RETURN(@retval) -- 0 means success
END
GO



/**************************************************************/
/* SP_GET_JOBSTEP_DB_USERNAME                                 */
/*                                                            */
/* NOTE: For NT login names this procedure can take several   */
/*       seconds to return as it hits the PDC/BDC.            */
/*       SQLServerAgent calls this at runtime.                */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_get_jobstep_db_username...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_get_jobstep_db_username')
              AND (type = 'P')))
  DROP PROCEDURE sp_get_jobstep_db_username
go
CREATE PROCEDURE sp_get_jobstep_db_username
  @database_name        sysname,
  @login_name           sysname = NULL,
  @username_in_targetdb sysname OUTPUT
AS
BEGIN
  DECLARE @suser_sid_clause NVARCHAR(512)

  -- Check the database name
  IF (DB_ID(@database_name) IS NULL)
  BEGIN
    RAISERROR(14262, 16, 1, 'database', @database_name)
    RETURN(1) -- Failure
  END

  -- Initialize return value
  SELECT @username_in_targetdb = NULL

  -- Make sure login name is never NULL
  IF (@login_name IS NULL)
    SELECT @login_name = SUSER_SNAME()
  IF (@login_name IS NULL)
    RETURN(1) -- Failure

  -- Handle an NT login name
  IF (@login_name LIKE N'%\%')
  BEGIN
    -- Special case...
    IF (UPPER(@login_name collate SQL_Latin1_General_CP1_CS_AS) = N'NT AUTHORITY\SYSTEM')
      SELECT @username_in_targetdb = N'dbo'
    ELSE
      SELECT @username_in_targetdb = @login_name

    RETURN(0) -- Success
  END

  -- Handle a SQL login name
  SELECT @suser_sid_clause = N'SUSER_SID(N' + QUOTENAME(@login_name, '''') + N')'
  IF (SUSER_SID(@login_name) IS NULL)
    RETURN(1) -- Failure

  DECLARE @quoted_database_name NVARCHAR(258)
  SELECT @quoted_database_name = QUOTENAME(@database_name, N'[')

  DECLARE @temp_username TABLE (user_name sysname COLLATE database_default NOT NULL, is_aliased BIT)

  -- 1) Look for the user name of the current login in the target database
  INSERT INTO @temp_username
  EXECUTE (N'SET NOCOUNT ON
             SELECT name, isaliased
             FROM '+ @quoted_database_name + N'.[dbo].[sysusers]
             WHERE (sid = ' + @suser_sid_clause + N')
               AND (hasdbaccess = 1)')

  -- 2) Look for the alias user name of the current login in the target database
  IF (EXISTS (SELECT *
              FROM @temp_username
              WHERE (is_aliased = 1)))
  BEGIN
    DELETE FROM @temp_username
    INSERT INTO @temp_username
    EXECUTE (N'SET NOCOUNT ON
               SELECT name, 0
               FROM '+ @quoted_database_name + N'.[dbo].[sysusers]
               WHERE uid = (SELECT altuid
                            FROM ' + @quoted_database_name + N'.[dbo].[sysusers]
                            WHERE (sid = ' + @suser_sid_clause + N'))
                 AND (hasdbaccess = 1)')
  END

  -- 3) Look for the guest user name in the target database
  IF (NOT EXISTS (SELECT *
                  FROM @temp_username))
    INSERT INTO @temp_username
    EXECUTE (N'SET NOCOUNT ON
               SELECT name, 0
               FROM '+ @quoted_database_name + N'.[dbo].[sysusers]
               WHERE (name = N''guest'')
                 AND (hasdbaccess = 1)')

  SELECT @username_in_targetdb = user_name
  FROM @temp_username

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_VERIFY_JOBSTEP                                          */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_jobstep...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_jobstep')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_jobstep
go
CREATE PROCEDURE sp_verify_jobstep
  @job_id             UNIQUEIDENTIFIER,
  @step_id            INT,
  @step_name          sysname,
  @subsystem          NVARCHAR(40),
  @command            NVARCHAR(max),
  @server             sysname,
  @on_success_action  TINYINT,
  @on_success_step_id INT,
  @on_fail_action     TINYINT,
  @on_fail_step_id    INT,
  @os_run_priority    INT,
  @database_name      sysname OUTPUT,
  @database_user_name sysname OUTPUT,
  @flags              INT,
  @output_file_name   NVARCHAR(200),
  @proxy_id         INT 
AS
BEGIN
  DECLARE @max_step_id             INT
  DECLARE @retval                  INT
  DECLARE @valid_values            VARCHAR(50)
  DECLARE @database_name_temp      NVARCHAR(258)
  DECLARE @database_user_name_temp NVARCHAR(256)
  DECLARE @temp_command            NVARCHAR(max)
  DECLARE @iPos                    INT
  DECLARE @create_count            INT
  DECLARE @destroy_count           INT
  DECLARE @is_olap_subsystem       BIT
  DECLARE @owner_sid               VARBINARY(85)
  DECLARE @owner_name              sysname
  -- Remove any leading/trailing spaces from parameters
  SELECT @subsystem        = LTRIM(RTRIM(@subsystem))
  SELECT @server           = LTRIM(RTRIM(@server))
  SELECT @output_file_name = LTRIM(RTRIM(@output_file_name))

  -- Get current maximum step id
  SELECT @max_step_id = ISNULL(MAX(step_id), 0)
  FROM msdb.dbo.sysjobsteps
  WHERE (job_id = @job_id)

  -- Check step id
  IF (@step_id < 1) OR (@step_id > @max_step_id + 1)
  BEGIN
    SELECT @valid_values = '1..' + CONVERT(VARCHAR, @max_step_id + 1)
    RAISERROR(14266, -1, -1, '@step_id', @valid_values)
    RETURN(1) -- Failure
  END

  -- Check subsystem
  EXECUTE @retval = sp_verify_subsystem @subsystem
  IF (@retval <> 0)
    RETURN(1) -- Failure
  
  --check if proxy is allowed for this subsystem for current user
  IF (@proxy_id IS NOT NULL)
  BEGIN
    --get the job owner
    SELECT @owner_sid = owner_sid FROM sysjobs
    WHERE  job_id = @job_id
    IF @owner_sid = 0xFFFFFFFF
    BEGIN
      --ask to verify for the special account
      EXECUTE @retval = sp_verify_proxy_permissions 
        @subsystem_name = @subsystem, 
        @proxy_id = @proxy_id, 
        @name = NULL, 
        @raise_error = 1, 
        @allow_disable_proxy = 1, 
        @verify_special_account = 1
      IF (@retval <> 0)
        RETURN(1) -- Failure
    END
    ELSE
    BEGIN
      SELECT @owner_name = SUSER_SNAME(@owner_sid)
      EXECUTE @retval = sp_verify_proxy_permissions 
      @subsystem_name = @subsystem, 
      @proxy_id = @proxy_id, 
      @name = @owner_name, 
      @raise_error = 1, 
      @allow_disable_proxy = 1
      IF (@retval <> 0)
        RETURN(1) -- Failure
    END
  END

  --Only sysadmin can specify @output_file_name 
  IF (@output_file_name IS NOT NULL) AND  (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
  BEGIN
    RAISERROR(14582, -1, -1)
    RETURN(1) -- Failure    
  END

  --Determmine if this is a olap subsystem jobstep
  IF ( UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) in (N'ANALYSISQUERY', N'ANALYSISCOMMAND') )
    SELECT @is_olap_subsystem = 1
  ELSE
    SELECT @is_olap_subsystem = 0

  -- Check command length
  -- not necessary now, command can be any length
/*
  IF ((DATALENGTH(@command) / 2) > 3200)
  BEGIN
    RAISERROR(14250, 16, 1, '@command', 3200)
    RETURN(1) -- Failure
  END
*/
  -- For a VBScript command, check that object creations are paired with object destructions
  IF ((UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'ACTIVESCRIPTING') AND (@database_name = N'VBScript'))
  BEGIN
    SET @temp_command = @command

    SELECT @create_count = 0
    SELECT @iPos = PATINDEX('%[Cc]reate[Oo]bject[ (]%', @temp_command)
    WHILE(@iPos > 0)
    BEGIN
      SELECT @temp_command = SUBSTRING(@temp_command, @iPos + 1, DATALENGTH(@temp_command) / 2)
      SELECT @iPos = PATINDEX('%[Cc]reate[Oo]bject[ (]%', @temp_command)
      SELECT @create_count = @create_count + 1
    END

    -- restore @temp_command for next loop
    SET @temp_command = @command
    
    SELECT @destroy_count = 0
    SELECT @iPos = PATINDEX('%[Ss]et %=%[Nn]othing%', @temp_command)
    WHILE(@iPos > 0)
    BEGIN
      SELECT @temp_command = SUBSTRING(@temp_command, @iPos + 1, DATALENGTH(@temp_command) / 2)
      SELECT @iPos = PATINDEX('%[Ss]et %=%[Nn]othing%', @temp_command)
      SELECT @destroy_count = @destroy_count + 1
    END

    IF(@create_count > @destroy_count)
    BEGIN
      RAISERROR(14277, -1, -1)
      RETURN(1) -- Failure
    END
  END

  -- Check step name
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobsteps
              WHERE (job_id = @job_id)
                AND (step_name = @step_name)))
  BEGIN
    RAISERROR(14261, -1, -1, '@step_name', @step_name)
    RETURN(1) -- Failure
  END

  -- Check on-success action/step
  IF (@on_success_action <> 1) AND -- Quit Qith Success
     (@on_success_action <> 2) AND -- Quit Qith Failure
     (@on_success_action <> 3) AND -- Goto Next Step
     (@on_success_action <> 4)     -- Goto Step
  BEGIN
    RAISERROR(14266, -1, -1, '@on_success_action', '1, 2, 3, 4')
    RETURN(1) -- Failure
  END
  IF (@on_success_action = 4) AND
     ((@on_success_step_id < 1) OR (@on_success_step_id = @step_id))
  BEGIN
    -- NOTE: We allow forward references to non-existant steps to prevent the user from
    --       having to make a second update pass to fix up the flow
    RAISERROR(14235, -1, -1, '@on_success_step', @step_id)
    RETURN(1) -- Failure
  END

  -- Check on-fail action/step
  IF (@on_fail_action <> 1) AND -- Quit With Success
     (@on_fail_action <> 2) AND -- Quit With Failure
     (@on_fail_action <> 3) AND -- Goto Next Step
     (@on_fail_action <> 4)     -- Goto Step
  BEGIN
    RAISERROR(14266, -1, -1, '@on_failure_action', '1, 2, 3, 4')
    RETURN(1) -- Failure
  END
  IF (@on_fail_action = 4) AND
     ((@on_fail_step_id < 1) OR (@on_fail_step_id = @step_id))
  BEGIN
    -- NOTE: We allow forward references to non-existant steps to prevent the user from
    --       having to make a second update pass to fix up the flow
    RAISERROR(14235, -1, -1, '@on_failure_step', @step_id)
    RETURN(1) -- Failure
  END

  -- Warn the user about forward references
  IF ((@on_success_action = 4) AND (@on_success_step_id > @max_step_id))
    RAISERROR(14236, 0, 1, '@on_success_step_id')
  IF ((@on_fail_action = 4) AND (@on_fail_step_id > @max_step_id))
    RAISERROR(14236, 0, 1, '@on_fail_step_id')

  --Special case the olap subsystem. It can have any server name. 
  --Default it to the local server if @server is null 
  IF(@is_olap_subsystem = 1)
  BEGIN
    IF(@server IS NULL)
    BEGIN
    --TODO: needs error better message ? >> 'Specify the OLAP server name in the %s parameter'
      --Must specify the olap server name
      RAISERROR(14262, -1, -1, '@server', @server)
      RETURN(1) -- Failure    
    END
  END
  ELSE
  BEGIN
    -- Check server (this is the replication server, NOT the job-target server)
    IF (@server IS NOT NULL) AND (NOT EXISTS (SELECT *
                                              FROM master.dbo.sysservers
                                              WHERE (UPPER(srvname) = UPPER(@server))))
    BEGIN
      RAISERROR(14234, -1, -1, '@server', 'sp_helpserver')
      RETURN(1) -- Failure
    END
  END

  -- Check run priority: must be a valid value to pass to SetThreadPriority:
  -- [-15 = IDLE, -1 = BELOW_NORMAL, 0 = NORMAL, 1 = ABOVE_NORMAL, 15 = TIME_CRITICAL]
  IF (@os_run_priority NOT IN (-15, -1, 0, 1, 15))
  BEGIN
    RAISERROR(14266, -1, -1, '@os_run_priority', '-15, -1, 0, 1, 15')
    RETURN(1) -- Failure
  END

  -- Check flags
  IF ((@flags < 0) OR (@flags > 114))
  BEGIN
    RAISERROR(14266, -1, -1, '@flags', '0..114')
    RETURN(1) -- Failure
  END

  IF (((@flags & 64) <> 0) AND (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('CMDEXEC')))
  BEGIN
    RAISERROR(14545, -1, -1, '@flags', '@subsystem')
    RETURN(1) -- Failure
  END

  -- Check output file
  IF (@output_file_name IS NOT NULL) AND (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('TSQL', 'CMDEXEC', 'ANALYSISQUERY', 'ANALYSISCOMMAND', 'SSIS', 'POWERSHELL' ))
  BEGIN
    RAISERROR(14545, -1, -1, '@output_file_name', @subsystem)
    RETURN(1) -- Failure
  END

  -- Check writing to table flags
  IF (@flags IS NOT NULL) AND (((@flags & 8) <> 0) OR ((@flags & 16) <> 0)) AND (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('TSQL', 'CMDEXEC', 'ANALYSISQUERY', 'ANALYSISCOMMAND', 'SSIS', 'POWERSHELL' ))
  BEGIN
    RAISERROR(14545, -1, -1, '@flags', @subsystem)
    RETURN(1) -- Failure
  END

  -- For CmdExec steps database-name and database-user-name should both be null
  IF (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'CMDEXEC')
    SELECT @database_name = NULL,
           @database_user_name = NULL

  -- For non-TSQL steps, database-user-name should be null
  IF (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) <> 'TSQL')
    SELECT @database_user_name = NULL

  -- For a TSQL step, get (and check) the username of the caller in the target database.
  IF (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = 'TSQL')
  BEGIN
    SET NOCOUNT ON

    -- But first check if remote server name has been supplied
    IF (@server IS NOT NULL)
      SELECT @server = NULL

    -- Default database to 'master' if not supplied
    IF (LTRIM(RTRIM(@database_name)) IS NULL)
      SELECT @database_name = N'master'

    -- Check the database (although this is no guarantee that @database_user_name can access it)
    IF (DB_ID(@database_name) IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@database_name', @database_name)
      RETURN(1) -- Failure
    END

    SELECT @database_user_name = LTRIM(RTRIM(@database_user_name))

    -- Only if a SysAdmin is creating the job can the database user name be non-NULL [since only
    -- SysAdmin's can call SETUSER].
    -- NOTE: In this case we don't try to validate the user name (it's too costly to do so)
    --       so if it's bad we'll get a runtime error when the job executes.
    IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)
    BEGIN
      -- If this is a multi-server job then @database_user_name must be null
      IF (@database_user_name IS NOT NULL)
      BEGIN
        IF (EXISTS (SELECT *
                    FROM msdb.dbo.sysjobs       sj,
                         msdb.dbo.sysjobservers sjs
                    WHERE (sj.job_id = sjs.job_id)
                      AND (sj.job_id = @job_id)
                      AND (sjs.server_id <> 0)))
        BEGIN
          RAISERROR(14542, -1, -1, N'database_user_name')
          RETURN(1) -- Failure
        END
      END

      -- For a SQL-user, check if it exists
      IF (@database_user_name NOT LIKE N'%\%')
      BEGIN
        SELECT @database_user_name_temp = replace(@database_user_name, N'''', N'''''')
        SELECT @database_name_temp = QUOTENAME(@database_name)

        EXECUTE(N'DECLARE @ret INT
                  SELECT @ret = COUNT(*)
                  FROM ' + @database_name_temp + N'.dbo.sysusers
                  WHERE (name = N''' + @database_user_name_temp + N''')
                  HAVING (COUNT(*) > 0)')
        IF (@@ROWCOUNT = 0)
        BEGIN
          RAISERROR(14262, -1, -1, '@database_user_name', @database_user_name)
          RETURN(1) -- Failure
        END
      END
    END
    ELSE
      SELECT @database_user_name = NULL

  END  -- End of TSQL property verification

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_ADD_JOBSTEP_INTERNAL                                    */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_jobstep_internal...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_jobstep_internal')
              AND (type = 'P')))
  DROP PROCEDURE dbo.sp_add_jobstep_internal
go
CREATE PROCEDURE dbo.sp_add_jobstep_internal
  @job_id                UNIQUEIDENTIFIER = NULL,   -- Must provide either this or job_name
  @job_name              sysname          = NULL,   -- Must provide either this or job_id
  @step_id               INT              = NULL,   -- The proc assigns a default
  @step_name             sysname,
  @subsystem             NVARCHAR(40)     = N'TSQL',
  @command               NVARCHAR(max)    = NULL,
  @additional_parameters NTEXT            = NULL,
  @cmdexec_success_code  INT              = 0,
  @on_success_action     TINYINT          = 1,      -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step
  @on_success_step_id    INT              = 0,
  @on_fail_action        TINYINT          = 2,      -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step
  @on_fail_step_id       INT              = 0,
  @server                sysname          = NULL,
  @database_name         sysname          = NULL,
  @database_user_name    sysname          = NULL,
  @retry_attempts        INT              = 0,      -- No retries
  @retry_interval        INT              = 0,      -- 0 minute interval
  @os_run_priority       INT              = 0,      -- -15 = Idle, -1 = Below Normal, 0 = Normal, 1 = Above Normal, 15 = Time Critical)
  @output_file_name      NVARCHAR(200)    = NULL,
  @flags                 INT              = 0,       --  0 = Normal, 
                                                     --  1 = Encrypted command (read only), 
                                                     --  2 = Append output files (if any), 
                                                     --  4 = Write TSQL step output to step history
                                                     --  8 = Write log to table (overwrite existing history)
                                                     -- 16 = Write log to table (append to existing history)
                                                     -- 32 = Write all output to job history
                                                     -- 64 = Create a Windows event to use as a signal for the Cmd jobstep to abort
  @proxy_id               int               = NULL,
  @proxy_name              sysname           = NULL,
  -- mutual exclusive; must specify only one of above 2 parameters to 
  -- identify the proxy. 
  @step_uid UNIQUEIDENTIFIER              = NULL OUTPUT
AS
BEGIN
  DECLARE @retval         INT
  DECLARE @max_step_id    INT
  DECLARE @job_owner_sid  VARBINARY(85)
  DECLARE @subsystem_id   INT
  DECLARE @auto_proxy_name sysname
  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @step_name          = LTRIM(RTRIM(@step_name))
  SELECT @subsystem          = LTRIM(RTRIM(@subsystem))
  SELECT @server             = LTRIM(RTRIM(@server))
  SELECT @database_name      = LTRIM(RTRIM(@database_name))
  SELECT @database_user_name = LTRIM(RTRIM(@database_user_name))
  SELECT @output_file_name   = LTRIM(RTRIM(@output_file_name))
  SELECT @proxy_name         = LTRIM(RTRIM(@proxy_name))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@server             = N'') SELECT @server             = NULL
  IF (@database_name      = N'') SELECT @database_name      = NULL
  IF (@database_user_name = N'') SELECT @database_user_name = NULL
  IF (@output_file_name   = N'') SELECT @output_file_name   = NULL
  IF (@proxy_name         = N'') SELECT @proxy_name         = NULL

  -- Check authority (only SQLServerAgent can add a step to a non-local job)
  EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%'
  IF (@retval <> 0)
    RETURN(@retval)

  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT,
                                               @owner_sid = @job_owner_sid OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) AND
      (SUSER_SID() <> @job_owner_sid))
  BEGIN
     RAISERROR(14525, -1, -1)
     RETURN(1) -- Failure
  END
  

  -- check proxy identifiers only if a proxy has been provided
  IF (@proxy_id IS NOT NULL) or (@proxy_name IS NOT NULL)
  BEGIN
    EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name',
                                                  '@proxy_id',
                                                   @proxy_name OUTPUT,
                                                   @proxy_id   OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure
   END

  -- Default step id (if not supplied)
  IF (@step_id IS NULL)
  BEGIN
    SELECT @step_id = ISNULL(MAX(step_id), 0) + 1
    FROM msdb.dbo.sysjobsteps
    WHERE (job_id = @job_id)
  END

  -- Check parameters
  EXECUTE @retval = sp_verify_jobstep @job_id,
                                      @step_id,
                                      @step_name,
                                      @subsystem,
                                      @command,
                                      @server,
                                      @on_success_action,
                                      @on_success_step_id,
                                      @on_fail_action,
                                      @on_fail_step_id,
                                      @os_run_priority,
                                      @database_name      OUTPUT,
                                      @database_user_name OUTPUT,
                                      @flags,
                                      @output_file_name,
                                               @proxy_id

  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Get current maximum step id
  SELECT @max_step_id = ISNULL(MAX(step_id), 0)
  FROM msdb.dbo.sysjobsteps
  WHERE (job_id = @job_id)

  DECLARE @TranCounter INT;
  SET @TranCounter = @@TRANCOUNT;
  IF @TranCounter = 0
  BEGIN
      -- start our own transaction if there is no outer transaction
      BEGIN TRANSACTION;
  END
  
  -- Modify database.
  BEGIN TRY
    -- Update the job's version/last-modified information
    UPDATE msdb.dbo.sysjobs
    SET version_number = version_number + 1,
        date_modified = GETDATE()
    WHERE (job_id = @job_id)

    -- Adjust step id's (unless the new step is being inserted at the 'end')
    -- NOTE: We MUST do this before inserting the step.
    IF (@step_id <= @max_step_id)
    BEGIN
      UPDATE msdb.dbo.sysjobsteps
      SET step_id = step_id + 1
      WHERE (step_id >= @step_id)
        AND (job_id = @job_id)

      -- Clean up OnSuccess/OnFail references
      UPDATE msdb.dbo.sysjobsteps
      SET on_success_step_id = on_success_step_id + 1
      WHERE (on_success_step_id >= @step_id)
        AND (job_id = @job_id)

      UPDATE msdb.dbo.sysjobsteps
      SET on_fail_step_id = on_fail_step_id + 1
      WHERE (on_fail_step_id >= @step_id)
        AND (job_id = @job_id)

      UPDATE msdb.dbo.sysjobsteps
      SET on_success_step_id = 0,
          on_success_action = 1  -- Quit With Success
      WHERE (on_success_step_id = @step_id)
        AND (job_id = @job_id)

      UPDATE msdb.dbo.sysjobsteps
      SET on_fail_step_id = 0,
          on_fail_action = 2     -- Quit With Failure
      WHERE (on_fail_step_id = @step_id)
        AND (job_id = @job_id)
    END

    SELECT @step_uid = NEWID()

    -- Insert the step
    INSERT INTO msdb.dbo.sysjobsteps
           (job_id,
            step_id,
            step_name,
            subsystem,
            command,
            flags,
            additional_parameters,
            cmdexec_success_code,
            on_success_action,
            on_success_step_id,
            on_fail_action,
            on_fail_step_id,
            server,
            database_name,
            database_user_name,
            retry_attempts,
            retry_interval,
            os_run_priority,
            output_file_name,
            last_run_outcome,
            last_run_duration,
            last_run_retries,
            last_run_date,
            last_run_time,
            proxy_id,
         step_uid)
    VALUES (@job_id,
            @step_id,
            @step_name,
            @subsystem,
            @command,
            @flags,
            @additional_parameters,
            @cmdexec_success_code,
            @on_success_action,
            @on_success_step_id,
            @on_fail_action,
            @on_fail_step_id,
            @server,
            @database_name,
            @database_user_name,
            @retry_attempts,
            @retry_interval,
            @os_run_priority,
            @output_file_name,
            0,
            0,
            0,
            0,
            0,
         @proxy_id,
         @step_uid)
         
  IF @TranCounter = 0
  BEGIN
      -- start our own transaction if there is no outer transaction
      COMMIT TRANSACTION;
  END

  END TRY
  BEGIN CATCH

      -- Prepare tp echo error information to the caller.
      DECLARE @ErrorMessage NVARCHAR(400)
      DECLARE @ErrorSeverity INT
      DECLARE @ErrorState INT

      SELECT @ErrorMessage = ERROR_MESSAGE()
      SELECT @ErrorSeverity = ERROR_SEVERITY()
      SELECT @ErrorState = ERROR_STATE()
      
      IF @TranCounter = 0
      BEGIN
          -- Transaction started in procedure.
          -- Roll back complete transaction.
          ROLLBACK TRANSACTION;
      END
      RAISERROR (@ErrorMessage, -- Message text.
                  @ErrorSeverity, -- Severity.
                  @ErrorState -- State.
                  )
      RETURN (1)                  
  END CATCH
  
  -- Make sure that SQLServerAgent refreshes the job if the 'Has Steps' property has changed
  IF ((SELECT COUNT(*)
       FROM msdb.dbo.sysjobsteps
       WHERE (job_id = @job_id)) = 1)
  BEGIN
    -- NOTE: We only notify SQLServerAgent if we know the job has been cached
    IF (EXISTS (SELECT *
                FROM msdb.dbo.sysjobservers
                WHERE (job_id = @job_id)
                  AND (server_id = 0)))
      EXECUTE msdb.dbo.sp_sqlagent_notify @op_type       = N'J',
                                            @job_id      = @job_id,
                                            @action_type = N'U'
  END

  -- For a multi-server job, push changes to the target servers
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers
              WHERE (job_id = @job_id)
                AND (server_id <> 0)))
  BEGIN
    EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_ADD_JOBSTEP                                             */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_jobstep...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_jobstep')
              AND (type = 'P')))
  DROP PROCEDURE dbo.sp_add_jobstep
go
CREATE PROCEDURE dbo.sp_add_jobstep
  @job_id                UNIQUEIDENTIFIER = NULL,   -- Must provide either this or job_name
  @job_name              sysname          = NULL,   -- Must provide either this or job_id
  @step_id               INT              = NULL,   -- The proc assigns a default
  @step_name             sysname,
  @subsystem             NVARCHAR(40)     = N'TSQL',
  @command               NVARCHAR(max)   = NULL,   
  @additional_parameters NTEXT            = NULL,
  @cmdexec_success_code  INT              = 0,
  @on_success_action     TINYINT          = 1,      -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step
  @on_success_step_id    INT              = 0,
  @on_fail_action        TINYINT          = 2,      -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step
  @on_fail_step_id       INT              = 0,
  @server                sysname      = NULL,
  @database_name         sysname          = NULL,
  @database_user_name    sysname          = NULL,
  @retry_attempts        INT              = 0,      -- No retries
  @retry_interval        INT              = 0,      -- 0 minute interval
  @os_run_priority       INT              = 0,      -- -15 = Idle, -1 = Below Normal, 0 = Normal, 1 = Above Normal, 15 = Time Critical)
  @output_file_name      NVARCHAR(200)    = NULL,
  @flags                 INT              = 0,       -- 0 = Normal, 
                                                     -- 1 = Encrypted command (read only), 
                                                     -- 2 = Append output files (if any), 
                                                     -- 4 = Write TSQL step output to step history,                                            
                                                     -- 8 = Write log to table (overwrite existing history), 
                                                     -- 16 = Write log to table (append to existing history)
                                                     -- 32 = Write all output to job history
                                                     -- 64 = Create a Windows event to use as a signal for the Cmd jobstep to abort
  @proxy_id                 INT                = NULL,
  @proxy_name               sysname          = NULL,
  -- mutual exclusive; must specify only one of above 2 parameters to 
  -- identify the proxy. 
  @step_uid UNIQUEIDENTIFIER = NULL OUTPUT
AS
BEGIN
  DECLARE @retval      INT

  SET NOCOUNT ON
  -- Only sysadmin's or db_owner's of msdb can add replication job steps directly
  IF (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) IN
                        (N'DISTRIBUTION',
                         N'SNAPSHOT',
                         N'LOGREADER',
                         N'MERGE',
                         N'QUEUEREADER'))
  BEGIN
    IF NOT ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR
            (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1) OR
            (UPPER(USER_NAME() collate SQL_Latin1_General_CP1_CS_AS) = N'DBO'))
    BEGIN
      RAISERROR(14260, -1, -1)
      RETURN(1) -- Failure
    END
  END

  --Only sysadmin can specify @database_user_name
  IF (@database_user_name IS NOT NULL) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
  BEGIN
    RAISERROR(14583, -1, -1)
    RETURN(1) -- Failure    
  END

  -- Make sure Dts is translated into new subsystem's name SSIS
  IF UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DTS'
  BEGIN
    SET @subsystem = N'SSIS'
  END

  EXECUTE @retval = dbo.sp_add_jobstep_internal @job_id = @job_id,
                                                @job_name = @job_name,
                                                @step_id = @step_id,
                                                @step_name = @step_name,
                                                @subsystem = @subsystem,
                                                @command = @command,
                                                @additional_parameters = @additional_parameters,
                                                @cmdexec_success_code = @cmdexec_success_code,
                                                @on_success_action = @on_success_action,
                                                @on_success_step_id = @on_success_step_id,
                                                @on_fail_action = @on_fail_action,
                                                @on_fail_step_id = @on_fail_step_id,
                                                @server = @server,
                                                @database_name = @database_name,
                                                @database_user_name = @database_user_name,
                                                @retry_attempts = @retry_attempts,
                                                @retry_interval = @retry_interval,
                                                @os_run_priority = @os_run_priority,
                                                @output_file_name = @output_file_name,
                                                @flags = @flags,
                                                            @proxy_id = @proxy_id,
                                                @proxy_name = @proxy_name,
                                                            @step_uid = @step_uid OUTPUT


  RETURN(@retval)
END
GO

/**************************************************************/
/* SP_UPDATE_JOBSTEP                                          */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_update_jobstep...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_update_jobstep')
              AND (type = 'P')))
  DROP PROCEDURE sp_update_jobstep
go
CREATE PROCEDURE sp_update_jobstep
  @job_id                 UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name
  @job_name               sysname          = NULL, -- Not updatable (provided for identification purposes only)
  @step_id                INT,                     -- Not updatable (provided for identification purposes only)
  @step_name              sysname          = NULL,
  @subsystem              NVARCHAR(40)     = NULL,
  @command                NVARCHAR(max)    = NULL,
  @additional_parameters  NTEXT            = NULL,
  @cmdexec_success_code   INT              = NULL,
  @on_success_action      TINYINT          = NULL,
  @on_success_step_id     INT              = NULL,
  @on_fail_action         TINYINT          = NULL,
  @on_fail_step_id        INT              = NULL,
  @server                 sysname          = NULL,
  @database_name          sysname          = NULL,
  @database_user_name     sysname          = NULL,
  @retry_attempts         INT              = NULL,
  @retry_interval         INT              = NULL,
  @os_run_priority        INT              = NULL,
  @output_file_name       NVARCHAR(200)    = NULL,
  @flags                  INT              = NULL,
  @proxy_id            int          = NULL,
  @proxy_name          sysname         = NULL
  -- mutual exclusive; must specify only one of above 2 parameters to 
  -- identify the proxy. 
AS
BEGIN
  DECLARE @retval                 INT
  DECLARE @os_run_priority_code   INT
  DECLARE @step_id_as_char        VARCHAR(10)
  DECLARE @new_step_name          sysname
  DECLARE @x_step_name            sysname
  DECLARE @x_subsystem            NVARCHAR(40)
  DECLARE @x_command              NVARCHAR(max)
  DECLARE @x_flags                INT
  DECLARE @x_cmdexec_success_code INT
  DECLARE @x_on_success_action    TINYINT
  DECLARE @x_on_success_step_id   INT
  DECLARE @x_on_fail_action       TINYINT
  DECLARE @x_on_fail_step_id      INT
  DECLARE @x_server               sysname
  DECLARE @x_database_name        sysname
  DECLARE @x_database_user_name   sysname
  DECLARE @x_retry_attempts       INT
  DECLARE @x_retry_interval       INT
  DECLARE @x_os_run_priority      INT
  DECLARE @x_output_file_name     NVARCHAR(200)
  DECLARE @x_proxy_id             INT         
  DECLARE @x_last_run_outcome     TINYINT      -- Not updatable (but may be in future)
  DECLARE @x_last_run_duration    INT          -- Not updatable (but may be in future)
  DECLARE @x_last_run_retries     INT          -- Not updatable (but may be in future)
  DECLARE @x_last_run_date        INT          -- Not updatable (but may be in future)
  DECLARE @x_last_run_time        INT          -- Not updatable (but may be in future)

  DECLARE @new_proxy_id           INT
  DECLARE @subsystem_id           INT
  DECLARE @auto_proxy_name        sysname
  DECLARE @job_owner_sid        VARBINARY(85)
  
  SET NOCOUNT ON

  SELECT @new_proxy_id = NULL

  -- Remove any leading/trailing spaces from parameters
  SELECT @step_name          = LTRIM(RTRIM(@step_name))
  SELECT @subsystem          = LTRIM(RTRIM(@subsystem))
  SELECT @command            = LTRIM(RTRIM(@command))
  SELECT @server             = LTRIM(RTRIM(@server))
  SELECT @database_name      = LTRIM(RTRIM(@database_name))
  SELECT @database_user_name = LTRIM(RTRIM(@database_user_name))
  SELECT @output_file_name   = LTRIM(RTRIM(@output_file_name))
  SELECT @proxy_name         = LTRIM(RTRIM(@proxy_name))

  -- Make sure Dts is translated into new subsystem's name SSIS
  IF (@subsystem IS NOT NULL AND UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DTS')
  BEGIN
    SET @subsystem = N'SSIS'
  END

  -- Only sysadmin's or db_owner's of msdb can directly change
  -- an existing job step to use one of the replication
  -- subsystems
  IF (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) IN
                        (N'DISTRIBUTION',
                         N'SNAPSHOT',
                         N'LOGREADER',
                         N'MERGE',
                         N'QUEUEREADER'))
  BEGIN
    IF NOT ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR
            (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1) OR
            (UPPER(USER_NAME() collate SQL_Latin1_General_CP1_CS_AS) = N'DBO'))
    BEGIN
      RAISERROR(14260, -1, -1)
      RETURN(1) -- Failure
    END
  END

  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT,
                                               @owner_sid = @job_owner_sid OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check permissions beyond what's checked by the sysjobs_view
  -- SQLAgentReader and SQLAgentOperator roles that can see all jobs
  -- cannot modify jobs they do not own
  IF (@job_owner_sid <> SUSER_SID()                   -- does not own the job
     AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))   -- is not sysadmin
  BEGIN
   RAISERROR(14525, -1, -1);
   RETURN(1) -- Failure
  END

  -- Check that the step exists
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysjobsteps
                  WHERE (job_id = @job_id)
                    AND (step_id = @step_id)))
  BEGIN
    SELECT @step_id_as_char = CONVERT(VARCHAR(10), @step_id)
    RAISERROR(14262, -1, -1, '@step_id', @step_id_as_char)
    RETURN(1) -- Failure
  END

  -- check proxy identifiers only if a proxy has been provided
  -- @proxy_name = N'' is a special case to allow change of an existing proxy with NULL
  IF (@proxy_id IS NOT NULL) OR (@proxy_name IS NOT NULL AND @proxy_name <> N'') 
  BEGIN
    EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name',
                                                  '@proxy_id',
                                                   @proxy_name OUTPUT,
                                                   @proxy_id   OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure

     SELECT @new_proxy_id  = @proxy_id

  END

  -- Check authority (only SQLServerAgent can modify a step of a non-local job)
  EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%'
  IF (@retval <> 0)
    RETURN(@retval)

  -- Set the x_ (existing) variables
  SELECT @x_step_name            = step_name,
         @x_subsystem            = subsystem,
         @x_command              = command,
         @x_flags                = flags,
         @x_cmdexec_success_code = cmdexec_success_code,
         @x_on_success_action    = on_success_action,
         @x_on_success_step_id   = on_success_step_id,
         @x_on_fail_action       = on_fail_action,
         @x_on_fail_step_id      = on_fail_step_id,
         @x_server               = server,
         @x_database_name        = database_name,
         @x_database_user_name   = database_user_name,
         @x_retry_attempts       = retry_attempts,
         @x_retry_interval       = retry_interval,
         @x_os_run_priority      = os_run_priority,
         @x_output_file_name     = output_file_name,
         @x_proxy_id             = proxy_id,
         @x_last_run_outcome     = last_run_outcome,
         @x_last_run_duration    = last_run_duration,
         @x_last_run_retries     = last_run_retries,
         @x_last_run_date        = last_run_date,
         @x_last_run_time        = last_run_time
  FROM msdb.dbo.sysjobsteps
  WHERE (job_id = @job_id)
    AND (step_id = @step_id)

  IF ((@step_name IS NOT NULL) AND (@step_name <> @x_step_name))
    SELECT @new_step_name = @step_name

  -- Fill out the values for all non-supplied parameters from the existing values
  IF (@step_name            IS NULL) SELECT @step_name            = @x_step_name
  IF (@subsystem            IS NULL) SELECT @subsystem            = @x_subsystem
  IF (@command              IS NULL) SELECT @command              = @x_command
  IF (@flags                IS NULL) SELECT @flags                = @x_flags
  IF (@cmdexec_success_code IS NULL) SELECT @cmdexec_success_code = @x_cmdexec_success_code
  IF (@on_success_action    IS NULL) SELECT @on_success_action    = @x_on_success_action
  IF (@on_success_step_id   IS NULL) SELECT @on_success_step_id   = @x_on_success_step_id
  IF (@on_fail_action       IS NULL) SELECT @on_fail_action       = @x_on_fail_action
  IF (@on_fail_step_id      IS NULL) SELECT @on_fail_step_id      = @x_on_fail_step_id
  IF (@server               IS NULL) SELECT @server               = @x_server
  IF (@database_name        IS NULL) SELECT @database_name        = @x_database_name
  IF (@database_user_name   IS NULL) SELECT @database_user_name   = @x_database_user_name
  IF (@retry_attempts       IS NULL) SELECT @retry_attempts       = @x_retry_attempts
  IF (@retry_interval       IS NULL) SELECT @retry_interval       = @x_retry_interval
  IF (@os_run_priority      IS NULL) SELECT @os_run_priority      = @x_os_run_priority
  IF (@output_file_name     IS NULL) SELECT @output_file_name     = @x_output_file_name
  IF (@proxy_id             IS NULL) SELECT @new_proxy_id         = @x_proxy_id

  --if an empty proxy_name is supplied the proxy is removed
  IF @proxy_name = N'' SELECT @new_proxy_id = NULL
  -- Turn [nullable] empty string parameters into NULLs
  IF (@command            = N'') SELECT @command            = NULL
  IF (@server             = N'') SELECT @server             = NULL
  IF (@database_name      = N'') SELECT @database_name      = NULL
  IF (@database_user_name = N'') SELECT @database_user_name = NULL
  IF (@output_file_name   = N'') SELECT @output_file_name   = NULL


  -- Check new values
  EXECUTE @retval = sp_verify_jobstep @job_id,
                                      @step_id,
                                      @new_step_name,
                                      @subsystem,
                                      @command,
                                      @server,
                                      @on_success_action,
                                      @on_success_step_id,
                                      @on_fail_action,
                                      @on_fail_step_id,
                                      @os_run_priority,
                                      @database_name      OUTPUT,
                                      @database_user_name OUTPUT,
                                      @flags,
                                      @output_file_name,
                                               @new_proxy_id
  IF (@retval <> 0)
    RETURN(1) -- Failure

  BEGIN TRANSACTION

    -- Update the job's version/last-modified information
    UPDATE msdb.dbo.sysjobs
    SET version_number = version_number + 1,
        date_modified = GETDATE()
    WHERE (job_id = @job_id)

    -- Update the step
    UPDATE msdb.dbo.sysjobsteps
    SET step_name             = @step_name,
        subsystem             = @subsystem,
        command               = @command,
        flags                 = @flags,
        cmdexec_success_code  = @cmdexec_success_code,
        on_success_action     = @on_success_action,
        on_success_step_id    = @on_success_step_id,
        on_fail_action        = @on_fail_action,
        on_fail_step_id       = @on_fail_step_id,
        server                = @server,
        database_name         = @database_name,
        database_user_name    = @database_user_name,
        retry_attempts        = @retry_attempts,
        retry_interval        = @retry_interval,
        os_run_priority       = @os_run_priority,
        output_file_name      = @output_file_name,
        last_run_outcome      = @x_last_run_outcome,
        last_run_duration     = @x_last_run_duration,
        last_run_retries      = @x_last_run_retries,
        last_run_date         = @x_last_run_date,
        last_run_time         = @x_last_run_time,
          proxy_id                 = @new_proxy_id
    WHERE (job_id = @job_id)
      AND (step_id = @step_id)

    -- Since we can't declare TEXT parameters (and therefore use the @x_ technique) we handle
    -- @additional_parameters as a special case...
    IF (@additional_parameters IS NOT NULL)
    BEGIN
      UPDATE msdb.dbo.sysjobsteps
      SET additional_parameters = @additional_parameters
      WHERE (job_id = @job_id)
        AND (step_id = @step_id)
    END

  COMMIT TRANSACTION

  -- For a multi-server job, push changes to the target servers
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers
              WHERE (job_id = @job_id)
                AND (server_id <> 0)))
  BEGIN
    EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_DELETE_JOBSTEP                                          */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_jobstep...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_jobstep')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_jobstep
go
CREATE PROCEDURE sp_delete_jobstep
  @job_id   UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name
  @job_name sysname          = NULL, -- Must provide either this or job_id
  @step_id  INT
AS
BEGIN
  DECLARE @retval      INT
  DECLARE @max_step_id INT
  DECLARE @valid_range VARCHAR(50)
  DECLARE @job_owner_sid VARBINARY(85)

  SET NOCOUNT ON

  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT,
                                               @owner_sid = @job_owner_sid OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check authority (only SQLServerAgent can delete a step of a non-local job)
  EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = 'SQLAgent%'
  IF (@retval <> 0)
    RETURN(@retval)
    
  -- SQLAgentReader and SQLAgentOperator roles that can see all jobs
  -- cannot modify jobs they do not own
  IF (@job_owner_sid <> SUSER_SID()                   -- does not own the job
     AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))   -- is not sysadmin
  BEGIN
   RAISERROR(14525, -1, -1);
   RETURN(1) -- Failure
  END

  -- Get current maximum step id
  SELECT @max_step_id = ISNULL(MAX(step_id), 0)
  FROM msdb.dbo.sysjobsteps
  WHERE (job_id = @job_id)

  -- Check step id
  IF (@step_id < 0) OR (@step_id > @max_step_id)
  BEGIN
    SELECT @valid_range = FORMATMESSAGE(14201) + CONVERT(VARCHAR, @max_step_id)
    RAISERROR(14266, -1, -1, '@step_id', @valid_range)
    RETURN(1) -- Failure
  END

  BEGIN TRANSACTION

    -- Delete either the specified step or ALL the steps (if step id is 0)
    IF (@step_id = 0)
    BEGIN
      DELETE FROM msdb.dbo.sysjobsteps
      WHERE (job_id = @job_id)
     END
      ELSE
     BEGIN
      DELETE FROM msdb.dbo.sysjobsteps
      WHERE (job_id = @job_id)
        AND (step_id = @step_id)
    END

    IF (@step_id <> 0)
    BEGIN
      -- Adjust step id's
      UPDATE msdb.dbo.sysjobsteps
      SET step_id = step_id - 1
      WHERE (step_id > @step_id)
        AND (job_id = @job_id)

      -- Clean up OnSuccess/OnFail references
      UPDATE msdb.dbo.sysjobsteps
      SET on_success_step_id = on_success_step_id - 1
      WHERE (on_success_step_id > @step_id)
        AND (job_id = @job_id)

      UPDATE msdb.dbo.sysjobsteps
      SET on_fail_step_id = on_fail_step_id - 1
      WHERE (on_fail_step_id > @step_id)
        AND (job_id = @job_id)

      UPDATE msdb.dbo.sysjobsteps
      SET on_success_step_id = 0,
          on_success_action = 1   -- Quit With Success
      WHERE (on_success_step_id = @step_id)
        AND (job_id = @job_id)

      UPDATE msdb.dbo.sysjobsteps
      SET on_fail_step_id = 0,
          on_fail_action = 2   -- Quit With Failure
      WHERE (on_fail_step_id = @step_id)
        AND (job_id = @job_id)
        
    END

    
    -- Update the job's version/last-modified information
    UPDATE msdb.dbo.sysjobs
    SET version_number = version_number + 1,
        date_modified = GETDATE()
    WHERE (job_id = @job_id)

  COMMIT TRANSACTION

  -- Make sure that SQLServerAgent refreshes the job if the 'Has Steps' property has changed
  IF ((SELECT COUNT(*)
       FROM msdb.dbo.sysjobsteps
       WHERE (job_id = @job_id)) = 0)
  BEGIN
    -- NOTE: We only notify SQLServerAgent if we know the job has been cached
    IF (EXISTS (SELECT *
                FROM msdb.dbo.sysjobservers
                WHERE (job_id = @job_id)
                  AND (server_id = 0)))
      EXECUTE msdb.dbo.sp_sqlagent_notify @op_type       = N'J',
                                            @job_id      = @job_id,
                                            @action_type = N'U'
  END

  -- For a multi-server job, push changes to the target servers
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers
              WHERE (job_id = @job_id)
                AND (server_id <> 0)))
  BEGIN
    EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_HELP_JOBSTEP                                            */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_jobstep...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_jobstep')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_jobstep
go
CREATE PROCEDURE sp_help_jobstep
  @job_id    UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name
  @job_name  sysname          = NULL, -- Must provide either this or job_id
  @step_id   INT              = NULL,
  @step_name sysname          = NULL,
  @suffix    BIT              = 0     -- A flag to control how the result set is formatted
AS
BEGIN
  DECLARE @retval      INT
  DECLARE @max_step_id INT
  DECLARE @valid_range VARCHAR(50)

  SET NOCOUNT ON

  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT,
                                              'NO_TEST'
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- The suffix flag must be either 0 (ie. no suffix) or 1 (ie. add suffix). 0 is the default.
  IF (@suffix <> 0)
    SELECT @suffix = 1

  -- Check step id (if supplied)
  IF (@step_id IS NOT NULL)
  BEGIN
    -- Get current maximum step id
    SELECT @max_step_id = ISNULL(MAX(step_id), 0)
    FROM msdb.dbo.sysjobsteps
    WHERE job_id = @job_id
   IF @max_step_id = 0
   BEGIN
      RAISERROR(14528, -1, -1, @job_name)
      RETURN(1) -- Failure 
   END
    ELSE IF (@step_id < 1) OR (@step_id > @max_step_id)
    BEGIN
      SELECT @valid_range = '1..' + CONVERT(VARCHAR, @max_step_id)
      RAISERROR(14266, -1, -1, '@step_id', @valid_range)
      RETURN(1) -- Failure
    END
  END

  -- Check step name (if supplied)
  -- NOTE: A supplied step id overrides a supplied step name
  IF ((@step_id IS NULL) AND (@step_name IS NOT NULL))
  BEGIN
    SELECT @step_id = step_id
    FROM msdb.dbo.sysjobsteps
    WHERE (step_name = @step_name)
      AND (job_id = @job_id)

    IF (@step_id IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@step_name', @step_name)
      RETURN(1) -- Failure
    END
  END

  -- Return the job steps for this job (or just return the specific step)
  IF (@suffix = 0)
  BEGIN
    SELECT step_id,
           step_name,
           subsystem,
           command,
           flags,
           cmdexec_success_code,
           on_success_action,
           on_success_step_id,
           on_fail_action,
           on_fail_step_id,
           server,
           database_name,
           database_user_name,
           retry_attempts,
           retry_interval,
           os_run_priority,
           output_file_name,
           last_run_outcome,
           last_run_duration,
           last_run_retries,
           last_run_date,
           last_run_time,
         proxy_id
    FROM msdb.dbo.sysjobsteps
    WHERE (job_id = @job_id)
      AND ((@step_id IS NULL) OR (step_id = @step_id))
    ORDER BY job_id, step_id
  END
  ELSE
  BEGIN
    SELECT step_id,
           step_name,
           subsystem,
           command,
          'flags' = CONVERT(NVARCHAR, flags) + N' (' +
                    ISNULL(CASE WHEN (flags = 0)     THEN FORMATMESSAGE(14561) END, '') +
                    ISNULL(CASE WHEN (flags & 1) = 1 THEN FORMATMESSAGE(14558) + ISNULL(CASE WHEN (flags > 1) THEN N', ' END, '') END, '') +
                    ISNULL(CASE WHEN (flags & 2) = 2 THEN FORMATMESSAGE(14559) + ISNULL(CASE WHEN (flags > 3) THEN N', ' END, '') END, '') +
                    ISNULL(CASE WHEN (flags & 4) = 4 THEN FORMATMESSAGE(14560) END, '') + N')',
           cmdexec_success_code,
          'on_success_action' = CASE on_success_action
                                  WHEN 1 THEN CONVERT(NVARCHAR, on_success_action) + N' ' + FORMATMESSAGE(14562)
                                  WHEN 2 THEN CONVERT(NVARCHAR, on_success_action) + N' ' + FORMATMESSAGE(14563)
                                  WHEN 3 THEN CONVERT(NVARCHAR, on_success_action) + N' ' + FORMATMESSAGE(14564)
                                  WHEN 4 THEN CONVERT(NVARCHAR, on_success_action) + N' ' + FORMATMESSAGE(14565)
                                  ELSE        CONVERT(NVARCHAR, on_success_action) + N' ' + FORMATMESSAGE(14205)
                                END,
           on_success_step_id,
          'on_fail_action' = CASE on_fail_action
                               WHEN 1 THEN CONVERT(NVARCHAR, on_fail_action) + N' ' + FORMATMESSAGE(14562)
                               WHEN 2 THEN CONVERT(NVARCHAR, on_fail_action) + N' ' + FORMATMESSAGE(14563)
                               WHEN 3 THEN CONVERT(NVARCHAR, on_fail_action) + N' ' + FORMATMESSAGE(14564)
                               WHEN 4 THEN CONVERT(NVARCHAR, on_fail_action) + N' ' + FORMATMESSAGE(14565)
                               ELSE        CONVERT(NVARCHAR, on_fail_action) + N' ' + FORMATMESSAGE(14205)
                             END,
           on_fail_step_id,
           server,
           database_name,
           database_user_name,
           retry_attempts,
           retry_interval,
          'os_run_priority' = CASE os_run_priority
                                WHEN -15 THEN CONVERT(NVARCHAR, os_run_priority) + N' ' + FORMATMESSAGE(14566)
                                WHEN -1  THEN CONVERT(NVARCHAR, os_run_priority) + N' ' + FORMATMESSAGE(14567)
                                WHEN  0  THEN CONVERT(NVARCHAR, os_run_priority) + N' ' + FORMATMESSAGE(14561)
                                WHEN  1  THEN CONVERT(NVARCHAR, os_run_priority) + N' ' + FORMATMESSAGE(14568)
                                WHEN  15 THEN CONVERT(NVARCHAR, os_run_priority) + N' ' + FORMATMESSAGE(14569)
                                ELSE          CONVERT(NVARCHAR, os_run_priority) + N' ' + FORMATMESSAGE(14205)
                              END,
           output_file_name,
           last_run_outcome,
           last_run_duration,
           last_run_retries,
           last_run_date,
           last_run_time,
         proxy_id
    FROM msdb.dbo.sysjobsteps
    WHERE (job_id = @job_id)
      AND ((@step_id IS NULL) OR (step_id = @step_id))
    ORDER BY job_id, step_id
  END

  RETURN(@@error) -- 0 means success

END
go


/**************************************************************/
/* SP_WRITE_SYSJOBSTEP_LOG                                    */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_write_sysjobstep_log...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_write_sysjobstep_log')
              AND (type = 'P')))
  DROP PROCEDURE sp_write_sysjobstep_log
go
CREATE PROCEDURE sp_write_sysjobstep_log
  @job_id    UNIQUEIDENTIFIER, 
  @step_id   INT,
  @log_text  NVARCHAR(MAX),
  @append_to_last INT = 0
AS
BEGIN
  DECLARE @step_uid UNIQUEIDENTIFIER
  DECLARE @log_already_exists int
  SET @log_already_exists = 0

  SET @step_uid = ( SELECT step_uid FROM  msdb.dbo.sysjobsteps
      WHERE (job_id = @job_id)
        AND (step_id = @step_id) )
  

  IF(EXISTS(SELECT * FROM msdb.dbo.sysjobstepslogs
                      WHERE step_uid = @step_uid ))
  BEGIN
     SET @log_already_exists = 1
  END

  --Need create log if "overwrite is selected or log does not exists. 
  IF (@append_to_last = 0) OR (@log_already_exists = 0)
  BEGIN
     -- flag is overwrite
     
     --if overwrite and log exists, delete it
     IF (@append_to_last = 0 AND @log_already_exists = 1)
     BEGIN
        -- remove previous logs entries 
        EXEC sp_delete_jobsteplog @job_id, NULL, @step_id, NULL   
     END
   
     INSERT INTO msdb.dbo.sysjobstepslogs
      (
         log,
         log_size,
         step_uid
      )
      VALUES
      (
         @log_text,
         DATALENGTH(@log_text),
         @step_uid
      )
  END
  ELSE
  BEGIN
     DECLARE @log_id   INT
     --Selecting TOP is just a safety net - there is only one log entry row per step.
     SET @log_id = ( SELECT TOP 1 log_id FROM msdb.dbo.sysjobstepslogs
         WHERE (step_uid = @step_uid)
           ORDER BY log_id DESC ) 

      -- Append @log_text to the existing log record. Note that if this
      -- action would make the value of the log column longer than
      -- nvarchar(max), then the engine will raise error 599.
      UPDATE msdb.dbo.sysjobstepslogs
        SET 
             log .WRITE(@log_text,NULL,0),
             log_size = DATALENGTH(log) + DATALENGTH(@log_text) ,
             date_modified = getdate()
      WHERE log_id = @log_id
  END

  RETURN(@@error) -- 0 means success

END
go

/**************************************************************/
/* SP_HELP_JOBSTEPLOG                                    */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_help_jobsteplog...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_jobsteplog')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_jobsteplog
go
CREATE PROCEDURE sp_help_jobsteplog
  @job_id    UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name
  @job_name  sysname          = NULL, -- Must provide either this or job_id
  @step_id   INT              = NULL,
  @step_name sysname          = NULL
AS
BEGIN
  DECLARE @retval      INT
  DECLARE @max_step_id INT
  DECLARE @valid_range VARCHAR(50)

  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT,
                                              'NO_TEST'
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check step id (if supplied)
  IF (@step_id IS NOT NULL)
  BEGIN
    -- Get current maximum step id
    SELECT @max_step_id = ISNULL(MAX(step_id), 0)
    FROM msdb.dbo.sysjobsteps
    WHERE job_id = @job_id
   IF @max_step_id = 0
   BEGIN
      RAISERROR(14528, -1, -1, @job_name)
      RETURN(1) -- Failure 
   END
    ELSE IF (@step_id < 1) OR (@step_id > @max_step_id)
    BEGIN
      SELECT @valid_range = '1..' + CONVERT(VARCHAR, @max_step_id)
      RAISERROR(14266, -1, -1, '@step_id', @valid_range)
      RETURN(1) -- Failure
    END
  END

  -- Check step name (if supplied)
  -- NOTE: A supplied step id overrides a supplied step name
  IF ((@step_id IS NULL) AND (@step_name IS NOT NULL))
  BEGIN
    SELECT @step_id = step_id
    FROM msdb.dbo.sysjobsteps
    WHERE (step_name = @step_name)
      AND (job_id = @job_id)

    IF (@step_id IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@step_name', @step_name)
      RETURN(1) -- Failure
    END
  END


    SELECT sjv.job_id,
           @job_name as job_name,
           steps.step_id,
           steps.step_name,
           steps.step_uid,
           logs.date_created,
           logs.date_modified,
           logs.log_size,
           logs.log
    FROM msdb.dbo.sysjobs_view sjv, msdb.dbo.sysjobsteps as steps, msdb.dbo.sysjobstepslogs as logs 
    WHERE (sjv.job_id = @job_id)
      AND (steps.job_id = @job_id)
      AND ((@step_id IS NULL) OR (step_id = @step_id))
      AND (steps.step_uid = logs.step_uid)
   
  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_DELETE_JOBSTEPLOG                                    */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_delete_jobsteplog...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_jobsteplog')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_jobsteplog
go
CREATE PROCEDURE sp_delete_jobsteplog
  @job_id      UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name
  @job_name    sysname          = NULL, -- Must provide either this or job_id
  @step_id     INT              = NULL,
  @step_name   sysname          = NULL,
  @older_than  datetime         = NULL,
  @larger_than int      = NULL   -- (in megabytes)
AS
BEGIN
  DECLARE @retval      INT
  DECLARE @max_step_id INT
  DECLARE @valid_range VARCHAR(50)

  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT,
                                              'NO_TEST'
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check step id (if supplied)
  IF (@step_id IS NOT NULL)
  BEGIN
    -- Get current maximum step id
    SELECT @max_step_id = ISNULL(MAX(step_id), 0)
    FROM msdb.dbo.sysjobsteps
    WHERE job_id = @job_id
   IF @max_step_id = 0
   BEGIN
      RAISERROR(14528, -1, -1, @job_name)
      RETURN(1) -- Failure 
   END
    ELSE IF (@step_id < 1) OR (@step_id > @max_step_id)
    BEGIN
      SELECT @valid_range = '1..' + CONVERT(VARCHAR, @max_step_id)
      RAISERROR(14266, -1, -1, '@step_id', @valid_range)
      RETURN(1) -- Failure
    END
  END

  -- Check step name (if supplied)
  -- NOTE: A supplied step id overrides a supplied step name
  IF ((@step_id IS NULL) AND (@step_name IS NOT NULL))
  BEGIN
    SELECT @step_id = step_id
    FROM msdb.dbo.sysjobsteps
    WHERE (step_name = @step_name)
      AND (job_id = @job_id)

    IF (@step_id IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@step_name', @step_name)
      RETURN(1) -- Failure
    END
  END


   -- Delete either the specified step or ALL the steps (if step id is NULL)
   
   DELETE FROM msdb.dbo.sysjobstepslogs
   WHERE (step_uid IN (SELECT DISTINCT step_uid 
                        FROM   msdb.dbo.sysjobsteps js, msdb.dbo.sysjobs_view jv
                        WHERE (  @job_id = jv.job_id )
                          AND (js.job_id = jv.job_id )
                          AND ((@step_id IS NULL) OR (@step_id = step_id)))) 
    AND ((@older_than IS NULL) OR (date_modified < @older_than))
    AND ((@larger_than IS NULL) OR (log_size > @larger_than))

  RETURN(@retval) -- 0 means success

END
go


/**************************************************************/
/* SP_GET_SCHEDULE_DESCRIPTION                                */
/*                                                            */
/* NOTE: This SP only returns an English description of the   */
/*       schedule due to the piecemeal nature of the          */
/*       description's construction.                          */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_get_schedule_description...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_get_schedule_description')
              AND (type = 'P')))
  DROP PROCEDURE sp_get_schedule_description
go
CREATE PROCEDURE sp_get_schedule_description
  @freq_type              INT          = NULL,
  @freq_interval          INT          = NULL,
  @freq_subday_type       INT          = NULL,
  @freq_subday_interval   INT          = NULL,
  @freq_relative_interval INT          = NULL,
  @freq_recurrence_factor INT          = NULL,
  @active_start_date      INT          = NULL,
  @active_end_date        INT          = NULL,
  @active_start_time      INT          = NULL,
  @active_end_time        INT          = NULL,
  @schedule_description   NVARCHAR(255) OUTPUT
AS
BEGIN
  DECLARE @loop              INT
  DECLARE @idle_cpu_percent  INT
  DECLARE @idle_cpu_duration INT

  SET NOCOUNT ON

  IF (@freq_type = 0x1) -- OneTime
  BEGIN
    SELECT @schedule_description = N'Once on ' + CONVERT(NVARCHAR, @active_start_date) + N' at ' + CONVERT(NVARCHAR, @active_start_time)
    RETURN
  END

  IF (@freq_type = 0x4) -- Daily
  BEGIN
    SELECT @schedule_description = N'Every day '
  END

  IF (@freq_type = 0x8) -- Weekly
  BEGIN
    SELECT @schedule_description = N'Every ' + CONVERT(NVARCHAR, @freq_recurrence_factor) + N' week(s) on '
    SELECT @loop = 1
    WHILE (@loop <= 7)
    BEGIN
      IF (@freq_interval & POWER(2, @loop - 1) = POWER(2, @loop - 1))
        SELECT @schedule_description = @schedule_description + DATENAME(dw, N'1996120' + CONVERT(NVARCHAR, @loop)) + N', '
      SELECT @loop = @loop + 1
    END
    IF (RIGHT(@schedule_description, 2) = N', ')
      SELECT @schedule_description = SUBSTRING(@schedule_description, 1, (DATALENGTH(@schedule_description) / 2) - 2) + N' '
  END

  IF (@freq_type = 0x10) -- Monthly
  BEGIN
    SELECT @schedule_description = N'Every ' + CONVERT(NVARCHAR, @freq_recurrence_factor) + N' months(s) on day ' + CONVERT(NVARCHAR, @freq_interval) + N' of that month '
  END

  IF (@freq_type = 0x20) -- Monthly Relative
  BEGIN
    SELECT @schedule_description = N'Every ' + CONVERT(NVARCHAR, @freq_recurrence_factor) + N' months(s) on the '
    SELECT @schedule_description = @schedule_description +
      CASE @freq_relative_interval
        WHEN 0x01 THEN N'first '
        WHEN 0x02 THEN N'second '
        WHEN 0x04 THEN N'third '
        WHEN 0x08 THEN N'fourth '
        WHEN 0x10 THEN N'last '
      END +
      CASE
        WHEN (@freq_interval > 00)
         AND (@freq_interval < 08) THEN DATENAME(dw, N'1996120' + CONVERT(NVARCHAR, @freq_interval))
        WHEN (@freq_interval = 08) THEN N'day'
        WHEN (@freq_interval = 09) THEN N'week day'
        WHEN (@freq_interval = 10) THEN N'weekend day'
      END + N' of that month '
  END

  IF (@freq_type = 0x40) -- AutoStart
  BEGIN
    SELECT @schedule_description = FORMATMESSAGE(14579)
    RETURN
  END

  IF (@freq_type = 0x80) -- OnIdle
  BEGIN
    EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                           N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                           N'IdleCPUPercent',
                                           @idle_cpu_percent OUTPUT,
                                           N'no_output'
    EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                           N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                           N'IdleCPUDuration',
                                           @idle_cpu_duration OUTPUT,
                                           N'no_output'
    SELECT @schedule_description = FORMATMESSAGE(14578, ISNULL(@idle_cpu_percent, 10), ISNULL(@idle_cpu_duration, 600))
    RETURN
  END

  -- Subday stuff
  SELECT @schedule_description = @schedule_description +
    CASE @freq_subday_type
      WHEN 0x1 THEN N'at ' + CONVERT(NVARCHAR, @active_start_time)
      WHEN 0x2 THEN N'every ' + CONVERT(NVARCHAR, @freq_subday_interval) + N' second(s)'
      WHEN 0x4 THEN N'every ' + CONVERT(NVARCHAR, @freq_subday_interval) + N' minute(s)'
      WHEN 0x8 THEN N'every ' + CONVERT(NVARCHAR, @freq_subday_interval) + N' hour(s)'
    END
  IF (@freq_subday_type IN (0x2, 0x4, 0x8))
    SELECT @schedule_description = @schedule_description + N' between ' +
           CONVERT(NVARCHAR, @active_start_time) + N' and ' + CONVERT(NVARCHAR, @active_end_time)
END
go

CHECKPOINT
go


/**************************************************************/
/* SP_ADD_JOBSCHEDULE                                         */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_jobschedule...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_jobschedule')
              AND (type = 'P')))
  DROP PROCEDURE sp_add_jobschedule
go
CREATE PROCEDURE sp_add_jobschedule                 -- This SP is deprecated. Use sp_add_schedule and sp_attach_schedule.
  @job_id                 UNIQUEIDENTIFIER = NULL,
  @job_name               sysname          = NULL,
  @name                   sysname,
  @enabled                TINYINT          = 1,
  @freq_type              INT              = 1,
  @freq_interval          INT              = 0,
  @freq_subday_type       INT              = 0,
  @freq_subday_interval   INT              = 0,
  @freq_relative_interval INT              = 0,
  @freq_recurrence_factor INT              = 0,
  @active_start_date      INT              = NULL,     -- sp_verify_schedule assigns a default
  @active_end_date        INT              = 99991231, -- December 31st 9999
  @active_start_time      INT              = 000000,   -- 12:00:00 am
  @active_end_time        INT              = 235959,    -- 11:59:59 pm
  @schedule_id            INT              = NULL  OUTPUT,
  @automatic_post         BIT              = 1,         -- If 1 will post notifications to all tsx servers to that run this job
  @schedule_uid           UNIQUEIDENTIFIER = NULL OUTPUT
AS
BEGIN
  DECLARE @retval           INT
  DECLARE @owner_login_name sysname

  SET NOCOUNT ON

  -- Check authority (only SQLServerAgent can add a schedule to a non-local job)
  EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%'
  IF (@retval <> 0)
    RETURN(@retval)

  -- Check that we can uniquely identify the job
  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Get the owner of the job. Prior to resusable schedules the job owner also owned the schedule
  SELECT @owner_login_name = dbo.SQLAGENT_SUSER_SNAME(owner_sid)
  FROM   sysjobs
  WHERE  (job_id = @job_id) 

  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) AND
      (SUSER_SNAME() <> @owner_login_name))
  BEGIN
   RAISERROR(14525, -1, -1)
   RETURN(1) -- Failure
  END

  -- Check authority (only SQLServerAgent can add a schedule to a non-local job)
  EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%'
  IF (@retval <> 0)
    RETURN(@retval)

  -- Add the schedule first
  EXECUTE @retval = msdb.dbo.sp_add_schedule @schedule_name          = @name,
                                             @enabled                = @enabled,
                                             @freq_type              = @freq_type,
                                             @freq_interval          = @freq_interval,
                                             @freq_subday_type       = @freq_subday_type,
                                             @freq_subday_interval   = @freq_subday_interval,
                                             @freq_relative_interval = @freq_relative_interval,
                                             @freq_recurrence_factor = @freq_recurrence_factor,
                                             @active_start_date      = @active_start_date,
                                             @active_end_date        = @active_end_date,
                                             @active_start_time      = @active_start_time,
                                             @active_end_time        = @active_end_time,
                                             @owner_login_name       = @owner_login_name,
                                             @schedule_uid           = @schedule_uid OUTPUT,
                                             @schedule_id            = @schedule_id  OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure
 
 
  EXECUTE @retval = msdb.dbo.sp_attach_schedule @job_id           = @job_id, 
                                                @job_name         = NULL,
                                                @schedule_id      = @schedule_id,
                                                @schedule_name    = NULL,
                                                @automatic_post   = @automatic_post
  IF (@retval <> 0)
    RETURN(1) -- Failure
    
    

  RETURN(@retval) -- 0 means success
END
go

/**************************************************************/
/* SP_UPDATE_JOBSCHEDULE                                      */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_update_jobschedule...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_update_jobschedule')
              AND (type = 'P')))
  DROP PROCEDURE sp_update_jobschedule
go
CREATE PROCEDURE sp_update_jobschedule              -- This SP is deprecated by sp_update_schedule.
  @job_id                 UNIQUEIDENTIFIER = NULL,
  @job_name               sysname          = NULL,
  @name                   sysname,
  @new_name               sysname          = NULL,
  @enabled                TINYINT          = NULL,
  @freq_type              INT              = NULL,
  @freq_interval          INT              = NULL,
  @freq_subday_type       INT              = NULL,
  @freq_subday_interval   INT              = NULL,
  @freq_relative_interval INT              = NULL,
  @freq_recurrence_factor INT              = NULL,
  @active_start_date      INT              = NULL,
  @active_end_date        INT              = NULL,
  @active_start_time      INT              = NULL,
  @active_end_time        INT              = NULL,
  @automatic_post         BIT              = 1         -- If 1 will post notifications to all tsx servers to that run this job
AS
BEGIN
  DECLARE @retval                   INT
  DECLARE @sched_count              INT
  DECLARE @schedule_id              INT
  DECLARE @job_owner_sid            VARBINARY(85)
  DECLARE @enable_only_used         INT

  DECLARE @x_name                   sysname
  DECLARE @x_enabled                TINYINT
  DECLARE @x_freq_type              INT
  DECLARE @x_freq_interval          INT
  DECLARE @x_freq_subday_type       INT
  DECLARE @x_freq_subday_interval   INT
  DECLARE @x_freq_relative_interval INT
  DECLARE @x_freq_recurrence_factor INT
  DECLARE @x_active_start_date      INT
  DECLARE @x_active_end_date        INT
  DECLARE @x_active_start_time      INT
  DECLARE @x_active_end_time        INT
  DECLARE @owner_sid                VARBINARY(85)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name     = LTRIM(RTRIM(@name))
  SELECT @new_name = LTRIM(RTRIM(@new_name))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@new_name = N'') SELECT @new_name = NULL

  -- Check authority (only SQLServerAgent can modify a schedule of a non-local job)
  EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = 'SQLAgent%'
  IF (@retval <> 0)
    RETURN(@retval)

  -- Check that we can uniquely identify the job
  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT,
                                               @owner_sid = @job_owner_sid OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure
    
  -- Is @enable the only parameter used beside jobname and jobid?
  IF ((@enabled                   IS NOT NULL) AND
     (@name                 IS NULL) AND
     (@new_name                IS NULL) AND
      (@freq_type              IS NULL) AND
      (@freq_interval             IS NULL) AND
      (@freq_subday_type          IS NULL) AND
      (@freq_subday_interval      IS NULL) AND
      (@freq_relative_interval       IS NULL) AND
      (@freq_recurrence_factor       IS NULL) AND
      (@active_start_date         IS NULL) AND
      (@active_end_date           IS NULL) AND
      (@active_start_time         IS NULL) AND
      (@active_end_time           IS NULL))
    SELECT @enable_only_used = 1
  ELSE
    SELECT @enable_only_used = 0
    

  IF ((SUSER_SID() <> @job_owner_sid)
     AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
      AND (@enable_only_used <> 1 OR ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) <> 1))
  BEGIN
   RAISERROR(14525, -1, -1)
   RETURN(1) -- Failure
  END

  -- Make sure the schedule_id can be uniquely identified and that it exists
  -- Note: It's safe use the values returned by the MIN() function because the SP errors out if more than 1 record exists
  SELECT @sched_count = COUNT(*),
         @schedule_id = MIN(sched.schedule_id),
         @owner_sid   = MIN(sched.owner_sid)
  FROM msdb.dbo.sysjobschedules as jsched
    JOIN msdb.dbo.sysschedules_localserver_view as sched
      ON jsched.schedule_id = sched.schedule_id
  WHERE (jsched.job_id = @job_id)
    AND (sched.name = @name)

  -- Need to use sp_update_schedule to update this ambiguous schedule name
  IF(@sched_count > 1)
  BEGIN
    RAISERROR(14375, -1, -1, @name, @job_name)
    RETURN(1) -- Failure
  END

  IF (@schedule_id IS NULL)
  BEGIN
   --raise an explicit message if the schedule does exist but isn't attached to this job
   IF EXISTS(SELECT * 
           FROM sysschedules_localserver_view
              WHERE (name = @name))
   BEGIN
      RAISERROR(14374, -1, -1, @name, @job_name)
   END
   ELSE
    BEGIN
      --If the schedule is from an MSX and a sysadmin is calling report a specific 'MSX' message
      IF(PROGRAM_NAME() NOT LIKE N'SQLAgent%' AND
         ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 AND
         EXISTS(SELECT * 
                FROM msdb.dbo.sysschedules as sched
                  JOIN msdb.dbo.sysoriginatingservers_view as svr
                    ON sched.originating_server_id = svr.originating_server_id
                  JOIN msdb.dbo.sysjobschedules as js 
                    ON sched.schedule_id = js.schedule_id
                WHERE (svr.master_server = 1) AND
                      (sched.name = @name) AND
                      (js.job_id = @job_id)))
     BEGIN
       RAISERROR(14274, -1, -1)
     END
      ELSE
      BEGIN
        --Generic message that the schedule doesn't exist
        RAISERROR(14262, -1, -1, 'Schedule Name', @name)
      END
   END

   RETURN(1) -- Failure
  END

  -- Set the x_ (existing) variables
  SELECT @x_name                   = name,
         @x_enabled                = enabled,
         @x_freq_type              = freq_type,
         @x_freq_interval          = freq_interval,
         @x_freq_subday_type       = freq_subday_type,
         @x_freq_subday_interval   = freq_subday_interval,
         @x_freq_relative_interval = freq_relative_interval,
         @x_freq_recurrence_factor = freq_recurrence_factor,
         @x_active_start_date      = active_start_date,
         @x_active_end_date        = active_end_date,
         @x_active_start_time      = active_start_time,
         @x_active_end_time        = active_end_time
  FROM msdb.dbo.sysschedules_localserver_view
  WHERE (schedule_id = @schedule_id )

  
  -- Fill out the values for all non-supplied parameters from the existing values
  IF (@new_name               IS NULL) SELECT @new_name               = @x_name
  IF (@enabled                IS NULL) SELECT @enabled                = @x_enabled
  IF (@freq_type              IS NULL) SELECT @freq_type              = @x_freq_type
  IF (@freq_interval          IS NULL) SELECT @freq_interval          = @x_freq_interval
  IF (@freq_subday_type       IS NULL) SELECT @freq_subday_type       = @x_freq_subday_type
  IF (@freq_subday_interval   IS NULL) SELECT @freq_subday_interval   = @x_freq_subday_interval
  IF (@freq_relative_interval IS NULL) SELECT @freq_relative_interval = @x_freq_relative_interval
  IF (@freq_recurrence_factor IS NULL) SELECT @freq_recurrence_factor = @x_freq_recurrence_factor
  IF (@active_start_date      IS NULL) SELECT @active_start_date      = @x_active_start_date
  IF (@active_end_date        IS NULL) SELECT @active_end_date        = @x_active_end_date
  IF (@active_start_time      IS NULL) SELECT @active_start_time      = @x_active_start_time
  IF (@active_end_time        IS NULL) SELECT @active_end_time        = @x_active_end_time

  -- Check schedule (frequency and owner) parameters
  EXECUTE @retval = sp_verify_schedule @schedule_id             = @schedule_id,
                                       @name                    = @new_name,
                                       @enabled                 = @enabled,
                                       @freq_type               = @freq_type,
                                       @freq_interval           = @freq_interval            OUTPUT,
                                       @freq_subday_type        = @freq_subday_type         OUTPUT,
                                       @freq_subday_interval    = @freq_subday_interval     OUTPUT,
                                       @freq_relative_interval  = @freq_relative_interval   OUTPUT,
                                       @freq_recurrence_factor  = @freq_recurrence_factor   OUTPUT,
                                       @active_start_date       = @active_start_date        OUTPUT,
                                       @active_start_time       = @active_start_time        OUTPUT,
                                       @active_end_date         = @active_end_date          OUTPUT,
                                       @active_end_time         = @active_end_time          OUTPUT,
                                       @owner_sid               = @owner_sid
  IF (@retval <> 0)
    RETURN(1) -- Failure


  -- Update the JobSchedule
  UPDATE msdb.dbo.sysschedules
  SET name                   = @new_name,
      enabled                = @enabled,
      freq_type              = @freq_type,
      freq_interval          = @freq_interval,
      freq_subday_type       = @freq_subday_type,
      freq_subday_interval   = @freq_subday_interval,
      freq_relative_interval = @freq_relative_interval,
      freq_recurrence_factor = @freq_recurrence_factor,
      active_start_date      = @active_start_date,
      active_end_date        = @active_end_date,
      active_start_time      = @active_start_time,
      active_end_time        = @active_end_time,
      date_modified          = GETDATE(),
      version_number         = version_number + 1
  WHERE (schedule_id = @schedule_id)

  SELECT @retval = @@error

  -- Update the job's version/last-modified information
  UPDATE msdb.dbo.sysjobs
  SET version_number = version_number + 1,
      date_modified = GETDATE()
  WHERE (job_id = @job_id)

  -- Notify SQLServerAgent of the change, but only if we know the job has been cached
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers
              WHERE (job_id = @job_id)
                AND (server_id = 0)))
  BEGIN
    EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'S',
                                        @job_id      = @job_id,
                                        @schedule_id = @schedule_id,
                                        @action_type = N'U'
  END

  -- For a multi-server job, remind the user that they need to call sp_post_msx_operation
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers
              WHERE (job_id = @job_id)
                AND (server_id <> 0)))
    -- Instruct the tsx servers to pick up the altered schedule
    IF (@automatic_post = 1)
    BEGIN
      DECLARE @schedule_uid UNIQUEIDENTIFIER
      SELECT @schedule_uid = schedule_uid 
      FROM sysschedules 
      WHERE schedule_id = @schedule_id

      IF(NOT @schedule_uid IS NULL)
      BEGIN
        -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines 
        EXECUTE sp_post_msx_operation @operation = 'INSERT', @object_type = 'SCHEDULE', @schedule_uid = @schedule_uid
      END
    END
    ELSE
      RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation')

  -- Automatic addition and removal of -Continous parameter for replication agent
  EXECUTE sp_update_replication_job_parameter @job_id = @job_id,
                                              @old_freq_type = @x_freq_type,
                                              @new_freq_type = @freq_type

  RETURN(@retval) -- 0 means success
END
go

/**************************************************************/
/* SP_DELETE_JOBSCHEDULE                                      */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_jobschedule...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_delete_jobschedule')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_jobschedule
go
CREATE PROCEDURE sp_delete_jobschedule           -- This SP is deprecated. Use sp_detach_schedule and sp_delete_schedule.
  @job_id           UNIQUEIDENTIFIER = NULL,
  @job_name         sysname          = NULL,
  @name             sysname,
  @keep_schedule    int              = 0,
  @automatic_post       BIT          = 1         -- If 1 will post notifications to all tsx servers to that run this schedule
AS
BEGIN
  DECLARE @retval           INT
  DECLARE @sched_count      INT
  DECLARE @schedule_id      INT
  DECLARE @job_owner_sid    VARBINARY(85)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name = LTRIM(RTRIM(@name))

  -- Check authority (only SQLServerAgent can delete a schedule of a non-local job)
  EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%'
  IF (@retval <> 0)
    RETURN(@retval)

  -- Check that we can uniquely identify the job
  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT,
                                               @owner_sid = @job_owner_sid OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) AND
      (SUSER_SID() <> @job_owner_sid))
  BEGIN
   RAISERROR(14525, -1, -1)
   RETURN(1) -- Failure
  END


  IF (UPPER(@name collate SQL_Latin1_General_CP1_CS_AS) = N'ALL')
  BEGIN
    SELECT @schedule_id = -1  -- We use this in the call to sp_sqlagent_notify
    
    --Delete the schedule(s) if it isn't being used by other jobs
    DECLARE @temp_schedules_to_delete TABLE (schedule_id INT NOT NULL)


    --If user requests that the schedules be removed (the legacy behavoir)
    --make sure it isnt being used by other jobs
    IF (@keep_schedule = 0)
    BEGIN
        --Get the list of schedules to delete
        INSERT INTO @temp_schedules_to_delete
        SELECT DISTINCT schedule_id 
        FROM   msdb.dbo.sysschedules
        WHERE (schedule_id IN 
                (SELECT schedule_id
                FROM msdb.dbo.sysjobschedules
                WHERE (job_id = @job_id)))
            
        --make sure no other jobs use these schedules
        IF( EXISTS(SELECT *
                    FROM msdb.dbo.sysjobschedules 
                    WHERE (job_id <> @job_id)
                    AND (schedule_id in ( SELECT schedule_id 
                                            FROM @temp_schedules_to_delete ))))
        BEGIN
        RAISERROR(14367, -1, -1)   
        RETURN(1) -- Failure
        END
    END

    --OK to delete the jobschedule
    DELETE FROM msdb.dbo.sysjobschedules
    WHERE (job_id = @job_id)
    
    --OK to delete the schedule - temp_schedules_to_delete is empty if @keep_schedule <> 0
    DELETE FROM msdb.dbo.sysschedules
    WHERE schedule_id IN 
    (SELECT schedule_id from @temp_schedules_to_delete)
  END
  ELSE
  BEGIN

    -- Make sure the schedule_id can be uniquely identified and that it exists
    -- Note: It's safe use the values returned by the MIN() function because the SP errors out if more than 1 record exists
    SELECT @sched_count = COUNT(*),
           @schedule_id = MIN(sched.schedule_id)
    FROM msdb.dbo.sysjobschedules as jsched
      JOIN msdb.dbo.sysschedules_localserver_view as sched
        ON jsched.schedule_id = sched.schedule_id
    WHERE (jsched.job_id = @job_id)
      AND (sched.name = @name)
  
    -- Need to use sp_detach_schedule to remove this ambiguous schedule name
    IF(@sched_count > 1)
    BEGIN
      RAISERROR(14376, -1, -1, @name, @job_name)
      RETURN(1) -- Failure
    END

    IF (@schedule_id IS NULL)
    BEGIN    
     --raise an explicit message if the schedule does exist but isn't attached to this job
     IF EXISTS(SELECT * 
             FROM sysschedules_localserver_view
                WHERE (name = @name))
     BEGIN
      RAISERROR(14374, -1, -1, @name, @job_name)
     END
     ELSE
      BEGIN
        --If the schedule is from an MSX and a sysadmin is calling report a specific 'MSX' message
        IF(PROGRAM_NAME() NOT LIKE N'SQLAgent%' AND
           ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 AND
           EXISTS(SELECT * 
                  FROM msdb.dbo.sysschedules as sched
                    JOIN msdb.dbo.sysoriginatingservers_view as svr
                      ON sched.originating_server_id = svr.originating_server_id
                    JOIN msdb.dbo.sysjobschedules as js 
                      ON sched.schedule_id = js.schedule_id
                  WHERE (svr.master_server = 1) AND
                        (sched.name = @name) AND
                        (js.job_id = @job_id)))
       BEGIN
         RAISERROR(14274, -1, -1)
       END
        ELSE
        BEGIN
          --Generic message that the schedule doesn't exist
          RAISERROR(14262, -1, -1, '@name', @name)
        END
     END

      RETURN(1) -- Failure
    END

    --If user requests that the schedule be removed (the legacy behavoir)
    --make sure it isnt being used by another job
    IF (@keep_schedule = 0)
    BEGIN
      IF( EXISTS(SELECT * 
                 FROM msdb.dbo.sysjobschedules
                 WHERE (schedule_id = @schedule_id)
                   AND (job_id <> @job_id) ))
      BEGIN
        RAISERROR(14368, -1, -1, @name)
        RETURN(1) -- Failure
      END
    END

    --Delete the job schedule link first
    DELETE FROM msdb.dbo.sysjobschedules
    WHERE (job_id = @job_id)
    AND (schedule_id = @schedule_id)
    --Delete schedule if required
    IF (@keep_schedule = 0)
    BEGIN
      --Now delete the schedule if required
      DELETE FROM msdb.dbo.sysschedules
      WHERE (schedule_id = @schedule_id)   
    END

  END


  -- Update the job's version/last-modified information
  UPDATE msdb.dbo.sysjobs
  SET version_number = version_number + 1,
      date_modified = GETDATE()
  WHERE (job_id = @job_id)

  -- Notify SQLServerAgent of the change, but only if we know the job has been cached
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers
              WHERE (job_id = @job_id)
                AND (server_id = 0)))
  BEGIN
    EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'S',
                                        @job_id      = @job_id,
                                        @schedule_id = @schedule_id,
                                        @action_type = N'D'
  END

  -- For a multi-server job, remind the user that they need to call sp_post_msx_operation
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers
              WHERE (job_id = @job_id)
                AND (server_id <> 0)))
    -- Instruct the tsx servers to pick up the altered schedule
    IF (@automatic_post = 1)
    BEGIN
      DECLARE @schedule_uid UNIQUEIDENTIFIER
      SELECT @schedule_uid = schedule_uid 
      FROM sysschedules 
      WHERE schedule_id = @schedule_id

      IF(NOT @schedule_uid IS NULL)
      BEGIN
        -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines 
        EXECUTE sp_post_msx_operation @operation = 'INSERT', @object_type = 'SCHEDULE', @schedule_uid = @schedule_uid
      END
    END
    ELSE
      RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation')

  RETURN(@retval) -- 0 means success
END
go


/**************************************************************/
/* SP_HELP_SCHEDULE                                           */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_schedule...'
go

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_schedule')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_schedule
go

CREATE PROCEDURE sp_help_schedule
  @schedule_id              INT     = NULL, -- If both @schedule_id and @schedule_name are NULL retreive all schedules 
  @schedule_name            sysname = NULL,
  @attached_schedules_only  BIT     = 0,    -- If 1 only retreive all schedules that are attached to jobs
  @include_description      BIT     = 0     -- 1 if a schedule description is required (NOTE: It's expensive to generate the description)
AS
BEGIN
  DECLARE @retval                 INT
  DECLARE @schedule_description   NVARCHAR(255)
  DECLARE @name                   sysname
  DECLARE @freq_type              INT
  DECLARE @freq_interval          INT
  DECLARE @freq_subday_type       INT
  DECLARE @freq_subday_interval   INT
  DECLARE @freq_relative_interval INT
  DECLARE @freq_recurrence_factor INT
  DECLARE @active_start_date      INT
  DECLARE @active_end_date        INT
  DECLARE @active_start_time      INT
  DECLARE @active_end_time        INT
  DECLARE @schedule_id_as_char    VARCHAR(10)
  
  SET NOCOUNT ON
    
  -- If both @schedule_id and @schedule_name are NULL retreive all schedules (depending on @attached_schedules_only)
  -- otherwise verify the schedule exists
  IF (@schedule_id IS NOT NULL) OR (@schedule_name IS NOT NULL)  
  BEGIN    
    -- Check that we can uniquely identify the schedule
    EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name',
                                                              @name_of_id_parameter   = '@schedule_id',
                                                              @schedule_name          = @schedule_name OUTPUT,
                                                              @schedule_id            = @schedule_id   OUTPUT,
                                                              @owner_sid              = NULL,
                                                              @orig_server_id         = NULL
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END


  -- Get the schedule(s) that are attached to a job (or all schs if @attached_schedules_only = 0) into a temporary table
  SELECT schedule_id,
         schedule_uid,
        'schedule_name' = name,
         enabled,
         freq_type,
         freq_interval,
         freq_subday_type,
         freq_subday_interval,
         freq_relative_interval,
         freq_recurrence_factor,
         active_start_date,
         active_end_date,
         active_start_time,
         active_end_time,
         date_created,
        'schedule_description' = FORMATMESSAGE(14549)
  INTO #temp_jobschedule
  FROM msdb.dbo.sysschedules_localserver_view as sch
  WHERE ( (@attached_schedules_only = 0) 
         OR (EXISTS(SELECT * FROM sysjobschedules as jobsch WHERE sch.schedule_id = jobsch.schedule_id)) )
    AND((@schedule_id IS NULL) OR (schedule_id = @schedule_id))

  IF (@include_description = 1)
  BEGIN
    -- For each schedule, generate the textual schedule description and update the temporary
    -- table with it
    IF (EXISTS (SELECT *
                FROM #temp_jobschedule))
    BEGIN
      WHILE (EXISTS (SELECT *
                     FROM #temp_jobschedule
                     WHERE schedule_description = FORMATMESSAGE(14549)))
      BEGIN
        SET ROWCOUNT 1
        SELECT @name                   = schedule_name,
               @freq_type              = freq_type,
               @freq_interval          = freq_interval,
               @freq_subday_type       = freq_subday_type,
               @freq_subday_interval   = freq_subday_interval,
               @freq_relative_interval = freq_relative_interval,
               @freq_recurrence_factor = freq_recurrence_factor,
               @active_start_date      = active_start_date,
               @active_end_date        = active_end_date,
               @active_start_time      = active_start_time,
               @active_end_time        = active_end_time
        FROM #temp_jobschedule
        WHERE (schedule_description = FORMATMESSAGE(14549))
        SET ROWCOUNT 0

        EXECUTE sp_get_schedule_description
          @freq_type,
          @freq_interval,
          @freq_subday_type,
          @freq_subday_interval,
          @freq_relative_interval,
          @freq_recurrence_factor,
          @active_start_date,
          @active_end_date,
          @active_start_time,
          @active_end_time,
          @schedule_description OUTPUT

        UPDATE #temp_jobschedule
        SET schedule_description = ISNULL(LTRIM(RTRIM(@schedule_description)), FORMATMESSAGE(14205))
        WHERE (schedule_name = @name)
      END -- While
    END
  END

  -- Return the result set, adding job count
  SELECT *, (SELECT COUNT(*) FROM sysjobschedules WHERE sysjobschedules.schedule_id = #temp_jobschedule.schedule_id) as 'job_count'
  FROM #temp_jobschedule
  ORDER BY schedule_id

  RETURN(@@error) -- 0 means success
END
go


/**************************************************************/
/* SP_HELP_JOBSCHEDULE                                        */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_jobschedule...'

go


IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_jobschedule')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_jobschedule

go

CREATE PROCEDURE sp_help_jobschedule
  @job_id              UNIQUEIDENTIFIER = NULL,
  @job_name            sysname          = NULL,
  @schedule_name       sysname          = NULL,
  @schedule_id         INT              = NULL,
  @include_description BIT              = 0 -- 1 if a schedule description is required (NOTE: It's expensive to generate the description)
AS
BEGIN
  DECLARE @retval                 INT
  DECLARE @schedule_description   NVARCHAR(255)
  DECLARE @name                   sysname
  DECLARE @freq_type              INT
  DECLARE @freq_interval          INT
  DECLARE @freq_subday_type       INT
  DECLARE @freq_subday_interval   INT
  DECLARE @freq_relative_interval INT
  DECLARE @freq_recurrence_factor INT
  DECLARE @active_start_date      INT
  DECLARE @active_end_date        INT
  DECLARE @active_start_time      INT
  DECLARE @active_end_time        INT
  DECLARE @schedule_id_as_char    VARCHAR(10)
  DECLARE @job_count               INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @schedule_name = LTRIM(RTRIM(@schedule_name))
  SELECT @job_count = 0

  -- Turn [nullable] empty string parameters into NULLs
  IF (@schedule_name = N'') SELECT @schedule_name = NULL

  -- The user must provide either:
  -- 1) job_id (or job_name) and (optionally) a schedule name
  -- or...
  -- 2) just schedule_id
  IF (@schedule_id IS NULL) AND
     (@job_id      IS NULL) AND
     (@job_name    IS NULL)
  BEGIN
    RAISERROR(14273, -1, -1)
    RETURN(1) -- Failure
  END

  IF (@schedule_id IS NOT NULL) AND ((@job_id        IS NOT NULL) OR
                                     (@job_name      IS NOT NULL) OR
                                     (@schedule_name IS NOT NULL))
  BEGIN
    RAISERROR(14273, -1, -1)
    RETURN(1) -- Failure
  END

  -- Check that the schedule (by ID) exists and it is only used by one job. 
  -- Allowing this for backward compatibility with versions prior to V9
  IF (@schedule_id IS NOT NULL) AND 
     (@job_id      IS NULL) AND
     (@job_name    IS NULL)
  BEGIN
  
    SELECT @job_count = COUNT(*)
    FROM msdb.dbo.sysjobschedules
    WHERE (schedule_id = @schedule_id) 
    
    if(@job_count > 1)
    BEGIN
      SELECT @schedule_id_as_char = CONVERT(VARCHAR, @schedule_id)
      RAISERROR(14369, -1, -1, @schedule_id_as_char)
      RETURN(1) -- Failure
    END
  
    SELECT @job_id = job_id
    FROM msdb.dbo.sysjobschedules
    WHERE (schedule_id = @schedule_id)
    IF (@job_id IS NULL)
    BEGIN
      SELECT @schedule_id_as_char = CONVERT(VARCHAR, @schedule_id)
      RAISERROR(14262, -1, -1, '@schedule_id', @schedule_id_as_char)
      RETURN(1) -- Failure
    END
  END

  -- Check that we can uniquely identify the job
  IF (@job_id IS NOT NULL) OR (@job_name IS NOT NULL)
  BEGIN
    EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                                '@job_id',
                                                 @job_name OUTPUT,
                                                 @job_id   OUTPUT,
                                                'NO_TEST'
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END

  IF (@schedule_id IS NOT NULL OR @schedule_name IS NOT NULL)
  BEGIN
    -- Check that we can uniquely identify the schedule
    EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name',
                                                              @name_of_id_parameter   = '@schedule_id',
                                                              @schedule_name          = @schedule_name OUTPUT,
                                                              @schedule_id            = @schedule_id   OUTPUT,
                                                              @owner_sid              = NULL,
                                                              @orig_server_id         = NULL,
                                                              @job_id_filter          = @job_id
    IF (@retval <> 0)
      RETURN(1) -- Failure
  
  END

  -- Check that the schedule (by name) exists
  IF (@schedule_name IS NOT NULL)
  BEGIN
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.sysjobschedules AS js
                      JOIN msdb.dbo.sysschedules AS s
                        ON js.schedule_id = s.schedule_id
                    WHERE (js.job_id = @job_id)
                      AND (s.name = @schedule_name)))
    BEGIN
      RAISERROR(14262, -1, -1, '@schedule_name', @schedule_name)
      RETURN(1) -- Failure
    END
  END

  -- Get the schedule(s) into a temporary table
  SELECT s.schedule_id,
        'schedule_name' = name,
         enabled,
         freq_type,
         freq_interval,
         freq_subday_type,
         freq_subday_interval,
         freq_relative_interval,
         freq_recurrence_factor,
         active_start_date,
         active_end_date,
         active_start_time,
         active_end_time,
         date_created,
        'schedule_description' = FORMATMESSAGE(14549),
         js.next_run_date,
         js.next_run_time,
         s.schedule_uid
  INTO #temp_jobschedule
  FROM msdb.dbo.sysjobschedules AS js
    JOIN msdb.dbo.sysschedules AS s
      ON js.schedule_id = s.schedule_id
  WHERE ((@job_id IS NULL) OR (js.job_id = @job_id))
    AND ((@schedule_name IS NULL) OR (s.name = @schedule_name))
    AND ((@schedule_id IS NULL) OR (s.schedule_id = @schedule_id))

  IF (@include_description = 1)
  BEGIN
    -- For each schedule, generate the textual schedule description and update the temporary
    -- table with it
    IF (EXISTS (SELECT *
                FROM #temp_jobschedule))
    BEGIN
      WHILE (EXISTS (SELECT *
                     FROM #temp_jobschedule
                     WHERE schedule_description = FORMATMESSAGE(14549)))
      BEGIN
        SET ROWCOUNT 1
        SELECT @name                   = schedule_name,
               @freq_type              = freq_type,
               @freq_interval          = freq_interval,
               @freq_subday_type       = freq_subday_type,
               @freq_subday_interval   = freq_subday_interval,
               @freq_relative_interval = freq_relative_interval,
               @freq_recurrence_factor = freq_recurrence_factor,
               @active_start_date      = active_start_date,
               @active_end_date        = active_end_date,
               @active_start_time      = active_start_time,
               @active_end_time        = active_end_time
        FROM #temp_jobschedule
        WHERE (schedule_description = FORMATMESSAGE(14549))
        SET ROWCOUNT 0

        EXECUTE sp_get_schedule_description
          @freq_type,
          @freq_interval,
          @freq_subday_type,
          @freq_subday_interval,
          @freq_relative_interval,
          @freq_recurrence_factor,
          @active_start_date,
          @active_end_date,
          @active_start_time,
          @active_end_time,
          @schedule_description OUTPUT

        UPDATE #temp_jobschedule
        SET schedule_description = ISNULL(LTRIM(RTRIM(@schedule_description)), FORMATMESSAGE(14205))
        WHERE (schedule_name = @name)
      END -- While
    END
  END

  -- Return the result set, adding job count to it
  SELECT *, (SELECT COUNT(*) FROM sysjobschedules WHERE sysjobschedules.schedule_id = #temp_jobschedule.schedule_id) as 'job_count'
  FROM #temp_jobschedule
  ORDER BY schedule_id

  RETURN(@@error) -- 0 means success
END

go

CHECKPOINT
go

/**************************************************************/
/* SP_VERIFY_JOB                                              */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_job...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_job')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_job
go
CREATE PROCEDURE sp_verify_job
  @job_id                       UNIQUEIDENTIFIER,
  @name                         sysname,
  @enabled                      TINYINT,
  @start_step_id                INT,
  @category_name                sysname,
  @owner_sid                    VARBINARY(85) OUTPUT, -- Output since we may modify it
  @notify_level_eventlog        INT,
  @notify_level_email           INT           OUTPUT, -- Output since we may reset it to 0
  @notify_level_netsend         INT           OUTPUT, -- Output since we may reset it to 0
  @notify_level_page            INT           OUTPUT, -- Output since we may reset it to 0
  @notify_email_operator_name   sysname,
  @notify_netsend_operator_name sysname,
  @notify_page_operator_name    sysname,
  @delete_level                 INT,
  @category_id                  INT           OUTPUT, -- The ID corresponding to the name
  @notify_email_operator_id     INT           OUTPUT, -- The ID corresponding to the name
  @notify_netsend_operator_id   INT           OUTPUT, -- The ID corresponding to the name
  @notify_page_operator_id      INT           OUTPUT, -- The ID corresponding to the name
  @originating_server           sysname       OUTPUT  -- Output since we may modify it
AS
BEGIN
  DECLARE @job_type           INT
  DECLARE @retval             INT
  DECLARE @current_date       INT
  DECLARE @res_valid_range    NVARCHAR(200)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name                       = LTRIM(RTRIM(@name))
  SELECT @category_name              = LTRIM(RTRIM(@category_name))
  SELECT @originating_server         = UPPER(LTRIM(RTRIM(@originating_server)))

  SELECT @originating_server = ISNULL(@originating_server, UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))))

  -- Check originating server (only the SQLServerAgent can add jobs that originate from a remote server)
  IF (@originating_server <> UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) AND
     (PROGRAM_NAME() NOT LIKE N'SQLAgent%')
  BEGIN
    RAISERROR(14275, -1, -1)
    RETURN(1) -- Failure
  END

  -- NOTE: We allow jobs with the same name (since job_id is always unique) but only if
  --       they originate from different servers.  Thus jobs can flow from an MSX to a TSX
  --       without having to worry about naming conflicts.
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobs as job
                JOIN msdb.dbo.sysoriginatingservers_view as svr 
                  ON (svr.originating_server_id = job.originating_server_id)  
              WHERE (name = @name)
                AND (svr.originating_server = @originating_server)
                AND (job_id <> ISNULL(@job_id, 0x911)))) -- When adding a new job @job_id is NULL
  BEGIN
    RAISERROR(14261, -1, -1, '@name', @name)
    RETURN(1) -- Failure
  END

  -- Check enabled state
  IF (@enabled <> 0) AND (@enabled <> 1)
  BEGIN
    RAISERROR(14266, -1, -1, '@enabled', '0, 1')
    RETURN(1) -- Failure
  END

  -- Check start step
  IF (@job_id IS NULL)
  BEGIN
    -- New job
    -- NOTE: For [new] MSX jobs we allow the start step to be other than 1 since
    --       the start step was validated when the job was created at the MSX
    IF (@start_step_id <> 1) AND (@originating_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))))
    BEGIN
      RAISERROR(14266, -1, -1, '@start_step_id', '1')
      RETURN(1) -- Failure
    END
  END
  ELSE
  BEGIN
    -- Existing job
    DECLARE @max_step_id INT
    DECLARE @valid_range VARCHAR(50)

    -- Get current maximum step id
    SELECT @max_step_id = ISNULL(MAX(step_id), 0)
    FROM msdb.dbo.sysjobsteps
    WHERE (job_id = @job_id)

    IF (@start_step_id < 1) OR (@start_step_id > @max_step_id + 1)
    BEGIN
      SELECT @valid_range = '1..' + CONVERT(VARCHAR, @max_step_id + 1)
      RAISERROR(14266, -1, -1, '@start_step_id', @valid_range)
      RETURN(1) -- Failure
    END
  END

  -- Check category
  SELECT @job_type = NULL

  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers
              WHERE (job_id = @job_id)
                AND (server_id = 0)))
    SELECT @job_type = 1 -- LOCAL

  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers
              WHERE (job_id = @job_id)
                AND (server_id <> 0)))
    SELECT @job_type = 2 -- MULTI-SERVER

  -- A local job cannot be added to a multi-server job_category
  IF (@job_type = 1) AND (EXISTS (SELECT *
                                  FROM msdb.dbo.syscategories
                                  WHERE (category_class = 1) -- Job
                                    AND (category_type = 2) -- Multi-Server
                                    AND (name = @category_name)))
  BEGIN
    RAISERROR(14285, -1, -1)
    RETURN(1) -- Failure
  END

  -- A multi-server job cannot be added to a local job_category
  IF (@job_type = 2) AND (EXISTS (SELECT *
                                  FROM msdb.dbo.syscategories
                                  WHERE (category_class = 1) -- Job
                                    AND (category_type = 1) -- Local
                                    AND (name = @category_name)))
  BEGIN
    RAISERROR(14286, -1, -1)
    RETURN(1) -- Failure
  END

  -- Get the category_id, handling any special-cases as appropriate
  SELECT @category_id = NULL
  IF (@category_name = N'[DEFAULT]') -- User wants to revert to the default job category
  BEGIN
    SELECT @category_id = CASE ISNULL(@job_type, 1)
                            WHEN 1 THEN 0 -- [Uncategorized (Local)]
                            WHEN 2 THEN 2 -- [Uncategorized (Multi-Server)]
                          END
  END
  ELSE
  IF (@category_name IS NULL) -- The sp_add_job default
  BEGIN
    SELECT @category_id = 0
  END
  ELSE
  BEGIN
    SELECT @category_id = category_id
    FROM msdb.dbo.syscategories
    WHERE (category_class = 1) -- Job
      AND (name = @category_name)
  END

  IF (@category_id IS NULL)
  BEGIN
    RAISERROR(14234, -1, -1, '@category_name', 'sp_help_category')
    RETURN(1) -- Failure
  END

  -- Only SQLServerAgent may add jobs to the 'Jobs From MSX' category
  IF (@category_id = 1) AND
     (PROGRAM_NAME() NOT LIKE N'SQLAgent%')
  BEGIN
    RAISERROR(14267, -1, -1, @category_name)
    RETURN(1) -- Failure
  END

  -- Check owner
  -- Default the owner to be the calling user if:
  -- caller is not a sysadmin
  -- caller is not SQLAgentOperator and job_id is NULL, meaning new job 
  IF (((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) AND
      ((ISNULL(IS_MEMBER('SQLAgentOperatorRole'), 0) = 0) AND @job_id IS NULL)) AND
      (@owner_sid <> SUSER_SID()))
  BEGIN
    SELECT @owner_sid = SUSER_SID()
  END

  -- Now just check that the login id is valid (ie. it exists and isn't an NT group)
  IF (@owner_sid <> 0x010100000000000512000000) AND -- NT AUTHORITY\SYSTEM sid
     (@owner_sid <> 0x010100000000000514000000)     -- NT AUTHORITY\NETWORK SERVICE sid
  BEGIN
     IF (@owner_sid IS NULL) OR (EXISTS (SELECT *
                                      FROM master.dbo.syslogins
                                      WHERE (sid = @owner_sid)
                                        AND (isntgroup <> 0)))
     BEGIN
       -- NOTE: In the following message we quote @owner_login_name instead of @owner_sid
       --       since this is the parameter the user passed to the calling SP (ie. either
       --       sp_add_job or sp_update_job)
       SELECT @res_valid_range = FORMATMESSAGE(14203)
       RAISERROR(14234, -1, -1, '@owner_login_name', @res_valid_range)
       RETURN(1) -- Failure
     END
  END
  
  -- Check notification levels (must be 0, 1, 2 or 3)
  IF (@notify_level_eventlog & 0x3 <> @notify_level_eventlog)
  BEGIN
    RAISERROR(14266, -1, -1, '@notify_level_eventlog', '0, 1, 2, 3')
    RETURN(1) -- Failure
  END
  IF (@notify_level_email & 0x3 <> @notify_level_email)
  BEGIN
    RAISERROR(14266, -1, -1, '@notify_level_email', '0, 1, 2, 3')
    RETURN(1) -- Failure
  END
  IF (@notify_level_netsend & 0x3 <> @notify_level_netsend)
  BEGIN
    RAISERROR(14266, -1, -1, '@notify_level_netsend', '0, 1, 2, 3')
    RETURN(1) -- Failure
  END
  IF (@notify_level_page & 0x3 <> @notify_level_page)
  BEGIN
    RAISERROR(14266, -1, -1, '@notify_level_page', '0, 1, 2, 3')
    RETURN(1) -- Failure
  END

  -- If we're at a TSX, only SQLServerAgent may add jobs that notify 'MSXOperator'
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.systargetservers)) AND
     ((@notify_email_operator_name = N'MSXOperator') OR
      (@notify_page_operator_name = N'MSXOperator') OR
      (@notify_netsend_operator_name = N'MSXOperator')) AND
     (PROGRAM_NAME() NOT LIKE N'SQLAgent%')
  BEGIN
    RAISERROR(14251, -1, -1, 'MSXOperator')
    RETURN(1) -- Failure
  END

  -- Check operator to notify (via email)
  IF (@notify_email_operator_name IS NOT NULL)
  BEGIN
    SELECT @notify_email_operator_id = id
    FROM msdb.dbo.sysoperators
    WHERE (name = @notify_email_operator_name)

    IF (@notify_email_operator_id IS NULL)
    BEGIN
      RAISERROR(14234, -1, -1, '@notify_email_operator_name', 'sp_help_operator')
      RETURN(1) -- Failure
    END
    -- If a valid operator is specified the level must be non-zero
    IF (@notify_level_email = 0)
    BEGIN
      RAISERROR(14266, -1, -1, '@notify_level_email', '1, 2, 3')
      RETURN(1) -- Failure
    END
  END
  ELSE
  BEGIN
    SELECT @notify_email_operator_id = 0
    SELECT @notify_level_email = 0
  END

  -- Check operator to notify (via netsend)
  IF (@notify_netsend_operator_name IS NOT NULL)
  BEGIN
    SELECT @notify_netsend_operator_id = id
    FROM msdb.dbo.sysoperators
    WHERE (name = @notify_netsend_operator_name)

    IF (@notify_netsend_operator_id IS NULL)
    BEGIN
      RAISERROR(14234, -1, -1, '@notify_netsend_operator_name', 'sp_help_operator')
      RETURN(1) -- Failure
    END
    -- If a valid operator is specified the level must be non-zero
    IF (@notify_level_netsend = 0)
    BEGIN
      RAISERROR(14266, -1, -1, '@notify_level_netsend', '1, 2, 3')
      RETURN(1) -- Failure
    END
  END
  ELSE
  BEGIN
    SELECT @notify_netsend_operator_id = 0
    SELECT @notify_level_netsend = 0
  END

  -- Check operator to notify (via page)
  IF (@notify_page_operator_name IS NOT NULL)
  BEGIN
    SELECT @notify_page_operator_id = id
    FROM msdb.dbo.sysoperators
    WHERE (name = @notify_page_operator_name)

    IF (@notify_page_operator_id IS NULL)
    BEGIN
      RAISERROR(14234, -1, -1, '@notify_page_operator_name', 'sp_help_operator')
      RETURN(1) -- Failure
    END
    -- If a valid operator is specified the level must be non-zero
    IF (@notify_level_page = 0)
    BEGIN
      RAISERROR(14266, -1, -1, '@notify_level_page', '1, 2, 3')
      RETURN(1) -- Failure
    END
  END
  ELSE
  BEGIN
    SELECT @notify_page_operator_id = 0
    SELECT @notify_level_page = 0
  END

  -- Check delete level (must be 0, 1, 2 or 3)
  IF (@delete_level & 0x3 <> @delete_level)
  BEGIN
    RAISERROR(14266, -1, -1, '@delete_level', '0, 1, 2, 3')
    RETURN(1) -- Failure
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_ADD_JOB                                                 */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_job...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_job')
              AND (type = 'P')))
  DROP PROCEDURE sp_add_job
go
CREATE PROCEDURE sp_add_job
  @job_name                     sysname,
  @enabled                      TINYINT          = 1,        -- 0 = Disabled, 1 = Enabled
  @description                  NVARCHAR(512)    = NULL,
  @start_step_id                INT              = 1,
  @category_name                sysname          = NULL,
  @category_id                  INT              = NULL,     -- A language-independent way to specify which category to use
  @owner_login_name             sysname          = NULL,     -- The procedure assigns a default
  @notify_level_eventlog        INT              = 2,        -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always
  @notify_level_email           INT              = 0,        -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always
  @notify_level_netsend         INT              = 0,        -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always
  @notify_level_page            INT              = 0,        -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always
  @notify_email_operator_name   sysname          = NULL,
  @notify_netsend_operator_name sysname          = NULL,
  @notify_page_operator_name    sysname          = NULL,
  @delete_level                 INT              = 0,        -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always
  @job_id                       UNIQUEIDENTIFIER = NULL OUTPUT,
  @originating_server           sysname           = NULL      -- For SQLAgent use only
AS
BEGIN
  DECLARE @retval                     INT
  DECLARE @notify_email_operator_id   INT
  DECLARE @notify_netsend_operator_id INT
  DECLARE @notify_page_operator_id    INT
  DECLARE @owner_sid                  VARBINARY(85)
  DECLARE @originating_server_id      INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters (except @owner_login_name)
  SELECT @originating_server           = UPPER(LTRIM(RTRIM(@originating_server)))
  SELECT @job_name                     = LTRIM(RTRIM(@job_name))
  SELECT @description                  = LTRIM(RTRIM(@description))
  SELECT @category_name                = LTRIM(RTRIM(@category_name))
  SELECT @notify_email_operator_name   = LTRIM(RTRIM(@notify_email_operator_name))
  SELECT @notify_netsend_operator_name = LTRIM(RTRIM(@notify_netsend_operator_name))
  SELECT @notify_page_operator_name    = LTRIM(RTRIM(@notify_page_operator_name))
  SELECT @originating_server_id        = NULL

  -- Turn [nullable] empty string parameters into NULLs
  IF (@originating_server           = N'') SELECT @originating_server           = NULL
  IF (@description                  = N'') SELECT @description                  = NULL
  IF (@category_name                = N'') SELECT @category_name                = NULL
  IF (@notify_email_operator_name   = N'') SELECT @notify_email_operator_name   = NULL
  IF (@notify_netsend_operator_name = N'') SELECT @notify_netsend_operator_name = NULL
  IF (@notify_page_operator_name    = N'') SELECT @notify_page_operator_name    = NULL

  IF (@originating_server IS NULL) OR (@originating_server = '(LOCAL)')
    SELECT @originating_server= UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))

  --only members of sysadmins role can set the owner
  IF (@owner_login_name IS NOT NULL AND ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) AND (@owner_login_name <> SUSER_SNAME())
  BEGIN
    RAISERROR(14515, -1, -1)
    RETURN(1) -- Failure
  END
    
  -- Default the owner (if not supplied or if a non-sa is [illegally] trying to create a job for another user)
  -- allow special account only when caller is sysadmin
  IF (@owner_login_name = N'$(SQLAgentAccount)')  AND 
     (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)
  BEGIN
    SELECT @owner_sid = 0xFFFFFFFF   
  END
  ELSE 
  IF (@owner_login_name IS NULL) OR ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) AND (@owner_login_name <> SUSER_SNAME()))
  BEGIN
    SELECT @owner_sid = SUSER_SID()
  END
  ELSE
  BEGIN
    --force case insensitive comparation for NT users
    SELECT @owner_sid = SUSER_SID(@owner_login_name, 0) -- If @owner_login_name is invalid then SUSER_SID() will return NULL
  END

  -- Default the description (if not supplied)
  IF (@description IS NULL)
    SELECT @description = FORMATMESSAGE(14571)

  -- If a category ID is provided this overrides any supplied category name
  EXECUTE @retval = sp_verify_category_identifiers '@category_name',
                                                   '@category_id',
                                                    @category_name OUTPUT,
                                                    @category_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check parameters
  EXECUTE @retval = sp_verify_job NULL,  --  The job id is null since this is a new job
                                  @job_name,
                                  @enabled,
                                  @start_step_id,
                                  @category_name,
                                  @owner_sid                  OUTPUT,
                                  @notify_level_eventlog,
                                  @notify_level_email         OUTPUT,
                                  @notify_level_netsend       OUTPUT,
                                  @notify_level_page          OUTPUT,
                                  @notify_email_operator_name,
                                  @notify_netsend_operator_name,
                                  @notify_page_operator_name,
                                  @delete_level,
                                  @category_id                OUTPUT,
                                  @notify_email_operator_id   OUTPUT,
                                  @notify_netsend_operator_id OUTPUT,
                                  @notify_page_operator_id    OUTPUT,
                                  @originating_server         OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure
    
    
  SELECT @originating_server_id = originating_server_id 
  FROM msdb.dbo.sysoriginatingservers_view 
  WHERE (originating_server = @originating_server)
  IF (@originating_server_id IS NULL)
  BEGIN
    RAISERROR(14370, -1, -1)
    RETURN(1) -- Failure
  END
    

  IF (@job_id IS NULL)
  BEGIN
    -- Assign the GUID
    SELECT @job_id = NEWID()
  END
  ELSE
  BEGIN
    -- A job ID has been provided, so check that the caller is SQLServerAgent (inserting an MSX job)
    IF (PROGRAM_NAME() NOT LIKE N'SQLAgent%')
    BEGIN
      RAISERROR(14274, -1, -1)
      RETURN(1) -- Failure
    END
  END

  INSERT INTO msdb.dbo.sysjobs
         (job_id,
          originating_server_id,
          name,
          enabled,
          description,
          start_step_id,
          category_id,
          owner_sid,
          notify_level_eventlog,
          notify_level_email,
          notify_level_netsend,
          notify_level_page,
          notify_email_operator_id,
          notify_netsend_operator_id,
          notify_page_operator_id,
          delete_level,
          date_created,
          date_modified,
          version_number)
  VALUES  (@job_id,
          @originating_server_id,
          @job_name,
          @enabled,
          @description,
          @start_step_id,
          @category_id,
          @owner_sid,
          @notify_level_eventlog,
          @notify_level_email,
          @notify_level_netsend,
          @notify_level_page,
          @notify_email_operator_id,
          @notify_netsend_operator_id,
          @notify_page_operator_id,
          @delete_level,
          GETDATE(),
          GETDATE(),
          1) -- Version number 1
  SELECT @retval = @@error

  -- NOTE: We don't notify SQLServerAgent to update it's cache (we'll do this in sp_add_jobserver)

  RETURN(@retval) -- 0 means success
END
go

/**************************************************************/
/* SP_UPDATE_JOB                                              */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_update_job...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_update_job')
              AND (type = 'P')))
  DROP PROCEDURE sp_update_job
go
CREATE PROCEDURE sp_update_job
  @job_id                       UNIQUEIDENTIFIER = NULL, -- Must provide this or current_name
  @job_name                     sysname          = NULL, -- Must provide this or job_id
  @new_name                     sysname          = NULL,
  @enabled                      TINYINT          = NULL,
  @description                  NVARCHAR(512)    = NULL,
  @start_step_id                INT              = NULL,
  @category_name                sysname          = NULL,
  @owner_login_name             sysname          = NULL,
  @notify_level_eventlog        INT              = NULL,
  @notify_level_email           INT              = NULL,
  @notify_level_netsend         INT              = NULL,
  @notify_level_page            INT              = NULL,
  @notify_email_operator_name   sysname          = NULL,
  @notify_netsend_operator_name sysname          = NULL,
  @notify_page_operator_name    sysname          = NULL,
  @delete_level                 INT              = NULL,
  @automatic_post               BIT              = 1     -- Flag for SEM use only
AS
BEGIN
  DECLARE @retval                        INT
  DECLARE @category_id                   INT
  DECLARE @notify_email_operator_id      INT
  DECLARE @notify_netsend_operator_id    INT
  DECLARE @notify_page_operator_id       INT
  DECLARE @owner_sid                     VARBINARY(85)
  DECLARE @alert_id                      INT
  DECLARE @cached_attribute_modified     INT
  DECLARE @is_sysadmin                   INT
  DECLARE @current_owner                 sysname
  DECLARE @enable_only_used              INT

  DECLARE @x_new_name                    sysname
  DECLARE @x_enabled                     TINYINT
  DECLARE @x_description                 NVARCHAR(512)
  DECLARE @x_start_step_id               INT
  DECLARE @x_category_name               sysname
  DECLARE @x_category_id                 INT
  DECLARE @x_owner_sid                   VARBINARY(85)
  DECLARE @x_notify_level_eventlog       INT
  DECLARE @x_notify_level_email          INT
  DECLARE @x_notify_level_netsend        INT
  DECLARE @x_notify_level_page           INT
  DECLARE @x_notify_email_operator_name  sysname
  DECLARE @x_notify_netsnd_operator_name sysname
  DECLARE @x_notify_page_operator_name   sysname
  DECLARE @x_delete_level                INT
  DECLARE @x_originating_server_id       INT -- Not updatable
  DECLARE @x_master_server               BIT

  -- Remove any leading/trailing spaces from parameters (except @owner_login_name)
  SELECT @job_name                     = LTRIM(RTRIM(@job_name))
  SELECT @new_name                     = LTRIM(RTRIM(@new_name))
  SELECT @description                  = LTRIM(RTRIM(@description))
  SELECT @category_name                = LTRIM(RTRIM(@category_name))
  SELECT @notify_email_operator_name   = LTRIM(RTRIM(@notify_email_operator_name))
  SELECT @notify_netsend_operator_name = LTRIM(RTRIM(@notify_netsend_operator_name))
  SELECT @notify_page_operator_name    = LTRIM(RTRIM(@notify_page_operator_name))

  SET NOCOUNT ON

  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Are we modifying an attribute which SQLServerAgent caches?
  IF ((@new_name                     IS NOT NULL) OR
      (@enabled                      IS NOT NULL) OR
      (@start_step_id                IS NOT NULL) OR
      (@owner_login_name             IS NOT NULL) OR
      (@notify_level_eventlog        IS NOT NULL) OR
      (@notify_level_email           IS NOT NULL) OR
      (@notify_level_netsend         IS NOT NULL) OR
      (@notify_level_page            IS NOT NULL) OR
      (@notify_email_operator_name   IS NOT NULL) OR
      (@notify_netsend_operator_name IS NOT NULL) OR
      (@notify_page_operator_name    IS NOT NULL) OR
      (@delete_level                 IS NOT NULL))
    SELECT @cached_attribute_modified = 1
  ELSE
    SELECT @cached_attribute_modified = 0
    
  -- Is @enable the only parameter used beside jobname and jobid?
  IF ((@enabled                   IS NOT NULL) AND
     (@new_name                IS NULL) AND
     (@description                  IS NULL) AND
     (@start_step_id                IS NULL) AND
     (@category_name                IS NULL) AND
     (@owner_login_name             IS NULL) AND
     (@notify_level_eventlog        IS NULL) AND
     (@notify_level_email           IS NULL) AND
     (@notify_level_netsend         IS NULL) AND
     (@notify_level_page            IS NULL) AND
     (@notify_email_operator_name   IS NULL) AND
     (@notify_netsend_operator_name IS NULL) AND
     (@notify_page_operator_name    IS NULL) AND
     (@delete_level                 IS NULL))
    SELECT @enable_only_used = 1
  ELSE
    SELECT @enable_only_used = 0

  -- Set the x_ (existing) variables
  SELECT @x_new_name                    = sjv.name,
         @x_enabled                     = sjv.enabled,
         @x_description                 = sjv.description,
         @x_start_step_id               = sjv.start_step_id,
         @x_category_name               = sc.name,                  -- From syscategories
         @x_category_id                 = sc.category_id,           -- From syscategories
         @x_owner_sid                   = sjv.owner_sid,
         @x_notify_level_eventlog       = sjv.notify_level_eventlog,
         @x_notify_level_email          = sjv.notify_level_email,
         @x_notify_level_netsend        = sjv.notify_level_netsend,
         @x_notify_level_page           = sjv.notify_level_page,
         @x_notify_email_operator_name  = so1.name,                   -- From sysoperators
         @x_notify_netsnd_operator_name = so2.name,                   -- From sysoperators
         @x_notify_page_operator_name   = so3.name,                   -- From sysoperators
         @x_delete_level                = sjv.delete_level,
         @x_originating_server_id       = sjv.originating_server_id,
         @x_master_server               = master_server
  FROM msdb.dbo.sysjobs_view                 sjv
       LEFT OUTER JOIN msdb.dbo.sysoperators so1 ON (sjv.notify_email_operator_id = so1.id)
       LEFT OUTER JOIN msdb.dbo.sysoperators so2 ON (sjv.notify_netsend_operator_id = so2.id)
       LEFT OUTER JOIN msdb.dbo.sysoperators so3 ON (sjv.notify_page_operator_id = so3.id),
       msdb.dbo.syscategories                sc
  WHERE (sjv.job_id = @job_id)
    AND (sjv.category_id = sc.category_id)

  -- Check authority (only SQLServerAgent can modify a non-local job)
  IF ((@x_master_server = 1) AND (PROGRAM_NAME() NOT LIKE N'SQLAgent%') )
  BEGIN
    RAISERROR(14274, -1, -1)
    RETURN(1) -- Failure
  END
  
  -- Check permissions beyond what's checked by the sysjobs_view
  -- SQLAgentReader and SQLAgentOperator roles that can see all jobs
  -- cannot modify jobs they do not own
  IF ( (@x_owner_sid <> SUSER_SID())                  -- does not own the job
      AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)   -- is not sysadmin
      AND (@enable_only_used <> 1 OR ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) <> 1))
  BEGIN
   RAISERROR(14525, -1, -1);
   RETURN(1) -- Failure
  END

  -- Check job category, only sysadmin can modify mutli-server jobs        
  IF (EXISTS (SELECT * FROM msdb.dbo.syscategories WHERE (category_class = 1) -- Job
                                                     AND (category_type = 2) -- Multi-Server
                                                     AND (category_id = @x_category_id)
                                                     AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))) -- is not sysadmin
  BEGIN
     RAISERROR(14396, -1, -1);
     RETURN(1) -- Failure
  END          
            
  IF (@new_name = N'') SELECT @new_name = NULL

  -- Fill out the values for all non-supplied parameters from the existing values
  IF (@new_name                     IS NULL) SELECT @new_name                     = @x_new_name
  IF (@enabled                      IS NULL) SELECT @enabled                      = @x_enabled
  IF (@description                  IS NULL) SELECT @description                  = @x_description
  IF (@start_step_id                IS NULL) SELECT @start_step_id                = @x_start_step_id
  IF (@category_name                IS NULL) SELECT @category_name                = @x_category_name
  IF (@owner_sid                    IS NULL) SELECT @owner_sid                    = @x_owner_sid
  IF (@notify_level_eventlog        IS NULL) SELECT @notify_level_eventlog        = @x_notify_level_eventlog
  IF (@notify_level_email           IS NULL) SELECT @notify_level_email           = @x_notify_level_email
  IF (@notify_level_netsend         IS NULL) SELECT @notify_level_netsend         = @x_notify_level_netsend
  IF (@notify_level_page            IS NULL) SELECT @notify_level_page            = @x_notify_level_page
  IF (@notify_email_operator_name   IS NULL) SELECT @notify_email_operator_name   = @x_notify_email_operator_name
  IF (@notify_netsend_operator_name IS NULL) SELECT @notify_netsend_operator_name = @x_notify_netsnd_operator_name
  IF (@notify_page_operator_name    IS NULL) SELECT @notify_page_operator_name    = @x_notify_page_operator_name
  IF (@delete_level                 IS NULL) SELECT @delete_level                 = @x_delete_level

  -- If the SA is attempting to assign ownership of the job to someone else, then convert
  -- the login name to an ID
  IF (@owner_login_name = N'$(SQLAgentAccount)')  AND 
     (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)
  BEGIN
    SELECT @owner_sid = 0xFFFFFFFF   
  END
  ELSE IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) AND (@owner_login_name IS NOT NULL))
  BEGIN
    --force case insensitive comparation for NT users
    SELECT @owner_sid = SUSER_SID(@owner_login_name, 0) -- If @owner_login_name is invalid then SUSER_SID() will return NULL
  END

  -- Only the SA can re-assign jobs
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) AND (@owner_login_name IS NOT NULL))
    RAISERROR(14242, -1, -1)

  -- Ownership of a multi-server job cannot be assigned to a non-sysadmin
  IF (@owner_login_name IS NOT NULL) AND
     (EXISTS (SELECT *
              FROM msdb.dbo.sysjobs       sj,
                   msdb.dbo.sysjobservers sjs
              WHERE (sj.job_id = sjs.job_id)
                AND (sj.job_id = @job_id)
                AND (sjs.server_id <> 0)))
  BEGIN
    IF (@owner_login_name = N'$(SQLAgentAccount)') -- allow distributed jobs to be assigned to special account
    BEGIN
      SELECT @is_sysadmin = 1    
    END
    ELSE
    BEGIN
      SELECT @is_sysadmin = 0
      EXECUTE msdb.dbo.sp_sqlagent_has_server_access @login_name = @owner_login_name, @is_sysadmin_member = @is_sysadmin OUTPUT
    END

    IF (@is_sysadmin = 0)
    BEGIN
      SELECT @current_owner = dbo.SQLAGENT_SUSER_SNAME(@x_owner_sid)
      RAISERROR(14543, -1, -1, @current_owner, N'sysadmin')
      RETURN(1) -- Failure
    END
  END

  -- Turn [nullable] empty string parameters into NULLs
  IF (@description                  = N'') SELECT @description                  = NULL
  IF (@category_name                = N'') SELECT @category_name                = NULL
  IF (@notify_email_operator_name   = N'') SELECT @notify_email_operator_name   = NULL
  IF (@notify_netsend_operator_name = N'') SELECT @notify_netsend_operator_name = NULL
  IF (@notify_page_operator_name    = N'') SELECT @notify_page_operator_name    = NULL

  -- Check new values
  EXECUTE @retval = sp_verify_job @job_id,
                                  @new_name,
                                  @enabled,
                                  @start_step_id,
                                  @category_name,
                                  @owner_sid                  OUTPUT,
                                  @notify_level_eventlog,
                                  @notify_level_email         OUTPUT,
                                  @notify_level_netsend       OUTPUT,
                                  @notify_level_page          OUTPUT,
                                  @notify_email_operator_name,
                                  @notify_netsend_operator_name,
                                  @notify_page_operator_name,
                                  @delete_level,
                                  @category_id                OUTPUT,
                                  @notify_email_operator_id   OUTPUT,
                                  @notify_netsend_operator_id OUTPUT,
                                  @notify_page_operator_id    OUTPUT,
                                  NULL  
  IF (@retval <> 0)
    RETURN(1) -- Failure

  BEGIN TRANSACTION

  -- If the job is being re-assigned, modify sysjobsteps.database_user_name as necessary
  IF (@owner_login_name IS NOT NULL)
  BEGIN
    IF (EXISTS (SELECT *
                FROM msdb.dbo.sysjobsteps
                WHERE (job_id = @job_id)
                  AND (subsystem = N'TSQL')))
    BEGIN
      IF (EXISTS (SELECT *
                  FROM master.dbo.syslogins
                  WHERE (sid = @owner_sid)
                    AND (sysadmin <> 1)))
      BEGIN
        -- The job is being re-assigned to an non-SA
        UPDATE msdb.dbo.sysjobsteps
        SET database_user_name = NULL
        WHERE (job_id = @job_id)
          AND (subsystem = N'TSQL')
      END
    END
  END

  UPDATE msdb.dbo.sysjobs
  SET name                       = @new_name,
      enabled                    = @enabled,
      description                = @description,
      start_step_id              = @start_step_id,
      category_id                = @category_id,              -- Returned from sp_verify_job
      owner_sid                  = @owner_sid,
      notify_level_eventlog      = @notify_level_eventlog,
      notify_level_email         = @notify_level_email,
      notify_level_netsend       = @notify_level_netsend,
      notify_level_page          = @notify_level_page,
      notify_email_operator_id   = @notify_email_operator_id,   -- Returned from sp_verify_job
      notify_netsend_operator_id = @notify_netsend_operator_id, -- Returned from sp_verify_job
      notify_page_operator_id    = @notify_page_operator_id,    -- Returned from sp_verify_job
      delete_level               = @delete_level,
      version_number             = version_number + 1,  -- Update the job's version
      date_modified              = GETDATE()            -- Update the job's last-modified information
  WHERE (job_id = @job_id)
  SELECT @retval = @@error

  COMMIT TRANSACTION

  -- Always re-post the job if it's an auto-delete job (or if we're updating an auto-delete job
  -- to be non-auto-delete)
  IF (((SELECT delete_level
        FROM msdb.dbo.sysjobs
        WHERE (job_id = @job_id)) <> 0) OR
      ((@x_delete_level = 1) AND (@delete_level = 0)))
    EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id
  ELSE
  BEGIN
    -- Post the update to target servers
    IF (@automatic_post = 1)
      EXECUTE msdb.dbo.sp_post_msx_operation 'UPDATE', 'JOB', @job_id
  END

  -- Keep SQLServerAgent's cache in-sync
  -- NOTE: We only notify SQLServerAgent if we know the job has been cached and if
  --       attributes other than description or category have been changed (since
  --       SQLServerAgent doesn't cache these two)
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers
              WHERE (job_id = @job_id)
                AND (server_id = 0)
                AND (@cached_attribute_modified = 1)))
    EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'J',
                                        @job_id      = @job_id,
                                        @action_type = N'U'

  -- If the name was changed, make SQLServerAgent re-cache any alerts that reference the job
  -- since the alert cache contains the job name
  IF ((@job_name <> @new_name) AND (EXISTS (SELECT *
                                            FROM msdb.dbo.sysalerts
                                            WHERE (job_id = @job_id))))
  BEGIN
    DECLARE sysalerts_cache_update CURSOR LOCAL
    FOR
    SELECT id
    FROM msdb.dbo.sysalerts
    WHERE (job_id = @job_id)

    OPEN sysalerts_cache_update
    FETCH NEXT FROM sysalerts_cache_update INTO @alert_id

    WHILE (@@fetch_status = 0)
    BEGIN
      EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'A',
                                          @alert_id    = @alert_id,
                                          @action_type = N'U'
      FETCH NEXT FROM sysalerts_cache_update INTO @alert_id
    END
    DEALLOCATE sysalerts_cache_update
  END

  RETURN(@retval) -- 0 means success
END
go

/**************************************************************/
/* SP_DELETE_JOB                                              */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_job...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_job')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_job
go
CREATE PROCEDURE sp_delete_job
  @job_id               UNIQUEIDENTIFIER = NULL, -- If provided should NOT also provide job_name
  @job_name             sysname          = NULL, -- If provided should NOT also provide job_id
  @originating_server      sysname         = NULL, -- Reserved (used by SQLAgent)
  @delete_history       BIT              = 1,    -- Reserved (used by SQLAgent)
  @delete_unused_schedule   BIT              = 1     -- For backward compatibility schedules are deleted by default if they are not 
                                        -- being used by another job. With the introduction of reusable schedules in V9 
                                        -- callers should set this to 0 so the schedule will be preserved for reuse.
AS
BEGIN
  DECLARE @current_msx_server sysname
  DECLARE @bMSX_job           BIT
  DECLARE @retval             INT
  DECLARE @local_machine_name sysname
  DECLARE @category_id        INT
  DECLARE @job_owner_sid      VARBINARY(85)
  
  SET NOCOUNT ON
  -- Remove any leading/trailing spaces from parameters
  SELECT @originating_server = UPPER(LTRIM(RTRIM(@originating_server)))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@originating_server = N'') SELECT @originating_server = NULL

  -- Change server name to always reflect real servername or servername\instancename
  IF (@originating_server IS NOT NULL AND @originating_server = '(LOCAL)')
    SELECT @originating_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))

  IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL))
  BEGIN
    EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                                '@job_id',
                                                 @job_name OUTPUT,
                                                 @job_id   OUTPUT,
                                                 @owner_sid = @job_owner_sid OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure

  END

  -- We need either a job name or a server name, not both
  IF ((@job_name IS NULL)     AND (@originating_server IS NULL)) OR
     ((@job_name IS NOT NULL) AND (@originating_server IS NOT NULL))
  BEGIN
    RAISERROR(14279, -1, -1)
    RETURN(1) -- Failure
  END

  -- Get category to see if it is a misc. replication agent. @category_id will be
  -- NULL if there is no @job_id.
  select @category_id = category_id from msdb.dbo.sysjobs where job_id = @job_id

  -- If job name was given, determine if the job is from an MSX
  IF (@job_id IS NOT NULL)
  BEGIN
    SELECT @bMSX_job = CASE UPPER(originating_server)
                         WHEN UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) THEN 0
                         ELSE 1
                       END
    FROM msdb.dbo.sysjobs_view
    WHERE (job_id = @job_id)
  END

  -- If server name was given, warn user if different from current MSX
  IF (@originating_server IS NOT NULL)
  BEGIN
    EXECUTE @retval = master.dbo.xp_getnetname @local_machine_name OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure

    IF ((@originating_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) OR (@originating_server = UPPER(@local_machine_name)))
      SELECT @originating_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))

    EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                           N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                           N'MSXServerName',
                                           @current_msx_server OUTPUT,
                                           N'no_output'

    SELECT @current_msx_server = UPPER(@current_msx_server)
    -- If server name was given but it's not the current MSX, print a warning
    SELECT @current_msx_server = LTRIM(RTRIM(@current_msx_server))
    IF ((@current_msx_server IS NOT NULL) AND (@current_msx_server <> N'') AND (@originating_server <> @current_msx_server))
      RAISERROR(14224, 0, 1, @current_msx_server)
  END

  -- Check authority (only SQLServerAgent can delete a non-local job)
  IF (((@originating_server IS NOT NULL) AND (@originating_server <> UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))))) OR (@bMSX_job = 1)) AND
     (PROGRAM_NAME() NOT LIKE N'SQLAgent%')
  BEGIN
    RAISERROR(14274, -1, -1)
    RETURN(1) -- Failure
  END
  
  -- Check permissions beyond what's checked by the sysjobs_view
  -- SQLAgentReader and SQLAgentOperator roles that can see all jobs
  -- cannot delete jobs they do not own
  IF (@job_id IS NOT NULL)
  BEGIN
   IF (@job_owner_sid <> SUSER_SID()                     -- does not own the job
       AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) -- is not sysadmin
   BEGIN
     RAISERROR(14525, -1, -1);
     RETURN(1) -- Failure
    END
  END

  -- Do the delete (for a specific job)
  IF (@job_id IS NOT NULL)
  BEGIN
    -- Note: This temp table is referenced by msdb.dbo.sp_delete_job_references,
    -- so it cannot be declared as a local table.
    CREATE TABLE #temp_jobs_to_delete (job_id UNIQUEIDENTIFIER NOT NULL PRIMARY KEY CLUSTERED,
                                       job_is_cached INT NOT NULL)

    DECLARE @temp_schedules_to_delete TABLE (schedule_id INT NOT NULL)  

    INSERT INTO #temp_jobs_to_delete
    SELECT job_id, (SELECT COUNT(*)
                    FROM msdb.dbo.sysjobservers
                    WHERE (job_id = @job_id)
                      AND (server_id = 0))
    FROM msdb.dbo.sysjobs_view
    WHERE (job_id = @job_id)

    -- Check if we have any work to do
    IF (NOT EXISTS (SELECT *
                    FROM #temp_jobs_to_delete))
    BEGIN
      DROP TABLE #temp_jobs_to_delete
      RETURN(0) -- Success
    END

    -- Post the delete to any target servers (need to do this BEFORE
    -- deleting the job itself, but AFTER clearing all all pending
    -- download instructions).  Note that if the job is NOT a
    -- multi-server job then sp_post_msx_operation will catch this and
    -- will do nothing. Since it will do nothing that is why we need
    -- to NOT delete any pending delete requests, because that delete
    -- request might have been for the last target server and thus
    -- this job isn't a multi-server job anymore so posting the global
    -- delete would do nothing.
    DELETE FROM msdb.dbo.sysdownloadlist
    WHERE (object_id = @job_id)
      and (operation_code != 3) -- Delete
    EXECUTE msdb.dbo.sp_post_msx_operation 'DELETE', 'JOB', @job_id


    -- Must do this before deleting the job itself since sp_sqlagent_notify does a lookup on sysjobs_view
    -- Note: Don't notify agent in this call. It is done after the transaction is committed
    --       just in case this job is in the process of deleting itself
    EXECUTE msdb.dbo.sp_delete_job_references @notify_sqlagent = 0

    -- Delete all traces of the job
    BEGIN TRANSACTION

    DECLARE @err int

   --Get the schedules to delete before deleting records from sysjobschedules
    IF(@delete_unused_schedule = 1)
    BEGIN
        --Get the list of schedules to delete
        INSERT INTO @temp_schedules_to_delete
        SELECT DISTINCT schedule_id 
        FROM   msdb.dbo.sysschedules
        WHERE (schedule_id IN 
                (SELECT schedule_id
                FROM msdb.dbo.sysjobschedules
                WHERE (job_id = @job_id)))
    END


    DELETE FROM msdb.dbo.sysjobschedules
    WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete)
    
    DELETE FROM msdb.dbo.sysjobservers
    WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete)

    DELETE FROM msdb.dbo.sysjobsteps
    WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete)

    DELETE FROM msdb.dbo.sysjobs
    WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete)
    SELECT @err = @@ERROR
 
    IF @err <> 0
    BEGIN
    ROLLBACK TRANSACTION
    RETURN @err
    END

    
    --Delete the schedule(s) if requested to and it isn't being used by other jobs
    IF(@delete_unused_schedule = 1)
    BEGIN
      --Now OK to delete the schedule
      DELETE FROM msdb.dbo.sysschedules
      WHERE schedule_id IN 
        (SELECT schedule_id
         FROM @temp_schedules_to_delete as sdel
         WHERE NOT EXISTS(SELECT * 
                          FROM msdb.dbo.sysjobschedules AS js
                          WHERE (js.schedule_id = sdel.schedule_id)))
    END


    -- Delete the job history if requested    
    IF (@delete_history = 1)
    BEGIN
      DELETE FROM msdb.dbo.sysjobhistory
      WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete)
    END
    -- All done
    COMMIT TRANSACTION

    -- Now notify agent to delete the job.
    IF(EXISTS(SELECT * FROM #temp_jobs_to_delete WHERE job_is_cached > 0))
    BEGIN
      DECLARE @nt_user_name   NVARCHAR(100)
      SELECT @nt_user_name = ISNULL(NT_CLIENT(), ISNULL(SUSER_SNAME(), FORMATMESSAGE(14205)))
      --Call the xp directly. sp_sqlagent_notify checks sysjobs_view and the record has already been deleted
      EXEC master.dbo.xp_sqlagent_notify N'J', @job_id, 0, 0, N'D', @nt_user_name, 1, @@trancount, NULL, NULL
    END

  END
  ELSE
  -- Do the delete (for all jobs originating from the specific server)
  IF (@originating_server IS NOT NULL)
  BEGIN
    EXECUTE msdb.dbo.sp_delete_all_msx_jobs @msx_server = @originating_server

    -- NOTE: In this case there is no need to propagate the delete via sp_post_msx_operation
    --       since this type of delete is only ever performed on a TSX.
  END

  IF (OBJECT_ID(N'tempdb.dbo.#temp_jobs_to_delete', 'U') IS NOT NULL)    
    DROP TABLE #temp_jobs_to_delete

  RETURN(0) -- 0 means success
END
go


/**************************************************************/
/* SP_GET_COMPOSITE_JOB_INFO                                  */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_get_composite_job_info...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_get_composite_job_info')
              AND (type = 'P')))
  DROP PROCEDURE sp_get_composite_job_info
go
CREATE PROCEDURE sp_get_composite_job_info
  @job_id             UNIQUEIDENTIFIER = NULL,
  @job_type           VARCHAR(12)      = NULL,  -- LOCAL or MULTI-SERVER
  @owner_login_name   sysname          = NULL,
  @subsystem          NVARCHAR(40)     = NULL,
  @category_id        INT              = NULL,
  @enabled            TINYINT          = NULL,
  @execution_status   INT              = NULL,  -- 0 = Not idle or suspended, 1 = Executing, 2 = Waiting For Thread, 3 = Between Retries, 4 = Idle, 5 = Suspended, [6 = WaitingForStepToFinish], 7 = PerformingCompletionActions
  @date_comparator    CHAR(1)          = NULL,  -- >, < or =
  @date_created       DATETIME         = NULL,
  @date_last_modified DATETIME         = NULL,
  @description        NVARCHAR(512)    = NULL,  -- We do a LIKE on this so it can include wildcards
  @schedule_id        INT              = NULL   -- if supplied only return the jobs that use this schedule
AS
BEGIN
  DECLARE @can_see_all_running_jobs INT
  DECLARE @job_owner   sysname

  SET NOCOUNT ON

  -- By 'composite' we mean a combination of sysjobs and xp_sqlagent_enum_jobs data.
  -- This proc should only ever be called by sp_help_job, so we don't verify the
  -- parameters (sp_help_job has already done this).

  -- Step 1: Create intermediate work tables
  DECLARE @job_execution_state TABLE (job_id                  UNIQUEIDENTIFIER NOT NULL,
                                     date_started            INT              NOT NULL,
                                     time_started            INT              NOT NULL,
                                     execution_job_status    INT              NOT NULL,
                                     execution_step_id       INT              NULL,
                                     execution_step_name     sysname          COLLATE database_default NULL,
                                     execution_retry_attempt INT              NOT NULL,
                                     next_run_date           INT              NOT NULL,
                                     next_run_time           INT              NOT NULL,
                                     next_run_schedule_id    INT              NOT NULL)
  DECLARE @filtered_jobs TABLE (job_id                   UNIQUEIDENTIFIER NOT NULL,
                               date_created             DATETIME         NOT NULL,
                               date_last_modified       DATETIME         NOT NULL,
                               current_execution_status INT              NULL,
                               current_execution_step   sysname          COLLATE database_default NULL,
                               current_retry_attempt    INT              NULL,
                               last_run_date            INT              NOT NULL,
                               last_run_time            INT              NOT NULL,
                               last_run_outcome         INT              NOT NULL,
                               next_run_date            INT              NULL,
                               next_run_time            INT              NULL,
                               next_run_schedule_id     INT              NULL,
                               type                     INT              NOT NULL)
  DECLARE @xp_results TABLE (job_id                UNIQUEIDENTIFIER NOT NULL,
                            last_run_date         INT              NOT NULL,
                            last_run_time         INT              NOT NULL,
                            next_run_date         INT              NOT NULL,
                            next_run_time         INT              NOT NULL,
                            next_run_schedule_id  INT              NOT NULL,
                            requested_to_run      INT              NOT NULL, -- BOOL
                            request_source        INT              NOT NULL,
                            request_source_id     sysname          COLLATE database_default NULL,
                            running               INT              NOT NULL, -- BOOL
                            current_step          INT              NOT NULL,
                            current_retry_attempt INT              NOT NULL,
                            job_state             INT              NOT NULL)

  -- Step 2: Capture job execution information (for local jobs only since that's all SQLServerAgent caches)
  SELECT @can_see_all_running_jobs = ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0)
  IF (@can_see_all_running_jobs = 0)
  BEGIN
    SELECT @can_see_all_running_jobs = ISNULL(IS_MEMBER(N'SQLAgentReaderRole'), 0)
  END
  SELECT @job_owner = SUSER_SNAME()

  IF ((@@microsoftversion / 0x01000000) >= 8) -- SQL Server 8.0 or greater
    INSERT INTO @xp_results
    EXECUTE master.dbo.xp_sqlagent_enum_jobs @can_see_all_running_jobs, @job_owner, @job_id
  ELSE
    INSERT INTO @xp_results
    EXECUTE master.dbo.xp_sqlagent_enum_jobs @can_see_all_running_jobs, @job_owner

  INSERT INTO @job_execution_state
  SELECT xpr.job_id,
         xpr.last_run_date,
         xpr.last_run_time,
         xpr.job_state,
         sjs.step_id,
         sjs.step_name,
         xpr.current_retry_attempt,
         xpr.next_run_date,
         xpr.next_run_time,
         xpr.next_run_schedule_id
  FROM @xp_results                          xpr
       LEFT OUTER JOIN msdb.dbo.sysjobsteps sjs ON ((xpr.job_id = sjs.job_id) AND (xpr.current_step = sjs.step_id)),
       msdb.dbo.sysjobs_view                sjv
  WHERE (sjv.job_id = xpr.job_id)

  -- Step 3: Filter on everything but dates and job_type
  IF ((@subsystem        IS NULL) AND
      (@owner_login_name IS NULL) AND
      (@enabled          IS NULL) AND
      (@category_id      IS NULL) AND
      (@execution_status IS NULL) AND
      (@description      IS NULL) AND
      (@job_id           IS NULL))
  BEGIN
    -- Optimize for the frequently used case...
    INSERT INTO @filtered_jobs
    SELECT sjv.job_id,
           sjv.date_created,
           sjv.date_modified,
           ISNULL(jes.execution_job_status, 4), -- Will be NULL if the job is non-local or is not in @job_execution_state (NOTE: 4 = STATE_IDLE)
           CASE ISNULL(jes.execution_step_id, 0)
             WHEN 0 THEN NULL                   -- Will be NULL if the job is non-local or is not in @job_execution_state
             ELSE CONVERT(NVARCHAR, jes.execution_step_id) + N' (' + jes.execution_step_name + N')'
           END,
           jes.execution_retry_attempt,         -- Will be NULL if the job is non-local or is not in @job_execution_state
           0,  -- last_run_date placeholder    (we'll fix it up in step 3.3)
           0,  -- last_run_time placeholder    (we'll fix it up in step 3.3)
           5,  -- last_run_outcome placeholder (we'll fix it up in step 3.3 - NOTE: We use 5 just in case there are no jobservers for the job)
           jes.next_run_date,                   -- Will be NULL if the job is non-local or is not in @job_execution_state
           jes.next_run_time,                   -- Will be NULL if the job is non-local or is not in @job_execution_state
           jes.next_run_schedule_id,            -- Will be NULL if the job is non-local or is not in @job_execution_state
           0   -- type placeholder             (we'll fix it up in step 3.4)
    FROM msdb.dbo.sysjobs_view                sjv
         LEFT OUTER JOIN @job_execution_state jes ON (sjv.job_id = jes.job_id)
    WHERE ((@schedule_id IS NULL)
      OR   (EXISTS(SELECT * 
                 FROM sysjobschedules as js
                 WHERE (sjv.job_id = js.job_id)
                   AND (js.schedule_id = @schedule_id))))
  END
  ELSE
  BEGIN
    INSERT INTO @filtered_jobs
    SELECT DISTINCT
           sjv.job_id,
           sjv.date_created,
           sjv.date_modified,
           ISNULL(jes.execution_job_status, 4), -- Will be NULL if the job is non-local or is not in @job_execution_state (NOTE: 4 = STATE_IDLE)
           CASE ISNULL(jes.execution_step_id, 0)
             WHEN 0 THEN NULL                   -- Will be NULL if the job is non-local or is not in @job_execution_state
             ELSE CONVERT(NVARCHAR, jes.execution_step_id) + N' (' + jes.execution_step_name + N')'
           END,
           jes.execution_retry_attempt,         -- Will be NULL if the job is non-local or is not in @job_execution_state
           0,  -- last_run_date placeholder    (we'll fix it up in step 3.3)
           0,  -- last_run_time placeholder    (we'll fix it up in step 3.3)
           5,  -- last_run_outcome placeholder (we'll fix it up in step 3.3 - NOTE: We use 5 just in case there are no jobservers for the job)
           jes.next_run_date,                   -- Will be NULL if the job is non-local or is not in @job_execution_state
           jes.next_run_time,                   -- Will be NULL if the job is non-local or is not in @job_execution_state
           jes.next_run_schedule_id,            -- Will be NULL if the job is non-local or is not in @job_execution_state
           0   -- type placeholder             (we'll fix it up in step 3.4)
    FROM msdb.dbo.sysjobs_view                sjv
         LEFT OUTER JOIN @job_execution_state jes ON (sjv.job_id = jes.job_id)
         LEFT OUTER JOIN msdb.dbo.sysjobsteps sjs ON (sjv.job_id = sjs.job_id)
    WHERE ((@subsystem        IS NULL) OR (sjs.subsystem            = @subsystem))
      AND ((@owner_login_name IS NULL) 
          OR (sjv.owner_sid            = dbo.SQLAGENT_SUSER_SID(@owner_login_name)))--force case insensitive comparation for NT users
      AND ((@enabled          IS NULL) OR (sjv.enabled              = @enabled))
      AND ((@category_id      IS NULL) OR (sjv.category_id          = @category_id))
      AND ((@execution_status IS NULL) OR ((@execution_status > 0) AND (jes.execution_job_status = @execution_status))
                                       OR ((@execution_status = 0) AND (jes.execution_job_status <> 4) AND (jes.execution_job_status <> 5)))
      AND ((@description      IS NULL) OR (sjv.description       LIKE @description))
      AND ((@job_id           IS NULL) OR (sjv.job_id               = @job_id))
      AND ((@schedule_id IS NULL)
        OR (EXISTS(SELECT * 
                 FROM sysjobschedules as js
                 WHERE (sjv.job_id = js.job_id)
                   AND (js.schedule_id = @schedule_id))))
  END

  -- Step 3.1: Change the execution status of non-local jobs from 'Idle' to 'Unknown'
  UPDATE @filtered_jobs
  SET current_execution_status = NULL
  WHERE (current_execution_status = 4)
    AND (job_id IN (SELECT job_id
                    FROM msdb.dbo.sysjobservers
                    WHERE (server_id <> 0)))

  -- Step 3.2: Check that if the user asked to see idle jobs that we still have some.
  --           If we don't have any then the query should return no rows.
  IF (@execution_status = 4) AND
     (NOT EXISTS (SELECT *
                  FROM @filtered_jobs
                  WHERE (current_execution_status = 4)))
  BEGIN
    DELETE FROM @filtered_jobs
  END

  -- Step 3.3: Populate the last run date/time/outcome [this is a little tricky since for
  --           multi-server jobs there are multiple last run details in sysjobservers, so
  --           we simply choose the most recent].
  IF (EXISTS (SELECT *
              FROM msdb.dbo.systargetservers))
  BEGIN
    UPDATE @filtered_jobs
    SET last_run_date = sjs.last_run_date,
        last_run_time = sjs.last_run_time,
        last_run_outcome = sjs.last_run_outcome
    FROM @filtered_jobs         fj,
         msdb.dbo.sysjobservers sjs
    WHERE (CONVERT(FLOAT, sjs.last_run_date) * 1000000) + sjs.last_run_time =
           (SELECT MAX((CONVERT(FLOAT, last_run_date) * 1000000) + last_run_time)
            FROM msdb.dbo.sysjobservers
            WHERE (job_id = sjs.job_id))
      AND (fj.job_id = sjs.job_id)
  END
  ELSE
  BEGIN
    UPDATE @filtered_jobs
    SET last_run_date = sjs.last_run_date,
        last_run_time = sjs.last_run_time,
        last_run_outcome = sjs.last_run_outcome
    FROM @filtered_jobs         fj,
         msdb.dbo.sysjobservers sjs
    WHERE (fj.job_id = sjs.job_id)
  END

  -- Step 3.4 : Set the type of the job to local (1) or multi-server (2)
  --            NOTE: If the job has no jobservers then it wil have a type of 0 meaning
  --                  unknown.  This is marginally inconsistent with the behaviour of
  --                  defaulting the category of a new job to [Uncategorized (Local)], but
  --                  prevents incompletely defined jobs from erroneously showing up as valid
  --                  local jobs.
  UPDATE @filtered_jobs
  SET type = 1 -- LOCAL
  FROM @filtered_jobs         fj,
       msdb.dbo.sysjobservers sjs
  WHERE (fj.job_id = sjs.job_id)
    AND (server_id = 0)
  UPDATE @filtered_jobs
  SET type = 2 -- MULTI-SERVER
  FROM @filtered_jobs         fj,
       msdb.dbo.sysjobservers sjs
  WHERE (fj.job_id = sjs.job_id)
    AND (server_id <> 0)

  -- Step 4: Filter on job_type
  IF (@job_type IS NOT NULL)
  BEGIN
    IF (UPPER(@job_type collate SQL_Latin1_General_CP1_CS_AS) = 'LOCAL')
      DELETE FROM @filtered_jobs
      WHERE (type <> 1) -- IE. Delete all the non-local jobs
    IF (UPPER(@job_type collate SQL_Latin1_General_CP1_CS_AS) = 'MULTI-SERVER')
      DELETE FROM @filtered_jobs
      WHERE (type <> 2) -- IE. Delete all the non-multi-server jobs
  END

  -- Step 5: Filter on dates
  IF (@date_comparator IS NOT NULL)
  BEGIN
    IF (@date_created IS NOT NULL)
    BEGIN
      IF (@date_comparator = '=')
        DELETE FROM @filtered_jobs WHERE (date_created <> @date_created)
      IF (@date_comparator = '>')
        DELETE FROM @filtered_jobs WHERE (date_created <= @date_created)
      IF (@date_comparator = '<')
        DELETE FROM @filtered_jobs WHERE (date_created >= @date_created)
    END
    IF (@date_last_modified IS NOT NULL)
    BEGIN
      IF (@date_comparator = '=')
        DELETE FROM @filtered_jobs WHERE (date_last_modified <> @date_last_modified)
      IF (@date_comparator = '>')
        DELETE FROM @filtered_jobs WHERE (date_last_modified <= @date_last_modified)
      IF (@date_comparator = '<')
        DELETE FROM @filtered_jobs WHERE (date_last_modified >= @date_last_modified)
    END
  END

  -- Return the result set (NOTE: No filtering occurs here)
  SELECT sjv.job_id,
         originating_server, 
         sjv.name,
         sjv.enabled,
         sjv.description,
         sjv.start_step_id,
         category = ISNULL(sc.name, FORMATMESSAGE(14205)),
         owner = dbo.SQLAGENT_SUSER_SNAME(sjv.owner_sid),
         sjv.notify_level_eventlog,
         sjv.notify_level_email,
         sjv.notify_level_netsend,
         sjv.notify_level_page,
         notify_email_operator   = ISNULL(so1.name, FORMATMESSAGE(14205)),
         notify_netsend_operator = ISNULL(so2.name, FORMATMESSAGE(14205)),
         notify_page_operator    = ISNULL(so3.name, FORMATMESSAGE(14205)),
         sjv.delete_level,
         sjv.date_created,
         sjv.date_modified,
         sjv.version_number,
         fj.last_run_date,
         fj.last_run_time,
         fj.last_run_outcome,
         next_run_date = ISNULL(fj.next_run_date, 0),                                 -- This column will be NULL if the job is non-local
         next_run_time = ISNULL(fj.next_run_time, 0),                                 -- This column will be NULL if the job is non-local
         next_run_schedule_id = ISNULL(fj.next_run_schedule_id, 0),                   -- This column will be NULL if the job is non-local
         current_execution_status = ISNULL(fj.current_execution_status, 0),           -- This column will be NULL if the job is non-local
         current_execution_step = ISNULL(fj.current_execution_step, N'0 ' + FORMATMESSAGE(14205)), -- This column will be NULL if the job is non-local
         current_retry_attempt = ISNULL(fj.current_retry_attempt, 0),                 -- This column will be NULL if the job is non-local
         has_step = (SELECT COUNT(*)
                     FROM msdb.dbo.sysjobsteps sjst
                     WHERE (sjst.job_id = sjv.job_id)),
         has_schedule = (SELECT COUNT(*)
                         FROM msdb.dbo.sysjobschedules sjsch
                         WHERE (sjsch.job_id = sjv.job_id)),
         has_target = (SELECT COUNT(*)
                       FROM msdb.dbo.sysjobservers sjs
                       WHERE (sjs.job_id = sjv.job_id)),
         type = fj.type
  FROM @filtered_jobs                         fj
       LEFT OUTER JOIN msdb.dbo.sysjobs_view  sjv ON (fj.job_id = sjv.job_id)
       LEFT OUTER JOIN msdb.dbo.sysoperators  so1 ON (sjv.notify_email_operator_id = so1.id)
       LEFT OUTER JOIN msdb.dbo.sysoperators  so2 ON (sjv.notify_netsend_operator_id = so2.id)
       LEFT OUTER JOIN msdb.dbo.sysoperators  so3 ON (sjv.notify_page_operator_id = so3.id)
       LEFT OUTER JOIN msdb.dbo.syscategories sc  ON (sjv.category_id = sc.category_id)
  ORDER BY sjv.job_id

END
go


/**************************************************************/
/* SP_HELP_JOB                                                */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_job...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_job')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_job
go
CREATE PROCEDURE sp_help_job
  -- Individual job parameters
  @job_id                     UNIQUEIDENTIFIER = NULL,  -- If provided should NOT also provide job_name
  @job_name                   sysname          = NULL,  -- If provided should NOT also provide job_id
  @job_aspect                 VARCHAR(9)       = NULL,  -- JOB, STEPS, SCHEDULES, TARGETS or ALL
  -- Job set parameters
  @job_type                   VARCHAR(12)      = NULL,  -- LOCAL or MULTI-SERVER
  @owner_login_name           sysname          = NULL,
  @subsystem                  NVARCHAR(40)     = NULL,
  @category_name              sysname          = NULL,
  @enabled                    TINYINT          = NULL,
  @execution_status           INT              = NULL,  -- 1 = Executing, 2 = Waiting For Thread, 3 = Between Retries, 4 = Idle, 5 = Suspended, 6 = [obsolete], 7 = PerformingCompletionActions
  @date_comparator            CHAR(1)          = NULL,  -- >, < or =
  @date_created               DATETIME         = NULL,
  @date_last_modified         DATETIME         = NULL,
  @description                NVARCHAR(512)    = NULL   -- We do a LIKE on this so it can include wildcards
AS
BEGIN
  DECLARE @retval          INT
  DECLARE @category_id     INT
  DECLARE @job_id_as_char  VARCHAR(36)
  DECLARE @res_valid_range NVARCHAR(200)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters (except @owner_login_name)
  SELECT @job_name         = LTRIM(RTRIM(@job_name))
  SELECT @job_aspect       = LTRIM(RTRIM(@job_aspect))
  SELECT @job_type         = LTRIM(RTRIM(@job_type))
  SELECT @subsystem        = LTRIM(RTRIM(@subsystem))
  SELECT @category_name    = LTRIM(RTRIM(@category_name))
  SELECT @description      = LTRIM(RTRIM(@description))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@job_name         = N'') SELECT @job_name = NULL
  IF (@job_aspect       = '')  SELECT @job_aspect = NULL
  IF (@job_type         = '')  SELECT @job_type = NULL
  IF (@owner_login_name = N'') SELECT @owner_login_name = NULL
  IF (@subsystem        = N'') SELECT @subsystem = NULL
  IF (@category_name    = N'') SELECT @category_name = NULL
  IF (@description      = N'') SELECT @description = NULL

  IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL))
  BEGIN
    EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                                '@job_id',
                                                 @job_name OUTPUT,
                                                 @job_id   OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END

  SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id)

  -- If the user provided a job name or id but no aspect, default to ALL
  IF ((@job_name IS NOT NULL) OR (@job_id IS NOT NULL)) AND (@job_aspect IS NULL)
    SELECT @job_aspect = 'ALL'

  -- The caller must supply EITHER job name (or job id) and aspect OR one-or-more of the set
  -- parameters OR no parameters at all
  IF (((@job_name IS NOT NULL) OR (@job_id IS NOT NULL))
      AND ((@job_aspect          IS NULL)     OR
           (@job_type            IS NOT NULL) OR
           (@owner_login_name    IS NOT NULL) OR
           (@subsystem           IS NOT NULL) OR
           (@category_name       IS NOT NULL) OR
           (@enabled             IS NOT NULL) OR
           (@date_comparator     IS NOT NULL) OR
           (@date_created        IS NOT NULL) OR
           (@date_last_modified  IS NOT NULL)))
     OR
     ((@job_name IS NULL) AND (@job_id IS NULL) AND (@job_aspect IS NOT NULL))
  BEGIN
    RAISERROR(14280, -1, -1)
    RETURN(1) -- Failure
  END

  IF (@job_id IS NOT NULL)
  BEGIN
    -- Individual job...

    -- Check job aspect
    SELECT @job_aspect = UPPER(@job_aspect collate SQL_Latin1_General_CP1_CS_AS)
    IF (@job_aspect NOT IN ('JOB', 'STEPS', 'SCHEDULES', 'TARGETS', 'ALL'))
    BEGIN
      RAISERROR(14266, -1, -1, '@job_aspect', 'JOB, STEPS, SCHEDULES, TARGETS, ALL')
      RETURN(1) -- Failure
    END

    -- Generate results set...

    IF (@job_aspect IN ('JOB', 'ALL'))
    BEGIN
      IF (@job_aspect = 'ALL')
      BEGIN
        RAISERROR(14213, 0, 1)
        PRINT REPLICATE('=', DATALENGTH(FORMATMESSAGE(14213)) / 2)
      END
      EXECUTE sp_get_composite_job_info @job_id,
                                        @job_type,
                                        @owner_login_name,
                                        @subsystem,
                                        @category_id,
                                        @enabled,
                                        @execution_status,
                                        @date_comparator,
                                        @date_created,
                                        @date_last_modified,
                                        @description
    END

    IF (@job_aspect IN ('STEPS', 'ALL'))
    BEGIN
      IF (@job_aspect = 'ALL')
      BEGIN
        PRINT ''
        RAISERROR(14214, 0, 1)
        PRINT REPLICATE('=', DATALENGTH(FORMATMESSAGE(14214)) / 2)
      END
      EXECUTE ('EXECUTE sp_help_jobstep @job_id = ''' + @job_id_as_char + ''', @suffix = 1')
    END

    IF (@job_aspect IN ('SCHEDULES', 'ALL'))
    BEGIN
      IF (@job_aspect = 'ALL')
      BEGIN
        PRINT ''
        RAISERROR(14215, 0, 1)
        PRINT REPLICATE('=', DATALENGTH(FORMATMESSAGE(14215)) / 2)
      END
      EXECUTE ('EXECUTE sp_help_jobschedule @job_id = ''' + @job_id_as_char + '''')
    END

    IF (@job_aspect IN ('TARGETS', 'ALL'))
    BEGIN
      IF (@job_aspect = 'ALL')
      BEGIN
        PRINT ''
        RAISERROR(14216, 0, 1)
        PRINT REPLICATE('=', DATALENGTH(FORMATMESSAGE(14216)) / 2)
      END
      EXECUTE ('EXECUTE sp_help_jobserver @job_id = ''' + @job_id_as_char + ''', @show_last_run_details = 1')
    END
  END
  ELSE
  BEGIN
    -- Set of jobs...

    -- Check job type
    IF (@job_type IS NOT NULL)
    BEGIN
      SELECT @job_type = UPPER(@job_type collate SQL_Latin1_General_CP1_CS_AS)
      IF (@job_type NOT IN ('LOCAL', 'MULTI-SERVER'))
      BEGIN
        RAISERROR(14266, -1, -1, '@job_type', 'LOCAL, MULTI-SERVER')
        RETURN(1) -- Failure
      END
    END

    -- Check owner
    IF (@owner_login_name IS NOT NULL)
    BEGIN
      IF (SUSER_SID(@owner_login_name, 0) IS NULL)--force case insensitive comparation for NT users
      BEGIN
        RAISERROR(14262, -1, -1, '@owner_login_name', @owner_login_name)
        RETURN(1) -- Failure
      END
    END

    -- Check subsystem
    IF (@subsystem IS NOT NULL)
    BEGIN
      EXECUTE @retval = sp_verify_subsystem @subsystem
      IF (@retval <> 0)
        RETURN(1) -- Failure
    END

    -- Check job category
    IF (@category_name IS NOT NULL)
    BEGIN
      SELECT @category_id = category_id
      FROM msdb.dbo.syscategories
      WHERE (category_class = 1) -- Job
        AND (name = @category_name)
      IF (@category_id IS NULL)
      BEGIN
        RAISERROR(14262, -1, -1, '@category_name', @category_name)
        RETURN(1) -- Failure
      END
    END

    -- Check enabled state
    IF (@enabled IS NOT NULL) AND (@enabled NOT IN (0, 1))
    BEGIN
      RAISERROR(14266, -1, -1, '@enabled', '0, 1')
      RETURN(1) -- Failure
    END

    -- Check current execution status
    IF (@execution_status IS NOT NULL)
    BEGIN
      IF (@execution_status NOT IN (0, 1, 2, 3, 4, 5, 7))
      BEGIN
        SELECT @res_valid_range = FORMATMESSAGE(14204)
        RAISERROR(14266, -1, -1, '@execution_status', @res_valid_range)
        RETURN(1) -- Failure
      END
    END

    -- If a date comparator is supplied, we must have either a date-created or date-last-modified
    IF ((@date_comparator IS NOT NULL) AND (@date_created IS NOT NULL) AND (@date_last_modified IS NOT NULL)) OR
       ((@date_comparator IS NULL)     AND ((@date_created IS NOT NULL) OR (@date_last_modified IS NOT NULL)))
    BEGIN
      RAISERROR(14282, -1, -1)
      RETURN(1) -- Failure
    END

    -- Check dates / comparator
    IF (@date_comparator IS NOT NULL) AND (@date_comparator NOT IN ('=', '<', '>'))
    BEGIN
      RAISERROR(14266, -1, -1, '@date_comparator', '=, >, <')
      RETURN(1) -- Failure
    END
    IF (@date_created IS NOT NULL) AND
       ((@date_created < '19900101') OR (@date_created > '99991231 23:59'))
    BEGIN
      RAISERROR(14266, -1, -1, '@date_created', '1990-01-01 12:00am .. 9999-12-31 11:59pm')
      RETURN(1) -- Failure
    END
    IF (@date_last_modified IS NOT NULL) AND
       ((@date_last_modified < '19900101') OR (@date_last_modified > '99991231 23:59'))
    BEGIN
      RAISERROR(14266, -1, -1, '@date_last_modified', '1990-01-01 12:00am .. 9999-12-31 11:59pm')
      RETURN(1) -- Failure
    END

    -- Generate results set...
    EXECUTE sp_get_composite_job_info @job_id,
                                      @job_type,
                                      @owner_login_name,
                                      @subsystem,
                                      @category_id,
                                      @enabled,
                                      @execution_status,
                                      @date_comparator,
                                      @date_created,
                                      @date_last_modified,
                                      @description
  END

  RETURN(0) -- Success
END
go

CHECKPOINT
go


/**************************************************************/
/* sp_help_jobcount                                           */
/*      At least one parameter must be specified              */
/*      returns a one row/one column recordset with a integer */
/*      representing the number of jobs assigned to the       */
/*      specified schedule                                    */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_jobcount...'
GO
IF (NOT OBJECT_ID(N'dbo.sp_help_jobcount', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_help_jobcount
GO

CREATE PROCEDURE sp_help_jobcount 
  @schedule_name       sysname  = NULL, -- Specify if @schedule_id is null
  @schedule_id         INT      = NULL  -- Specify if @schedule_name is null
AS
BEGIN
  SET NOCOUNT ON

  DECLARE @retval   INT

  -- Check that we can uniquely identify the schedule. This only returns a schedule that is visible to this user
  EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name',
                                                            @name_of_id_parameter   = '@schedule_id',
                                                            @schedule_name          = @schedule_name    OUTPUT,
                                                            @schedule_id            = @schedule_id      OUTPUT,
                                                            @owner_sid              = NULL,
                                                            @orig_server_id         = NULL
  IF (@retval <> 0)
    RETURN(1) -- Failure 


  SELECT COUNT(*) AS JobCount
  FROM msdb.dbo.sysjobschedules
  WHERE (schedule_id = @schedule_id)


  RETURN (0) -- 0 means success
END
go



/**************************************************************/
/* sp_help_jobs_in_schedule                                   */
/*      At least one parameter must be specified to identify  */
/*      the schedule. Returns the same information as         */
/*      sp_help_job. Only jobs in the specified schedule are  */
/*      in the recordset                                      */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_jobs_in_schedule...'
GO
IF (NOT OBJECT_ID(N'dbo.sp_help_jobs_in_schedule', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_help_jobs_in_schedule
GO

CREATE PROCEDURE sp_help_jobs_in_schedule 
  @schedule_name       sysname  = NULL, -- Specify if @schedule_id is null
  @schedule_id         INT      = NULL  -- Specify if @schedule_name is null
AS
BEGIN
  SET NOCOUNT ON

  DECLARE @retval   INT

  -- Check that we can uniquely identify the schedule. This only returns a schedule that is visible to this user
  EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name',
                                                            @name_of_id_parameter   = '@schedule_id',
                                                            @schedule_name          = @schedule_name    OUTPUT,
                                                            @schedule_id            = @schedule_id      OUTPUT,
                                                            @owner_sid              = NULL,
                                                            @orig_server_id         = NULL
  IF (@retval <> 0)
    RETURN(1) -- Failure 

  EXECUTE @retval = msdb.dbo.sp_get_composite_job_info @schedule_id = @schedule_id
  IF (@retval <> 0)
    RETURN(1) -- Failure 


  RETURN (0) -- 0 means success
END
go


/**************************************************************/
/* SP_MANAGE_JOBS_BY_LOGIN                                    */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_manage_jobs_by_login...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_manage_jobs_by_login')
              AND (type = 'P')))
  DROP PROCEDURE sp_manage_jobs_by_login
go
CREATE PROCEDURE sp_manage_jobs_by_login
  @action                   VARCHAR(10), -- DELETE or REASSIGN
  @current_owner_login_name sysname,
  @new_owner_login_name     sysname = NULL
AS
BEGIN
  DECLARE @current_sid   VARBINARY(85)
  DECLARE @new_sid       VARBINARY(85)
  DECLARE @job_id        UNIQUEIDENTIFIER
  DECLARE @rows_affected INT
  DECLARE @is_sysadmin   INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @action                   = LTRIM(RTRIM(@action))
  SELECT @current_owner_login_name = LTRIM(RTRIM(@current_owner_login_name))
  SELECT @new_owner_login_name     = LTRIM(RTRIM(@new_owner_login_name))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@new_owner_login_name = N'') SELECT @new_owner_login_name = NULL

  -- Only a sysadmin can do this
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Check action
  IF (@action NOT IN ('DELETE', 'REASSIGN'))
  BEGIN
    RAISERROR(14266, -1, -1, '@action', 'DELETE, REASSIGN')
    RETURN(1) -- Failure
  END

  -- Check parameter combinations
  IF ((@action = 'DELETE') AND (@new_owner_login_name IS NOT NULL))
    RAISERROR(14281, 0, 1)

  IF ((@action = 'REASSIGN') AND (@new_owner_login_name IS NULL))
  BEGIN
    RAISERROR(14237, -1, -1)
    RETURN(1) -- Failure
  END

  -- Check current login
  SELECT @current_sid = dbo.SQLAGENT_SUSER_SID(@current_owner_login_name)
  IF (@current_sid IS NULL)
  BEGIN
    RAISERROR(14262, -1, -1, '@current_owner_login_name', @current_owner_login_name)
    RETURN(1) -- Failure
  END

  -- Check new login (if supplied)
  IF (@new_owner_login_name IS NOT NULL)
  BEGIN
    SELECT @new_sid = dbo.SQLAGENT_SUSER_SID(@new_owner_login_name)
    IF (@new_sid IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@new_owner_login_name', @new_owner_login_name)
      RETURN(1) -- Failure
    END
  END

  IF (@action = 'DELETE')
  BEGIN
    DECLARE jobs_to_delete CURSOR LOCAL
    FOR
    SELECT job_id
    FROM msdb.dbo.sysjobs
    WHERE (owner_sid = @current_sid)

    OPEN jobs_to_delete
    FETCH NEXT FROM jobs_to_delete INTO @job_id

    SELECT @rows_affected = 0
    WHILE (@@fetch_status = 0)
    BEGIN
      EXECUTE sp_delete_job @job_id = @job_id
      SELECT @rows_affected = @rows_affected + 1
      FETCH NEXT FROM jobs_to_delete INTO @job_id
    END
    DEALLOCATE jobs_to_delete
    RAISERROR(14238, 0, 1, @rows_affected)
  END
  ELSE
  IF (@action = 'REASSIGN')
  BEGIN
    -- Check if the current owner owns any multi-server jobs.
    -- If they do, then the new owner must be member of the sysadmin role.
    IF (EXISTS (SELECT *
                FROM msdb.dbo.sysjobs       sj,
                     msdb.dbo.sysjobservers sjs
                WHERE (sj.job_id = sjs.job_id)
                  AND (sj.owner_sid = @current_sid)
                  AND (sjs.server_id <> 0)) AND @new_sid <> 0xFFFFFFFF) -- speical account allowed for MSX jobs
    BEGIN
      SELECT @is_sysadmin = 0
      EXECUTE msdb.dbo.sp_sqlagent_has_server_access @login_name = @new_owner_login_name, @is_sysadmin_member = @is_sysadmin OUTPUT
      IF (@is_sysadmin = 0)
      BEGIN
        RAISERROR(14543, -1, -1, @current_owner_login_name, N'sysadmin')
        RETURN(1) -- Failure
      END
    END

    UPDATE msdb.dbo.sysjobs
    SET owner_sid = @new_sid
    WHERE (owner_sid = @current_sid)
    RAISERROR(14239, 0, 1, @@rowcount, @new_owner_login_name)
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_APPLY_JOB_TO_TARGETS                                    */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_apply_job_to_targets...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_apply_job_to_targets')
              AND (type = 'P')))
  DROP PROCEDURE sp_apply_job_to_targets
go
CREATE PROCEDURE sp_apply_job_to_targets
  @job_id               UNIQUEIDENTIFIER = NULL,   -- Must provide either this or job_name
  @job_name             sysname          = NULL,   -- Must provide either this or job_id
  @target_server_groups NVARCHAR(2048)   = NULL,   -- A comma-separated list of target server groups
  @target_servers       NVARCHAR(2048)   = NULL,   -- An comma-separated list of target servers
  @operation            VARCHAR(7)       = 'APPLY' -- Or 'REMOVE'
AS
BEGIN
  DECLARE @retval        INT
  DECLARE @rows_affected INT
  DECLARE @server_name   sysname
  DECLARE @groups        NVARCHAR(2048)
  DECLARE @group         sysname
  DECLARE @servers       NVARCHAR(2048)
  DECLARE @server        sysname
  DECLARE @pos_of_comma  INT

  SET NOCOUNT ON

  -- Only a sysadmin can do this
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Remove any leading/trailing spaces from parameters
  SELECT @target_server_groups = LTRIM(RTRIM(@target_server_groups))
  SELECT @target_servers       = UPPER(LTRIM(RTRIM(@target_servers)))
  SELECT @operation            = LTRIM(RTRIM(@operation))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@target_server_groups = NULL) SELECT @target_server_groups = NULL
  IF (@target_servers       = NULL) SELECT @target_servers = NULL
  IF (@operation            = NULL) SELECT @operation = NULL

  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check operation type
  IF ((@operation <> 'APPLY') AND (@operation <> 'REMOVE'))
  BEGIN
    RAISERROR(14266, -1, -1, '@operation', 'APPLY, REMOVE')
    RETURN(1) -- Failure
  END

  -- Check that we have a target server group list and/or a target server list
  IF ((@target_server_groups IS NULL) AND (@target_servers IS NULL))
  BEGIN
    RAISERROR(14283, -1, -1)
    RETURN(1) -- Failure
  END

  DECLARE @temp_groups TABLE (group_name sysname COLLATE database_default NOT NULL)
  DECLARE @temp_server_name TABLE (server_name sysname COLLATE database_default NOT NULL)

  -- Parse the Target Server comma-separated list (if supplied)
  IF (@target_servers IS NOT NULL)
  BEGIN
    SELECT @servers = @target_servers
    SELECT @pos_of_comma = CHARINDEX(N',', @servers)
    WHILE (@pos_of_comma <> 0)
    BEGIN
      SELECT @server = SUBSTRING(@servers, 1, @pos_of_comma - 1)
      INSERT INTO @temp_server_name (server_name) VALUES (LTRIM(RTRIM(@server)))
      SELECT @servers = RIGHT(@servers, (DATALENGTH(@servers) / 2) - @pos_of_comma)
      SELECT @pos_of_comma = CHARINDEX(N',', @servers)
    END
    INSERT INTO @temp_server_name (server_name) VALUES (LTRIM(RTRIM(@servers)))
  END

  -- Parse the Target Server Groups comma-separated list
  IF (@target_server_groups IS NOT NULL)
  BEGIN
    SELECT @groups = @target_server_groups
    SELECT @pos_of_comma = CHARINDEX(N',', @groups)
    WHILE (@pos_of_comma <> 0)
    BEGIN
      SELECT @group = SUBSTRING(@groups, 1, @pos_of_comma - 1)
      INSERT INTO @temp_groups (group_name) VALUES (LTRIM(RTRIM(@group)))
      SELECT @groups = RIGHT(@groups, (DATALENGTH(@groups) / 2) - @pos_of_comma)
      SELECT @pos_of_comma = CHARINDEX(N',', @groups)
    END
    INSERT INTO @temp_groups (group_name) VALUES (LTRIM(RTRIM(@groups)))
  END

  -- Check server groups
  SET ROWCOUNT 1 -- We do this so that we catch the FIRST invalid group
  SELECT @group = NULL
  SELECT @group = group_name
  FROM @temp_groups
  WHERE group_name NOT IN (SELECT name
                           FROM msdb.dbo.systargetservergroups)
  IF (@group IS NOT NULL)
  BEGIN
    RAISERROR(14262, -1, -1, '@target_server_groups', @group)
    RETURN(1) -- Failure
  END
  SET ROWCOUNT 0

  -- Find the distinct list of servers being targeted
  INSERT INTO @temp_server_name (server_name)
  SELECT DISTINCT sts.server_name
  FROM msdb.dbo.systargetservergroups       stsg,
       msdb.dbo.systargetservergroupmembers stsgm,
       msdb.dbo.systargetservers            sts
  WHERE (stsg.name IN (SELECT group_name FROM @temp_groups))
    AND (stsg.servergroup_id = stsgm.servergroup_id)
    AND (stsgm.server_id = sts.server_id)
    AND (UPPER(sts.server_name) NOT IN (SELECT server_name
                                        FROM @temp_server_name))

  IF (@operation = 'APPLY')
  BEGIN
    -- Remove those servers to which the job has already been applied
    DELETE FROM @temp_server_name
    WHERE server_name IN (SELECT sts.server_name
                          FROM msdb.dbo.sysjobservers    sjs,
                               msdb.dbo.systargetservers sts
                          WHERE (sjs.job_id = @job_id)
                            AND (sjs.server_id = sts.server_id))
  END

  IF (@operation = 'REMOVE')
  BEGIN
    -- Remove those servers to which the job is not currently applied
    DELETE FROM @temp_server_name
    WHERE server_name NOT IN (SELECT sts.server_name
                              FROM msdb.dbo.sysjobservers    sjs,
                                   msdb.dbo.systargetservers sts
                              WHERE (sjs.job_id = @job_id)
                                AND (sjs.server_id = sts.server_id))
  END

  SELECT @rows_affected = COUNT(*)
  FROM @temp_server_name

  SET ROWCOUNT 1
  WHILE (EXISTS (SELECT *
                 FROM @temp_server_name))
  BEGIN
    SELECT @server_name = server_name
    FROM @temp_server_name
    IF (@operation = 'APPLY')
      EXECUTE sp_add_jobserver @job_id = @job_id, @server_name = @server_name
    ELSE
    IF (@operation = 'REMOVE')
      EXECUTE sp_delete_jobserver @job_id = @job_id, @server_name = @server_name
    DELETE FROM @temp_server_name
    WHERE (server_name = @server_name)
  END
  SET ROWCOUNT 0

  IF (@operation = 'APPLY')
    RAISERROR(14240, 0, 1, @rows_affected)
  IF (@operation = 'REMOVE')
    RAISERROR(14241, 0, 1, @rows_affected)

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_REMOVE_JOB_FROM_TARGETS                                 */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_remove_job_from_targets...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_remove_job_from_targets')
              AND (type = 'P')))
  DROP PROCEDURE sp_remove_job_from_targets
go
CREATE PROCEDURE sp_remove_job_from_targets
  @job_id               UNIQUEIDENTIFIER = NULL,   -- Must provide either this or job_name
  @job_name             sysname          = NULL,   -- Must provide either this or job_id
  @target_server_groups NVARCHAR(1024)   = NULL,   -- A comma-separated list of target server groups
  @target_servers       NVARCHAR(1024)   = NULL    -- A comma-separated list of target servers
AS
BEGIN
  DECLARE @retval INT

  SET NOCOUNT ON

  EXECUTE @retval = sp_apply_job_to_targets @job_id,
                                            @job_name,
                                            @target_server_groups,
                                            @target_servers,
                                           'REMOVE'
  RETURN(@retval) -- 0 means success
END
go

/**************************************************************/
/* SP_GET_JOB_ALERTS                                          */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_get_job_alerts...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_get_job_alerts')
              AND (type = 'P')))
  DROP PROCEDURE sp_get_job_alerts
go
CREATE PROCEDURE sp_get_job_alerts
  @job_id   UNIQUEIDENTIFIER = NULL,
  @job_name sysname          = NULL
AS
BEGIN
  DECLARE @retval INT

  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  SELECT id,
         name,
         enabled,
       type = CASE ISNULL(performance_condition, '!')
         WHEN '!' THEN 1              -- SQL Server event alert
         ELSE CASE event_id
            WHEN 8 THEN 3          -- WMI event alert
            ELSE 2                    -- SQL Server performance condition alert
         END
       END
  FROM msdb.dbo.sysalerts
  WHERE (job_id = @job_id)

  RETURN(0) -- Success
END
go

/**************************************************************/
/*                                                            */
/*   S  U  P  P  O  R  T     P  R  O  C  E  D  U  R  E  S     */
/*                                                            */
/**************************************************************/

/**************************************************************/
/* SP_CONVERT_JOBID_TO_CHAR [used by SEM only]                */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_convert_jobid_to_char...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_convert_jobid_to_char')
              AND (type = 'P')))
  DROP PROCEDURE sp_convert_jobid_to_char
go
CREATE PROCEDURE sp_convert_jobid_to_char
  @job_id         UNIQUEIDENTIFIER,
  @job_id_as_char NVARCHAR(34) OUTPUT -- 34 because of the leading '0x'
AS
BEGIN
  DECLARE @job_id_as_binary BINARY(16)
  DECLARE @temp             NCHAR(8)
  DECLARE @counter          INT
  DECLARE @byte_value       INT
  DECLARE @high_word        INT
  DECLARE @low_word         INT
  DECLARE @high_high_nybble INT
  DECLARE @high_low_nybble  INT
  DECLARE @low_high_nybble  INT
  DECLARE @low_low_nybble   INT

  SET NOCOUNT ON

  SELECT @job_id_as_binary = CONVERT(BINARY(16), @job_id)
  SELECT @temp = CONVERT(NCHAR(8), @job_id_as_binary)

  SELECT @job_id_as_char = N''
  SELECT @counter = 1

  WHILE (@counter <= (DATALENGTH(@temp) / 2))
  BEGIN
    SELECT @byte_value       = CONVERT(INT, CONVERT(BINARY(2), SUBSTRING(@temp, @counter, 1)))
    SELECT @high_word        = (@byte_value & 0xff00) / 0x100
    SELECT @low_word         = (@byte_value & 0x00ff)
    SELECT @high_high_nybble = (@high_word & 0xff) / 16
    SELECT @high_low_nybble  = (@high_word & 0xff) % 16
    SELECT @low_high_nybble  = (@low_word & 0xff) / 16
    SELECT @low_low_nybble   = (@low_word & 0xff) % 16

    IF (@high_high_nybble < 10)
      SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('0') + @high_high_nybble)
    ELSE
      SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('A') + (@high_high_nybble - 10))

    IF (@high_low_nybble < 10)
      SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('0') + @high_low_nybble)
    ELSE
      SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('A') + (@high_low_nybble - 10))

    IF (@low_high_nybble < 10)
      SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('0') + @low_high_nybble)
    ELSE
      SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('A') + (@low_high_nybble - 10))

    IF (@low_low_nybble < 10)
      SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('0') + @low_low_nybble)
    ELSE
      SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('A') + (@low_low_nybble - 10))

    SELECT @counter = @counter + 1
  END

  SELECT @job_id_as_char = N'0x' + LOWER(@job_id_as_char)
END
go

/**************************************************************/
/* SP_START_JOB                                               */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_start_job...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_start_job')
              AND (type = 'P')))
  DROP PROCEDURE sp_start_job
go
CREATE PROCEDURE sp_start_job
  @job_name    sysname          = NULL,
  @job_id      UNIQUEIDENTIFIER = NULL,
  @error_flag  INT              = 1,    -- Set to 0 to suppress the error from sp_sqlagent_notify if SQLServerAgent is not running
  @server_name sysname          = NULL, -- The specific target server to start the [multi-server] job on
  @step_name   sysname          = NULL, -- The name of the job step to start execution with [for use with a local job only]
  @output_flag INT              = 1     -- Set to 0 to suppress the success message
AS
BEGIN
  DECLARE @job_id_as_char VARCHAR(36)
  DECLARE @retval         INT
  DECLARE @step_id        INT
  DECLARE @job_owner_sid  VARBINARY(85)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @job_name    = LTRIM(RTRIM(@job_name))
  SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name)))
  SELECT @step_name   = LTRIM(RTRIM(@step_name))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@job_name = N'')    SELECT @job_name = NULL
  IF (@server_name = N'') SELECT @server_name = NULL
  IF (@step_name = N'')   SELECT @step_name = NULL

  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT,
                                               @owner_sid = @job_owner_sid OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check permissions beyond what's checked by the sysjobs_view
  -- SQLAgentReader role can see all jobs but
  -- cannot start/stop jobs they do not own
  IF (@job_owner_sid <> SUSER_SID()                      -- does not own the job
     AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0)     -- is not sysadmin
     AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0))  -- is not SQLAgentOperatorRole
  BEGIN
   RAISERROR(14393, -1, -1);  
   RETURN(1) -- Failure
  END

  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysjobservers
                  WHERE (job_id = @job_id)))
  BEGIN
    SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id)
    RAISERROR(14256, -1, -1, @job_name, @job_id_as_char)
    RETURN(1) -- Failure
  END

  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysjobservers
              WHERE (job_id = @job_id)
                AND (server_id = 0)))
  BEGIN
    -- The job is local, so start (run) the job locally

    -- Check the step name (if supplied)
    IF (@step_name IS NOT NULL)
    BEGIN
      SELECT @step_id = step_id
      FROM msdb.dbo.sysjobsteps
      WHERE (step_name = @step_name)
        AND (job_id = @job_id)

      IF (@step_id IS NULL)
      BEGIN
        RAISERROR(14262, -1, -1, '@step_name', @step_name)
        RETURN(1) -- Failure
      END
    END

    EXECUTE @retval = msdb.dbo.sp_sqlagent_notify @op_type     = N'J',
                                                  @job_id      = @job_id,
                                                  @schedule_id = @step_id, -- This is the start step
                                                  @action_type = N'S',
                                                  @error_flag  = @error_flag
    IF ((@retval = 0) AND (@output_flag = 1))
      RAISERROR(14243, 0, 1, @job_name)
  END
  ELSE
  BEGIN
    -- The job is a multi-server job

      -- Only sysadmin can start multi-server job
      IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
      BEGIN
         RAISERROR(14397, -1, -1);
         RETURN(1) -- Failure
      END            

    -- Check target server name (if any)
    IF (@server_name IS NOT NULL)
    BEGIN
      IF (NOT EXISTS (SELECT *
                      FROM msdb.dbo.systargetservers
                      WHERE (UPPER(server_name) = @server_name)))
      BEGIN
        RAISERROR(14262, -1, -1, '@server_name', @server_name)
        RETURN(1) -- Failure
      END
    END

    -- Re-post the job if it's an auto-delete job
    IF ((SELECT delete_level
         FROM msdb.dbo.sysjobs
         WHERE (job_id = @job_id)) <> 0)
      EXECUTE @retval = msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id, @server_name

    -- Post start instruction(s)
    EXECUTE @retval = msdb.dbo.sp_post_msx_operation 'START', 'JOB', @job_id, @server_name
  END

  RETURN(@retval) -- 0 means success
END
go

/**************************************************************/
/* SP_STOP_JOB                                                */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_stop_job...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_stop_job')
              AND (type = 'P')))
  DROP PROCEDURE sp_stop_job
go
CREATE PROCEDURE sp_stop_job
  @job_name           sysname          = NULL,
  @job_id             UNIQUEIDENTIFIER = NULL,
  @originating_server sysname          = NULL, -- So that we can stop ALL jobs that came from the given server
  @server_name        sysname        = NULL  -- The specific target server to stop the [multi-server] job on
AS
BEGIN
  DECLARE @job_id_as_char           VARCHAR(36)
  DECLARE @retval                   INT
  DECLARE @num_parameters           INT
  DECLARE @job_owner_sid         VARBINARY(85)
  
  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @job_name           = LTRIM(RTRIM(@job_name))
  SELECT @originating_server = UPPER(LTRIM(RTRIM(@originating_server)))
  SELECT @server_name        = UPPER(LTRIM(RTRIM(@server_name)))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@job_name           = N'') SELECT @job_name = NULL
  IF (@originating_server = N'') SELECT @originating_server = NULL
  IF (@server_name        = N'') SELECT @server_name = NULL

  -- We must have EITHER a job id OR a job name OR an originating server
  SELECT @num_parameters = 0
  IF (@job_id IS NOT NULL)
    SELECT @num_parameters = @num_parameters + 1
  IF (@job_name IS NOT NULL)
    SELECT @num_parameters = @num_parameters + 1
  IF (@originating_server IS NOT NULL)
    SELECT @num_parameters = @num_parameters + 1
  IF (@num_parameters <> 1)
  BEGIN
    RAISERROR(14232, -1, -1)
    RETURN(1) -- Failure
  END
  
  IF (@originating_server IS NOT NULL)
  BEGIN 
    -- Stop (cancel) ALL local jobs that originated from the specified server
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.sysjobs_view
                    WHERE (originating_server = @originating_server)))
    BEGIN
      RAISERROR(14268, -1, -1, @originating_server)
      RETURN(1) -- Failure
    END

    -- Check permissions beyond what's checked by the sysjobs_view
    -- SQLAgentReader role that can see all jobs but
    -- cannot start/stop jobs they do not own
    IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0)          -- is not sysadmin
       AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0)) -- is not SQLAgentOperatorRole
    BEGIN
       RAISERROR(14393, -1, -1);
       RETURN(1) -- Failure
    END

    DECLARE @total_counter   INT
    DECLARE @success_counter INT

    DECLARE stop_jobs CURSOR LOCAL
    FOR
    SELECT job_id
    FROM msdb.dbo.sysjobs_view
    WHERE (originating_server = @originating_server)

    SELECT @total_counter = 0, @success_counter = 0
    OPEN stop_jobs
    FETCH NEXT FROM stop_jobs INTO @job_id
    WHILE (@@fetch_status = 0)
    BEGIN
      SELECT @total_counter + @total_counter + 1
      EXECUTE @retval = msdb.dbo.sp_sqlagent_notify @op_type     = N'J',
                                                    @job_id      = @job_id,
                                                    @action_type = N'C'
      IF (@retval = 0)
        SELECT @success_counter = @success_counter + 1
      FETCH NEXT FROM stop_jobs INTO @job_id
    END
    RAISERROR(14253, 0, 1, @success_counter, @total_counter)
    DEALLOCATE stop_jobs

    RETURN(0) -- 0 means success
  END
  ELSE
  BEGIN
    -- Stop ONLY the specified job
    EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                                '@job_id',
                                                 @job_name OUTPUT,
                                                 @job_id   OUTPUT,
                                                 @owner_sid = @job_owner_sid OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure

    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.sysjobservers
                    WHERE (job_id = @job_id)))
    BEGIN
      SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id)
      RAISERROR(14257, -1, -1, @job_name, @job_id_as_char)
      RETURN(1) -- Failure
    END
    
    -- Check permissions beyond what's checked by the sysjobs_view
    -- SQLAgentReader role that can see all jobs but
    -- cannot start/stop jobs they do not own
    IF (@job_owner_sid <> SUSER_SID()                  -- does not own the job
       AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0)       -- is not sysadmin
       AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0)) -- is not SQLAgentOperatorRole
    BEGIN
     RAISERROR(14393, -1, -1);
     RETURN(1) -- Failure
    END

    IF (EXISTS (SELECT *
                FROM msdb.dbo.sysjobservers
                WHERE (job_id = @job_id)
                  AND (server_id = 0)))
    BEGIN
      -- The job is local, so stop (cancel) the job locally
      EXECUTE @retval = msdb.dbo.sp_sqlagent_notify @op_type     = N'J',
                                                    @job_id      = @job_id,
                                                    @action_type = N'C'
      IF (@retval = 0)
        RAISERROR(14254, 0, 1, @job_name)
    END
    ELSE
    BEGIN
      -- The job is a multi-server job

      -- Only sysadmin can stop multi-server job
      IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
      BEGIN
         RAISERROR(14397, -1, -1);
         RETURN(1) -- Failure
      END            

      -- Check target server name (if any)
      IF (@server_name IS NOT NULL)
      BEGIN
        IF (NOT EXISTS (SELECT *
                        FROM msdb.dbo.systargetservers
                        WHERE (UPPER(server_name) = @server_name)))
        BEGIN
          RAISERROR(14262, -1, -1, '@server_name', @server_name)
          RETURN(1) -- Failure
        END
      END

      -- Post the stop instruction(s)
      EXECUTE @retval = sp_post_msx_operation 'STOP', 'JOB', @job_id, @server_name
    END

    RETURN(@retval) -- 0 means success
  END

END
go

/**************************************************************/
/* SP_CYCLE_AGENT_ERRORLOG                                    */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_cycle_agent_errorlog...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_cycle_agent_errorlog')
              AND (type = 'P')))
  DROP PROCEDURE sp_cycle_agent_errorlog
go

CREATE PROCEDURE sp_cycle_agent_errorlog
AS
BEGIN
   exec sp_sqlagent_notify N'L'
END
go

/**************************************************************/
/* SP_GET_CHUNKED_JOBSTEP_PARAMS                              */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_get_chunked_jobstep_params...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_get_chunked_jobstep_params')
              AND (type = 'P')))
  DROP PROCEDURE sp_get_chunked_jobstep_params
go
CREATE PROCEDURE sp_get_chunked_jobstep_params
  @job_name sysname,
  @step_id  INT = 1
AS
BEGIN
  DECLARE @job_id           UNIQUEIDENTIFIER
  DECLARE @step_id_as_char  VARCHAR(10)
  DECLARE @text_pointer     VARBINARY(16)
  DECLARE @remaining_length INT
  DECLARE @offset           INT
  DECLARE @chunk            INT
  DECLARE @retval           INT

  SET NOCOUNT ON

  -- Check that the job exists
  EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                              '@job_id',
                                               @job_name OUTPUT,
                                               @job_id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check that the step exists
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysjobsteps
                  WHERE (job_id = @job_id)
                    AND (step_id = @step_id)))
  BEGIN
    SELECT @step_id_as_char = CONVERT(VARCHAR(10), @step_id)
    RAISERROR(14262, -1, -1, '@step_id', @step_id_as_char)
    RETURN(1) -- Failure
  END

  -- Return the sysjobsteps.additional_parameters TEXT column as multiple readtexts of
  -- length 2048

  SELECT @text_pointer = TEXTPTR(additional_parameters),
         @remaining_length = (DATALENGTH(additional_parameters) / 2)
  FROM msdb.dbo.sysjobsteps
  WHERE (job_id = @job_id)
    AND (step_id = @step_id)

  SELECT @offset = 0, @chunk = 100

  -- Get all the chunks of @chunk size
  WHILE (@remaining_length > @chunk)
  BEGIN
    READTEXT msdb.dbo.sysjobsteps.additional_parameters @text_pointer @offset @chunk
    SELECT @offset = @offset + @chunk
    SELECT @remaining_length = @remaining_length - @chunk
  END

  -- Get the last chunk
  IF (@remaining_length > 0)
    READTEXT msdb.dbo.sysjobsteps.additional_parameters @text_pointer @offset @remaining_length

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_CHECK_FOR_OWNED_JOBS                                    */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_check_for_owned_jobs...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_check_for_owned_jobs')
              AND (type = 'P')))
  DROP PROCEDURE sp_check_for_owned_jobs
go
CREATE PROCEDURE sp_check_for_owned_jobs
  @login_name sysname,
  @table_name sysname
AS
BEGIN
  SET NOCOUNT ON

  -- This procedure is called by sp_droplogin to check if the login being dropped
  -- still owns jobs.  The return value (the number of jobs owned) is passed back
  -- via the supplied table name [this cumbersome approach is necessary because
  -- sp_check_for_owned_jobs is invoked via an EXEC() and because we always want
  -- sp_droplogin to work, even if msdb and/or sysjobs does not exist].

  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysobjects
              WHERE (name = N'sysjobs')
                AND (type = 'U')))
  BEGIN
    DECLARE @sql NVARCHAR(1024)
    SET @sql = N'INSERT INTO ' + QUOTENAME(@table_name, N'[') + N' SELECT COUNT(*) FROM msdb.dbo.sysjobs WHERE (owner_sid = SUSER_SID(N' + QUOTENAME(@login_name, '''') + ', 0))' --force case insensitive comparation for NT users
    EXEC sp_executesql @statement = @sql  
  END
END
go

/**************************************************************/
/* SP_CHECK_FOR_OWNED_JOBSTEPS                                */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_check_for_owned_jobsteps...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_check_for_owned_jobsteps')
              AND (type = 'P')))
  DROP PROCEDURE sp_check_for_owned_jobsteps
go
CREATE PROCEDURE sp_check_for_owned_jobsteps
  @login_name         sysname = NULL,  -- Supply this OR the database_X parameters, but not both
  @database_name      sysname = NULL,
  @database_user_name sysname = NULL
AS
BEGIN
  DECLARE @db_name            NVARCHAR(128)
  DECLARE @delimited_db_name  NVARCHAR(258)
  DECLARE @escaped_db_name    NVARCHAR(256) -- double sysname
  DECLARE @escaped_login_name NVARCHAR(256) -- double sysname

  SET NOCOUNT ON

  CREATE TABLE #work_table
  (
  database_name      sysname COLLATE database_default,
  database_user_name sysname COLLATE database_default
  )

  IF ((@login_name IS NOT NULL) AND (@database_name IS NULL) AND (@database_user_name IS NULL))
  BEGIN
    IF (SUSER_SID(@login_name, 0) IS NULL)--force case insensitive comparation for NT users
    BEGIN
      DROP TABLE #work_table

      RAISERROR(14262, -1, -1, '@login_name', @login_name)
      RETURN(1) -- Failure
    END

    DECLARE all_databases CURSOR LOCAL
    FOR
    SELECT name
    FROM master.dbo.sysdatabases

    OPEN all_databases
    FETCH NEXT FROM all_databases INTO @db_name

    -- Double up any single quotes in @login_name
    SELECT @escaped_login_name = REPLACE(@login_name, N'''', N'''''')

    WHILE (@@fetch_status = 0)
    BEGIN
      SELECT @delimited_db_name = QUOTENAME(@db_name, N'[')
      SELECT @escaped_db_name = REPLACE(@db_name, '''', '''''')
      EXECUTE(N'INSERT INTO #work_table
                SELECT N''' + @escaped_db_name + N''', name
                FROM ' + @delimited_db_name + N'.dbo.sysusers
                WHERE (sid = SUSER_SID(N''' + @escaped_login_name + N''', 0))')--force case insensitive comparation for NT users
      FETCH NEXT FROM all_databases INTO @db_name
    END

    DEALLOCATE all_databases

    -- If the login is an NT login, check for steps run as the login directly (as is the case with transient NT logins)
    IF (@login_name LIKE '%\%')
    BEGIN
      INSERT INTO #work_table
      SELECT database_name, database_user_name
      FROM msdb.dbo.sysjobsteps
      WHERE (database_user_name = @login_name)
    END
  END

  IF ((@login_name IS NULL) AND (@database_name IS NOT NULL) AND (@database_user_name IS NOT NULL))
  BEGIN
    INSERT INTO #work_table
    SELECT @database_name, @database_user_name
  END

  IF (EXISTS (SELECT *
              FROM #work_table wt,
                   msdb.dbo.sysjobsteps sjs
              WHERE (wt.database_name = sjs.database_name)
                AND (wt.database_user_name = sjs.database_user_name)))
  BEGIN
    SELECT sjv.job_id,
           sjv.name,
           sjs.step_id,
           sjs.step_name
    FROM #work_table           wt,
         msdb.dbo.sysjobsteps  sjs,
         msdb.dbo.sysjobs_view sjv
    WHERE (wt.database_name = sjs.database_name)
      AND (wt.database_user_name = sjs.database_user_name)
      AND (sjv.job_id = sjs.job_id)
    ORDER BY sjs.job_id
  END

  DROP TABLE #work_table
  RETURN(0) -- 0 means success
END
go

/**************************************************************/
/* SP_SQLAGENT_REFRESH_JOB                                    */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_sqlagent_refresh_job...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_sqlagent_refresh_job')
              AND (type = 'P')))
  DROP PROCEDURE sp_sqlagent_refresh_job
go
CREATE PROCEDURE sp_sqlagent_refresh_job
  @job_id      UNIQUEIDENTIFIER = NULL,
  @server_name sysname          = NULL -- This parameter allows a TSX to use this SP when updating a job
AS
BEGIN
  DECLARE @server_id INT

  SET NOCOUNT ON

  IF (@server_name IS NULL) OR (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) = '(LOCAL)')
    SELECT @server_name = CONVERT(sysname, SERVERPROPERTY('ServerName'))

  SELECT @server_name = UPPER(@server_name)

  SELECT @server_id = server_id
  FROM msdb.dbo.systargetservers_view
  WHERE (UPPER(server_name) = ISNULL(@server_name, UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))))

  SELECT @server_id = ISNULL(@server_id, 0)

  SELECT sjv.job_id,
         sjv.name,
         sjv.enabled,
         sjv.start_step_id,
         owner = dbo.SQLAGENT_SUSER_SNAME(sjv.owner_sid),
         sjv.notify_level_eventlog,
         sjv.notify_level_email,
         sjv.notify_level_netsend,
         sjv.notify_level_page,
         sjv.notify_email_operator_id,
         sjv.notify_netsend_operator_id,
         sjv.notify_page_operator_id,
         sjv.delete_level,
         has_step = (SELECT COUNT(*)
                     FROM msdb.dbo.sysjobsteps sjst
                     WHERE (sjst.job_id = sjv.job_id)),
         sjv.version_number,
         last_run_date = ISNULL(sjs.last_run_date, 0),
         last_run_time = ISNULL(sjs.last_run_time, 0),
         sjv.originating_server,
         sjv.description,
         agent_account = CASE sjv.owner_sid
              WHEN 0xFFFFFFFF THEN 1
              ELSE                 0
         END
  FROM msdb.dbo.sysjobservers sjs,
       msdb.dbo.sysjobs_view  sjv
  WHERE ((@job_id IS NULL) OR (@job_id = sjv.job_id))
    AND (sjv.job_id = sjs.job_id)
    AND (sjs.server_id = @server_id)
  ORDER BY sjv.job_id
  OPTION (FORCE ORDER)

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_JOBHISTORY_ROW_LIMITER                                  */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_jobhistory_row_limiter...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_jobhistory_row_limiter')
              AND (type = 'P')))
  DROP PROCEDURE dbo.sp_jobhistory_row_limiter
go
CREATE PROCEDURE sp_jobhistory_row_limiter
  @job_id UNIQUEIDENTIFIER
AS
BEGIN
  DECLARE @max_total_rows         INT -- This value comes from the registry (MaxJobHistoryTableRows)
  DECLARE @max_rows_per_job       INT -- This value comes from the registry (MaxJobHistoryRows)
  DECLARE @rows_to_delete         INT
  DECLARE @current_rows           INT
  DECLARE @current_rows_per_job   INT

  SET NOCOUNT ON

  -- Get max-job-history-rows from the registry
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'JobHistoryMaxRows',
                                         @max_total_rows OUTPUT,
                                         N'no_output'

  -- Check if we are limiting sysjobhistory rows
  IF (ISNULL(@max_total_rows, -1) = -1)
    RETURN(0)

  -- Check that max_total_rows is more than 1
  IF (ISNULL(@max_total_rows, 0) < 2)
  BEGIN
    -- It isn't, so set the default to 1000 rows
    SELECT @max_total_rows = 1000
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'JobHistoryMaxRows',
                                            N'REG_DWORD',
                                            @max_total_rows
  END

  -- Get the per-job maximum number of rows to keep
  SELECT @max_rows_per_job = 0
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'JobHistoryMaxRowsPerJob',
                                         @max_rows_per_job OUTPUT,
                                         N'no_output'

  -- Check that max_rows_per_job is <= max_total_rows
  IF ((@max_rows_per_job > @max_total_rows) OR (@max_rows_per_job < 1))
  BEGIN
    -- It isn't, so default the rows_per_job to max_total_rows
    SELECT @max_rows_per_job = @max_total_rows
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'JobHistoryMaxRowsPerJob',
                                            N'REG_DWORD',
                                            @max_rows_per_job
  END

  BEGIN TRANSACTION

  SELECT @current_rows_per_job = COUNT(*)
  FROM msdb.dbo.sysjobhistory with (TABLOCKX)
  WHERE (job_id = @job_id)

  -- Delete the oldest history row(s) for the job being inserted if the new row has
  -- pushed us over the per-job row limit (MaxJobHistoryRows)
  SELECT @rows_to_delete = @current_rows_per_job - @max_rows_per_job

  IF (@rows_to_delete > 0)
  BEGIN
    WITH RowsToDelete AS (
      SELECT TOP (@rows_to_delete) *
      FROM msdb.dbo.sysjobhistory
      WHERE (job_id = @job_id)
      ORDER BY instance_id
    )
    DELETE FROM RowsToDelete;
  END

  -- Delete the oldest history row(s) if inserting the new row has pushed us over the
  -- global MaxJobHistoryTableRows limit.
  SELECT @current_rows = COUNT(*)
  FROM msdb.dbo.sysjobhistory

  SELECT @rows_to_delete = @current_rows - @max_total_rows

  IF (@rows_to_delete > 0)
  BEGIN
    WITH RowsToDelete AS (
      SELECT TOP (@rows_to_delete) *
      FROM msdb.dbo.sysjobhistory
      ORDER BY instance_id
    )
    DELETE FROM RowsToDelete;
  END

  IF (@@trancount > 0)
    COMMIT TRANSACTION

  RETURN(0) -- Success
END
go


/**************************************************************/
/* SP_SQLAGENT_LOG_JOBHISTORY                                 */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_sqlagent_log_jobhistory...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_sqlagent_log_jobhistory')
              AND (type = 'P')))
  DROP PROCEDURE sp_sqlagent_log_jobhistory
go
CREATE PROCEDURE sp_sqlagent_log_jobhistory
  @job_id               UNIQUEIDENTIFIER,
  @step_id              INT,
  @sql_message_id       INT = 0,
  @sql_severity         INT = 0,
  @message              NVARCHAR(4000) = NULL,
  @run_status           INT, -- SQLAGENT_EXEC_X code
  @run_date             INT,
  @run_time             INT,
  @run_duration         INT,
  @operator_id_emailed  INT = 0,
  @operator_id_netsent  INT = 0,
  @operator_id_paged    INT = 0,
  @retries_attempted    INT,
  @server               sysname = NULL,
  @session_id           INT = 0
AS
BEGIN
  DECLARE @retval              INT
  DECLARE @operator_id_as_char VARCHAR(10)
  DECLARE @step_name           sysname
  DECLARE @error_severity      INT

  SET NOCOUNT ON

  IF (@server IS NULL) OR (UPPER(@server collate SQL_Latin1_General_CP1_CS_AS) = '(LOCAL)')
    SELECT @server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))

  -- Check authority (only SQLServerAgent can add a history entry for a job)
  EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%'
  IF (@retval <> 0)
    RETURN(@retval)

  -- NOTE: We raise all errors as informational (sev 0) to prevent SQLServerAgent from caching
  --       the operation (if it fails) since if the operation will never run successfully we
  --       don't want it to hang around in the operation cache.
  SELECT @error_severity = 0

  -- Check job_id
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysjobs_view
                  WHERE (job_id = @job_id)))
  BEGIN
    DECLARE @job_id_as_char      VARCHAR(36)
    SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id)
    RAISERROR(14262, @error_severity, -1, 'Job', @job_id_as_char)
    RETURN(1) -- Failure
  END

  -- Check step id
  IF (@step_id <> 0) -- 0 means 'for the whole job'
  BEGIN
    SELECT @step_name = step_name
    FROM msdb.dbo.sysjobsteps
    WHERE (job_id = @job_id)
      AND (step_id = @step_id)
    IF (@step_name IS NULL)
    BEGIN
      DECLARE @step_id_as_char     VARCHAR(10)
      SELECT @step_id_as_char = CONVERT(VARCHAR, @step_id)
      RAISERROR(14262, @error_severity, -1, '@step_id', @step_id_as_char)
      RETURN(1) -- Failure
    END
  END
  ELSE
    SELECT @step_name = FORMATMESSAGE(14570)

  -- Check run_status
  IF (@run_status NOT IN (0, 1, 2, 3, 4, 5)) -- SQLAGENT_EXEC_X code
  BEGIN
    RAISERROR(14266, @error_severity, -1, '@run_status', '0, 1, 2, 3, 4, 5')
    RETURN(1) -- Failure
  END

  -- Check run_date
  EXECUTE @retval = sp_verify_job_date @run_date, '@run_date', 10
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check run_time
  EXECUTE @retval = sp_verify_job_time @run_time, '@run_time', 10
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check operator_id_emailed
  IF (@operator_id_emailed <> 0)
  BEGIN
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.sysoperators
                    WHERE (id = @operator_id_emailed)))
    BEGIN
      SELECT @operator_id_as_char = CONVERT(VARCHAR, @operator_id_emailed)
      RAISERROR(14262, @error_severity, -1, '@operator_id_emailed', @operator_id_as_char)
      RETURN(1) -- Failure
    END
  END

  -- Check operator_id_netsent
  IF (@operator_id_netsent <> 0)
  BEGIN
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.sysoperators
                    WHERE (id = @operator_id_netsent)))
    BEGIN
      SELECT @operator_id_as_char = CONVERT(VARCHAR, @operator_id_netsent)
      RAISERROR(14262, @error_severity, -1, '@operator_id_netsent', @operator_id_as_char)
      RETURN(1) -- Failure
    END
  END

  -- Check operator_id_paged
  IF (@operator_id_paged <> 0)
  BEGIN
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.sysoperators
                    WHERE (id = @operator_id_paged)))
    BEGIN
      SELECT @operator_id_as_char = CONVERT(VARCHAR, @operator_id_paged)
      RAISERROR(14262, @error_severity, -1, '@operator_id_paged', @operator_id_as_char)
      RETURN(1) -- Failure
    END
  END

  -- Insert the history row
  INSERT INTO msdb.dbo.sysjobhistory
         (job_id,
          step_id,
          step_name,
          sql_message_id,
          sql_severity,
          message,
          run_status,
          run_date,
          run_time,
          run_duration,
          operator_id_emailed,
          operator_id_netsent,
          operator_id_paged,
          retries_attempted,
          server)
  VALUES (@job_id,
          @step_id,
          @step_name,
          @sql_message_id,
          @sql_severity,
          @message,
          @run_status,
          @run_date,
          @run_time,
          @run_duration,
          @operator_id_emailed,
          @operator_id_netsent,
          @operator_id_paged,
          @retries_attempted,
          @server)

  -- Update sysjobactivity table 
  IF (@step_id = 0) --only update for job, not for each step
  BEGIN
    UPDATE msdb.dbo.sysjobactivity
    SET stop_execution_date = DATEADD(ms, -DATEPART(ms, GetDate()),  GetDate()),
        job_history_id = SCOPE_IDENTITY()
    WHERE
        session_id = @session_id AND job_id = @job_id
  END
  -- Special handling of replication jobs 
  DECLARE @job_name sysname
  DECLARE @category_id int
  SELECT  @job_name = name, @category_id = category_id from msdb.dbo.sysjobs 
   WHERE job_id = @job_id 
 
  -- If replicatio agents (snapshot, logreader, distribution, merge, and queuereader
  -- and the step has been canceled and if we are at the distributor.
  IF @category_id in (10,13,14,15,19) and @run_status = 3 and 
   object_id('MSdistributiondbs') is not null
  BEGIN
    -- Get the database
    DECLARE @database sysname
    SELECT @database = database_name from sysjobsteps where job_id = @job_id and 
   lower(subsystem) in (N'distribution', N'logreader','snapshot',N'merge',
      N'queuereader')
    -- If the database is a distribution database
    IF EXISTS (select * from MSdistributiondbs where name = @database)
    BEGIN
   DECLARE @proc nvarchar(500)
   SELECT @proc = quotename(@database) + N'.dbo.sp_MSlog_agent_cancel'
   EXEC @proc @job_id = @job_id, @category_id = @category_id, 
      @message = @message
    END  
  END

  -- Delete any history rows that are over the registry-defined limits
  EXECUTE msdb.dbo.sp_jobhistory_row_limiter @job_id

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_SQLAGENT_CHECK_MSX_VERSION                              */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_sqlagent_check_msx_version...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_sqlagent_check_msx_version')
              AND (type = 'P')))
  DROP PROCEDURE sp_sqlagent_check_msx_version
go
CREATE PROCEDURE sp_sqlagent_check_msx_version
  @required_microsoft_version INT = NULL
AS
BEGIN
  SET NOCOUNT ON

  DECLARE @msx_version          NVARCHAR(16)
  DECLARE @required_msx_version NVARCHAR(16)

  IF (@required_microsoft_version IS NULL)
    SELECT @required_microsoft_version = 0x07000252 -- 7.0.594

  IF (@@microsoftversion < @required_microsoft_version)
  BEGIN
    SELECT @msx_version = CONVERT( NVARCHAR(2), CONVERT( INT, CONVERT( BINARY(1), @@microsoftversion / 0x1000000 ) ) )
   + N'.' 
   + CONVERT( NVARCHAR(2), CONVERT( INT, CONVERT( BINARY(1), CONVERT( BINARY(2), ((@@microsoftversion / 0x10000) % 0x100) ) ) ) )
   + N'.'
   + CONVERT( NVARCHAR(4), @@microsoftversion % 0x10000 )

    SELECT @required_msx_version = CONVERT( NVARCHAR(2), CONVERT( INT, CONVERT( BINARY(1), @required_microsoft_version / 0x1000000 ) ) )
   + N'.'
   + CONVERT( NVARCHAR(2), CONVERT( INT, CONVERT( BINARY(1), CONVERT( BINARY(2), ((@required_microsoft_version / 0x10000) % 0x100) ) ) ) )
   + N'.' 
   + CONVERT( NVARCHAR(4), @required_microsoft_version % 0x10000 )    

   RAISERROR(14541, -1, -1, @msx_version, @required_msx_version)
    RETURN(1) -- Failure
  END
  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_SQLAGENT_PROBE_MSX                                      */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_sqlagent_probe_msx...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_sqlagent_probe_msx')
              AND (type = 'P')))
  DROP PROCEDURE sp_sqlagent_probe_msx
go
CREATE PROCEDURE sp_sqlagent_probe_msx
  @server_name          sysname,  -- The name of the target server probing the MSX
  @local_time           NVARCHAR(100), -- The local time at the target server in the format YYYY/MM/DD HH:MM:SS
  @poll_interval        INT,           -- The frequency (in seconds) with which the target polls the MSX
  @time_zone_adjustment INT = NULL     -- The offset from GMT in minutes (may be NULL if unknown)
AS
BEGIN
  DECLARE @bad_enlistment        BIT
  DECLARE @blocking_instructions INT
  DECLARE @pending_instructions  INT

  SET NOCOUNT ON

  SELECT @server_name = UPPER(@server_name)
  SELECT @bad_enlistment = 0, @blocking_instructions = 0, @pending_instructions = 0

  UPDATE msdb.dbo.systargetservers
  SET last_poll_date = GETDATE(),
      local_time_at_last_poll = CONVERT(DATETIME, @local_time, 111),
      poll_interval = @poll_interval,
      time_zone_adjustment = ISNULL(@time_zone_adjustment, time_zone_adjustment)
  WHERE (UPPER(server_name) = @server_name)

  -- If the systargetservers entry is missing (and no DEFECT instruction has been posted)
  -- then the enlistment is bad
  IF (NOT EXISTS (SELECT 1
                  FROM msdb.dbo.systargetservers
                  WHERE (UPPER(server_name) = @server_name))) AND
     (NOT EXISTS (SELECT 1
                  FROM msdb.dbo.sysdownloadlist
                  WHERE (target_server = @server_name)
                    AND (operation_code = 7)
                    AND (object_type = 2)))
    SELECT @bad_enlistment = 1

  SELECT @blocking_instructions = COUNT(*)
  FROM msdb.dbo.sysdownloadlist
  WHERE (target_server = @server_name)
    AND (error_message IS NOT NULL)

  SELECT @pending_instructions = COUNT(*)
  FROM msdb.dbo.sysdownloadlist
  WHERE (target_server = @server_name)
    AND (error_message IS NULL)
    AND (status = 0)

  SELECT @bad_enlistment, @blocking_instructions, @pending_instructions
END
go

/**************************************************************/
/* SP_SET_LOCAL_TIME                                          */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_set_local_time...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_set_local_time')
              AND (type = 'P')))
  DROP PROCEDURE sp_set_local_time
go
CREATE PROCEDURE sp_set_local_time
  @server_name           sysname = NULL,
  @adjustment_in_minutes INT          = 0 -- Only needed for Win9x
AS
BEGIN
  DECLARE @ret              INT
  DECLARE @local_time       INT
  DECLARE @local_date       INT
  DECLARE @current_datetime DATETIME
  DECLARE @local_time_sz    VARCHAR(30)
  DECLARE @cmd              NVARCHAR(200)
  DECLARE @date_format      NVARCHAR(64)
  DECLARE @year_sz          NVARCHAR(16)
  DECLARE @month_sz         NVARCHAR(16)
  DECLARE @day_sz           NVARCHAR(16)

  -- Synchronize the clock with the remote server (if supplied)
  -- NOTE: NT takes timezones into account, whereas Win9x does not
  IF (@server_name IS NOT NULL)
  BEGIN
    SELECT @cmd = N'net time \\' + @server_name + N' /set /y'
    EXECUTE @ret = master.dbo.xp_cmdshell @cmd, no_output
    IF (@ret <> 0)
      RETURN(1) -- Failure
  END

  -- Since NET TIME on Win9x does not take time zones into account we need to manually adjust
  -- for this using @adjustment_in_minutes which will be the difference between the MSX GMT
  -- offset and the target server GMT offset
  IF ((PLATFORM() & 0x2) = 0x2) -- Win9x
  BEGIN
    -- Get the date format from the registry (so that we can construct our DATE command-line command)
    EXECUTE master.dbo.xp_regread N'HKEY_CURRENT_USER',
                                  N'Control Panel\International',
                                  N'sShortDate',
                                  @date_format OUTPUT,
                                  N'no_output'
    SELECT @date_format = LOWER(@date_format)

    IF (@adjustment_in_minutes <> 0)
    BEGIN
      -- Wait for SQLServer to re-cache the OS time
      WAITFOR DELAY '00:01:00'

      SELECT @current_datetime = DATEADD(mi, @adjustment_in_minutes, GETDATE())
      SELECT @local_time_sz = SUBSTRING(CONVERT(VARCHAR, @current_datetime, 8), 1, 5)
      SELECT @local_time = CONVERT(INT, LTRIM(SUBSTRING(@local_time_sz, 1, PATINDEX('%:%', @local_time_sz) - 1)  + SUBSTRING(@local_time_sz, PATINDEX('%:%', @local_time_sz) + 1, 2)))
      SELECT @local_date = CONVERT(INT, CONVERT(VARCHAR, @current_datetime, 112))

      -- Set the date
      SELECT @year_sz = CONVERT(NVARCHAR, @local_date / 10000)
      SELECT @month_sz = CONVERT(NVARCHAR, (@local_date % 10000) / 100)
      SELECT @day_sz = CONVERT(NVARCHAR, @local_date % 100)

      IF (@date_format LIKE N'y%m%d')
        SELECT @cmd = N'DATE ' + @year_sz + N'-' + @month_sz + N'-' + @day_sz
      IF (@date_format LIKE N'y%d%m')
        SELECT @cmd = N'DATE ' + @year_sz + N'-' + @day_sz + N'-' + @month_sz
      IF (@date_format LIKE N'm%d%y')
        SELECT @cmd = N'DATE ' + @month_sz + N'-' + @day_sz + N'-' + @year_sz
      IF (@date_format LIKE N'd%m%y')
        SELECT @cmd = N'DATE ' + @day_sz + N'-' + @month_sz + N'-' + @year_sz

      EXECUTE @ret = master.dbo.xp_cmdshell @cmd, no_output
      IF (@ret <> 0)
        RETURN 1 -- Failure

      -- Set the time (NOTE: We can't set the millisecond part of the time, so we may be up to .999 sec off)
      SELECT @cmd = N'TIME ' + CONVERT(NVARCHAR, @local_time / 100) + N':' + CONVERT(NVARCHAR, @local_time % 100) + ':' + CONVERT(NVARCHAR(2), DATEPART(SS, GETDATE()))
      EXECUTE @ret = master.dbo.xp_cmdshell @cmd, no_output
      IF (@ret <> 0)
        RETURN 1 -- Failure
    END

  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_MULTI_SERVER_JOB_SUMMARY [used by SEM only]             */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_multi_server_job_summary...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_multi_server_job_summary')
              AND (type = 'P')))
  DROP PROCEDURE sp_multi_server_job_summary
go
CREATE PROCEDURE sp_multi_server_job_summary
  @job_id   UNIQUEIDENTIFIER = NULL,
  @job_name sysname          = NULL
AS
BEGIN
  DECLARE @retval INT

  SET NOCOUNT ON

  IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL))
  BEGIN
    EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                                '@job_id',
                                                 @job_name OUTPUT,
                                                 @job_id   OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END

  -- NOTE: We join with syscategories - not sysjobservers - since we want to include jobs
  --       which are of type multi-server but which don't currently have any servers
  SELECT 'job_id'   = sj.job_id,
         'job_name' = sj.name,
         'enabled'  = sj.enabled,
         'category_name'  = sc.name,
         'target_servers' = (SELECT COUNT(*)
                             FROM msdb.dbo.sysjobservers sjs
                             WHERE (sjs.job_id = sj.job_id)),
         'pending_download_instructions' = (SELECT COUNT(*)
                                            FROM msdb.dbo.sysdownloadlist sdl
                                            WHERE (sdl.object_id = sj.job_id)
                                              AND (status = 0)),
         'download_errors' = (SELECT COUNT(*)
                              FROM msdb.dbo.sysdownloadlist sdl
                              WHERE (sdl.object_id = sj.job_id)
                                AND (sdl.error_message IS NOT NULL)),
         'execution_failures' = (SELECT COUNT(*)
                                 FROM msdb.dbo.sysjobservers sjs
                                 WHERE (sjs.job_id = sj.job_id)
                                   AND (sjs.last_run_date <> 0)
                                   AND (sjs.last_run_outcome <> 1)) -- 1 is success
  FROM msdb.dbo.sysjobs sj,
       msdb.dbo.syscategories sc
  WHERE (sj.category_id = sc.category_id)
    AND (sc.category_class = 1) -- JOB
    AND (sc.category_type  = 2) -- Multi-Server
    AND ((@job_id IS NULL)   OR (sj.job_id = @job_id))
    AND ((@job_name IS NULL) OR (sj.name = @job_name))

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_TARGET_SERVER_SUMMARY [used by SEM only]                */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_target_server_summary...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_target_server_summary')
              AND (type = 'P')))
  DROP PROCEDURE sp_target_server_summary
go
CREATE PROCEDURE sp_target_server_summary
  @target_server sysname = NULL
AS
BEGIN
  SET NOCOUNT ON

  SELECT server_id,
         server_name,
        'local_time' = DATEADD(SS, DATEDIFF(SS, last_poll_date, GETDATE()), local_time_at_last_poll),
         last_poll_date,
        'unread_instructions' = (SELECT COUNT(*)
                                 FROM msdb.dbo.sysdownloadlist sdl
                                 WHERE (UPPER(sdl.target_server) = UPPER(sts.server_name))
                                   AND (sdl.status = 0)),
        'blocked' = (SELECT COUNT(*)
                     FROM msdb.dbo.sysdownloadlist sdl
                     WHERE (UPPER(sdl.target_server) = UPPER(sts.server_name))
                       AND (sdl.error_message IS NOT NULL)),
         poll_interval
  FROM msdb.dbo.systargetservers sts
  WHERE ((@target_server IS NULL) OR (UPPER(@target_server) = UPPER(sts.server_name)))
END
go

CHECKPOINT
go


/**************************************************************/
/*                                                            */
/*         6  .  X     P  R  O  C  E  D  U  R  E  S           */
/*                                                            */
/* These procedures are provided for backwards compatability  */
/* with 6.x scripts and 6.x replication.  The re-implemented  */
/* procedures are as follows:                                 */
/*                                                            */
/* - sp_uniquetaskname  (SQLDMO)                              */
/* - systasks_view      (INSTDIST.SQL)                        */
/* - sp_addtask         (INSTREPL.SQL, INSTDIST.SQL, SQLDMO)  */
/* - sp_droptask        (INSTREPL.SQL, INSTDIST.SQL, SQLDMO)  */
/* - systasks           (For completeness only)               */
/**************************************************************/


/**************************************************************/
/* SP_UNIQUETASKNAME                                          */
/**************************************************************/

PRINT ''
PRINT 'Creating [legacy] procedure sp_uniquetaskname...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_uniquetaskname')
              AND (type = 'P')))
  DROP PROCEDURE sp_uniquetaskname
go
CREATE PROCEDURE sp_uniquetaskname
  @seed NVARCHAR(92)
AS
BEGIN
  DECLARE @newest_suffix INT

  SET NOCOUNT ON

  -- We're going to add a suffix of 8 characters so make sure the seed is at most 84 characters
  SELECT @seed = LTRIM(RTRIM(@seed))
  IF (DATALENGTH(@seed) > 0)
    SELECT @seed = SUBSTRING(@seed, 1, 84)

  -- Find the newest (highest) suffix so far
  SELECT @newest_suffix = MAX(CONVERT(INT, RIGHT(name, 8)))
  FROM msdb.dbo.sysjobs -- DON'T use sysjobs_view here!
  WHERE (name LIKE N'%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]')

  -- Generate the task name by appending the 'newest suffix' value (plus one) to the seed
  IF (@newest_suffix IS NOT NULL)
  BEGIN
    SELECT @newest_suffix = @newest_suffix + 1
    SELECT 'TaskName' = CONVERT(NVARCHAR(92), @seed + REPLICATE(N'0', 8 - (DATALENGTH(CONVERT(NVARCHAR, @newest_suffix)) / 2)) + CONVERT(NVARCHAR, @newest_suffix))
  END
  ELSE
    SELECT 'TaskName' = CONVERT(NVARCHAR(92), @seed + N'00000001')
END
go

/**************************************************************/
/* SP_ADDTASK                                                 */
/**************************************************************/

PRINT ''
PRINT 'Creating [legacy] procedure sp_addtask...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_addtask')
              AND (type = 'P')))
  DROP PROCEDURE sp_addtask
go
CREATE PROCEDURE sp_addtask
  @name                   sysname,               -- Was VARCHAR(100) in 6.x
  @subsystem              NVARCHAR(40)   = N'TSQL', -- Was VARCHAR(30) in 6.x
  @server                 sysname        = NULL,
  @username               sysname        = NULL, -- Was VARCHAR(30) in 6.x
  @databasename           sysname        = NULL, -- Was VARCHAR(30) in 6.x
  @enabled                TINYINT        = 0,
  @freqtype               INT            = 2,    -- 2 means OnDemand
  @freqinterval           INT            = 1,
  @freqsubtype            INT            = 1,
  @freqsubinterval        INT            = 1,
  @freqrelativeinterval   INT            = 1,
  @freqrecurrencefactor   INT            = 1,
  @activestartdate        INT            = 0,
  @activeenddate          INT            = 0,
  @activestarttimeofday   INT            = 0,
  @activeendtimeofday     INT            = 0,
  @nextrundate            INT            = 0,
  @nextruntime            INT            = 0,
  @runpriority            INT            = 0,
  @emailoperatorname      sysname        = NULL, -- Was VARCHAR(50) in 6.x
  @retryattempts          INT            = 0,
  @retrydelay             INT            = 10,
  @command                NVARCHAR(max) = NULL,
  @loghistcompletionlevel INT            = 2,
  @emailcompletionlevel   INT            = 0,
  @description            NVARCHAR(512)  = NULL, -- Was VARCHAR(255) in 6.x
  @tagadditionalinfo      VARCHAR(96)    = NULL, -- Obsolete in 7.0
  @tagobjectid            INT            = NULL, -- Obsolete in 7.0
  @tagobjecttype          INT            = NULL, -- Obsolete in 7.0
  @newid                  INT            = NULL OUTPUT,
  @parameters             NTEXT          = NULL, -- Was TEXT in 6.x
  @cmdexecsuccesscode     INT            = 0,
  @category_name          sysname        = NULL, -- New for 7.0
  @category_id            INT            = NULL  -- New for 7.0
AS
BEGIN
  DECLARE @retval INT
  DECLARE @job_id UNIQUEIDENTIFIER
  DECLARE @id     INT
  DECLARE @distdb sysname
  DECLARE @proc nvarchar(255)

  SET NOCOUNT ON

  SELECT @retval = 1 -- 0 means success, 1 means failure

  -- Set 7.0 category names for 6.5 replication tasks
  IF (LOWER(@subsystem) = N'sync')
    SELECT @category_id = 15
  ELSE IF (LOWER(@subsystem) = N'logreader')
    SELECT @category_id = 13
  ELSE IF (LOWER(@subsystem) = N'distribution')
    SELECT @category_id = 10

  -- Convert old replication synchronization subsystem name to the 7.0 name
  IF (LOWER(@subsystem) = N'sync')
    SELECT @subsystem = N'Snapshot'

  -- If a category ID is provided this overrides any supplied category name
  IF (@category_id IS NOT NULL)
  BEGIN
    SELECT @category_name = name
    FROM msdb.dbo.syscategories
    WHERE (category_id = @category_id)
    SELECT @category_name = ISNULL(@category_name, FORMATMESSAGE(14205))
  END

  -- In 6.x active start date was not restricted, but it is in 7.0; so to avoid a "noisey"
  -- failure in sp_add_jobschedule we modify the value accordingly
  IF ((@activestartdate <> 0) AND (@activestartdate < 19900101))
    SELECT @activestartdate = 19900101

  BEGIN TRANSACTION

    -- Add the job
    EXECUTE @retval = sp_add_job
      @job_name                   = @name,
      @enabled                    = @enabled,
      @start_step_id              = 1,
      @description                = @description,
      @category_name              = @category_name,
      @notify_level_eventlog      = @loghistcompletionlevel,
      @notify_level_email         = @emailcompletionlevel,
      @notify_email_operator_name = @emailoperatorname,
      @job_id                     = @job_id OUTPUT

    IF (@retval <> 0)
    BEGIN
      ROLLBACK TRANSACTION
      GOTO Quit
    END

    -- Add an entry to systaskids for the new job (created by a 6.x client)
    INSERT INTO msdb.dbo.systaskids (job_id) VALUES (@job_id)

    -- Get the assigned task id
    SELECT @id = task_id, @newid = task_id
    FROM msdb.dbo.systaskids
    WHERE (job_id = @job_id)

    -- Add the job step
    EXECUTE @retval = sp_add_jobstep
      @job_id                = @job_id,
      @step_id               = 1,
      @step_name             = N'Step 1',
      @subsystem             = @subsystem,
      @command               = @command,
      @additional_parameters = @parameters,
      @cmdexec_success_code  = @cmdexecsuccesscode,
      @server                = @server,
      @database_name         = @databasename,
      @database_user_name    = @username,
      @retry_attempts        = @retryattempts,
      @retry_interval        = @retrydelay,
      @os_run_priority       = @runpriority

    IF (@retval <> 0)
    BEGIN
      ROLLBACK TRANSACTION
      GOTO Quit
    END

    -- Add the job schedule
    IF (@activestartdate = 0)
      SELECT @activestartdate = NULL
    IF (@activeenddate = 0)
      SELECT @activeenddate = NULL
    IF (@activestarttimeofday = 0)
      SELECT @activestarttimeofday = NULL
    IF (@activeendtimeofday = 0)
      SELECT @activeendtimeofday = NULL
    IF (@freqtype <> 0x2) -- OnDemand tasks simply have no schedule in 7.0
    BEGIN
      EXECUTE @retval = sp_add_jobschedule
        @job_id                 = @job_id,
        @name                   = N'6.x schedule',
        @enabled                = 1,
        @freq_type              = @freqtype,
        @freq_interval          = @freqinterval,
        @freq_subday_type       = @freqsubtype,
        @freq_subday_interval   = @freqsubinterval,
        @freq_relative_interval = @freqrelativeinterval,
        @freq_recurrence_factor = @freqrecurrencefactor,
        @active_start_date      = @activestartdate,
        @active_end_date        = @activeenddate,
        @active_start_time      = @activestarttimeofday,
        @active_end_time        = @activeendtimeofday

      IF (@retval <> 0)
      BEGIN
        ROLLBACK TRANSACTION
        GOTO Quit
      END
    END

    -- And finally, add the job server
    EXECUTE @retval = sp_add_jobserver @job_id = @job_id, @server_name = NULL

    IF (@retval <> 0)
    BEGIN
      ROLLBACK TRANSACTION
      GOTO Quit
    END

    -- Add the replication agent for monitoring
    IF (@category_id = 13) -- Logreader
    BEGIN
      SELECT @distdb = distribution_db from MSdistpublishers where name = @server
      SELECT @proc = @distdb + '.dbo.sp_MSadd_logreader_agent'

      EXECUTE @retval = @proc
        @name = @name,
        @publisher = @server,
        @publisher_db = @databasename,
        @publication = '',
        @local_job = 1,
        @job_existing = 1,
        @job_id = @job_id

      IF (@retval <> 0)
      BEGIN
        ROLLBACK TRANSACTION
        GOTO Quit
      END
    END
    ELSE
    IF (@category_id = 15) -- Snapshot
    BEGIN
      DECLARE @publication sysname

      EXECUTE @retval = master.dbo.sp_MSget_publication_from_taskname
                            @taskname = @name,
                            @publisher = @server,
                            @publisherdb = @databasename,
                            @publication = @publication OUTPUT

      IF (@publication IS NOT NULL)
      BEGIN

        SELECT @distdb = distribution_db from MSdistpublishers where name = @server
        SELECT @proc = @distdb + '.dbo.sp_MSadd_snapshot_agent'

        EXECUTE @retval = @proc
                @name = @name,
                @publisher = @server,
                @publisher_db = @databasename,
                @publication = @publication,
                @local_job = 1,
                @job_existing = 1,
                @snapshot_jobid = @job_id

        IF (@retval <> 0)
        BEGIN
          ROLLBACK TRANSACTION
          GOTO Quit
        END

        SELECT @proc = @distdb + '.dbo.sp_MSadd_publication'
        EXECUTE @retval = @proc
                @publisher = @server,
                @publisher_db = @databasename,
                @publication = @publication,
                @publication_type = 0 -- Transactional
        IF (@retval <> 0)
        BEGIN
          ROLLBACK TRANSACTION
          GOTO Quit
        END
      END
    END

  COMMIT TRANSACTION

  -- If this is an autostart LogReader or Distribution job, add the [new] '-Continuous' paramter to the command
  IF (@freqtype = 0x40) AND ((UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'LOGREADER') OR (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DISTRIBUTION'))
  BEGIN
    UPDATE msdb.dbo.sysjobsteps
    SET command = command + N' -Continuous'
    WHERE (job_id = @job_id)
      AND (step_id = 1)
  END

  -- If this is an autostart job, start it now (for backwards compatibility with 6.x SQLExecutive behaviour)
  IF (@freqtype = 0x40)
    EXECUTE msdb.dbo.sp_start_job @job_id = @job_id, @error_flag = 0, @output_flag = 0

Quit:
  RETURN(@retval) -- 0 means success

END
go


/**************************************************************/
/* SP_DROPTASK                                                */
/**************************************************************/

PRINT ''
PRINT 'Creating [legacy] procedure sp_droptask...'

go

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_droptask')
              AND (type = 'P')))
  DROP PROCEDURE sp_droptask

go

CREATE PROCEDURE sp_droptask
  @name      sysname = NULL, -- Was VARCHAR(100) in 6.x
  @loginname sysname = NULL, -- Was VARCHAR(30) in 6.x
  @id        INT     = NULL
AS
BEGIN
  DECLARE @retval INT
  DECLARE @job_id UNIQUEIDENTIFIER
  DECLARE @category_id int

  SET NOCOUNT ON

  IF ((@name      IS NULL)     AND (@id    IS NULL)     AND (@loginname IS NULL)) OR
     ((@name      IS NOT NULL) AND ((@id   IS NOT NULL) OR  (@loginname IS NOT NULL))) OR
     ((@id        IS NOT NULL) AND ((@name IS NOT NULL) OR  (@loginname IS NOT NULL))) OR
     ((@loginname IS NOT NULL) AND ((@name IS NOT NULL) OR  (@id        IS NOT NULL)))
  BEGIN
    RAISERROR(14245, -1, -1)
    RETURN(1) -- Failure
  END

  -- If the name is supplied, get the job_id directly from sysjobs
  IF (@name IS NOT NULL)
  BEGIN
    -- Check if the name is ambiguous
    IF ((SELECT COUNT(*)
         FROM msdb.dbo.sysjobs_view
         WHERE (name = @name)) > 1)
    BEGIN
      RAISERROR(14292, -1, -1, @name, '@id', '@name')
      RETURN(1) -- Failure
    END

    SELECT @job_id = job_id, @category_id = category_id
    FROM msdb.dbo.sysjobs_view
    WHERE (name = @name)

    SELECT @id = task_id
    FROM msdb.dbo.systaskids
    WHERE (job_id = @job_id)

    IF (@job_id IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@name', @name)
      RETURN(1) -- Failure
    END
  END

  -- If the id is supplied lookup the corresponding job_id from systaskids
  IF (@id IS NOT NULL)
  BEGIN
    SELECT @job_id = job_id
    FROM msdb.dbo.systaskids
    WHERE (task_id = @id)

    -- Check that the job still exists
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.sysjobs_view
                    WHERE (job_id = @job_id)))
    BEGIN
      SELECT @name = CONVERT(NVARCHAR, @id)
      RAISERROR(14262, -1, -1, '@id', @name)
      RETURN(1) -- Failure
    END

    -- Get the name of this job
    SELECT @name = name, @category_id = category_id
    FROM msdb.dbo.sysjobs_view
    WHERE (job_id = @job_id)
  END

  -- Delete the specific job
  IF (@name IS NOT NULL)
  BEGIN
    BEGIN TRANSACTION

    DELETE FROM msdb.dbo.systaskids
    WHERE (job_id = @job_id)
    EXECUTE @retval = sp_delete_job @job_id = @job_id
    IF (@retval <> 0)
   BEGIN
      ROLLBACK TRANSACTION
     GOTO Quit
   END

   -- If a Logreader or Snapshot task, delete corresponding replication agent information
   IF @category_id = 13 or @category_id = 15
   BEGIN
        EXECUTE @retval = sp_MSdrop_6x_replication_agent @job_id, @category_id
     IF (@retval <> 0)
     BEGIN
      ROLLBACK TRANSACTION
      GOTO Quit
     END
   END

    COMMIT TRANSACTION
  END

  -- Delete all jobs belonging to the specified login
  IF (@loginname IS NOT NULL)
  BEGIN
    BEGIN TRANSACTION

    DELETE FROM msdb.dbo.systaskids
    WHERE job_id IN (SELECT job_id
                     FROM msdb.dbo.sysjobs_view
                     WHERE (owner_sid = SUSER_SID(@loginname)))
    EXECUTE @retval = sp_manage_jobs_by_login @action = 'DELETE',
                                              @current_owner_login_name = @loginname
    IF (@retval <> 0)
    BEGIN
      ROLLBACK TRANSACTION
      GOTO Quit
    END     

    COMMIT TRANSACTION
  END

Quit:
  RETURN(@retval) -- 0 means success

END
go



/**************************************************************/
/*                                                            */
/*         E  R  R  O  R    M  E  S  S  A  G  E  S            */
/*                                                            */
/*  These are now created by MESSAGES.SQL.                    */
/*                                                            */
/*  NOTE: 14255 and 14265 are called by dynamic SQL generated */
/*        by SQLServerAgent.                                  */
/**************************************************************/


/**************************************************************/
/*                                                            */
/*                   T  R  I  G  G  E  R  S                   */
/*                                                            */
/**************************************************************/

/**************************************************************/
/* TRIG_TARGETSERVER_INSERT                                   */
/**************************************************************/

PRINT ''
PRINT 'Creating trigger trig_targetserver_insert...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'trig_targetserver_insert')
              AND (type = 'TR')))
  DROP TRIGGER dbo.trig_targetserver_insert
go
CREATE TRIGGER trig_targetserver_insert
ON msdb.dbo.systargetservers
FOR INSERT, DELETE
AS
BEGIN
  SET NOCOUNT ON

  -- Disallow the insert if the server is called 'ALL'
  -- NOTE: We have to do this check here in the trigger since there is no sp_add_targetserver
  --       (target servers insert a row for themselves when they 'enlist' in an MSX)
  IF (EXISTS (SELECT *
              FROM inserted
              WHERE (server_name = N'ALL')))
  BEGIN
    DELETE FROM msdb.dbo.systargetservers
    WHERE (server_name = N'ALL')
    RAISERROR(14271, -1, -1, 'ALL')
    RETURN
  END

  -- Set (or delete) the registy flag (so that SETUP can detect if we're an MSX)
  IF ((SELECT COUNT(*)
       FROM msdb.dbo.systargetservers) = 0)
  BEGIN
    DECLARE @val INT

    EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                           N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                           N'MSXServer',
                                           @val OUTPUT,
                                           N'no_output'
    IF (@val IS NOT NULL)
      EXECUTE master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE',
                                                    N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                                    N'MSXServer'
  END
  ELSE
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'MSXServer',
                                            N'REG_DWORD',
                                            1
END
go

CHECKPOINT
go



/**************************************************************/
/**                                                          **/
/**          A L E R T S  A N D  O P E R A T O R S           **/
/**                                                          **/
/**************************************************************/

/**************************************************************/
/*                                                            */
/*        C  O  R  E     P  R  O  C  E  D  U  R  E  S         */
/*                                                            */
/**************************************************************/


/**************************************************************/
/* SP_ADD_ALERT_INTERNAL                                      */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_alert_internal...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_alert_internal')
              AND (type = 'P')))
  DROP PROCEDURE sp_add_alert_internal
go
CREATE PROCEDURE sp_add_alert_internal
  @name                         sysname,
  @message_id                   INT              = 0,
  @severity                     INT              = 0,
  @enabled                      TINYINT          = 1,
  @delay_between_responses      INT              = 0,
  @notification_message         NVARCHAR(512)    = NULL,
  @include_event_description_in TINYINT          = 5,    -- 0 = None, 1 = Email, 2 = Pager, 4 = NetSend, 7 = All
  @database_name                sysname          = NULL,
  @event_description_keyword    NVARCHAR(100)    = NULL,
  @job_id                       UNIQUEIDENTIFIER = NULL, -- If provided must NOT also provide job_name
  @job_name                     sysname          = NULL, -- If provided must NOT also provide job_id
  @raise_snmp_trap              TINYINT          = 0,
  @performance_condition        NVARCHAR(512)    = NULL, -- New for 7.0
  @category_name                sysname          = NULL, -- New for 7.0
 @wmi_namespace                NVARCHAR(512)     = NULL, -- New for 9.0
  @wmi_query                    NVARCHAR(512)     = NULL, -- New for 9.0
  @verify_alert                    TINYINT             = 1     -- 0 = do not verify alert, 1(or anything else) = verify alert before adding
AS
BEGIN
  DECLARE @event_source           NVARCHAR(100)
  DECLARE @event_category_id      INT
  DECLARE @event_id               INT
  DECLARE @last_occurrence_date   INT
  DECLARE @last_occurrence_time   INT
  DECLARE @last_notification_date INT
  DECLARE @last_notification_time INT
  DECLARE @occurrence_count       INT
  DECLARE @count_reset_date       INT
  DECLARE @count_reset_time       INT
  DECLARE @has_notification       INT
  DECLARE @return_code            INT
  DECLARE @duplicate_name         sysname
  DECLARE @category_id            INT
  DECLARE @alert_id               INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name                      = LTRIM(RTRIM(@name))
  SELECT @notification_message      = LTRIM(RTRIM(@notification_message))
  SELECT @database_name             = LTRIM(RTRIM(@database_name))
  SELECT @event_description_keyword = LTRIM(RTRIM(@event_description_keyword))
  SELECT @job_name                  = LTRIM(RTRIM(@job_name))
  SELECT @performance_condition     = LTRIM(RTRIM(@performance_condition))
  SELECT @category_name             = LTRIM(RTRIM(@category_name))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@notification_message      = N'') SELECT @notification_message = NULL
  IF (@database_name             = N'') SELECT @database_name = NULL
  IF (@event_description_keyword = N'') SELECT @event_description_keyword = NULL
  IF (@job_name                  = N'') SELECT @job_name = NULL
  IF (@performance_condition     = N'') SELECT @performance_condition = NULL
  IF (@category_name             = N'') SELECT @category_name = NULL

  SELECT @message_id = ISNULL(@message_id, 0)
  SELECT @severity = ISNULL(@severity, 0)

  -- Only a sysadmin can do this
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Check if SQLServerAgent is in the process of starting
  EXECUTE @return_code = msdb.dbo.sp_is_sqlagent_starting
  IF (@return_code <> 0)
    RETURN(1) -- Failure

  -- Hard-code the new Alert defaults
  -- event source needs to be instance aware
  DECLARE @instance_name sysname
  SELECT @instance_name = CONVERT (sysname, SERVERPROPERTY ('InstanceName'))
  IF (@instance_name IS NULL OR @instance_name = N'MSSQLSERVER')
    SELECT @event_source  = N'MSSQLSERVER'
  ELSE
    SELECT @event_source  = N'MSSQL$' + @instance_name

  SELECT @event_category_id = NULL
  SELECT @event_id = NULL
  SELECT @last_occurrence_date = 0
  SELECT @last_occurrence_time = 0
  SELECT @last_notification_date = 0
  SELECT @last_notification_time = 0
  SELECT @occurrence_count = 0
  SELECT @count_reset_date = 0
  SELECT @count_reset_time = 0
  SELECT @has_notification = 0
  
  IF (@category_name IS NULL)
  BEGIN
    --Default category_id for alerts
    SELECT @category_id = 98

    SELECT @category_name = name
    FROM msdb.dbo.syscategories
    WHERE (category_id = 98)
  END

  -- Map a job_id of 0 to the real value we use to mean 'no job'
  IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) AND (@job_name IS NULL)
    SELECT @job_name = N''

  -- Verify the Alert if @verify_alert <> 0
  IF (@verify_alert <> 0)
  BEGIN
    IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00))
        SELECT @job_id = NULL
    EXECUTE @return_code = sp_verify_alert @name,
                                            @message_id,
                                            @severity,
                                            @enabled,
                                            @delay_between_responses,
                                            @notification_message,
                                            @include_event_description_in,
                                            @database_name,
                                            @event_description_keyword,
                                            @job_id OUTPUT,
                                            @job_name OUTPUT,
                                            @occurrence_count,
                                            @raise_snmp_trap,
                                            @performance_condition,
                                            @category_name,
                                            @category_id OUTPUT,
                                            @count_reset_date,
                                            @count_reset_time,
                                            @wmi_namespace,
                                            @wmi_query,
                                            @event_id OUTPUT
    IF (@return_code <> 0)
    BEGIN
        RETURN(1) -- Failure
    END
  END

  -- For WMI alerts replace 
  -- database_name with wmi_namespace and 
  -- performance_conditon with wmi_query
  -- so we can store them in those columns in sysalerts table
  IF (@event_id = 8)
  BEGIN
    SELECT @database_name = @wmi_namespace
    SELECT @performance_condition = @wmi_query
  END
  
  -- Check if this Alert already exists
  SELECT @duplicate_name = FORMATMESSAGE(14205)
  SELECT @duplicate_name = name
  FROM msdb.dbo.sysalerts
  WHERE ((event_id = 8) AND 
       (ISNULL(performance_condition, N'') = ISNULL(@performance_condition, N'')) AND
       (ISNULL(database_name, N'') = ISNULL(@database_name, N''))) OR
      ((ISNULL(event_id,1) <> 8) AND 
       (ISNULL(performance_condition, N'apples') = ISNULL(@performance_condition, N'oranges'))) OR 
      ((performance_condition IS NULL) AND
         (message_id = @message_id) AND
         (severity = @severity) AND
         (ISNULL(database_name, N'') = ISNULL(@database_name, N'')) AND
         (ISNULL(event_description_keyword, N'') = ISNULL(@event_description_keyword, N'')))
  IF (@duplicate_name <> FORMATMESSAGE(14205))
  BEGIN
    RAISERROR(14501, 16, 1, @duplicate_name)
    RETURN(1) -- Failure
  END
  
  -- Finally, do the actual INSERT
  INSERT INTO msdb.dbo.sysalerts
         (name,
          event_source,
          event_category_id,
          event_id,
          message_id,
          severity,
          enabled,
          delay_between_responses,
          last_occurrence_date,
          last_occurrence_time,
          last_response_date,
          last_response_time,
          notification_message,
          include_event_description,
          database_name,
          event_description_keyword,
          occurrence_count,
          count_reset_date,
          count_reset_time,
          job_id,
          has_notification,
          flags,
          performance_condition,
          category_id)
  VALUES (@name,
          @event_source,
          @event_category_id,
          @event_id,
          @message_id,
          @severity,
          @enabled,
          @delay_between_responses,
          @last_occurrence_date,
          @last_occurrence_time,
          @last_notification_date,
          @last_notification_time,
          @notification_message,
          @include_event_description_in,
          @database_name,
          @event_description_keyword,
          @occurrence_count,
          @count_reset_date,
          @count_reset_time,
          ISNULL(@job_id, CONVERT(UNIQUEIDENTIFIER, 0x00)),
          @has_notification,
          @raise_snmp_trap,
          @performance_condition,
          @category_id)

  -- Notify SQLServerAgent of the change
  SELECT @alert_id = id
  FROM msdb.dbo.sysalerts
  WHERE (name = @name)
  EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'A',
                                      @alert_id    = @alert_id,
                                      @action_type = N'I'
  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_ADD_ALERT                                               */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_alert...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_alert')
              AND (type = 'P')))
  DROP PROCEDURE sp_add_alert
go
CREATE PROCEDURE sp_add_alert
  @name                         sysname,
  @message_id                   INT              = 0,
  @severity                     INT              = 0,
  @enabled                      TINYINT          = 1,
  @delay_between_responses      INT              = 0,
  @notification_message         NVARCHAR(512)    = NULL,
  @include_event_description_in TINYINT          = 5,    -- 0 = None, 1 = Email, 2 = Pager, 4 = NetSend, 7 = All
  @database_name                sysname          = NULL,
  @event_description_keyword    NVARCHAR(100)    = NULL,
  @job_id                       UNIQUEIDENTIFIER = NULL, -- If provided must NOT also provide job_name
  @job_name                     sysname          = NULL, -- If provided must NOT also provide job_id
  @raise_snmp_trap              TINYINT          = 0,
  @performance_condition        NVARCHAR(512)    = NULL, -- New for 7.0
  @category_name                sysname          = NULL, -- New for 7.0
  @wmi_namespace                sysname             = NULL, -- New for 9.0
  @wmi_query                    NVARCHAR(512)     = NULL  -- New for 9.0
AS
BEGIN
  DECLARE @verify_alert         INT
  
  --Always verify alerts before adding
  SELECT @verify_alert = 1

  EXECUTE msdb.dbo.sp_add_alert_internal @name,
                                         @message_id,
                                         @severity,
                                         @enabled,
                                         @delay_between_responses,
                                         @notification_message,
                                         @include_event_description_in,
                                         @database_name,
                                         @event_description_keyword,
                                         @job_id,
                                         @job_name,
                                         @raise_snmp_trap,
                                         @performance_condition,
                                         @category_name,
                                         @wmi_namespace,
                                         @wmi_query,
                                         @verify_alert
END
GO


/**************************************************************/
/* SP_DELETE_ALERT                                            */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_alert...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_alert')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_alert
go
CREATE PROCEDURE sp_delete_alert
  @name sysname
AS
BEGIN
  DECLARE @alert_id    INT
  DECLARE @return_code INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name = LTRIM(RTRIM(@name))

  -- Only a sysadmin can do this
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Check if SQLServerAgent is in the process of starting
  EXECUTE @return_code = msdb.dbo.sp_is_sqlagent_starting
  IF (@return_code <> 0)
    RETURN(1) -- Failure

  -- Check if this Alert exists
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysalerts
                  WHERE (name = @name)))
  BEGIN
    RAISERROR(14262, 16, 1, '@name', @name)
    RETURN(1) -- Failure
  END

  -- Convert the Name to it's ID
  SELECT @alert_id = id
  FROM msdb.dbo.sysalerts
  WHERE (name = @name)

  BEGIN TRANSACTION

    -- Delete sysnotifications entries
    DELETE FROM msdb.dbo.sysnotifications
    WHERE (alert_id = @alert_id)

    -- Finally, do the actual DELETE
    DELETE FROM msdb.dbo.sysalerts
    WHERE (id = @alert_id)

  COMMIT TRANSACTION

  -- Notify SQLServerAgent of the change
  EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'A',
                                      @alert_id    = @alert_id,
                                      @action_type = N'D'
  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_HELP_ALERT                                              */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_alert...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_alert')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_alert
go
CREATE PROCEDURE sp_help_alert
  @alert_name    sysname = NULL,
  @order_by      sysname = N'name',
  @alert_id      INT     = NULL,
  @category_name sysname = NULL,
  @legacy_format BIT  = 0 
AS
BEGIN
  DECLARE @alert_id_as_char NVARCHAR(10)
  DECLARE @escaped_alert_name NVARCHAR(256) -- double sysname
  DECLARE @escaped_category_name NVARCHAR(256) -- double sysname
  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @alert_name    = LTRIM(RTRIM(@alert_name))
  SELECT @order_by      = LTRIM(RTRIM(@order_by))
  SELECT @category_name = LTRIM(RTRIM(@category_name))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@category_name = N'') SELECT @category_name = NULL
  IF (@alert_name = N'')    SELECT @alert_name = NULL

  -- Check alert name
  IF (@alert_name IS NOT NULL)
  BEGIN
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.sysalerts
                    WHERE (name = @alert_name)))
    BEGIN
      RAISERROR(14262, -1, -1, '@alert_name', @alert_name)
      RETURN(1) -- Failure
    END
  END

  -- Check alert id
  IF (@alert_id IS NOT NULL)
  BEGIN
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.sysalerts
                    WHERE (id = @alert_id)))
    BEGIN
      SELECT @alert_id_as_char = CONVERT(VARCHAR, @alert_id)
      RAISERROR(14262, -1, -1, '@alert_id', @alert_id_as_char)
      RETURN(1) -- Failure
    END
  END

  IF (@alert_id IS NOT NULL)
    SELECT @alert_id_as_char = CONVERT(VARCHAR, @alert_id)
  ELSE
    SELECT @alert_id_as_char = N'NULL'

  -- Double up any single quotes in @alert_name
  IF (@alert_name IS NOT NULL)
    SELECT @escaped_alert_name = REPLACE(@alert_name, N'''', N'''''')

  -- Double up any single quotes in @category_name
  IF (@category_name IS NOT NULL)
    SELECT @escaped_category_name = REPLACE(@category_name, N'''', N'''''')

  IF (@legacy_format <> 0)
  BEGIN
    
     -- @order_by parameter validation. 
     IF  ( (@order_by IS NOT NULL) AND 
           (EXISTS(SELECT so.object_id FROM msdb.sys.objects so 
                      JOIN msdb.sys.columns sc ON (so.object_id = sc.object_id) 
                   WHERE so.type='U' AND so.name='sysalerts' 
                                     AND LOWER(sc.name collate SQL_Latin1_General_CP1_CS_AS)=LOWER(@order_by collate SQL_Latin1_General_CP1_CS_AS)
                  )
          ) )
     BEGIN
       SELECT @order_by = N'sa.' + @order_by
     END
     ELSE 
     BEGIN
        IF (LOWER(@order_by collate SQL_Latin1_General_CP1_CS_AS) NOT IN ( N'job_name', N'category_name', N'type' ) )
           AND --special "order by" clause used only by sqlagent. if you change it you need to change agent too
           (@order_by <> N'event_id DESC, severity ASC, message_id ASC, database_name DESC') 
           AND
           (@order_by <> N'severity ASC, message_id ASC, database_name DESC')
        BEGIN
          RAISERROR(18750, -1, -1, 'sp_help_alert', '@order_by')
          RETURN(1) -- Failure
        END
     END
    
    -- Old query version (for SQL Server 2000 and older servers)
    -- database_name and performance_conditions are reported
    -- directly from sysalerts columns
    EXECUTE (N'SELECT sa.id,
               sa.name,
                    sa.event_source,
                    sa.event_category_id,
                    sa.event_id,
                    sa.message_id,
                    sa.severity,
                    sa.enabled,
                    sa.delay_between_responses,
                    sa.last_occurrence_date,
                    sa.last_occurrence_time,
                    sa.last_response_date,
                    sa.last_response_time,
                    sa.notification_message,
                    sa.include_event_description,
                    sa.database_name,
                    sa.event_description_keyword,
                    sa.occurrence_count,
                    sa.count_reset_date,
                    sa.count_reset_time,
                    sjv.job_id,
                    job_name = sjv.name,
                    sa.has_notification,
                    sa.flags,
                    sa.performance_condition,
                    category_name = sc.name,
                    type = CASE ISNULL(sa.performance_condition, ''!'')
                  WHEN ''!'' THEN 1            -- SQL Server event alert
                  ELSE CASE sa.event_id
                     WHEN 8 THEN 4          -- WMI event alert
                     ELSE 2                    -- SQL Server performance condition alert
                  END
               END
             FROM msdb.dbo.sysalerts                     sa
                  LEFT OUTER JOIN msdb.dbo.sysjobs_view  sjv ON (sa.job_id = sjv.job_id)
                  LEFT OUTER JOIN msdb.dbo.syscategories sc  ON (sa.category_id = sc.category_id)
             WHERE ((N''' + @escaped_alert_name + N''' = N'''') OR (sa.name = N''' + @escaped_alert_name + N'''))
               AND ((' + @alert_id_as_char + N' IS NULL) OR (sa.id = ' + @alert_id_as_char + N'))
               AND ((N''' + @escaped_category_name + N''' = N'''') OR (sc.name = N''' + @escaped_category_name + N'''))
             ORDER BY ' + @order_by)
  END
  ELSE
  BEGIN

     -- @order_by parameter validation. 
     IF  ( (@order_by IS NOT NULL) AND 
           (EXISTS(SELECT so.object_id FROM msdb.sys.objects so 
                      JOIN msdb.sys.columns sc ON (so.object_id = sc.object_id) 
                   WHERE so.type='U' AND so.name='sysalerts' 
                                     AND LOWER(sc.name collate SQL_Latin1_General_CP1_CS_AS)=LOWER(@order_by collate SQL_Latin1_General_CP1_CS_AS)
                  )
          ) )
     BEGIN
       SELECT @order_by = N'sa.' + @order_by
     END
     ELSE 
     BEGIN
        IF (LOWER(@order_by collate SQL_Latin1_General_CP1_CS_AS) NOT IN (N'database_name', N'job_name', N'performance_condition', N'category_name', N'wmi_namespace', N'wmi_query', N'type' ) )
           AND --special "order by" clause used only by sqlagent. if you change it you need to change agent too
           (@order_by <> N'event_id DESC, severity ASC, message_id ASC, database_name DESC') 
           AND
           (@order_by <> N'severity ASC, message_id ASC, database_name DESC')
        BEGIN
           RAISERROR(18750, -1, -1, 'sp_help_alert', '@order_by')
           RETURN(1) -- Failure
        END
     END

    -- New query version. If alert is a WMI alert 
    -- then database_name is reported as wmi_namespace and
    -- performance_condition is reported as wmi_query.
    -- For other alerts those two new columns are NULL
    EXECUTE (N'SELECT sa.id,
                    sa.name,
                    sa.event_source,
                    sa.event_category_id,
                    sa.event_id,
                    sa.message_id,
                    sa.severity,
                    sa.enabled,
                    sa.delay_between_responses,
                    sa.last_occurrence_date,
                    sa.last_occurrence_time,
                    sa.last_response_date,
                    sa.last_response_time,
                    sa.notification_message,
                    sa.include_event_description,
               database_name = CASE ISNULL(sa.event_id, 1)
                  WHEN 8 THEN NULL
                  ELSE sa.database_name 
               END,
                    sa.event_description_keyword,
                    sa.occurrence_count,
                    sa.count_reset_date,
                    sa.count_reset_time,
                    sjv.job_id,
                    job_name = sjv.name,
                    sa.has_notification,
                    sa.flags,
               performance_condition = CASE ISNULL(sa.event_id, 1)
                  WHEN 8 THEN NULL
                  ELSE sa.performance_condition 
               END,
                    category_name = sc.name,
                    wmi_namespace = CASE ISNULL(sa.event_id, 1)
                  WHEN 8 THEN sa.database_name
                  ELSE NULL
               END,
               wmi_query = CASE ISNULL(sa.event_id, 1)
                  WHEN 8 THEN sa.performance_condition
                  ELSE NULL
               END,
                    type = CASE ISNULL(sa.performance_condition, ''!'')
                  WHEN ''!'' THEN 1            -- SQL Server event alert
                  ELSE CASE sa.event_id
                     WHEN 8 THEN 4          -- WMI event alert
                     ELSE 2                    -- SQL Server performance condition alert
                  END
               END
             FROM msdb.dbo.sysalerts                     sa
                  LEFT OUTER JOIN msdb.dbo.sysjobs_view  sjv ON (sa.job_id = sjv.job_id)
                  LEFT OUTER JOIN msdb.dbo.syscategories sc  ON (sa.category_id = sc.category_id)
             WHERE ((N''' + @escaped_alert_name + N''' = N'''') OR (sa.name = N''' + @escaped_alert_name + N'''))
               AND ((' + @alert_id_as_char + N' IS NULL) OR (sa.id = ' + @alert_id_as_char + N'))
               AND ((N''' + @escaped_category_name + N''' = N'''') OR (sc.name = N''' + @escaped_category_name + N'''))
             ORDER BY ' + @order_by)
  END

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_VERIFY_OPERATOR                                         */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_operator...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_operator')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_operator
go
CREATE PROCEDURE sp_verify_operator
  @name                      sysname,
  @enabled                   TINYINT,
  @pager_days                TINYINT,
  @weekday_pager_start_time  INT,
  @weekday_pager_end_time    INT,
  @saturday_pager_start_time INT,
  @saturday_pager_end_time   INT,
  @sunday_pager_start_time   INT,
  @sunday_pager_end_time     INT,
  @category_name             sysname,
  @category_id               INT OUTPUT
AS
BEGIN
  DECLARE @return_code     TINYINT
  DECLARE @res_valid_range NVARCHAR(100)

  SET NOCOUNT ON

  SELECT @res_valid_range = FORMATMESSAGE(14209)

  -- Remove any leading/trailing spaces from parameters
  SELECT @name          = LTRIM(RTRIM(@name))
  SELECT @category_name = LTRIM(RTRIM(@category_name))

  -- The name must be unique
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysoperators
              WHERE (name = @name)))
  BEGIN
    RAISERROR(14261, 16, 1, '@name', @name)
    RETURN(1) -- Failure
  END

  -- Enabled must be 0 or 1
  IF (@enabled NOT IN (0, 1))
  BEGIN
    RAISERROR(14266, 16, 1, '@enabled', '0, 1')
    RETURN(1) -- Failure
  END

  -- Check PagerDays
  IF (@pager_days < 0) OR (@pager_days > 127)
  BEGIN
    RAISERROR(14266, 16, 1, '@pager_days', @res_valid_range)
    RETURN(1) -- Failure
  END

  -- Check Start/End Times
  EXECUTE @return_code = sp_verify_job_time @weekday_pager_start_time, '@weekday_pager_start_time'
  IF (@return_code <> 0)
    RETURN(1)

  EXECUTE @return_code = sp_verify_job_time @weekday_pager_end_time, '@weekday_pager_end_time'
  IF (@return_code <> 0)
    RETURN(1)

  EXECUTE @return_code = sp_verify_job_time @saturday_pager_start_time, '@saturday_pager_start_time'
  IF (@return_code <> 0)
    RETURN(1)

  EXECUTE @return_code = sp_verify_job_time @saturday_pager_end_time, '@saturday_pager_end_time'
  IF (@return_code <> 0)
    RETURN(1)

  EXECUTE @return_code = sp_verify_job_time @sunday_pager_start_time, '@sunday_pager_start_time'
  IF (@return_code <> 0)
    RETURN(1)

  EXECUTE @return_code = sp_verify_job_time @sunday_pager_end_time, '@sunday_pager_end_time'
  IF (@return_code <> 0)
    RETURN(1)

  -- Check category name
  IF (@category_name = N'[DEFAULT]')
    SELECT @category_id = 99
  ELSE
  BEGIN
    SELECT @category_id = category_id
    FROM msdb.dbo.syscategories
    WHERE (category_class = 3) -- Operators
      AND (category_type = 3) -- None
      AND (name = @category_name)
  END
  IF (@category_id IS NULL)
  BEGIN
    RAISERROR(14262, -1, -1, '@category_name', @category_name)
    RETURN(1) -- Failure
  END

  RETURN(0)
END
go

/**************************************************************/
/* SP_ADD_OPERATOR                                            */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_operator...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_operator')
              AND (type = 'P')))
  DROP PROCEDURE sp_add_operator
go
CREATE PROCEDURE sp_add_operator
  @name                      sysname,
  @enabled                   TINYINT       = 1,
  @email_address             NVARCHAR(100) = NULL,
  @pager_address             NVARCHAR(100) = NULL,
  @weekday_pager_start_time  INT           = 090000, -- HHMMSS using 24 hour clock
  @weekday_pager_end_time    INT           = 180000, -- As above
  @saturday_pager_start_time INT           = 090000, -- As above
  @saturday_pager_end_time   INT           = 180000, -- As above
  @sunday_pager_start_time   INT           = 090000, -- As above
  @sunday_pager_end_time     INT           = 180000, -- As above
  @pager_days                TINYINT       = 0,      -- 1 = Sunday .. 64 = Saturday
  @netsend_address           NVARCHAR(100) = NULL,   -- New for 7.0
  @category_name             sysname       = NULL    -- New for 7.0
AS
BEGIN
  DECLARE @return_code TINYINT
  DECLARE @category_id INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name            = LTRIM(RTRIM(@name))
  SELECT @email_address   = LTRIM(RTRIM(@email_address))
  SELECT @pager_address   = LTRIM(RTRIM(@pager_address))
  SELECT @netsend_address = LTRIM(RTRIM(@netsend_address))
  SELECT @category_name   = LTRIM(RTRIM(@category_name))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@email_address   = N'') SELECT @email_address   = NULL
  IF (@pager_address   = N'') SELECT @pager_address   = NULL
  IF (@netsend_address = N'') SELECT @netsend_address = NULL
  IF (@category_name   = N'') SELECT @category_name   = NULL

  -- Only a sysadmin can do this
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  IF (@category_name IS NULL)
  BEGIN
    SELECT @category_name = name
    FROM msdb.dbo.syscategories
    WHERE (category_id = 99)
  END

  -- Verify the operator
  EXECUTE @return_code = sp_verify_operator @name,
                                            @enabled,
                                            @pager_days,
                                            @weekday_pager_start_time,
                                            @weekday_pager_end_time,
                                            @saturday_pager_start_time,
                                            @saturday_pager_end_time,
                                            @sunday_pager_start_time,
                                            @sunday_pager_end_time,
                                            @category_name,
                                            @category_id OUTPUT
  IF (@return_code <> 0)
    RETURN(1) -- Failure

  -- Finally, do the INSERT
  INSERT INTO msdb.dbo.sysoperators
         (name,
          enabled,
          email_address,
          last_email_date,
          last_email_time,
          pager_address,
          last_pager_date,
          last_pager_time,
          weekday_pager_start_time,
          weekday_pager_end_time,
          saturday_pager_start_time,
          saturday_pager_end_time,
          sunday_pager_start_time,
          sunday_pager_end_time,
          pager_days,
          netsend_address,
          last_netsend_date,
          last_netsend_time,
          category_id)
  VALUES (@name,
          @enabled,
          @email_address,
          0,
          0,
          @pager_address,
          0,
          0,
          @weekday_pager_start_time,
          @weekday_pager_end_time,
          @saturday_pager_start_time,
          @saturday_pager_end_time,
          @sunday_pager_start_time,
          @sunday_pager_end_time,
          @pager_days,
          @netsend_address,
          0,
          0,
          @category_id)

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_UPDATE_OPERATOR                                         */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_update_operator...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_update_operator')
              AND (type = 'P')))
  DROP PROCEDURE sp_update_operator
go
CREATE PROCEDURE sp_update_operator
  @name                      sysname,
  @new_name                  sysname       = NULL,
  @enabled                   TINYINT       = NULL,
  @email_address             NVARCHAR(100) = NULL,
  @pager_address             NVARCHAR(100) = NULL,
  @weekday_pager_start_time  INT           = NULL, -- HHMMSS using 24 hour clock
  @weekday_pager_end_time    INT           = NULL, -- As above
  @saturday_pager_start_time INT           = NULL, -- As above
  @saturday_pager_end_time   INT           = NULL, -- As above
  @sunday_pager_start_time   INT           = NULL, -- As above
  @sunday_pager_end_time     INT           = NULL, -- As above
  @pager_days                TINYINT       = NULL,
  @netsend_address           NVARCHAR(100) = NULL, -- New for 7.0
  @category_name             sysname       = NULL  -- New for 7.0
AS
BEGIN
  DECLARE @x_enabled                   TINYINT
  DECLARE @x_email_address             NVARCHAR(100)
  DECLARE @x_pager_address             NVARCHAR(100)
  DECLARE @x_weekday_pager_start_time  INT
  DECLARE @x_weekday_pager_end_time    INT
  DECLARE @x_saturday_pager_start_time INT
  DECLARE @x_saturday_pager_end_time   INT
  DECLARE @x_sunday_pager_start_time   INT
  DECLARE @x_sunday_pager_end_time     INT
  DECLARE @x_pager_days                TINYINT
  DECLARE @x_netsend_address           NVARCHAR(100)
  DECLARE @x_category_id               INT

  DECLARE @return_code                 INT
  DECLARE @notification_method         INT
  DECLARE @alert_fail_safe_operator    sysname
  DECLARE @current_msx_server          sysname
  DECLARE @category_id                 INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name            = LTRIM(RTRIM(@name))
  SELECT @new_name        = LTRIM(RTRIM(@new_name))
  SELECT @email_address   = LTRIM(RTRIM(@email_address))
  SELECT @pager_address   = LTRIM(RTRIM(@pager_address))
  SELECT @netsend_address = LTRIM(RTRIM(@netsend_address))
  SELECT @category_name   = LTRIM(RTRIM(@category_name))

  -- Only a sysadmin can do this
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Check if this Operator exists
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysoperators
                  WHERE (name = @name)))
  BEGIN
    RAISERROR(14262, 16, 1, '@name', @name)
    RETURN(1) -- Failure
  END

  -- Check if this operator is 'MSXOperator'
  IF (@name = N'MSXOperator')
  BEGIN
    -- Disallow the update operation if we're at a TSX for all callers other than xp_msx_enlist
    EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                           N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                           N'MSXServerName',
                                           @current_msx_server OUTPUT,
                                           N'no_output'
    IF ((@current_msx_server IS NOT NULL) AND (PROGRAM_NAME() <> N'xp_msx_enlist'))
    BEGIN
      RAISERROR(14223, 16, 1, 'MSXOperator', 'TSX')
      RETURN(1) -- Failure
    END
  END

  -- Get existing (@x_) operator property values
  SELECT @x_enabled                   = enabled,
         @x_email_address             = email_address,
         @x_pager_address             = pager_address,
         @x_weekday_pager_start_time  = weekday_pager_start_time,
         @x_weekday_pager_end_time    = weekday_pager_end_time,
         @x_saturday_pager_start_time = saturday_pager_start_time,
         @x_saturday_pager_end_time   = saturday_pager_end_time,
         @x_sunday_pager_start_time   = sunday_pager_start_time,
         @x_sunday_pager_end_time     = sunday_pager_end_time,
         @x_pager_days                = pager_days,
         @x_netsend_address           = netsend_address,
         @x_category_id               = category_id
  FROM msdb.dbo.sysoperators
  WHERE (name = @name)

  -- Fill out the values for all non-supplied parameters from the existsing values
  IF (@enabled                   IS NULL) SELECT @enabled                   = @x_enabled
  IF (@email_address             IS NULL) SELECT @email_address             = @x_email_address
  IF (@pager_address             IS NULL) SELECT @pager_address             = @x_pager_address
  IF (@weekday_pager_start_time  IS NULL) SELECT @weekday_pager_start_time  = @x_weekday_pager_start_time
  IF (@weekday_pager_end_time    IS NULL) SELECT @weekday_pager_end_time    = @x_weekday_pager_end_time
  IF (@saturday_pager_start_time IS NULL) SELECT @saturday_pager_start_time = @x_saturday_pager_start_time
  IF (@saturday_pager_end_time   IS NULL) SELECT @saturday_pager_end_time   = @x_saturday_pager_end_time
  IF (@sunday_pager_start_time   IS NULL) SELECT @sunday_pager_start_time   = @x_sunday_pager_start_time
  IF (@sunday_pager_end_time     IS NULL) SELECT @sunday_pager_end_time     = @x_sunday_pager_end_time
  IF (@pager_days                IS NULL) SELECT @pager_days                = @x_pager_days
  IF (@netsend_address           IS NULL) SELECT @netsend_address           = @x_netsend_address
  IF (@category_name             IS NULL) SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = @x_category_id)

  IF (@category_name IS NULL)
  BEGIN
    SELECT @category_name = name
    FROM msdb.dbo.syscategories
    WHERE (category_id = 99)
  END

  -- Turn [nullable] empty string parameters into NULLs
  IF (@email_address   = N'') SELECT @email_address   = NULL
  IF (@pager_address   = N'') SELECT @pager_address   = NULL
  IF (@netsend_address = N'') SELECT @netsend_address = NULL
  IF (@category_name   = N'') SELECT @category_name   = NULL

  -- Verify the operator
  EXECUTE @return_code = sp_verify_operator @new_name,
                                            @enabled,
                                            @pager_days,
                                            @weekday_pager_start_time,
                                            @weekday_pager_end_time,
                                            @saturday_pager_start_time,
                                            @saturday_pager_end_time,
                                            @sunday_pager_start_time,
                                            @sunday_pager_end_time,
                                            @category_name,
                                            @category_id OUTPUT
  IF (@return_code <> 0)
    RETURN(1) -- Failure

  -- If no new name is supplied, use the old one
  -- NOTE: We must do this AFTER calling sp_verify_operator.
  IF (@new_name IS NULL)
    SELECT @new_name = @name
  ELSE
  BEGIN
    -- You can't rename the MSXOperator
    IF (@name = N'MSXOperator')
    BEGIN
      RAISERROR(14222, 16, 1, 'MSXOperator')
      RETURN(1) -- Failure
    END
  END

  -- Do the UPDATE
  UPDATE msdb.dbo.sysoperators
  SET name                      = @new_name,
      enabled                   = @enabled,
      email_address             = @email_address,
      pager_address             = @pager_address,
      weekday_pager_start_time  = @weekday_pager_start_time,
      weekday_pager_end_time    = @weekday_pager_end_time,
      saturday_pager_start_time = @saturday_pager_start_time,
      saturday_pager_end_time   = @saturday_pager_end_time,
      sunday_pager_start_time   = @sunday_pager_start_time,
      sunday_pager_end_time     = @sunday_pager_end_time,
      pager_days                = @pager_days,
      netsend_address           = @netsend_address,
      category_id               = @category_id
  WHERE (name = @name)

  -- Check if the operator is 'MSXOperator', in which case we need to re-enlist all the targets
  -- so that they will download the new MSXOperator details
  IF ((@name = N'MSXOperator') AND ((SELECT COUNT(*) FROM msdb.dbo.systargetservers) > 0))
    EXECUTE msdb.dbo.sp_post_msx_operation 'RE-ENLIST', 'SERVER', 0x00

  -- Check if this operator is the FailSafe Operator
  EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                         N'AlertFailSafeOperator',
                                         @alert_fail_safe_operator OUTPUT,
                                         N'no_output'

  -- If it is, we update the 4 'AlertFailSafe...' registry entries and AlertNotificationMethod
  IF (LTRIM(RTRIM(@alert_fail_safe_operator)) = @name)
  BEGIN
    -- Update AlertFailSafeX values
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'AlertFailSafeOperator',
                                            N'REG_SZ',
                                            @new_name
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'AlertFailSafeEmailAddress',
                                            N'REG_SZ',
                                            @email_address
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'AlertFailSafePagerAddress',
                                            N'REG_SZ',
                                            @pager_address
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'AlertFailSafeNetSendAddress',
                                            N'REG_SZ',
                                            @netsend_address

    -- Update AlertNotificationMethod values
    EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                           N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                           N'AlertNotificationMethod',
                                           @notification_method OUTPUT,
                                           N'no_output'
    IF (LTRIM(RTRIM(@email_address)) IS NULL)
      SELECT @notification_method = @notification_method & ~1
    IF (LTRIM(RTRIM(@pager_address)) IS NULL)
      SELECT @notification_method = @notification_method & ~2
    IF (LTRIM(RTRIM(@netsend_address)) IS NULL)
      SELECT @notification_method = @notification_method & ~4
    EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                            N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                            N'AlertNotificationMethod',
                                            N'REG_DWORD',
                                            @notification_method

    -- And finally, let SQLServerAgent know of the changes
    EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'G'
  END

  RETURN(0) -- Success
END
go


/**************************************************************/
/* SP_HELP_OPERATOR                                           */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_operator...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_operator')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_operator
go
CREATE PROCEDURE sp_help_operator
  @operator_name sysname = NULL,
  @operator_id   INT     = NULL
AS
BEGIN
  DECLARE @operator_id_as_char VARCHAR(10)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @operator_name = LTRIM(RTRIM(@operator_name))
  IF (@operator_name = '') SELECT @operator_name = NULL

  -- Check operator name
  IF (@operator_name IS NOT NULL)
  BEGIN
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.sysoperators
                    WHERE (name = @operator_name)))
    BEGIN
      RAISERROR(14262, -1, -1, '@operator_name', @operator_name)
      RETURN(1) -- Failure
    END
  END

  -- Check operator id
  IF (@operator_id IS NOT NULL)
  BEGIN
    IF (NOT EXISTS (SELECT *
                    FROM msdb.dbo.sysoperators
                    WHERE (id = @operator_id)))
    BEGIN
      SELECT @operator_id_as_char = CONVERT(VARCHAR, @operator_id)
      RAISERROR(14262, -1, -1, '@operator_id', @operator_id_as_char)
      RETURN(1) -- Failure
    END
  END

  SELECT so.id,
         so.name,
         so.enabled,
         so.email_address,
         so.last_email_date,
         so.last_email_time,
         so.pager_address,
         so.last_pager_date,
         so.last_pager_time,
         so.weekday_pager_start_time,
         so.weekday_pager_end_time,
         so.saturday_pager_start_time,
         so.saturday_pager_end_time,
         so.sunday_pager_start_time,
         so.sunday_pager_end_time,
         so.pager_days,
         so.netsend_address,
         so.last_netsend_date,
         so.last_netsend_time,
         category_name = sc.name
  FROM msdb.dbo.sysoperators                  so
       LEFT OUTER JOIN msdb.dbo.syscategories sc ON (so.category_id = sc.category_id)
  WHERE ((@operator_name IS NULL) OR (so.name = @operator_name))
    AND ((@operator_id IS NULL) OR (so.id = @operator_id))
  ORDER BY so.name

  RETURN(@@error) -- 0 means success
END
go

/**************************************************************/
/* SP_HELP_OPERATOR_JOBS                                      */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_operator_jobs...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_help_operator_jobs')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_operator_jobs
go
CREATE PROCEDURE sp_help_operator_jobs
  @operator_name sysname = NULL
AS
BEGIN
  DECLARE @operator_id INT

  SET NOCOUNT ON

  -- Check operator name
  SELECT @operator_id = id
  FROM msdb.dbo.sysoperators
  WHERE (name = @operator_name)
  IF (@operator_id IS NULL)
  BEGIN
    RAISERROR(14262, -1, -1, '@operator_name', @operator_name)
    RETURN(1) -- Failure
  END

  -- Get the job info
  SELECT job_id, name, notify_level_email, notify_level_netsend, notify_level_page
  FROM msdb.dbo.sysjobs_view
  WHERE ((notify_email_operator_id = @operator_id)   AND (notify_level_email <> 0))
     OR ((notify_netsend_operator_id = @operator_id) AND (notify_level_netsend <> 0))
     OR ((notify_page_operator_id = @operator_id)    AND (notify_level_page <> 0))

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_VERIFY_OPERATOR_IDENTIFIERS                                */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_operator_identifiers...'
IF (NOT OBJECT_ID(N'dbo.sp_verify_operator_identifiers', 'P') IS NULL)
  DROP PROCEDURE dbo.sp_verify_operator_identifiers
go

CREATE PROCEDURE sp_verify_operator_identifiers
   @name_of_name_parameter [varchar](60),
   @name_of_id_parameter [varchar](60),
   @operator_name [sysname] OUTPUT,
   @operator_id [INT] OUTPUT
AS
BEGIN
  DECLARE @retval              INT
  DECLARE @operator_id_as_char NVARCHAR(36)

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter))
  SELECT @name_of_id_parameter   = LTRIM(RTRIM(@name_of_id_parameter))
  SELECT @operator_name             = LTRIM(RTRIM(@operator_name))

  IF (@operator_name = N'') SELECT @operator_name = NULL

  IF ((@operator_name IS NULL)     AND (@operator_id IS NULL)) OR
     ((@operator_name IS NOT NULL) AND (@operator_id IS NOT NULL))
  BEGIN
    RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter)
    RETURN(1) -- Failure
  END

  -- Check job id
  IF (@operator_id IS NOT NULL)
  BEGIN
    SELECT @operator_name = name
    FROM msdb.dbo.sysoperators
    WHERE (id = @operator_id)
    IF (@operator_name IS NULL)
    BEGIN
     SELECT @operator_id_as_char = CONVERT(nvarchar(36), @operator_id)
      RAISERROR(14262, -1, -1, '@operator_id', @operator_id_as_char)
      RETURN(1) -- Failure
    END
  END
  ELSE
  -- Check proxy name
  IF (@operator_name IS NOT NULL)
  BEGIN
    -- The name is not ambiguous, so get the corresponding operator_id (if the job exists)
    SELECT @operator_id = id
    FROM msdb.dbo.sysoperators
    WHERE (name = @operator_name)
    IF (@operator_id IS NULL)
    BEGIN
      RAISERROR(14262, -1, -1, '@operator_name', @operator_name)
      RETURN(1) -- Failure
    END
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_NOTIFY_OPERATOR                                         */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_notify_operator...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_notify_operator')
              AND (type = 'P')))
  DROP PROCEDURE sp_notify_operator
go
CREATE PROCEDURE sp_notify_operator
  @profile_name           sysname       = NULL,          
  --name of Database Mail profile to be used for sending email, cannot be null
  @id                     INT            = NULL,  
  @name                   sysname        = NULL, 
  --mutual exclusive, one and only one should be non null. Specify the operator whom mail adress will be used to send this email
  @subject                NVARCHAR(256)  = NULL,
  @body                   NVARCHAR(MAX)  = NULL, 
  -- This is the body of the email message
  @file_attachments       NVARCHAR(512)  = NULL,
  @mail_database          sysname       = N'msdb'
  -- Have infrastructure in place to support this but disabled by default
  -- For first implementation we will have this parameters but using it will generate an error - not implemented yet.
AS
BEGIN
  DECLARE @retval INT
  DECLARE @email_address NVARCHAR(100)
  DECLARE @enabled TINYINT
  DECLARE @qualified_sp_sendmail sysname
  DECLARE @db_id INT

  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters
  SELECT @profile_name              = LTRIM(RTRIM(@profile_name))
  SELECT @name                      = LTRIM(RTRIM(@name))
  SELECT @file_attachments          = LTRIM(RTRIM(@file_attachments))
  SELECT @mail_database          = LTRIM(RTRIM(@mail_database))


  IF @profile_name       = ''    SELECT @profile_name      = NULL
  IF @name               = ''    SELECT @name              = NULL
  IF @file_attachments   = ''    SELECT @file_attachments  = NULL
  IF @mail_database      = ''    SELECT @mail_database      = NULL

  EXECUTE @retval = sp_verify_operator_identifiers '@name',
                                                   '@id',
                                                   @name OUTPUT,
                                                   @id   OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  --is operator enabled?
  SELECT @enabled = enabled, @email_address = email_address FROM sysoperators WHERE id = @id
  IF @enabled = 0 
  BEGIN
    RAISERROR(14601, 16, 1, @name)
    RETURN 1
  END
  
  IF @email_address IS NULL
  BEGIN
    RAISERROR(14602, 16, 1, @name)
    RETURN 1
  END
  
  SELECT @qualified_sp_sendmail = @mail_database + '.dbo.sp_send_dbmail'

  EXEC   @retval = @qualified_sp_sendmail @profile_name = @profile_name,
                               @recipients       = @email_address,
                               @subject          = @subject,
                               @body              = @body,
                               @file_attachments = @file_attachments
  RETURN @retval                            
END
go

/**************************************************************/
/* SP_VERIFY_NOTIFICATION                                     */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_verify_notification...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_verify_notification')
              AND (type = 'P')))
  DROP PROCEDURE sp_verify_notification
go
CREATE PROCEDURE sp_verify_notification
  @alert_name          sysname,
  @operator_name       sysname,
  @notification_method TINYINT,
  @alert_id            INT OUTPUT,
  @operator_id         INT OUTPUT
AS
BEGIN
  DECLARE @res_valid_range NVARCHAR(100)

  SET NOCOUNT ON

  SELECT @res_valid_range = FORMATMESSAGE(14208)

  -- Remove any leading/trailing spaces from parameters
  SELECT @alert_name    = LTRIM(RTRIM(@alert_name))
  SELECT @operator_name = LTRIM(RTRIM(@operator_name))

  -- Check if the AlertName is valid
  SELECT @alert_id = id
  FROM msdb.dbo.sysalerts
  WHERE (name = @alert_name)

  IF (@alert_id IS NULL)
  BEGIN
    RAISERROR(14262, 16, 1, '@alert_name', @alert_name)
    RETURN(1) -- Failure
  END

  -- Check if the OperatorName is valid
  SELECT @operator_id = id
  FROM msdb.dbo.sysoperators
  WHERE (name = @operator_name)

  IF (@operator_id IS NULL)
  BEGIN
    RAISERROR(14262, 16, 1, '@operator_name', @operator_name)
    RETURN(1) -- Failure
  END

  -- If we're at a TSX, we disallow using operator 'MSXOperator'
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.systargetservers)) AND
     (@operator_name = N'MSXOperator')
  BEGIN
    RAISERROR(14251, -1, -1, @operator_name)
    RETURN(1) -- Failure
  END

  -- Check if the NotificationMethod is valid
  IF ((@notification_method < 1) OR (@notification_method > 7))
  BEGIN
    RAISERROR(14266, 16, 1, '@notification_method', @res_valid_range)
    RETURN(1) -- Failure
  END

  RETURN(0) -- Success
END
go

/**************************************************************/
/* SP_ADD_NOTIFICATION                                        */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_notification...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_notification')
              AND (type = 'P')))
  DROP PROCEDURE sp_add_notification
go
CREATE PROCEDURE sp_add_notification
  @alert_name          sysname,
  @operator_name       sysname,
  @notification_method TINYINT -- 1 = Email, 2 = Pager, 4 = NetSend, 7 = All
AS
BEGIN
  DECLARE @alert_id             INT
  DECLARE @operator_id          INT
  DECLARE @notification         NVARCHAR(512)
  DECLARE @retval               INT
  DECLARE @old_has_notification INT
  DECLARE @new_has_notification INT
  DECLARE @res_notification     NVARCHAR(100)

  SET NOCOUNT ON

  SELECT @res_notification = FORMATMESSAGE(14210)

  -- Remove any leading/trailing spaces from parameters
  SELECT @alert_name    = LTRIM(RTRIM(@alert_name))
  SELECT @operator_name = LTRIM(RTRIM(@operator_name))

  -- Only a sysadmin can do this
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Check if the Notification is valid
  EXECUTE @retval = msdb.dbo.sp_verify_notification @alert_name,
                                                    @operator_name,
                                                    @notification_method,
                                                    @alert_id     OUTPUT,
                                                    @operator_id  OUTPUT
  IF (@retval <> 0)
    RETURN(1) -- Failure

  -- Check if this notification already exists
  -- NOTE: The unique index would catch this, but testing for the problem here lets us
  --       control the message.
  IF (EXISTS (SELECT *
              FROM msdb.dbo.sysnotifications
              WHERE (alert_id = @alert_id)
                AND (operator_id = @operator_id)))
  BEGIN
    SELECT @notification = @alert_name + N' / ' + @operator_name 
    RAISERROR(14261, 16, 1, @res_notification, @notification)
    RETURN(1) -- Failure
  END

  SELECT @old_has_notification = has_notification
  FROM msdb.dbo.sysalerts
  WHERE (id = @alert_id)

  -- Do the INSERT
  INSERT INTO msdb.dbo.sysnotifications
         (alert_id,
          operator_id,
          notification_method)
  VALUES (@alert_id,
          @operator_id,
          @notification_method)

  SELECT @retval = @@error

  SELECT @new_has_notification = has_notification
  FROM msdb.dbo.sysalerts
  WHERE (id = @alert_id)

  -- Notify SQLServerAgent of the change - if any - to has_notifications
  IF (@old_has_notification <> @new_has_notification)
    EXECUTE msdb.dbo.sp_sqlagent_notify @op_type     = N'A',
                                        @alert_id    = @alert_id,
                                        @action_type = N'U'

  RETURN(@retval) -- 0 means success
END
go

/**************************************************************/
/* SP_UPDATE_NOTIFICATION                                     */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_update_notification...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_update_notification')
              AND (type = 'P')))
  DROP PROCEDURE sp_update_notification
go
CREATE PROCEDURE sp_update_notification
  @alert_name          sysname,
  @operator_name       sysname,
  @notification_method TINYINT -- 1 = Email, 2 = Pager, 4 = NetSend, 7 = All
AS
BEGIN
  DECLARE @alert_id             INT
  DECLARE @operator_id          INT
  DECLARE @notification         NVARCHAR(512)
  DECLARE @retval               INT
  DECLARE @old_has_notification INT
  DECLARE @new_has_notification INT
  DECLARE @res_notification     NVARCHAR(100)

  SET NOCOUNT ON

  SELECT @res_notification = FORMATMESSAGE(14210)

  -- Remove any leading/trailing spaces from parameters
  SELECT @alert_name    = LTRIM(RTRIM(@alert_name))
  SELECT @operator_name = LTRIM(RTRIM(@operator_name))

  -- Only a sysadmin can do this
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Check if the Notification is valid
  EXECUTE sp_verify_notification @alert_name,
                                 @operator_name,
                                 @notification_method,
                                 @alert_id     OUTPUT,
                                 @operator_id  OUTPUT

  -- Check if this notification exists
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysnotifications
                  WHERE (alert_id = @alert_id)
                    AND (operator_id = @operator_id)))
  BEGIN
    SELECT @notification = @alert_name + N' / ' + @operator_name
    RAISERROR(14262, 16, 1, @res_notification, @notification)
    RETURN(1) -- Failure
  END

  SELECT @old_has_notification = has_notification
  FROM msdb.dbo.sysalerts
  WHERE (id = @alert_id)

  -- Do the UPDATE
  UPDATE msdb.dbo.sysnotifications
  SET notification_method = @notification_method
  WHERE (alert_id = @alert_id)
    AND (operator_id = @operator_id)

  SELECT @retval = @@error

  SELECT @new_has_notification = has_notification
  FROM msdb.dbo.sysalerts
  WHERE (id = @alert_id)

  -- Notify SQLServerAgent of the change - if any - to has_notifications
  IF (@old_has_notification <> @new_has_notification)
    EXECUTE msdb.dbo.sp_sqlagent_notify @op_type       = N'A',
                                          @alert_id    = @alert_id,
                                          @action_type = N'U'

  RETURN(@retval) -- 0 means success
END
go

/**************************************************************/
/* SP_DELETE_NOTIFICATION                                     */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_notification...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_notification')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_notification
go
CREATE PROCEDURE sp_delete_notification
  @alert_name    sysname,
  @operator_name sysname
AS
BEGIN
  DECLARE @alert_id             INT
  DECLARE @operator_id          INT
  DECLARE @ignored              TINYINT
  DECLARE @notification         NVARCHAR(512)
  DECLARE @retval               INT
  DECLARE @old_has_notification INT
  DECLARE @new_has_notification INT
  DECLARE @res_notification     NVARCHAR(100)

  SET NOCOUNT ON

  SELECT @res_notification = FORMATMESSAGE(14210)

  -- Remove any leading/trailing spaces from parameters
  SELECT @alert_name    = LTRIM(RTRIM(@alert_name))
  SELECT @operator_name = LTRIM(RTRIM(@operator_name))

  -- Only a sysadmin can do this
  IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  -- Get the alert and operator ID's
  EXECUTE sp_verify_notification @alert_name,
                                 @operator_name,
                                 7,           -- A dummy (but valid) value
                                 @alert_id    OUTPUT,
                                 @operator_id OUTPUT

  -- Check if this notification exists
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysnotifications
                  WHERE (alert_id = @alert_id)
                    AND (operator_id = @operator_id)))
  BEGIN
    SELECT @notification = @alert_name + N' / ' + @operator_name
    RAISERROR(14262, 16, 1, @res_notification, @notification)
    RETURN(1) -- Failure
  END

  SELECT @old_has_notification = has_notification
  FROM msdb.dbo.sysalerts
  WHERE (id = @alert_id)

  -- Do the Delete
  DELETE FROM msdb.dbo.sysnotifications
  WHERE (alert_id = @alert_id)
    AND (operator_id = @operator_id)

  SELECT @retval = @@error

  SELECT @new_has_notification = has_notification
  FROM msdb.dbo.sysalerts
  WHERE (id = @alert_id)

  -- Notify SQLServerAgent of the change - if any - to has_notifications
  IF (@old_has_notification <> @new_has_notification)
    EXECUTE msdb.dbo.sp_sqlagent_notify @op_type       = N'A',
                                          @alert_id    = @alert_id,
                                          @action_type = N'U'

  RETURN(@retval) -- 0 means success
END
go

/**************************************************************/
/* SP_HELP_NOTIFICATION                                       */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_help_notification...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_notification')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_notification
go
CREATE PROCEDURE sp_help_notification
  @object_type          CHAR(9),   -- Either 'ALERTS'    (enumerates Alerts for given Operator)
                                   --     or 'OPERATORS' (enumerates Operators for given Alert)
  @name                 sysname,   -- Either an Operator Name (if @object_type is 'ALERTS')
                                   --     or an Alert Name    (if @object_type is 'OPERATORS')
  @enum_type            CHAR(10),  -- Either 'ALL'    (enumerate all objects [eg. all alerts irrespective of whether 'Fred' receives a notification for them])
                                   --     or 'ACTUAL' (enumerate only the associated objects [eg. only the alerts which 'Fred' receives a notification for])
                                   --     or 'TARGET' (enumerate only the objects matching @target_name [eg. a single row showing how 'Fred' is notfied for alert 'Test'])
  @notification_method  TINYINT,   -- Either 1 (Email)   - Modifies the result set to only show use_email column
                                   --     or 2 (Pager)   - Modifies the result set to only show use_pager column
                                   --     or 4 (NetSend) - Modifies the result set to only show use_netsend column
                                   --     or 7 (All)     - Modifies the result set to show all the use_xxx columns
  @target_name   sysname = NULL    -- Either an Alert Name    (if @object_type is 'ALERTS')
                                   --     or an Operator Name (if @object_type is 'OPERATORS')
                                   -- NOTE: This parameter is only required if @enum_type is 'TARGET')
AS
BEGIN
  DECLARE @id              INT    -- We use this to store the decode of @name
  DECLARE @target_id       INT    -- We use this to store the decode of @target_name
  DECLARE @select_clause   NVARCHAR(1024)
  DECLARE @from_clause     NVARCHAR(512)
  DECLARE @where_clause    NVARCHAR(512)
  DECLARE @res_valid_range NVARCHAR(100)

  SET NOCOUNT ON

  SELECT @res_valid_range = FORMATMESSAGE(14208)

  -- Remove any leading/trailing spaces from parameters
  SELECT @object_type = UPPER(LTRIM(RTRIM(@object_type)) collate SQL_Latin1_General_CP1_CS_AS)
  SELECT @name        = LTRIM(RTRIM(@name))
  SELECT @enum_type   = UPPER(LTRIM(RTRIM(@enum_type)) collate SQL_Latin1_General_CP1_CS_AS)
  SELECT @target_name = LTRIM(RTRIM(@target_name))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@target_name = N'') SELECT @target_name = NULL

  -- Check ObjectType
  IF (@object_type NOT IN ('ALERTS', 'OPERATORS'))
  BEGIN
    RAISERROR(14266, 16, 1, '@object_type', 'ALERTS, OPERATORS')
    RETURN(1) -- Failure
  END

  -- Check AlertName
  IF (@object_type = 'OPERATORS') AND
     (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysalerts
                  WHERE (name = @name)))
  BEGIN
    RAISERROR(14262, 16, 1, '@name', @name)
    RETURN(1) -- Failure
  END

  -- Check OperatorName
  IF (@object_type = 'ALERTS') AND
     (NOT EXISTS (SELECT *
                  FROM msdb.dbo.sysoperators
                  WHERE (name = @name)))
  BEGIN
    RAISERROR(14262, 16, 1, '@name', @name)
    RETURN(1) -- Failure
  END

  -- Check EnumType
  IF (@enum_type NOT IN ('ALL', 'ACTUAL', 'TARGET'))
  BEGIN
    RAISERROR(14266, 16, 1, '@enum_type', 'ALL, ACTUAL, TARGET')
    RETURN(1) -- Failure
  END

  -- Check Notification Method
  IF ((@notification_method < 1) OR (@notification_method > 7))
  BEGIN
    RAISERROR(14266, 16, 1, '@notification_method', @res_valid_range)
    RETURN(1) -- Failure
  END

  -- If EnumType is 'TARGET', check if we have a @TargetName parameter
  IF (@enum_type = 'TARGET') AND (@target_name IS NULL)
  BEGIN
    RAISERROR(14502, 16, 1)
    RETURN(1) -- Failure
  END

  -- If EnumType isn't 'TARGET', we shouldn't have an @target_name parameter
  IF (@enum_type <> 'TARGET') AND (@target_name IS NOT NULL)
  BEGIN
    RAISERROR(14503, 16, 1)
    RETURN(1) -- Failure
  END

  -- Translate the Name into an ID
  IF (@object_type = 'ALERTS')
  BEGIN
    SELECT @id = id
    FROM msdb.dbo.sysoperators
    WHERE (name = @name)
  END
  IF (@object_type = 'OPERATORS')
  BEGIN
    SELECT @id = id
    FROM msdb.dbo.sysalerts
    WHERE (name = @name)
  END

  -- Translate the TargetName into a TargetID
  IF (@target_name IS NOT NULL)
  BEGIN
    IF (@object_type = 'OPERATORS')
    BEGIN
      SELECT @target_id = id
      FROM msdb.dbo.sysoperators
      WHERE (name = @target_name )
    END
    IF (@object_type = 'ALERTS')
    BEGIN
      SELECT @target_id = id
      FROM msdb.dbo.sysalerts
      WHERE (name = @target_name)
    END
    IF (@target_id IS NULL) -- IE. the Target Name is invalid
    BEGIN
      RAISERROR(14262, 16, 1, @object_type, @target_name)
      RETURN(1) -- Failure
    END
  END

  -- Ok, the parameters look good so generate the SQL then EXECUTE() it...

  -- Generate the 'stub' SELECT clause and the FROM clause
  IF (@object_type = 'OPERATORS') -- So we want a list of Operators for the supplied AlertID
  BEGIN
    SELECT @select_clause = N'SELECT operator_id = o.id, operator_name = o.name, '
    IF (@enum_type = 'ALL')
      SELECT @from_clause = N'FROM msdb.dbo.sysoperators o LEFT OUTER JOIN msdb.dbo.sysnotifications sn ON (o.id = sn.operator_id) '
    ELSE
      SELECT @from_clause = N'FROM msdb.dbo.sysoperators o, msdb.dbo.sysnotifications sn '
  END
  IF (@object_type = 'ALERTS') -- So we want a list of Alerts for the supplied OperatorID
  BEGIN
    SELECT @select_clause = N'SELECT alert_id = a.id, alert_name = a.name, '
    IF (@enum_type = 'ALL')
      SELECT @from_clause = N'FROM msdb.dbo.sysalerts a LEFT OUTER JOIN msdb.dbo.sysnotifications sn ON (a.id = sn.alert_id) '
    ELSE
      SELECT @from_clause = N'FROM msdb.dbo.sysalerts a, msdb.dbo.sysnotifications sn '
  END

  -- Add the required use_xxx columns to the SELECT clause
  IF (@notification_method & 1 = 1)
    SELECT @select_clause = @select_clause + N'use_email = ISNULL((sn.notification_method & 1) / POWER(2, 0), 0), '
  IF (@notification_method & 2 = 2)
    SELECT @select_clause = @select_clause + N'use_pager = ISNULL((sn.notification_method & 2) / POWER(2, 1), 0), '
  IF (@notification_method & 4 = 4)
    SELECT @select_clause = @select_clause + N'use_netsend = ISNULL((sn.notification_method & 4) / POWER(2, 2), 0), '

  -- Remove the trailing comma
  SELECT @select_clause = SUBSTRING(@select_clause, 1, (DATALENGTH(@select_clause) / 2) - 2) + N' '

  -- Generate the WHERE clause
  IF (@object_type = 'OPERATORS')
  BEGIN
    IF (@enum_type = 'ALL')
      SELECT @from_clause = @from_clause + N' AND (sn.alert_id = ' + CONVERT(VARCHAR(10), @id) + N')'

    IF (@enum_type = 'ACTUAL')
      SELECT @where_clause = N'WHERE (o.id = sn.operator_id) AND (sn.alert_id = ' + CONVERT(VARCHAR(10), @id) + N') AND (sn.notification_method & ' + CONVERT(VARCHAR, @notification_method) + N' <> 0)'

    IF (@enum_type = 'TARGET')
      SELECT @where_clause = N'WHERE (o.id = sn.operator_id) AND (sn.operator_id = ' + CONVERT(VARCHAR(10), @target_id) + N') AND (sn.alert_id = ' + CONVERT(VARCHAR(10), @id) + N')'
  END
  IF (@object_type = 'ALERTS')
  BEGIN
    IF (@enum_type = 'ALL')
      SELECT @from_clause = @from_clause + N' AND (sn.operator_id = ' + CONVERT(VARCHAR(10), @id) + N')'

    IF (@enum_type = 'ACTUAL')
      SELECT @where_clause = N'WHERE (a.id = sn.alert_id) AND (sn.operator_id = ' + CONVERT(VARCHAR(10), @id) + N') AND (sn.notification_method & ' + CONVERT(VARCHAR, @notification_method) + N' <> 0)'

    IF (@enum_type = 'TARGET')
      SELECT @where_clause = N'WHERE (a.id = sn.alert_id) AND (sn.alert_id = ' + CONVERT(VARCHAR(10), @target_id) + N') AND (sn.operator_id = ' + CONVERT(VARCHAR(10), @id) + N')'
  END

  -- Add the has_email and has_pager columns to the SELECT clause
  IF (@object_type = 'OPERATORS')
  BEGIN
    SELECT @select_clause = @select_clause + N', has_email = PATINDEX(N''%[^ ]%'', ISNULL(o.email_address, N''''))'
    SELECT @select_clause = @select_clause + N', has_pager = PATINDEX(N''%[^ ]%'', ISNULL(o.pager_address, N''''))'
    SELECT @select_clause = @select_clause + N', has_netsend = PATINDEX(N''%[^ ]%'', ISNULL(o.netsend_address, N''''))'
  END
  IF (@object_type = 'ALERTS')
  BEGIN
    -- NOTE: We return counts so that the UI can detect 'unchecking' the last notification
    SELECT @select_clause = @select_clause + N', has_email = (SELECT COUNT(*) FROM sysnotifications WHERE (alert_id = a.id) AND ((notification_method & 1) = 1)) '
    SELECT @select_clause = @select_clause + N', has_pager = (SELECT COUNT(*) FROM sysnotifications WHERE (alert_id = a.id) AND ((notification_method & 2) = 2)) '
    SELECT @select_clause = @select_clause + N', has_netsend = (SELECT COUNT(*) FROM sysnotifications WHERE (alert_id = a.id) AND ((notification_method & 4) = 4)) '
  END

  EXECUTE (@select_clause + @from_clause + @where_clause)

  RETURN(@@error) -- 0 means success
END
go

PRINT ''
PRINT 'Creating procedure sp_help_jobactivity...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = 'sp_help_jobactivity')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_jobactivity
go
CREATE PROCEDURE sp_help_jobactivity
  @job_id     UNIQUEIDENTIFIER = NULL,  -- If provided should NOT also provide job_name
  @job_name   sysname          = NULL,  -- If provided should NOT also provide job_id
  @session_id INT = NULL
AS
BEGIN
  DECLARE @retval          INT
  DECLARE @session_id_as_char NVARCHAR(16)
  SET NOCOUNT ON

  -- Remove any leading/trailing spaces from parameters (except @owner_login_name)
  SELECT @job_name         = LTRIM(RTRIM(@job_name))

  -- Turn [nullable] empty string parameters into NULLs
  IF (@job_name         = N'') SELECT @job_name = NULL

  IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL))
  BEGIN
    EXECUTE @retval = sp_verify_job_identifiers '@job_name',
                                                '@job_id',
                                                 @job_name OUTPUT,
                                                 @job_id   OUTPUT
    IF (@retval <> 0)
      RETURN(1) -- Failure
  END
  


  IF @session_id IS NULL
    SELECT TOP(1) @session_id = session_id FROM syssessions ORDER by agent_start_date DESC 
  ELSE IF NOT EXISTS( SELECT * FROM syssessions WHERE session_id = @session_id)
  BEGIN
    SELECT @session_id_as_char = CONVERT(NVARCHAR(16), @session_id)
    RAISERROR(14262, -1, -1, '@session_id', @session_id_as_char)
    RETURN(1) --failure
  END

  SELECT
      ja.session_id,                
      ja.job_id,
    j.name AS job_name,
    ja.run_requested_date,        
    ja.run_requested_source,      
    ja.queued_date,               
    ja.start_execution_date,      
    ja.last_executed_step_id,     
    ja.last_executed_step_date,   
    ja.stop_execution_date,       
    ja.next_scheduled_run_date,
    ja.job_history_id,            
    jh.message,
    jh.run_status,
    jh.operator_id_emailed,
    jh.operator_id_netsent,
    jh.operator_id_paged
  FROM
    (msdb.dbo.sysjobactivity ja LEFT JOIN msdb.dbo.sysjobhistory jh ON ja.job_history_id = jh.instance_id)
    join msdb.dbo.sysjobs_view j on ja.job_id = j.job_id
  WHERE
    (@job_id IS NULL OR ja.job_id = @job_id) AND
     ja.session_id = @session_id

  RETURN(0)
END
go
/**************************************************************/
/*                                                            */
/*                    T  R  I G  G  E  R  S                   */
/*                                                            */
/**************************************************************/

/**************************************************************/
/* DROP THE OLD 6.x TRIGGERS                                  */
/* [multiple triggers of the same type are allowed in 7.0]    */
/**************************************************************/

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'NewOrChangedNotification')
              AND (type = 'TR')))
  DROP TRIGGER NewOrChangedNotification

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'RemovedNotification')
              AND (type = 'TR')))
  DROP TRIGGER RemovedNotification
go

/**************************************************************/
/* TRIG_NOTIFICATION_INS_OR_UPD                               */
/**************************************************************/

PRINT ''
PRINT 'Creating trigger trig_notification_ins_or_upd...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'trig_notification_ins_or_upd')
              AND (type = 'TR')))
  DROP TRIGGER trig_notification_ins_or_upd
go
CREATE TRIGGER trig_notification_ins_or_upd
ON msdb.dbo.sysnotifications
FOR INSERT,
    UPDATE
AS
BEGIN
  SET NOCOUNT ON

  -- First, throw out 'non-notification' rows
  DELETE FROM msdb.dbo.sysnotifications
  WHERE (notification_method = 0)

  -- Reset the has_notification flag for the affected alerts
  UPDATE msdb.dbo.sysalerts
  SET has_notification = 0
  FROM inserted           i,
       msdb.dbo.sysalerts sa
  WHERE (i.alert_id = sa.id)

  -- Update sysalerts.has_notification (for email)
  UPDATE msdb.dbo.sysalerts
  SET has_notification = has_notification | 1
  FROM msdb.dbo.sysalerts        sa,
       msdb.dbo.sysnotifications sn,
       inserted                  i
  WHERE (sa.id = sn.alert_id)
    AND (sa.id = i.alert_id)
    AND ((sn.notification_method & 1) = 1)

  -- Update sysalerts.has_notification (for pager)
  UPDATE msdb.dbo.sysalerts
  SET has_notification = has_notification | 2
  FROM msdb.dbo.sysalerts        sa,
       msdb.dbo.sysnotifications sn,
       inserted                  i
  WHERE (sa.id = sn.alert_id)
    AND (sa.id = i.alert_id)
    AND ((sn.notification_method & 2) = 2)

  -- Update sysalerts.has_notification (for netsend)
  UPDATE msdb.dbo.sysalerts
  SET has_notification = has_notification | 4
  FROM msdb.dbo.sysalerts        sa,
       msdb.dbo.sysnotifications sn,
       inserted                  i
  WHERE (sa.id = sn.alert_id)
    AND (sa.id = i.alert_id)
    AND ((sn.notification_method & 4) = 4)
END
go

/**************************************************************/
/* TRIG_NOTIFICATION_DELETE                                   */
/**************************************************************/

PRINT ''
PRINT 'Creating trigger trig_notification_delete...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'trig_notification_delete')
              AND (type = 'TR')))
  DROP TRIGGER trig_notification_delete
go
CREATE TRIGGER trig_notification_delete
ON msdb.dbo.sysnotifications
FOR DELETE
AS
BEGIN
  SET NOCOUNT ON

  -- Reset the has_notification flag for the affected alerts
  UPDATE msdb.dbo.sysalerts
  SET has_notification = 0
  FROM deleted            d,
       msdb.dbo.sysalerts sa
  WHERE (d.alert_id = sa.id)

  -- Update sysalerts.has_notification (for email)
  UPDATE msdb.dbo.sysalerts
  SET has_notification = has_notification | 1
  FROM msdb.dbo.sysalerts        sa,
       msdb.dbo.sysnotifications sn,
       deleted                   d
  WHERE (sa.id = sn.alert_id)
    AND (sa.id = d.alert_id)
    AND ((sn.notification_method & 1) = 1)

  -- Update sysalerts.has_notification (for pager)
  UPDATE msdb.dbo.sysalerts
  SET has_notification = has_notification | 2
  FROM msdb.dbo.sysalerts        sa,
       msdb.dbo.sysnotifications sn,
       deleted                   d
  WHERE (sa.id = sn.alert_id)
    AND (sa.id = d.alert_id)
    AND ((sn.notification_method & 2) = 2)

  -- Update sysalerts.has_notification (for netsend)
  UPDATE msdb.dbo.sysalerts
  SET has_notification = has_notification | 4
  FROM msdb.dbo.sysalerts        sa,
       msdb.dbo.sysnotifications sn,
       deleted                   d
  WHERE (sa.id = sn.alert_id)
    AND (sa.id = d.alert_id)
    AND ((sn.notification_method & 4) = 4)
END
go

/**************************************************************/
/**                                                          **/
/**       B A C K U P   H I S T O R Y   S U P P O R T        **/
/**                                                          **/
/**************************************************************/

/**************************************************************/
/* T A B L E S                                                */
/**************************************************************/

/**************************************************************/
/* BACKUPMEDIASET                                             */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = 'backupmediaset')))
BEGIN
  PRINT ''
  PRINT 'Creating table backupmediaset...'

  CREATE TABLE backupmediaset
  (
  media_set_id       INT IDENTITY     NOT NULL PRIMARY KEY,
  media_uuid         UNIQUEIDENTIFIER NULL,  -- Null if this media set only one media family
  media_family_count TINYINT          NULL,  -- Number of media families in the media set
  name               NVARCHAR(128)    NULL,
  description        NVARCHAR(255)    NULL,
  software_name      NVARCHAR(128)    NULL,
  software_vendor_id INT              NULL,
  MTF_major_version  TINYINT          NULL,
  mirror_count       TINYINT          NULL,   -- number of mirror plexes
  is_password_protected BIT           NULL,
  is_compressed      BIT              NULL    -- 1 if backup compression was used
  )

  CREATE INDEX backupmediasetuuid ON backupmediaset (media_uuid)
END
ELSE
BEGIN
  IF EXISTS (
    select * from msdb.dbo.syscolumns where name='password_protected' and id =
        (select id from msdb.dbo.sysobjects where name='backupmediaset'))

    ALTER TABLE backupmediaset DROP COLUMN password_protected

  IF NOT EXISTS (
    select * from msdb.dbo.syscolumns where name='is_password_protected' and id =
        (select id from msdb.dbo.sysobjects where name='backupmediaset'))

    ALTER TABLE backupmediaset ADD is_password_protected BIT NULL

  IF NOT EXISTS (
    select * from msdb.dbo.syscolumns where name='is_compressed' and id =
        (select id from msdb.dbo.sysobjects where name='backupmediaset'))

    ALTER TABLE backupmediaset ADD is_compressed BIT NULL

  IF NOT EXISTS (
    select * from msdb.dbo.syscolumns where name='mirror_count' and id =
        (select id from msdb.dbo.sysobjects where name='backupmediaset'))

    ALTER TABLE backupmediaset ADD mirror_count TINYINT NULL

END
go

/**************************************************************/
/* BACKUPMEDIAFAMILY                                          */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = 'backupmediafamily')))
BEGIN
  PRINT ''
  PRINT 'Creating table backupmediafamily...'

  CREATE TABLE backupmediafamily
  (
  media_set_id           INT              NOT NULL REFERENCES backupmediaset(media_set_id),
  family_sequence_number TINYINT          NOT NULL, -- Raid sequence number
  media_family_id        UNIQUEIDENTIFIER NULL,     -- This will be a uuid in MTF 2.0, allow space
  media_count            INT              NULL,     -- Number of media in the family
  logical_device_name    NVARCHAR(128)    NULL,     -- Name from sysdevices, if any
  physical_device_name   NVARCHAR(260)    NULL,     -- To facilitate restores from online media (disk)
  device_type            TINYINT          NULL,  -- Disk, tape, pipe, ...
  physical_block_size    INT              NULL,
  mirror             TINYINT        DEFAULT 0 NOT NULL
  PRIMARY KEY (media_set_id, family_sequence_number, mirror)
  )

  CREATE INDEX backupmediafamilyuuid ON backupmediafamily (media_family_id)
END
ELSE
BEGIN
  IF NOT EXISTS (
    select * from msdb.dbo.syscolumns where name='mirror' and id =
        (select id from msdb.dbo.sysobjects where name='backupmediafamily'))
  BEGIN
    begin tran

    -- remove any old constraint, not involving mirror
   declare @pkName sysname
   DECLARE @sql NVARCHAR(4000)
   select @pkName=i.name
      from
         sys.indexes i,
         sys.all_objects o
      where
         o.object_id = object_id ('backupmediafamily')
         and o.object_id = i.object_id
         and i.is_primary_key = 1
   IF (@pkName IS NOT NULL)
   begin
      select @sql = N'ALTER TABLE backupmediafamily DROP CONSTRAINT ' + QUOTENAME(@pkName)
      EXEC (@sql)
   end

   ALTER TABLE backupmediafamily ADD mirror TINYINT DEFAULT 0 NOT NULL

   ALTER TABLE backupmediafamily ADD CONSTRAINT backupmediafamily_PK
      PRIMARY KEY (media_set_id, family_sequence_number, mirror)

   commit    
  END
END
go

/**************************************************************/
/* BACKUPSET - One row per backup operation.                  */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = 'backupset')))
BEGIN
  PRINT ''
  PRINT 'Creating table backupset...'

  CREATE TABLE backupset
  (
  backup_set_id          INT IDENTITY     NOT NULL PRIMARY KEY,
  backup_set_uuid        UNIQUEIDENTIFIER NOT NULL,
  media_set_id           INT              NOT NULL REFERENCES backupmediaset(media_set_id),
  first_family_number    TINYINT          NULL,  -- family number & media number of the media
  first_media_number     SMALLINT         NULL,  -- containing the start of this backup (first SSET)
  last_family_number     TINYINT          NULL,  -- family number & media number of the media
  last_media_number      SMALLINT         NULL,  -- containing the end of this backup (ESET after MBC)
  catalog_family_number  TINYINT          NULL,  -- family number & media number of the media
  catalog_media_number   SMALLINT         NULL,  -- containing the start of the 'directory' data stream

  position               INT              NULL,  -- For FILE=
  expiration_date        DATETIME         NULL,

  -- From SSET...
  software_vendor_id     INT              NULL,  -- Might want table for sw vendors
  name                   NVARCHAR(128)    NULL,
  description            NVARCHAR(255)    NULL,
  user_name              NVARCHAR(128)    NULL,
  software_major_version TINYINT          NULL, 
  software_minor_version TINYINT          NULL, 
  software_build_version SMALLINT         NULL,
  time_zone              SMALLINT         NULL,    
  mtf_minor_version      TINYINT          NULL,

  -- From CONFIG_INFO...
  first_lsn              NUMERIC(25,0)    NULL,
  last_lsn               NUMERIC(25,0)    NULL,
  checkpoint_lsn         NUMERIC(25,0)    NULL,
  database_backup_lsn    NUMERIC(25,0)    NULL,
  database_creation_date DATETIME         NULL,
  backup_start_date      DATETIME         NULL,
  backup_finish_date     DATETIME         NULL,
  type                   CHAR(1)          NULL,
  sort_order             SMALLINT         NULL,
  code_page              SMALLINT         NULL,
  compatibility_level    TINYINT          NULL,
  database_version       INT              NULL,
  backup_size            NUMERIC(20,0)    NULL,
  database_name          NVARCHAR(128)    NULL,
  server_name            NVARCHAR(128)    NULL,
  machine_name           NVARCHAR(128)    NULL,
  flags                  INT              NULL,
  unicode_locale         INT              NULL,
  unicode_compare_style  INT              NULL,
  collation_name         NVARCHAR(128)    NULL,
  is_password_protected  BIT              NULL,
  recovery_model         NVARCHAR(60)     NULL,
  has_bulk_logged_data   BIT              NULL,
  is_snapshot            BIT              NULL,
  is_readonly            BIT              NULL,
  is_single_user         BIT              NULL,
  has_backup_checksums   BIT              NULL,
  is_damaged             BIT              NULL,
  begins_log_chain       BIT              NULL,
  has_incomplete_metadata BIT             NULL,
  is_force_offline       BIT              NULL,
  is_copy_only           BIT              NULL,
  first_recovery_fork_guid UNIQUEIDENTIFIER NULL,
  last_recovery_fork_guid UNIQUEIDENTIFIER NULL,
  fork_point_lsn         NUMERIC(25,0)    NULL,
  database_guid          UNIQUEIDENTIFIER NULL,
  family_guid            UNIQUEIDENTIFIER NULL,
  differential_base_lsn  NUMERIC(25,0)    NULL,
  differential_base_guid UNIQUEIDENTIFIER NULL,
  compressed_backup_size NUMERIC(20,0)    NULL
  )

  CREATE INDEX backupsetuuid ON backupset (backup_set_uuid)
  CREATE INDEX backupsetDate ON backupset (backup_finish_date) -- helps sp_delete_backuphistory
END
ELSE
BEGIN
  IF EXISTS (
    select * from msdb.dbo.syscolumns where name='password_protected' and id =
        (select id from msdb.dbo.sysobjects where name='backupset'))

    ALTER TABLE backupset DROP COLUMN password_protected

  IF NOT EXISTS (
    select * from msdb.dbo.syscolumns where name='flags' and id =
        (select id from msdb.dbo.sysobjects where name='backupset'))

    ALTER TABLE backupset ADD flags INT NULL

  IF NOT EXISTS (
    select * from msdb.dbo.syscolumns where name='collation_name' and id =
        (select id from msdb.dbo.sysobjects where name='backupset'))

    ALTER TABLE backupset ADD
      unicode_locale         INT              NULL,
      unicode_compare_style  INT              NULL,
      collation_name         NVARCHAR(128)    NULL

  IF NOT EXISTS (
    select * from msdb.dbo.syscolumns where name='is_password_protected' and id =
        (select id from msdb.dbo.sysobjects where name='backupset'))

    ALTER TABLE backupset ADD is_password_protected BIT NULL

  IF NOT EXISTS (
    select * from msdb.dbo.syscolumns where name='compressed_backup_size' and id =
        (select id from msdb.dbo.sysobjects where name='backupset'))

    ALTER TABLE backupset ADD compressed_backup_size NUMERIC(20,0) NULL

  IF NOT EXISTS (
    select * from msdb.dbo.syscolumns where name='recovery_model' and id =
        (select id from msdb.dbo.sysobjects where name='backupset'))
   ALTER TABLE backupset ADD
      recovery_model         NVARCHAR(60)     NULL,
      has_bulk_logged_data   BIT              NULL,
      is_snapshot            BIT              NULL,
      is_readonly            BIT              NULL,
      is_single_user         BIT              NULL,
      has_backup_checksums   BIT              NULL,
      is_damaged             BIT              NULL,
      begins_log_chain       BIT              NULL,
      has_incomplete_metadata BIT             NULL,
      is_force_offline       BIT              NULL,
      is_copy_only           BIT              NULL,
      first_recovery_fork_guid UNIQUEIDENTIFIER NULL,
      last_recovery_fork_guid UNIQUEIDENTIFIER NULL,
      fork_point_lsn         NUMERIC(25,0)    NULL,
      database_guid          UNIQUEIDENTIFIER NULL,
      family_guid            UNIQUEIDENTIFIER NULL,
      differential_base_lsn  NUMERIC(25,0)    NULL,
      differential_base_guid UNIQUEIDENTIFIER NULL

  IF NOT EXISTS (
      SELECT *
      FROM msdb.sys.indexes
      WHERE (name = 'backupsetDate'))
    CREATE INDEX backupsetDate ON backupset (backup_finish_date)

END
go

/**************************************************************/
-- BACKUPFILE/FILEGROUP 
-- One row per file/filegroup backed up
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = 'backupfilegroup')))
BEGIN
  PRINT ''
  PRINT 'Creating table backupfilegroup...'

  CREATE TABLE backupfilegroup
  (
  backup_set_id          INT           NOT NULL REFERENCES backupset(backup_set_id),
  name                   NVARCHAR(128) NOT NULL,
  filegroup_id           INT           NOT NULL,
  filegroup_guid         UNIQUEIDENTIFIER NULL,   
  type                   CHAR(2)       NOT NULL,
  type_desc              NVARCHAR(60)  NOT NULL,
  is_default             BIT           NOT NULL,
  is_readonly            BIT           NOT NULL,
  log_filegroup_guid     UNIQUEIDENTIFIER NULL
  PRIMARY KEY (backup_set_id, filegroup_id)
  )
END
go

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = 'backupfile')))
BEGIN
  PRINT ''
  PRINT 'Creating table backupfile...'

  CREATE TABLE backupfile
  (
  backup_set_id          INT           NOT NULL REFERENCES backupset(backup_set_id),
  first_family_number    TINYINT       NULL,     -- Family number & media number of he first media
  first_media_number     SMALLINT      NULL,     -- containing this file
  filegroup_name         NVARCHAR(128) NULL,
  page_size              INT           NULL,
  file_number            NUMERIC(10,0) NOT NULL,
  backed_up_page_count   NUMERIC(10,0) NULL,
  file_type              CHAR(1)       NULL,     -- database or log
  source_file_block_size NUMERIC(10,0) NULL,
  file_size              NUMERIC(20,0) NULL,
  logical_name           NVARCHAR(128) NULL,
  physical_drive         NVARCHAR(260) NULL,     -- Drive or partition name
  physical_name          NVARCHAR(260) NULL,     -- Remainder of physical (OS) filename
  state                  TINYINT       NULL,
  state_desc             NVARCHAR(64)  NULL,
  create_lsn             NUMERIC(25,0) NULL,
  drop_lsn               NUMERIC(25,0) NULL,
  file_guid              UNIQUEIDENTIFIER NULL,
  read_only_lsn          NUMERIC(25,0) NULL,
  read_write_lsn         NUMERIC(25,0) NULL,
  differential_base_lsn  NUMERIC(25,0) NULL,
  differential_base_guid UNIQUEIDENTIFIER NULL,
  backup_size            NUMERIC(20,0) NULL,
  filegroup_guid         UNIQUEIDENTIFIER NULL,
  is_readonly            BIT NULL,
  is_present             BIT NULL
  PRIMARY KEY (backup_set_id, file_number)
  )
END
ELSE
BEGIN
  IF NOT EXISTS (
    select * from msdb.dbo.syscolumns where name='state' and id =
        (select id from msdb.dbo.sysobjects where name='backupfile'))
  BEGIN
    -- we want NVARCHAR instead of VARCHAR
   ALTER TABLE backupfile ALTER COLUMN
      physical_drive         NVARCHAR(260) NULL
   ALTER TABLE backupfile ALTER COLUMN
      physical_name          NVARCHAR(260) NULL

    ALTER TABLE backupfile ADD
     state                  TINYINT       NULL,
     state_desc             NVARCHAR(64)  NULL,
     create_lsn             NUMERIC(25,0) NULL,
     drop_lsn               NUMERIC(25,0) NULL,
     file_guid              UNIQUEIDENTIFIER NULL,
     read_only_lsn          NUMERIC(25,0) NULL,
     read_write_lsn         NUMERIC(25,0) NULL,
     differential_base_lsn  NUMERIC(25,0) NULL,
     differential_base_guid UNIQUEIDENTIFIER NULL,
     backup_size            NUMERIC(20,0) NULL,
     filegroup_guid         UNIQUEIDENTIFIER NULL,
     is_readonly            BIT NULL,
     is_present             BIT NULL
  END
END
go

/**************************************************************/
/* RESTOREHISTORY - One row per restore operation.            */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = 'restorehistory')))
BEGIN
  PRINT ''
  PRINT 'Creating table restorehistory...'

  CREATE TABLE restorehistory
  (
  restore_history_id        INT           NOT NULL IDENTITY PRIMARY KEY,
  restore_date              DATETIME      NULL,
  destination_database_name NVARCHAR(128) NULL,
  user_name                 NVARCHAR(128) NULL,
  backup_set_id             INT           NOT NULL REFERENCES backupset(backup_set_id), -- The backup set restored
  restore_type              CHAR(1)       NULL,      -- Database, file, filegroup, log, verifyonly, ...

  -- Various options...
  replace                   BIT           NULL,      -- Replace(1), Noreplace(0)
  recovery                  BIT           NULL,      -- Recovery(1), Norecovery(0)
  restart                   BIT           NULL,      -- Restart(1), Norestart(0)
  stop_at                   DATETIME      NULL,
  device_count              TINYINT       NULL,      -- Can be less than number of media families
  stop_at_mark_name         NVARCHAR(128) NULL,
  stop_before               BIT           NULL
  )

  CREATE INDEX restorehistorybackupset ON restorehistory (backup_set_id)
END

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.syscolumns
                WHERE (name in ('stop_at_mark_name', 'stop_before'))
                AND (id = (SELECT id
                          FROM msdb.dbo.sysobjects
                          WHERE (name = 'restorehistory')))))
BEGIN
  IF NOT EXISTS (
    select * from msdb.dbo.syscolumns where name='stop_before' and id =
        (select id from msdb.dbo.sysobjects where name='restorehistory'))
  BEGIN
    PRINT ''
    PRINT 'Adding columns to table restorehistory...'

    ALTER TABLE restorehistory
      ADD
      stop_at_mark_name       NVARCHAR(128) NULL,
      stop_before             BIT           NULL
  END
END
go

/**************************************************************/
/* RESTOREFILE - One row per file restored.                   */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = 'restorefile')))
BEGIN
  PRINT ''
  PRINT 'Creating table restorefile...'

  CREATE TABLE restorefile
  (
  restore_history_id     INT           NOT NULL REFERENCES restorehistory(restore_history_id),
  file_number            NUMERIC(10,0) NULL,      -- Note: requires database to make unique
  destination_phys_drive NVARCHAR(260)  NULL,
  destination_phys_name  NVARCHAR(260)  NULL
  )
END
ELSE
BEGIN
  ALTER TABLE restorefile ALTER COLUMN
   destination_phys_drive NVARCHAR(260) NULL
  ALTER TABLE restorefile ALTER COLUMN
   destination_phys_name  NVARCHAR(260) NULL
END
go

/**************************************************************/
/* RESTOREFILEGROUP - One row per filegroup restored.         */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = 'restorefilegroup')))
BEGIN
  PRINT ''
  PRINT 'Creating table restorefilegroup...'

  CREATE TABLE restorefilegroup
  (
  restore_history_id INT           NOT NULL REFERENCES restorehistory(restore_history_id),
  filegroup_name     NVARCHAR(128) NULL
  )
END
go

/**************************************************************/
/* LOGMARKHISTORY - One row per log mark generated            */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = 'logmarkhistory')))
BEGIN
  PRINT ''
  PRINT 'Creating table logmarkhistory...'

  CREATE TABLE logmarkhistory
  (
  database_name     NVARCHAR(128)   NOT NULL,
  mark_name         NVARCHAR(128)   NOT NULL,
  description       NVARCHAR(255)   NULL,
  user_name         NVARCHAR(128)   NOT NULL,
  lsn               NUMERIC(25,0)   NOT NULL,
  mark_time         DATETIME        NOT NULL
  )

  CREATE INDEX logmarkhistory1 ON logmarkhistory (database_name, mark_name)

  CREATE INDEX logmarkhistory2 ON logmarkhistory (database_name, lsn)
END
go

IF (EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = 'trig_backupset_delete')
                  AND (OBJECTPROPERTY(id, 'IsTrigger') != 0)))
BEGIN
  DROP TRIGGER trig_backupset_delete
END
go

CREATE TRIGGER trig_backupset_delete ON msdb.dbo.backupset FOR DELETE AS
BEGIN
  DELETE FROM msdb.dbo.logmarkhistory from deleted
  WHERE (msdb.dbo.logmarkhistory.database_name = deleted.database_name)
    AND (msdb.dbo.logmarkhistory.lsn >= deleted.first_lsn)
    AND (msdb.dbo.logmarkhistory.lsn < deleted.last_lsn)
END
go

/**************************************************************/
/* suspect_pages */
/**************************************************************/

IF (EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = 'badpagehistory')))
   DROP TABLE badpagehistory
go               
IF (EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = 'suspect_page_table')))
   DROP TABLE suspect_page_table
go               

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = 'suspect_pages')))
BEGIN
  PRINT ''
  PRINT 'Creating table suspect_pages...'

  CREATE TABLE suspect_pages
  (
  database_id           INT          NOT NULL,
  file_id               INT          NOT NULL,
  page_id               bigint       NOT NULL, -- we only use unsigned 32bits
  event_type            INT          NOT NULL,
  error_count           INT          NOT NULL,
  last_update_date      DATETIME     NOT NULL DEFAULT GETDATE()
  )
END
go


/**************************************************************/
/**                                                          **/
/**           O B J E C T    P E R M I S S I O N S           **/
/**                                                          **/
/**************************************************************/

--------------------------------------------------------------
-- SQL Agent roles and procs
--------------------------------------------------------------
PRINT ''
PRINT 'Setting object permissions...'
go
-- Create the TargetServers role (for use by target servers when downloading jobs / uploading status)
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysusers
            WHERE (name = N'TargetServersRole')
              AND (issqlrole = 1)))
BEGIN
  -- If there are no members in the role, then drop and re-create it
  IF ((SELECT COUNT(*)
       FROM msdb.dbo.sysusers   su,
            msdb.dbo.sysmembers sm
       WHERE (su.uid = sm.groupuid)
         AND (su.name = N'TargetServersRole')
         AND (su.issqlrole = 1)) = 0)
  BEGIN
    EXECUTE msdb.dbo.sp_droprole @rolename = N'TargetServersRole'
    EXECUTE msdb.dbo.sp_addrole @rolename = N'TargetServersRole'
  END
END
ELSE
  EXECUTE msdb.dbo.sp_addrole @rolename = N'TargetServersRole'
  
  
 -- Create the SQLAgentUserRole role
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysusers
            WHERE (name = N'SQLAgentUserRole')
              AND (issqlrole = 1)))
BEGIN
  -- If there are no members in the role, then drop and re-create it
  IF ((SELECT COUNT(*)
       FROM msdb.dbo.sysusers   su,
            msdb.dbo.sysmembers sm
       WHERE (su.uid = sm.groupuid)
         AND (su.name = N'SQLAgentUserRole')
         AND (su.issqlrole = 1)) = 0)
  BEGIN
    EXECUTE msdb.dbo.sp_droprole @rolename = N'SQLAgentUserRole'
    EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentUserRole'
  END
END
ELSE
  EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentUserRole'  

-- Create the SQLAgentReaderRole role
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysusers
            WHERE (name = N'SQLAgentReaderRole')
              AND (issqlrole = 1)))
BEGIN
  -- If there are no members in the role, then drop and re-create it
  IF ((SELECT COUNT(*)
       FROM msdb.dbo.sysusers   su,
            msdb.dbo.sysmembers sm
       WHERE (su.uid = sm.groupuid)
         AND (su.name = N'SQLAgentReaderRole')
         AND (su.issqlrole = 1)) = 0)
  BEGIN
    EXECUTE msdb.dbo.sp_droprole @rolename = N'SQLAgentReaderRole'
    EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentReaderRole'
  END
END
ELSE
  EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentReaderRole'  

-- Create the SQLAgentOperatorRole role
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysusers
            WHERE (name = N'SQLAgentOperatorRole')
              AND (issqlrole = 1)))
BEGIN
  -- If there are no members in the role, then drop and re-create it
  IF ((SELECT COUNT(*)
       FROM msdb.dbo.sysusers   su,
            msdb.dbo.sysmembers sm
       WHERE (su.uid = sm.groupuid)
         AND (su.name = N'SQLAgentOperatorRole')
         AND (su.issqlrole = 1)) = 0)
  BEGIN
    EXECUTE msdb.dbo.sp_droprole @rolename = N'SQLAgentOperatorRole'
    EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentOperatorRole'
  END
END
ELSE
  EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentOperatorRole'  


-- Add roles to each other.
-- SQLAgentReaderRole is also SQLAgentUserRole
-- SQLAgentOperatorRole is also SQLAgentReaderRole and SQLAgentUserRole

EXECUTE sp_addrolemember @rolename = 'SQLAgentUserRole' , 
                   @membername = 'SQLAgentReaderRole' 

EXECUTE sp_addrolemember @rolename = 'SQLAgentReaderRole' , 
                   @membername = 'SQLAgentOperatorRole' 
go

GRANT EXECUTE ON sp_notify_operator          TO SQLAgentUserRole

-- Permissions a non-SA needs to create/update/delete a job
GRANT EXECUTE ON sp_get_sqlagent_properties  TO SQLAgentUserRole
GRANT EXECUTE ON sp_help_category            TO SQLAgentUserRole
GRANT EXECUTE ON sp_enum_sqlagent_subsystems TO SQLAgentUserRole
GRANT EXECUTE ON sp_add_jobserver            TO SQLAgentUserRole
GRANT EXECUTE ON sp_delete_jobserver         TO SQLAgentUserRole
GRANT SELECT  ON syscategories               TO SQLAgentUserRole

GRANT EXECUTE ON sp_help_jobhistory  TO SQLAgentUserRole
GRANT EXECUTE ON sp_help_jobhistory_full TO SQLAgentUserRole
GRANT EXECUTE ON sp_help_jobhistory_summary TO SQLAgentUserRole
GRANT EXECUTE ON sp_help_jobhistory_sem TO SQLAgentUserRole

GRANT EXECUTE ON sp_purge_jobhistory  TO SQLAgentOperatorRole

GRANT EXECUTE ON sp_add_jobstep    TO SQLAgentUserRole
GRANT EXECUTE ON sp_update_jobstep TO SQLAgentUserRole
GRANT EXECUTE ON sp_delete_jobstep TO SQLAgentUserRole
GRANT EXECUTE ON sp_help_jobstep   TO SQLAgentUserRole

GRANT EXECUTE ON sp_help_jobsteplog      TO SQLAgentUserRole
GRANT EXECUTE ON sp_delete_jobsteplog    TO SQLAgentUserRole

--Schedule related SP's
GRANT EXECUTE ON sp_add_schedule       TO SQLAgentUserRole
GRANT EXECUTE ON sp_update_schedule       TO SQLAgentUserRole
GRANT EXECUTE ON sp_delete_schedule       TO SQLAgentUserRole
GRANT EXECUTE ON sp_attach_schedule       TO SQLAgentUserRole
GRANT EXECUTE ON sp_detach_schedule       TO SQLAgentUserRole
GRANT EXECUTE ON sp_help_schedule         TO SQLAgentUserRole
GRANT EXECUTE ON sp_help_jobcount         TO SQLAgentUserRole
GRANT EXECUTE ON sp_help_jobs_in_schedule TO SQLAgentUserRole

GRANT EXECUTE ON sp_add_jobschedule    TO SQLAgentUserRole
GRANT EXECUTE ON sp_update_jobschedule TO SQLAgentUserRole
GRANT EXECUTE ON sp_delete_jobschedule TO SQLAgentUserRole
GRANT EXECUTE ON sp_help_jobschedule   TO SQLAgentUserRole

GRANT EXECUTE ON sp_add_job             TO SQLAgentUserRole
GRANT EXECUTE ON sp_update_job          TO SQLAgentUserRole
GRANT EXECUTE ON sp_delete_job          TO SQLAgentUserRole
GRANT EXECUTE ON sp_help_job            TO SQLAgentUserRole
GRANT EXECUTE ON sp_start_job           TO SQLAgentUserRole
GRANT EXECUTE ON sp_stop_job            TO SQLAgentUserRole

--alert spocs
GRANT EXECUTE ON sp_help_alert              TO SQLAgentOperatorRole

--proxy sprocs
GRANT EXECUTE ON sp_help_proxy           TO SQLAgentUserRole
GRANT EXECUTE ON sp_enum_login_for_proxy TO SQLAgentOperatorRole

--other
GRANT EXECUTE ON sp_help_jobserver        TO SQLAgentUserRole
GRANT EXECUTE ON sp_help_targetserver    TO SQLAgentOperatorRole
GRANT EXECUTE ON sp_help_notification    TO SQLAgentOperatorRole

GRANT EXECUTE ON sp_check_for_owned_jobs     TO SQLAgentUserRole
GRANT EXECUTE ON sp_check_for_owned_jobsteps TO SQLAgentUserRole
GRANT EXECUTE ON sp_get_jobstep_db_username  TO SQLAgentUserRole
GRANT EXECUTE ON sp_get_job_alerts           TO SQLAgentUserRole

GRANT EXECUTE ON sp_uniquetaskname TO SQLAgentUserRole
GRANT EXECUTE ON sp_addtask        TO SQLAgentUserRole
GRANT EXECUTE ON sp_droptask       TO SQLAgentUserRole

GRANT SELECT ON sysjobs_view                    TO SQLAgentUserRole
GRANT SELECT ON sysschedules_localserver_view   TO SQLAgentUserRole

GRANT SELECT  ON sysnotifications        TO SQLAgentOperatorRole
GRANT SELECT  ON sysoperators            TO SQLAgentOperatorRole
GRANT SELECT  ON sysalerts               TO SQLAgentOperatorRole
GRANT SELECT  ON sysalerts_performance_counters_view TO SQLAgentOperatorRole

REVOKE ALL ON systargetservers                 FROM PUBLIC
REVOKE ALL ON systargetservers_view            FROM PUBLIC
REVOKE ALL ON systargetservergroups            FROM PUBLIC
REVOKE ALL ON systargetservergroupmembers      FROM PUBLIC
REVOKE INSERT, UPDATE, DELETE ON syscategories FROM PUBLIC
REVOKE ALL ON sysalerts                        FROM PUBLIC
REVOKE ALL ON sysalerts_performance_counters_view FROM PUBLIC
REVOKE ALL ON sysoperators                     FROM PUBLIC
REVOKE ALL ON sysnotifications                 FROM PUBLIC

REVOKE ALL ON systargetservers                 FROM SQLAgentUserRole
REVOKE ALL ON systargetservers_view            FROM SQLAgentUserRole
REVOKE ALL ON systargetservergroups            FROM SQLAgentUserRole
REVOKE ALL ON systargetservergroupmembers      FROM SQLAgentUserRole
REVOKE INSERT, UPDATE, DELETE ON syscategories FROM SQLAgentUserRole

REVOKE ALL ON sysalerts                        FROM SQLAgentUserRole
REVOKE ALL ON sysalerts_performance_counters_view FROM SQLAgentUserRole
REVOKE ALL ON sysoperators                     FROM SQLAgentUserRole
REVOKE ALL ON sysnotifications                 FROM SQLAgentUserRole

--DENY TargetServerRole permission that would allow modifying of jobs
DENY ALL ON sp_add_jobserver     TO TargetServersRole
DENY ALL ON sp_delete_jobserver  TO TargetServersRole

DENY ALL ON sp_add_jobstep    TO TargetServersRole
DENY ALL ON sp_update_jobstep TO TargetServersRole
DENY ALL ON sp_delete_jobstep TO TargetServersRole

DENY ALL ON sp_add_jobschedule    TO TargetServersRole
DENY ALL ON sp_update_jobschedule TO TargetServersRole
DENY ALL ON sp_delete_jobschedule TO TargetServersRole

DENY ALL ON sp_add_job    TO TargetServersRole
DENY ALL ON sp_update_job TO TargetServersRole
DENY ALL ON sp_delete_job TO TargetServersRole
DENY ALL ON sp_start_job  TO TargetServersRole
DENY ALL ON sp_stop_job   TO TargetServersRole
DENY ALL ON sp_post_msx_operation       TO TargetServersRole

DENY ALL ON sp_addtask        TO TargetServersRole
DENY ALL ON sp_droptask       TO TargetServersRole


GRANT SELECT ON backupfile        TO PUBLIC
GRANT SELECT ON backupmediafamily TO PUBLIC
GRANT SELECT ON backupmediaset    TO PUBLIC
GRANT SELECT ON backupset         TO PUBLIC
GRANT SELECT ON restorehistory    TO PUBLIC
GRANT SELECT ON restorefile       TO PUBLIC
GRANT SELECT ON restorefilegroup  TO PUBLIC
GRANT SELECT ON logmarkhistory    TO PUBLIC
GRANT SELECT ON suspect_pages     TO PUBLIC

GRANT SELECT, UPDATE, DELETE ON sysdownloadlist               TO TargetServersRole
GRANT SELECT, UPDATE         ON sysjobservers                 TO TargetServersRole
GRANT SELECT, UPDATE         ON systargetservers              TO TargetServersRole
GRANT EXECUTE                ON sp_downloaded_row_limiter     TO TargetServersRole
GRANT SELECT                 ON sysjobs                       TO TargetServersRole
GRANT EXECUTE                ON sp_help_jobstep               TO TargetServersRole
GRANT EXECUTE                ON sp_help_jobschedule           TO TargetServersRole
GRANT EXECUTE                ON sp_sqlagent_refresh_job       TO TargetServersRole
GRANT EXECUTE                ON sp_sqlagent_probe_msx         TO TargetServersRole
GRANT EXECUTE                ON sp_sqlagent_check_msx_version TO TargetServersRole
GRANT EXECUTE                ON sp_enlist_tsx                 TO TargetServersRole
GRANT SELECT                 ON syssubsystems                 TO TargetServersRole

GRANT EXECUTE                ON sp_help_jobactivity           TO SQLAgentUserRole
GRANT EXECUTE                ON sp_help_operator              TO SQLAgentUserRole
                                                    
go

USE msdb                                                  
go                                                                          
                                                          
/**************************************************************/         
/**************************************************************/         
/* BEGIN DTS                                                  */         
/**************************************************************/         
/**************************************************************/

/**************************************************************/
/* DTS TABLES                                                 */
/* These are never dropped since we dropped MSDB itself if    */
/* this was an upgrade from pre-beta3, and we preserve beta3  */
/* packages.  However, we need to add the owner_sid column    */
/* if it's not there already, defaulting to sa ownership.     */
/**************************************************************/

/**************************************************************/
/* SYSDTSCATEGORIES                                           */
/**************************************************************/
IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysdtscategories')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysdtscategories...'
  CREATE TABLE sysdtscategories
  (
    name                   sysname             NOT NULL,
    description            NVARCHAR(1024)      NULL,
    id                     UNIQUEIDENTIFIER    NOT NULL,
    parentid               UNIQUEIDENTIFIER    NOT NULL,         --// IID_NULL if a predefined root category
    CONSTRAINT pk_dtscategories PRIMARY KEY (id),
    CONSTRAINT uq_dtscategories_name_parent UNIQUE (name, parentid)
  )

  /**************************************************************/
  /* PREDEFINED DTS CATEGORIES                                  */
  /**************************************************************/
  PRINT ''
  PRINT 'Adding predefined dts categories...'
  --// MUST BE IN SYNC with DTSPkg.h!
  --// These must be INSERTed explicitly as the IID_NULL parent does not exist.
  INSERT sysdtscategories VALUES (N'Local', 'DTS Packages stored on local SQL Server', 'B8C30000-A282-11D1-B7D9-00C04FB6EFD5', '00000000-0000-0000-0000-000000000000')
  INSERT sysdtscategories VALUES (N'Repository', 'DTS Packages stored on Repository', 'B8C30001-A282-11D1-B7D9-00C04FB6EFD5', '00000000-0000-0000-0000-000000000000')

  --// Default location for DTSPackage.SaveToSQLServer
  INSERT sysdtscategories VALUES (N'LocalDefault', 'Default local subcategory for DTS Packages', 'B8C30002-A282-11D1-B7D9-00C04FB6EFD5', 'B8C30000-A282-11D1-B7D9-00C04FB6EFD5')
END
GO

/**************************************************************/
/* SYSDTSPACKAGES                                             */
/**************************************************************/
IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysdtspackages')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysdtspackages...'
  CREATE TABLE sysdtspackages
  (
    name                   sysname             NOT NULL,                   --// May have multiple ids
    id                     UNIQUEIDENTIFIER    NOT NULL,                   --// May have multiple versionids
    versionid              UNIQUEIDENTIFIER    NOT NULL UNIQUE,
    description            NVARCHAR(1024)      NULL,
    categoryid             UNIQUEIDENTIFIER    NOT NULL REFERENCES sysdtscategories (id),
    createdate             DATETIME,
    owner                  sysname,
    packagedata            IMAGE,
    owner_sid              VARBINARY(85)       NOT NULL DEFAULT SUSER_SID(N'sa'),
    packagetype            int                 NOT NULL DEFAULT 0          --// DTSPkgType_Default
    CONSTRAINT pk_dtspackages PRIMARY KEY (id, versionid)
  )
END ELSE BEGIN
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.syscolumns
                  WHERE name = N'owner_sid' AND id = OBJECT_ID(N'sysdtspackages')))
  BEGIN
    PRINT ''
    PRINT 'Altering table sysdtspackages for owner_sid and packagetype...'
    ALTER TABLE sysdtspackages ADD owner_sid VARBINARY(85) NOT NULL DEFAULT SUSER_SID(N'sa'),
                                   packagetype int NOT NULL DEFAULT 0      --// DTSPkgType_Default
  END
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.syscolumns
                  WHERE name = N'packagetype' AND id = OBJECT_ID(N'sysdtspackages')))
  BEGIN
    PRINT ''
    PRINT 'Altering table sysdtspackages for packagetype...'
    ALTER TABLE sysdtspackages ADD packagetype int NOT NULL DEFAULT 0      --// DTSPkgType_Default
  END
END
GO


/***************************************************************/
/* Create SSIS roles										   */
/***************************************************************/
if not exists (select * from dbo.sysusers where [name] = N'db_ssisadmin' and [issqlrole] = 1)
BEGIN
EXEC sp_addrole N'db_ssisadmin' 
END
GO

if not exists (select * from dbo.sysusers where [name] = N'db_ssisltduser' and [issqlrole] = 1)
BEGIN
EXEC sp_addrole N'db_ssisltduser'
END
GO

if not exists (select * from dbo.sysusers where [name] = N'db_ssisoperator' and [issqlrole] = 1)
BEGIN
EXEC sp_addrole N'db_ssisoperator'
END
GO


/**************************************************************/
/* SP_MAKE_DTSPACKAGENAME                                     */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_get_dtsversion...'
go
IF OBJECT_ID(N'sp_get_dtsversion') IS NOT NULL
  DROP PROCEDURE sp_get_dtsversion
go
CREATE PROCEDURE sp_get_dtsversion
AS
  /* Values for this are same as @@microsoftversion */
  /* @@microsoftversion format is 0xaaiibbbb (aa = major, ii = minor, bb[bb] = build #) */
  DECLARE @i INT
  select @i = 0x08000000   /* Must be in hex! */

  /* Select the numeric value, and a conversion to make it readable */
  select N'Microsoft SQLDTS Scripts' = @i, N'Version' = convert(binary(4), @i)
GO
GRANT  EXECUTE  ON sp_get_dtsversion	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_get_dtsversion	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_get_dtsversion	TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_MAKE_DTSPACKAGENAME                                     */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_make_dtspackagename...'
go
IF OBJECT_ID(N'sp_make_dtspackagename') IS NOT NULL
  DROP PROCEDURE sp_make_dtspackagename
go
CREATE PROCEDURE sp_make_dtspackagename
  @categoryid UNIQUEIDENTIFIER,
  @name sysname OUTPUT,
  @flags int = 0
AS
  SET NOCOUNT ON

  --// If NULL catid, default to the LocalDefault category.
  IF (@categoryid IS NULL)
    SELECT @categoryid = 'B8C30002-A282-11d1-B7D9-00C04FB6EFD5'

  --// Validate category.  We'll generate a unique name within category.
  DECLARE @stringfromclsid NVARCHAR(200)
  IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @categoryid)
  BEGIN
    SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @categoryid)
    RAISERROR(14262, 16, 1, '@categoryid', @stringfromclsid)
    RETURN(1) -- Failure
  END

  --// Autogenerate the next name in our format.
  DECLARE @max sysname, @i INT, @spidchar NVARCHAR(20)

  --// Any logic we use may have collisions so let's get the max and wrap if we have to.
  --// @@spid is necessary for guaranteed uniqueness but makes it ugly so for now, don't use it.
  --// Note:  use only 9 characters as it makes the pattern match easier without overflowing.
  SELECT @i = 0, @spidchar = '_'               -- + LTRIM(STR(@@spid)) + '_'
  SELECT @max = MAX(name)
    FROM sysdtspackages
    WHERE name like 'DTS_' + @spidchar + '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
  IF @max IS NOT NULL
    SELECT @i = CONVERT(INT, SUBSTRING(@max, (DATALENGTH(N'DTS_' + @spidchar) / 2) + 1, 9))

  --// Wrap if needed.  Find a gap in the names.
  IF @i < 999999999
  BEGIN
    SELECT @i = @i + 1
  END ELSE BEGIN
    SELECT @i = 1
    DECLARE @existingname sysname
    DECLARE hC CURSOR LOCAL FOR SELECT name FROM sysdtspackages WHERE categoryid = @categoryid ORDER BY name FOR READ ONLY
    OPEN hC
    FETCH NEXT FROM hC INTO @existingname
    WHILE @@FETCH_STATUS = 0 AND @i < 999999999
    BEGIN
      SELECT @name = 'DTS_' + @spidchar + REPLICATE('0', 9 - DATALENGTH(LTRIM(STR(@i)))) + LTRIM(STR(@i))
      IF @existingname > @name
        BREAK
      SELECT @i = @i + 1
      FETCH NEXT FROM hC INTO @existingname
    END
    CLOSE hC
    DEALLOCATE hC
  END

  --// Set the name.
  SELECT @name = 'DTS_' + @spidchar + REPLICATE('0', 9 - DATALENGTH(LTRIM(STR(@i)))) + LTRIM(STR(@i))
  IF (@flags & 1) <> 0
    SELECT @name
GO
GRANT  EXECUTE  ON sp_make_dtspackagename	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_make_dtspackagename	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_make_dtspackagename	TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_ADD_DTSPACKAGE                                          */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_add_dtspackage...'
GO
IF OBJECT_ID(N'sp_add_dtspackage') IS NOT NULL
  DROP PROCEDURE sp_add_dtspackage
GO
CREATE PROCEDURE sp_add_dtspackage
  @name sysname,
  @id UNIQUEIDENTIFIER,
  @versionid UNIQUEIDENTIFIER,
  @description NVARCHAR(255),
  @categoryid UNIQUEIDENTIFIER,
  @owner sysname,
  @packagedata IMAGE,
  @packagetype int = 0     --// DTSPkgType_Default
AS
  SET NOCOUNT ON

  --// If NULL catid, default to the LocalDefault category.
  IF (@categoryid IS NULL)
    SELECT @categoryid = 'B8C30002-A282-11d1-B7D9-00C04FB6EFD5'

  --// Autogenerate name if it came in NULL.  If it didn't, the below will validate uniqueness.
  IF DATALENGTH(@name) = 0
    SELECT @name = NULL
  IF @name IS NULL
  BEGIN
    --// First see if they specified a new version based on id instead of name.
    if @id IS NOT NULL
    BEGIN
      SELECT @name = name
        FROM sysdtspackages WHERE @id = id
      IF @name IS NOT NULL
        GOTO AddPackage          -- OK, add with the existing name
    END

    --// Name not available, autogenerate one.
    exec sp_make_dtspackagename @categoryid, @name OUTPUT
    GOTO AddPackage
  END

  --// Verify name unique within category.  Allow a new versionid of the same name though.
  IF EXISTS (SELECT * FROM sysdtspackages WHERE name = @name AND categoryid = @categoryid AND id <> @id)
  BEGIN
    RAISERROR (14590, -1, -1, @name)
    RETURN(1) -- Failure
  END

  --// Verify that the same id is not getting a different name.
  IF EXISTS (SELECT * FROM sysdtspackages WHERE id = @id AND name <> @name)
  BEGIN
    DECLARE @stringfromclsid NVARCHAR(200)
    SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @id)
    RAISERROR (14597, -1, -1, @stringfromclsid)
    RETURN(1) -- Failure
  END

  --// Verify all versions of a package go in the same category.
  IF EXISTS (SELECT * FROM sysdtspackages WHERE id = @id AND categoryid <> @categoryid)
  BEGIN
    RAISERROR (14596, -1, -1, @name)
    RETURN(1) -- Failure
  END

  --// The real information is in the IMAGE; the rest is "documentary".
  --// Therefore, there is no need to verify anything.
  --// The REFERENCE in sysdtspackages will validate @categoryid.
AddPackage:

  --// We will use the original owner_sid for all new versions - all must have the same owner.
  --// New packages will get the current login's SID as owner_sid.
  DECLARE @owner_sid VARBINARY(85)
  SELECT @owner_sid = MIN(owner_sid) FROM sysdtspackages WHERE id = @id
  IF @@rowcount = 0 OR @owner_sid IS NULL
  BEGIN
    SELECT @owner_sid = SUSER_SID()
  END ELSE BEGIN
    --// Only the owner of DTS Package ''%s'' or a member of the sysadmin role may create new versions of it.
    IF (@owner_sid <> SUSER_SID() AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))
    BEGIN
      RAISERROR (14586, -1, -1, @name)
      RETURN(1) -- Failure
    END
  END

  --// Everything checks out, add the package or its new version.
  INSERT sysdtspackages (
    name,
    id,
    versionid,
    description,
    categoryid,
    createdate,
    owner,
    packagedata,
    owner_sid,
   packagetype
  ) VALUES (
    @name,
    @id,
    @versionid,
    @description,
    @categoryid,
    GETDATE(),
    @owner,
    @packagedata,
    @owner_sid,
   @packagetype
  )
  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_add_dtspackage	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_add_dtspackage	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_add_dtspackage	TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_DROP_DTSPACKAGE                                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_drop_dtspackage...'
go
IF OBJECT_ID(N'sp_drop_dtspackage') IS NOT NULL
  DROP PROCEDURE sp_drop_dtspackage
go
CREATE PROCEDURE sp_drop_dtspackage
  @name sysname,
  @id UNIQUEIDENTIFIER,
  @versionid UNIQUEIDENTIFIER
AS
  SET NOCOUNT ON

  --// Does the specified package (uniquely) exist?  Referencing by name only may not be unique.
  --// We do a bit of a hack here as SQL can't handle a DISTINCT clause with UNIQUEIDENTIFIER.
  --// @id will get the first id returned; if only name specified, see if there are more.
  DECLARE @findid UNIQUEIDENTIFIER
  SELECT @findid = id FROM sysdtspackages
    WHERE (@name IS NOT NULL OR @id IS NOT NULL OR @versionid IS NOT NULL)
      AND (@name IS NULL OR @name = name)
      AND (@id IS NULL OR @id = id)
      AND (@versionid IS NULL or @versionid = versionid)
  IF @@rowcount = 0
  BEGIN
    DECLARE @pkgnotfound NVARCHAR(200)
    DECLARE @dts_package_res NVARCHAR(100)
    SELECT @pkgnotfound = FORMATMESSAGE(14599) + ' = ''' + ISNULL(@name, FORMATMESSAGE(14589)) + '''; ' + FORMATMESSAGE(14588) + ' {'
    SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @id IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @id) END + '}.{'
    SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @versionid IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @versionid) END + '}'
    SELECT @dts_package_res = FORMATMESSAGE(14594)
    RAISERROR(14262, 16, 1, @dts_package_res, @pkgnotfound)
    RETURN(1) -- Failure
  END ELSE IF @name IS NOT NULL AND @id IS NULL AND @versionid IS NULL AND
      EXISTS (SELECT * FROM sysdtspackages WHERE name = @name AND id <> @findid)
  BEGIN
    RAISERROR(14595, -1, -1, @name)
    RETURN(1) -- Failure
  END
  SELECT @id = @findid

  --// Only the owner of DTS Package ''%s'' or a member of the sysadmin role may drop it or any of its versions.
  --// sp_add_dtspackage ensures that all versions have the same owner_sid.
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
  BEGIN
    IF (NOT EXISTS (SELECT * FROM sysdtspackages WHERE id = @id AND owner_sid = SUSER_SID()))
    BEGIN
      SELECT @name = name FROM sysdtspackages WHERE id = @id
      RAISERROR (14587, -1, -1, @name)
      RETURN(1) -- Failure
    END
  END

  --// If @versionid is NULL, drop all versions of name, else only the @versionid version.
  DELETE sysdtspackages
  WHERE id = @id
    AND (@versionid IS NULL OR @versionid = versionid)
  RETURN 0    -- SUCCESS
go
GRANT  EXECUTE  ON sp_drop_dtspackage	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_drop_dtspackage	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_drop_dtspackage	TO [db_ssisoperator]
go

/**************************************************************/
/* SP_REASSIGN_DTSPACKAGEOWNER                                */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_reassign_dtspackageowner...'
go
IF OBJECT_ID(N'sp_reassign_dtspackageowner') IS NOT NULL
  DROP PROCEDURE sp_reassign_dtspackageowner
go
CREATE PROCEDURE sp_reassign_dtspackageowner
  @name sysname,
  @id UNIQUEIDENTIFIER,
  @newloginname sysname
AS
  SET NOCOUNT ON

  --// First, is this a valid login?
  IF SUSER_SID(@newloginname) IS NULL
  BEGIN
    RAISERROR(14262, -1, -1, '@newloginname', @newloginname)
    RETURN(1) -- Failure
  END

  --// Does the specified package (uniquely) exist?  Referencing by name only may not be unique.
  --// We do a bit of a hack here as SQL can't handle a DISTINCT clause with UNIQUEIDENTIFIER.
  --// @id will get the first id returned; if only name specified, see if there are more.
  DECLARE @findid UNIQUEIDENTIFIER
  SELECT @findid = id FROM sysdtspackages
    WHERE (@name IS NOT NULL OR @id IS NOT NULL)
      AND (@name IS NULL OR @name = name)
      AND (@id IS NULL OR @id = id)
  IF @@rowcount = 0
  BEGIN
    DECLARE @pkgnotfound NVARCHAR(200)
    DECLARE @dts_package_res NVARCHAR(100)
    SELECT @pkgnotfound = FORMATMESSAGE(14599) + ' = ''' + ISNULL(@name, FORMATMESSAGE(14589)) + '''; ' + FORMATMESSAGE(14588) + ' {'
    SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @id IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @id) END + '}.{'
    SELECT @pkgnotfound = @pkgnotfound + FORMATMESSAGE(14589) + '}'
    SELECT @dts_package_res = FORMATMESSAGE(14594)
    RAISERROR(14262, 16, 1, @dts_package_res, @pkgnotfound)
    RETURN(1) -- Failure
  END ELSE IF @name IS NOT NULL AND @id IS NULL AND
      EXISTS (SELECT * FROM sysdtspackages WHERE name = @name AND id <> @findid)
  BEGIN
    RAISERROR(14595, -1, -1, @name)
    RETURN(1) -- Failure
  END
  SELECT @id = @findid

  --// Only the owner of DTS Package ''%s'' or a member of the sysadmin role may reassign its ownership.
  --// sp_add_dtspackage ensures that all versions have the same owner_sid.
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
  BEGIN
    IF (NOT EXISTS (SELECT * FROM sysdtspackages WHERE id = @id AND owner_sid = SUSER_SID()))
    BEGIN
      SELECT @name = name FROM sysdtspackages WHERE id = @id
      RAISERROR (14585, -1, -1, @name)
      RETURN(1) -- Failure
    END
  END

  --// Everything checks out, so reassign the owner.
  --// Note that @newloginname may be a sql server login rather than a network user,
  --// which is not quite the same as when a package is created.
  UPDATE sysdtspackages
    SET owner_sid = SUSER_SID(@newloginname),
       owner = @newloginname
   WHERE id = @id

  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_reassign_dtspackageowner	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_reassign_dtspackageowner	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_reassign_dtspackageowner	TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_GET_DTSPACKAGE                                          */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_get_dtspackage...'
go
IF OBJECT_ID(N'sp_get_dtspackage') IS NOT NULL
  DROP PROCEDURE sp_get_dtspackage
go
CREATE PROCEDURE sp_get_dtspackage
  @name sysname,
  @id UNIQUEIDENTIFIER,
  @versionid UNIQUEIDENTIFIER
AS
  SET NOCOUNT ON

  --// Does the specified package (uniquely) exist?  Dropping by name only may not be unique.
  --// We do a bit of a hack here as SQL can't handle a DISTINCT clause with UNIQUEIDENTIFIER.
  --// @id will get the first id returned; if only name specified, see if there are more.
  DECLARE @findid UNIQUEIDENTIFIER
  SELECT @findid = id FROM sysdtspackages
    WHERE (@name IS NOT NULL OR @id IS NOT NULL OR @versionid IS NOT NULL)
      AND (@name IS NULL OR @name = name)
      AND (@id IS NULL OR @id = id)
      AND (@versionid IS NULL or @versionid = versionid)
  IF @@rowcount = 0
  BEGIN
    DECLARE @pkgnotfound NVARCHAR(200)
    DECLARE @dts_package_res NVARCHAR(100)
    SELECT @pkgnotfound = FORMATMESSAGE(14599) + ' = ''' + ISNULL(@name, FORMATMESSAGE(14589)) + '''; ' + FORMATMESSAGE(14588) + ' {'
    SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @id IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @id) END + '}.{'
    SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @versionid IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @versionid) END + '}'
    SELECT @dts_package_res = FORMATMESSAGE(14594)
    RAISERROR(14262, 16, 1, @dts_package_res, @pkgnotfound)
    RETURN(1) -- Failure
  END ELSE IF @name IS NOT NULL AND @id IS NULL AND @versionid IS NULL AND
      EXISTS (SELECT * FROM sysdtspackages WHERE name = @name AND id <> @findid)
  BEGIN
    RAISERROR(14595, -1, -1, @name)
    RETURN(1) -- Failure
  END
  SELECT @id = @findid

  --// If @versionid is NULL, select all versions of name, else only the @versionid version.
  --// This must return the IMAGE as the rightmost column.
  SELECT
    name,
    id,
    versionid,
    description,
    createdate,
    owner,
    pkgsize = datalength(packagedata),
    packagedata,
    isowner = CASE WHEN (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 OR owner_sid = SUSER_SID()) THEN 1 ELSE 0 END,
   packagetype
  FROM sysdtspackages
  WHERE id = @id
    AND (@versionid IS NULL OR @versionid = versionid)
  ORDER BY name, createdate DESC

  RETURN 0    -- SUCCESS
go
GRANT  EXECUTE  ON sp_get_dtspackage	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_get_dtspackage	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_get_dtspackage	TO [db_ssisoperator]
go

/**************************************************************/
/* SP_REASSIGN_DTSPACKAGECATEGORY                             */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_reassign_dtspackagecategory...'
go
IF OBJECT_ID(N'sp_reassign_dtspackagecategory') IS NOT NULL
  DROP PROCEDURE sp_reassign_dtspackagecategory
go
CREATE PROCEDURE sp_reassign_dtspackagecategory
  @packageid UNIQUEIDENTIFIER,
  @categoryid UNIQUEIDENTIFIER
AS
  SET NOCOUNT ON

  --// Does the package exist?
  DECLARE @stringfromclsid NVARCHAR(200)
  IF NOT EXISTS (SELECT * from sysdtspackages WHERE id = @packageid)
  BEGIN
    SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @packageid)
    RAISERROR(14262, 16, 1, '@packageid', @stringfromclsid)
    RETURN(1) -- Failure
  END

  --// Does the category exist?
  IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @categoryid)
  BEGIN
    SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @categoryid)
    RAISERROR(14262, 16, 1, '@categoryid', @stringfromclsid)
    RETURN(1) -- Failure
  END

  UPDATE sysdtspackages SET categoryid = @categoryid WHERE id = @packageid
go

/**************************************************************/
/* SP_ENUM_DTSPACKAGES                                        */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_enum_dtspackages...'
go
IF OBJECT_ID(N'sp_enum_dtspackages') IS NOT NULL
  DROP PROCEDURE sp_enum_dtspackages
go
CREATE PROCEDURE sp_enum_dtspackages
  @name_like sysname = '%',
  @description_like NVARCHAR(255) = '%',
  @categoryid UNIQUEIDENTIFIER = NULL,
  @flags INT = 0,          --// Bitmask:  0x01 == return image data
                           --//           0x02 == recursive (packagenames and categorynames only)
                           --//           0x04 == all versions (default == only most-recent-versions)
                           --//           0x08 == all prior versions versions (not most-recent; requires @id)
  @id UNIQUEIDENTIFIER = NULL,    --// If non-NULL, enum versions of this package.
  @wanttype int = NULL            --// If non-NULL, enum only packages of the given type
AS
  IF (@flags & 0x02) <> 0
    GOTO DO_RECURSE

  --// Just return the non-IMAGE stuff - sp_get_dtspackage will return the
  --// actual dtspackage info.
  DECLARE @latestversiondate datetime
  SELECT @latestversiondate = NULL
  IF (@flags & 0x08 = 0x08)
  BEGIN
    SELECT @latestversiondate = MAX(t.createdate) FROM sysdtspackages t WHERE t.id = @id
    IF @latestversiondate IS NULL
    BEGIN
      DECLARE @pkgnotfound NVARCHAR(200)
      DECLARE @dts_package_res NVARCHAR(100)
      SELECT @pkgnotfound = FORMATMESSAGE(14599) + ' = ' + FORMATMESSAGE(14589) + '; ' + FORMATMESSAGE(14588) + ' {'
      SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @id IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @id) END + '}.{'
      SELECT @pkgnotfound = @pkgnotfound + FORMATMESSAGE(14589) + '}'
      SELECT @dts_package_res = FORMATMESSAGE(14594)
      RAISERROR(14262, 16, 1, @dts_package_res, @pkgnotfound)
      RETURN(1) -- Failure
    END
  END
  SELECT
    p.name,
    p.id,
    p.versionid,
    p.description,
    p.createdate,
    p.owner,
    size = datalength(p.packagedata),
    packagedata = CASE (@flags & 0x01) WHEN 0 THEN NULL ELSE p.packagedata END,
    isowner = CASE WHEN (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 OR p.owner_sid = SUSER_SID()) THEN 1 ELSE 0 END,
   p.packagetype
  FROM sysdtspackages p
  WHERE (@name_like IS NULL OR p.name LIKE @name_like)
    AND (@description_like IS NULL OR p.description LIKE @description_like)
    AND (@categoryid IS NULL OR p.categoryid = @categoryid)
    AND (@id is NULL OR p.id = @id)
    -- These filter by version
    AND ( (@flags & 0x08 = 0x08 AND p.createdate < @latestversiondate)
          OR ( (@flags & 0x04 = 0x04)
             OR (@flags & 0x08 = 0 AND p.createdate = (SELECT MAX(t.createdate) FROM sysdtspackages t WHERE t.id = p.id))
             )
        )
   AND (@wanttype is NULL or p.packagetype = @wanttype)
  ORDER BY id, createdate DESC
  RETURN 0    -- SUCCESS

  DO_RECURSE:
  DECLARE @packagesfound INT
  SELECT @packagesfound = 0

  --// Starting parent category.  If null, start at root.
  if (@categoryid IS NULL)
    SELECT @categoryid = '00000000-0000-0000-0000-000000000000'

  IF EXISTS (SELECT *
      FROM sysdtspackages p INNER JOIN sysdtscategories c ON p.categoryid = c.id
      WHERE p.categoryid = @categoryid
      AND (@name_like IS NULL OR p.name LIKE @name_like)
      AND (@description_like IS NULL OR p.description LIKE @description_like)
    )
    SELECT @packagesfound = 1

  IF (@packagesfound <> 0)
  BEGIN
    --// Identify the category and list its Packages.
    SELECT 'Level' = @@nestlevel, 'PackageName' = p.name, 'CategoryName' = c.name
        FROM sysdtspackages p INNER JOIN sysdtscategories c ON p.categoryid = c.id
        WHERE p.categoryid = @categoryid
        AND (@name_like IS NULL OR p.name LIKE @name_like)
        AND (@description_like IS NULL OR p.description LIKE @description_like)
  END

  --// List its subcategories' packages
  DECLARE @childid UNIQUEIDENTIFIER
  DECLARE hC CURSOR LOCAL FOR SELECT id FROM sysdtscategories c WHERE parentid = @categoryid ORDER BY c.name FOR READ ONLY
  OPEN hC
  FETCH NEXT FROM hC INTO @childid
  WHILE @@FETCH_STATUS = 0
  BEGIN
    EXECUTE sp_enum_dtspackages @name_like, @description_like, @childid, @flags
    FETCH NEXT FROM hC INTO @childid
  END
  CLOSE hC
  DEALLOCATE hC
  RETURN 0
go

GRANT  EXECUTE  ON sp_enum_dtspackages	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_enum_dtspackages	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_enum_dtspackages	TO [db_ssisoperator]
go

/**************************************************************/
/* SP_ADD_DTSCATEGORY                                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_add_dtscategory...'
go
IF OBJECT_ID(N'sp_add_dtscategory') IS NOT NULL
  DROP PROCEDURE sp_add_dtscategory
go
CREATE PROCEDURE sp_add_dtscategory
  @name sysname,
  @description NVARCHAR(1024),
  @id UNIQUEIDENTIFIER,
  @parentid UNIQUEIDENTIFIER
AS
  SET NOCOUNT ON

  --// If parentid is NULL, use 'Local'
  IF @parentid IS NULL
    SELECT @parentid = 'B8C30000-A282-11d1-B7D9-00C04FB6EFD5'

  --// First do some simple validation of "non-assert" cases.  UI should validate others and the table
  --// definitions will act as an "assert", but we check here (with a nice message) for user-error stuff
  --// it would be hard for UI to validate.
  IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @parentid)
  BEGIN
    DECLARE @stringfromclsid NVARCHAR(200)
    SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @parentid)
    RAISERROR(14262, 16, 1, '@parentid', @stringfromclsid)
    RETURN(1) -- Failure
  END

  IF EXISTS (SELECT * FROM sysdtscategories WHERE name = @name AND parentid = @parentid)
  BEGIN
    RAISERROR(14591, 16, -1, @name)
    RETURN(1) -- Failure
  END

  --// id uniqueness is ensured by the primary key.
  INSERT sysdtscategories (
    name,
    description,
    id,
    parentid
  ) VALUES (
    @name,
    @description,
    @id,
    @parentid
  )
  RETURN 0    -- SUCCESS
go

/**************************************************************/
/* SP_DROP_DTSCATEGORY                                        */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_drop_dtscategory...'
go
IF OBJECT_ID(N'sp_drop_dtscategory') IS NOT NULL
  DROP PROCEDURE sp_drop_dtscategory
go
CREATE PROCEDURE sp_drop_dtscategory
  @name_like sysname,
  @id UNIQUEIDENTIFIER = NULL,
  @flags INT = 0           --// Bitmask:  0x01 == recursive (drop all subcategories and packages)
AS
  SET NOCOUNT ON

  --// Temp table in case recursion is needed.
  DECLARE @recurse TABLE (id UNIQUEIDENTIFIER, passcount INT DEFAULT(0))

  IF (@name_like IS NOT NULL)
  BEGIN
    INSERT @recurse (id) SELECT id FROM sysdtscategories WHERE name LIKE @name_like
    IF @@rowcount = 0
    BEGIN
      RAISERROR(14262, 16, 1, '@name_like', @name_like)
      RETURN(1) -- Failure
    END
    IF @@rowcount > 1
    BEGIN
      RAISERROR(14592, 16, -1, @name_like)
      RETURN(1) -- Failure
    END
    SELECT @name_like = name, @id = id FROM sysdtscategories WHERE name LIKE @name_like
  END ELSE BEGIN
    --// Verify the id.  @name_like will be NULL if we're here so no need to initialize.
    SELECT @name_like = name FROM sysdtscategories WHERE id = @id
    IF @name_like IS NULL
    BEGIN
      DECLARE @stringfromclsid NVARCHAR(200)
      SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @id)
      RAISERROR(14262, 16, 1, '@id', @stringfromclsid)
      RETURN(1) -- Failure
    END
    INSERT @recurse (id) VALUES (@id)
  END

  --// We now have a unique category.

  --// Cannot drop the predefined categories (or the root, which already failed above as IID_NULL
  --// is not an id in sysdtscategories).  These will be at top level.
  IF @id IN (
    'B8C30000-A282-11d1-B7D9-00C04FB6EFD5'
    , 'B8C30001-A282-11d1-B7D9-00C04FB6EFD5'
    , 'B8C30002-A282-11d1-B7D9-00C04FB6EFD5'
  ) BEGIN
      RAISERROR(14598, 16, 1)
      RETURN(1) -- Failure
  END

  --// Check for subcategories or packages.
  IF EXISTS (SELECT * FROM sysdtspackages WHERE categoryid = @id)
             OR EXISTS (SELECT * FROM sysdtscategories WHERE parentid = @id)
  BEGIN
    --// It does.  Make sure recursion was requested.
    IF (@flags & 0x01 = 0)
    BEGIN
      RAISERROR(14593, 16, -1, @name_like)
      RETURN(1) -- Failure
    END

    --// Fill up @recurse.
    UPDATE @recurse SET passcount = 0
    WHILE (1 = 1)
    BEGIN
      UPDATE @recurse SET passcount = passcount + 1
      INSERT @recurse (id, passcount)
        SELECT c.id, 0 FROM sysdtscategories c INNER JOIN @recurse r ON c.parentid = r.id
        WHERE passcount = 1
      IF @@rowcount = 0
        BREAK
    END
  END

  DELETE sysdtspackages FROM sysdtspackages INNER JOIN @recurse r ON sysdtspackages.categoryid = r.id
  DELETE sysdtscategories FROM sysdtscategories INNER JOIN @recurse r ON sysdtscategories.id = r.id

  RETURN(0) -- SUCCESS
go

/**************************************************************/
/* SP_MODIFY_DTSCATEGORY                                      */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_modify_dtscategory...'
go
IF OBJECT_ID(N'sp_modify_dtscategory') IS NOT NULL
  DROP PROCEDURE sp_modify_dtscategory
go
CREATE PROCEDURE sp_modify_dtscategory
  @id UNIQUEIDENTIFIER,
  @name sysname,
  @description NVARCHAR(1024),
  @parentid UNIQUEIDENTIFIER
AS
  SET NOCOUNT ON

  --// Validate.
  DECLARE @stringfromclsid NVARCHAR(200)
  IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @id)
  BEGIN
    SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @id)
    RAISERROR(14262, 16, 1, '@id', @stringfromclsid)
    RETURN(1) -- Failure
  END

  IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @parentid)
  BEGIN
    SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @parentid)
    RAISERROR(14262, 16, 1, '@parentid', @stringfromclsid)
    RETURN(1) -- Failure
  END

  --// Check the name uniqueness within parent, but make sure the id is different (we may just be renaming
  --// without reassigning parentage).
  IF EXISTS (SELECT * FROM sysdtscategories WHERE name = @name AND parentid = @parentid and id <> @id)
  BEGIN
    RAISERROR(14591, 16, -1, @name)
    RETURN(1) -- Failure
  END

  UPDATE sysdtscategories SET name = @name, description = @description, parentid = @parentid
    WHERE id = @id
  RETURN(0) -- SUCCESS
go

/**************************************************************/
/* SP_ENUM_DTSCATEGORIES                                      */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_enum_dtscategories...'
go
IF OBJECT_ID(N'sp_enum_dtscategories') IS NOT NULL
  DROP PROCEDURE sp_enum_dtscategories
go
CREATE PROCEDURE sp_enum_dtscategories
  @parentid UNIQUEIDENTIFIER = NULL,
  @flags INT = 0           --// Bitmask:  0x01 == recursive (enum all subcategories; names only)
AS
  IF (@flags & 0x01) <> 0
    GOTO DO_RECURSE

  --// Go to the root if no parentid specified
  IF @parentid IS NULL
    SELECT @parentid = '00000000-0000-0000-0000-000000000000'

  --// 'No results' is valid here.
  SELECT name, description, id FROM sysdtscategories WHERE parentid = @parentid
    ORDER BY name
  RETURN 0

  DO_RECURSE:

  --// Identify the category.
  IF @@nestlevel <> 0
    SELECT 'Level' = @@nestlevel, name FROM sysdtscategories WHERE id = @parentid

  --// List its subcategories
  DECLARE @childid UNIQUEIDENTIFIER
  DECLARE hC CURSOR LOCAL FOR SELECT id FROM sysdtscategories c WHERE parentid = @parentid ORDER BY c.name FOR READ ONLY
  OPEN hC
  FETCH NEXT FROM hC INTO @childid
  WHILE @@FETCH_STATUS = 0
  BEGIN
    EXECUTE sp_enum_dtscategories @childid, @flags
    FETCH NEXT FROM hC INTO @childid
  END
  CLOSE hC
  DEALLOCATE hC
  RETURN 0
go

/**************************************************************/
/* Drop Beta1 DTS Logging objects                             */
/**************************************************************/

if OBJECT_ID('sysdtspackagestepslog') IS NOT NULL
BEGIN
   PRINT ''
   PRINT 'Dropping Beta1 logging tables and stored procedures...'
   DROP TABLE sysdtspackagestepslog
   IF OBJECT_ID('sysdtspackagelog') IS NOT NULL
      DROP TABLE sysdtspackagelog
   IF OBJECT_ID('sp_log_dtspackage') IS NOT NULL
      DROP PROCEDURE sp_log_dtspackage
   IF OBJECT_ID('sp_log_dtspackagesteps') IS NOT NULL
      DROP PROCEDURE sp_log_dtspackagesteps
END

/**************************************************************/
/* SYSDTSPACKAGELOG                                           */
/**************************************************************/
if OBJECT_ID('sysdtspackagelog') IS NULL
BEGIN
  PRINT ''
  PRINT 'Creating table sysdtspackagelog...'
  CREATE TABLE sysdtspackagelog
  (
    name          sysname        NOT NULL,
    description            NVARCHAR(1000)    NULL,
    id               UNIQUEIDENTIFIER  NOT NULL,
    versionid           UNIQUEIDENTIFIER  NOT NULL,
    lineagefull            UNIQUEIDENTIFIER  NOT NULL PRIMARY KEY,
    lineageshort        INT         NOT NULL,
    starttime           DATETIME    NOT NULL,
    endtime          DATETIME    NULL,
    elapsedtime            double precision  NULL,
    computer            sysname        NOT NULL,
    operator            sysname        NOT NULL,
    logdate          datetime    NOT NULL DEFAULT GETDATE(),
    errorcode           INT         NULL,
    errordescription       NVARCHAR(2000)    NULL
  )
END

/**************************************************************/
/* SYSDTSSTEPLOG                                              */
/**************************************************************/
if OBJECT_ID('sysdtssteplog') IS NULL
BEGIN
  PRINT ''
  PRINT 'Creating table sysdtssteplog...'
  CREATE TABLE sysdtssteplog
  (
    stepexecutionid        BIGINT IDENTITY (1, 1)  NOT NULL PRIMARY KEY,
    lineagefull            UNIQUEIDENTIFIER  NOT NULL 
               REFERENCES sysdtspackagelog(lineagefull)
               ON DELETE CASCADE,
    stepname            sysname        NOT NULL,
    stepexecstatus         int            NULL,
    stepexecresult         int            NULL,
    starttime           DATETIME    NOT NULL,
    endtime          DATETIME    NULL,
    elapsedtime            double precision  NULL,
    errorcode           INT         NULL,
    errordescription       NVARCHAR(2000)    NULL,
    progresscount       BIGINT         NULL
  )
END ELSE BEGIN
  IF (NOT EXISTS (SELECT *
                  FROM msdb.dbo.syscolumns
                  WHERE name = N'stepexecresult' AND id = OBJECT_ID(N'sysdtssteplog')))
  BEGIN
    PRINT ''
    PRINT 'Altering table sysdtssteplog...'
    ALTER TABLE sysdtssteplog ADD stepexecresult INT NULL DEFAULT 0
  END
END

/**************************************************************/
/* SYSDTSTASKLOG                                              */
/**************************************************************/
if OBJECT_ID('sysdtstasklog') IS NULL
BEGIN
  PRINT ''
  PRINT 'Creating table sysdtstasklog...'
  CREATE TABLE sysdtstasklog
  (
    stepexecutionid        BIGINT         NOT NULL
               REFERENCES sysdtssteplog (stepexecutionid)
               ON DELETE CASCADE,
    sequenceid          INT         NOT NULL,
    errorcode           INT         NOT NULL,
    description            NVARCHAR(2000)    NULL,
    PRIMARY KEY            (stepexecutionid, sequenceid)
  )
END

/**************************************************************/
/* SP_LOG_DTSPACKAGE_BEGIN                                    */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_log_dtspackage_begin...'
GO
IF OBJECT_ID(N'sp_log_dtspackage_begin') IS NOT NULL
  DROP PROCEDURE sp_log_dtspackage_begin
GO
CREATE PROCEDURE sp_log_dtspackage_begin
  @name        sysname,
  @description    NVARCHAR(1000),
  @id       UNIQUEIDENTIFIER,
  @versionid      UNIQUEIDENTIFIER,
  @lineagefull    UNIQUEIDENTIFIER,
  @lineageshort      INT,
  @starttime      DATETIME,
  @computer    sysname,
  @operator    sysname
AS
  SET NOCOUNT ON

  INSERT sysdtspackagelog (
    name,
    description,
    id,
    versionid,
    lineagefull,
    lineageshort,
    starttime,
    computer,
    operator
  ) VALUES (
    @name,
    @description,
    @id,
    @versionid,
    @lineagefull,
    @lineageshort,
    @starttime,
    @computer,
    @operator
  )
  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_log_dtspackage_begin	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_log_dtspackage_begin	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_log_dtspackage_begin	TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_LOG_DTSPACKAGE_END                                      */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_log_dtspackage_end...'
GO
IF OBJECT_ID(N'sp_log_dtspackage_end') IS NOT NULL
  DROP PROCEDURE sp_log_dtspackage_end
GO
CREATE PROCEDURE sp_log_dtspackage_end
  @lineagefull    UNIQUEIDENTIFIER,
  @endtime     DATETIME,
  @elapsedtime    double precision,
  @errorcode      INT,
  @errordescription  NVARCHAR(2000)
AS
  SET NOCOUNT ON

  --// Validate lineage.
  DECLARE @stringfromclsid NVARCHAR(200)
  IF NOT EXISTS (SELECT * FROM sysdtspackagelog WHERE lineagefull = @lineagefull)
  BEGIN
    SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @lineagefull)
    RAISERROR(14262, 16, 1, '@lineagefull', @stringfromclsid)
    RETURN(1) -- Failure
  END

  UPDATE sysdtspackagelog
    SET 
        endtime = @endtime,
        elapsedtime = @elapsedtime,
        errorcode = @errorcode,
        errordescription = @errordescription
    WHERE lineagefull = @lineagefull

  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_log_dtspackage_end	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_log_dtspackage_end	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_log_dtspackage_end	TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_LOG_DTSSTEP_BEGIN                                       */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_log_dtsstep_begin...'
GO
IF OBJECT_ID(N'sp_log_dtsstep_begin') IS NOT NULL
  DROP PROCEDURE sp_log_dtsstep_begin
GO
CREATE PROCEDURE sp_log_dtsstep_begin
  @lineagefull    UNIQUEIDENTIFIER,
  @stepname    sysname,
  @starttime      DATETIME
AS
  SET NOCOUNT ON

  --// Validate lineage.
  DECLARE @stringfromclsid NVARCHAR(200)
  IF NOT EXISTS (SELECT * FROM sysdtspackagelog WHERE lineagefull = @lineagefull)
  BEGIN
    SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @lineagefull)
    RAISERROR(14262, 16, 1, '@lineagefull', @stringfromclsid)
    RETURN(1) -- Failure
  END

  INSERT sysdtssteplog (
    lineagefull,
    stepname,
    starttime
  ) VALUES (
    @lineagefull,
    @stepname,
    @starttime
  )

  --// Return the @@identity for sp_log_dtstask and sp_logdtsstep_end
  SELECT @@IDENTITY

  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_log_dtsstep_begin	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_log_dtsstep_begin	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_log_dtsstep_begin	TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_LOG_DTSSTEP_END                                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_log_dtsstep_end...'
GO
IF OBJECT_ID(N'sp_log_dtsstep_end') IS NOT NULL
  DROP PROCEDURE sp_log_dtsstep_end
GO
CREATE PROCEDURE sp_log_dtsstep_end
  @stepexecutionid   BIGINT,
  @stepexecstatus int,
  @stepexecresult int,
  @endtime     DATETIME,
  @elapsedtime    double precision,
  @errorcode      INT,
  @errordescription  NVARCHAR(2000),
  @progresscount  BIGINT
AS
  SET NOCOUNT ON

  --// Validate @stepexecutionid.
  DECLARE @stringfromclsid NVARCHAR(200)
  IF NOT EXISTS (SELECT * FROM sysdtssteplog WHERE stepexecutionid = @stepexecutionid)
  BEGIN
    SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @stepexecutionid)
    RAISERROR(14262, 16, 1, '@stepexecutionid', @stringfromclsid)
    RETURN(1) -- Failure
  END

  UPDATE sysdtssteplog
    SET 
        stepexecstatus = @stepexecstatus,
        stepexecresult = @stepexecresult,
        endtime = @endtime,
        elapsedtime = @elapsedtime,
        errorcode = @errorcode,
        errordescription = @errordescription,
        progresscount = @progresscount
    WHERE stepexecutionid = @stepexecutionid

  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_log_dtsstep_end	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_log_dtsstep_end	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_log_dtsstep_end	TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_LOG_DTSTASK                                             */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_log_dtstask...'
GO
IF OBJECT_ID(N'sp_log_dtstask') IS NOT NULL
  DROP PROCEDURE sp_log_dtstask
GO
CREATE PROCEDURE sp_log_dtstask
  @stepexecutionid   BIGINT,
  @sequenceid     INT,
  @errorcode      INT,
  @description    NVARCHAR(2000)
AS
  SET NOCOUNT ON

  --// Validate @stepexecutionid.
  DECLARE @stringfromclsid NVARCHAR(200)
  IF NOT EXISTS (SELECT * FROM sysdtssteplog WHERE stepexecutionid = @stepexecutionid)
  BEGIN
    SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @stepexecutionid)
    RAISERROR(14262, 16, 1, '@stepexecutionid', @stringfromclsid)
    RETURN(1) -- Failure
  END

  INSERT sysdtstasklog (
    stepexecutionid,
    sequenceid,
    errorcode,
    description
  ) VALUES (
    @stepexecutionid,
    @sequenceid,
    @errorcode,
    @description
  )

  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_log_dtstask	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_log_dtstask	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_log_dtstask	TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_ENUM_DTSPACKAGELOG                                      */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_enum_dtspackagelog...'
GO
IF OBJECT_ID(N'sp_enum_dtspackagelog') IS NOT NULL
  DROP PROCEDURE sp_enum_dtspackagelog
GO
CREATE PROCEDURE sp_enum_dtspackagelog
  @name sysname,
  @flags INT = 0,                --// Bitmask:  0x01 == return only latest
  @id UNIQUEIDENTIFIER = NULL,      --// If non-NULL, use instead of @name.
  @versionid UNIQUEIDENTIFIER = NULL,  --// If non-NULL, use instead of @id or @name
  @lineagefull UNIQUEIDENTIFIER = NULL --// If non-NULL, use instead of @versionid or @id or @name
AS
  SET NOCOUNT ON

  --// This is used for realtime viewing of package logs, so don't error if no entries
  --// found, simply return an empty result set.
  SELECT
    p.name,
    p.description,
    p.id,
    p.versionid,
    p.lineagefull,
    p.lineageshort,
    p.starttime,
    p.endtime,
    p.elapsedtime,
    p.computer,
    p.operator,
    p.logdate,
    p.errorcode,
    p.errordescription
  FROM sysdtspackagelog p
  WHERE ((@lineagefull IS NULL OR p.lineagefull = @lineagefull)
      AND  (@versionid IS NULL OR p.versionid = @versionid)
      AND (@id IS NULL OR p.id = @id)
      AND (@name IS NULL OR p.name = @name))
    AND ((@flags & 0x01) = 0
      OR p.logdate = 
      (
        SELECT MAX(logdate) 
        FROM sysdtspackagelog d
        WHERE (d.id = p.id)
      )
     )
  ORDER BY logdate 

  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_enum_dtspackagelog	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_enum_dtspackagelog	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_enum_dtspackagelog	TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_ENUM_DTSSTEPLOG                                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_enum_dtssteplog...'
GO
IF OBJECT_ID(N'sp_enum_dtssteplog') IS NOT NULL
  DROP PROCEDURE sp_enum_dtssteplog
GO
CREATE PROCEDURE sp_enum_dtssteplog
  @lineagefull    UNIQUEIDENTIFIER = NULL,   -- all steps in this package execution
  @stepexecutionid   BIGINT = NULL
AS
  SET NOCOUNT ON

  --// This is used for realtime viewing of package logs, so don't error if no entries
  --// found, simply return an empty result set.
  --// This query must be restricted within a single package execution (lineage); it may
  --// be further restricted by stepexecutionid to a single step within that package execution.
  SELECT
    stepexecutionid,
    lineagefull,
    stepname,
    stepexecstatus,
    stepexecresult,
    starttime,
    endtime,
    elapsedtime,
    errorcode,
    errordescription,
    progresscount
  FROM sysdtssteplog
  WHERE (@lineagefull IS NULL OR lineagefull = @lineagefull)
    AND (@stepexecutionid IS NULL OR stepexecutionid = @stepexecutionid)
  ORDER BY stepexecutionid

  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_enum_dtssteplog	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_enum_dtssteplog	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_enum_dtssteplog	TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_ENUM_DTSTASKLOG                                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_enum_dtstasklog...'
GO
IF OBJECT_ID(N'sp_enum_dtstasklog') IS NOT NULL
  DROP PROCEDURE sp_enum_dtstasklog
GO
CREATE PROCEDURE sp_enum_dtstasklog
  @stepexecutionid   BIGINT,
  @sequenceid     INT = NULL
AS
  SET NOCOUNT ON

  --// This is used for realtime viewing of package logs, so don't error if no entries
  --// found, simply return an empty result set.
  --// This query must be restricted within a single step execution; it may
  --// be further restricted by stepexecutionid to a single record within that step execution.
  SELECT
    -- stepexecutionid,  -- this is always passed in so we don't need to return it.
    sequenceid,
    errorcode,
    description
  FROM sysdtstasklog
  WHERE (stepexecutionid IS NULL or stepexecutionid = @stepexecutionid)
    AND (@sequenceid IS NULL OR sequenceid = @sequenceid)
  ORDER BY sequenceid

  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_enum_dtstasklog	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_enum_dtstasklog	TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_enum_dtstasklog	TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_DUMP_DTSLOG_ALL                                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_dump_dtslog_all...'
GO
IF OBJECT_ID(N'sp_dump_dtslog_all') IS NOT NULL
  DROP PROCEDURE sp_dump_dtslog_all
GO
CREATE PROCEDURE sp_dump_dtslog_all
AS
  SET NOCOUNT ON

  --// sysadmin only.
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  DELETE sysdtspackagelog
  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_dump_dtslog_all		TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_dump_dtslog_all		TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_dump_dtslog_all		TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_DUMP_DTSPACKAGELOG                                      */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_dump_dtspackagelog...'
GO
IF OBJECT_ID(N'sp_dump_dtspackagelog') IS NOT NULL
  DROP PROCEDURE sp_dump_dtspackagelog
GO
CREATE PROCEDURE sp_dump_dtspackagelog
  @name sysname,
  @flags INT = 0,                --// Bitmask:  0x01 == preserve latest
  @id UNIQUEIDENTIFIER = NULL,      --// If non-NULL, use instead of @name.
  @versionid UNIQUEIDENTIFIER = NULL,  --// If non-NULL, use instead of @id or @name
  @lineagefull UNIQUEIDENTIFIER = NULL --// If non-NULL, use instead of @versionid or @id or @name
AS
  SET NOCOUNT ON

  --// sysadmin only.
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  --// Don't error if no entries found, as the desired result will be met.
  --// DELETE will CASCADE
  DELETE sysdtspackagelog
  FROM sysdtspackagelog p
  WHERE ((@lineagefull IS NULL OR p.lineagefull = @lineagefull)
      AND  (@versionid IS NULL OR p.versionid = @versionid)
      AND (@id IS NULL OR p.id = @id)
      AND (@name IS NULL OR p.name = @name))
    AND ((@flags & 0x01) = 0
      OR p.logdate < 
      (
        SELECT MAX(logdate) 
        FROM sysdtspackagelog d
        WHERE (d.id = p.id)
      )
     )

  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_dump_dtspackagelog	TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_dump_dtspackagelog    TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_dump_dtspackagelog    TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_DUMP_DTSSTEPLOG                                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_dump_dtssteplog...'
GO
IF OBJECT_ID(N'sp_dump_dtssteplog') IS NOT NULL
  DROP PROCEDURE sp_dump_dtssteplog
GO
CREATE PROCEDURE sp_dump_dtssteplog
  @lineagefull    UNIQUEIDENTIFIER = NULL,   -- all steps in this package execution
  @stepexecutionid   BIGINT = NULL
AS
  SET NOCOUNT ON

  --// sysadmin only.
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  --// Don't error if no entries found, as the desired result will be met.
  --// DELETE will CASCADE
  DELETE sysdtssteplog
  WHERE (@lineagefull IS NULL OR lineagefull = @lineagefull)
    AND (@stepexecutionid IS NULL OR stepexecutionid = @stepexecutionid)

  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_dump_dtssteplog   TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_dump_dtssteplog   TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_dump_dtssteplog   TO [db_ssisoperator]
GO

/**************************************************************/
/* SP_DUMP_DTSTASKLOG                                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_dump_dtstasklog...'
GO
IF OBJECT_ID(N'sp_dump_dtstasklog') IS NOT NULL
  DROP PROCEDURE sp_dump_dtstasklog
GO
CREATE PROCEDURE sp_dump_dtstasklog
  @stepexecutionid   BIGINT,
  @sequenceid     INT = NULL
AS
  SET NOCOUNT ON

  --// sysadmin only.
  IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
  BEGIN
    RAISERROR(15003, 16, 1, N'sysadmin')
    RETURN(1) -- Failure
  END

  --// Don't error if no entries found, as the desired result will be met.
  DELETE sysdtstasklog
  WHERE (stepexecutionid IS NULL or stepexecutionid = @stepexecutionid)
    AND (@sequenceid IS NULL OR sequenceid = @sequenceid)

  RETURN 0    -- SUCCESS
GO
GRANT  EXECUTE  ON sp_dump_dtstasklog   TO [db_ssisadmin]
GRANT  EXECUTE  ON sp_dump_dtstasklog   TO [db_ssisltduser]
GRANT  EXECUTE  ON sp_dump_dtstasklog   TO [db_ssisoperator]
GO


/**************************************************************/
/* SP_DTS_SECURE                                              */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_dts_secure ...'
GO
IF OBJECT_ID(N'sp_dts_secure') IS NOT NULL
	DROP PROCEDURE sp_dts_secure
GO
CREATE PROCEDURE sp_dts_secure
	@flag	INT
AS
	SET NOCOUNT ON

	--// sysadmin only.
	IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)
	BEGIN
		RAISERROR(15003, 16, 1, N'sysadmin')
		RETURN(1) -- Failure
	END
	
	IF(@flag <> 0)
	BEGIN
		REVOKE EXECUTE ON sp_get_dtsversion				FROM PUBLIC
		REVOKE EXECUTE ON sp_make_dtspackagename		FROM PUBLIC
		REVOKE EXECUTE ON sp_add_dtspackage				FROM PUBLIC
		REVOKE EXECUTE ON sp_drop_dtspackage			FROM PUBLIC
		REVOKE EXECUTE ON sp_reassign_dtspackageowner	FROM PUBLIC
		REVOKE EXECUTE ON sp_get_dtspackage				FROM PUBLIC
		REVOKE EXECUTE ON sp_enum_dtspackages			FROM PUBLIC
		REVOKE EXECUTE ON sp_log_dtspackage_begin		FROM PUBLIC
		REVOKE EXECUTE ON sp_log_dtspackage_end			FROM PUBLIC
		REVOKE EXECUTE ON sp_log_dtsstep_begin			FROM PUBLIC
		REVOKE EXECUTE ON sp_log_dtsstep_end			FROM PUBLIC
		REVOKE EXECUTE ON sp_log_dtstask				FROM PUBLIC
		REVOKE EXECUTE ON sp_enum_dtspackagelog			FROM PUBLIC
		REVOKE EXECUTE ON sp_enum_dtssteplog			FROM PUBLIC
		REVOKE EXECUTE ON sp_enum_dtstasklog			FROM PUBLIC
		REVOKE EXECUTE ON sp_dump_dtslog_all			FROM PUBLIC
		REVOKE EXECUTE ON sp_dump_dtspackagelog			FROM PUBLIC
		REVOKE EXECUTE ON sp_dump_dtssteplog			FROM PUBLIC
		REVOKE EXECUTE ON sp_dump_dtstasklog			FROM PUBLIC
	END
	ELSE
	BEGIN
		GRANT EXECUTE ON sp_get_dtsversion				TO PUBLIC
		GRANT EXECUTE ON sp_make_dtspackagename			TO PUBLIC
		GRANT EXECUTE ON sp_add_dtspackage				TO PUBLIC
		GRANT EXECUTE ON sp_drop_dtspackage				TO PUBLIC
		GRANT EXECUTE ON sp_reassign_dtspackageowner	TO PUBLIC
		GRANT EXECUTE ON sp_get_dtspackage				TO PUBLIC
		GRANT EXECUTE ON sp_enum_dtspackages			TO PUBLIC
		GRANT EXECUTE ON sp_log_dtspackage_begin		TO PUBLIC
		GRANT EXECUTE ON sp_log_dtspackage_end			TO PUBLIC
		GRANT EXECUTE ON sp_log_dtsstep_begin			TO PUBLIC
		GRANT EXECUTE ON sp_log_dtsstep_end				TO PUBLIC
		GRANT EXECUTE ON sp_log_dtstask					TO PUBLIC
		GRANT EXECUTE ON sp_enum_dtspackagelog			TO PUBLIC
		GRANT EXECUTE ON sp_enum_dtssteplog				TO PUBLIC
		GRANT EXECUTE ON sp_enum_dtstasklog				TO PUBLIC
		GRANT EXECUTE ON sp_dump_dtslog_all				TO PUBLIC
		GRANT EXECUTE ON sp_dump_dtspackagelog			TO PUBLIC
		GRANT EXECUTE ON sp_dump_dtssteplog				TO PUBLIC
		GRANT EXECUTE ON sp_dump_dtstasklog				TO PUBLIC
	END
	
	RETURN 0
GO



/**************************************************************/
/*                                                            */
/*                  D A T A B A S E    M A I L                */
/*                                                            */
/**************************************************************/

/**************************************************************/
/*                                                            */
/*  Database Mail Tables                                      */
/*                                                            */
/**************************************************************/

----------------------------------------------------------------
-- Database Mail: general configuraiton tables
----------------------------------------------------------------

IF (OBJECT_ID(N'dbo.sysmail_profile', 'U') IS NULL)
BEGIN
   PRINT ''
   PRINT 'Creating table sysmail_profile...'
   
   CREATE TABLE dbo.sysmail_profile
   (
      profile_id int identity not null,
      name sysname not null,
      description nvarchar(256) null,
      last_mod_datetime datetime not null default getdate(),
      last_mod_user sysname not null default suser_sname()

      CONSTRAINT [SYSMAIL_PROFILE_IDMustBeUnique] PRIMARY KEY(profile_id),
      CONSTRAINT [SYSMAIL_PROFILE_NameMustBeUnique] UNIQUE (name)
   )
END
ELSE
BEGIN
  ALTER TABLE dbo.sysmail_profile ALTER COLUMN description nvarchar(256) null
END
go

IF (OBJECT_ID(N'dbo.sysmail_principalprofile', 'U') IS NULL)
BEGIN
   PRINT ''
   PRINT 'Creating table sysmail_principalprofile...'
   
   CREATE TABLE dbo.sysmail_principalprofile
   (
      profile_id int not null references sysmail_profile(profile_id) on delete cascade,
      principal_sid varbinary(85) not null, -- sys.database_principals.sid
      is_default bit not null default 0,
      last_mod_datetime datetime not null default getdate(),
      last_mod_user sysname not null default suser_sname()

      CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique] PRIMARY KEY(profile_id,principal_sid),
   )
END
ELSE
BEGIN
   -- add principal_sid column
   IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='principal_sid' and id =
        (SELECT OBJECT_ID(N'dbo.sysmail_principalprofile', 'U')))
   BEGIN
      ALTER TABLE dbo.sysmail_principalprofile ADD principal_sid varbinary(85) not null default 0xFFFF
   END
END
go

IF (OBJECT_ID(N'dbo.sysmail_account', 'U') IS NULL)
BEGIN
   PRINT ''
   PRINT 'Creating table sysmail_account...'
   
   CREATE TABLE dbo.sysmail_account
   (
      account_id int identity not null,
      name sysname not null,
      description nvarchar(256) null,
      email_address nvarchar(128) not null,
      display_name nvarchar(128) null,
      replyto_address nvarchar(128) null,      
      last_mod_datetime datetime not null default getdate(),
      last_mod_user sysname not null default suser_sname()

      CONSTRAINT [SYSMAIL_ACCOUNT_IDMustBeUnique] PRIMARY KEY(account_id),
      CONSTRAINT [SYSMAIL_ACCOUNT_NameMustBeUnique] UNIQUE (name)
   )
END
ELSE
BEGIN
  ALTER TABLE dbo.sysmail_account ALTER COLUMN description nvarchar(256) null
  ALTER TABLE dbo.sysmail_account ALTER COLUMN display_name nvarchar(128) null
  ALTER TABLE dbo.sysmail_account ALTER COLUMN replyto_address nvarchar(128) null
END
go

IF (OBJECT_ID(N'dbo.sysmail_profileaccount', 'U') IS NULL)
BEGIN
   PRINT ''
   PRINT 'Creating table sysmail_profileaccount...'

   CREATE TABLE dbo.sysmail_profileaccount
   (
      profile_id int not null,
      account_id int not null references sysmail_account(account_id) on delete cascade,
      sequence_number int null,
      last_mod_datetime datetime not null default getdate(),
      last_mod_user sysname not null default suser_sname()

      CONSTRAINT [SYSMAIL_ACCOUNT_ProfileAccountMustBeUnique] PRIMARY KEY(profile_id,account_id)
   )  
END
ELSE
BEGIN
  ALTER TABLE dbo.sysmail_profileaccount ALTER COLUMN sequence_number int null
END
go

IF (OBJECT_ID(N'dbo.sysmail_servertype', 'U') IS NULL)
BEGIN
   PRINT ''
   PRINT 'Creating table sysmail_servertype...'

   CREATE TABLE dbo.sysmail_servertype
   (
      servertype sysname not null,
      is_incoming bit not null default 0,
      is_outgoing bit not null default 1,
      last_mod_datetime datetime not null default getdate(),
      last_mod_user sysname not null default suser_sname()

      CONSTRAINT [SYSMAIL_SERVERTYPE_TypeMustBeUnique] PRIMARY KEY(servertype),
   )
END
go

IF (OBJECT_ID(N'dbo.sysmail_server', 'U') IS NULL)
BEGIN
   PRINT ''
   PRINT 'Creating table sysmail_server...'
   
   CREATE TABLE dbo.sysmail_server
   (
      account_id int not null references sysmail_account(account_id) on delete cascade,
      servertype sysname not null references sysmail_servertype(servertype),
      servername sysname not null,
      port int not null default 25,
      username nvarchar(128) null,
      credential_id int null,
      use_default_credentials bit not null default 0,
      enable_ssl bit not null default 0,
      flags int not null default 0,
      timeout int NULL,
      last_mod_datetime datetime not null default getdate(),
      last_mod_user sysname not null default suser_sname()

      CONSTRAINT [SYSMAIL_ACCOUNT_AccountServerTypeMustBeUnique] PRIMARY KEY(account_id,servertype)
   )
END
ELSE -- check if we need to add missing columns
BEGIN
   IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='use_default_credentials' and id =
        (SELECT OBJECT_ID(N'dbo.sysmail_server', 'U')))
   BEGIN
      ALTER TABLE dbo.sysmail_server ADD use_default_credentials bit not null default 0
   END

   IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='enable_ssl' and id =
        (SELECT OBJECT_ID(N'dbo.sysmail_server', 'U')))
   BEGIN
      ALTER TABLE dbo.sysmail_server ADD enable_ssl bit not null default 0
   END

   IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='flags' and id =
        (SELECT OBJECT_ID(N'dbo.sysmail_server', 'U')))
   BEGIN
      ALTER TABLE dbo.sysmail_server ADD flags int not null default 0
   END
   
   IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='timeout' and id =
        (SELECT OBJECT_ID(N'dbo.sysmail_server', 'U')))
   BEGIN
      ALTER TABLE dbo.sysmail_server ADD timeout int NULL
   END
END
go

IF (OBJECT_ID(N'dbo.sysmail_configuration', 'U') IS NULL)
BEGIN
   PRINT ''
   PRINT 'Creating table sysmail_configuration...'

   CREATE TABLE dbo.sysmail_configuration
   (
      paramname nvarchar(256) not null,
      paramvalue nvarchar(256) null,
      description nvarchar(256) null,
      last_mod_datetime datetime not null default getdate(),
      last_mod_user sysname not null default suser_sname()

      CONSTRAINT [SYSMAIL_CONFIGURATION_ParamnameMustBeUnique] PRIMARY KEY(paramname)
   )
END
ELSE
BEGIN
  ALTER TABLE dbo.sysmail_configuration ALTER COLUMN paramvalue nvarchar(256) null
  ALTER TABLE dbo.sysmail_configuration ALTER COLUMN description nvarchar(256) null
END
go

-- populate default configuration settings
DECLARE @description NVARCHAR(256)

SELECT @description = FORMATMESSAGE(14642)
IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'DefaultAttachmentEncoding')
  INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'DefaultAttachmentEncoding', N'MIME', @description)
ELSE
  UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'DefaultAttachmentEncoding'

-- maximum size of an Database Mail atachement 1MB
SELECT @description = FORMATMESSAGE(14644)
IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'MaxFileSize')
  INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'MaxFileSize', N'1000000', @description)
ELSE
  UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'MaxFileSize'

SELECT @description = FORMATMESSAGE(14645)
IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'ProhibitedExtensions')
  INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'ProhibitedExtensions', N'exe,dll,vbs,js', @description)
ELSE
  UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'ProhibitedExtensions'

SELECT @description = FORMATMESSAGE(14646)
IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'AccountRetryAttempts')
  INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'AccountRetryAttempts', N'1', @description)
ELSE
  UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'AccountRetryAttempts'

SELECT @description = FORMATMESSAGE(14647)
IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'AccountRetryDelay')
  INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'AccountRetryDelay', N'60', @description)
ELSE
  UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'AccountRetryDelay'

SELECT @description = FORMATMESSAGE(14648)
IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'DatabaseMailExeMinimumLifeTime')
  INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'DatabaseMailExeMinimumLifeTime', N'600', @description)
ELSE
  UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'DatabaseMailExeMinimumLifeTime'

-- component logging level: normal - 1, extended - 2 (default), verbose - 3
SELECT @description = FORMATMESSAGE(14664)
IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'LoggingLevel')
  INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'LoggingLevel', N'2', @description)
ELSE
  UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'LoggingLevel'

go

----------------------------------------------------------------
-- Database Mail: mail host database specific tables
----------------------------------------------------------------

-----------------------------------------------------------
-- TABLE sysmail_mailitems      
-----------------------------------------------------------
-- sysmail_mailitems      : Contains one row for each mail sent using sp_send_dbmail.
--                          Contains mails that are waiting to be sent and the sent mail.
--                          sent_status determines its status
--
-- mailitem_id            : Id for the row. Auto-generated.
-- profile_id             : ID of profile to use to send the mail.
-- recipients             : People on the To list.
-- copy_recipients        : People on the Cc list.
-- blind_copy_recipients  : People on the Bcc list.
-- subject                : Subject of the email.
-- body                   : Body of the email.
-- body_format         : Body format. 0 (Text), 1(Html)                        
-- importance             : Importance of the email:
--                          0(Low), 1(Normal), 2(High).
-- sensitivity            : Sensitivity of the email:
--                          0(Normal), 1(Personal), 2(Private), 3(Confidential).
-- attachment_encoding    : Encoding to use for mail and attachments: 
--                          0(MIME), 1(UUEncode), 2(BINHEX), 3(S/MIME).
-- query                  : SQL query that was executed in this mail 
-- execute_query_database : The database to execute the query in  
-- attach_query_result_as_file : Option for attaching the query result 
--                               as a file instead of in the mail body
-- query_result_header    : Option for including query result column headers
-- query_result_width     : The query result overall width in characters
-- query_result_separator : The query result column separaror character
-- exclude_query_output   : Option for supressing query output being returned to
--                          the client that is sending the mail
-- append_query_error     : Option for appending query error messages to the mail item
-- send_request_date   : Date this mail item was created
-- send_request_user      : The user that created this mail item
-- sent_account_id        : The account_id that was used to send this mail item 
-- sent_status            : The current status of the mail item. 
--                        : 0(PendingSend), 1(SendSuccessful), 2(SendFailed), 3(AttemptingSendRetry)
-- sent_date              : Date the mail item was sent or failed to be sent
-- from_adress            : Optional override of the from header.
-- reply_to               : Optional setting of the reply-to header.
-----------------------------------------------------------
IF(OBJECT_ID('dbo.sysmail_mailitems', 'U') IS NULL)
BEGIN
  PRINT 'Creating TABLE sysmail_mailitems'

    CREATE TABLE sysmail_mailitems
    (
       mailitem_id                 INT                     IDENTITY(1,1) NOT NULL,
       profile_id                  INT                     NOT NULL,        
       recipients                  VARCHAR(MAX)    NULL, 
       copy_recipients             VARCHAR(MAX)    NULL,
       blind_copy_recipients       VARCHAR(MAX)    NULL,
       subject                     NVARCHAR(255)      NULL,
       from_address                VARCHAR(MAX)    NULL,
       reply_to                    VARCHAR(MAX)    NULL,
       body                        NVARCHAR(MAX)      NULL, 
       body_format                 VARCHAR(20)             NULL, 
       importance                  VARCHAR(6)              NULL,
       sensitivity                 VARCHAR(12)             NULL,
       file_attachments            NVARCHAR(MAX)      NULL,  
       attachment_encoding         VARCHAR(20)             NULL,
       query                       NVARCHAR(MAX)      NULL,
       execute_query_database      sysname                 NULL,  
       attach_query_result_as_file BIT                     NULL,
       query_result_header         BIT        NULL,
       query_result_width          INT                     NULL,  
       query_result_separator      CHAR(1)         NULL,
       exclude_query_output        BIT       NULL,
       append_query_error          BIT       NULL,
       send_request_date           DATETIME     NOT NULL DEFAULT GETDATE(),
       send_request_user           sysname              NOT NULL DEFAULT SUSER_SNAME(),
       sent_account_id             INT       NULL,
       sent_status                 TINYINT         NULL     DEFAULT 0,
       sent_date                   DATETIME     NULL,
       last_mod_date               DATETIME     NOT NULL DEFAULT GETDATE(),
       last_mod_user               sysname              NOT NULL DEFAULT SUSER_SNAME(),

       CONSTRAINT [sysmail_mailitems_id_MustBeUnique] PRIMARY KEY(mailitem_id),
       CONSTRAINT [sysmail_OutMailMustHaveAtleastOneRecipient]
                            CHECK (NOT (recipients IS NULL AND copy_recipients IS NULL AND blind_copy_recipients IS NULL)),
       CONSTRAINT [sysmail_OutMailRecipientCannotBeEmpty]           
                            CHECK (DATALENGTH(ISNULL(recipients, ''))      + 
            DATALENGTH(ISNULL(copy_recipients, '')) +  
            DATALENGTH(ISNULL(blind_copy_recipients, '')) <> 0),
       CONSTRAINT [sysmail_OutMailAttachmentEncodingMustBeValid]
             CHECK (attachment_encoding IN ('MIME', 'S/MIME', 'BINHEX', 'UUENCODE')),       
       CONSTRAINT [sysmail_OutMailImportanceMustBeValid]
             CHECK (importance IN ('LOW', 'NORMAL', 'HIGH')),
       CONSTRAINT [sysmail_OutMailSensitivityMustBeValid]           
             CHECK (sensitivity IN('NORMAL', 'PERSONAL', 'PRIVATE', 'CONFIDENTIAL'))
    )
END
ELSE
BEGIN
    -- handle schema upgrade for sysmail_mailitems table
    IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='profile_id' and id =
        (SELECT OBJECT_ID(N'dbo.sysmail_mailitems', 'U')))
    BEGIN
       ALTER TABLE dbo.sysmail_mailitems ADD profile_id INT NOT NULL DEFAULT -1
    END

    IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='from_address' and id =
        (SELECT OBJECT_ID(N'dbo.sysmail_mailitems', 'U')))
    BEGIN
       ALTER TABLE dbo.sysmail_mailitems ADD from_address VARCHAR(MAX) NULL
       ALTER TABLE dbo.sysmail_mailitems ADD reply_to VARCHAR(MAX) NULL
    END
END
GO

/**************************************************************/
/* sysmail_allitems                                               */
/**************************************************************/

PRINT ''
PRINT 'Creating view sysmail_allitems...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sysmail_allitems')
              AND (type = 'V')))
  DROP VIEW sysmail_allitems
go

CREATE VIEW sysmail_allitems
AS
SELECT mailitem_id,
       profile_id,
       recipients,
       copy_recipients,
       blind_copy_recipients,
       subject,
       body,
       body_format,
       importance,
       sensitivity,
       file_attachments,
       attachment_encoding,
       query,
       execute_query_database,
       attach_query_result_as_file,
       query_result_header,
       query_result_width,
       query_result_separator,
       exclude_query_output,
       append_query_error,
       send_request_date,
       send_request_user,
       sent_account_id,
       CASE sent_status 
          WHEN 0 THEN 'unsent' 
          WHEN 1 THEN 'sent' 
          WHEN 3 THEN 'retrying' 
          ELSE 'failed' 
       END as sent_status,
       sent_date,
       last_mod_date,
       last_mod_user
FROM msdb.dbo.sysmail_mailitems
WHERE (send_request_user = SUSER_SNAME()) OR (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)

GO

/**************************************************************/
/* sysmail_sentitems                                               */
/**************************************************************/

PRINT ''
PRINT 'Creating view sysmail_sentitems...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sysmail_sentitems')
              AND (type = 'V')))
  DROP VIEW sysmail_sentitems
go

CREATE VIEW sysmail_sentitems
AS
SELECT * FROM msdb.dbo.sysmail_allitems WHERE sent_status = 'sent'

go

/**************************************************************/
/* sysmail_unsentitems                                               */
/**************************************************************/

PRINT ''
PRINT 'Creating view sysmail_unsentitems...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sysmail_unsentitems')
              AND (type = 'V')))
  DROP VIEW sysmail_unsentitems
go

CREATE VIEW sysmail_unsentitems
AS
SELECT * FROM msdb.dbo.sysmail_allitems WHERE (sent_status = 'unsent' OR sent_status = 'retrying')

go

/**************************************************************/
/* sysmail_faileditems                                               */
/**************************************************************/

PRINT ''
PRINT 'Creating view sysmail_faileditems...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sysmail_faileditems')
              AND (type = 'V')))
  DROP VIEW sysmail_faileditems
go

CREATE VIEW sysmail_faileditems
AS
SELECT * FROM msdb.dbo.sysmail_allitems WHERE sent_status = 'failed'

go

-----------------------------------------------------------
-- procedure sysmail_delete_mailitems_sp
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sysmail_delete_mailitems_sp', 'P') IS NULL
    DROP PROCEDURE dbo.sysmail_delete_mailitems_sp
GO
-----
PRINT 'Creating sysmail_delete_mailitems_sp'
-----
GO
CREATE PROCEDURE sysmail_delete_mailitems_sp
   @sent_before DATETIME   = NULL, -- sent before
   @sent_status varchar(8)   = NULL -- sent status
AS
BEGIN

   SET @sent_status       = LTRIM(RTRIM(@sent_status))
   IF @sent_status           = '' SET @sent_status = NULL

   IF ( (@sent_status IS NOT NULL) AND
         (LOWER(@sent_status collate SQL_Latin1_General_CP1_CS_AS) NOT IN ( 'unsent', 'sent', 'failed', 'retrying') ) )
   BEGIN
      RAISERROR(14266, -1, -1, '@sent_status', 'unsent, sent, failed, retrying')
      RETURN(1) -- Failure
   END

   IF ( @sent_before IS NULL AND @sent_status IS NULL )
   BEGIN
      RAISERROR(14608, -1, -1, '@sent_before', '@sent_status')  
      RETURN(1) -- Failure
   END

   DELETE FROM msdb.dbo.sysmail_allitems 
   WHERE 
        ((@sent_before IS NULL) OR ( send_request_date < @sent_before))
   AND ((@sent_status IS NULL) OR (sent_status = @sent_status))

   DECLARE @localmessage nvarchar(255)
    SET @localmessage = FORMATMESSAGE(14665, SUSER_SNAME(), @@ROWCOUNT)
    exec msdb.dbo.sysmail_logmailevent_sp @event_type=1, @description=@localmessage

END
GO

-----------------------------------------------------------
-- TABLE sysmail_attachments
-----------------------------------------------------------
-- sysmail_attachments  : Contains mail item attachments
--
-- attachment_id        : Id for the row. Auto-generated
-- mailitem_id          : Optional key to the mail items that this entry is a about
-- filename             : The filename of the attachment
-- filesize             : Size of the file
-- encoded_attachment   : The file data encoded in base64
----------------------------------------------------------------
IF (OBJECT_ID('dbo.sysmail_attachments', 'U') IS NULL)
BEGIN
  PRINT 'Creating TABLE sysmail_attachments'

    CREATE TABLE sysmail_attachments
    (
        attachment_id       INT   IDENTITY(1, 1) NOT NULL,
        mailitem_id      INT  NOT NULL CONSTRAINT 
         FK_sysmail_mailitems_mailitem_id 
                    FOREIGN KEY (mailitem_id) REFERENCES sysmail_mailitems(mailitem_id)  ON DELETE CASCADE,
        filename            NVARCHAR(260)       NOT NULL,
        filesize            INT                 NOT NULL,
        attachment          VARBINARY(MAX)      NULL,
        last_mod_date       DATETIME            NOT NULL DEFAULT GETDATE(),
        last_mod_user       sysname             NOT NULL DEFAULT SUSER_SNAME()
    )
END
ELSE
BEGIN
   BEGIN TRAN

   -- remove any old constraint
   declare @fkName sysname
   DECLARE @sql NVARCHAR(4000)
   SELECT @fkName = cstr.name
      FROM
         sys.tables AS tbl
      INNER JOIN sys.foreign_keys AS cstr ON cstr.parent_object_id=tbl.object_id
      LEFT OUTER JOIN sys.indexes AS ki ON ki.index_id = cstr.key_index_id and ki.object_id = cstr.referenced_object_id
      INNER JOIN sys.tables rtbl ON rtbl.object_id = cstr.referenced_object_id
      WHERE
      tbl.name=N'sysmail_attachments' 

   IF (@fkName IS NOT NULL)
   BEGIN
      select @sql = N'ALTER TABLE sysmail_attachments DROP CONSTRAINT ' + QUOTENAME(@fkName)
      EXEC (@sql)
   END
   
   ALTER TABLE sysmail_attachments ADD CONSTRAINT FK_sysmail_mailitems_mailitem_id 
                    FOREIGN KEY (mailitem_id) REFERENCES sysmail_mailitems(mailitem_id) ON DELETE CASCADE
   COMMIT
END
GO


/**************************************************************/
/* sysmail_mailattachments                                */
/**************************************************************/

PRINT ''
PRINT 'Creating view sysmail_mailattachments...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sysmail_mailattachments')
              AND (type = 'V')))
  DROP VIEW sysmail_mailattachments
go

CREATE VIEW sysmail_mailattachments
AS
SELECT attachment_id,
       sa.mailitem_id,
       filename,
       filesize,
       attachment,
       sa.last_mod_date,
       sa.last_mod_user
  FROM msdb.dbo.sysmail_attachments sa
  JOIN msdb.dbo.sysmail_mailitems sm ON sa.mailitem_id = sm.mailitem_id
  WHERE (sm.send_request_user = SUSER_SNAME()) OR (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)
GO

-----------------------------------------------------------
-- TABLE sysmail_send_retries
-----------------------------------------------------------
-- sysmail_send_retries : Contains send mail retry history
--
-- conversation_handle  : The conversation handle that initiated the retry
-- mailitem_id          : Optional key to the mail items that this entry is a about
-- send_attempts        : The current number of send attempts
-- last_send_attempt_date : date of the last send attempt
----------------------------------------------------------------
IF (OBJECT_ID('dbo.sysmail_send_retries', 'U') IS NULL)
BEGIN
    PRINT 'Creating TABLE sysmail_send_retries'
    CREATE TABLE sysmail_send_retries
    (
        conversation_handle     uniqueidentifier PRIMARY KEY NOT NULL,
        mailitem_id             INT              NOT NULL CONSTRAINT FK_mailitems_mailitem_id 
                    FOREIGN KEY (mailitem_id) REFERENCES sysmail_mailitems(mailitem_id) ON DELETE CASCADE,
        send_attempts           INT              NOT NULL DEFAULT 1,
        last_send_attempt_date  DATETIME         NOT NULL DEFAULT GETDATE()
    )
END
ELSE
BEGIN
   BEGIN TRAN

   -- remove any old constraint
   declare @fkName sysname
   DECLARE @sql NVARCHAR(4000)
   SELECT @fkName = cstr.name
      FROM
         sys.tables AS tbl
      INNER JOIN sys.foreign_keys AS cstr ON cstr.parent_object_id=tbl.object_id
      LEFT OUTER JOIN sys.indexes AS ki ON ki.index_id = cstr.key_index_id and ki.object_id = cstr.referenced_object_id
      INNER JOIN sys.tables rtbl ON rtbl.object_id = cstr.referenced_object_id
      WHERE
      tbl.name=N'sysmail_send_retries' 

   IF (@fkName IS NOT NULL)
   BEGIN
      SET @sql = N'ALTER TABLE sysmail_send_retries DROP CONSTRAINT ' + QUOTENAME(@fkName)
      EXECUTE (@sql)
   END

   ALTER TABLE sysmail_send_retries ADD CONSTRAINT FK_mailitems_mailitem_id 
                    FOREIGN KEY (mailitem_id) REFERENCES sysmail_mailitems(mailitem_id) ON DELETE CASCADE

   COMMIT
END
GO

-----------------------------------------------------------
-- TABLE sysmail_log
-----------------------------------------------------------
-- sysmail_log      : Contains error and event logging 
--
-- log_id           : Id for the row. Auto-generated.
-- event_type       : The event type for this record
--                    0(Success), 1(information), 2(Warning), 3(error)

-- log_date         : Create date of this log entry
-- description      : The text description of this entry
-- process_id       : The DatabaseMail (exe) process id that added this entry 
-- mailitem_id      : Optional key to the mail items that this entry is a about
-- account_id       : Optional account_id hat this entry is a about
----------------------------------------------------------------
IF (OBJECT_ID('dbo.sysmail_log', 'U') IS NULL)
BEGIN
  PRINT 'Creating TABLE sysmail_log'

    CREATE TABLE sysmail_log
    (
        log_id         INT             IDENTITY(1, 1) NOT NULL,
        event_type      INT             NOT NULL,
        log_date        DATETIME        NOT NULL    DEFAULT GETDATE(),
        description     NVARCHAR(max)   NULL,
        process_id      INT             NULL,
        mailitem_id     INT             NULL,
        account_id      INT             NULL,
        last_mod_date   DATETIME        NOT NULL DEFAULT GETDATE(),
        last_mod_user   sysname     NOT NULL DEFAULT SUSER_SNAME(),

        CONSTRAINT [sysmail_log_id_MustBeUnique] PRIMARY KEY(log_id),
    )
END
ELSE
BEGIN
   -- remove any old constraint
   declare @fkName sysname
   DECLARE @sql NVARCHAR(4000)
   SELECT @fkName = cstr.name
      FROM
         sys.tables AS tbl
      INNER JOIN sys.foreign_keys AS cstr ON cstr.parent_object_id=tbl.object_id
      LEFT OUTER JOIN sys.indexes AS ki ON ki.index_id = cstr.key_index_id and ki.object_id = cstr.referenced_object_id
      INNER JOIN sys.tables rtbl ON rtbl.object_id = cstr.referenced_object_id
      WHERE
      tbl.name=N'sysmail_log' 

   IF (@fkName IS NOT NULL)
   begin
      select @sql = N'ALTER TABLE sysmail_log DROP CONSTRAINT ' + QUOTENAME(@fkName)
      EXEC (@sql)
   end
END
GO

/**************************************************************/
/* sysmail_event_log                                         */
/**************************************************************/
use msdb
go

PRINT ''
PRINT 'Creating view sysmail_event_log...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sysmail_event_log')
              AND (type = 'V')))
  DROP VIEW sysmail_event_log
go

CREATE VIEW sysmail_event_log
AS
SELECT log_id,
       CASE event_type 
          WHEN 0 THEN 'success' 
          WHEN 1 THEN 'information' 
          WHEN 2 THEN 'warning' 
          ELSE 'error' 
       END as event_type,
       log_date,
       description,
       process_id,
       sl.mailitem_id,
       account_id,
       sl.last_mod_date,
       sl.last_mod_user
FROM [dbo].[sysmail_log]  sl
WHERE (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR 
      (EXISTS ( SELECT mailitem_id FROM [dbo].[sysmail_allitems] ai WHERE sl.mailitem_id = ai.mailitem_id ))

GO

-----------------------------------------------------------
-- procedure sysmail_delete_log_sp
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sysmail_delete_log_sp', 'P') IS NULL
    DROP PROCEDURE dbo.sysmail_delete_log_sp
GO
-----
PRINT 'Creating sysmail_delete_log_sp'
-----
GO
CREATE PROCEDURE sysmail_delete_log_sp
   @logged_before DATETIME   = NULL, 
   @event_type varchar(15)   = NULL
AS
BEGIN

   SET @event_type       = LTRIM(RTRIM(@event_type))
   IF @event_type        = '' SET @event_type = NULL
   DECLARE @event_type_numeric INT

   IF ( (@event_type IS NOT NULL) AND
         (LOWER(@event_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ( 'success', 'warning', 'error', 'information' ) ) )
   BEGIN
        RAISERROR(14266, -1, -1, '@event_type', 'success, warning, error, information')
      RETURN(1) -- Failure
   END   
   
   IF ( @event_type IS NOT NULL)
   BEGIN
      SET @event_type_numeric = ( SELECT CASE 
                           WHEN @event_type = 'success' THEN 0
                           WHEN @event_type = 'information' THEN 1
                           WHEN @event_type = 'warning' THEN 2
                           ELSE 3 END 
                        )
   END
   ELSE
      SET @event_type_numeric = NULL

   DELETE FROM msdb.dbo.sysmail_log 
   WHERE 
        ((@logged_before IS NULL) OR ( log_date < @logged_before))
   AND ((@event_type_numeric IS NULL) OR (@event_type_numeric = event_type))
END
GO

-----------------------------------------------------------
-- sysmail_query_transfer : Table used to transfer data between a helper xp's and the calling sp's.
--                   Rows are created and deleted in the context of each call
--
-- uid              : guid for the row. Generated by the user  
-- text_data        : Attachment data in binary form
----------------------------------------------------------------
IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysmail_query_transfer')
                  AND (type = 'U')))
BEGIN
PRINT 'Creating TABLE sysmail_query_transfer'
CREATE TABLE sysmail_query_transfer
(
    uid             uniqueidentifier    NOT NULL PRIMARY KEY,
    text_data       NVARCHAR(max)       NULL,
    create_date     DATETIME            NOT NULL DEFAULT GETDATE()
)
END
GO

-----------------------------------------------------------
-- sysmail_attachments_transfer : Table used to transfer data between a helper xp's
--                        and the calling sp's. Rows are created and deleted 
--                        in the context of each call
--
-- uid              : guid for the row. Generated by the user  
-- filename         : Attachment file name
-- filesize         : Attachment file size in bytes
-- attachment       : Attachment data in binary form
----------------------------------------------------------------
IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysmail_attachments_transfer')
                  AND (type = 'U')))
BEGIN
PRINT 'Creating TABLE sysmail_attachments_transfer'
CREATE TABLE sysmail_attachments_transfer
(
    transfer_id       INT                 IDENTITY(1, 1) NOT NULL PRIMARY KEY,
    uid         uniqueidentifier    NOT NULL,
    filename        NVARCHAR(260)       NOT NULL,
    filesize        INT                 NOT NULL,
    attachment      VARBINARY(MAX)      NULL,
    create_date     DATETIME            NOT NULL DEFAULT GETDATE()
)
END
GO

/*************************************************************************/
/*                                                                       */
/*  Database Mail Triggers                                               */
/*                                                                       */
/*************************************************************************/

------------------------------------------------------------
-- Database Mail: triggers on general configuration tables
------------------------------------------------------------
PRINT ''
PRINT 'Creating trigger trig_sysmail_profile...'

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'trig_sysmail_profile')
              AND (type = 'TR')))
   DROP TRIGGER dbo.trig_sysmail_profile
go

CREATE TRIGGER trig_sysmail_profile
ON msdb.dbo.sysmail_profile
FOR UPDATE
AS
BEGIN
   SET NOCOUNT ON  

   IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_profile'), 'AFTER' , 'DML' ) <= 1) 
   BEGIN  
      UPDATE msdb.dbo.sysmail_profile 
      SET last_mod_datetime = getdate(),last_mod_user = suser_sname() 
      FROM sysmail_profile p, inserted i
      WHERE p.profile_id = i.profile_id
   END
END
go

PRINT ''
PRINT 'Creating trigger trig_principalprofile...'

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'trig_principalprofile')
              AND (type = 'TR')))
   DROP TRIGGER dbo.trig_principalprofile
go

CREATE TRIGGER trig_principalprofile
ON msdb.dbo.sysmail_principalprofile
FOR UPDATE
AS
BEGIN
   SET NOCOUNT ON  

   IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_principalprofile'), 'AFTER' , 'DML' ) <= 1) 
   BEGIN  
      UPDATE msdb.dbo.sysmail_principalprofile 
      SET last_mod_datetime = getdate(),last_mod_user = suser_sname() 
      FROM sysmail_principalprofile p, inserted i
      WHERE p.profile_id = i.profile_id and p.principal_sid = i.principal_sid
   END
END
go

PRINT ''
PRINT 'Creating trigger trig_sysmail_account...'

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'trig_sysmail_account')
              AND (type = 'TR')))
   DROP TRIGGER dbo.trig_sysmail_account
go

CREATE TRIGGER trig_sysmail_account
ON msdb.dbo.sysmail_account
FOR UPDATE
AS
BEGIN
   SET NOCOUNT ON  

   IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_account'), 'AFTER' , 'DML' ) <= 1) 
   BEGIN  
      UPDATE msdb.dbo.sysmail_account 
      SET last_mod_datetime = getdate(),last_mod_user = suser_sname() 
      FROM sysmail_account a, inserted i
      WHERE a.account_id = i.account_id
   END
END
go

PRINT ''
PRINT 'Creating trigger trig_sysmail_profileaccount...'

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'trig_sysmail_profileaccount')
              AND (type = 'TR')))
   DROP TRIGGER dbo.trig_sysmail_profileaccount
go

CREATE TRIGGER trig_sysmail_profileaccount
ON msdb.dbo.sysmail_profileaccount
FOR UPDATE
AS
BEGIN
   SET NOCOUNT ON  

   IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_profileaccount'), 'AFTER' , 'DML' ) <= 1) 
   BEGIN  
      UPDATE msdb.dbo.sysmail_profileaccount 
      SET last_mod_datetime = getdate(),last_mod_user = suser_sname() 
      FROM sysmail_profileaccount p, inserted i
      WHERE p.profile_id = i.profile_id and p.account_id = i.account_id
   END
END
go

PRINT ''
PRINT 'Creating trigger trig_sysmail_profile_delete...'

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'trig_sysmail_profile_delete')
              AND (type = 'TR')))
   DROP TRIGGER dbo.trig_sysmail_profile_delete
go

CREATE TRIGGER trig_sysmail_profile_delete
ON msdb.dbo.sysmail_profile
FOR DELETE
AS
BEGIN
   DELETE FROM msdb.dbo.sysmail_profileaccount
   WHERE profile_id IN (SELECT profile_id FROM deleted)
END
go

PRINT ''
PRINT 'Creating trigger trig_sysmail_servertype...'

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'trig_sysmail_servertype')
              AND (type = 'TR')))
   DROP TRIGGER dbo.trig_sysmail_servertype
go

CREATE TRIGGER trig_sysmail_servertype
ON msdb.dbo.sysmail_servertype
FOR UPDATE
AS
BEGIN
   SET NOCOUNT ON  

   IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_servertype'), 'AFTER' , 'DML' ) <= 1) 
   BEGIN  
      UPDATE msdb.dbo.sysmail_servertype 
      SET last_mod_datetime = getdate(),last_mod_user = suser_sname() 
      FROM sysmail_servertype s, inserted i
      where s.servertype = i.servertype
   END
END
go

SET NOCOUNT ON
IF NOT EXISTS(SELECT * FROM dbo.sysmail_servertype WHERE servertype = N'SMTP')
BEGIN
    INSERT INTO dbo.sysmail_servertype (servertype) VALUES (N'SMTP')
END
SET NOCOUNT OFF
go

PRINT ''
PRINT 'Creating trigger trig_sysmail_server...'

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'trig_sysmail_server')
              AND (type = 'TR')))
   DROP TRIGGER dbo.trig_sysmail_server
go

CREATE TRIGGER trig_sysmail_server
ON msdb.dbo.sysmail_server
FOR UPDATE
AS
BEGIN
   SET NOCOUNT ON  

   IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_server'), 'AFTER' , 'DML' ) <= 1) 
   BEGIN  
      UPDATE msdb.dbo.sysmail_server 
      SET last_mod_datetime = getdate(),last_mod_user = suser_sname() 
      FROM sysmail_server s, inserted i
      WHERE s.account_id = i.account_id and s.servertype = i.servertype
   END
END
go

PRINT ''
PRINT 'Creating trigger trig_sysmail_configuration...'

IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'trig_sysmail_configuration')
              AND (type = 'TR')))
   DROP TRIGGER dbo.trig_sysmail_configuration
go

CREATE TRIGGER trig_sysmail_configuration
ON msdb.dbo.sysmail_configuration
FOR UPDATE
AS
BEGIN
   SET NOCOUNT ON  

   IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_configuration'), 'AFTER' , 'DML' ) <= 1) 
   BEGIN  
      UPDATE msdb.dbo.sysmail_configuration 
      SET last_mod_datetime = getdate(),last_mod_user = suser_sname() 
      FROM sysmail_configuration c, inserted i
      WHERE c.paramname = i.paramname
   END
END
go

-------------------------------------------------------------------------
-- Database Mail: triggers on general mail host database specific tables
-------------------------------------------------------------------------
IF (OBJECT_ID('dbo.trig_sysmail_mailitems', 'TR') IS NOT NULL)
    DROP TRIGGER dbo.trig_sysmail_mailitems
GO

CREATE TRIGGER trig_sysmail_mailitems
ON msdb.dbo.sysmail_mailitems
FOR UPDATE
AS
BEGIN
   SET NOCOUNT ON  

   IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_mailitems'), 'AFTER' , 'DML' ) <= 1) 
   BEGIN  
      UPDATE msdb.dbo.sysmail_mailitems 
      SET last_mod_date = GETDATE(), last_mod_user = SUSER_SNAME() 
      FROM sysmail_mailitems m, inserted i
      WHERE m.mailitem_id = i.mailitem_id
   END
END
GO

IF (OBJECT_ID('dbo.trig_sysmail_attachments', 'TR') IS NOT NULL)
    DROP TRIGGER dbo.trig_sysmail_attachments
GO

CREATE TRIGGER trig_sysmail_attachments
ON msdb.dbo.sysmail_attachments
FOR UPDATE
AS
BEGIN
   SET NOCOUNT ON  

   IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_attachments'), 'AFTER' , 'DML' ) <= 1) 
   BEGIN  
      UPDATE msdb.dbo.sysmail_attachments 
      SET last_mod_date = GETDATE(), last_mod_user = SUSER_SNAME() 
      FROM sysmail_attachments a, inserted i
      WHERE a.attachment_id = i.attachment_id
   END
END
GO

IF (OBJECT_ID('dbo.trig_sysmail_log', 'TR') IS NOT NULL)
    DROP TRIGGER dbo.trig_sysmail_log
GO

CREATE TRIGGER trig_sysmail_log
ON msdb.dbo.sysmail_log
FOR UPDATE
AS
BEGIN
   SET NOCOUNT ON  

   IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_log'), 'AFTER' , 'DML' ) <= 1) 
   BEGIN  
      UPDATE msdb.dbo.sysmail_log 
      SET last_mod_date = GETDATE(), last_mod_user = SUSER_SNAME() 
      FROM sysmail_log l, inserted i
      WHERE l.log_id = i.log_id
   END
END
GO

/*********************************************************************************/
/*                                                                               */
/*  Database Mail Utility Functions                                              */
/*                                                                               */
/*********************************************************************************/
-----------------------------------------------------------
-- ConvertToInt : Converts a string to integer. Returns null
--                if the input string is not a valid int.
--
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.ConvertToInt', 'FN') IS NULL
    DROP FUNCTION dbo.ConvertToInt
GO

CREATE FUNCTION dbo.ConvertToInt(@string nvarchar(255), @maxValue int, @defValue int) RETURNS int
AS
BEGIN
    DECLARE @value bigint   
    SET @value = @defValue 
    SET @string = LTRIM(RTRIM(@string))

    -- Check if there is any character other than 0-9 in the string.
    IF ((@string IS NOT NULL AND @string <> N'') AND (@string NOT LIKE '%[^0-9]%'))
    BEGIN
        --INT's have a max of 10 digits
        IF(LEN(@string) <= 10)
        BEGIN
        -- Try converting to bigint. Return default if the value is bigger than @maxValue
        SET @value = CONVERT(bigint, @string)
        IF(@value > CONVERT(bigint, @maxValue))
            SET @value = @defValue
        END
    END

    RETURN CONVERT(int, @value)
END
GO

/*********************************************************************************/
/*                                                                               */
/*  Database Mail Stored Procedures                                              */
/*                                                                               */
/*********************************************************************************/

-------------------------------------------------------
-- Database Mail: configuration stored procedures
-------------------------------------------------------
PRINT ''
PRINT 'Creating procedure sysmail_verify_accountparams_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_verify_accountparams_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_verify_accountparams_sp
go

CREATE PROCEDURE dbo.sysmail_verify_accountparams_sp
   @use_default_credentials bit,
   @mailserver_type sysname      OUTPUT,  -- @mailserver_type must be provided. Usually SMTP
   @username      nvarchar(128)  OUTPUT, -- returns trimmed value, NULL if empty
   @password      nvarchar(128)  OUTPUT  -- returns trimmed value,  NULL if empty
AS
   SET @username = LTRIM(RTRIM(@username))
   SET @password = LTRIM(RTRIM(@password))
   SET @mailserver_type = LTRIM(RTRIM(@mailserver_type))

    IF(@username = N'')         SET @username = NULL
    IF(@password = N'')         SET @password = NULL
    IF(@mailserver_type = N'')  SET @mailserver_type = NULL

   IF(@mailserver_type IS NULL)
   BEGIN
      RAISERROR(14614, -1, -1, @mailserver_type)   
      RETURN (1)  
   END

   -- default credentials should supercede any explicit credentials passed in
   IF((@use_default_credentials = 1) AND (@username IS NOT NULL))
   BEGIN
      RAISERROR(14666, -1, -1)   
      RETURN (1)
   END  

   --If a password is specified then @username must be a non empty string
   IF((@password IS NOT NULL) AND (@username IS NULL))
   BEGIN
      RAISERROR(14615, -1, -1)   
      RETURN (1)
   END  

   RETURN(0) -- SUCCESS
go

PRINT ''
PRINT 'Creating procedure sysmail_verify_principal_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_verify_principal_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_verify_principal_sp
go

CREATE PROCEDURE dbo.sysmail_verify_principal_sp
   @principal_id int,
   @principal_name sysname,
   @allow_both_nulls bit,
   @principal_sid varbinary(85) OUTPUT
AS
   IF @allow_both_nulls = 0
   BEGIN
      -- at least one parameter must be supplied
      IF (@principal_id IS NULL AND @principal_name IS NULL)
      BEGIN
         RAISERROR(14604, -1, -1, 'principal')  
         RETURN(1)
      END
   END

   DECLARE @principalid int

   IF (@principal_id IS NOT NULL AND @principal_name IS NOT NULL) -- both parameters supplied
   BEGIN
     SELECT @principalid=principal_id FROM msdb.sys.database_principals 
            WHERE type in ('U','S','G') AND principal_id = @principal_id AND name = @principal_name

      IF (@principalid IS NULL)
      BEGIN
         RAISERROR(14605, -1, -1, 'principal')  
         RETURN(2)
      END
   END
   ELSE IF (@principal_id IS NOT NULL) -- use id
   BEGIN
     SELECT @principalid=principal_id FROM msdb.sys.database_principals 
            WHERE type in ('U','S','G') AND principal_id = @principal_id

      IF (@principalid IS NULL)
      BEGIN
         RAISERROR(14606, -1, -1, 'principal')
         RETURN(3)
      END      
   END
   ELSE IF (@principal_name IS NOT NULL)  -- use name
   BEGIN
     SELECT @principalid=principal_id FROM msdb.sys.database_principals 
            WHERE type in ('U','S','G') AND name = @principal_name

      IF (@principalid IS NULL)
      BEGIN
         RAISERROR(14607, -1, -1, 'principal')
         RETURN(4)
      END      
   END

   -- populate return variable
   SELECT @principal_sid = dbo.get_principal_sid(@principalid)

   RETURN(0) -- SUCCESS
go

PRINT ''
PRINT 'Creating procedure sysmail_verify_profile_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_verify_profile_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_verify_profile_sp
go

CREATE PROCEDURE dbo.sysmail_verify_profile_sp
   @profile_id int,
   @profile_name sysname,
   @allow_both_nulls bit,
   @allow_id_name_mismatch bit,
   @profileid int OUTPUT
AS
   IF @allow_both_nulls = 0
   BEGIN
      -- at least one parameter must be supplied
      IF (@profile_id IS NULL AND @profile_name IS NULL)
      BEGIN
         RAISERROR(14604, -1, -1, 'profile') 
         RETURN(1)
      END
   END
   
   IF ((@allow_id_name_mismatch = 0) AND (@profile_id IS NOT NULL AND @profile_name IS NOT NULL)) -- use both parameters
   BEGIN
      SELECT @profileid = profile_id FROM msdb.dbo.sysmail_profile WHERE profile_id=@profile_id AND name=@profile_name
      IF (@profileid IS NULL) -- id and name do not match
      BEGIN
         RAISERROR(14605, -1, -1, 'profile')
         RETURN(2)
      END      
   END
   ELSE IF (@profile_id IS NOT NULL) -- use id
   BEGIN
      SELECT @profileid = profile_id FROM msdb.dbo.sysmail_profile WHERE profile_id=@profile_id
      IF (@profileid IS NULL) -- id is invalid
      BEGIN
         RAISERROR(14606, -1, -1, 'profile')
         RETURN(3)
      END      
   END
   ELSE IF (@profile_name IS NOT NULL) -- use name
   BEGIN
      SELECT @profileid = profile_id FROM msdb.dbo.sysmail_profile WHERE name=@profile_name
      IF (@profileid IS NULL) -- name is invalid
      BEGIN
         RAISERROR(14607, -1, -1, 'profile')
         RETURN(4)
      END      
   END
   RETURN(0) -- SUCCESS
go

PRINT ''
PRINT 'Creating procedure sysmail_verify_account_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_verify_account_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_verify_account_sp
go

CREATE PROCEDURE dbo.sysmail_verify_account_sp
   @account_id int,
   @account_name sysname,
   @allow_both_nulls bit,
   @allow_id_name_mismatch bit,
   @accountid int OUTPUT
AS
   IF @allow_both_nulls = 0
   BEGIN
      -- at least one parameter must be supplied
      IF (@account_id IS NULL AND @account_name IS NULL)
      BEGIN
         RAISERROR(14604, -1, -1, 'account') 
         RETURN(1)
      END
   END
   
   IF ((@allow_id_name_mismatch = 0) AND (@account_id IS NOT NULL AND @account_name IS NOT NULL)) -- use both parameters
   BEGIN
      SELECT @accountid = account_id FROM msdb.dbo.sysmail_account WHERE account_id=@account_id AND name=@account_name
      IF (@accountid IS NULL) -- id and name do not match
      BEGIN
         RAISERROR(14605, -1, -1, 'account')
         RETURN(2)
      END      
   END
   ELSE IF (@account_id IS NOT NULL) -- use id
   BEGIN
      SELECT @accountid = account_id FROM msdb.dbo.sysmail_account WHERE account_id=@account_id
      IF (@accountid IS NULL) -- id is invalid
      BEGIN
         RAISERROR(14606, -1, -1, 'account')
         RETURN(3)
      END      
   END
   ELSE IF (@account_name IS NOT NULL) -- use name
   BEGIN
      SELECT @accountid = account_id FROM msdb.dbo.sysmail_account WHERE name=@account_name
      IF (@accountid IS NULL) -- name is invalid
      BEGIN
         RAISERROR(14607, -1, -1, 'account')
         RETURN(4)
      END      
   END
   RETURN(0) -- SUCCESS
go

PRINT ''
PRINT 'Creating procedure sysmail_add_profile_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_add_profile_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_add_profile_sp
go

CREATE PROCEDURE dbo.sysmail_add_profile_sp
   @profile_name sysname,
   @description nvarchar(256) = NULL,
   @profile_id int = NULL OUTPUT 
AS
   SET NOCOUNT ON

   -- insert new profile record, rely on primary key constraint to error out
   INSERT INTO msdb.dbo.sysmail_profile (name,description) VALUES (@profile_name, @description)
   
   -- fetch back profile_id
   SELECT @profile_id = profile_id FROM msdb.dbo.sysmail_profile WHERE name = @profile_name

   RETURN(0)
go

PRINT ''
PRINT 'Creating procedure sysmail_update_profile_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_update_profile_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_update_profile_sp
go

CREATE PROCEDURE dbo.sysmail_update_profile_sp
   @profile_id int = NULL, -- must provide either id or name
   @profile_name sysname = NULL,
   @description nvarchar(256) = NULL
AS
   SET NOCOUNT ON
  
   DECLARE @rc int
   DECLARE @profileid int
   exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 0, 1, @profileid OUTPUT
   IF @rc <> 0
      RETURN(1)
   
   IF (@profile_name IS NOT NULL AND @description IS NOT NULL)
      UPDATE msdb.dbo.sysmail_profile 
      SET name=@profile_name, description = @description
      WHERE profile_id = @profileid
      
   ELSE IF (@profile_name IS NOT NULL)
      UPDATE msdb.dbo.sysmail_profile 
      SET name=@profile_name
      WHERE profile_id = @profileid

   ELSE IF (@description IS NOT NULL)
      UPDATE msdb.dbo.sysmail_profile 
      SET description = @description
      WHERE profile_id = @profileid
      
   ELSE
   BEGIN
      RAISERROR(14610, -1, -1)   
      RETURN(1)
   END

   RETURN(0)
go

PRINT ''
PRINT 'Creating procedure sysmail_delete_profile_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_delete_profile_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_delete_profile_sp
go

USE [msdb]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF
GO


CREATE PROCEDURE [dbo].[sysmail_delete_profile_sp]
   @profile_id int = NULL, -- must provide either id or name
   @profile_name sysname = NULL,
   @force_delete BIT = 1
AS
   SET NOCOUNT ON
  
   DECLARE @rc int
   DECLARE @profileid int
   exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 0, 0, @profileid OUTPUT
   IF @rc <> 0
      RETURN(1)

IF(EXISTS (select * from sysmail_unsentitems WHERE 
   sysmail_unsentitems.profile_id = @profileid) AND @force_delete <> 1)
BEGIN
    IF(@profile_name IS NULL)
    BEGIN
        select @profile_name = name from dbo.sysmail_profile WHERE profile_id = @profileid
    END
    RAISERROR(14668, -1, -1, @profile_name)
    RETURN (1)   
END

UPDATE [msdb].[dbo].[sysmail_mailitems]
SET [sent_status] = 2, [sent_date] = getdate()
WHERE profile_id = @profileid AND sent_status <> 1
     
   DELETE FROM msdb.dbo.sysmail_profile 
   WHERE profile_id = @profileid
   RETURN(0)
GO

PRINT ''
PRINT 'Creating procedure sysmail_help_profile_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_help_profile_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_help_profile_sp
go

CREATE PROCEDURE dbo.sysmail_help_profile_sp
   @profile_id int = NULL,
   @profile_name sysname = NULL
AS
   SET NOCOUNT ON

   DECLARE @rc int
   DECLARE @profileid int
   exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 1, 0, @profileid OUTPUT
   IF @rc <> 0
      RETURN(1)

   IF (@profileid IS NOT NULL)
      SELECT profile_id, name, description 
      FROM msdb.dbo.sysmail_profile 
      WHERE profile_id = @profileid
      
   ELSE -- don't filter the output
      SELECT profile_id, name, description      
      FROM msdb.dbo.sysmail_profile 

   RETURN(0)
go


PRINT ''
PRINT 'Creating procedure sysmail_create_user_credential_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_create_user_credential_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_create_user_credential_sp
go

CREATE PROCEDURE dbo.sysmail_create_user_credential_sp
   @username      nvarchar(128),
   @password      nvarchar(128),
   @credential_id int            OUTPUT
AS
   SET NOCOUNT ON
   DECLARE @rc int
   DECLARE @credential_name UNIQUEIDENTIFIER
   DECLARE @credential_name_as_str varchar(40)
   DECLARE @sql NVARCHAR(max)

   -- create a GUID as the name for the credential
   SET @credential_name = newid()
   SET @credential_name_as_str = convert(varchar(40), @credential_name)
   SET @sql = N'CREATE CREDENTIAL [' + @credential_name_as_str
            + N'] WITH IDENTITY = ' + QUOTENAME(@username, '''')
            + N', SECRET = ' + QUOTENAME(ISNULL(@password, N''), '''')

   EXEC @rc = sp_executesql @statement = @sql
   IF(@rc <> 0)
      RETURN @rc

   SELECT @credential_id = credential_id 
   FROM sys.credentials
   WHERE name = convert(sysname, @credential_name)
    IF(@credential_id IS NULL)
   BEGIN
      RAISERROR(14616, -1, -1, @credential_name_as_str)
      RETURN 1
   END

   RETURN(0)
go



PRINT ''
PRINT 'Creating procedure sysmail_alter_user_credential_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_alter_user_credential_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_alter_user_credential_sp
go

CREATE PROCEDURE dbo.sysmail_alter_user_credential_sp
   @credential_name sysname,
   @username      nvarchar(128),
   @password      nvarchar(128)
AS
   SET NOCOUNT ON
   DECLARE @rc int
   DECLARE @sql NVARCHAR(max)

   -- alter credential DDL
   SET @sql = N'ALTER CREDENTIAL ' + QUOTENAME(@credential_name)
         + N' WITH IDENTITY = ' + QUOTENAME(@username, '''')
         + N', SECRET = ' + QUOTENAME(ISNULL(@password, N''), '''')

   EXEC @rc = sp_executesql @statement = @sql
   IF(@rc <> 0)
      RETURN @rc

   RETURN(0)
go


PRINT ''
PRINT 'Creating procedure sysmail_drop_user_credential_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_drop_user_credential_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_drop_user_credential_sp
go

CREATE PROCEDURE dbo.sysmail_drop_user_credential_sp
   @credential_name sysname
AS
   SET NOCOUNT ON
   DECLARE @rc int
   DECLARE @sql NVARCHAR(max)

   -- Drop credential DDL
   SET @sql = N'DROP CREDENTIAL ' + QUOTENAME(@credential_name)

   EXEC @rc = sp_executesql @statement = @sql
   IF(@rc <> 0)
      RETURN @rc

   RETURN(0)
go

         
PRINT ''
PRINT 'Creating procedure sysmail_add_account_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_add_account_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_add_account_sp
go

CREATE PROCEDURE dbo.sysmail_add_account_sp
   @account_name sysname,
   @email_address nvarchar(128),
   @display_name nvarchar(128) = NULL,
   @replyto_address nvarchar(128) = NULL,
   @description nvarchar(256) = NULL,  
   @mailserver_name sysname = NULL, -- the following fields are part of server definition
   @mailserver_type sysname = N'SMTP',
   @port int = 25,
   @username nvarchar(128) = NULL,
   @password nvarchar(128) = NULL,
   @use_default_credentials bit = 0,
   @enable_ssl bit = 0,
   @account_id int = NULL OUTPUT
AS
   SET NOCOUNT ON
   DECLARE @rc int
   DECLARE @credential_id int

   EXEC @rc = msdb.dbo.sysmail_verify_accountparams_sp
            @use_default_credentials = @use_default_credentials,
            @mailserver_type = @mailserver_type OUTPUT, -- validates and returns trimmed value
            @username = @username OUTPUT, -- returns trimmed value, NULL if empty
            @password = @password OUTPUT  -- returns trimmed value, NULL if empty
   IF(@rc <> 0)
      RETURN (1)

   --transact this in case sysmail_create_user_credential_sp fails
   BEGIN TRANSACTION

   -- insert new account record, rely on primary key constraint to error out
   INSERT INTO msdb.dbo.sysmail_account (name,description,email_address,display_name,replyto_address) 
   VALUES (@account_name,@description,@email_address,@display_name,@replyto_address)
   IF (@@ERROR <> 0)
   BEGIN
      ROLLBACK TRANSACTION
      RETURN (2)
   END
   
   -- fetch back account_id
   SELECT @account_id = account_id FROM msdb.dbo.sysmail_account WHERE name = @account_name

   IF (@mailserver_name IS NULL) -- use local server as default
      SELECT @mailserver_name=@@SERVERNAME

   --create a credential in the credential store if a password needs to be stored
   IF(@username IS NOT NULL)
   BEGIN
      EXEC @rc = msdb.dbo.sysmail_create_user_credential_sp
                     @username,
                     @password,
                     @credential_id OUTPUT
      IF(@rc <> 0)
      BEGIN 
         ROLLBACK TRANSACTION
         RETURN (3)
      END
   END
   
   INSERT INTO msdb.dbo.sysmail_server (account_id,servertype,servername,port,username,credential_id,use_default_credentials,enable_ssl) 
   VALUES (@account_id,@mailserver_type,@mailserver_name,@port,@username,@credential_id,@use_default_credentials,@enable_ssl)
   IF (@@ERROR <> 0)
   BEGIN
      ROLLBACK TRANSACTION
      RETURN (4)
   END

   COMMIT TRANSACTION
   RETURN(0)
go

PRINT ''
PRINT 'Creating procedure sysmail_update_account_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_update_account_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_update_account_sp
go

USE [msdb]
GO
/****** Object:  StoredProcedure [dbo].[sysmail_update_account_sp]    Script Date: 06/26/2006 10:45:40 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF
GO

CREATE PROCEDURE [dbo].[sysmail_update_account_sp]
   @account_id int = NULL, -- must provide either id or name
   @account_name sysname = NULL,
   @email_address nvarchar(128) = NULL,
   @display_name nvarchar(128) = NULL,
   @replyto_address nvarchar(128) = NULL,
   @description nvarchar(256) = NULL,
   @mailserver_name sysname = NULL,  
   @mailserver_type sysname = NULL,  
   @port int = NULL,
   @username sysname = NULL,
   @password sysname = NULL,
   @use_default_credentials bit = NULL,
   @enable_ssl bit = NULL,
   @timeout int = NULL,
   @no_credential_change bit = NULL  -- BOOL
  -- WITH EXECUTE AS OWNER --Allows access to sys.credentials
AS
   SET NOCOUNT ON

   DECLARE @rc int
   DECLARE @accountid int
   DECLARE @credential_id int
   DECLARE @credential_name sysname

   SELECT @no_credential_change = ISNULL(@no_credential_change, 0)

   exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 0, 1, @accountid OUTPUT
   IF @rc <> 0
	  RETURN(1)


   IF(@email_address IS NULL)
   BEGIN
   SELECT @email_address = email_address FROM msdb.dbo.sysmail_account WHERE account_id=@accountid
   END


   IF(@display_name IS NULL)
   BEGIN
   SELECT @display_name = display_name FROM msdb.dbo.sysmail_account WHERE account_id=@accountid
   END

   IF(@replyto_address IS NULL)
   BEGIN
   SELECT @replyto_address = replyto_address FROM msdb.dbo.sysmail_account WHERE account_id=@accountid
   END

   IF(@description IS NULL)
   BEGIN
   SELECT @description = description FROM msdb.dbo.sysmail_account WHERE account_id=@accountid
   END


   IF(@port IS NULL)
   BEGIN
   SELECT @port = port FROM msdb.dbo.sysmail_server WHERE account_id=@accountid
   END

   IF(@use_default_credentials IS NULL)
   BEGIN
   SELECT @use_default_credentials = use_default_credentials FROM msdb.dbo.sysmail_server WHERE account_id=@accountid
   END

   IF(@enable_ssl IS NULL)
   BEGIN
   SELECT @enable_ssl = enable_ssl FROM msdb.dbo.sysmail_server WHERE account_id=@accountid
   END

   IF(@timeout IS NULL)
   BEGIN
   SELECT @timeout = timeout FROM msdb.dbo.sysmail_server WHERE account_id=@accountid
   END
   
   IF(@mailserver_type IS NULL)
   BEGIN
   SELECT @mailserver_type = servertype FROM msdb.dbo.sysmail_server WHERE account_id=@accountid
   END


   EXEC @rc = msdb.dbo.sysmail_verify_accountparams_sp 
			@use_default_credentials = @use_default_credentials,
			@mailserver_type = @mailserver_type OUTPUT, -- validates and returns trimmed value
			@username = @username OUTPUT, -- returns trimmed value
			@password = @password OUTPUT  -- returns empty string if @username is given and @password is null 
   IF(@rc <> 0)
	  RETURN (1)

   --transact this in case credential updates fail
   BEGIN TRAN
   -- update account table
   IF (@account_name IS NOT NULL)
	  IF (@email_address IS NOT NULL)
		 UPDATE msdb.dbo.sysmail_account
		 SET name=@account_name, description=@description, email_address=@email_address, display_name=@display_name, replyto_address=@replyto_address
		 WHERE account_id=@accountid
	  ELSE
		 UPDATE msdb.dbo.sysmail_account
		 SET name=@account_name, description=@description, display_name=@display_name, replyto_address=@replyto_address
		 WHERE account_id=@accountid

   ELSE
	  IF (@email_address IS NOT NULL)
		 UPDATE msdb.dbo.sysmail_account
		 SET description=@description, email_address=@email_address, display_name=@display_name, replyto_address=@replyto_address
		 WHERE account_id=@accountid
	  ELSE
		 UPDATE msdb.dbo.sysmail_account
		 SET description=@description, display_name=@display_name, replyto_address=@replyto_address
		 WHERE account_id=@accountid
      
   -- see if a credential has been stored for this account
   SELECT @credential_name = name,
		 @credential_id = c.credential_id
   FROM sys.credentials as c
	 JOIN msdb.dbo.sysmail_server as ms
	   ON c.credential_id = ms.credential_id
   WHERE account_id = @accountid 
	 AND servertype = @mailserver_type

   --update the credential store
   IF((@credential_name IS NOT NULL) AND (@no_credential_change = 0))
   BEGIN
	  --Remove the unneed credential
	  IF(@username IS NULL)
	  BEGIN
		 SET @credential_id = NULL
		 EXEC @rc = msdb.dbo.sysmail_drop_user_credential_sp 
						@credential_name = @credential_name
	  END
	  -- Update the credential
	  ELSE
	  BEGIN
		 EXEC @rc = msdb.dbo.sysmail_alter_user_credential_sp
						@credential_name = @credential_name,
						@username = @username,
						@password = @password
	  END

	  IF(@rc <> 0)
	  BEGIN 
		 ROLLBACK TRAN
		 RETURN (1)
	  END
   END
   -- create a new credential if one doesn't exist
   ELSE IF(@credential_name IS NULL AND @username IS NOT NULL)
   BEGIN
	  EXEC @rc = msdb.dbo.sysmail_create_user_credential_sp
					 @username = @username,
					 @password = @password,
					 @credential_id = @credential_id  OUTPUT
	  IF(@rc <> 0)
	  BEGIN 
		 ROLLBACK TRAN
		 RETURN (1)
	  END
   END

   -- update server table
   IF (@no_credential_change = 0)
   BEGIN
      IF (@mailserver_name IS NOT NULL)
	  UPDATE msdb.dbo.sysmail_server 
	  SET servername=@mailserver_name, port=@port, username=@username, credential_id = @credential_id, use_default_credentials = @use_default_credentials, enable_ssl = @enable_ssl, timeout = @timeout
	  WHERE account_id=@accountid AND servertype=@mailserver_type
   
      ELSE
	  UPDATE msdb.dbo.sysmail_server 
	  SET port=@port, username=@username, credential_id = @credential_id, use_default_credentials = @use_default_credentials, enable_ssl = @enable_ssl, timeout = @timeout
	  WHERE account_id=@accountid AND servertype=@mailserver_type
   END
   ELSE
   BEGIN
      -- Since no_credential_change is true, @username is empty. Do not pass it.
      -- If we gave @username, sysmail_server would be set with the empty @username.
      IF (@mailserver_name IS NOT NULL)
         UPDATE msdb.dbo.sysmail_server 
         SET servername=@mailserver_name, port=@port, credential_id = @credential_id, use_default_credentials = @use_default_credentials, enable_ssl = @enable_ssl, timeout = @timeout
         WHERE account_id=@accountid AND servertype=@mailserver_type
   
      ELSE
         UPDATE msdb.dbo.sysmail_server 
         SET port=@port, credential_id = @credential_id, use_default_credentials = @use_default_credentials, enable_ssl = @enable_ssl, timeout = @timeout
         WHERE account_id=@accountid AND servertype=@mailserver_type
   END
      
   COMMIT TRAN
   RETURN(0)
go


PRINT ''
PRINT 'Creating procedure sysmail_delete_account_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_delete_account_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_delete_account_sp
go

CREATE PROCEDURE dbo.sysmail_delete_account_sp
   @account_id int = NULL, -- must provide either id or name
   @account_name sysname = NULL
AS
   SET NOCOUNT ON
  
   DECLARE @rc int
   DECLARE @accountid int
   DECLARE @credential_name sysname

   exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 0, 0, @accountid OUTPUT
   IF @rc <> 0
      RETURN(1)

   -- Get all the credentials has been stored for this account
   DECLARE cur CURSOR FOR
      SELECT c.name
      FROM sys.credentials as c
      JOIN msdb.dbo.sysmail_server as ms
         ON c.credential_id = ms.credential_id
      WHERE account_id = @accountid

   OPEN cur
   FETCH NEXT FROM cur INTO @credential_name
   WHILE @@FETCH_STATUS = 0
   BEGIN
      -- drop the credential
      EXEC msdb.dbo.sysmail_drop_user_credential_sp @credential_name = @credential_name

      FETCH NEXT FROM cur INTO @credential_name
   END

   CLOSE cur
   DEALLOCATE cur
     
   DELETE FROM msdb.dbo.sysmail_account
   WHERE account_id = @accountid
   
   RETURN(0)
go


PRINT ''
PRINT 'Creating procedure sysmail_help_account_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_help_account_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_help_account_sp
go

CREATE PROCEDURE dbo.sysmail_help_account_sp
   @account_id int = NULL,
   @account_name sysname = NULL
AS
   SET NOCOUNT ON

   DECLARE @rc int
   DECLARE @accountid int
   exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 1, 0, @accountid OUTPUT
   IF @rc <> 0
      RETURN(1)

   IF (@accountid IS NOT NULL)
      SELECT a.account_id, a.name, a.description, a.email_address, a.display_name, a.replyto_address, s.servertype, s.servername, s.port, s.username, s.use_default_credentials, s.enable_ssl 
      FROM msdb.dbo.sysmail_account a, msdb.dbo.sysmail_server s
      WHERE a.account_id = s.account_id AND a.account_id = @accountid
      
   ELSE
      SELECT a.account_id, a.name, a.description, a.email_address, a.display_name, a.replyto_address, s.servertype, s.servername, s.port, s.username, s.use_default_credentials, s.enable_ssl
      FROM msdb.dbo.sysmail_account a, msdb.dbo.sysmail_server s
      WHERE a.account_id = s.account_id

   RETURN(0)
go

-- Access to the complete account info. Required by databasemail.exe
PRINT ''
PRINT 'Creating procedure sysmail_help_admin_account_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_help_admin_account_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_help_admin_account_sp
go

CREATE PROCEDURE dbo.sysmail_help_admin_account_sp
@account_id int
AS
   SET NOCOUNT ON

   DECLARE @rc         int,
	  @acc_id         int,
	  @name           sysname,
	  @description    nvarchar(256),
	  @email_address  nvarchar(128),
	  @display_name   nvarchar(128),
	  @replyto_address nvarchar(128),  
	  @servertype     sysname,
	  @servername     sysname,
	  @port           int,
	  @username       nvarchar(128),
	  @passwordsize   int,  
	  @cryptpassword  varbinary(1024),
	  @credential_id  int,
	  @use_default_credentials bit,
	  @enable_ssl     bit,
	  @timeout     int

	SET @passwordsize = 0

   EXEC @rc = msdb.dbo.sysmail_verify_account_sp @account_id, NULL, 1, 0, NULL
   IF @rc <> 0
	  RETURN(1)

   SELECT 
	  @acc_id         = a.account_id,
	  @name           = a.name, 
	  @description    = a.description, 
	  @email_address  = a.email_address, 
	  @display_name   = a.display_name, 
	  @replyto_address= a.replyto_address,
	  @servertype     = s.servertype, 
	  @servername     = s.servername, 
	  @port           = s.port, 
	  @username       = s.username,
	  @credential_id  = s.credential_id,
	  @use_default_credentials = s.use_default_credentials,
	  @enable_ssl     = s.enable_ssl,
	  @timeout        = s.timeout
   FROM msdb.dbo.sysmail_account a, msdb.dbo.sysmail_server s
   WHERE (a.account_id = s.account_id) AND 
	  (a.account_id = @account_id)
    
	--get the encrypted password if required 
	IF(@username IS NOT NULL)
	BEGIN
		DECLARE @cred TABLE([size] INT, blob VARBINARY(1024));

		INSERT @cred
		EXEC @rc = master.dbo.sp_PostAgentInfo @credential_id
		IF @rc <> 0
		BEGIN
		  RETURN(1)
		END
        
		SELECT @passwordsize = [size], @cryptpassword = [blob] 
		FROM @cred
	END
    
	--All done return result
	SELECT
		@acc_id         as 'account_id',
		@name           as 'name',  
		@description    as 'description',
		@email_address  as 'email_address',
		@display_name   as 'display_name',
		@replyto_address as 'replyto_address',
		@servertype     as 'servertype',
		@servername     as 'servername',
		@port           as 'port',
		@username       as 'username',
		@passwordsize   as 'password_size',
		@cryptpassword  as 'password_crypt',
		@use_default_credentials as 'use_default_credentials',
		@enable_ssl     as 'enable_ssl',
		@timeout        as 'timeout'

   RETURN(0)
go

PRINT ''
PRINT 'Creating procedure sysmail_add_profileaccount_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_add_profileaccount_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_add_profileaccount_sp
go

CREATE PROCEDURE dbo.sysmail_add_profileaccount_sp
   @profile_id int = NULL, -- must provide id or name
   @profile_name sysname = NULL,
   @account_id int = NULL, -- must provide id or name
   @account_name sysname = NULL,
   @sequence_number int -- account with the lowest sequence number is picked as default
AS
   SET NOCOUNT ON

   DECLARE @rc int
   DECLARE @profileid int
   DECLARE @accountid int

   exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 0, 0, @profileid OUTPUT
   IF @rc <> 0
      RETURN(1)

   exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 0, 0, @accountid OUTPUT
   IF @rc <> 0
      RETURN(2)

   -- insert new account record, rely on primary key constraint to error out
   INSERT INTO msdb.dbo.sysmail_profileaccount (profile_id,account_id,sequence_number)
   VALUES (@profileid,@accountid,@sequence_number)
   
   RETURN(0)
go

PRINT ''
PRINT 'Creating procedure sysmail_update_profileaccount_sp...'
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sysmail_update_profileaccount_sp')
              AND (type = 'P')))
  DROP PROCEDURE dbo.sysmail_update_profileaccount_sp
go

CREATE PROCEDURE dbo.sysmail_update_profileaccount_sp
   @profile_id int = NULL, -- must provide id or name
   @profile_name sysname = NULL,
   @account_id int = NULL, -- must provide id or name
   @account_name sysname = NULL,
   @sequence_number int -- account with the lowest sequence number is picked as default
AS
   SET NOCOUNT ON

   DECLARE @rc int
   DECLARE @profileid int
   DECLARE @accountid int

   exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 0, 0, @profileid OUTPUT
   IF @rc <> 0
      RETURN(1)

   exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 0, 0, @accountid OUTPUT
   IF @rc <> 0
      RETURN(2)

   IF (@sequence_number IS NULL)
   BEGIN
      RAISERROR(14611, -1, -1)   
      RETURN(3)
   END
   
   UPDATE msdb.dbo.sysmail_profileaccount
   SET sequence_number=@sequence_number
   WHERE profile_id=@profileid AND account_id=@accountid
   
   RETURN(0)
go

PRINT ''
PRINT 'Creating procedure sysmail_delete_profileaccount_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_delete_profileaccount_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_delete_profileaccount_sp
go

CREATE PROCEDURE dbo.sysmail_delete_profileaccount_sp
   @profile_id int = NULL, -- must provide id or name
   @profile_name sysname = NULL,
   @account_id int = NULL, -- must provide id or name
   @account_name sysname = NULL
AS
   SET NOCOUNT ON

   DECLARE @rc int
   DECLARE @profileid int
   DECLARE @accountid int

   exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 1, 0, @profileid OUTPUT
   IF @rc <> 0
      RETURN(1)

   exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 1, 0, @accountid OUTPUT
   IF @rc <> 0
      RETURN(2)

   IF (@profileid IS NOT NULL AND @accountid IS NOT NULL) -- both parameters supplied for deletion
      DELETE FROM msdb.dbo.sysmail_profileaccount
      WHERE profile_id=@profileid AND account_id=@accountid

   ELSE IF (@profileid IS NOT NULL) -- profile id is supplied
      DELETE FROM msdb.dbo.sysmail_profileaccount
      WHERE profile_id=@profileid

   ELSE IF (@accountid IS NOT NULL) -- account id is supplied
      DELETE FROM msdb.dbo.sysmail_profileaccount
      WHERE account_id=@accountid

   ELSE -- no parameters are supplied for deletion
   BEGIN
      RAISERROR(14608, -1, -1, 'profile', 'account')  
      RETURN(3)   
   END

   RETURN(0)
go

PRINT ''
PRINT 'Creating procedure sysmail_help_profileaccount_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_help_profileaccount_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_help_profileaccount_sp
go

CREATE PROCEDURE dbo.sysmail_help_profileaccount_sp
   @profile_id int = NULL, -- must provide id or name
   @profile_name sysname = NULL,
   @account_id int = NULL, -- must provide id or name
   @account_name sysname = NULL
AS
   SET NOCOUNT ON

   DECLARE @rc int
   DECLARE @profileid int
   DECLARE @accountid int

   exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 1, 0, @profileid OUTPUT
   IF @rc <> 0
      RETURN(1)

   exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 1, 0, @accountid OUTPUT
   IF @rc <> 0
      RETURN(2)

   IF (@profileid IS NOT NULL AND @accountid IS NOT NULL)
      SELECT p.profile_id,profile_name=p.name,a.account_id,account_name=a.name,c.sequence_number
      FROM msdb.dbo.sysmail_profile p, msdb.dbo.sysmail_account a, msdb.dbo.sysmail_profileaccount c
      WHERE p.profile_id=c.profile_id AND a.account_id=c.account_id AND c.profile_id=@profileid AND c.account_id=@accountid
   
   ELSE IF (@profileid IS NOT NULL)
      SELECT p.profile_id,profile_name=p.name,a.account_id,account_name=a.name,c.sequence_number
      FROM msdb.dbo.sysmail_profile p, msdb.dbo.sysmail_account a, msdb.dbo.sysmail_profileaccount c
      WHERE p.profile_id=c.profile_id AND a.account_id=c.account_id AND c.profile_id=@profileid

   ELSE IF (@accountid IS NOT NULL)
      SELECT p.profile_id,profile_name=p.name,a.account_id,account_name=a.name,c.sequence_number
      FROM msdb.dbo.sysmail_profile p, msdb.dbo.sysmail_account a, msdb.dbo.sysmail_profileaccount c
      WHERE p.profile_id=c.profile_id AND a.account_id=c.account_id AND c.account_id=@accountid

   ELSE
      SELECT p.profile_id,profile_name=p.name,a.account_id,account_name=a.name,c.sequence_number
      FROM msdb.dbo.sysmail_profile p, msdb.dbo.sysmail_account a, msdb.dbo.sysmail_profileaccount c
      WHERE p.profile_id=c.profile_id AND a.account_id=c.account_id
      
   RETURN(0)
go

PRINT ''
PRINT 'Creating procedure sysmail_configure_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_configure_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_configure_sp
go

CREATE PROCEDURE dbo.sysmail_configure_sp
   @parameter_name nvarchar(256),
   @parameter_value nvarchar(256),
   @description nvarchar(256) = NULL
AS
   SET NOCOUNT ON
   
   IF (@description IS NOT NULL)
      UPDATE msdb.dbo.sysmail_configuration
      SET paramvalue=@parameter_value, description=@description
      WHERE paramname=@parameter_name
   ELSE
      UPDATE msdb.dbo.sysmail_configuration
      SET paramvalue=@parameter_value
      WHERE paramname=@parameter_name

   RETURN(0)
go

PRINT ''
PRINT 'Creating procedure sysmail_help_configure_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_help_configure_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_help_configure_sp
go

CREATE PROCEDURE dbo.sysmail_help_configure_sp
   @parameter_name nvarchar(256) = NULL
AS
   SET NOCOUNT ON

    SELECT paramname, paramvalue, description
    FROM msdb.dbo.sysmail_configuration
    WHERE paramname = ISNULL(@parameter_name, paramname)

    RETURN(0)
go


PRINT ''
PRINT 'Creating procedure sysmail_help_configure_value_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_help_configure_value_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_help_configure_value_sp
go

CREATE PROCEDURE dbo.sysmail_help_configure_value_sp
   @parameter_name nvarchar(256),
   @parameter_value nvarchar(256) OUTPUT
AS
   SET NOCOUNT ON
   SET @parameter_value = NULL
    
   IF (@parameter_name IS NULL)
   BEGIN
      RAISERROR(14618, 16, 1, '@parameter_name')
      RETURN(1)   
   END

    SELECT @parameter_value = paramvalue
    FROM msdb.dbo.sysmail_configuration
    WHERE paramname = @parameter_name

   RETURN(0)
go


PRINT ''
PRINT 'Creating procedure sysmail_add_principalprofile_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_add_principalprofile_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_add_principalprofile_sp
go

CREATE PROCEDURE dbo.sysmail_add_principalprofile_sp
   @principal_id int = NULL, -- must provide id or name
   @principal_name sysname = NULL,
   @profile_id int = NULL, -- must provide id or name
   @profile_name sysname = NULL,
   @is_default bit
AS
   SET NOCOUNT ON

   DECLARE @rc int
   DECLARE @principal_sid varbinary(85)
   DECLARE @profileid int

   IF (@principal_id IS NOT NULL AND @principal_id = 0) OR (@principal_name IS NOT NULL AND @principal_name = N'public')
   BEGIN
      IF (@principal_id IS NOT NULL AND @principal_id <> 0) OR (@principal_name IS NOT NULL AND @principal_name <> N'public')
      BEGIN
         RAISERROR(14605, -1, -1, 'principal')  -- id and name do not match
      END
      SET @principal_sid = 0x00 -- public
   END
   ELSE
   BEGIN
      exec @rc = msdb.dbo.sysmail_verify_principal_sp @principal_id, @principal_name, 0, @principal_sid OUTPUT
      IF @rc <> 0
         RETURN(2)
   END
   
   exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 0, 0, @profileid OUTPUT
   IF @rc <> 0
      RETURN(3)

   -- insert new account record, rely on primary key constraint to error out
   INSERT INTO msdb.dbo.sysmail_principalprofile (principal_sid,profile_id,is_default)
   VALUES (@principal_sid,@profileid,@is_default)

   IF (@is_default IS NOT NULL AND @is_default = 1 )
   BEGIN
      -- a principal can only have one default profile so reset other, if there are any
      UPDATE msdb.dbo.sysmail_principalprofile
      SET is_default=0
      WHERE principal_sid = @principal_sid AND profile_id <> @profileid
   END
   RETURN(0)
go

PRINT ''
PRINT 'Creating procedure sysmail_update_principalprofile_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_update_principalprofile_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_update_principalprofile_sp
go

CREATE PROCEDURE dbo.sysmail_update_principalprofile_sp
   @principal_id int = NULL, -- must provide id or name
   @principal_name sysname = NULL,
   @profile_id int = NULL, -- must provide id or name
   @profile_name sysname = NULL,
   @is_default bit
AS
   SET NOCOUNT ON

   DECLARE @rc int
   DECLARE @principal_sid varbinary(85)
   DECLARE @profileid int

   IF (@principal_id IS NOT NULL AND @principal_id = 0) OR (@principal_name IS NOT NULL AND @principal_name = N'public')
   BEGIN
      IF (@principal_id IS NOT NULL AND @principal_id <> 0) OR (@principal_name IS NOT NULL AND @principal_name <> N'public')
      BEGIN
         RAISERROR(14605, -1, -1, 'principal')  -- id and name do not match
      END
      SET @principal_sid = 0x00 -- public
   END
   ELSE
   BEGIN
      exec @rc = msdb.dbo.sysmail_verify_principal_sp @principal_id, @principal_name, 0, @principal_sid OUTPUT
      IF @rc <> 0
         RETURN(1)
   END
   
   exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 0, 0, @profileid OUTPUT
   IF @rc <> 0
      RETURN(2)

   UPDATE msdb.dbo.sysmail_principalprofile
   SET is_default=@is_default
   WHERE principal_sid = @principal_sid AND profile_id = @profileid

   IF (@is_default IS NOT NULL AND @is_default = 1)
   BEGIN
      -- a principal can only have one default profile so reset others (if there are any)
      UPDATE msdb.dbo.sysmail_principalprofile
      SET is_default=0
      WHERE principal_sid = @principal_sid AND profile_id <> @profileid
   END

   RETURN(0)
go

PRINT ''
PRINT 'Creating procedure sysmail_delete_principalprofile_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_delete_principalprofile_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_delete_principalprofile_sp
go

CREATE PROCEDURE dbo.sysmail_delete_principalprofile_sp
   @principal_id int = NULL, -- must provide id or name
   @principal_name sysname = NULL,
   @profile_id int = NULL, -- must provide id or name
   @profile_name sysname = NULL
AS
   SET NOCOUNT ON

   DECLARE @rc int
   DECLARE @principal_sid varbinary(85)
   DECLARE @profileid int

   IF (@principal_id IS NOT NULL AND @principal_id = 0) OR (@principal_name IS NOT NULL AND @principal_name = N'public')
   BEGIN
      IF (@principal_id IS NOT NULL AND @principal_id <> 0) OR (@principal_name IS NOT NULL AND @principal_name <> N'public')
      BEGIN
         RAISERROR(14605, -1, -1, 'principal')  -- id and name do not match
      END
      SET @principal_sid = 0x00 -- public
   END
   ELSE
   BEGIN
      IF (@principal_id IS NOT NULL OR @principal_name IS NOT NULL) 
      BEGIN
         exec @rc = msdb.dbo.sysmail_verify_principal_sp @principal_id, @principal_name, 1, @principal_sid OUTPUT
         IF @rc <> 0
            RETURN(1)
      END
   END
   
   exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 1, 0, @profileid OUTPUT
   IF @rc <> 0
      RETURN(2)

   IF ((@principal_id IS NOT NULL OR @principal_name IS NOT NULL) AND @profileid IS NOT NULL)
   BEGIN
      DELETE FROM msdb.dbo.sysmail_principalprofile WHERE principal_sid=@principal_sid AND profile_id = @profileid
   END
   ELSE IF (@principal_id IS NOT NULL OR @principal_name IS NOT NULL)
   BEGIN
      DELETE FROM msdb.dbo.sysmail_principalprofile WHERE principal_sid=@principal_sid
   END
   ELSE IF (@profileid IS NOT NULL)
   BEGIN
      DELETE FROM msdb.dbo.sysmail_principalprofile WHERE profile_id = @profileid
   END
   ELSE -- no parameters are supplied for deletion
   BEGIN
      RAISERROR(14608, -1, -1, 'principal', 'profile') 
      RETURN(6)
   END
   
   RETURN(0)
go

PRINT ''
PRINT 'Creating procedure sysmail_help_principalprofile_sp...'
IF (NOT OBJECT_ID(N'dbo.sysmail_help_principalprofile_sp', 'P') IS NULL)
  DROP PROCEDURE dbo.sysmail_help_principalprofile_sp
go

CREATE PROCEDURE dbo.sysmail_help_principalprofile_sp
   @principal_id int = NULL, -- must provide id or name
   @principal_name sysname = NULL,
   @profile_id int = NULL, -- must provide id or name
   @profile_name sysname = NULL
AS
   SET NOCOUNT ON

   DECLARE @rc int
   DECLARE @principal_sid varbinary(85)
   DECLARE @profileid int

   exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 1, 0, @profileid OUTPUT
   IF @rc <> 0
      RETURN(1)

   IF (@principal_id IS NOT NULL AND @principal_id = 0) OR (@principal_name IS NOT NULL AND @principal_name = N'public')
   BEGIN
      IF (@principal_id IS NOT NULL AND @principal_id <> 0) OR (@principal_name IS NOT NULL AND @principal_name <> N'public')
      BEGIN
         RAISERROR(14605, -1, -1, 'principal')  -- id and name do not match
      END
      SET @principal_sid = 0x00 -- public

      IF (@profileid IS NOT NULL)
      BEGIN
         SELECT principal_id=0, 
                principal_name = N'public', 
                prof.profile_id, 
                profile_name=prof.name, 
                prin.is_default
         FROM msdb.dbo.sysmail_principalprofile prin, msdb.dbo.sysmail_profile prof 
         WHERE prin.profile_id=prof.profile_id AND 
               prin.principal_sid = @principal_sid AND 
               prof.profile_id=@profileid
      END
      ELSE
      BEGIN
         SELECT principal_id=0, 
                principal_name = N'public', 
                prof.profile_id, 
                profile_name=prof.name, 
                prin.is_default
         FROM msdb.dbo.sysmail_principalprofile prin, msdb.dbo.sysmail_profile prof 
         WHERE prin.profile_id=prof.profile_id AND 
               prin.principal_sid = @principal_sid
      END
   END
   ELSE -- non-public profiles
   BEGIN
      IF (@principal_id IS NOT NULL OR @principal_name IS NOT NULL)      
      BEGIN
            exec @rc = msdb.dbo.sysmail_verify_principal_sp @principal_id, @principal_name, 1, @principal_sid OUTPUT
            IF @rc <> 0
               RETURN(2)
      END

      IF ((@principal_id IS NOT NULL OR @principal_name IS NOT NULL) AND @profileid IS NOT NULL)
      BEGIN
            SELECT principal_id=dbprin.principal_id,
                   principal_name=dbprin.name,
                   prof.profile_id,
                   profile_name=prof.name,
                   prinprof.is_default
            FROM sys.database_principals dbprin, msdb.dbo.sysmail_principalprofile prinprof, msdb.dbo.sysmail_profile prof
            WHERE dbprin.principal_id = dbo.get_principal_id(prinprof.principal_sid) AND
                  (prinprof.principal_sid = @principal_sid OR prinprof.principal_sid = 0x00) AND
                  prof.profile_id = prinprof.profile_id AND
                  prinprof.profile_id = @profileid
      END
      ELSE IF (@principal_id IS NOT NULL OR @principal_name IS NOT NULL)
      BEGIN
            SELECT principal_id=dbprin.principal_id,
                   principal_name=dbprin.name,
                   prof.profile_id,
                   profile_name=prof.name,
                   prinprof.is_default
            FROM sys.database_principals dbprin, msdb.dbo.sysmail_principalprofile prinprof, msdb.dbo.sysmail_profile prof
            WHERE dbprin.principal_id = dbo.get_principal_id(prinprof.principal_sid) AND
                  (prinprof.principal_sid = @principal_sid OR prinprof.principal_sid = 0x00) AND
                  prof.profile_id = prinprof.profile_id
      END
      ELSE IF (@profileid IS NOT NULL)
      BEGIN
            SELECT principal_id=dbprin.principal_id,
                   principal_name=dbprin.name,
                   prof.profile_id,
                   profile_name=prof.name,
                   prinprof.is_default
            FROM sys.database_principals dbprin, msdb.dbo.sysmail_principalprofile prinprof, msdb.dbo.sysmail_profile prof
            WHERE dbprin.principal_id = dbo.get_principal_id(prinprof.principal_sid) AND
                  prof.profile_id = prinprof.profile_id AND
                  prinprof.profile_id = @profileid
      END
      ELSE -- no parameters are supplied for filtering
      BEGIN
            SELECT principal_id=dbprin.principal_id,
                   principal_name=dbprin.name,
                   prof.profile_id,
                   profile_name=prof.name,
                   prinprof.is_default
            FROM sys.database_principals dbprin, msdb.dbo.sysmail_principalprofile prinprof, msdb.dbo.sysmail_profile prof
            WHERE dbprin.principal_id = dbo.get_principal_id(prinprof.principal_sid) AND
                  prof.profile_id = prinprof.profile_id
      END
   END
   RETURN(0)
go

-----------------------------------------------------------
-- Database Mail: mail host database stored procedures
-----------------------------------------------------------

-----------------------------------------------------------
-- procedure sysmail_logmailevent_sp
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sysmail_logmailevent_sp', 'P') IS NULL
    DROP PROCEDURE dbo.sysmail_logmailevent_sp
GO
-----
PRINT 'Creating sysmail_logmailevent_sp'
-----
GO
-- sysmail_logmailevent_sp : inserts an entry in the sysmail_log table
CREATE PROCEDURE sysmail_logmailevent_sp
    @event_type     INT,
    @description    NVARCHAR(max)   = NULL, 
    @process_id     INT             = NULL,
    @mailitem_id    INT             = NULL,
    @account_id     INT             = NULL
AS
    SET NOCOUNT ON

    --Try and get the optional logging level for the DatabaseMail process
    DECLARE @loggingLevel nvarchar(256)
    EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'LoggingLevel', 
                                                  @parameter_value = @loggingLevel OUTPUT

    DECLARE @loggingLevelInt int   
    SET @loggingLevelInt = dbo.ConvertToInt(@loggingLevel, 3, 2) 

    IF (@event_type = 3) OR                           -- error
       (@event_type = 2 AND @loggingLevelInt >= 2) OR -- warning with extended logging
       (@event_type = 1 AND @loggingLevelInt >= 2) OR -- info with extended logging
       (@event_type = 0 AND @loggingLevelInt >= 3)    -- success with verbose logging
    BEGIN
       INSERT sysmail_log(event_type, description, process_id, mailitem_id, account_id) 
       VALUES(@event_type, @description, @process_id , @mailitem_id, @account_id)
    END
RETURN 0
GO

-----------------------------------------------------------
-- procedure sysmail_start_sp
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sysmail_start_sp', 'P') IS NULL
    DROP PROCEDURE dbo.sysmail_start_sp
GO
-----
PRINT 'Creating sysmail_start_sp'
-----
GO
-- sysmail_start_sp : allows databasemail to process mail from the queue 
CREATE PROCEDURE sysmail_start_sp
AS
    SET NOCOUNT ON
    DECLARE @rc INT 
   DECLARE @localmessage nvarchar(255)

    ALTER QUEUE ExternalMailQueue WITH STATUS = ON
    SELECT @rc = @@ERROR
    IF(@rc = 0)
    BEGIN
      ALTER QUEUE ExternalMailQueue WITH ACTIVATION (STATUS = ON);
       SET @localmessage = FORMATMESSAGE(14639, SUSER_SNAME())
       exec msdb.dbo.sysmail_logmailevent_sp @event_type=1, @description=@localmessage
    END
RETURN @rc
GO

-----------------------------------------------------------
-- procedure sysmail_stop_sp
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sysmail_stop_sp', 'P') IS NULL
    DROP PROCEDURE dbo.sysmail_stop_sp
GO
-----
PRINT 'Creating sysmail_stop_sp'
-----
GO
-- sysmail_stop_sp : stops the DatabaseMail process. Mail items remain in the queue until sqlmail started 
CREATE PROCEDURE sysmail_stop_sp
AS
    SET NOCOUNT ON
    DECLARE @rc INT
   DECLARE @localmessage nvarchar(255)
  
    ALTER QUEUE ExternalMailQueue WITH ACTIVATION (STATUS = OFF);
    SELECT @rc = @@ERROR
    IF(@rc = 0)
    BEGIN
       ALTER QUEUE ExternalMailQueue WITH STATUS = OFF;
       SELECT @rc = @@ERROR
       IF(@rc = 0)
       BEGIN
          SET @localmessage = FORMATMESSAGE(14640, SUSER_SNAME())
          exec msdb.dbo.sysmail_logmailevent_sp @event_type=1, @description=@localmessage
       END
    END
RETURN @rc
GO


-----------------------------------------------------------
-- procedure sysmail_help_status_sp
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sysmail_help_status_sp', 'P') IS NULL
    DROP PROCEDURE dbo.sysmail_help_status_sp
GO
-----
PRINT 'Creating sysmail_help_status_sp'
-----
GO
CREATE PROCEDURE sysmail_help_status_sp
  WITH EXECUTE AS 'dbo'
AS
BEGIN
    IF NOT EXISTS (SELECT * FROM sys.service_queues WHERE name = N'ExternalMailQueue' AND is_receive_enabled = 1)
       SELECT 'STOPPED' AS Status
    ELSE
       SELECT 'STARTED' AS Status
END
GO


-----------------------------------------------------------
-- procedure sysmail_help_queue_sp
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sysmail_help_queue_sp', 'P') IS NULL
    DROP PROCEDURE dbo.sysmail_help_queue_sp
GO
-----
PRINT 'Creating sysmail_help_queue_sp'
-----
GO
CREATE PROCEDURE sysmail_help_queue_sp
   @queue_type nvarchar(6) = NULL -- Type of queue
AS
BEGIN

   SELECT @queue_type       = LTRIM(RTRIM(@queue_type))
   IF @queue_type           = '' SELECT @queue_type = NULL

   IF ( (@queue_type IS NOT NULL) AND
         (LOWER(@queue_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ( N'mail', N'status') ) )
   BEGIN
        RAISERROR(14266, -1, -1, '@queue_type', 'mail, status')
      RETURN(1) -- Failure
   END   

   DECLARE @depth      int
   DECLARE @temp TABLE (
                        queue_type nvarchar(6),
                        length INT NOT NULL, 
                        state nvarchar(64), 
                        last_empty_rowset_time DATETIME,
                        last_activated_time DATETIME
                       )
  
   IF ( (@queue_type IS NULL) OR (LOWER(@queue_type collate SQL_Latin1_General_CP1_CS_AS) = N'mail' ) )
   BEGIN
      SET @depth = (SELECT COUNT(*) FROM ExternalMailQueue WITH (NOWAIT NOLOCK READUNCOMMITTED))

      INSERT INTO @temp
         SELECT 
                N'mail',
                @depth, 
            qm.state as state,
            qm.last_empty_rowset_time as last_empty_rowset_time,
            qm.last_activated_time as last_activated_time
         FROM sys.dm_broker_queue_monitors qm
         JOIN sys.service_queues sq ON sq.object_id = qm.queue_id
         WHERE sq.name = 'ExternalMailQueue'
   END

   IF ( (@queue_type IS NULL) OR (LOWER(@queue_type collate SQL_Latin1_General_CP1_CS_AS) = N'status' ) )
   BEGIN
      SET @depth = (SELECT COUNT(*) FROM InternalMailQueue WITH (NOWAIT NOLOCK READUNCOMMITTED))

      INSERT INTO @temp
         SELECT 
                N'status',
                @depth, 
            qm.state as state,
            qm.last_empty_rowset_time as last_empty_rowset_time,
            qm.last_activated_time as last_activated_time
         FROM sys.dm_broker_queue_monitors qm
         JOIN sys.service_queues sq ON sq.object_id = qm.queue_id
         WHERE sq.name = 'InternalMailQueue'
   END

   SELECT * from @temp
END
GO

-----------------------------------------------------------
-- procedure sp_SendMailMessage
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sp_SendMailMessage', 'P') IS NULL
    DROP PROCEDURE dbo.sp_SendMailMessage
GO
-----
PRINT 'Creating sp_SendMailMessage'
-----
GO
-- sp_SendMailMessage : Sends a request on the mail items SSB queue
CREATE PROCEDURE sp_SendMailMessage
    @contract_name sysname, -- Name of contract
    @message_type sysname,  -- Type of message
    @request varchar(max) -- XML message to send
  WITH EXECUTE AS 'dbo'
AS

SET NOCOUNT ON

DECLARE @conversationHandle uniqueidentifier;
DECLARE @error int

-- Start a conversation with the remote service
BEGIN DIALOG  @conversationHandle
    FROM SERVICE    [InternalMailService]
    TO SERVICE      'ExternalMailService'
    ON CONTRACT     @contract_name
    WITH ENCRYPTION=OFF

-- Check error
SET @error = @@ERROR
IF @error <> 0
BEGIN
    RETURN @error
END

-- Send message
;SEND ON CONVERSATION @conversationHandle MESSAGE TYPE @message_type (@request)

-- Check error
SET @error = @@ERROR
IF @error <> 0
BEGIN
    RETURN @error
END

RETURN 0
GO

-----------------------------------------------------------
-- procedure sp_isprohibited
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sp_isprohibited', 'P') IS NULL
    DROP PROCEDURE dbo.sp_isprohibited
GO
-----
PRINT 'Creating sp_isprohibited'
-----
GO
-- sp_isprohibited : To test if the attachment is prohibited or not.
--
CREATE PROCEDURE sp_isprohibited
   @attachment nvarchar(max),
   @prohibitedextensions nvarchar(1000)
AS

DECLARE @extensionIndex int
DECLARE @extensionName nvarchar(255)

IF (@attachment IS NOT NULL AND LEN(@attachment) > 0) 
BEGIN
    SET @prohibitedextensions = UPPER(@prohibitedextensions)

   -- find @extensionName: the substring between the last '.' and the end of the string
   SET @extensionIndex = 0
   WHILE (1=1)
   BEGIN
      DECLARE @lastExtensionIndex int
      SET @lastExtensionIndex = CHARINDEX('.', @attachment, @extensionIndex+1)
      IF (@lastExtensionIndex = 0)
         BREAK
      SET @extensionIndex = @lastExtensionIndex
   END

   IF (@extensionIndex > 0)
   BEGIN
      SET @extensionName = SUBSTRING(@attachment, @extensionIndex + 1, (LEN(@attachment) - @extensionIndex)) 
      SET @extensionName = UPPER(@extensionName)

      -- compare @extensionName with each extension in the comma-separated @prohibitedextensions list
      DECLARE @currentExtensionStart int
      DECLARE @currentExtensionEnd int

      SET @currentExtensionStart = 0
      SET @currentExtensionEnd = 0
      WHILE (@currentExtensionEnd < LEN(@prohibitedextensions))
      BEGIN
         SET @currentExtensionEnd = CHARINDEX(',', @prohibitedextensions, @currentExtensionStart)

         IF (@currentExtensionEnd = 0) -- we have reached the last extension of the list, or the list was empty
            SET @currentExtensionEnd = LEN(@prohibitedextensions)+1

         DECLARE @prohibitedExtension nvarchar(1000)
         SET @prohibitedExtension = SUBSTRING(@prohibitedextensions, @currentExtensionStart, @currentExtensionEnd - @currentExtensionStart) 
         SET @prohibitedExtension = RTRIM(LTRIM(@prohibitedExtension))

         IF( @extensionName = @prohibitedExtension )
            RETURN 1

         SET @currentExtensionStart = @currentExtensionEnd + 1
      END
   END

   RETURN 0
END
GO

-----------------------------------------------------------
-- procedure sp_SendMailQueues
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sp_SendMailQueues', 'P') IS NULL
    DROP PROCEDURE dbo.sp_SendMailQueues
GO
-----
PRINT 'Creating sp_SendMailQueues'
-----
GO
-- sp_SendMailQueues : Writes a send mail request to the queue.
--
CREATE  PROCEDURE sp_SendMailQueues
    @message_data      varchar(max) -- The request in XML
AS
BEGIN
    SET NOCOUNT ON

    DECLARE @contract_name nvarchar(128)
    DECLARE @message_type nvarchar(128)
    DECLARE @retValue int

    SET @message_type = '{//www.microsoft.com/databasemail/messages}SendMail'
    SET @contract_name = '//www.microsoft.com/databasemail/contracts/SendMail/v1.0'

    --Writes the message to the queue
    EXEC @retValue = sp_SendMailMessage @contract_name, @message_type, @message_data

    RETURN @retValue
END
GO


-----------------------------------------------------------
-- procedure sp_ProcessResponse
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sp_ProcessResponse', 'P') IS NULL
    DROP PROCEDURE dbo.sp_ProcessResponse
GO
-----
PRINT 'Creating sp_ProcessResponse'
-----
USE [msdb]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF


GO
-- Processes responses from dbmail 
--
CREATE PROCEDURE [dbo].[sp_ProcessResponse]
    @conv_handle        uniqueidentifier,
    @message_type_name  NVARCHAR(256),
    @xml_message_body   NVARCHAR(max)
AS
BEGIN
    DECLARE 
        @idoc               INT,
        @mailitem_id        INT,
        @sent_status        INT,
        @rc                 INT,
        @index              INT,
        @processId          INT,
        @sent_date          DATETIME,
        @localmessage       NVARCHAR(max),
        @LogMessage         NVARCHAR(max),
        @retry_hconv        uniqueidentifier,
        @paramStr           NVARCHAR(256),
        @accRetryDelay      INT

    --------------------------
    --Always send the response 
    ;SEND ON CONVERSATION @conv_handle MESSAGE TYPE @message_type_name (@xml_message_body)


    --
    -- Need to handle the case where a sent retry is requested. 
    -- This is done by setting a conversation timer, The timer with go off in the external queue

    -- Get the handle to the xml document
    EXEC @rc = sp_xml_preparedocument 
                    @idoc OUTPUT, 
                    @xml_message_body, 
                    N'<ns xmlns:responses="http://schemas.microsoft.com/databasemail/responses" />'
    IF(@rc <> 0)
    BEGIN
        --Log the error. The response has already sent to the Internal queue. 
        -- This will update the mail with the latest staus
        SET @localmessage = FORMATMESSAGE(14655, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body)
        exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage

        GOTO ErrorHandler;
    END

    -- Execute a SELECT statement that uses the OPENXML rowset provider to get the MailItemId and sent status.
    SELECT @mailitem_id     = MailItemId, 
           @sent_status     = SentStatus
    FROM OPENXML (@idoc, '/responses:SendMail', 1)
        WITH (MailItemId    INT      './MailItemId/@Id', 
              SentStatus    INT      './SentStatus/@Status')

    --Close the handle to the xml document
    EXEC sp_xml_removedocument @idoc

    IF(@mailitem_id IS NULL OR @sent_status IS NULL)
    BEGIN  
        --Log error and continue.
        SET @localmessage = FORMATMESSAGE(14652, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body)
        exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage

        GOTO ErrorHandler;
    END      

    -- Update the status of the email item
    UPDATE msdb.dbo.sysmail_mailitems
    SET sent_status = @sent_status
    WHERE mailitem_id = @mailitem_id

    --
    -- A send retry has been requested. Set a conversation timer
    IF(@sent_status = 3)
    BEGIN
        -- Get the associated mail item data for the given @conversation_handle (if it exists)
       SELECT @retry_hconv = conversation_handle
       FROM sysmail_send_retries as sr
            RIGHT JOIN sysmail_mailitems as mi
            ON sr.mailitem_id = mi.mailitem_id
       WHERE mi.mailitem_id = @mailitem_id

        --Must be the first retry attempt. Create a sysmail_send_retries record to track retries
        IF(@retry_hconv IS NULL)
        BEGIN
            INSERT sysmail_send_retries(conversation_handle, mailitem_id) --last_send_attempt_date
            VALUES(@conv_handle, @mailitem_id)
        END
        ELSE
        BEGIN
            --Update existing retry record
            UPDATE sysmail_send_retries
            SET last_send_attempt_date = GETDATE(),
                send_attempts = send_attempts + 1
            WHERE mailitem_id = @mailitem_id

        END

        --Get the global retry delay time
        EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'AccountRetryDelay', 
                                                    @parameter_value = @paramStr OUTPUT
        --ConvertToInt will return the default if @paramStr is null
        SET @accRetryDelay = dbo.ConvertToInt(@paramStr, 0x7fffffff, 300) -- 5 min default


        --Now set the dialog timer. This triggers the send retry
        ;BEGIN CONVERSATION TIMER (@conv_handle) TIMEOUT = @accRetryDelay 

    END
    ELSE
    BEGIN
        --Only end theconversation if a retry isn't being attempted
        END CONVERSATION @conv_handle
    END


    -- All done OK
    goto ExitProc;

    -----------------
    -- Error Handler
    -----------------
ErrorHandler:

    ------------------
    -- Exit Procedure
    ------------------
ExitProc:
    RETURN (@rc);

END
GO


-----------------------------------------------------------
-- procedure sp_MailItemResultSets
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sp_MailItemResultSets', 'P') IS NULL
    DROP PROCEDURE dbo.sp_MailItemResultSets
GO
-----
PRINT 'Creating sp_MailItemResultSets'
-----
GO
-- sp_MailItemResultSets : 
--  Sends back multiple rowsets with the mail items data
CREATE PROCEDURE sp_MailItemResultSets
    @mailitem_id            INT,
    @profile_id             INT,
    @conversation_handle    uniqueidentifier,
   @service_contract_name  NVARCHAR(256),
   @message_type_name      NVARCHAR(256)
AS
BEGIN
    SET NOCOUNT ON
   --
   -- Send back multiple rowsets with the mail items data

   ----
   -- 1) MessageTypeName
   SELECT @message_type_name  as 'message_type_name',
      @service_contract_name as 'service_contract_name',
      @conversation_handle   as 'conversation_handle',
      @mailitem_id           as 'mailitem_id'

   -----
   -- 2) The mail item record from sysmail_mailitems.
   SELECT 
      mi.mailitem_id,
      mi.profile_id,
      (SELECT name FROM msdb.dbo.sysmail_profile p WHERE p.profile_id = mi.profile_id) as 'profile_name',
      mi.recipients,
      mi.copy_recipients,
      mi.blind_copy_recipients,
      mi.subject,
      mi.body, 
      mi.body_format, 
      mi.importance,
      mi.sensitivity,
      ISNULL(sr.send_attempts, 0) as retry_attempt,
      ISNULL(mi.from_address, '') as from_address,
      ISNULL(mi.reply_to, '')     as reply_to
   FROM sysmail_mailitems as mi
      LEFT JOIN sysmail_send_retries as sr
         ON sr.mailitem_id = mi.mailitem_id 
   WHERE mi.mailitem_id = @mailitem_id

   -----
   -- 3) Account information 
   SELECT a.account_id,
         a.name
   FROM msdb.dbo.sysmail_profileaccount as pa
   JOIN msdb.dbo.sysmail_account as a
      ON pa.account_id = a.account_id
   WHERE pa.profile_id = @profile_id
   ORDER BY pa.sequence_number 

   -----
   -- 4) Attachments if any
   SELECT attachment_id,
      mailitem_id,
      filename,
      filesize,
      attachment
   FROM sysmail_attachments
   WHERE mailitem_id = @mailitem_id
   

   RETURN 0
END
GO

-----------------------------------------------------------
-- procedure sp_process_DialogTimer
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sp_process_DialogTimer', 'P') IS NULL
    DROP PROCEDURE dbo.sp_process_DialogTimer
GO
-----
PRINT 'Creating sp_process_DialogTimer'
-----
GO
-- Processes a DialogTimer message from the the queue. This is used for send mail retries.
-- Returns the mail to be send if a retry is required or logs a failure if max retry count has been reached 
CREATE PROCEDURE sp_process_DialogTimer
    @conversation_handle    uniqueidentifier,
   @service_contract_name  NVARCHAR(256),
   @message_type_name      NVARCHAR(256)
AS
BEGIN
    SET NOCOUNT ON

    -- Declare all variables
    DECLARE 
        @mailitem_id        INT,
        @profile_id         INT,
        @send_attempts      INT,
        @mail_request_date  DATETIME,
        @localmessage       NVARCHAR(255),
        @paramStr           NVARCHAR(256),
        @accRetryAttempts   INT

    -- Get the associated mail item data for the given @conversation_handle
   SELECT @mailitem_id     = mi.mailitem_id,
      @profile_id         = mi.profile_id,
        @mail_request_date  = mi.send_request_date,
      @send_attempts      = sr.send_attempts
   FROM sysmail_send_retries as sr
      JOIN sysmail_mailitems as mi
        ON sr.mailitem_id = mi.mailitem_id
   WHERE sr.conversation_handle = @conversation_handle

   -- If not able to find a mailitem_id return and move to the next message.
    -- This could happen if the mail items table was cleared before the retry was fired
   IF(@mailitem_id IS NULL)
   BEGIN
        --Log warning and continue
        -- "mailitem_id on conversation %s was not found in the sysmail_send_retries table. This mail item will not be sent."
        SET @localmessage = FORMATMESSAGE(14662, convert(NVARCHAR(50), @conversation_handle))
        exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@localmessage

        -- Resource clean-up
        IF(@conversation_handle IS NOT NULL)
        BEGIN
           END CONVERSATION @conversation_handle;
        END

        -- return code has special meaning and will be propagated to the calling process
        RETURN 2;
    END


    --Get the retry attempt count from sysmailconfig.
    EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'AccountRetryAttempts', 
                                                 @parameter_value = @paramStr OUTPUT
    --ConvertToInt will return the default if @paramStr is null
    SET @accRetryAttempts = dbo.ConvertToInt(@paramStr, 0x7fffffff, 1)


   --Check the send attempts and log and error if send_attempts >= retry count.
    --This shouldn't happen unless the retry configuration was changed
   IF(@send_attempts > @accRetryAttempts)
   BEGIN
        --Log warning and continue
        -- "Mail Id %d has exceeded the retry count. This mail item will not be sent."
        SET @localmessage = FORMATMESSAGE(14663, @mailitem_id)
        exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@localmessage, @mailitem_id=@mailitem_id

        -- Resource clean-up
        IF(@conversation_handle IS NOT NULL)
        BEGIN
           END CONVERSATION @conversation_handle;
        END

        -- return code has special meaning and will be propagated to the calling process
        RETURN 3;
    END

    -- This returns the mail item to the client as multiple result sets
    EXEC sp_MailItemResultSets
            @mailitem_id            = @mailitem_id,
            @profile_id             = @profile_id,
            @conversation_handle    = @conversation_handle,
           @service_contract_name  = @service_contract_name,
           @message_type_name      = @message_type_name

   
   RETURN 0

END
GO

-----------------------------------------------------------
-- procedure sp_readrequest
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sp_readrequest', 'P') IS NULL
    DROP PROCEDURE dbo.sp_readrequest
GO
-----
PRINT 'Creating sp_readrequest'
-----
GO
-- sp_readrequest : Reads a request from the the queue and returns its
--                  contents.
CREATE PROCEDURE sp_readrequest
    @receive_timeout    INT     -- the max time this read will wait for new message
AS
BEGIN
    SET NOCOUNT ON

    -- Table to store message information.
    DECLARE @msgs TABLE
    (
       [conversation_handle] uniqueidentifier,
       [service_contract_name] nvarchar(256),
       [message_type_name] nvarchar(256),
       [message_body] varbinary(max)
    )

    -- Declare variables to store row values fetched from the cursor
    DECLARE 
        @exit                   INT,
        @idoc                   INT,
        @mailitem_id            INT,
        @profile_id             INT,
        @conversation_handle    uniqueidentifier,
        @service_contract_name  NVARCHAR(256),
        @message_type_name      NVARCHAR(256),
        @xml_message_body       VARCHAR(max),
        @timediff               INT,
        @rec_timeout            INT,
        @start_time             DATETIME,
        @localmessage           NVARCHAR(256),
        @rc                     INT

    --Init variables
    SELECT @start_time = GETDATE(),
           @timediff = 0,
           @exit = 0,
           @rc = 0

    WHILE (@timediff <= @receive_timeout)
    BEGIN
        -- Delete all messages from @msgs table
        DELETE FROM @msgs

        -- Pick all message from queue
        SET @rec_timeout = @receive_timeout - @timediff
        WAITFOR(RECEIVE conversation_handle, service_contract_name, message_type_name, message_body 
                FROM ExternalMailQueue INTO @msgs), TIMEOUT @rec_timeout

        -- Check if there was some error in reading from queue
        SET @rc = @@ERROR
        IF (@rc <> 0)
        BEGIN
           IF(@rc < 4) -- make sure return code is not in reserved range (1-3)
               SET @rc = 4

           --Note: we will get error no. 9617 if the service queue 'ExternalMailQueue' is currently disabled.
           BREAK
        END

       --If there is no message in the queue return 1 to indicate a timeout
        IF NOT EXISTS(SELECT * FROM @msgs)
        BEGIN
          SET @rc = 1
          BREAK
        END

        -- Create a cursor to iterate through the messages.
        DECLARE msgs_cursor CURSOR FOR
        SELECT conversation_handle, 
            service_contract_name, 
            message_type_name,
            CONVERT(VARCHAR(MAX), message_body)
        FROM @msgs;

        -- Open the cursor
        OPEN msgs_cursor;

        -- Perform the first fetch and store the values in the variables.
        FETCH NEXT FROM msgs_cursor
        INTO 
            @conversation_handle,
            @service_contract_name,
            @message_type_name,
            @xml_message_body

        -- Check @@FETCH_STATUS to see if there are any more rows to fetch.
        WHILE (@@FETCH_STATUS = 0)
        BEGIN
            -- Check if the message is a send mail message
            IF(@message_type_name = N'{//www.microsoft.com/databasemail/messages}SendMail')
            BEGIN
                -- Get the handle to the xml document
                EXEC @rc = sp_xml_preparedocument 
                                @idoc OUTPUT, 
                                @xml_message_body, 
                                N'<ns xmlns:requests="http://schemas.microsoft.com/databasemail/requests" />'
                IF(@rc <> 0)
                BEGIN
                   IF(@rc < 4) -- make sure return code is not in reserved rang (1-3)
                      SET @rc = 4
                END
                ELSE -- parse the document and process its contents
                BEGIN
                   -- Execute a SELECT statement that uses the OPENXML rowset provider to get the MailItemId.
                   SELECT @mailitem_id = MailItemId
                   FROM OPENXML (@idoc, '/requests:SendMail', 1)
                        WITH (MailItemId INT './MailItemId')

                   --Close the handle to the xml document
                   EXEC sp_xml_removedocument @idoc

                   -- get account information 
                   SELECT @profile_id = profile_id
                   FROM sysmail_mailitems 
                   WHERE mailitem_id = @mailitem_id

                   IF(@profile_id IS NULL) -- mail item has been deleted from the database
                   BEGIN
                      -- log warning
                      SET @localmessage = FORMATMESSAGE(14667, convert(NVARCHAR(50), @mailitem_id))
                      exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@localmessage

                      -- Resource clean-up
                      IF(@conversation_handle IS NOT NULL)
                         END CONVERSATION @conversation_handle;
                   
                      -- return code has special meaning and will be propagated to the calling process
                      SET @rc = 2
                   END
                   ELSE
                   BEGIN
                      -- This returns the mail item to the client as multiple result sets
                      EXEC sp_MailItemResultSets
                           @mailitem_id            = @mailitem_id,
                           @profile_id             = @profile_id,
                           @conversation_handle    = @conversation_handle,
                           @service_contract_name  = @service_contract_name,
                           @message_type_name      = @message_type_name
                
                      SET @exit = 1 -- make sure we exit outer loop
                   END
                END

                -- always break from the loop upon processing SendMail message
                BREAK
            END
            -- Check if the message is a dialog timer. This is used for account retries
            ELSE IF(@message_type_name = N'http://schemas.microsoft.com/SQL/ServiceBroker/DialogTimer')
            BEGIN
                -- Handle the retry case. - DialogTimer is used for send mail reties
                EXEC @rc = sp_process_DialogTimer
                            @conversation_handle    = @conversation_handle,
                            @service_contract_name  = @service_contract_name,
                            @message_type_name      = N'{//www.microsoft.com/databasemail/messages}SendMail'

                -- Always break from the loop upon processing DialogTimer message
                -- In case of failure return code from procedure call will simply be propagated to the calling process
                SET @exit = 1 -- make sure we exit outer loop
                BREAK
            END
            -- Error case
            ELSE IF (@message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/Error')
            -- Error in the conversation, hence ignore all the messages of this conversation.
                BREAK

            -- This is executed as long as fetch succeeds.
            FETCH NEXT FROM msgs_cursor
            INTO 
                @conversation_handle,
                @service_contract_name,
                @message_type_name,
                @xml_message_body
        END

        CLOSE msgs_cursor;
        DEALLOCATE msgs_cursor;

        -- Check if we read any request or only SSB generated messages
        -- If a valid request is read with or without an error break out of loop
        IF (@exit = 1 OR @rc <> 0)
            BREAK

       --Keep track of how long this sp has been running
        select @timediff = DATEDIFF(ms, @start_time, getdate()) 
    END

    -- propagate return code to the calling process
    RETURN @rc
END
GO


-----------------------------------------------------------
-- procedure sp_GetAttachmentData
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sp_GetAttachmentData', 'P') IS NULL
    DROP PROCEDURE dbo.sp_GetAttachmentData
GO
-----
PRINT 'Creating sp_GetAttachmentData'
-----
GO

CREATE PROCEDURE sp_GetAttachmentData
   @attachments           nvarchar(max),
   @temp_table_uid        uniqueidentifier,
   @exclude_query_output  BIT        = 0
AS
BEGIN
    SET NOCOUNT ON
    SET QUOTED_IDENTIFIER ON

    DECLARE @rc             INT,
            @prohibitedExts NVARCHAR(1000),
            @attachFilePath NVARCHAR(260),
            @scIndex        INT,
            @startLocation  INT,
            @fileSizeStr    NVARCHAR(256),
            @fileSize       INT,
            @mailDbName     sysname,
            @uidStr         VARCHAR(36)

    --Get the maximum file size allowed for attachments from sysmailconfig.
    EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'MaxFileSize', 
                                                @parameter_value = @fileSizeStr OUTPUT
    --ConvertToInt will return the default if @fileSizeStr is null
    SET @fileSize = dbo.ConvertToInt(@fileSizeStr, 0x7fffffff, 100000)

    --May need this if attaching files
    EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'ProhibitedExtensions', 
                                                @parameter_value = @prohibitedExts OUTPUT

    SET @mailDbName = DB_NAME()
    SET @uidStr = CONVERT(VARCHAR(36), @temp_table_uid)

   SET @attachments = @attachments + ';'
   SET @startLocation = 0
   SET @scIndex = CHARINDEX(';', @attachments, @startLocation)

   WHILE (@scIndex <> 0)
   BEGIN
      SET @attachFilePath = SUBSTRING(@attachments, @startLocation, (@scIndex - @startLocation))
      
      -- Make sure we have an attachment file name to work with, and that it hasn't been truncated
      IF (@scIndex - @startLocation > 260 )
      BEGIN
            RAISERROR(14628, 16, 1)
          RETURN 1 
      END

        IF ((@attachFilePath IS NULL) OR (LEN(@attachFilePath) = 0))
        BEGIN
            RAISERROR(14628, 16, 1)
          RETURN 1 
        END

      --Check if attachment ext is allowed 
      EXEC @rc = sp_isprohibited @attachFilePath, @prohibitedExts
      IF (@rc <> 0)
      BEGIN
          RAISERROR(14630, 16, 1, @attachFilePath, @prohibitedExts)
          RETURN 2
      END

        DECLARE  @no_output_int  INT
        SET @no_output_int         = CONVERT(int, @exclude_query_output)

        -- return code checked after select and delete calls
        EXEC @rc = master..xp_sysmail_attachment_load @message       = @mailDbName, 
                                                      @attachments      = @attachFilePath,
                                                      @subject       = @uidStr,
                                                      @max_attachment_size = @fileSize,
                                                      @no_output = @no_output_int
      IF (@rc <> 0)
            RETURN (@rc)
               
        --Get next substring index
      SET @startLocation = @scIndex + 1
      SET @scIndex = CHARINDEX(';', @attachments, @startLocation)

      IF (@scIndex = 0)
         BREAK
   END

    RETURN 0
END
GO


-----------------------------------------------------------
-- procedure sp_RunMailQuery
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sp_RunMailQuery', 'P') IS NULL
    DROP PROCEDURE dbo.sp_RunMailQuery
GO
-----
PRINT 'Creating sp_RunMailQuery'
-----
USE msdb
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF
GO

CREATE PROCEDURE [dbo].[sp_RunMailQuery]
   @query                      NVARCHAR(max),
   @attach_results             BIT,
   @query_attachment_filename  NVARCHAR(260) = NULL,
   @no_output                  BIT,
   @query_result_header        BIT,
   @separator                  VARCHAR(1),
   @echo_error                 BIT,
   @dbuse                      sysname,
   @width                      INT,
   @temp_table_uid             uniqueidentifier,
   @query_no_truncate          BIT,
   @query_result_no_padding    BIT
AS
BEGIN
    SET NOCOUNT ON
    SET QUOTED_IDENTIFIER ON

    DECLARE @rc             INT,
            @prohibitedExts NVARCHAR(1000),
            @fileSizeStr    NVARCHAR(256),
            @fileSize       INT,
            @attach_res_int INT,
            @no_output_int  INT,
            @no_header_int  INT,
            @echo_error_int INT,
         @query_no_truncate_int INT,
         @query_result_no_padding_int   INT,
            @mailDbName     sysname,
            @uid            uniqueidentifier,
            @uidStr         VARCHAR(36)

    --
    --Get config settings and verify parameters
    --
    SET @query_attachment_filename = LTRIM(RTRIM(@query_attachment_filename))

    --Get the maximum file size allowed for attachments from sysmailconfig.
    EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'MaxFileSize', 
                                                @parameter_value = @fileSizeStr OUTPUT
    --ConvertToInt will return the default if @fileSizeStr is null
    SET @fileSize = dbo.ConvertToInt(@fileSizeStr, 0x7fffffff, 100000)

    IF (@attach_results = 1)
    BEGIN
        --Need this if attaching the query
        EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'ProhibitedExtensions', 
                                                    @parameter_value = @prohibitedExts OUTPUT

        -- If attaching query results to a file and a filename isn't given create one
        IF ((@query_attachment_filename IS NOT NULL) AND (LEN(@query_attachment_filename) > 0))
        BEGIN 
          EXEC @rc = sp_isprohibited @query_attachment_filename, @prohibitedExts
          IF (@rc <> 0)
          BEGIN
              RAISERROR(14630, 16, 1, @query_attachment_filename, @prohibitedExts)
              RETURN 2
          END
        END
        ELSE
        BEGIN
            --If queryfilename is not specified, generate a random name (doesn't have to be unique)
           SET @query_attachment_filename = 'QueryResults' + CONVERT(varchar, ROUND(RAND() * 1000000, 0)) + '.txt'
        END
    END

    --Init variables used in the query execution
    SET @mailDbName = db_name()
    SET @uidStr = convert(varchar(36), @temp_table_uid)

    SET @attach_res_int        = CONVERT(int, @attach_results)
    SET @no_output_int         = CONVERT(int, @no_output)
    IF(@query_result_header = 0) SET @no_header_int  = 1 ELSE SET @no_header_int  = 0
    SET @echo_error_int        = CONVERT(int, @echo_error)
    SET @query_no_truncate_int = CONVERT(int, @query_no_truncate)
    SET @query_result_no_padding_int = CONVERT(int, @query_result_no_padding )

    EXEC @rc = master..xp_sysmail_format_query  
                @query        = @query,
                @message      = @mailDbName,
                    @subject     = @uidStr,
                    @dbuse       = @dbuse, 
                    @attachments = @query_attachment_filename,
                    @attach_results = @attach_res_int,
                    -- format params
                    @separator      = @separator,
                    @no_header      = @no_header_int,
                    @no_output      = @no_output_int,
                    @echo_error     = @echo_error_int,
                @max_attachment_size = @fileSize,
                    @width       = @width, 
                    @query_no_truncate = @query_no_truncate_int,
                    @query_result_no_padding    = @query_result_no_padding_int
   RETURN @rc
END
GO

-----------------------------------------------------------
-- procedure sp_validate_user, used by sp_send_dbmail
-----------------------------------------------------------
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_validate_user')))
  DROP PROCEDURE sp_validate_user
go
use msdb
GO
/****** Object:  StoredProcedure [dbo].sp_validate_user ********/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF
GO
CREATE PROCEDURE [dbo].[sp_validate_user]
    @send_request_user sysname,
    @user_sid varbinary(85) OUTPUT
  WITH EXECUTE AS 'dbo'
AS
BEGIN
    SET NOCOUNT ON
    -- And make sure ARITHABORT is on. This is the default for yukon DB's
    SET ARITHABORT ON

    declare @groupSid varbinary(85)
    declare @temp table
    ([account name] sysname, 
    [type] char(8),
    [privilege] char(9),
    [mapped login name] sysname,
    [permission path] sysname null)
    
    declare @sidlist table
    ([account name] sysname,
     [accountsid] varbinary(85) null,
     [permission path] sysname null)

    SET @user_sid = NULL
    SET @groupSid = NULL

    -- Lookup the Windows Group membership, if any, that grants this
    -- user access to SQL Server. xp_logininfo may fail if the sql
    -- server service account cannot talk to the domain controller to
    -- validate the windows username, or it may fail if the
    -- @send_request_user is not a valid windows user or group name.
    BEGIN TRY 
        insert @temp exec master.dbo.xp_logininfo @send_request_user, 'all'
        -- For a given account name, Get account name -> group accountsid mapping to a temp table variable
        insert @sidlist
            select [account name], suser_sid([permission path]),[permission path]
            from @temp
    END TRY
    BEGIN CATCH
        RETURN 2
    END CATCH
                                    
    -- for a given account name, there has to be atleast one account sid that is not null and
    -- there has to be atleast one mail profile for the list of account sids
    IF ((EXISTS(SELECT [account name] 
                FROM @sidlist
                WHERE accountsid is not NULL)
    AND (EXISTS(SELECT profile_id 
                FROM msdb.dbo.sysmail_principalprofile pp, @sidlist s
                WHERE s.accountsid = pp.principal_sid))))

    BEGIN
        -- Get the first account's sid that meets following criteria
        --  1) return first default profile (if available)
        --  2) if no default profile is  defined, then return the first non-default profile for this account
        SELECT TOP 1  @groupSid = accountsid 
        FROM @sidlist s, msdb.dbo.sysmail_principalprofile pp
        WHERE s.accountsid is not NULL
        AND s.accountsid = pp.principal_sid
        ORDER BY is_default DESC
    END

    -- Lookup a default profile for the Group. If there is one,
    -- then use the group's mail profile.
    IF (@groupSid IS NOT NULL)
    BEGIN
        SET @user_sid = @groupSid
        RETURN 0
    END
    RETURN 1
END
GO



-----------------------------------------------------------
-- procedure sp_send_dbmail
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sp_send_dbmail', 'P') IS NULL
    DROP PROCEDURE dbo.sp_send_dbmail
GO
-----
PRINT 'Creating sp_send_dbmail'
-----
USE msdb
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF
GO
-- sp_send_dbmail : Sends a mail from Yukon outbox.
--
CREATE PROCEDURE [dbo].[sp_send_dbmail]
   @profile_name               sysname    = NULL,        
   @recipients                 VARCHAR(MAX)  = NULL, 
   @copy_recipients            VARCHAR(MAX)  = NULL,
   @blind_copy_recipients      VARCHAR(MAX)  = NULL,
   @subject                    NVARCHAR(255) = NULL,
   @body                       NVARCHAR(MAX) = NULL, 
   @body_format                VARCHAR(20)   = NULL, 
   @importance                 VARCHAR(6)    = 'NORMAL',
   @sensitivity                VARCHAR(12)   = 'NORMAL',
   @file_attachments           NVARCHAR(MAX) = NULL,  
   @query                      NVARCHAR(MAX) = NULL,
   @execute_query_database     sysname       = NULL,  
   @attach_query_result_as_file BIT          = 0,
   @query_attachment_filename  NVARCHAR(260) = NULL,  
   @query_result_header        BIT           = 1,
   @query_result_width         INT           = 256,            
   @query_result_separator     CHAR(1)       = ' ',
   @exclude_query_output       BIT           = 0,
   @append_query_error         BIT           = 0,
   @query_no_truncate          BIT           = 0,
   @query_result_no_padding    BIT           = 0,
   @mailitem_id               INT            = NULL OUTPUT,
   @from_address               VARCHAR(max)  = NULL,
   @reply_to                   VARCHAR(max)  = NULL
  WITH EXECUTE AS 'dbo'
AS
BEGIN
    SET NOCOUNT ON

    -- And make sure ARITHABORT is on. This is the default for yukon DB's
    SET ARITHABORT ON

    --Declare variables used by the procedure internally
    DECLARE @profile_id         INT,
            @temp_table_uid     uniqueidentifier,
            @sendmailxml        VARCHAR(max),
            @CR_str             NVARCHAR(2),
            @localmessage       NVARCHAR(255),
            @QueryResultsExist  INT,
            @AttachmentsExist   INT,
            @RetErrorMsg        NVARCHAR(4000), --Impose a limit on the error message length to avoid memory abuse 
            @rc                 INT,
            @procName           sysname,
            @trancountSave      INT,
            @tranStartedBool    INT,
            @is_sysadmin        BIT,
            @send_request_user  sysname,
            @database_user_id   INT,
            @sid                varbinary(85)

    -- Initialize 
    SELECT  @rc                 = 0,
            @QueryResultsExist  = 0,
            @AttachmentsExist   = 0,
            @temp_table_uid     = NEWID(),
            @procName           = OBJECT_NAME(@@PROCID),
            @tranStartedBool    = 0,
            @trancountSave      = @@TRANCOUNT,
            @sid                = NULL

    EXECUTE AS CALLER
       SELECT @is_sysadmin       = IS_SRVROLEMEMBER('sysadmin'),
              @send_request_user = SUSER_SNAME(),
              @database_user_id  = USER_ID()
    REVERT

    --Check if SSB is enabled in this database
    IF (ISNULL(DATABASEPROPERTYEX(DB_NAME(), N'IsBrokerEnabled'), 0) <> 1)
    BEGIN
       RAISERROR(14650, 16, 1)
       RETURN 1
    END

    --Report error if the mail queue has been stopped. 
    --sysmail_stop_sp/sysmail_start_sp changes the receive status of the SSB queue
    IF NOT EXISTS (SELECT * FROM sys.service_queues WHERE name = N'ExternalMailQueue' AND is_receive_enabled = 1)
    BEGIN
       RAISERROR(14641, 16, 1)
       RETURN 1
    END

    -- Get the relevant profile_id 
    --
    IF (@profile_name IS NULL)
    BEGIN
        -- Use the global or users default if profile name is not supplied
        SELECT TOP (1) @profile_id = pp.profile_id
        FROM msdb.dbo.sysmail_principalprofile as pp
        WHERE (pp.is_default = 1) AND
            (dbo.get_principal_id(pp.principal_sid) = @database_user_id OR pp.principal_sid = 0x00)
        ORDER BY dbo.get_principal_id(pp.principal_sid) DESC

        --Was a profile found
        IF(@profile_id IS NULL)
        BEGIN
            -- Try a profile lookup based on Windows Group membership, if any
            EXEC @rc = msdb.dbo.sp_validate_user @send_request_user, @sid OUTPUT
            IF (@rc = 0)
            BEGIN
                SELECT TOP (1) @profile_id = pp.profile_id
                FROM msdb.dbo.sysmail_principalprofile as pp
                WHERE (pp.is_default = 1) AND
                      (pp.principal_sid = @sid)
                ORDER BY dbo.get_principal_id(pp.principal_sid) DESC
            END

            IF(@profile_id IS NULL)
            BEGIN
                RAISERROR(14636, 16, 1)
                RETURN 1
            END
        END
    END
    ELSE
    BEGIN
        --Get primary account if profile name is supplied
        EXEC @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id = NULL, 
                         @profile_name = @profile_name, 
                         @allow_both_nulls = 0, 
                         @allow_id_name_mismatch = 0,
                         @profileid = @profile_id OUTPUT
        IF (@rc <> 0)
            RETURN @rc

        --Make sure this user has access to the specified profile.
        --sysadmins can send on any profiles
        IF ( @is_sysadmin <> 1)
        BEGIN
            --Not a sysadmin so check users access to profile
            iF NOT EXISTS(SELECT * 
                        FROM msdb.dbo.sysmail_principalprofile 
                        WHERE ((profile_id = @profile_id) AND
                                (dbo.get_principal_id(principal_sid) = @database_user_id OR principal_sid = 0x00)))
            BEGIN
                EXEC msdb.dbo.sp_validate_user @send_request_user, @sid OUTPUT
                IF(@sid IS NULL)
                BEGIN
                    RAISERROR(14607, -1, -1, 'profile')
                    RETURN 1
                END
            END
        END
    END

    --Attach results must be specified
    IF @attach_query_result_as_file IS NULL
    BEGIN
       RAISERROR(14618, 16, 1, 'attach_query_result_as_file')
       RETURN 2
    END

    --No output must be specified
    IF @exclude_query_output IS NULL
    BEGIN
       RAISERROR(14618, 16, 1, 'exclude_query_output')
       RETURN 3
    END

    --No header must be specified
    IF @query_result_header IS NULL
    BEGIN
       RAISERROR(14618, 16, 1, 'query_result_header')
       RETURN 4
    END

    -- Check if query_result_separator is specifed
    IF @query_result_separator IS NULL OR DATALENGTH(@query_result_separator) = 0
    BEGIN
       RAISERROR(14618, 16, 1, 'query_result_separator')
       RETURN 5
    END

    --Echo error must be specified
    IF @append_query_error IS NULL
    BEGIN
       RAISERROR(14618, 16, 1, 'append_query_error')
       RETURN 6
    END

    --@body_format can be TEXT (default) or HTML
    IF (@body_format IS NULL)
    BEGIN
       SET @body_format = 'TEXT'
    END
    ELSE
    BEGIN
       SET @body_format = UPPER(@body_format)

       IF @body_format NOT IN ('TEXT', 'HTML') 
       BEGIN
          RAISERROR(14626, 16, 1, @body_format)
          RETURN 13
       END
    END

    --Importance must be specified
    IF @importance IS NULL
    BEGIN
       RAISERROR(14618, 16, 1, 'importance')
       RETURN 15
    END

    SET @importance = UPPER(@importance)

    --Importance must be one of the predefined values
    IF @importance NOT IN ('LOW', 'NORMAL', 'HIGH')
    BEGIN
       RAISERROR(14622, 16, 1, @importance)
       RETURN 16
    END

    --Sensitivity must be specified
    IF @sensitivity IS NULL
    BEGIN
       RAISERROR(14618, 16, 1, 'sensitivity')
       RETURN 17
    END

    SET @sensitivity = UPPER(@sensitivity)

    --Sensitivity must be one of predefined values
    IF @sensitivity NOT IN ('NORMAL', 'PERSONAL', 'PRIVATE', 'CONFIDENTIAL')
    BEGIN
       RAISERROR(14623, 16, 1, @sensitivity)
       RETURN 18
    END

    --Message body cannot be null. Atleast one of message, subject, query,
    --attachments must be specified.
    IF( (@body IS NULL AND @query IS NULL AND @file_attachments IS NULL AND @subject IS NULL)
       OR
    ( (LEN(@body) IS NULL OR LEN(@body) <= 0)  
       AND (LEN(@query) IS NULL  OR  LEN(@query) <= 0)
       AND (LEN(@file_attachments) IS NULL OR LEN(@file_attachments) <= 0)
       AND (LEN(@subject) IS NULL OR LEN(@subject) <= 0)
    )
    )
    BEGIN
       RAISERROR(14624, 16, 1, '@body, @query, @file_attachments, @subject')
       RETURN 19
    END   
    ELSE
       IF @subject IS NULL OR LEN(@subject) <= 0
          SET @subject='SQL Server Message'

    --Recipients cannot be empty. Atleast one of the To, Cc, Bcc must be specified
    IF ( (@recipients IS NULL AND @copy_recipients IS NULL AND 
       @blind_copy_recipients IS NULL
        )     
       OR
        ( (LEN(@recipients) IS NULL OR LEN(@recipients) <= 0)
       AND (LEN(@copy_recipients) IS NULL OR LEN(@copy_recipients) <= 0)
       AND (LEN(@blind_copy_recipients) IS NULL OR LEN(@blind_copy_recipients) <= 0)
        )
    )
    BEGIN
       RAISERROR(14624, 16, 1, '@recipients, @copy_recipients, @blind_copy_recipients')
       RETURN 20
    END

    --If query is not specified, attach results and no header cannot be true.
    IF ( (@query IS NULL OR LEN(@query) <= 0) AND @attach_query_result_as_file = 1)
    BEGIN
       RAISERROR(14625, 16, 1)
       RETURN 21
    END

    --
    -- Execute Query if query is specified
    IF ((@query IS NOT NULL) AND (LEN(@query) > 0))
    BEGIN
        EXECUTE AS CALLER
        EXEC @rc = sp_RunMailQuery 
                    @query                     = @query,
               @attach_results            = @attach_query_result_as_file,
                    @query_attachment_filename = @query_attachment_filename,
               @no_output                 = @exclude_query_output,
               @query_result_header       = @query_result_header,
               @separator                 = @query_result_separator,
               @echo_error                = @append_query_error,
               @dbuse                     = @execute_query_database,
               @width                     = @query_result_width,
                @temp_table_uid            = @temp_table_uid,
            @query_no_truncate         = @query_no_truncate,
            @query_result_no_padding           = @query_result_no_padding
      -- This error indicates that query results size was over the configured MaxFileSize.
      -- Note, an error has already beed raised in this case
      IF(@rc = 101)
         GOTO ErrorHandler;
         REVERT
 
         -- Always check the transfer tables for data. They may also contain error messages
         -- Only one of the tables receives data in the call to sp_RunMailQuery
         IF(@attach_query_result_as_file = 1)
         BEGIN
             IF EXISTS(SELECT * FROM sysmail_attachments_transfer WHERE uid = @temp_table_uid)
            SET @AttachmentsExist = 1
         END
         ELSE
         BEGIN
             IF EXISTS(SELECT * FROM sysmail_query_transfer WHERE uid = @temp_table_uid AND uid IS NOT NULL)
            SET @QueryResultsExist = 1
         END

         -- Exit if there was an error and caller doesn't want the error appended to the mail
         IF (@rc <> 0 AND @append_query_error = 0)
         BEGIN
            --Error msg with be in either the attachment table or the query table 
            --depending on the setting of @attach_query_result_as_file
            IF(@attach_query_result_as_file = 1)
            BEGIN
               --Copy query results from the attachments table to mail body
               SELECT @RetErrorMsg = CONVERT(NVARCHAR(4000), attachment)
               FROM sysmail_attachments_transfer 
               WHERE uid = @temp_table_uid
            END
            ELSE
            BEGIN
               --Copy query results from the query table to mail body
               SELECT @RetErrorMsg = text_data 
               FROM sysmail_query_transfer 
               WHERE uid = @temp_table_uid
            END

            GOTO ErrorHandler;
         END
         SET @AttachmentsExist = @attach_query_result_as_file
    END
    ELSE
    BEGIN
        --If query is not specified, attach results cannot be true.
        IF (@attach_query_result_as_file = 1)
        BEGIN
           RAISERROR(14625, 16, 1)
           RETURN 21
        END
    END

    --Get the prohibited extensions for attachments from sysmailconfig.
    IF ((@file_attachments IS NOT NULL) AND (LEN(@file_attachments) > 0)) 
    BEGIN
        EXECUTE AS CALLER
        EXEC @rc = sp_GetAttachmentData 
                        @attachments = @file_attachments, 
                        @temp_table_uid = @temp_table_uid,
                        @exclude_query_output = @exclude_query_output
        REVERT
        IF (@rc <> 0)
            GOTO ErrorHandler;
        
        IF EXISTS(SELECT * FROM sysmail_attachments_transfer WHERE uid = @temp_table_uid)
            SET @AttachmentsExist = 1
    END

    -- Start a transaction if not already in one. 
    -- Note: For rest of proc use GOTO ErrorHandler for falures  
    if (@trancountSave = 0) 
       BEGIN TRAN @procName

    SET @tranStartedBool = 1

    -- Store complete mail message for history/status purposes  
    INSERT sysmail_mailitems
    (
       profile_id,   
       recipients,
       copy_recipients,
       blind_copy_recipients,
       subject,
       body, 
       body_format, 
       importance,
       sensitivity,
       file_attachments,  
       attachment_encoding,
       query,
       execute_query_database,
       attach_query_result_as_file,
       query_result_header,
       query_result_width,          
       query_result_separator,
       exclude_query_output,
       append_query_error,
       send_request_user,
       from_address,
       reply_to
    )
    VALUES
    (
       @profile_id,        
       @recipients, 
       @copy_recipients,
       @blind_copy_recipients,
       @subject,
       @body, 
       @body_format, 
       @importance,
       @sensitivity,
       @file_attachments,  
       'MIME',
       @query,
       @execute_query_database,  
       @attach_query_result_as_file,
       @query_result_header,
       @query_result_width,            
       @query_result_separator,
       @exclude_query_output,
       @append_query_error,
       @send_request_user,
       @from_address,
       @reply_to
    )

    SELECT @rc          = @@ERROR,
           @mailitem_id = SCOPE_IDENTITY()

    IF(@rc <> 0)
        GOTO ErrorHandler;

    --Copy query into the message body
    IF(@QueryResultsExist = 1)
    BEGIN
      -- if the body is null initialize it
        UPDATE sysmail_mailitems
        SET body = N''
        WHERE mailitem_id = @mailitem_id
        AND body is null

        --Add CR, a \r followed by \n, which is 0xd and then 0xa
        SET @CR_str = CHAR(13) + CHAR(10)
        UPDATE sysmail_mailitems
        SET body.WRITE(@CR_str, NULL, NULL)
        WHERE mailitem_id = @mailitem_id

   --Copy query results to mail body
        UPDATE sysmail_mailitems
        SET body.WRITE( (SELECT text_data from sysmail_query_transfer WHERE uid = @temp_table_uid), NULL, NULL )
        WHERE mailitem_id = @mailitem_id

    END

    --Copy into the attachments table
    IF(@AttachmentsExist = 1)
    BEGIN
        --Copy temp attachments to sysmail_attachments      
        INSERT INTO sysmail_attachments(mailitem_id, filename, filesize, attachment)
        SELECT @mailitem_id, filename, filesize, attachment
        FROM sysmail_attachments_transfer
        WHERE uid = @temp_table_uid
    END

    -- Create the primary SSB xml maessage
    SET @sendmailxml = '<requests:SendMail xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.microsoft.com/databasemail/requests RequestTypes.xsd" xmlns:requests="http://schemas.microsoft.com/databasemail/requests"><MailItemId>'
                        + CONVERT(NVARCHAR(20), @mailitem_id) + N'</MailItemId></requests:SendMail>'

    -- Send the send request on queue.
    EXEC @rc = sp_SendMailQueues @sendmailxml
    IF @rc <> 0
    BEGIN
       RAISERROR(14627, 16, 1, @rc, 'send mail')
       GOTO ErrorHandler;
    END

    -- Print success message if required
    IF (@exclude_query_output = 0)
    BEGIN
       SET @localmessage = FORMATMESSAGE(14635)
       PRINT @localmessage
    END  

    --
    -- See if the transaction needs to be commited
    --
    IF (@trancountSave = 0 and @tranStartedBool = 1)
       COMMIT TRAN @procName

    -- All done OK
    goto ExitProc;

    -----------------
    -- Error Handler
    -----------------
ErrorHandler:
    IF (@tranStartedBool = 1) 
       ROLLBACK TRAN @procName

    ------------------
    -- Exit Procedure
    ------------------
ExitProc:
   
    --Always delete query and attactment transfer records. 
   --Note: Query results can also be returned in the sysmail_attachments_transfer table
    DELETE sysmail_attachments_transfer WHERE uid = @temp_table_uid
    DELETE sysmail_query_transfer WHERE uid = @temp_table_uid

   --Raise an error it the query execution fails
   -- This will only be the case when @append_query_error is set to 0 (false)
   IF( (@RetErrorMsg IS NOT NULL) AND (@exclude_query_output=0) )
   BEGIN
      RAISERROR(14661, -1, -1, @RetErrorMsg)
   END

    RETURN (@rc)
END
GO

-----------------------------------------------------------
-- procedure sp_ExternalMailQueueListener
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sp_ExternalMailQueueListener', 'P') IS NULL
    DROP PROCEDURE dbo.sp_ExternalMailQueueListener
GO
-----
PRINT 'Creating sp_ExternalMailQueueListener'
-----
USE [msdb]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF

GO
-- Processes messages from the external mail queue
--
CREATE PROCEDURE [dbo].[sp_ExternalMailQueueListener]
AS
BEGIN
    DECLARE 
        @idoc               INT,
        @mailitem_id        INT,
        @sent_status        INT,
        @sent_account_id    INT,
        @rc                 INT,
        @processId          INT,
        @sent_date          DATETIME,
        @localmessage       NVARCHAR(max),
        @conv_handle        uniqueidentifier,
       @message_type_name  NVARCHAR(256),
       @xml_message_body   NVARCHAR(max),
        @LogMessage         NVARCHAR(max)

    -- Table to store message information.
    DECLARE @msgs TABLE
    (
        [conversation_handle]   uniqueidentifier,
       [message_type_name]     nvarchar(256),
       [message_body]          varbinary(max)
    )

    --RECEIVE messages from the external queue. 
    --MailItem status messages are sent from the external sql mail process along with other SSB notifications and errors
    ;RECEIVE conversation_handle, message_type_name, message_body FROM InternalMailQueue INTO @msgs
    -- Check if there was some error in reading from queue
    SET @rc = @@ERROR
    IF (@rc <> 0)
    BEGIN
        --Log error and continue. Don't want to block the following messages on the queue
        SET @localmessage = FORMATMESSAGE(@@ERROR)
        exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage

        GOTO ErrorHandler;
    END
   
    -----------------------------------
    --Process sendmail status messages
    SELECT 
        @conv_handle        = conversation_handle,
        @message_type_name  = message_type_name, 
        @xml_message_body   = CAST(message_body AS NVARCHAR(MAX))
    FROM @msgs 
    WHERE [message_type_name] = N'{//www.microsoft.com/databasemail/messages}SendMailStatus'

    IF(@message_type_name IS NOT NULL)
    BEGIN
        --
        --Expecting the xml body to be n the following form:
        --
        --<?xml version="1.0" encoding="utf-16"?>
        --<responses:SendMail xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.microsoft.com/databasemail/responses ResponseTypes.xsd" xmlns:responses="http://schemas.microsoft.com/databasemail/responses">
        --<Information>
        --    <Failure Message="THe error message...."/>
        --</Information>
        --<MailItemId Id="1" />
        --<SentStatus Status="3" />
        --<SentAccountId Id="0" />
        --<SentDate Date="2005-03-30T14:55:13" />
        --<CallingProcess Id="3012" />
        --</responses:SendMail>

        -- Get the handle to the xml document
        EXEC @rc = sp_xml_preparedocument 
                        @idoc OUTPUT, 
                        @xml_message_body, 
                        N'<ns xmlns:responses="http://schemas.microsoft.com/databasemail/responses" />'
        IF(@rc <> 0)
        BEGIN
            --Log error and continue. Don't want to block the following messages on the queue
            SET @localmessage = FORMATMESSAGE(14655, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body)
            exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage

            GOTO ErrorHandler;
        END

        -- Execute a SELECT statement that uses the OPENXML rowset provider to get the MailItemId and sent status.
        SELECT @mailitem_id     = MailItemId, 
               @sent_status     = SentStatus,
               @sent_account_id = SentAccountId,
               @sent_date       = SentDate,
               @processId       = CallingProcess,
               @LogMessage      = LogMessage
        FROM OPENXML (@idoc, '/responses:SendMail', 1)
            WITH (MailItemId    INT      './MailItemId/@Id', 
                  SentStatus    INT      './SentStatus/@Status',
                  SentAccountId INT      './SentAccountId/@Id',
                  SentDate      DATETIME './SentDate/@Date', --The date was formated using ISO8601
                  CallingProcess INT     './CallingProcess/@Id', 
                  LogMessage     NVARCHAR(max) './Information/Failure/@Message')

        --Close the handle to the xml document
        EXEC sp_xml_removedocument @idoc

        IF(@mailitem_id IS NULL)
        BEGIN  
            --Log error and continue. Don't want to block the following messages on the queue by rolling back the tran
            SET @localmessage = FORMATMESSAGE(14652, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body)
            exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage
        END      
        ELSE
        BEGIN
            -- check sent_status is valid : 0(PendingSend), 1(SendSuccessful), 2(SendFailed), 3(AttemptingSendRetry)
            IF(@sent_status NOT IN (1, 2, 3))
            BEGIN
                SET @localmessage = FORMATMESSAGE(14653, N'SentStatus', CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body)
                exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@localmessage

                --Set value to SendFailed
                SET @sent_status = 2
            END

            --Make the @sent_account_id NULL if it is 0. 
            IF(@sent_account_id IS NOT NULL AND @sent_account_id = 0)
                SET @sent_account_id = NULL

            --
            -- Update the mail status if not a retry. Nothing else needs to be done in this case
            UPDATE sysmail_mailitems
            SET sent_status     = CAST (@sent_status as TINYINT),
                sent_account_id = @sent_account_id,
                sent_date       = @sent_date
            WHERE mailitem_id = @mailitem_id
        
            -- Report a failure if no record is found in the sysmail_mailitems table
            IF (@@ROWCOUNT = 0)
            BEGIN
                SET @localmessage = FORMATMESSAGE(14653, N'MailItemId', CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body)
                exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage
            END

            IF (@LogMessage IS NOT NULL)
            BEGIN
                exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@LogMessage, @process_id=@processId, @mailitem_id=@mailitem_id, @account_id=@sent_account_id
            END
        END
    END

    -------------------------------------------------------
    --Process all other messages by logging to sysmail_log
    SET @conv_handle = NULL;
    
    --Always end the conversion if this message is received
    SELECT @conv_handle = conversation_handle
    FROM @msgs 
    WHERE [message_type_name] = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'
    
    IF(@conv_handle IS NOT NULL)
    BEGIN
        END CONVERSATION @conv_handle;
    END

    DECLARE @queuemessage nvarchar(max)
    DECLARE queue_messages_cursor CURSOR LOCAL 
    FOR
        SELECT FORMATMESSAGE(14654, CONVERT(NVARCHAR(50), conversation_handle), message_type_name, message_body)
        FROM @msgs 
        WHERE [message_type_name] 
              NOT IN (N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog',
                      N'{//www.microsoft.com/databasemail/messages}SendMailStatus')
  
    OPEN queue_messages_cursor 
    FETCH NEXT FROM queue_messages_cursor INTO @queuemessage
    WHILE (@@fetch_status = 0)
    BEGIN
        exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@queuemessage
        FETCH NEXT FROM queue_messages_cursor INTO @queuemessage
    END
    CLOSE queue_messages_cursor 
    DEALLOCATE queue_messages_cursor 

    -- All done OK
    goto ExitProc;

    -----------------
    -- Error Handler
    -----------------
ErrorHandler:

    ------------------
    -- Exit Procedure
    ------------------
ExitProc:
    RETURN (@rc)
END
GO

----------------------------------------------------------
-- procedure sp_sysmail_activate
-----------------------------------------------------------
IF NOT OBJECT_ID('dbo.sp_sysmail_activate', 'P') IS NULL
    DROP PROCEDURE dbo.sp_sysmail_activate
GO

-----
PRINT 'Creating sp_sysmail_activate'
-----
USE [msdb]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON

GO
-- sp_sysmail_activate : Starts the DatabaseMail process if it isn't already running
--
CREATE PROCEDURE [dbo].[sp_sysmail_activate]
AS
BEGIN
    DECLARE @mailDbName sysname
    DECLARE @mailDbId INT
    DECLARE @mailEngineLifeMin INT
    DECLARE @loggingLevel nvarchar(256)
    DECLARE @loggingLevelInt int   
    DECLARE @parameter_value nvarchar(256)
    DECLARE @localmessage nvarchar(max)
    DECLARE @readFromConfigFile INT
    DECLARE @rc INT

    SET NOCOUNT ON
    EXEC sp_executesql @statement = N'RECEIVE TOP(0) * FROM msdb.dbo.ExternalMailQueue'

    EXEC @rc = msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'DatabaseMailExeMinimumLifeTime', 
                                                        @parameter_value = @parameter_value OUTPUT
    IF(@rc <> 0)
        RETURN (1)

    --ConvertToInt will return the default if @parameter_value is null or config value can't be converted
    --Setting max exe lifetime is 1 week (604800 secs). Can't see a reason for it to ever run longer that this
    SET @mailEngineLifeMin = dbo.ConvertToInt(@parameter_value, 604800, 600) 

    EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'ReadFromConfigurationFile', 
                                                  @parameter_value = @parameter_value OUTPUT
    --Try to read the optional read from configuration file:
    SET @readFromConfigFile = dbo.ConvertToInt(@parameter_value, 1, 0) 

    --Try and get the optional logging level for the DatabaseMail process
    EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'LoggingLevel', 
                                                  @parameter_value = @loggingLevel OUTPUT

    --Convert logging level into string value for passing into XP
    SET @loggingLevelInt = dbo.ConvertToInt(@loggingLevel, 3, 2) 
    IF @loggingLevelInt = 1
       SET @loggingLevel = 'Normal'
    ELSE IF @loggingLevelInt = 3
       SET @loggingLevel = 'Verbose'
    ELSE -- default
       SET @loggingLevel = 'Extended'

    SET @mailDbName = DB_NAME()
    SET @mailDbId   = DB_ID()

    EXEC @rc = master..xp_sysmail_activate @mailDbId, @mailDbName, @readFromConfigFile,
    @mailEngineLifeMin, @loggingLevel
    IF(@rc <> 0)
    BEGIN
        SET @localmessage = FORMATMESSAGE(14637)
        exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage
    END
    ELSE
    BEGIN
        SET @localmessage = FORMATMESSAGE(14638)
        exec msdb.dbo.sysmail_logmailevent_sp @event_type=0, @description=@localmessage
    END

    RETURN @rc
END
GO

--------------------------------------------------------------
-- Database Mail roles and permissions
--------------------------------------------------------------

-- Create the DatabaseMailUserRole role
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysusers
            WHERE (name = N'DatabaseMailUserRole')
              AND (issqlrole = 1)))
BEGIN
  -- If there are no members in the role, then drop and re-create it
  IF ((SELECT COUNT(*)
       FROM msdb.dbo.sysusers   su,
            msdb.dbo.sysmembers sm
       WHERE (su.uid = sm.groupuid)
         AND (su.name = N'DatabaseMailUserRole')
         AND (su.issqlrole = 1)) = 0)
  BEGIN
    EXECUTE msdb.dbo.sp_droprole @rolename = N'DatabaseMailUserRole'
    EXECUTE msdb.dbo.sp_addrole @rolename = N'DatabaseMailUserRole'
  END
END
ELSE
  EXECUTE msdb.dbo.sp_addrole @rolename = N'DatabaseMailUserRole'  

go

GRANT EXECUTE   ON [dbo].[sp_send_dbmail]                TO DatabaseMailUserRole
                                                    
GRANT EXECUTE   ON [dbo].[sysmail_help_status_sp]        TO DatabaseMailUserRole
GRANT EXECUTE   ON [dbo].[sysmail_delete_mailitems_sp]   TO DatabaseMailUserRole

GRANT SELECT   ON [dbo].[sysmail_allitems]          TO DatabaseMailUserRole
GRANT SELECT   ON [dbo].[sysmail_sentitems]         TO DatabaseMailUserRole
GRANT SELECT   ON [dbo].[sysmail_unsentitems]       TO DatabaseMailUserRole
GRANT SELECT   ON [dbo].[sysmail_faileditems]       TO DatabaseMailUserRole

     
GRANT  SELECT   ON [dbo].[sysmail_mailattachments]  TO DatabaseMailUserRole
GRANT  SELECT   ON [dbo].[sysmail_event_log]        TO DatabaseMailUserRole

go

/*************************************************************************/
/*                                                                       */
/*  Database Mail SSB objects (Messages, Contracts, Queues, Services)    */
/*                                                                       */
/*************************************************************************/

PRINT ''
PRINT 'Dropping Database Mail MESSAGES, CONTRACTS, QUEUES AND SERVICES...'
PRINT ''

-- Drop service InternalMailService if existing.
IF EXISTS (SELECT * FROM sys.services WHERE name ='InternalMailService')
BEGIN
   PRINT 'Dropping SERVICE InternalMailService'
    DROP SERVICE InternalMailService;
END

-- Drop service ExternalMailService if existing.
IF EXISTS (SELECT * FROM sys.services WHERE name ='ExternalMailService')
BEGIN
   PRINT 'Dropping SERVICE ExternalMailService'
    DROP SERVICE ExternalMailService;
END

-- Drop queue InternalMailQueue if existing.
IF EXISTS (SELECT * FROM sys.objects WHERE name = 'InternalMailQueue' AND type = 'SQ')
BEGIN
   PRINT 'Dropping QUEUE InternalMailQueue'
    DROP QUEUE InternalMailQueue;
END    

-- Drop queue ExternalMailQueue if existing.
IF EXISTS (SELECT * FROM sys.objects WHERE name = 'ExternalMailQueue' AND type = 'SQ')
BEGIN
   PRINT 'Dropping QUEUE ExternalMailQueue'
    DROP QUEUE ExternalMailQueue;
END    

--Drop Notification service for activation of DatabaseMail.exe
IF EXISTS (SELECT * FROM sys.services WHERE name ='SQL/Notifications/SysMailNotification/v1.0')
BEGIN
   PRINT 'Dropping SERVICE [SQL/Notifications/SysMailNotification/v1.0]'
    DROP SERVICE [SQL/Notifications/SysMailNotification/v1.0];
END    

--Drop SysMailNotificationQueue if existing
IF EXISTS (SELECT * FROM sys.objects WHERE name = 'SysMailNotificationQueue' AND type = 'SQ')
BEGIN
   PRINT 'Dropping QUEUE SysMailNotificationQueue'
    DROP QUEUE SysMailNotificationQueue;
END    

-- Drop SendMail v1.0 contract if existing.
IF EXISTS(SELECT * FROM sys.service_contracts 
          WHERE name = '//www.microsoft.com/databasemail/contracts/SendMail/v1.0') 
BEGIN
   PRINT 'Dropping CONTRACT [//www.microsoft.com/databasemail/contracts/SendMail/v1.0]'          
   DROP CONTRACT [//www.microsoft.com/databasemail/contracts/SendMail/v1.0];
END

-- Drop SendMail message type if existing.
IF EXISTS(SELECT * FROM sys.service_message_types 
          WHERE name = '{//www.microsoft.com/databasemail/messages}SendMail')
BEGIN
   PRINT 'Dropping MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMail]'           
   DROP MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMail];
END   

-- Drop SendMailStatus message type if existing.
IF EXISTS(SELECT * FROM sys.service_message_types 
          WHERE name = '{//www.microsoft.com/databasemail/messages}SendMailStatus')
BEGIN
   PRINT 'Dropping MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMailStatus]'           
   DROP MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMailStatus];
END 

GO

-------------------------------------------------------------------
-- Create Database Mail MESSAGES, CONTRACTS, QUEUES AND SERVICES 
-------------------------------------------------------------------

PRINT ''
PRINT 'Creating MESSAGES, CONTRACTS, QUEUES AND SERVICES...'
PRINT ''

-- Create SendMail message type.
PRINT 'Creating MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMail]'

CREATE MESSAGE TYPE 
    [{//www.microsoft.com/databasemail/messages}SendMail] 
    VALIDATION = NONE 

CREATE MESSAGE TYPE 
    [{//www.microsoft.com/databasemail/messages}SendMailStatus]
    VALIDATION = NONE 

-- Create SendMail contract.
PRINT 'Creating CONTRACT [//www.microsoft.com/databasemail/contracts/SendMail/v1.0]'

CREATE CONTRACT [//www.microsoft.com/databasemail/contracts/SendMail/v1.0]
(
    [{//www.microsoft.com/databasemail/messages}SendMail]          SENT BY INITIATOR,
    [{//www.microsoft.com/databasemail/messages}SendMailStatus]    SENT BY TARGET
)

-- Create InternalMailQueue queue.
PRINT 'Creating QUEUE InternalMailQueue'

CREATE QUEUE InternalMailQueue
   WITH ACTIVATION (PROCEDURE_NAME = sp_ExternalMailQueueListener,                  
                    MAX_QUEUE_READERS = 1, 
                    EXECUTE AS SELF);

-- Create ExternalMailQueue queue.
PRINT 'Creating QUEUE ExternalMailQueue'

CREATE QUEUE ExternalMailQueue
   WITH ACTIVATION (PROCEDURE_NAME = sp_sysmail_activate,                  
                    MAX_QUEUE_READERS = 1, 
                    EXECUTE AS SELF);

-- Create InternalMailService service.
PRINT 'Creating SERVICE InternalMailService ON QUEUE InternalMailQueue'

CREATE SERVICE InternalMailService ON QUEUE InternalMailQueue
(
  [//www.microsoft.com/databasemail/contracts/SendMail/v1.0] 
 -- ,[//www.microsoft.com/databasemail/contracts/TestProfile/v1.0]
);

-- Create ExternalMailService service.
PRINT 'Creating SERVICE ExternalMailService ON QUEUE ExternalMailQueue'

CREATE SERVICE ExternalMailService ON QUEUE ExternalMailQueue
(
  [//www.microsoft.com/databasemail/contracts/SendMail/v1.0]
 -- ,[//www.microsoft.com/databasemail/contracts/TestProfile/v1.0]
);

GO

/**************************************************************/
/*                                                            */
/*  M  A  I  N  T  E  N  A  N  C  E    P  L  A  N  S          */
/*                                                            */
/**************************************************************/


/**************************************************************/
/* sp_maintplan_delete_log                                    */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_maintplan_delete_log...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_maintplan_delete_log')
              AND (type = 'P')))
  DROP PROCEDURE sp_maintplan_delete_log
go
CREATE PROCEDURE sp_maintplan_delete_log
    @plan_id        UNIQUEIDENTIFIER    = NULL,
    @subplan_id     UNIQUEIDENTIFIER    = NULL,
    @oldest_time    DATETIME            = NULL
AS
BEGIN
    -- @plan_id and @subplan_id must be both NULL or only one exclusively set
   IF (@plan_id IS NOT NULL) AND (@subplan_id IS NOT NULL)
   BEGIN
      RAISERROR(12980, -1, -1, '@plan_id', '@subplan_id')
      RETURN(1)
   END

   --Scenario 1: User wants to delete all logs
   --Scenario 2: User wants to delete all logs older than X date
   --Scenario 3: User wants to delete all logs for a given plan
   --Scenario 4: User wants to delete all logs for a specific subplan
   --Scenario 5: User wants to delete all logs for a given plan older than X date
   --Scenario 6: User wants to delete all logs for a specific subplan older than X date

   -- Special case 1: Delete all logs
   IF (@plan_id IS NULL) AND (@subplan_id IS NULL) AND (@oldest_time IS NULL)
   BEGIN
      DELETE msdb.dbo.sysmaintplan_logdetail
      DELETE msdb.dbo.sysmaintplan_log
      RETURN (0)
   END

   DELETE msdb.dbo.sysmaintplan_log 
    WHERE ( task_detail_id in 
            (SELECT task_detail_id 
             FROM msdb.dbo.sysmaintplan_log 
             WHERE ((@plan_id IS NULL)     OR (plan_id = @plan_id)) AND 
                   ((@subplan_id IS NULL)  OR (subplan_id = @subplan_id)) AND 
                   ((@oldest_time IS NULL) OR (start_time < @oldest_time))) )

    RETURN (0)
END
GO


/**************************************************************/
/* sp_maintplan_delete_subplan                                */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_maintplan_delete_subplan...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_maintplan_delete_subplan')
              AND (type = 'P')))
  DROP PROCEDURE sp_maintplan_delete_subplan
go
CREATE PROCEDURE sp_maintplan_delete_subplan
    @subplan_id       UNIQUEIDENTIFIER,
    @delete_jobs BIT                   = 1
AS
BEGIN

    DECLARE @retval     INT
    DECLARE @job        UNIQUEIDENTIFIER
    DECLARE @jobMsx     UNIQUEIDENTIFIER

    SET NOCOUNT ON
    SET @retval = 0

    -- Raise an error if the @subplan_id doesn't exist
    IF( NOT EXISTS(SELECT * FROM sysmaintplan_subplans WHERE subplan_id = @subplan_id))
    BEGIN
        DECLARE @subplan_id_as_char VARCHAR(36)
        SELECT @subplan_id_as_char = CONVERT(VARCHAR(36), @subplan_id)
        RAISERROR(14262, -1, -1, '@subplan_id', @subplan_id_as_char)
        RETURN(1)
    END


    BEGIN TRAN

    --Is there an Agent Job/Schedule associated with this subplan?
    SELECT @job = job_id, @jobMsx = msx_job_id
    FROM msdb.dbo.sysmaintplan_subplans 
    WHERE subplan_id = @subplan_id

    EXEC @retval = msdb.dbo.sp_maintplan_delete_log @subplan_id = @subplan_id
    IF (@retval <> 0)
    BEGIN
        ROLLBACK TRAN
        RETURN @retval
    END

    -- Delete the subplans table entry first since it has a foreign
    -- key constraint on its job_id existing in sysjobs.
    DELETE msdb.dbo.sysmaintplan_subplans 
    WHERE (subplan_id = @subplan_id)

    IF (@delete_jobs = 1)
    BEGIN
        --delete the local job associated with this subplan
        IF (@job IS NOT NULL)
        BEGIN
            EXEC @retval = msdb.dbo.sp_delete_job @job_id = @job, @delete_unused_schedule = 1
            IF (@retval <> 0)
            BEGIN
                ROLLBACK TRAN
                RETURN @retval
            END
        END

        --delete the multi-server job associated with this subplan.
        IF (@jobMsx IS NOT NULL)
        BEGIN 
            EXEC @retval = msdb.dbo.sp_delete_job @job_id = @jobMsx, @delete_unused_schedule = 1
            IF (@retval <> 0)
            BEGIN
                ROLLBACK TRAN
                RETURN @retval
            END
        END
    END

    COMMIT TRAN
    RETURN (0)
END
go

/**************************************************************/
/* SP_MAINTPLAN_UPDATE_SUBPLAN_TSX                            */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_maintplan_update_subplan_tsx...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_maintplan_update_subplan_tsx')
              AND (type = 'P')))
  DROP PROCEDURE sp_maintplan_update_subplan_tsx
go
-- This procedure is called when a maintenance plan subplan record
-- needs to be created or updated to match a multi-server Agent job
-- that has arrived from the master server.
CREATE PROCEDURE sp_maintplan_update_subplan_tsx
    @subplan_id    UNIQUEIDENTIFIER,
    @plan_id       UNIQUEIDENTIFIER,
    @name          sysname,
    @description   NVARCHAR(512),
    @job_id        UNIQUEIDENTIFIER
AS
BEGIN
    -- Find out what schedule, if any, is associated with the job.
    declare @schedule_id int
    select @schedule_id = (SELECT TOP(1) schedule_id
                           FROM msdb.dbo.sysjobschedules
                           WHERE (job_id = @job_id) )

    exec sp_maintplan_update_subplan @subplan_id, @plan_id, @name, @description, @job_id, @schedule_id, @allow_create=1

    -- Be sure to mark this subplan as coming from the master, not locally.
    update sysmaintplan_subplans
    set msx_plan = 1
    where subplan_id = @subplan_id
    
END
go

/**************************************************************/
/* SP_MAINTPLAN_SUBPLANS_BY_JOB                               */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_maintplan_subplans_by_job...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_maintplan_subplans_by_job')
              AND (type = 'P')))
  DROP PROCEDURE sp_maintplan_subplans_by_job
go
-- If the given job_id is associated with a maintenance plan,
-- then matching entries from sysmaintplan_subplans are returned.
CREATE PROCEDURE sp_maintplan_subplans_by_job
    @job_id  UNIQUEIDENTIFIER
AS
BEGIN
    select plans.name as 'plan_name', plans.id as 'plan_id', subplans.subplan_name, subplans.subplan_id
    from sysmaintplan_plans plans, sysmaintplan_subplans subplans
    where  plans.id = subplans.plan_id
    and (job_id = @job_id
         or msx_job_id = @job_id)
    order by subplans.plan_id, subplans.subplan_id
END
go


/**************************************************************/
/* sp_maintplan_open_logentry                                 */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_maintplan_open_logentry...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_maintplan_open_logentry')
              AND (type = 'P')))
  DROP PROCEDURE sp_maintplan_open_logentry
go
CREATE PROCEDURE sp_maintplan_open_logentry
    @plan_id       UNIQUEIDENTIFIER,
    @subplan_id       UNIQUEIDENTIFIER,   
    @start_time       DATETIME            = NULL,
    @task_detail_id  UNIQUEIDENTIFIER    = NULL OUTPUT
AS
BEGIN

   --Set defaults
   IF (@start_time IS NULL)
   BEGIN
      SELECT @start_time = GETDATE()
   END

   SELECT @task_detail_id = NEWID()

   --Insert a new record into sysmaintplan_log table
   INSERT INTO msdb.dbo.sysmaintplan_log(task_detail_id, plan_id, subplan_id, start_time)
    VALUES(@task_detail_id, @plan_id, @subplan_id, @start_time)

   RETURN (@@ERROR)
END
GO


/**************************************************************/
/* sp_maintplan_close_logentry                                */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_maintplan_close_logentry...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_maintplan_close_logentry')
              AND (type = 'P')))
  DROP PROCEDURE sp_maintplan_close_logentry
go
CREATE PROCEDURE sp_maintplan_close_logentry
    @task_detail_id     UNIQUEIDENTIFIER,
    @end_time          DATETIME            = NULL,
    @succeeded         TINYINT
AS
BEGIN

   --Set defaults
   IF (@end_time IS NULL)
   BEGIN
      SELECT @end_time = GETDATE()
   END

    -- Raise an error if the @task_detail_id doesn't exist
    IF( NOT EXISTS(SELECT * FROM sysmaintplan_log WHERE (task_detail_id = @task_detail_id)))
   BEGIN
        DECLARE @task_detail_id_as_char VARCHAR(36)
        SELECT @task_detail_id_as_char = CONVERT(VARCHAR(36), @task_detail_id)
        RAISERROR(14262, -1, -1, '@task_detail_id', @task_detail_id_as_char)
      RETURN(1)
   END

   UPDATE msdb.dbo.sysmaintplan_log 
    SET end_time = @end_time, succeeded = @succeeded 
    WHERE (task_detail_id = @task_detail_id)

    RETURN (@@ERROR)
END
go



/**************************************************************/
/* sp_maintplan_update_log                                    */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_maintplan_update_log...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_maintplan_update_log')
              AND (type = 'P')))
  DROP PROCEDURE sp_maintplan_update_log
go
CREATE PROCEDURE sp_maintplan_update_log
    --Updates the log_details table
    @task_detail_id      UNIQUEIDENTIFIER,       --Required
    @Line1              NVARCHAR(256),       --Required
    @Line2              NVARCHAR(256)   = NULL,
    @Line3              NVARCHAR(256)   = NULL,
    @Line4              NVARCHAR(256)   = NULL,
    @Line5              NVARCHAR(256)   = NULL,
    @server_name      sysname,            --Required
    @succeeded         TINYINT,           --Required
    @start_time           DATETIME,          --Required
    @end_time          DATETIME,          --Required
    @error_number     int=NULL,
    @error_message       NVARCHAR(max)   = NULL,
    @command           NVARCHAR(max)   = NULL
AS
BEGIN

   --Prep strings
   SET NOCOUNT ON
   SELECT @Line1 = LTRIM(RTRIM(@Line1))
   SELECT @Line2 = LTRIM(RTRIM(@Line2))
   SELECT @Line3 = LTRIM(RTRIM(@Line3))
   SELECT @Line4 = LTRIM(RTRIM(@Line4))
   SELECT @Line5 = LTRIM(RTRIM(@Line5))

   INSERT INTO msdb.dbo.sysmaintplan_logdetail(
        task_detail_id, 
        line1,
        line2, 
        line3, 
        line4, 
        line5, 
        server_name, 
        start_time, 
        end_time, 
        error_number, 
        error_message, 
        command, 
        succeeded)
   VALUES(
        @task_detail_id,
        @Line1,
        @Line2,
        @Line3,
        @Line4,
        @Line5,
        @server_name,
        @start_time,
        @end_time,
        @error_number,
        @error_message,
        @command,
        @succeeded)

    RETURN (@@ERROR)
END
GO



/**************************************************************/
/* sp_maintplan_update_subplan                                */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_maintplan_update_subplan...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_maintplan_update_subplan')
              AND (type = 'P')))
  DROP PROCEDURE sp_maintplan_update_subplan
go
CREATE PROCEDURE sp_maintplan_update_subplan
    @subplan_id       UNIQUEIDENTIFIER,
    @plan_id       UNIQUEIDENTIFIER    = NULL,
    @name          sysname             = NULL,
    @description  NVARCHAR(512)       = NULL,
    @job_id        UNIQUEIDENTIFIER    = NULL,
    @schedule_id  INT                 = NULL,
    @allow_create   BIT                 = 0,
    @msx_job_id    UNIQUEIDENTIFIER    = NULL
AS
BEGIN

   SET NOCOUNT ON

   SELECT @name = LTRIM(RTRIM(@name))
   SELECT @description = LTRIM(RTRIM(@description))

   --Are we creating a new entry or updating an existing one?

   IF( NOT EXISTS(SELECT * FROM msdb.dbo.sysmaintplan_subplans WHERE subplan_id = @subplan_id) )
   BEGIN
        -- Only allow creation of a record if user permits it
        IF(@allow_create = 0)
        BEGIN
            DECLARE @subplan_id_as_char VARCHAR(36)
            SELECT @subplan_id_as_char = CONVERT(VARCHAR(36), @subplan_id)
            RAISERROR(14262, -1, -1, '@subplan_id', @subplan_id_as_char)
          RETURN(1)
        END

        --Insert it's a new subplan
      IF (@name IS NULL)
      BEGIN
          RAISERROR(12981, -1, -1, '@name')
         RETURN(1) -- Failure
      END

      IF (@plan_id IS NULL)
      BEGIN
          RAISERROR(12981, -1, -1, '@plan_id')
         RETURN(1) -- Failure
      END

      INSERT INTO msdb.dbo.sysmaintplan_subplans(
            subplan_id,
            plan_id,
            subplan_description,
            subplan_name,
            job_id,
            schedule_id,
            msx_job_id)
      VALUES(
            @subplan_id,
            @plan_id,
            @description,
            @name,
            @job_id,
            @schedule_id,
            @msx_job_id)

   END
   ELSE
   BEGIN --Update the table

      DECLARE @s_subplan_name sysname
      DECLARE @s_job_id UNIQUEIDENTIFIER

      SELECT @s_subplan_name         = subplan_name,
            @s_job_id               = job_id
      FROM msdb.dbo.sysmaintplan_subplans
      WHERE (@subplan_id = subplan_id)

      --Determine if user wants to change these variables
      IF (@name IS NOT NULL)          SELECT @s_subplan_name          = @name
      IF (@job_id IS NOT NULL)        SELECT @s_job_id                = @job_id

      --UPDATE the record

      UPDATE msdb.dbo.sysmaintplan_subplans 
        SET subplan_name        = @s_subplan_name,
            subplan_description = @description,
            job_id              = @s_job_id,
            schedule_id         = @schedule_id,
            msx_job_id          = @msx_job_id
      WHERE (subplan_id = @subplan_id)

   END

    RETURN (@@ERROR)
END
GO


/**************************************************************/
/* sp_maintplan_delete_plan                                   */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_maintplan_delete_plan...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_maintplan_delete_plan')
              AND (type = 'P')))
  DROP PROCEDURE sp_maintplan_delete_plan
go
CREATE PROCEDURE sp_maintplan_delete_plan
    @plan_id   UNIQUEIDENTIFIER
AS
BEGIN
   SET NOCOUNT ON

   DECLARE @sp_id UNIQUEIDENTIFIER
    DECLARE @retval     INT

    SET @retval = 0

   --Loop through Subplans
   DECLARE sp CURSOR LOCAL FOR 
        SELECT subplan_id 
        FROM msdb.dbo.sysmaintplan_subplans 
        WHERE plan_id = @plan_id FOR READ ONLY

   OPEN sp
   FETCH NEXT FROM sp INTO @sp_id
   WHILE @@FETCH_STATUS = 0
   BEGIN 
     EXECUTE @retval = sp_maintplan_delete_subplan @subplan_id = @sp_id
      IF(@retval <> 0)
        BREAK

     FETCH NEXT FROM sp INTO @sp_id
   END
   CLOSE sp
   DEALLOCATE sp

    RETURN (@retval)
END
go



/**************************************************************/
/* sp_maintplan_start                                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_maintplan_start...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_maintplan_start')
              AND (type = 'P')))
  DROP PROCEDURE sp_maintplan_start
go
CREATE PROCEDURE sp_maintplan_start
    @plan_id        UNIQUEIDENTIFIER    = NULL,
    @subplan_id     UNIQUEIDENTIFIER    = NULL
AS
BEGIN
    SET NOCOUNT ON

    DECLARE @jobid  UNIQUEIDENTIFIER
    DECLARE @retval INT
    SET @retval = 0

    -- A @plan_id or @subplan_id must be supplied
   IF (@plan_id IS NULL) AND (@subplan_id IS NULL)
   BEGIN
      RAISERROR(12982, -1, -1, '@plan_id', '@subplan_id')
      RETURN(1)
   END

    -- either @plan_id or @subplan_id must be exclusively set
   IF (@plan_id IS NOT NULL) AND (@subplan_id IS NOT NULL)
   BEGIN
      RAISERROR(12982, -1, -1, '@plan_id', '@subplan_id')
      RETURN(1)
   END

    IF (@subplan_id IS NOT NULL)
    BEGIN 
        -- subplan_id supplied so simply start the subplan's job

        SELECT @jobid = job_id 
        FROM msdb.dbo.sysmaintplan_subplans 
        WHERE subplan_id = @subplan_id 

        if(@jobid IS NOT NULL)
        BEGIN
            EXEC @retval = msdb.dbo.sp_start_job @job_id = @jobid
        END

    END
    ELSE
    BEGIN
        -- Loop through Subplans and fire off all associated jobs
       DECLARE spj CURSOR LOCAL FOR 
            SELECT job_id
            FROM msdb.dbo.sysmaintplan_subplans 
            WHERE plan_id = @plan_id FOR READ ONLY

       OPEN spj
       FETCH NEXT FROM spj INTO @jobid
       WHILE (@@FETCH_STATUS = 0)
       BEGIN 
           EXEC @retval = msdb.dbo.sp_start_job @job_id = @jobid
            IF(@retval <> 0)
                BREAK

           FETCH NEXT FROM spj INTO @jobid
       END

       CLOSE spj
       DEALLOCATE spj

    END

    RETURN (@retval)
END
GO

/**************************************************************/
/* sp_get_script                                              */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_get_script...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_get_script')
              AND (type = 'P')))
  DROP PROCEDURE sp_get_script
go
CREATE PROCEDURE sp_get_script
    @name sysname
AS
BEGIN
    exec master.dbo.xp_get_script @name
END
GO


/*==================================================================*/
--TODO: The following SYSDBMAINT... tables and SP's will be removed  
/*==================================================================*/


/**************************************************************/
/* SYSDBMAINTPLANS                                            */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysdbmaintplans')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysdbmaintplans...'

  CREATE TABLE sysdbmaintplans
  (
  plan_id                    UNIQUEIDENTIFIER NOT NULL PRIMARY KEY CLUSTERED,
  plan_name                  sysname          NOT NULL,
  date_created               DATETIME         NOT NULL DEFAULT (GETDATE()),
  owner                      sysname          NOT NULL DEFAULT (ISNULL(NT_CLIENT(), SUSER_SNAME())),
  max_history_rows           INT              NOT NULL DEFAULT (0),
  remote_history_server      sysname          NOT NULL DEFAULT (''),
  max_remote_history_rows    INT              NOT NULL DEFAULT (0),
  user_defined_1             INT              NULL,
  user_defined_2             NVARCHAR(100)    NULL,
  user_defined_3             DATETIME         NULL,
  user_defined_4             UNIQUEIDENTIFIER NULL
  )
END
go

-- Add row for "plan 0"
IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysdbmaintplans
                WHERE (plan_id = CONVERT(UNIQUEIDENTIFIER, 0x00))))
  INSERT INTO sysdbmaintplans(plan_id, plan_name, owner) VALUES (0x00, N'All ad-hoc plans', N'sa')
go

/**************************************************************/
/* SYSDBMAINTPLAN_JOBS                                        */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysdbmaintplan_jobs')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysdbmaintplan_jobs...'

  CREATE TABLE sysdbmaintplan_jobs
  (
  plan_id UNIQUEIDENTIFIER NOT NULL UNIQUE CLUSTERED (plan_id, job_id)
                                    FOREIGN KEY REFERENCES msdb.dbo.sysdbmaintplans (plan_id),
  job_id  UNIQUEIDENTIFIER NOT NULL
  )
END
go

/**************************************************************/
/* SYSDBMAINTPLAN_DATABASES                                   */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysdbmaintplan_databases')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysdbmaintplan_databases...'

  CREATE TABLE sysdbmaintplan_databases
  (
  plan_id       UNIQUEIDENTIFIER NOT NULL UNIQUE CLUSTERED (plan_id, database_name)
                                          FOREIGN KEY REFERENCES msdb.dbo.sysdbmaintplans (plan_id),
  database_name sysname          NOT NULL
  )
END
go

/**************************************************************/
/* SYSDBMAINTPLAN_HISTORY                                     */
/**************************************************************/

IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sysdbmaintplan_history')
                  AND (type = 'U')))
BEGIN
  PRINT ''
  PRINT 'Creating table sysdbmaintplan_history...'

  CREATE TABLE sysdbmaintplan_history
  (
  sequence_id    INT               NOT NULL IDENTITY UNIQUE NONCLUSTERED,
  plan_id        UNIQUEIDENTIFIER  NOT NULL DEFAULT('00000000-0000-0000-0000-000000000000'),
  plan_name      sysname           NOT NULL DEFAULT('All ad-hoc plans'),
  database_name  sysname           NULL,
  server_name    sysname           NOT NULL DEFAULT (CONVERT(sysname, ServerProperty('ServerName'))),
  activity       NVARCHAR(128)     NULL,
  succeeded      BIT               NOT NULL DEFAULT (1),
  end_time       DATETIME          NOT NULL DEFAULT (GETDATE()),
  duration       INT               NULL     DEFAULT (0),
  start_time     AS                DATEADD (ss, -duration, end_time),
  error_number   INT               NOT NULL DEFAULT (0),
  message        NVARCHAR(512)     NULL
  )

  CREATE CLUSTERED INDEX clust ON sysdbmaintplan_history(plan_id)
END
-- ALTER TABLE to correct default constraint 
ELSE
BEGIN
  DECLARE @t TABLE
  (
  constraint_type         NVARCHAR(146)  COLLATE database_default NULL,
  constraint_name         sysname        COLLATE database_default NULL,
  delete_action           NVARCHAR(20)   COLLATE database_default NULL,
  update_action           NVARCHAR(20)   COLLATE database_default NULL,
  status_enabled          NVARCHAR(20)   COLLATE database_default NULL,
  status_for_replication  NVARCHAR(20)   COLLATE database_default NULL,
  constraint_keys         NVARCHAR(2126) COLLATE database_default NULL
  )

  INSERT INTO @t EXEC sp_helpconstraint N'sysdbmaintplan_history', 'nomsg'

  DECLARE @constraint_name sysname
  DECLARE @sql NVARCHAR(4000)

  SELECT @constraint_name = constraint_name 
  FROM   @t 
  WHERE  constraint_type = N'DEFAULT on column server_name' 
  AND    constraint_keys = N'(@@servername)'

  -- default found
  IF (@constraint_name IS NOT NULL)
  BEGIN
    PRINT ''
    PRINT 'Alter sysdbmaintplan_history ...'
    SELECT @sql = N'ALTER TABLE sysdbmaintplan_history DROP CONSTRAINT ' + QUOTENAME(@constraint_name)
    EXEC (@sql)

    ALTER TABLE sysdbmaintplan_history 
      ADD CONSTRAINT servername_default DEFAULT (CONVERT(sysname, ServerProperty('ServerName')))
      FOR server_name
  END
END
go

/**************************************************************/
/* SPs for the maintenance plans                              */
/**************************************************************/
/**************************************************************/
/* sp_clear_dbmaintplan_by_db                                 */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_clear_dbmaintplan_by_db...'
GO
IF (EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sp_clear_dbmaintplan_by_db') AND (type = 'P')))
  DROP PROCEDURE sp_clear_dbmaintplan_by_db
GO
CREATE PROCEDURE sp_clear_dbmaintplan_by_db
  @db_name sysname
AS
BEGIN
  DECLARE planid_cursor CURSOR
  FOR
  select plan_id from msdb.dbo.sysdbmaintplan_databases where database_name=@db_name
  OPEN planid_cursor
  declare @planid uniqueidentifier
  FETCH NEXT FROM planid_cursor INTO @planid
  WHILE (@@FETCH_STATUS <> -1)
  BEGIN
    IF (@@FETCH_STATUS <> -2)
    BEGIN
      delete from msdb.dbo.sysdbmaintplan_databases where plan_id=@planid AND database_name=@db_name
      if (NOT EXISTS(select * from msdb.dbo.sysdbmaintplan_databases where plan_id=@planid))
      BEGIN
        --delete the job
        DECLARE jobid_cursor CURSOR
        FOR
        select job_id from msdb.dbo.sysdbmaintplan_jobs where plan_id=@planid
        OPEN jobid_cursor
        DECLARE @jobid uniqueidentifier
        FETCH NEXT FROM jobid_cursor INTO @jobid
        WHILE (@@FETCH_STATUS <> -1)
        BEGIN
          if (@@FETCH_STATUS <> -2)
          BEGIN
            execute msdb.dbo.sp_delete_job @jobid
          END
          FETCH NEXT FROM jobid_cursor into @jobid
        END
        CLOSE jobid_cursor
        DEALLOCATE jobid_cursor
        --delete the history
        delete from msdb.dbo.sysdbmaintplan_history where plan_id=@planid
        --delete the plan
        delete from msdb.dbo.sysdbmaintplans where plan_id=@planid
      END
    END
    FETCH NEXT FROM planid_cursor INTO @planid
  END
  CLOSE planid_cursor
  DEALLOCATE planid_cursor
END
GO

/**************************************************************/
/* sp_add_maintenance_plan                                    */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_add_maintenance_plan...'
GO
IF (EXISTS (SELECT *
                FROM msdb.dbo.sysobjects
                WHERE (name = N'sp_add_maintenance_plan') AND (type = 'P')))
  DROP PROCEDURE sp_add_maintenance_plan
GO
CREATE PROCEDURE sp_add_maintenance_plan
  @plan_name varchar(128),
  @plan_id   UNIQUEIDENTIFIER OUTPUT
AS
BEGIN
  IF (NOT EXISTS (SELECT *
                FROM msdb.dbo.sysdbmaintplans
                WHERE plan_name=@plan_name))
    BEGIN
      SELECT @plan_id=NEWID()
      INSERT INTO msdb.dbo.sysdbmaintplans (plan_id, plan_name) VALUES (@plan_id, @plan_name)
    END
  ELSE
    BEGIN
      RAISERROR(14261,-1,-1,'@plan_name',@plan_name)
      RETURN(1) -- failure
    END
END
GO

/**************************************************************/
/* sp_delete_maintenance_plan                                 */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_maintenance_plan...'
GO
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_maintenance_plan')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_maintenance_plan
GO
CREATE PROCEDURE sp_delete_maintenance_plan
  @plan_id UNIQUEIDENTIFIER
AS
BEGIN
  /*check if the plan_id is valid*/
  IF (NOT EXISTS(SELECT *
                 FROM sysdbmaintplans
                 WHERE plan_id=@plan_id))
  BEGIN
    DECLARE @syserr VARCHAR(100)
    SELECT @syserr=CONVERT(VARCHAR(100),@plan_id)
    RAISERROR(14262,-1,-1,'@plan_id',@syserr)
    RETURN(1)
  END
  /* clean the related records in sysdbmaintplan_database */
  DELETE FROM msdb.dbo.sysdbmaintplan_databases
  WHERE plan_id=@plan_id
  /* clean the related records in sysdbmaintplan_jobs*/
  DELETE FROM msdb.dbo.sysdbmaintplan_jobs
  WHERE plan_id=@plan_id
  /* clean sysdbmaintplans */
  DELETE FROM msdb.dbo.sysdbmaintplans
  WHERE  plan_id= @plan_id
END
GO

/**************************************************************/
/* sp_add_maintenance_plan_db                                 */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_add_maintenance_plan_db...'
GO
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_maintenance_plan_db')
              AND (type = 'P')))
  DROP PROCEDURE sp_add_maintenance_plan_db
GO
CREATE PROCEDURE sp_add_maintenance_plan_db
  @plan_id UNIQUEIDENTIFIER,
  @db_name sysname
AS
BEGIN
  DECLARE @syserr VARCHAR(100)
  /*check if the plan_id is valid */
  IF (NOT EXISTS (SELECT plan_id
              FROM  msdb.dbo.sysdbmaintplans
              WHERE plan_id=@plan_id))
  BEGIN
    SELECT @syserr=CONVERT(VARCHAR(100),@plan_id)
    RAISERROR(14262,-1,-1,'@plan_id',@syserr)
    RETURN(1)
  END
  /*check if the database name is valid */
  IF (NOT EXISTS (SELECT name
              FROM master.dbo.sysdatabases
              WHERE name=@db_name))
   BEGIN
    RAISERROR(14262,-1,-1,'@db_name',@db_name)
    RETURN(1)
  END
  /*check if the (plan_id, database) pair already exists*/
  IF (EXISTS (SELECT *
              FROM sysdbmaintplan_databases
              WHERE plan_id=@plan_id AND database_name=@db_name))
  BEGIN
    SELECT @syserr=CONVERT(VARCHAR(100),@plan_id)+' + '+@db_name
    RAISERROR(14261,-1,-1,'@plan_id+@db_name',@syserr)
    RETURN(1)
  END
  INSERT INTO msdb.dbo.sysdbmaintplan_databases (plan_id,database_name) VALUES (@plan_id, @db_name)
END
GO

/**************************************************************/
/* sp_delete_maintenance_plan_db                              */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_delete_maintenance_plan_db...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_maintenance_plan_db')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_maintenance_plan_db
go
CREATE PROCEDURE sp_delete_maintenance_plan_db
  @plan_id uniqueidentifier,
  @db_name sysname
AS
BEGIN
  /*check if the (plan_id, db_name) exists in the table*/
  IF (NOT EXISTS(SELECT *
                 FROM msdb.dbo.sysdbmaintplan_databases
                 WHERE @plan_id=plan_id AND @db_name=database_name))
  BEGIN
    DECLARE @syserr VARCHAR(300)
    SELECT @syserr=CONVERT(VARCHAR(100),@plan_id)+' + '+@db_name
    RAISERROR(14262,-1,-1,'@plan_id+@db_name',@syserr)
    RETURN(1)
  END
  /*delete the pair*/
  DELETE FROM msdb.dbo.sysdbmaintplan_databases
  WHERE plan_id=@plan_id AND database_name=@db_name
END
GO

/**************************************************************/
/* sp_add_maintenance_plan_job                                */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_add_maintenance_plan_job...'
GO
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_add_maintenance_plan_job')
              AND (type = 'P')))
  DROP PROCEDURE sp_add_maintenance_plan_job
GO
CREATE PROCEDURE sp_add_maintenance_plan_job
  @plan_id UNIQUEIDENTIFIER,
  @job_id  UNIQUEIDENTIFIER
AS
BEGIN
  DECLARE @syserr varchar(100)
  /*check if the @plan_id is valid*/
  IF (NOT EXISTS(SELECT plan_id
                 FROM msdb.dbo.sysdbmaintplans
                 WHERE plan_id=@plan_id))
  BEGIN
    SELECT @syserr=CONVERT(VARCHAR(100),@plan_id)
    RAISERROR(14262,-1,-1,'@plan_id',@syserr)
    RETURN(1)
  END
  /*check if the @job_id is valid*/
  IF (NOT EXISTS(SELECT job_id
                 FROM msdb.dbo.sysjobs
                 WHERE job_id=@job_id))
  BEGIN
    SELECT @syserr=CONVERT(VARCHAR(100),@job_id)
    RAISERROR(14262,-1,-1,'@job_id',@syserr)
    RETURN(1)
  END
  /*check if the job has at least one step calling xp_sqlmaint*/
  DECLARE @maxind INT
  SELECT @maxind=(SELECT MAX(CHARINDEX('xp_sqlmaint', command))
                FROM  msdb.dbo.sysjobsteps
                WHERE @job_id=job_id)
  IF (@maxind<=0)
  BEGIN
    /*print N'Warning: The job is not for maitenance plan.' -- will add the new sysmessage here*/
    SELECT @syserr=CONVERT(VARCHAR(100),@job_id)
    RAISERROR(14199,-1,-1,@syserr)
    RETURN(1)
  END
  INSERT INTO msdb.dbo.sysdbmaintplan_jobs(plan_id,job_id) VALUES (@plan_id, @job_id) --don't have to check duplicate here
END
GO

/**************************************************************/
/* sp_delete_maintenance_plan_job                             */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_delete_maintenance_plan_job...'
GO
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_maintenance_plan_job')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_maintenance_plan_job
GO
CREATE PROCEDURE sp_delete_maintenance_plan_job
  @plan_id uniqueidentifier,
  @job_id  uniqueidentifier
AS
BEGIN
  /*check if the (plan_id, job_id) exists*/
  IF (NOT EXISTS(SELECT *
                 FROM sysdbmaintplan_jobs
                 WHERE @plan_id=plan_id AND @job_id=job_id))
  BEGIN
    DECLARE @syserr VARCHAR(300)
    SELECT @syserr=CONVERT(VARCHAR(100),@plan_id)+' + '+CONVERT(VARCHAR(100),@job_id)
    RAISERROR(14262,-1,-1,'@plan_id+@job_id',@syserr)
    RETURN(1)
  END
  DELETE FROM msdb.dbo.sysdbmaintplan_jobs
  WHERE plan_id=@plan_id AND job_id=@job_id
END
GO

/**************************************************************/
/* sp_help_maintenance_plan                                   */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_help_maintenance_plan...'
GO
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_help_maintenance_plan')
              AND (type = 'P')))
  DROP PROCEDURE sp_help_maintenance_plan
GO
CREATE PROCEDURE sp_help_maintenance_plan
  @plan_id UNIQUEIDENTIFIER = NULL
AS
BEGIN
  IF (@plan_id IS NOT NULL)
    BEGIN
      /*return the information about the plan itself*/
      SELECT *
      FROM msdb.dbo.sysdbmaintplans
      WHERE plan_id=@plan_id
      /*return the information about databases this plan defined on*/
      SELECT database_name
      FROM msdb.dbo.sysdbmaintplan_databases
      WHERE plan_id=@plan_id
      /*return the information about the jobs that relating to the plan*/
      SELECT job_id
      FROM msdb.dbo.sysdbmaintplan_jobs
      WHERE plan_id=@plan_id
    END
  ELSE
    BEGIN
      SELECT *
      FROM msdb.dbo.sysdbmaintplans
    END
END
GO

/**************************************************************/
/*                                                            */
/* B A C K U P  H I S T O R Y                                 */
/*                                                            */
/**************************************************************/
/**************************************************************/
/* sp_delete_database_backuphistory                           */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_database_backuphistory...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_database_backuphistory')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_database_backuphistory
go
CREATE   PROCEDURE sp_delete_database_backuphistory
  @database_name sysname
AS
BEGIN
  SET NOCOUNT ON

  DECLARE @backup_set_id TABLE      (backup_set_id INT)
  DECLARE @media_set_id TABLE       (media_set_id INT)
  DECLARE @restore_history_id TABLE (restore_history_id INT)

  INSERT INTO @backup_set_id (backup_set_id)
  SELECT DISTINCT backup_set_id
  FROM msdb.dbo.backupset
  WHERE database_name = @database_name

  INSERT INTO @media_set_id (media_set_id)
  SELECT DISTINCT media_set_id
  FROM msdb.dbo.backupset
  WHERE database_name = @database_name

  INSERT INTO @restore_history_id (restore_history_id)
  SELECT DISTINCT restore_history_id
  FROM msdb.dbo.restorehistory
  WHERE backup_set_id IN (SELECT backup_set_id
                          FROM @backup_set_id)

  BEGIN TRANSACTION

  DELETE FROM msdb.dbo.backupfile
  WHERE backup_set_id IN (SELECT backup_set_id
                          FROM @backup_set_id)
  IF (@@error > 0)
    GOTO Quit

  DELETE FROM msdb.dbo.backupfilegroup
  WHERE backup_set_id IN (SELECT backup_set_id
                          FROM @backup_set_id)
  IF (@@error > 0)
    GOTO Quit

  DELETE FROM msdb.dbo.restorefile
  WHERE restore_history_id IN (SELECT restore_history_id
                               FROM @restore_history_id)
  IF (@@error > 0)
    GOTO Quit

  DELETE FROM msdb.dbo.restorefilegroup
  WHERE restore_history_id IN (SELECT restore_history_id
                               FROM @restore_history_id)
  IF (@@error > 0)
    GOTO Quit

  DELETE FROM msdb.dbo.restorehistory
  WHERE restore_history_id IN (SELECT restore_history_id
                               FROM @restore_history_id)
  IF (@@error > 0)
    GOTO Quit

  DELETE FROM msdb.dbo.backupset
  WHERE backup_set_id IN (SELECT backup_set_id
                          FROM @backup_set_id)
  IF (@@error > 0)
    GOTO Quit

  DELETE msdb.dbo.backupmediafamily
  FROM msdb.dbo.backupmediafamily bmf
  WHERE bmf.media_set_id IN (SELECT media_set_id
                             FROM @media_set_id)
    AND ((SELECT COUNT(*)
          FROM msdb.dbo.backupset
          WHERE media_set_id = bmf.media_set_id) = 0)
  IF (@@error > 0)
    GOTO Quit

  DELETE msdb.dbo.backupmediaset
  FROM msdb.dbo.backupmediaset bms
  WHERE bms.media_set_id IN (SELECT media_set_id
                             FROM @media_set_id)
    AND ((SELECT COUNT(*)
          FROM msdb.dbo.backupset
          WHERE media_set_id = bms.media_set_id) = 0)
  IF (@@error > 0)
    GOTO Quit

  COMMIT TRANSACTION
  RETURN

Quit:
  ROLLBACK TRANSACTION
END
go

/**************************************************************/
/* SP_DELETE_BACKUPHISTORY                                    */
/**************************************************************/

PRINT ''
PRINT 'Creating procedure sp_delete_backuphistory...'
go
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysobjects
            WHERE (name = N'sp_delete_backuphistory')
              AND (type = 'P')))
  DROP PROCEDURE sp_delete_backuphistory
go
CREATE   PROCEDURE sp_delete_backuphistory
  @oldest_date datetime
AS
BEGIN
  SET NOCOUNT ON

  DECLARE @backup_set_id TABLE      (backup_set_id INT)
  DECLARE @media_set_id TABLE       (media_set_id INT)
  DECLARE @restore_history_id TABLE (restore_history_id INT)

  INSERT INTO @backup_set_id (backup_set_id)
  SELECT DISTINCT backup_set_id
  FROM msdb.dbo.backupset
  WHERE backup_finish_date < @oldest_date

  INSERT INTO @media_set_id (media_set_id)
  SELECT DISTINCT media_set_id
  FROM msdb.dbo.backupset
  WHERE backup_finish_date < @oldest_date

  INSERT INTO @restore_history_id (restore_history_id)
  SELECT DISTINCT restore_history_id
  FROM msdb.dbo.restorehistory
  WHERE backup_set_id IN (SELECT backup_set_id
                          FROM @backup_set_id)

  BEGIN TRANSACTION

  DELETE FROM msdb.dbo.backupfile
  WHERE backup_set_id IN (SELECT backup_set_id
                          FROM @backup_set_id)
  IF (@@error > 0)
    GOTO Quit

  DELETE FROM msdb.dbo.backupfilegroup
  WHERE backup_set_id IN (SELECT backup_set_id
                          FROM @backup_set_id)
  IF (@@error > 0)
    GOTO Quit

  DELETE FROM msdb.dbo.restorefile
  WHERE restore_history_id IN (SELECT restore_history_id
                               FROM @restore_history_id)
  IF (@@error > 0)
    GOTO Quit

  DELETE FROM msdb.dbo.restorefilegroup
  WHERE restore_history_id IN (SELECT restore_history_id
                               FROM @restore_history_id)
  IF (@@error > 0)
    GOTO Quit

  DELETE FROM msdb.dbo.restorehistory
  WHERE restore_history_id IN (SELECT restore_history_id
                               FROM @restore_history_id)
  IF (@@error > 0)
    GOTO Quit

  DELETE FROM msdb.dbo.backupset
  WHERE backup_set_id IN (SELECT backup_set_id
                          FROM @backup_set_id)
  IF (@@error > 0)
    GOTO Quit

  DELETE msdb.dbo.backupmediafamily
  FROM msdb.dbo.backupmediafamily bmf
  WHERE bmf.media_set_id IN (SELECT media_set_id
                             FROM @media_set_id)
    AND ((SELECT COUNT(*)
          FROM msdb.dbo.backupset
          WHERE media_set_id = bmf.media_set_id) = 0)
  IF (@@error > 0)
    GOTO Quit

  DELETE msdb.dbo.backupmediaset
  FROM msdb.dbo.backupmediaset bms
  WHERE bms.media_set_id IN (SELECT media_set_id
                             FROM @media_set_id)
    AND ((SELECT COUNT(*)
          FROM msdb.dbo.backupset
          WHERE media_set_id = bms.media_set_id) = 0)
  IF (@@error > 0)
    GOTO Quit

  COMMIT TRANSACTION
  RETURN

Quit:
  ROLLBACK TRANSACTION

END
go




/**********************************************************************/
/* TABLE : log_shipping_primaries                                     */
/* Populated on the monitor server                                    */
/*                                                                    */
/**********************************************************************/

IF (NOT EXISTS (SELECT *
            FROM INFORMATION_SCHEMA.TABLES
            WHERE (TABLE_NAME = N'log_shipping_primaries')))
BEGIN
 PRINT ''
 PRINT 'Creating table log_shipping_primaries...'
 CREATE TABLE log_shipping_primaries
 (
  primary_id                   INT IDENTITY     NOT NULL PRIMARY KEY,
  primary_server_name          sysname          NOT NULL,
  primary_database_name        sysname          NOT NULL,
  maintenance_plan_id          UNIQUEIDENTIFIER NULL,
  backup_threshold             INT              NOT NULL,
  threshold_alert              INT              NOT NULL,
  threshold_alert_enabled      BIT              NOT NULL, /* 1 = enabled, 0 = disabled */
  last_backup_filename         NVARCHAR(500)    NULL,
  last_updated                 DATETIME         NULL,
  planned_outage_start_time    INT              NOT NULL,
  planned_outage_end_time      INT              NOT NULL,
  planned_outage_weekday_mask  INT              NOT NULL,
  source_directory             NVARCHAR(500)    NULL
 )
END
ELSE 
BEGIN
  IF (NOT EXISTS (SELECT * 
    FROM INFORMATION_SCHEMA.COLUMNS
    WHERE (TABLE_NAME = N'log_shipping_primaries')
      AND (COLUMN_NAME = N'source_directory')))

  BEGIN
    PRINT ''
    PRINT 'Adding columns to table log_shipping_primaries...'

    ALTER TABLE log_shipping_primaries
     ADD source_directory NVARCHAR(500) NULL

  END
END 
go

/**********************************************************************/
/* TABLE : log_shipping_secondaries                                   */
/* Populated on the monitor server                                    */
/*                                                                    */
/**********************************************************************/

IF (NOT EXISTS (SELECT *
            FROM INFORMATION_SCHEMA.TABLES
            WHERE (TABLE_NAME = N'log_shipping_secondaries')))
BEGIN
 PRINT ''
 PRINT 'Creating table log_shipping_secondaries...'
 CREATE TABLE log_shipping_secondaries
 (
  primary_id                   INT                FOREIGN KEY REFERENCES log_shipping_primaries (primary_id),
  secondary_server_name        sysname,
  secondary_database_name      sysname,
  last_copied_filename         NVARCHAR(500),
  last_loaded_filename         NVARCHAR(500),
  last_copied_last_updated     DATETIME,
  last_loaded_last_updated     DATETIME,
  secondary_plan_id            UNIQUEIDENTIFIER,
  copy_enabled                 BIT,
  load_enabled                 BIT,              /* 1 = load enabled, 0 = load disabled */
  out_of_sync_threshold        INT,
  threshold_alert              INT,
  threshold_alert_enabled      BIT,              /*1 = enabled, 0 = disabled */
  planned_outage_start_time    INT,
  planned_outage_end_time      INT,
  planned_outage_weekday_mask  INT,
  allow_role_change            BIT DEFAULT (0)
 )
END
ELSE 
BEGIN
  IF (NOT EXISTS (SELECT * 
    FROM INFORMATION_SCHEMA.COLUMNS
    WHERE (TABLE_NAME = N'log_shipping_secondaries')
      AND (COLUMN_NAME = N'allow_role_change')))

  BEGIN
    PRINT ''
    PRINT 'Adding columns to table log_shipping_secondaries...'

    ALTER TABLE log_shipping_secondaries
     ADD allow_role_change BIT DEFAULT (0)

  END
END 
go

/**************************************************************/
/* sp_add_log_shipping_monitor_jobs                           */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_add_log_shipping_monitor_jobs...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_add_log_shipping_monitor_jobs' AND type = N'P')  )
  drop procedure sp_add_log_shipping_monitor_jobs
go
CREATE PROCEDURE sp_add_log_shipping_monitor_jobs AS 
BEGIN
  SET NOCOUNT ON
  BEGIN TRANSACTION
  DECLARE @rv INT
  DECLARE @backup_job_name sysname
  SET @backup_job_name = N'Log Shipping Alert Job - Backup'
  IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @backup_job_name))
  BEGIN
    EXECUTE @rv = msdb.dbo.sp_add_job @job_name = N'Log Shipping Alert Job - Backup'

    IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error 

    EXECUTE @rv = msdb.dbo.sp_add_jobstep 
      @job_name = N'Log Shipping Alert Job - Backup', 
      @step_id = 1, 
      @step_name = N'Log Shipping Alert - Backup', 
      @command = N'EXECUTE msdb.dbo.sp_log_shipping_monitor_backup',
      @on_fail_action = 2, 
      @flags = 4, 
      @subsystem = N'TSQL', 
      @on_success_step_id = 0, 
      @on_success_action = 1, 
      @on_fail_step_id = 0
    IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error 

   EXECUTE @rv = msdb.dbo.sp_add_jobschedule 
      @job_name = @backup_job_name, 
      @freq_type = 4, 
      @freq_interval = 1, 
      @freq_subday_type = 0x4, 
      @freq_subday_interval = 1, -- run every minute
      @freq_relative_interval = 0, 
      @name = @backup_job_name
    IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error

   EXECUTE @rv = msdb.dbo.sp_add_jobserver @job_name = @backup_job_name, @server_name = NULL
    IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error
  END

  DECLARE @restore_job_name sysname
  SET @restore_job_name = 'Log Shipping Alert Job - Restore'
  IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @restore_job_name))
  BEGIN
    EXECUTE @rv = msdb.dbo.sp_add_job @job_name = @restore_job_name

    IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error 

    EXECUTE @rv = msdb.dbo.sp_add_jobstep 
      @job_name = @restore_job_name, 
      @step_id = 1, 
      @step_name = @restore_job_name, 
      @command = N'EXECUTE msdb.dbo.sp_log_shipping_monitor_restore',
      @on_fail_action = 2, 
      @flags = 4, 
      @subsystem = N'TSQL', 
      @on_success_step_id = 0, 
      @on_success_action = 1, 
      @on_fail_step_id = 0
    IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error 

    EXECUTE @rv = msdb.dbo.sp_add_jobschedule 
      @job_name = @restore_job_name, 
      @freq_type = 4, 
      @freq_interval = 1, 
      @freq_subday_type = 0x4, 
      @freq_subday_interval = 1, -- run every minute
      @freq_relative_interval = 0, 
      @name = @restore_job_name
    IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error

    EXECUTE @rv = msdb.dbo.sp_add_jobserver @job_name = @restore_job_name, @server_name = NULL
    IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error
  END
  COMMIT TRANSACTION
  RETURN

rollback_quit:
  ROLLBACK TRANSACTION
END
go

/**************************************************************/
/* sp_add_log_shipping_primary                                */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_add_log_shipping_primary...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_add_log_shipping_primary' AND type = N'P'))
  drop procedure sp_add_log_shipping_primary
go
CREATE PROCEDURE sp_add_log_shipping_primary
  @primary_server_name         sysname,
  @primary_database_name       sysname,
  @maintenance_plan_id         UNIQUEIDENTIFIER = NULL,
  @backup_threshold            INT              = 60,
  @threshold_alert             INT              = 14420,
  @threshold_alert_enabled     BIT              = 1,
  @planned_outage_start_time   INT              = 0,
  @planned_outage_end_time     INT              = 0,
  @planned_outage_weekday_mask INT              = 0,
  @primary_id              INT = NULL OUTPUT       
AS
BEGIN
  SET NOCOUNT ON
  IF EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name)
  BEGIN  
    DECLARE @pair_name NVARCHAR 
   SELECT @pair_name = @primary_server_name + N'.' + @primary_database_name
   RAISERROR (14261,16,1, N'primary_server_name.primary_database_name', @pair_name)
    RETURN (1) -- error
  END
  INSERT INTO msdb.dbo.log_shipping_primaries (
    primary_server_name,
    primary_database_name,
    maintenance_plan_id,
    backup_threshold,
    threshold_alert,
    threshold_alert_enabled,
    last_backup_filename,
    last_updated,
    planned_outage_start_time,
    planned_outage_end_time,
    planned_outage_weekday_mask,
    source_directory)  
  VALUES (@primary_server_name,  
    @primary_database_name, 
    @maintenance_plan_id, 
    @backup_threshold,
    @threshold_alert,
    @threshold_alert_enabled,
    N'first_file_000000000000.trn',
    GETDATE (),
    @planned_outage_start_time,
    @planned_outage_end_time,
    @planned_outage_weekday_mask,
    NULL)

  SELECT @primary_id = @@IDENTITY

  EXECUTE msdb.dbo.sp_add_log_shipping_monitor_jobs
END
go

/**************************************************************/
/* sp_add_log_shipping_secondary                              */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_add_log_shipping_secondary...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_add_log_shipping_secondary' AND type = N'P'))
  drop procedure sp_add_log_shipping_secondary
go
CREATE PROCEDURE sp_add_log_shipping_secondary
  @primary_id                  INT,
  @secondary_server_name       sysname,
  @secondary_database_name     sysname,
  @secondary_plan_id           UNIQUEIDENTIFIER,
  @copy_enabled                BIT              = 1,
  @load_enabled                BIT              = 1,
  @out_of_sync_threshold       INT              = 60,
  @threshold_alert             INT              = 14421,
  @threshold_alert_enabled     BIT              = 1,
  @planned_outage_start_time   INT              = 0,
  @planned_outage_end_time     INT              = 0,
  @planned_outage_weekday_mask INT              = 0,
  @allow_role_change           BIT              = 0 
AS
BEGIN
  SET NOCOUNT ON
  IF NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries where primary_id = @primary_id)
  BEGIN
    RAISERROR (14262, 16, 1, N'primary_id', N'msdb.dbo.log_shipping_primaries')
    RETURN(1)
  END

  INSERT INTO msdb.dbo.log_shipping_secondaries (
    primary_id,
    secondary_server_name,
    secondary_database_name,
    last_copied_filename,
    last_loaded_filename,
    last_copied_last_updated,
    last_loaded_last_updated,
    secondary_plan_id,
    copy_enabled,
    load_enabled,
    out_of_sync_threshold,
    threshold_alert,
    threshold_alert_enabled,
    planned_outage_start_time,
    planned_outage_end_time,
    planned_outage_weekday_mask,
    allow_role_change)
   VALUES (@primary_id,
    @secondary_server_name,
    @secondary_database_name,
    N'first_file_000000000000.trn',
    N'first_file_000000000000.trn',
    GETDATE (),
    GETDATE (),
    @secondary_plan_id,
    @copy_enabled,
    @load_enabled,
    @out_of_sync_threshold,
    @threshold_alert,
    @threshold_alert_enabled,
    @planned_outage_start_time,
    @planned_outage_end_time,
    @planned_outage_weekday_mask,
    @allow_role_change)
END
go

/**************************************************************/
/* sp_delete_log_shipping_monitor_jobs                        */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_delete_log_shipping_monitor_jobs...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_delete_log_shipping_monitor_jobs' AND type = N'P')  )
  drop procedure sp_delete_log_shipping_monitor_jobs
go
CREATE PROCEDURE sp_delete_log_shipping_monitor_jobs AS
BEGIN
  DECLARE @backup_job_name sysname
  SET NOCOUNT ON
  SET @backup_job_name = N'Log Shipping Alert Job - Backup'
  IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @backup_job_name))
    EXECUTE msdb.dbo.sp_delete_job @job_name = N'Log Shipping Alert Job - Backup'

  DECLARE @restore_job_name sysname
  SET @restore_job_name = 'Log Shipping Alert Job - Restore'
  IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @restore_job_name))
    EXECUTE msdb.dbo.sp_delete_job @job_name = N'Log Shipping Alert Job - Restore'
END
go

/**************************************************************/
/* sp_delete_log_shipping_primary                             */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_delete_log_shipping_primary...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_delete_log_shipping_primary' AND type = N'P')  )
  drop procedure sp_delete_log_shipping_primary
go
CREATE PROCEDURE sp_delete_log_shipping_primary 
  @primary_server_name sysname,
  @primary_database_name sysname,
  @delete_secondaries BIT = 0
AS BEGIN
  DECLARE @primary_id INT

  SET NOCOUNT ON

  SELECT @primary_id = primary_id 
    FROM msdb.dbo.log_shipping_primaries 
    WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name
  IF (@primary_id IS NULL)
    RETURN (0)

  BEGIN TRANSACTION
  IF (EXISTS (SELECT * FROM msdb.dbo.log_shipping_secondaries WHERE primary_id = @primary_id))
  BEGIN
    IF (@delete_secondaries = 0)
    BEGIN
      RAISERROR (14429,-1,-1)
      goto rollback_quit
    END
    DELETE FROM msdb.dbo.log_shipping_secondaries WHERE primary_id = @primary_id
    IF (@@ERROR <> 0)
      GOTO rollback_quit
  END
  DELETE FROM msdb.dbo.log_shipping_primaries WHERE primary_id = @primary_id
  IF (@@ERROR <> 0)
    GOTO rollback_quit

  COMMIT TRANSACTION
  DECLARE @i INT
  SELECT @i = COUNT(*) FROM msdb.dbo.log_shipping_primaries
  IF (@i=0)
    EXECUTE msdb.dbo.sp_delete_log_shipping_monitor_jobs
  RETURN (0)

rollback_quit:
  ROLLBACK TRANSACTION
  RETURN(1) -- error
END
go

/**************************************************************/
/* sp_delete_log_shipping_secondary                           */
/**************************************************************/
PRINT ''
PRINT 'Creating sp_delete_log_shipping_secondary...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_delete_log_shipping_secondary' AND type = N'P')  )
  drop procedure sp_delete_log_shipping_secondary
go
CREATE PROCEDURE sp_delete_log_shipping_secondary 
  @secondary_server_name   sysname,
  @secondary_database_name sysname
AS BEGIN
  SET NOCOUNT ON
  DELETE FROM msdb.dbo.log_shipping_secondaries WHERE 
    secondary_server_name   = @secondary_server_name AND
    secondary_database_name = @secondary_database_name
END
go

/**************************************************************/
/* sp_log_shipping_in_sync                                    */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_log_shipping_in_sync...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_log_shipping_in_sync' AND type = N'P')  )
  drop procedure sp_log_shipping_in_sync
go
CREATE PROCEDURE sp_log_shipping_in_sync
  @last_updated        DATETIME,
  @compare_with        DATETIME,
  @threshold           INT,
  @outage_start_time   INT,
  @outage_end_time     INT,
  @outage_weekday_mask INT,
  @enabled             BIT = 1,
  @delta               INT = NULL OUTPUT
AS BEGIN
  SET NOCOUNT ON
  DECLARE @cur_time INT

  SELECT @delta = DATEDIFF (mi, @last_updated, @compare_with)
  -- in sync
  IF (@delta <= @threshold)
    RETURN (0) -- in sync

  IF (@enabled = 0) 
    RETURN(0) -- in sync

  IF (@outage_weekday_mask & DATEPART(dw, GETDATE ()) > 0) -- potentially in outage window
  BEGIN
    SELECT @cur_time = DATEPART (hh, GETDATE()) * 10000 +
                       DATEPART (mi, GETDATE()) * 100 + 
                       DATEPART (ss, GETDATE())
     -- outage doesn't span midnight
    IF (@outage_start_time < @outage_end_time)
    BEGIN
      IF (@cur_time >= @outage_start_time AND @cur_time < @outage_end_time)
        RETURN(1) -- in outage
    END
     -- outage does span midnight
   ELSE IF (@outage_start_time > @outage_end_time)
   BEGIN
     IF (@cur_time >= @outage_start_time OR @cur_time < @outage_end_time)
       RETURN(1) -- in outage
   END
  END
  RETURN(-1 ) -- not in outage, not in sync
END
go

/**************************************************************/
/* sp_log_shipping_get_date_from_file                         */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_log_shipping_get_date_from_file...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_log_shipping_get_date_from_file' AND type = N'P')  )
  drop procedure sp_log_shipping_get_date_from_file
go
CREATE PROCEDURE sp_log_shipping_get_date_from_file 
  @db_name sysname,
  @filename NVARCHAR (500),
  @file_date DATETIME OUTPUT
AS
BEGIN
  SET NOCOUNT ON

  DECLARE @tempname NVARCHAR (500)
  IF (LEN (@filename) - (LEN(@db_name) + LEN ('_tlog_')) <= 0)
    RETURN(1) -- filename string isn't long enough
  SELECT @tempname = RIGHT (@filename, LEN (@filename) - (LEN(@db_name) + LEN ('_tlog_')))
  IF (CHARINDEX ('.',@tempname,0) > 0)
    SELECT @tempname = LEFT (@tempname, CHARINDEX ('.',@tempname,0) - 1)
  IF (LEN (@tempname) <>  8 AND LEN (@tempname) <> 12)
    RETURN (1) -- error must be yyyymmddhhmm or yyyymmdd
  IF (ISNUMERIC (@tempname) = 0 OR CHARINDEX ('.',@tempname,0) <> 0 OR CONVERT (FLOAT,SUBSTRING (@tempname, 1,8)) < 1 )
    RETURN (1) -- must be numeric, can't contain any '.' etc
  SELECT @file_date = CONVERT (DATETIME,SUBSTRING (@tempname, 1,8),112)
  IF (LEN (@tempname) = 12)
  BEGIN
    SELECT @file_date = DATEADD (hh, CONVERT (INT, SUBSTRING (@tempname,9,2)),@file_date)
    SELECT @file_date = DATEADD (mi, CONVERT (INT, SUBSTRING (@tempname,11,2)),@file_date)
  END
  RETURN (0) -- success
END
go

/**************************************************************/
/* sp_get_log_shipping_monitor_info                           */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_get_log_shipping_monitor_info...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_get_log_shipping_monitor_info' AND type = N'P')  )
  drop procedure sp_get_log_shipping_monitor_info
go
CREATE PROCEDURE sp_get_log_shipping_monitor_info
  @primary_server_name     sysname = N'%',
  @primary_database_name   sysname = N'%',
  @secondary_server_name   sysname = N'%',
  @secondary_database_name sysname = N'%'
AS BEGIN
  SET NOCOUNT ON
  DECLARE @lsp TABLE (
    primary_server_name            sysname       COLLATE database_default NOT NULL,
    primary_database_name          sysname       COLLATE database_default NOT NULL,
    secondary_server_name          sysname       COLLATE database_default NOT NULL,
    secondary_database_name        sysname       COLLATE database_default NOT NULL,
    backup_threshold               INT           NOT NULL,
    backup_threshold_alert         INT           NOT NULL,
    backup_threshold_alert_enabled BIT           NOT NULL,
    last_backup_filename           NVARCHAR(500) COLLATE database_default NOT NULL,
    last_backup_last_updated       DATETIME      NOT NULL,
    backup_outage_start_time       INT           NOT NULL,
    backup_outage_end_time         INT           NOT NULL,
    backup_outage_weekday_mask     INT           NOT NULL,
    backup_in_sync                 INT           NULL, -- 0 = in sync, -1 = out of sync, 1 = in outage window
    backup_delta                   INT           NULL,
    last_copied_filename           NVARCHAR(500) COLLATE database_default NOT NULL,
    last_copied_last_updated       DATETIME      NOT NULL,
    last_loaded_filename           NVARCHAR(500) COLLATE database_default NOT NULL,
    last_loaded_last_updated       DATETIME      NOT NULL,
    copy_delta                     INT           NULL,
    copy_enabled                   BIT           NOT NULL,
    load_enabled                   BIT           NOT NULL,
    out_of_sync_threshold          INT           NOT NULL,
    load_threshold_alert           INT           NOT NULL,
    load_threshold_alert_enabled   BIT           NOT NULL,
    load_outage_start_time         INT           NOT NULL,
    load_outage_end_time           INT           NOT NULL,
    load_outage_weekday_mask       INT           NOT NULL,
    load_in_sync                   INT           NULL, -- 0 = in sync, -1 = out of sync, 1 = in outage window
    load_delta                     INT           NULL,
    maintenance_plan_id             UNIQUEIDENTIFIER NULL,
    secondary_plan_id              UNIQUEIDENTIFIER NOT NULL)

  INSERT INTO @lsp

 SELECT
    primary_server_name,
    primary_database_name,
    secondary_server_name,
    secondary_database_name,
    backup_threshold,
    p.threshold_alert,
    p.threshold_alert_enabled,
    last_backup_filename,
    p.last_updated,
    p.planned_outage_start_time,
    p.planned_outage_end_time,
    p.planned_outage_weekday_mask,
    NULL,
    NULL,
    last_copied_filename,
    last_copied_last_updated,
    last_loaded_filename,
    last_loaded_last_updated,
    NULL,
    copy_enabled,
    load_enabled,
    out_of_sync_threshold,
    s.threshold_alert,
    s.threshold_alert_enabled,
    s.planned_outage_start_time,
    s.planned_outage_weekday_mask,
    s.planned_outage_end_time,
    NULL,
    NULL,
    maintenance_plan_id,
    secondary_plan_id
  FROM msdb.dbo.log_shipping_primaries p, msdb.dbo.log_shipping_secondaries s
  WHERE 
    p.primary_id = s.primary_id AND
    primary_server_name LIKE @primary_server_name AND
    primary_database_name LIKE @primary_database_name AND
    secondary_server_name LIKE @secondary_server_name AND
    secondary_database_name LIKE @secondary_database_name

  DECLARE @load_in_sync                   INT
  DECLARE @backup_in_sync                 INT
  DECLARE @_primary_server_name           sysname 
  DECLARE @_primary_database_name         sysname 
  DECLARE @_secondary_server_name         sysname
  DECLARE @_secondary_database_name       sysname
  DECLARE @last_loaded_last_updated       DATETIME
  DECLARE @last_loaded_filename           NVARCHAR (500)
  DECLARE @last_copied_filename           NVARCHAR (500)
  DECLARE @last_backup_last_updated       DATETIME
  DECLARE @last_backup_filename           NVARCHAR (500)
  DECLARE @backup_outage_start_time       INT
  DECLARE @backup_outage_end_time         INT
  DECLARE @backup_outage_weekday_mask     INT
  DECLARE @backup_threshold               INT
  DECLARE @backup_threshold_alert_enabled BIT
  DECLARE @load_outage_start_time         INT
  DECLARE @load_outage_end_time           INT
  DECLARE @load_outage_weekday_mask       INT
  DECLARE @load_threshold                 INT
  DECLARE @load_threshold_alert_enabled   BIT
  DECLARE @backupdt                       DATETIME
  DECLARE @restoredt                      DATETIME
  DECLARE @copydt                         DATETIME
  DECLARE @rv                             INT
  DECLARE @dt                             DATETIME
  DECLARE @copy_delta                     INT
  DECLARE @load_delta                     INT
  DECLARE @backup_delta                   INT
  DECLARE @last_copied_last_updated       DATETIME

  SELECT @dt = GETDATE ()

  DECLARE sync_update CURSOR FOR
    SELECT 
      primary_server_name, 
      primary_database_name, 
      secondary_server_name, 
      secondary_database_name,
      last_backup_filename,
      last_backup_last_updated,
      last_loaded_filename,
      last_loaded_last_updated,
      backup_outage_start_time,
      backup_outage_end_time,
      backup_outage_weekday_mask,
      backup_threshold,
      backup_threshold_alert_enabled,
      load_outage_start_time,
      load_outage_end_time,
      out_of_sync_threshold,
      load_outage_weekday_mask,
      load_threshold_alert_enabled,
      last_copied_filename,
      last_copied_last_updated
    FROM @lsp
    FOR READ ONLY

  OPEN sync_update

loop:
  FETCH NEXT FROM sync_update INTO
    @_primary_server_name, 
    @_primary_database_name, 
    @_secondary_server_name, 
    @_secondary_database_name,
    @last_backup_filename,
    @last_backup_last_updated,
    @last_loaded_filename,
    @last_loaded_last_updated,
    @backup_outage_start_time,
    @backup_outage_end_time,
    @backup_outage_weekday_mask,
    @backup_threshold,
    @backup_threshold_alert_enabled,
    @load_outage_start_time,
    @load_outage_end_time,
    @load_threshold,
    @load_outage_weekday_mask,
    @load_threshold_alert_enabled,
    @last_copied_filename,
    @last_copied_last_updated

  IF @@fetch_status <> 0
    GOTO _loop

  EXECUTE @rv = sp_log_shipping_get_date_from_file @_primary_database_name, @last_backup_filename, @backupdt OUTPUT
  IF (@rv <> 0)
    SELECT @backupdt = @last_backup_last_updated
  EXECUTE @rv = sp_log_shipping_get_date_from_file @_primary_database_name, @last_loaded_filename, @restoredt OUTPUT
  IF  (@rv <> 0)
    SELECT @restoredt = @last_loaded_last_updated
  EXECUTE @rv = sp_log_shipping_get_date_from_file @_primary_database_name, @last_copied_filename, @copydt OUTPUT
  IF  (@rv <> 0)
    SELECT @copydt = @last_copied_last_updated
  
  EXECUTE @load_in_sync = msdb.dbo.sp_log_shipping_in_sync
    @restoredt,
    @backupdt,
    @load_threshold,
    @load_outage_start_time,
    @load_outage_end_time,
    @load_outage_weekday_mask,
    @load_threshold_alert_enabled,
    @load_delta OUTPUT

  EXECUTE @backup_in_sync = msdb.dbo.sp_log_shipping_in_sync
    @last_backup_last_updated,
    @dt,
    @backup_threshold,
    @backup_outage_start_time,
    @backup_outage_end_time,
    @backup_outage_weekday_mask,
    @backup_threshold_alert_enabled,
    @backup_delta OUTPUT

  EXECUTE msdb.dbo.sp_log_shipping_in_sync
    @copydt,
    @backupdt,
    1,0,0,0,0,
    @copy_delta OUTPUT

  UPDATE @lsp 
  SET backup_in_sync = @backup_in_sync, load_in_sync  = @load_in_sync, 
    copy_delta = @copy_delta, load_delta = @load_delta, backup_delta = @backup_delta
  WHERE primary_server_name = @_primary_server_name AND
    secondary_server_name = @_secondary_server_name AND
    primary_database_name = @_primary_database_name AND
    secondary_database_name = @_secondary_database_name 
  GOTO loop
_loop:
  CLOSE sync_update
  DEALLOCATE sync_update
  SELECT * FROM @lsp
END
go

/**************************************************************/
/* sp_update_log_shipping_monitor_info                        */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_update_log_shipping_monitor_info...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_update_log_shipping_monitor_info' AND type = N'P')  )
  DROP PROCEDURE sp_update_log_shipping_monitor_info
go
CREATE PROCEDURE sp_update_log_shipping_monitor_info
  @primary_server_name                 sysname,
  @primary_database_name               sysname,
  @secondary_server_name               sysname,
  @secondary_database_name             sysname,
  @backup_threshold                    INT = NULL,
  @backup_threshold_alert              INT = NULL,
  @backup_threshold_alert_enabled      BIT = NULL,
  @backup_outage_start_time            INT = NULL,
  @backup_outage_end_time              INT = NULL,
  @backup_outage_weekday_mask          INT = NULL,
  @copy_enabled                        BIT = NULL,
  @load_enabled                        BIT = NULL,
  @out_of_sync_threshold               INT = NULL,
  @out_of_sync_threshold_alert         INT = NULL,
  @out_of_sync_threshold_alert_enabled BIT = NULL,
  @out_of_sync_outage_start_time       INT = NULL,
  @out_of_sync_outage_end_time         INT = NULL,
  @out_of_sync_outage_weekday_mask     INT = NULL
AS BEGIN
  SET NOCOUNT ON
  DECLARE @_backup_threshold                    INT
  DECLARE @_backup_threshold_alert              INT
  DECLARE @_backup_threshold_alert_enabled      BIT
  DECLARE @_backup_outage_start_time            INT
  DECLARE @_backup_outage_end_time              INT
  DECLARE @_backup_outage_weekday_mask          INT
  DECLARE @_copy_enabled                        BIT
  DECLARE @_load_enabled                        BIT
  DECLARE @_out_of_sync_threshold               INT
  DECLARE @_out_of_sync_threshold_alert         INT
  DECLARE @_out_of_sync_threshold_alert_enabled BIT
  DECLARE @_out_of_sync_outage_start_time       INT
  DECLARE @_out_of_sync_outage_end_time         INT
  DECLARE @_out_of_sync_outage_weekday_mask     INT

  -- check that the primary exists
  IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name))
  BEGIN
    DECLARE @pp sysname
    SELECT @pp = @primary_server_name + N'.' + @primary_database_name
    RAISERROR (14262, 16, 1, N'primary_server_name.primary_database_name', @pp)
    RETURN (1) -- error
  END

  -- check that the secondary exists
  IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_secondaries WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name))
  BEGIN
    DECLARE @sp sysname
    SELECT @sp = @secondary_server_name + N'.' + @secondary_database_name
    RAISERROR (14262, 16, 1, N'secondary_server_name.secondary_database_name', @sp)
    RETURN (1) -- error
  END

  -- load the original variables

 SELECT
    @_backup_threshold                    = backup_threshold,
    @_backup_threshold_alert              = p.threshold_alert,
    @_backup_threshold_alert_enabled      = p.threshold_alert_enabled,
    @_backup_outage_start_time            = p.planned_outage_start_time,
    @_backup_outage_end_time              = p.planned_outage_end_time,
    @_backup_outage_weekday_mask          = p.planned_outage_weekday_mask,
    @_copy_enabled                        = copy_enabled,
    @_load_enabled                        = load_enabled,
    @_out_of_sync_threshold               = out_of_sync_threshold,
    @_out_of_sync_threshold_alert         = s.threshold_alert,
    @_out_of_sync_threshold_alert_enabled = s.threshold_alert_enabled,
    @_out_of_sync_outage_start_time       = s.planned_outage_start_time,
    @_out_of_sync_outage_weekday_mask     = s.planned_outage_weekday_mask,
    @_out_of_sync_outage_end_time         = s.planned_outage_end_time
  FROM msdb.dbo.log_shipping_primaries p, msdb.dbo.log_shipping_secondaries s
  WHERE 
    p.primary_id            = s.primary_id           AND
    primary_server_name     = @primary_server_name   AND
    primary_database_name   = @primary_database_name AND
    secondary_server_name   = @secondary_server_name AND
    secondary_database_name = @secondary_database_name

  SELECT @_backup_threshold                    = ISNULL (@backup_threshold,                    @_backup_threshold)
  SELECT @_backup_threshold_alert              = ISNULL (@backup_threshold_alert,              @_backup_threshold_alert)
  SELECT @_backup_threshold_alert_enabled      = ISNULL (@backup_threshold_alert_enabled,      @_backup_threshold_alert_enabled)
  SELECT @_backup_outage_start_time            = ISNULL (@backup_outage_start_time,            @_backup_outage_start_time)
  SELECT @_backup_outage_end_time              = ISNULL (@backup_outage_end_time,              @_backup_outage_end_time)
  SELECT @_backup_outage_weekday_mask          = ISNULL (@backup_outage_weekday_mask,          @_backup_outage_weekday_mask)
  SELECT @_copy_enabled                        = ISNULL (@copy_enabled,                        @_copy_enabled)
  SELECT @_load_enabled                        = ISNULL (@load_enabled,                        @_load_enabled)
  SELECT @_out_of_sync_threshold               = ISNULL (@out_of_sync_threshold,               @_out_of_sync_threshold)
  SELECT @_out_of_sync_threshold_alert         = ISNULL (@out_of_sync_threshold_alert,         @_out_of_sync_threshold_alert)
  SELECT @_out_of_sync_threshold_alert_enabled = ISNULL (@out_of_sync_threshold_alert_enabled, @_out_of_sync_threshold_alert_enabled)
  SELECT @_out_of_sync_outage_start_time       = ISNULL (@out_of_sync_outage_start_time,       @_out_of_sync_outage_start_time)
  SELECT @_out_of_sync_outage_end_time         = ISNULL (@out_of_sync_outage_end_time,         @_out_of_sync_outage_end_time)
  SELECT @_out_of_sync_outage_weekday_mask     = ISNULL (@out_of_sync_outage_weekday_mask,     @_out_of_sync_outage_weekday_mask)

  -- updates
  UPDATE msdb.dbo.log_shipping_primaries SET
    backup_threshold            = @_backup_threshold,
    threshold_alert             = @_backup_threshold_alert,
    threshold_alert_enabled     = @_backup_threshold_alert_enabled,
    planned_outage_start_time   = @_backup_outage_start_time,
    planned_outage_end_time     = @_backup_outage_end_time,
    planned_outage_weekday_mask = @_backup_outage_weekday_mask
  WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name

  UPDATE msdb.dbo.log_shipping_secondaries SET
    copy_enabled                = @_copy_enabled,
    load_enabled                = @_load_enabled,
    out_of_sync_threshold       = @_out_of_sync_threshold,
    threshold_alert             = @_out_of_sync_threshold_alert,
    threshold_alert_enabled     = @_out_of_sync_threshold_alert_enabled,
    planned_outage_start_time   = @_out_of_sync_outage_start_time,
    planned_outage_end_time     = @_out_of_sync_outage_weekday_mask,
    planned_outage_weekday_mask = @_out_of_sync_outage_end_time
  WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name
RETURN(0)
END
go

/**************************************************************/
/* sp_delete_log_shipping_monitor_info                        */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_delete_log_shipping_monitor_info...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_delete_log_shipping_monitor_info' AND type = N'P')  )
  DROP PROCEDURE sp_delete_log_shipping_monitor_info
go
CREATE PROCEDURE sp_delete_log_shipping_monitor_info
  @primary_server_name                 sysname,
  @primary_database_name               sysname,
  @secondary_server_name               sysname,
  @secondary_database_name             sysname
AS BEGIN
  -- check that the primary exists
  IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name))
  BEGIN
    DECLARE @pp sysname
    SELECT @pp = @primary_server_name + N'.' + @primary_database_name
    RAISERROR (14262, 16, 1, N'primary_server_name.primary_database_name', @pp)
    RETURN (1) -- error
  END

  -- check that the secondary exists
  IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_secondaries WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name))
  BEGIN
    DECLARE @sp sysname
    SELECT @sp = @secondary_server_name + N'.' + @secondary_database_name
    RAISERROR (14262, 16, 1, N'secondary_server_name.secondary_database_name', @sp)
    RETURN (1) -- error
  END

  BEGIN TRANSACTION

  -- delete the secondary
  DELETE FROM msdb.dbo.log_shipping_secondaries WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name
  IF (@@error <> 0)
    goto rollback_quit

  -- if there are no more secondaries for this primary then delete it
  IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries p, msdb.dbo.log_shipping_secondaries s WHERE p.primary_id = s.primary_id AND primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name))
  BEGIN
    DELETE FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name
    IF (@@error <> 0)
      goto rollback_quit
  END
 COMMIT TRANSACTION
 RETURN (0)

rollback_quit:
  ROLLBACK TRANSACTION
  RETURN(1) -- Failure
END
go 

/**************************************************************/
/* sp_remove_log_shipping_monitor_account                     */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_remove_log_shipping_monitor_account...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_remove_log_shipping_monitor_account' AND type = N'P')  )
  DROP PROCEDURE sp_remove_log_shipping_monitor_account
go

CREATE PROCEDURE sp_remove_log_shipping_monitor_account
AS
BEGIN
  SET NOCOUNT ON
  EXECUTE sp_dropuser N'log_shipping_monitor_probe'
  EXECUTE sp_droplogin N'log_shipping_monitor_probe'
END
go

/**************************************************************/
/* sp_log_shipping_monitor_backup                             */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_log_shipping_monitor_backup...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_log_shipping_monitor_backup' AND type = N'P')  )
  drop procedure sp_log_shipping_monitor_backup
go

CREATE PROCEDURE sp_log_shipping_monitor_backup AS
BEGIN
  DECLARE @primary_id                  sysname
  DECLARE @primary_server_name         sysname 
  DECLARE @primary_database_name       sysname 
  DECLARE @maintenance_plan_id         UNIQUEIDENTIFIER
  DECLARE @backup_threshold            INT
  DECLARE @threshold_alert             INT 
  DECLARE @threshold_alert_enabled     BIT 
  DECLARE @last_backup_filename        sysname 
  DECLARE @last_updated                DATETIME
  DECLARE @planned_outage_start_time   INT
  DECLARE @planned_outage_end_time     INT 
  DECLARE @planned_outage_weekday_mask INT
  DECLARE @sync_status                 INT
  DECLARE @backup_delta                INT
  DECLARE @delta_string                NVARCHAR (10)
  DECLARE @dt                             DATETIME

  SELECT @dt = GETDATE ()

  SET NOCOUNT ON

  DECLARE bmlsp_cur CURSOR FOR
    SELECT primary_id, 
           primary_server_name, 
           primary_database_name, 
         maintenance_plan_id, 
           backup_threshold, 
           threshold_alert, 
           threshold_alert_enabled, 
           last_backup_filename, 
           last_updated,
           planned_outage_start_time, 
           planned_outage_end_time, 
           planned_outage_weekday_mask 
    FROM msdb.dbo.log_shipping_primaries
    FOR READ ONLY

  OPEN bmlsp_cur
loop:
  FETCH NEXT FROM bmlsp_cur 
  INTO @primary_id, 
       @primary_server_name, 
      @primary_database_name, 
      @maintenance_plan_id,
       @backup_threshold, 
      @threshold_alert, 
      @threshold_alert_enabled, 
      @last_backup_filename, 
      @last_updated, 
      @planned_outage_start_time,
       @planned_outage_end_time, 
      @planned_outage_weekday_mask

  IF @@FETCH_STATUS <> 0 -- nothing more to fetch, finish the loop
    GOTO _loop

  EXECUTE @sync_status = sp_log_shipping_in_sync
    @last_updated,
    @dt,
     @backup_threshold,
   @planned_outage_start_time,
   @planned_outage_end_time,
    @planned_outage_weekday_mask,
   @threshold_alert_enabled,
   @backup_delta OUTPUT

   IF (@sync_status < 0)
   BEGIN
     SELECT @delta_string = CONVERT (NVARCHAR(10), @backup_delta)
     RAISERROR (@threshold_alert, 16, 1, @primary_server_name, @primary_database_name, @delta_string)
   END

  GOTO loop
_loop:
  CLOSE bmlsp_cur
  DEALLOCATE bmlsp_cur
END
go

/**************************************************************/
/* sp_log_shipping_monitor_restore                            */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_log_shipping_monitor_restore...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_log_shipping_monitor_restore' AND type = N'P')  )
  drop procedure sp_log_shipping_monitor_restore
go
CREATE PROCEDURE sp_log_shipping_monitor_restore AS
BEGIN
  SET NOCOUNT ON
  DECLARE @primary_id                  INT
  DECLARE @secondary_server_name       sysname
  DECLARE @secondary_database_name     sysname
  DECLARE @secondary_plan_id           UNIQUEIDENTIFIER
  DECLARE @out_of_sync_threshold       INT 
  DECLARE @threshold_alert             INT 
  DECLARE @threshold_alert_enabled     BIT 
  DECLARE @last_loaded_filename        NVARCHAR (500)
  DECLARE @last_backup_filename        NVARCHAR (500) 
  DECLARE @primary_database_name       sysname
  DECLARE @last_loaded_last_updated    DATETIME
  DECLARE @last_backup_last_updated    DATETIME
  DECLARE @planned_outage_start_time   INT 
  DECLARE @planned_outage_end_time     INT 
  DECLARE @planned_outage_weekday_mask INT
  DECLARE @sync_status                 INT
  DECLARE @sync_delta                  INT
  DECLARE @delta_string                NVARCHAR(10)

  SET NOCOUNT ON
  DECLARE @backupdt  DATETIME
  DECLARE @restoredt DATETIME
  DECLARE @rv        INT
  DECLARE rmlsp_cur CURSOR FOR
    SELECT s.primary_id, 
      s.secondary_server_name, 
      s.secondary_database_name, 
      s.secondary_plan_id, 
      s.out_of_sync_threshold, 
      s.threshold_alert, 
      s.threshold_alert_enabled, 
      s.last_loaded_filename, 
      s.last_loaded_last_updated,
      p.last_backup_filename,
      p.last_updated,
      p.primary_database_name,
      s.planned_outage_start_time, 
      s.planned_outage_end_time, 
      s.planned_outage_weekday_mask 
    FROM msdb.dbo.log_shipping_secondaries s 
    INNER JOIN msdb.dbo.log_shipping_primaries p 
    ON s.primary_id = p.primary_id
    FOR READ ONLY

  OPEN rmlsp_cur
loop:
  FETCH NEXT FROM rmlsp_cur 
  INTO @primary_id, 
      @secondary_server_name, 
         @secondary_database_name, 
         @secondary_plan_id, 
       @out_of_sync_threshold, 
         @threshold_alert, 
         @threshold_alert_enabled, 
         @last_loaded_filename, 
         @last_loaded_last_updated,
       @last_backup_filename,
       @last_backup_last_updated,
       @primary_database_name,
       @planned_outage_start_time, 
         @planned_outage_end_time, 
         @planned_outage_weekday_mask 

  IF @@FETCH_STATUS <> 0 -- nothing more to fetch, finish the loop
    GOTO _loop

  EXECUTE @rv = sp_log_shipping_get_date_from_file @primary_database_name, @last_backup_filename, @backupdt OUTPUT
  IF (@rv <> 0)
    SELECT @backupdt = @last_backup_last_updated
  
  EXECUTE @rv = sp_log_shipping_get_date_from_file @primary_database_name, @last_loaded_filename, @restoredt OUTPUT
  IF  (@rv <> 0)
    SELECT @restoredt = @last_loaded_last_updated

  EXECUTE @sync_status = sp_log_shipping_in_sync
    @restoredt,
    @backupdt,
     @out_of_sync_threshold,
     @planned_outage_start_time,
     @planned_outage_end_time,
    @planned_outage_weekday_mask,
    @threshold_alert_enabled,
    @sync_delta OUTPUT

   IF (@sync_status < 0)
   BEGIN
     SELECT @delta_string = CONVERT (NVARCHAR(10), @sync_delta)
     RAISERROR (@threshold_alert, 16, 1, @secondary_server_name, @secondary_database_name, @delta_string)
   END

  GOTO loop
_loop:
  CLOSE rmlsp_cur
  DEALLOCATE rmlsp_cur
END
go

/**************************************************************/
/* sp_change_monitor_role                                     */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_change_monitor_role...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_change_monitor_role' AND type = N'P')  )
  DROP PROCEDURE sp_change_monitor_role
go
CREATE PROCEDURE sp_change_monitor_role
  @primary_server     sysname,
  @secondary_server   sysname,
  @database           sysname,
  @new_source         NVARCHAR (128)
AS BEGIN
  SET NOCOUNT ON

  BEGIN TRANSACTION

  -- drop the secondary
  DELETE FROM msdb.dbo.log_shipping_secondaries 
    WHERE secondary_server_name = @secondary_server AND secondary_database_name = @database

  IF (@@ROWCOUNT <> 1)
  BEGIN
      ROLLBACK TRANSACTION
      RAISERROR (14442,-1,-1)
      return(1)
  END

  -- let everyone know that we are the new primary
  UPDATE msdb.dbo.log_shipping_primaries 
    SET primary_server_name = @secondary_server, primary_database_name = @database, source_directory = @new_source
    WHERE primary_server_name = @primary_server AND primary_database_name = @database

  IF (@@ROWCOUNT <> 1)
  BEGIN
      ROLLBACK TRANSACTION
      RAISERROR (14442,-1,-1)
      return(1)
  END
  COMMIT TRANSACTION

END
go

/**************************************************************/
/* sp_create_log_shipping_monitor_account                     */
/**************************************************************/
PRINT ''
PRINT 'Creating procedure sp_create_log_shipping_monitor_account...'
go
IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_create_log_shipping_monitor_account' AND type = N'P')  )
  drop procedure sp_create_log_shipping_monitor_account
go
CREATE PROCEDURE sp_create_log_shipping_monitor_account @password sysname
AS
BEGIN
  DECLARE @rv INT
  SET NOCOUNT ON
-- raise an error if the password already exists
  if exists(select * from master.dbo.syslogins where loginname = N'log_shipping_monitor_probe')
  begin
    raiserror(15025,-1,-1,N'log_shipping_monitor_probe')
    RETURN (1) -- error
  end

  IF (@password = N'')
  BEGIN
    EXECUTE @rv = sp_addlogin N'log_shipping_monitor_probe', @defdb = N'msdb'
    IF @@error <>0 or @rv <> 0
      RETURN (1) -- error
  END
  ELSE
  BEGIN
    EXECUTE @rv = sp_addlogin N'log_shipping_monitor_probe', @password, N'msdb'
    IF @@error <>0 or @rv <> 0
      RETURN (1) -- error
  END

  EXECUTE @rv = sp_grantdbaccess N'log_shipping_monitor_probe', N'log_shipping_monitor_probe'
  IF @@error <>0 or @rv <> 0
    RETURN (1) -- error

  GRANT UPDATE ON log_shipping_primaries   TO log_shipping_monitor_probe
  GRANT UPDATE ON log_shipping_secondaries TO log_shipping_monitor_probe
  GRANT SELECT ON log_shipping_primaries   TO log_shipping_monitor_probe
  GRANT SELECT ON log_shipping_secondaries TO log_shipping_monitor_probe

  RETURN (0)
END
go

/**************************************************************/
/* INTEGRATION SERVICES SECTION                               */
/**************************************************************/

USE msdb
GO

if not exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sysssispackages]'))
BEGIN
CREATE TABLE [dbo].[sysssispackages] (
   [name] [sysname] NOT NULL ,
   [id] [uniqueidentifier] NOT NULL ,
   [description] [nvarchar] (1024) NULL ,
   [createdate] [datetime] NOT NULL ,
   [folderid] [uniqueidentifier] NOT NULL ,
   [ownersid] [varbinary] (85) NOT NULL ,
   [packagedata] [image] NOT NULL ,
   [packageformat] [int] NOT NULL,
   [packagetype] [int] NOT NULL CONSTRAINT [DF__sysssispackages] DEFAULT (0),
   [vermajor] [int] NOT NULL,
   [verminor] [int] NOT NULL,
   [verbuild] [int] NOT NULL,
   [vercomments] [nvarchar] (1024) NULL,
   [verid] [uniqueidentifier] NOT NULL,
   [isencrypted] [bit] NOT NULL CONSTRAINT [DF__sysssispackages_2] DEFAULT (0),
   [readrolesid] [varbinary] (85) NULL,
   [writerolesid] [varbinary] (85) NULL,
   CONSTRAINT [pk_sysssispackages] PRIMARY KEY NONCLUSTERED 
   (
      [folderid],
      [name]
   ) ON [PRIMARY] ,
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
END
else
BEGIN
IF NOT EXISTS (
  select * from msdb.dbo.syscolumns where name='isencrypted' and id =
      (select id from msdb.dbo.sysobjects where name='sysssispackages'))
BEGIN
  ALTER TABLE [dbo].[sysssispackages] ADD [isencrypted] [bit] NOT NULL CONSTRAINT [DF__sysssispackages_2] DEFAULT (0)
  ALTER TABLE [dbo].[sysssispackages] ADD [readrolesid] [varbinary] (85) NULL
  ALTER TABLE [dbo].[sysssispackages] ADD [writerolesid] [varbinary] (85) NULL
END
ELSE
BEGIN
  IF NOT EXISTS (
    select * from msdb.dbo.syscolumns where name='readrolesid' and id =
        (select id from msdb.dbo.sysobjects where name='sysssispackages'))
  BEGIN
    ALTER TABLE [dbo].[sysssispackages] DROP COLUMN [readrole]
    ALTER TABLE [dbo].[sysssispackages] DROP COLUMN [writerole]
    ALTER TABLE [dbo].[sysssispackages] ADD [readrolesid] [varbinary] (85) NULL
    ALTER TABLE [dbo].[sysssispackages] ADD [writerolesid] [varbinary] (85) NULL
  END
END
END
GO

/**************************************************************/
/* sysmaintplan_plans                                      */
/**************************************************************/
PRINT ''
PRINT 'Creating view sysmaintplan_plans...'
go
IF (NOT OBJECT_ID(N'dbo.sysmaintplan_plans', 'V') IS NULL)
  DROP VIEW sysmaintplan_plans
go
CREATE VIEW sysmaintplan_plans
AS
   SELECT
   s.name AS [name],
   s.id AS [id],
   s.description AS [description],
   s.createdate AS [create_date],
   suser_sname(s.ownersid) AS [owner],
   s.vermajor AS [version_major],
   s.verminor AS [version_minor],
   s.verbuild AS [version_build],
   s.vercomments AS [version_comments],
   ISNULL((select TOP 1 msx_plan from sysmaintplan_subplans where plan_id = s.id), 0) AS [from_msx],
   CASE WHEN (NOT EXISTS (select TOP 1 msx_job_id 
                          from sysmaintplan_subplans subplans, sysjobservers jobservers
                          where plan_id = s.id 
                          and msx_job_id is not null
                          and subplans.msx_job_id = jobservers.job_id
                          and server_id != 0)) 
        then 0 
        else 1 END AS [has_targets]
   FROM
   msdb.dbo.sysssispackages AS s
   WHERE
   (s.folderid = '08aa12d5-8f98-4dab-a4fc-980b150a5dc8' and s.packagetype = 6)
go

if not exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sysssispackagefolders]'))
BEGIN
CREATE TABLE [dbo].[sysssispackagefolders] (
   [folderid] [uniqueidentifier] NOT NULL ,
   [parentfolderid] [uniqueidentifier] NULL ,
   [foldername] [sysname] NOT NULL ,
   CONSTRAINT [PK_sysssispackagefolders] PRIMARY KEY NONCLUSTERED 
   (
      [folderid]
   )  ON [PRIMARY],
   CONSTRAINT [U_sysssispackagefoldersuniquepath] UNIQUE NONCLUSTERED 
   (
      [parentfolderid], 
      [foldername]
   ) ON [PRIMARY]
) ON [PRIMARY]
END
GO

-- WARNING! IMPORTANT! If you change sysssislog table schema,
-- be sure to update \dts\src\dtr\runtime\logproviders.cpp !!!

if not exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sysssislog]'))
BEGIN
CREATE TABLE [dbo].[sysssislog] (
   [id] [int] NOT NULL IDENTITY PRIMARY KEY,
   [event] [sysname] NOT NULL,
   [computer] [nvarchar] (128) NOT NULL,
   [operator] [nvarchar] (128) NOT NULL,
   [source] [nvarchar] (1024) NOT NULL,
   [sourceid] [uniqueidentifier] NOT NULL,
   [executionid] [uniqueidentifier] NOT NULL,
   [starttime] [datetime] NOT NULL,
   [endtime] [datetime] NOT NULL,
   [datacode] [int] NOT NULL,
   [databytes] [image] NULL,
   [message] [nvarchar] (2048) NOT NULL,
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
END
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_addlogentry]'))
    drop procedure [dbo].[sp_ssis_addlogentry]   

execute sp_executesql 
 N'CREATE PROCEDURE [dbo].[sp_ssis_addlogentry]
  @event sysname,
  @computer nvarchar(128),
  @operator nvarchar(128),
  @source nvarchar(1024),
  @sourceid uniqueidentifier,
  @executionid uniqueidentifier,
  @starttime datetime,
  @endtime datetime,
  @datacode int,
  @databytes image,
  @message nvarchar(2048)
AS
  INSERT INTO sysssislog (
      event,
      computer,
      operator,
      source,
      sourceid,
      executionid,
      starttime,
      endtime,
      datacode,
      databytes,
      message )
  VALUES (
      @event,
      @computer,
      @operator,
      @source,
      @sourceid,
      @executionid,
      @starttime,
      @endtime,
      @datacode,
      @databytes,
      @message )
  RETURN 0
'
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_listpackages]'))
    drop procedure [dbo].[sp_ssis_listpackages]

execute sp_executesql 
 N'CREATE PROCEDURE [dbo].[sp_ssis_listpackages]
  @folderid uniqueidentifier
AS
  SELECT
      name,
      id,
      description,
      createdate,
      folderid,
      datalength(packagedata),
      vermajor,
      verminor,
      verbuild,
      vercomments,
      verid
  FROM
      sysssispackages
  WHERE
      [folderid] = @folderid
  ORDER BY
      name
'
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_listfolders]'))
    drop procedure [dbo].[sp_ssis_listfolders]

execute sp_executesql 
 N'CREATE PROCEDURE [dbo].[sp_ssis_listfolders]
  @parentfolderid uniqueidentifier = NULL
AS
  SELECT
   folderid,
   parentfolderid,
   foldername
  FROM
      sysssispackagefolders
  WHERE
      [parentfolderid] = @parentfolderid OR 
      (@parentfolderid IS NULL AND [parentfolderid] IS NULL)
  ORDER BY 
      foldername
'
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_deletepackage]'))
    drop procedure [dbo].[sp_ssis_deletepackage]

execute sp_executesql 
 N'CREATE PROCEDURE [dbo].[sp_ssis_deletepackage]
  @name sysname,
  @folderid uniqueidentifier
AS
  DECLARE @sid varbinary(85)
  DECLARE @writerolesid varbinary(85)
  DECLARE @writerole nvarchar(128)
  SELECT
      @sid = [ownersid],
      @writerolesid = [writerolesid]
  FROM
      sysssispackages
  WHERE
      [name] = @name AND
      [folderid] = @folderid
  IF @sid IS NOT NULL
  BEGIN
      --// The row exists, check security
      IF @writerolesid IS NOT NULL
      BEGIN
          SELECT @writerole = [name] FROM sys.database_principals WHERE [type] = ''R'' AND [sid] = @writerolesid
          IF @writerole IS NULL SET @writerole = ''db_ssisadmin''
      END
      IF @writerole IS NULL
      BEGIN
          IF (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1)
          BEGIN
              IF (@sid<>SUSER_SID()) OR (IS_MEMBER(''db_ssisltduser'')<>1)
              BEGIN
                  RAISERROR (14307, -1, -1, @name)
                  RETURN 1  -- Failure
              END
          END
      END
      ELSE
      BEGIN
          -- If writerrole is set for this package, 
          -- Allow sysadmins and the members of writer role to delete this package
          IF (IS_MEMBER(@writerole)<>1)  AND (IS_SRVROLEMEMBER(''sysadmin'')<>1)
          BEGIN
              IF (@sid<>SUSER_SID()) OR (IS_MEMBER(''db_ssisltduser'')<>1)
              BEGIN
                  RAISERROR (14307, -1, -1, @name)
                  RETURN 1  -- Failure
              END
          END
      END
  END
  DELETE FROM sysssispackages
  WHERE
      [name] = @name AND
      [folderid] = @folderid
'
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_deletefolder]'))
    drop procedure [dbo].[sp_ssis_deletefolder]

execute sp_executesql 
 N'CREATE PROCEDURE [dbo].[sp_ssis_deletefolder]
  @folderid uniqueidentifier
AS
   DECLARE @name  sysname
   DECLARE @count int

   IF @folderid = ''00000000-0000-0000-0000-000000000000''
   BEGIN
      RAISERROR (14307, -1, -1, ''00000000-0000-0000-0000-000000000000'')
      RETURN 1  -- Failure
   END

   SELECT
       @name = [foldername]
   FROM
       sysssispackagefolders
   WHERE
       [folderid] = @folderid
   IF @name IS NOT NULL
   BEGIN
       --// The row exists, check security
       IF (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1)
       BEGIN
           IF (IS_MEMBER(''db_ssisltduser'')<>1)
           BEGIN
               RAISERROR (14307, -1, -1, @name)
               RETURN 1  -- Failure
           END
       END
   END

   -- Get the number of packages in this folder
   SELECT
      @count = count(*)
   FROM
      sysssispackages
   WHERE
      [folderid] = @folderid

   -- Are there any packages in this folder
   IF @count > 0
   BEGIN
      -- Yes, do not delete
      RAISERROR (14593, -1, -1, @name)
      RETURN 1  -- Failure
   END

   -- Get the number of folders in this folder
   SELECT
      @count = count(*)
   FROM
      sysssispackagefolders
   WHERE
      [parentfolderid] = @folderid

   -- Are there any folders in this folder
   IF @count > 0
   BEGIN
      -- Yes, do not delete
      RAISERROR (14593, -1, -1, @name)
      RETURN 1  -- Failure
   END

   DELETE FROM sysssispackagefolders
   WHERE
       [folderid] = @folderid
'
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_getpackage]'))
    drop procedure [dbo].[sp_ssis_getpackage]

execute sp_executesql 
 N'CREATE PROCEDURE [dbo].[sp_ssis_getpackage]
  @name sysname,
  @folderid uniqueidentifier
AS
  DECLARE @sid varbinary(85)
  DECLARE @isencrypted bit
  DECLARE @readrolesid varbinary(85)
  DECLARE @readrole nvarchar(128)
  --// Check security, if the row exists
  SELECT @sid = [ownersid], @readrolesid = [readrolesid] FROM sysssispackages WHERE [name] = @name AND [folderid] = @folderid
  IF @sid IS NOT NULL
  BEGIN
      IF @readrolesid IS NOT NULL
      BEGIN
          SELECT @readrole = [name] FROM sys.database_principals WHERE [type] = ''R'' AND [sid] = @readrolesid
          IF @readrole IS NULL SET @readrole = ''db_ssisadmin''
      END
      IF @readrole IS NOT NULL
      BEGIN
          IF (IS_MEMBER(@readrole)<>1) AND (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1)
          BEGIN
              IF (IS_MEMBER(''db_ssisltduser'')<>1) OR (@sid<>SUSER_SID())
              BEGIN
                  RAISERROR (14307, -1, -1, @name)
                  RETURN 1  -- Failure
              END
          END
      END
      ELSE
      BEGIN
          IF (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) AND (IS_MEMBER(''db_ssisoperator'')<>1)
          BEGIN
              IF (IS_MEMBER(''db_ssisltduser'')<>1) OR (@sid<>SUSER_SID())
              BEGIN
                  RAISERROR (14586, -1, -1, @name)
                  RETURN 1  -- Failure
              END
          END
      END
  END

  SELECT
      packagedata
  FROM
      sysssispackages
  WHERE
      [name] = @name AND
      [folderid] = @folderid
'
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_getfolder]'))
    drop procedure [dbo].[sp_ssis_getfolder]

execute sp_executesql 
 N'CREATE PROCEDURE [dbo].[sp_ssis_getfolder]
  @name sysname,
  @parentfolderid uniqueidentifier
AS
  SELECT
   folder.folderid,
   folder.foldername,
   folder.parentfolderid,
   parent.foldername
  FROM
      sysssispackagefolders folder 
  LEFT OUTER JOIN 
      sysssispackagefolders parent
  ON
      folder.parentfolderid = parent.folderid
  WHERE
      folder.foldername = @name AND
      (folder.parentfolderid = @parentfolderid OR 
      (@parentfolderid IS NULL AND folder.parentfolderid IS NULL))
'
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_putpackage]'))
    drop procedure [dbo].[sp_ssis_putpackage]

execute sp_executesql 
 N'CREATE PROCEDURE [dbo].[sp_ssis_putpackage]
  @name sysname,
  @id uniqueidentifier,
  @description nvarchar(1024),
  @createdate datetime,
  @folderid uniqueidentifier,
  @packagedata image,
  @packageformat int,
  @packagetype int,
  @vermajor int,
  @verminor int,
  @verbuild int,
  @vercomments nvarchar(1024),
  @verid uniqueidentifier
AS
  SET NOCOUNT ON
  DECLARE @sid varbinary(85)
  DECLARE @writerolesid varbinary(85)
  DECLARE @writerole nvarchar(128)
  --// Determine if we should INSERT or UPDATE
  SELECT @sid = [ownersid], @writerolesid = [writerolesid] FROM sysssispackages WHERE [name] = @name AND [folderid] = @folderid
  IF @sid IS NOT NULL
  BEGIN
      --// The row exists, check security
      IF @writerolesid IS NOT NULL
      BEGIN
          SELECT @writerole = [name] FROM sys.database_principals WHERE [type] = ''R'' AND [sid] = @writerolesid
          IF @writerole IS NULL SET @writerole = ''db_ssisadmin''
      END
      IF @writerole IS NULL
      BEGIN
          IF (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1)
          BEGIN
              IF (@sid<>SUSER_SID()) OR (IS_MEMBER(''db_ssisltduser'')<>1)
              BEGIN
                  RAISERROR (14307, -1, -1, @name)
                  RETURN 1  -- Failure
              END
          END
      END
      ELSE
      BEGIN
          IF (IS_MEMBER(@writerole)<>1) AND (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1)
          BEGIN
              IF (@sid<>SUSER_SID()) OR (IS_MEMBER(''db_ssisltduser'')<>1)
              BEGIN
                  RAISERROR (14307, -1, -1, @name)
                  RETURN 1  -- Failure
              END
          END
      END
      --// Security check passed, UPDATE now
      UPDATE sysssispackages
      SET
          id = @id,
          description = @description,
          createdate = @createdate,
          packagedata = @packagedata,
          packageformat = @packageformat,
          packagetype = @packagetype,
          vermajor = @vermajor,
          verminor = @verminor,
          verbuild = @verbuild,
          vercomments = @vercomments,
          verid = @verid
      WHERE
          name = @name AND folderid = @folderid
  END
  ELSE
  BEGIN
      --// The row does not exist, check security
      IF (IS_MEMBER(''db_ssisltduser'')<>1) AND (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1)
      BEGIN
          RAISERROR (14307, -1, -1, @name)
          RETURN 1  -- Failure
      END
      --// Security check passed, INSERT now
      INSERT INTO sysssispackages (
          name,
          id,
          description,
          createdate,
          folderid,
          ownersid,
          packagedata,
          packageformat,
          packagetype,
          vermajor,
          verminor,
          verbuild,
          vercomments,
          verid )
      VALUES (
          @name,
          @id,
          @description,
          @createdate,
          @folderid,
          SUSER_SID(),
          @packagedata,
          @packageformat,
          @packagetype,
          @vermajor,
          @verminor,
          @verbuild,
          @vercomments,
          @verid )
  END
  RETURN 0    -- SUCCESS
'
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_checkexists]'))
    drop procedure [dbo].[sp_ssis_checkexists]

execute sp_executesql 
 N'CREATE PROCEDURE [dbo].[sp_ssis_checkexists]
  @name sysname,
  @folderid uniqueidentifier
AS
  SET NOCOUNT ON
  SELECT TOP 1 1 FROM sysssispackages WHERE [name] = @name AND [folderid] = @folderid
  RETURN 0    -- SUCCESS
'
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_addfolder]'))
    drop procedure [dbo].[sp_ssis_addfolder]

execute sp_executesql 
 N'CREATE PROCEDURE [dbo].[sp_ssis_addfolder]
  @parentfolderid uniqueidentifier,
  @name sysname,
  @folderid uniqueidentifier = NULL
AS
   --Check security
   IF (IS_MEMBER(''db_ssisltduser'')<>1) AND (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1)
   BEGIN
       RAISERROR (14591, -1, -1, @name)
       RETURN 1  -- Failure
   END

   --// Security check passed, INSERT now
   INSERT INTO sysssispackagefolders (folderid, parentfolderid, foldername)
   VALUES (ISNULL(@folderid, NEWID()), @parentfolderid, @name)
'
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_renamefolder]'))
    drop procedure [dbo].[sp_ssis_renamefolder]

execute sp_executesql 
 N'CREATE PROCEDURE [dbo].[sp_ssis_renamefolder]
  @folderid uniqueidentifier,
  @name sysname
AS
   --Check security
   IF (IS_MEMBER(''db_ssisltduser'')<>1) AND (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1)
   BEGIN
       RAISERROR (14591, -1, -1, @name)
       RETURN 1  -- Failure
   END

   --// Security check passed, INSERT now
   UPDATE sysssispackagefolders
   SET [foldername] = @name
   WHERE [folderid] = @folderid
'
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_setpackageroles]'))
DROP PROCEDURE [dbo].[sp_ssis_setpackageroles]
GO

execute sp_executesql 
 N'CREATE PROCEDURE [dbo].[sp_ssis_setpackageroles]
  @name sysname,
  @folderid uniqueidentifier,
  @readrole nvarchar (128),
  @writerole nvarchar (128)
AS
  SET NOCOUNT ON
  DECLARE @sid varbinary(85)
  --// Determine if we should INSERT or UPDATE
  SELECT @sid = ownersid FROM sysssispackages WHERE name = @name AND folderid = @folderid
  IF @sid IS NOT NULL
  BEGIN
      --// The row exists, check security
      IF (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1)
      BEGIN
          IF (@sid<>SUSER_SID())
          BEGIN
              RAISERROR (14307, -1, -1, @name)
              RETURN 1  -- Failure
          END
      END
      --// Security check passed, UPDATE now
      DECLARE @readrolesid varbinary(85)
      DECLARE @writerolesid varbinary(85)
      SELECT @readrolesid = [sid] FROM sys.database_principals WHERE [type] = ''R'' AND [name] = @readrole
      SELECT @writerolesid = [sid] FROM sys.database_principals WHERE [type] = ''R'' AND [name] = @writerole
      IF @readrolesid IS NULL AND @readrole IS NOT NULL
      BEGIN
          RAISERROR (15014, -1, -1, @readrole)
          RETURN 1
      END
      IF @writerolesid IS NULL AND @writerole IS NOT NULL
      BEGIN
          RAISERROR (15014, -1, -1, @writerole)
          RETURN 1
      END
      UPDATE sysssispackages
      SET
          [readrolesid] = @readrolesid,
          [writerolesid] = @writerolesid
      WHERE
          name = @name AND folderid = @folderid
  END
  ELSE
  BEGIN
      RAISERROR (14307, -1, -1, @name)
      RETURN 1  -- Failure
  END
  RETURN 0    -- SUCCESS
'
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_getpackageroles]'))
DROP PROCEDURE [dbo].[sp_ssis_getpackageroles]
GO

execute sp_executesql 
 N'CREATE PROCEDURE [dbo].[sp_ssis_getpackageroles]
  @name sysname,
  @folderid uniqueidentifier
AS
  DECLARE @readrolesid varbinary(85)
  DECLARE @writerolesid varbinary(85)
  DECLARE @readrole nvarchar(128)
  DECLARE @writerole nvarchar(128)
  SELECT
      @readrolesid = [readrolesid],
      @writerolesid = [writerolesid]
  FROM
      sysssispackages
  WHERE
      [name] = @name AND
      [folderid] = @folderid
  SELECT @readrole = [name] FROM sys.database_principals WHERE [type] = ''R'' AND [sid] = @readrolesid
  SELECT @writerole = [name] FROM sys.database_principals WHERE [type] = ''R'' AND [sid] = @writerolesid
  SELECT @readrole AS readrole, @writerole AS writerole
'
GO


GRANT  EXECUTE  ON [dbo].[sp_ssis_addlogentry]     TO [db_ssisadmin]
GRANT  EXECUTE  ON [dbo].[sp_ssis_addlogentry]     TO [db_ssisltduser]
GRANT  EXECUTE  ON [dbo].[sp_ssis_deletepackage]   TO [db_ssisadmin]
GRANT  EXECUTE  ON [dbo].[sp_ssis_deletepackage]   TO [db_ssisltduser]
GRANT  EXECUTE  ON [dbo].[sp_ssis_deletepackage]   TO [db_ssisoperator]
GRANT  EXECUTE  ON [dbo].[sp_ssis_getpackage]      TO [db_ssisadmin]
GRANT  EXECUTE  ON [dbo].[sp_ssis_getpackage]      TO [db_ssisltduser]
GRANT  EXECUTE  ON [dbo].[sp_ssis_getpackage]      TO [db_ssisoperator]
GRANT  EXECUTE  ON [dbo].[sp_ssis_listpackages]    TO [db_ssisadmin]
GRANT  EXECUTE  ON [dbo].[sp_ssis_listpackages]    TO [db_ssisltduser]
GRANT  EXECUTE  ON [dbo].[sp_ssis_listpackages]    TO [db_ssisoperator]
GRANT  EXECUTE  ON [dbo].[sp_ssis_putpackage]      TO [db_ssisadmin]
GRANT  EXECUTE  ON [dbo].[sp_ssis_putpackage]      TO [db_ssisltduser]
GRANT  EXECUTE  ON [dbo].[sp_ssis_putpackage]      TO [db_ssisoperator]
GRANT  EXECUTE  ON [dbo].[sp_ssis_checkexists]     TO [db_ssisadmin]
GRANT  EXECUTE  ON [dbo].[sp_ssis_checkexists]     TO [db_ssisltduser]
GRANT  EXECUTE  ON [dbo].[sp_ssis_checkexists]     TO [db_ssisoperator]
GRANT  EXECUTE  ON [dbo].[sp_ssis_listfolders]     TO [db_ssisadmin]  
GRANT  EXECUTE  ON [dbo].[sp_ssis_listfolders]     TO [db_ssisltduser]
GRANT  EXECUTE  ON [dbo].[sp_ssis_listfolders]     TO [db_ssisoperator] 
GRANT  EXECUTE  ON [dbo].[sp_ssis_deletefolder]    TO [db_ssisadmin]  
GRANT  EXECUTE  ON [dbo].[sp_ssis_deletefolder]    TO [db_ssisltduser]
GRANT  EXECUTE  ON [dbo].[sp_ssis_addfolder]       TO [db_ssisadmin]  
GRANT  EXECUTE  ON [dbo].[sp_ssis_addfolder]       TO [db_ssisltduser]
GRANT  EXECUTE  ON [dbo].[sp_ssis_renamefolder]    TO [db_ssisadmin]  
GRANT  EXECUTE  ON [dbo].[sp_ssis_renamefolder]    TO [db_ssisltduser]
GRANT  EXECUTE  ON [dbo].[sp_ssis_getfolder]       TO [db_ssisadmin]
GRANT  EXECUTE  ON [dbo].[sp_ssis_getfolder]       TO [db_ssisltduser]
GRANT  EXECUTE  ON [dbo].[sp_ssis_getfolder]       TO [db_ssisoperator]
GRANT  EXECUTE  ON [dbo].[sp_ssis_setpackageroles] TO [db_ssisadmin]
GRANT  EXECUTE  ON [dbo].[sp_ssis_setpackageroles] TO [db_ssisltduser]
GRANT  EXECUTE  ON [dbo].[sp_ssis_getpackageroles] TO [db_ssisadmin]
GRANT  EXECUTE  ON [dbo].[sp_ssis_getpackageroles] TO [db_ssisltduser]
GO

GRANT  ALL      ON [dbo].[sysssislog]            TO [db_ssisadmin]
GRANT  INSERT   ON [dbo].[sysssislog]            TO [db_ssisltduser]
GRANT  SELECT   ON [dbo].[sysssislog]            TO [db_ssisltduser]
GRANT  INSERT   ON [dbo].[sysssislog]            TO [db_ssisoperator]
GRANT  SELECT   ON [dbo].[sysssislog]            TO [db_ssisoperator]
GO

-- Maintenance Plans
 -- Allow SQLAgent on target servers to gather information about
 -- maintenance plans from the master.
GRANT EXECUTE ON sp_maintplan_subplans_by_job  TO SQLAgentUserRole
GRANT EXECUTE ON sp_maintplan_subplans_by_job  TO TargetServersRole


/**************************************************************/
/*                                                            */
/*  D  A  T  A     C  O  L  L  E  C  T  O  R                  */
/*                                                            */
/**************************************************************/
USE msdb
GO

---------------------------------------------------------------
-- Data Collector: Security: Database Principals
---------------------------------------------------------------

PRINT ''
PRINT 'Create dc_operator role...'
IF ( NOT EXISTS (SELECT * FROM sys.database_principals 
                    WHERE name = N'dc_operator' AND type = 'R'))
BEGIN
    CREATE ROLE [dc_operator]
END
ELSE -- if the role exists check to see if it has members
BEGIN
    IF NOT EXISTS (SELECT rm.member_principal_id
                FROM sys.database_principals dp 
                INNER JOIN sys.database_role_members rm ON rm.role_principal_id = dp.principal_id
                WHERE name = N'dc_operator' AND type = 'R')
    BEGIN
        -- if the role has no members drop and recreate it
        DROP ROLE [dc_operator]
        CREATE ROLE [dc_operator]
    END
END
GO

EXECUTE sp_addrolemember @rolename = 'db_ssisltduser' , 
                   @membername = 'dc_operator' 
GO

EXECUTE sp_addrolemember @rolename = 'db_ssisoperator' , 
                   @membername = 'dc_operator' 
GO

EXECUTE sp_addrolemember @rolename = 'SQLAgentUserRole' , 
                   @membername = 'dc_operator' 
GO

PRINT ''
PRINT 'Create dc_admin role...'
IF ( NOT EXISTS (SELECT * FROM sys.database_principals 
                    WHERE name = N'dc_admin' AND type = 'R'))
BEGIN
    CREATE ROLE [dc_admin]
END
ELSE -- if the role exists check to see if it has members
BEGIN
    IF NOT EXISTS (SELECT rm.member_principal_id
                FROM sys.database_principals dp 
                INNER JOIN sys.database_role_members rm ON rm.role_principal_id = dp.principal_id
                WHERE name = N'dc_admin' AND type = 'R')
    BEGIN
        -- if the role has no members drop and recreate it
        DROP ROLE [dc_admin]
        CREATE ROLE [dc_admin]
    END
END
GO

EXECUTE sp_addrolemember @rolename = 'dc_operator' , 
                   @membername = 'dc_admin' 
GO

PRINT ''
PRINT 'Create dc_proxy role...'
IF ( NOT EXISTS (SELECT * FROM sys.database_principals 
                    WHERE name = N'dc_proxy' AND type = 'R'))
BEGIN
    CREATE ROLE [dc_proxy]
END
ELSE -- if the role exists check to see if it has members
BEGIN
    IF NOT EXISTS (SELECT rm.member_principal_id
                FROM sys.database_principals dp 
                INNER JOIN sys.database_role_members rm ON rm.role_principal_id = dp.principal_id
                WHERE name = N'dc_proxy' AND type = 'R')
    BEGIN
        -- if the role has no members drop and recreate it
        DROP ROLE [dc_proxy]
        CREATE ROLE [dc_proxy]
    END
END
GO

EXECUTE sp_addrolemember @rolename = 'db_ssisltduser' , 
                   @membername = 'dc_proxy' 
GO

EXECUTE sp_addrolemember @rolename = 'db_ssisoperator' , 
                   @membername = 'dc_proxy' 
GO

PRINT ''
PRINT 'Create loginless user that has ownership of data collector agent-related securables...'
IF (NOT EXISTS(SELECT * FROM sys.database_principals WHERE NAME = 'MS_DataCollectorInternalUser'))
BEGIN
    CREATE USER [MS_DataCollectorInternalUser] WITHOUT LOGIN
END
GO

EXECUTE sp_addrolemember @rolename = 'db_ssisoperator' , 
                   @membername = 'MS_DataCollectorInternalUser' 
GO

EXECUTE sp_addrolemember @rolename = 'SQLAgentUserRole' , 
                   @membername = 'MS_DataCollectorInternalUser' 
GO

EXECUTE sp_addrolemember @rolename = 'dc_admin' , 
                   @membername = 'MS_DataCollectorInternalUser' 
GO

GRANT IMPERSONATE ON USER::[MS_DataCollectorInternalUser] TO [dc_admin];
GO

---------------------------------------------------------------
-- Configuration store
---------------------------------------------------------------
IF (OBJECT_ID(N'[dbo].[syscollector_config_store_internal]', 'U') IS NULL)
BEGIN
    PRINT 'Creating table [dbo].[syscollector_config_store_internal]...'
    CREATE TABLE [dbo].[syscollector_config_store_internal] (
        parameter_name                nvarchar(128) NOT NULL,
        parameter_value                sql_variant NULL,
        CONSTRAINT [PK_syscollector_config_store_internal_paremeter_name] PRIMARY KEY CLUSTERED (parameter_name ASC)
        )
END
GO

IF (NOT OBJECT_ID(N'[dbo].[syscollector_config_store]', 'V') IS NULL)
BEGIN
    PRINT 'Dropping view [dbo].[syscollector_config_store]...'
    DROP VIEW [dbo].[syscollector_config_store]
END
GO

PRINT 'Creating view [dbo].[syscollector_config_store]...'
GO
CREATE VIEW [dbo].[syscollector_config_store]
AS
    SELECT
        s.parameter_name,
        s.parameter_value
    FROM 
        [dbo].[syscollector_config_store_internal] s
GO

-- populate config store with known name and value pairs
IF (NOT EXISTS(SELECT * FROM [dbo].[syscollector_config_store_internal]
                WHERE parameter_name = N'MDWInstance'))
BEGIN
INSERT INTO [dbo].[syscollector_config_store_internal] (
    parameter_name, 
    parameter_value
)
VALUES
(
    N'MDWInstance',
    NULL
)
END

IF (NOT EXISTS(SELECT * FROM [dbo].[syscollector_config_store_internal]
                WHERE parameter_name = N'MDWDatabase'))
BEGIN
INSERT INTO [dbo].[syscollector_config_store_internal] (
    parameter_name, 
    parameter_value
)
VALUES
(
    N'MDWDatabase',
    NULL
)
END

IF (NOT EXISTS(SELECT * FROM [dbo].[syscollector_config_store_internal]
                WHERE parameter_name = 'CollectorEnabled'))
BEGIN
INSERT INTO [dbo].[syscollector_config_store_internal] (
    parameter_name, 
    parameter_value
)
VALUES
(
    'CollectorEnabled',
    0
)
END

IF (NOT EXISTS(SELECT * FROM [dbo].[syscollector_config_store_internal]
                WHERE parameter_name = 'CacheWindow'))
BEGIN
INSERT INTO [dbo].[syscollector_config_store_internal] (
    parameter_name, 
    parameter_value
)
VALUES
(
    'CacheWindow',
    1  --By default, the collector will keep 1 window's worth of uploads
)
END

IF (NOT EXISTS(SELECT * FROM [dbo].[syscollector_config_store_internal]
                WHERE parameter_name = 'CacheDirectory'))
BEGIN
INSERT INTO [dbo].[syscollector_config_store_internal] (
    parameter_name, 
    parameter_value
)
VALUES
(
    'CacheDirectory',
    NULL
)
END

GO

---------------------------------------------------------------
-- Access collector level properties
---------------------------------------------------------------

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_verify_collector_state]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_verify_collector_state]...'
    DROP PROCEDURE [dbo].[sp_syscollector_verify_collector_state]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_verify_collector_state]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_verify_collector_state]
    @desired_state          int
WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser'
AS
BEGIN
    DECLARE @collector_enabled      INT
    SET @collector_enabled = CONVERT(int, (SELECT parameter_value FROM dbo.syscollector_config_store_internal
                            WHERE parameter_name = 'CollectorEnabled'))

    IF (@collector_enabled IS NULL)
    BEGIN
        RAISERROR(14691, -1, -1)
        RETURN(1)
    END

    IF (@collector_enabled = 0) AND (@desired_state = 1)
    BEGIN
        RAISERROR(14681, -1, -1)
        RETURN(1)
    END

    IF (@collector_enabled = 1) AND (@desired_state = 0)
    BEGIN
        RAISERROR(14690, -1, -1)
        RETURN(1)
    END

    RETURN(0)
END
GO

IF (NOT OBJECT_ID('dbo.sp_syscollector_set_warehouse_instance_name', 'P') IS NULL)
BEGIN
    DROP PROCEDURE [dbo].[sp_syscollector_set_warehouse_instance_name]
END
GO

PRINT ''
PRINT 'Creating procedure [dbo].[sp_syscollector_set_warehouse_instance_name]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_set_warehouse_instance_name]
    @instance_name                    sysname = NULL
AS
BEGIN
    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_admin')
        RETURN(1) -- Failure
    END

    -- Check if the collector is disabled
    DECLARE @retVal int
    EXEC @retVal = [dbo].[sp_syscollector_verify_collector_state] @desired_state = 0
    IF (@retVal <> 0)
        RETURN (1)

    UPDATE [msdb].[dbo].[syscollector_config_store_internal]
    SET parameter_value = @instance_name
    WHERE parameter_name = N'MDWInstance'

    RETURN (0)
END
GO

IF (NOT OBJECT_ID('dbo.sp_syscollector_set_warehouse_database_name', 'P') IS NULL)
BEGIN
    DROP PROCEDURE [dbo].[sp_syscollector_set_warehouse_database_name]
END
GO

PRINT ''
PRINT 'Creating procedure [dbo].[sp_syscollector_set_warehouse_database_name]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_set_warehouse_database_name]
    @database_name                    sysname = NULL
AS
BEGIN
    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_admin')
        RETURN(1) -- Failure
    END

    -- Check if the collector is disabled
    DECLARE @retVal int
    EXEC @retVal = [dbo].[sp_syscollector_verify_collector_state] @desired_state = 0
    IF (@retVal <> 0)
        RETURN (1)

    UPDATE [msdb].[dbo].[syscollector_config_store_internal]
    SET parameter_value = @database_name
    WHERE parameter_name = N'MDWDatabase'

    RETURN (0)
END
GO

IF (NOT OBJECT_ID('dbo.sp_syscollector_set_cache_directory', 'P') IS NULL)
BEGIN
    DROP PROCEDURE [dbo].[sp_syscollector_set_cache_directory]
END
GO

PRINT ''
PRINT 'Creating procedure [dbo].[sp_syscollector_set_cache_directory]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_set_cache_directory]
    @cache_directory                    nvarchar(255) = NULL
AS
BEGIN
    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_admin')
        RETURN(1) -- Failure
    END

    SET @cache_directory = NULLIF(LTRIM(RTRIM(@cache_directory)), N'')

    -- Check if the collector is disabled
    DECLARE @retVal int
    EXEC @retVal = [dbo].[sp_syscollector_verify_collector_state] @desired_state = 0
    IF (@retVal <> 0)
        RETURN (1)

    UPDATE [msdb].[dbo].[syscollector_config_store_internal]
    SET parameter_value = @cache_directory
    WHERE parameter_name = N'CacheDirectory'

    RETURN (0)
END
GO

IF (NOT OBJECT_ID('dbo.sp_syscollector_set_cache_window', 'P') IS NULL)
BEGIN
    DROP PROCEDURE [dbo].[sp_syscollector_set_cache_window]
END
GO

PRINT ''
PRINT 'Creating procedure [dbo].[sp_syscollector_set_cache_window]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_set_cache_window]
    @cache_window                    int = 1
AS
BEGIN
    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_admin')
        RETURN(1) -- Failure
    END

    -- Check if the collector is disabled
    DECLARE @retVal int
    EXEC @retVal = [dbo].[sp_syscollector_verify_collector_state] @desired_state = 0
    IF (@retVal <> 0)
        RETURN (1)

    IF (@cache_window < -1)
    BEGIN
        RAISERROR(14687, -1, -1, @cache_window)
        RETURN(1)
    END

    UPDATE [msdb].[dbo].[syscollector_config_store_internal]
    SET parameter_value = @cache_window
    WHERE parameter_name = N'CacheWindow'

    RETURN (0)
END
GO

IF (NOT OBJECT_ID('dbo.sp_syscollector_get_warehouse_connection_string', 'P') IS NULL)
BEGIN
    DROP PROCEDURE [dbo].[sp_syscollector_get_warehouse_connection_string]
END
GO

PRINT ''
PRINT 'Creating procedure [dbo].[sp_syscollector_get_warehouse_connection_string]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_get_warehouse_connection_string]
    @connection_string              nvarchar(512) = NULL OUTPUT
AS
BEGIN
    DECLARE @instance_name sysname
    DECLARE @database_name sysname
    DECLARE @user_name sysname
    DECLARE @password sysname

    SELECT @instance_name = CONVERT(sysname,parameter_value)
    FROM [msdb].[dbo].[syscollector_config_store_internal]
    WHERE parameter_name = N'MDWInstance'

    IF (@instance_name IS NULL)
    BEGIN
        RAISERROR(14686, -1, -1)
        RETURN (1)
    END
    
    -- '"' is the delimiter for the sql client connection string
    SET @instance_name = QUOTENAME(@instance_name, '"')

    SELECT @database_name = CONVERT(sysname,parameter_value)
    FROM [msdb].[dbo].[syscollector_config_store_internal]
    WHERE parameter_name = N'MDWDatabase'

    IF (@database_name IS NULL)
    BEGIN
        RAISERROR(14686, -1, -1)
        RETURN (1)
    END

    SET @database_name = QUOTENAME(@database_name, '"')

    SET @connection_string = N'Data Source=' + @instance_name + N';Application Name="Data Collector - MDW";Initial Catalog=' + @database_name
    SET @connection_string = @connection_string + N';Use Encryption for Data=true;Trust Server Certificate=true;Provider=SQLNCLI10;Integrated Security=SSPI;Connect Timeout=60;';

    RETURN (0)
END
GO

-- 
-- Return the highest version of the Management Data Warehouse that this
-- Collector is no compatible with. Anything higher than this version
-- is fine.
--
-- If you change this number, make sure to change the corresponding versions
-- in HighestIncompatibleMDWVersion.cs
--
IF (NOT OBJECT_ID('dbo.fn_syscollector_highest_incompatible_mdw_version', 'FN') IS NULL)
BEGIN
    DROP FUNCTION [dbo].[fn_syscollector_highest_incompatible_mdw_version]
END
GO

PRINT ''
PRINT 'Creating function [dbo].[fn_syscollector_highest_incompatible_mdw_version]...'
GO
CREATE FUNCTION [dbo].[fn_syscollector_highest_incompatible_mdw_version]()
RETURNS nvarchar(50)
BEGIN
    RETURN '10.00.1300.13'  -- CTP6
END
GO
---------------------------------------------------------------
-- Collection set
---------------------------------------------------------------

IF (OBJECT_ID(N'[dbo].[syscollector_collection_sets_internal]', 'U') IS NULL)
BEGIN
    PRINT 'Creating table [dbo].[syscollector_collection_sets_internal]...'
    CREATE TABLE [dbo].[syscollector_collection_sets_internal] (
        collection_set_id            int IDENTITY NOT NULL,
        collection_set_uid            uniqueidentifier NOT NULL,      
        schedule_uid                uniqueidentifier NULL,          -- schedule to run collection or upload
        name                        sysname NOT NULL,               -- name of the collection set, must be unique
        name_id                        int NULL,                        -- sysmessage id of the name of the set (for localizing system collection set)
        target                        nvarchar(max) NULL,             -- future use
        is_running                    bit default 0 NOT NULL,         -- is the collection set active
        proxy_id                    int NULL,                       -- proxy to use to run the collection set
        is_system                    bit NOT NULL,                   -- indicates MS-shipped collection set
        collection_job_id            uniqueidentifier NULL,          -- id of the collection job
        upload_job_id                uniqueidentifier NULL,          -- id of the upload job
        collection_mode                smallint default 0 NOT NULL,    -- 0 - cached, 1 - non-cached
        logging_level                smallint default 2 NOT NULL,    -- 0 - errors only, 1 - errors & warnings, 2 - detailed
        description                    nvarchar(4000) NULL,            -- description of the set
        description_id                int NULL,                        -- sysmessage id of the description of the set (for localizing system collection set)
        days_until_expiration       smallint NOT NULL,              -- how long to keep the data from this collection set
        dump_on_any_error           bit default 0 NOT NULL,         -- configure SQL dumper to dump on any SSIS errors
        dump_on_codes               nvarchar(max) NULL,             -- configure SQL dumper to dump when we hit one of the specified SSIS errors 
        CONSTRAINT [PK_syscollector_collection_sets_internal] PRIMARY KEY CLUSTERED (collection_set_id ASC),
        CONSTRAINT [UQ_syscollector_collection_sets_internal_name] UNIQUE (name)
        )
    ALTER TABLE syscollector_collection_sets_internal
        ADD CONSTRAINT [FK_syscollector_collection_sets_internal_sysproxies] FOREIGN KEY(proxy_id)
        REFERENCES sysproxies (proxy_id)
    ALTER TABLE syscollector_collection_sets_internal
        ADD CONSTRAINT [FK_syscollector_collection_sets_collection_sysjobs] FOREIGN KEY(collection_job_id)
        REFERENCES sysjobs (job_id)
    ALTER TABLE syscollector_collection_sets_internal
        ADD CONSTRAINT [FK_syscollector_collection_sets_upload_sysjobs] FOREIGN KEY(upload_job_id)
        REFERENCES sysjobs(job_id)
END ELSE BEGIN
    IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'name_id' AND id = OBJECT_ID(N'[dbo].[syscollector_collection_sets_internal]')))
    BEGIN
        PRINT 'Altering table [dbo].[syscollector_collection_sets_internal]...'
        ALTER TABLE [dbo].[syscollector_collection_sets_internal] ADD name_id int NULL
    END
    
    IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'description_id' AND id = OBJECT_ID(N'[dbo].[syscollector_collection_sets_internal]')))
    BEGIN
        PRINT 'Altering table [dbo].[syscollector_collection_sets_internal]...'
        ALTER TABLE [dbo].[syscollector_collection_sets_internal] ADD description_id int NULL
    END
        IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'dump_on_any_error' AND id = OBJECT_ID(N'[dbo].[syscollector_collection_sets_internal]')))
    BEGIN
        PRINT 'Altering table [dbo].[syscollector_collection_sets_internal]...'
        ALTER TABLE [dbo].[syscollector_collection_sets_internal] ADD dump_on_any_error bit default 0
    END
    
    IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'dump_on_codes' AND id = OBJECT_ID(N'[dbo].[syscollector_collection_sets_internal]')))
    BEGIN
        PRINT 'Altering table [dbo].[syscollector_collection_sets_internal]...'
        ALTER TABLE [dbo].[syscollector_collection_sets_internal] ADD dump_on_codes nvarchar(max) NULL
    END    
END
GO

IF (NOT OBJECT_ID(N'[dbo].[syscollector_collection_sets]', 'V') IS NULL)
BEGIN
    PRINT 'Dropping view [dbo].[syscollector_collection_sets]...'
    DROP VIEW [dbo].[syscollector_collection_sets]
END
GO

PRINT 'Creating view [dbo].[syscollector_collection_sets]...'
GO
CREATE VIEW [dbo].[syscollector_collection_sets]
AS
    SELECT 
        s.collection_set_id,
        s.collection_set_uid,
        CASE 
            WHEN s.name_id IS NULL THEN s.name 
            ELSE FORMATMESSAGE(s.name_id)
        END AS name,        
        s.target,
        s.is_system,
        s.is_running,
        s.collection_mode,
        s.proxy_id,
        s.schedule_uid,
        s.collection_job_id,
        s.upload_job_id,
        s.logging_level,
        s.days_until_expiration,
        CASE 
            WHEN s.description_id IS NULL THEN s.description
            ELSE FORMATMESSAGE(s.description_id)
        END AS description,
        s.dump_on_any_error,
        s.dump_on_codes
    FROM 
        [dbo].[syscollector_collection_sets_internal] AS s
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_verify_collection_set]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_verify_collection_set]...'
    DROP PROCEDURE [dbo].[sp_syscollector_verify_collection_set]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_verify_collection_set]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_verify_collection_set]
    @collection_set_id        int = NULL OUTPUT,
    @name                    sysname = NULL OUTPUT
AS
BEGIN
    IF (@name IS NOT NULL)
    BEGIN
        -- Remove any leading/trailing spaces from parameters
        SET @name =            NULLIF(LTRIM(RTRIM(@name)), N'')
    END

    IF (@collection_set_id IS NULL AND @name IS NULL)
    BEGIN
        RAISERROR(14624, -1, -1, '@collection_set_id, @name')
        RETURN(1)
    END

    IF (@collection_set_id IS NOT NULL AND @name IS NOT NULL)
    BEGIN
        IF (NOT EXISTS(SELECT *
                        FROM dbo.syscollector_collection_sets
                        WHERE collection_set_id = @collection_set_id
                        AND name = @name))
        BEGIN
            DECLARE @errMsg NVARCHAR(196)
            SELECT @errMsg = CONVERT(NVARCHAR(36), @collection_set_id) + ', ' + @name
            RAISERROR(14262, -1, -1, '@collection_set_id, @name', @errMsg)
            RETURN(1)
        END
    END
    -- Check id
    ELSE IF (@collection_set_id IS NOT NULL)
    BEGIN
        SELECT @name = name
        FROM dbo.syscollector_collection_sets
        WHERE (collection_set_id = @collection_set_id)
    
        -- the view would take care of all the permissions issues.
        IF (@name IS NULL) 
        BEGIN
            DECLARE @collection_set_id_as_char VARCHAR(36)
            SELECT @collection_set_id_as_char = CONVERT(VARCHAR(36), @collection_set_id)
            RAISERROR(14262, -1, -1, '@collection_set_id', @collection_set_id_as_char)
            RETURN(1) -- Failure
        END
    END
    -- Check name
    ELSE IF (@name IS NOT NULL)
    BEGIN
        -- get the corresponding collection_set_id (if the collection set exists)
        SELECT @collection_set_id = collection_set_id
        FROM dbo.syscollector_collection_sets
        WHERE (name = @name)

        -- the view would take care of all the permissions issues.
        IF (@collection_set_id IS NULL)
        BEGIN
            RAISERROR(14262, -1, -1, '@name', @name)
            RETURN(1) -- Failure
        END
    END
    RETURN (0)
END
GO

-- Create one schedule that starts when SQL Agent starts so that all continuous
-- collection jobs can attach to it, the schedule has to be accessible to the internal dc user that owns agent objects
-- EXECUTE AS USER = 'MS_DataCollectorInternalUser';

IF (NOT EXISTS (SELECT * FROM sysschedules_localserver_view WHERE name = N'RunAsSQLAgentServiceStartSchedule'))
BEGIN
    EXEC dbo.sp_add_schedule
        @schedule_name = N'RunAsSQLAgentServiceStartSchedule',
        @freq_type = 0x40,            -- FREQTYPE_AUTOSTART
        @freq_interval = 1
END

-- REVERT;
GO


IF (NOT OBJECT_ID('[dbo].[sp_syscollector_create_jobs]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_create_jobs]...'
    DROP PROCEDURE [dbo].[sp_syscollector_create_jobs]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_create_jobs]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_create_jobs]
    @collection_set_id        int,
    @collection_set_uid        uniqueidentifier,
    @collection_set_name    sysname,
    @proxy_id                int = NULL,
    @schedule_id            int = NULL,
    @collection_mode        smallint,
    @collection_job_id        uniqueidentifier OUTPUT,
    @upload_job_id            uniqueidentifier OUTPUT
AS
BEGIN
    SET NOCOUNT ON

    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_syscollector_create_jobs
    ELSE
        BEGIN TRANSACTION

    BEGIN TRY

    -- job step names and commands shared between collection modes
    DECLARE @collection_set_id_as_char nvarchar(36)

    DECLARE @collection_step_command nvarchar(512)
    DECLARE @upload_step_command nvarchar(512)
    DECLARE @autostop_step_command nvarchar(512)
    DECLARE @purge_step_command nvarchar(1024)

    DECLARE @collection_step_name sysname
    DECLARE @upload_step_name sysname
    DECLARE @autostop_step_name sysname
    DECLARE @purge_step_name sysname

    DECLARE @job_name sysname
    DECLARE @job_id uniqueidentifier        
    DECLARE @description nvarchar(512)

    IF(@collection_set_id IS NOT NULL)
    BEGIN
        SET @collection_set_id_as_char = CONVERT(NVARCHAR(36), @collection_set_id)
        SET @collection_step_command = 
            N'dcexec -c -s ' + @collection_set_id_as_char + N' -i "$(ESCAPE_DQUOTE(MACH))\$(ESCAPE_DQUOTE(INST))"' + 
            N' -m ' + CONVERT(NVARCHAR(36), @collection_mode);
        SET @upload_step_command = 
            N'dcexec -u -s ' + @collection_set_id_as_char + N' -i "$(ESCAPE_DQUOTE(MACH))\$(ESCAPE_DQUOTE(INST))"';
        SET @autostop_step_command =
            N'exec dbo.sp_syscollector_stop_collection_set @collection_set_id=' + @collection_set_id_as_char 
            + N', @stop_collection_job = 0';  -- do not stop the collection job, otherwise you will abort yourself!
        SET @purge_step_command = 
            N'
            EXEC [dbo].[sp_syscollector_purge_collection_logs]
            '
    END

    -- verify that the proxy_id exists
    IF (@proxy_id IS NOT NULL)
    BEGIN
        DECLARE @proxy_name sysname
        DECLARE @retVal int
        -- this will throw an error of proxy_id does not exist
        EXEC @retVal = msdb.dbo.sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT
        IF (@retVal <> 0)
            RETURN (0)
    END

    -- add jobs, job steps and attach schedule separately for different modes
    IF (@collection_mode = 1)    -- non-cached mode
    BEGIN
        -- create 1 job and 2 steps, first for collection & upload, second for log purging
        SET @job_name = N'collection_set_' + @collection_set_id_as_char + '_noncached_collect_and_upload'
        SET @collection_step_name = @job_name + '_collect'
        SET @upload_step_name = @job_name + '_upload'
        SET @purge_step_name = @job_name + '_purge_logs'
        SET @description = N'Data Collector job for collection set ' + QUOTENAME(@collection_set_name)

        -- add agent job and job server
        EXEC dbo.sp_add_job 
            @job_name        = @job_name,
            @category_id    = 8, -- N'Data Collector'
            @enabled        = 0,
            @description    = @description,
            @job_id            = @job_id OUTPUT
        
        EXEC dbo.sp_add_jobserver
            @job_id            = @job_id,
            @server_name    = N'(local)'

        -- add both collect and upload job steps to the same job
        EXEC dbo.sp_add_jobstep
            @job_id                = @job_id,
            @step_name            = @collection_step_name,
            @subsystem            = 'CMDEXEC',
            @command            = @collection_step_command,
            @on_success_action    =  3,        -- go to the next job step (purge the log)
            @on_fail_action        =  2,        -- quit with failure
            @proxy_id            = @proxy_id,
            @flags              = 16        -- Write log to table (append to existing history)

        EXEC dbo.sp_add_jobstep
            @job_id                = @job_id,
            @step_name            = @purge_step_name,
            @subsystem            = 'TSQL',
            @database_name        = 'msdb',
            @command            = @purge_step_command,
            @on_success_action    =  3,        -- go to the next job step (upload)
            @on_fail_action        =  3,        -- go to the next job step (upload)
            @proxy_id            = NULL,
            @flags                = 16        -- write log to table (append to existing history)

        EXEC dbo.sp_add_jobstep
            @job_id                = @job_id,
            @step_name            = @upload_step_name,
            @subsystem            = 'CMDEXEC',
            @command            = @upload_step_command,
            @on_success_action    =  1,        -- quit with success
            @on_fail_action        =  2,        -- quit with failure
            @proxy_id            = @proxy_id,
            @flags              = 16        -- Write log to table (append to existing history)

        IF @schedule_id IS NOT NULL
        BEGIN
            -- attach the schedule
            EXEC dbo.sp_attach_schedule
                @job_id            = @job_id,
                @schedule_id    = @schedule_id
        END

        SET @upload_job_id = @job_id
        SET @collection_job_id = @job_id
    END

    IF (@collection_mode = 0) -- cached mode
    BEGIN
        -- create 2 jobs for collect and upload
        -- add to collect job an extra step that autostops collection called in case collect job fails
        DECLARE @upload_job_name        sysname
        DECLARE @collection_job_name    sysname
        SET @upload_job_name = N'collection_set_' + @collection_set_id_as_char + '_upload'
        SET @collection_job_name = N'collection_set_' + @collection_set_id_as_char + '_collection'

        SET @collection_step_name = @collection_job_name + '_collect'
        SET @autostop_step_name = @collection_job_name + '_autostop'
        SET @upload_step_name = @upload_job_name + '_upload'
        SET @purge_step_name = @upload_job_name + '_purge_logs'

        -- modify the collection step to pass in the stop event name passed in by agent
        SET @collection_step_command = @collection_step_command + N' -e $' + N'(ESCAPE_NONE(' + N'STOPEVENT))'

        -- add agent job and job server
        EXEC dbo.sp_add_job 
            @job_name        = @upload_job_name,
            @category_id    = 8, -- N'Data Collector'
            @enabled        = 0,
            @job_id            = @upload_job_id OUTPUT
        
        EXEC dbo.sp_add_jobserver
            @job_id            = @upload_job_id,
            @server_name    = N'(local)'

        EXEC dbo.sp_add_job 
            @job_name        = @collection_job_name,
            @category_id    = 8, -- N'Data Collector'
            @enabled        = 0,
            @job_id            = @collection_job_id OUTPUT

        EXEC dbo.sp_add_jobserver
            @job_id            = @collection_job_id,
            @server_name    = N'(local)'

        -- add upload job step to upload job and collection job
        -- step to collection job separately
        EXEC dbo.sp_add_jobstep
            @job_id                = @upload_job_id,
            @step_name            = @purge_step_name,
            @subsystem            = 'TSQL',
            @database_name        = 'msdb',
            @command            = @purge_step_command,
            @on_success_action    =  3,        -- go to next job step (upload)
            @on_fail_action        =  3,        -- go to next job step (upload)
            @proxy_id            = NULL,
            @flags                = 16        -- write log to table (append to existing history)

        EXEC dbo.sp_add_jobstep
            @job_id                = @upload_job_id,
            @step_name            = @upload_step_name,
            @subsystem            = 'CMDEXEC',
            @command            = @upload_step_command,
            @on_success_action    =  1,        -- quit with success
            @on_fail_action        =  2,        -- quit with failure
            @proxy_id            = @proxy_id

        EXEC dbo.sp_add_jobstep
            @job_id                = @collection_job_id,
            @step_name            = @collection_step_name,
            @subsystem            = 'CMDEXEC',
            @command            = @collection_step_command,
            @on_success_action    =  1,        -- quit with success
            @on_fail_action        =  3,        -- go to next job step (auto-stop)
            @proxy_id            = @proxy_id,
            @flags              = 80 -- 16 (write log to table (append to existing history) 
                                     -- + 64 (create a stop event and pass it to the command line)

        EXEC dbo.sp_add_jobstep
            @job_id                = @collection_job_id,
            @step_name            = @autostop_step_name,
            @subsystem            = 'TSQL',
            @database_name        = 'msdb',
            @command            = @autostop_step_command,
            @on_success_action    =  2,        -- quit with failure
            @on_fail_action        =  2,        -- quit with failure
            @proxy_id            = NULL,
            @flags                = 16        -- write log to table (append to existing history)

        -- attach the input schedule to the upload job
        EXEC dbo.sp_attach_schedule
            @job_id            = @upload_job_id,
            @schedule_id    = @schedule_id

        -- attach the RunAsSQLAgentServiceStartSchedule to the collection job
        EXEC dbo.sp_attach_schedule
            @job_id            = @collection_job_id,
            @schedule_name    = N'RunAsSQLAgentServiceStartSchedule'
    END

    IF (@TranCounter = 0)
        COMMIT TRANSACTION
    RETURN (0)

    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_syscollector_create_jobs

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);

        RETURN (1)
    END CATCH
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_create_collection_set]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_create_collection_set]...'
    DROP PROCEDURE [dbo].[sp_syscollector_create_collection_set]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_create_collection_set]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_create_collection_set]
    @name                        sysname,
    @target                        nvarchar(128) = NULL,
    @collection_mode            smallint = 0,    -- 0: cached, 1: non-cached
    @days_until_expiration      smallint = 730, -- two years
    @proxy_id                   int = NULL,     -- mutual exclusive; must specify either proxy_id or proxy_name to identify the proxy
    @proxy_name                    sysname = NULL,
    @schedule_uid               uniqueidentifier = NULL, 
    @schedule_name              sysname = NULL, -- mutual exclusive; must specify either schedule_uid or schedule_name to identify the schedule
    @logging_level                smallint = 1,
    @description                nvarchar(4000) = NULL,
    @collection_set_id            int OUTPUT,
    @collection_set_uid            uniqueidentifier = NULL OUTPUT
WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser'
AS
BEGIN
    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_create_collection_set
    ELSE
        BEGIN TRANSACTION

    BEGIN TRY

    -- Security check (role membership)
    EXECUTE AS CALLER;
    IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        REVERT;
        RAISERROR(14677, -1, -1, 'dc_admin')
        RETURN (1)
    END
    REVERT;

    -- Remove any leading/trailing spaces from parameters
    SET @name                    = NULLIF(LTRIM(RTRIM(@name)), N'')
    SET @proxy_name                = NULLIF(LTRIM(RTRIM(@proxy_name)), N'')
    SET @schedule_name            = NULLIF(LTRIM(RTRIM(@schedule_name)), N'')
    SET @target                    = NULLIF(LTRIM(RTRIM(@target)), N'')
    SET @description            = LTRIM(RTRIM(@description))

    IF (@name IS NULL)
    BEGIN
        RAISERROR(21263, -1, -1, '@name')
        RETURN (1)
    END

    -- can't have both name and uid for the schedule
    IF (@schedule_uid IS NOT NULL) AND (@schedule_name IS NOT NULL)
    BEGIN
        RAISERROR(14373, -1, -1, '@schedule_uid', '@schedule_name')
        RETURN (1)
    END

    -- Execute the check for the schedule as caller to ensure only schedules owned by caller can be attached
    EXECUTE AS CALLER;

    DECLARE @schedule_id int
    IF (@schedule_uid IS NOT NULL)
    BEGIN
        SElECT @schedule_id = schedule_id FROM sysschedules_localserver_view WHERE @schedule_uid = schedule_uid
    
        IF (@schedule_id IS NULL)
        BEGIN
            DECLARE @schedule_uid_as_char VARCHAR(36)
            SELECT @schedule_uid_as_char = CONVERT(VARCHAR(36), @schedule_uid)
            REVERT;
            RAISERROR(14262, -1, -1, N'@schedule_uid', @schedule_uid_as_char)
            RETURN (1)
        END
    END
    ELSE IF (@schedule_name IS NOT NULL)
    BEGIN
        SELECT @schedule_id = schedule_id, @schedule_uid = schedule_uid FROM sysschedules_localserver_view WHERE name = @schedule_name 
    
        IF (@schedule_id IS NULL)
        BEGIN
            REVERT;
            RAISERROR(14262, -1, -1, N'@schedule_name', @schedule_name)
            RETURN (1)
        END
    END

    REVERT;

    -- if collection_mode is cached, make sure schedule_id is not null
    IF    (@collection_mode = 0 AND @schedule_id IS NULL)
    BEGIN
        RAISERROR(14683, -1, -1)    
        RETURN (1)
    END    

    IF (@proxy_id IS NOT NULL) OR (@proxy_name IS NOT NULL) 
    BEGIN
        -- check if the proxy exists
        EXEC sp_verify_proxy_identifiers '@proxy_name',
                                         '@proxy_id',
                                         @proxy_name OUTPUT,
                                         @proxy_id   OUTPUT

        -- check if proxy_id is granted to dc_admin
        IF (@proxy_id NOT IN (SELECT proxy_id 
                              FROM sysproxylogin 
                              WHERE sid = USER_SID(USER_ID('dc_admin'))
                              )
            )
        BEGIN
            RAISERROR(14523, -1, -1, N'dc_admin', @proxy_name)
            RETURN (1)
        END
    END

    IF (@collection_mode < 0 OR @collection_mode > 1)
    BEGIN
        RAISERROR(14266, -1, -1, '@collection_mode', '0, 1')
        RETURN (1)
    END

    IF (@logging_level < 0 OR @logging_level > 2)
    BEGIN
        RAISERROR(14266, -1, -1, '@logging_level', '0, 1, or 2')
        RETURN (1)
    END

    IF (@collection_set_uid IS NULL)
    BEGIN
        SET @collection_set_uid = NEWID()
    END

    IF (@days_until_expiration < 0)
    BEGIN
        RAISERROR(14266, -1, -1, '@days_until_expiration', '>= 0')
        RETURN (1)
    END

    INSERT INTO [dbo].[syscollector_collection_sets_internal]
    (
        collection_set_uid,
        schedule_uid,
        name,
        target,
        is_running,
        proxy_id,
        is_system,
        upload_job_id,
        collection_job_id,
        collection_mode,
        logging_level,
        days_until_expiration,
        description
    )
    VALUES
    (
        @collection_set_uid,
        @schedule_uid,
        @name,
        @target,
        0,
        @proxy_id,
        0,
        NULL,
        NULL,
        @collection_mode,
        @logging_level,
        @days_until_expiration,
        @description
    )

    SET @collection_set_id = SCOPE_IDENTITY()

    IF (@collection_set_id IS NULL)
    BEGIN
        DECLARE @collection_set_id_as_char VARCHAR(36)
        SELECT @collection_set_id_as_char = CONVERT(VARCHAR(36), @collection_set_id)
        RAISERROR(14262, -1, -1, '@collection_set_id', @collection_set_id_as_char)
        RETURN (1)
    END

    IF (@TranCounter = 0)
        COMMIT TRANSACTION
    RETURN (0)

    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_create_collection_set

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');
        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);

        RETURN (1)        
    END CATCH
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_update_job_proxy]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_update_job_proxy]...'
    DROP PROCEDURE [dbo].[sp_syscollector_update_job_proxy]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_update_job_proxy]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_update_job_proxy]
    @job_id                    uniqueidentifier,
    @proxy_id                int = NULL,
    @proxy_name                sysname = NULL
AS
BEGIN
    -- update the proxy id for the all job steps
    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_syscollector_update_proxy
    ELSE
        BEGIN TRANSACTION
    
    BEGIN TRY
        DECLARE @step_id         INT
        DECLARE cursor_job_steps CURSOR FOR
            SELECT step_id FROM dbo.sysjobsteps WHERE job_id = @job_id AND subsystem = N'CMDEXEC'

        OPEN cursor_job_steps
        FETCH NEXT FROM cursor_job_steps INTO @step_id

        WHILE @@FETCH_STATUS = 0
        BEGIN
            EXEC dbo.sp_update_jobstep
                @job_id = @job_id,
                @step_id = @step_id,
                @proxy_id = @proxy_id,
                @proxy_name = @proxy_name

            FETCH NEXT FROM cursor_job_steps INTO @step_id
        END

        CLOSE      cursor_job_steps
        DEALLOCATE cursor_job_steps

        IF (@TranCounter = 0)
            COMMIT TRANSACTION
        RETURN (0)
    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_syscollector_update_proxy

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');
        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);

        RETURN (1)        
    END CATCH
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_update_collection_set_internal]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_update_collection_set_internal]...'
    DROP PROCEDURE [dbo].[sp_syscollector_update_collection_set_internal]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_update_collection_set_internal]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_update_collection_set_internal]
    @collection_set_id          int,
    @collection_set_uid         uniqueidentifier,
    @name                       sysname,
    @new_name                   sysname,
    @target                     nvarchar(128),
    @collection_mode            smallint,         
    @days_until_expiration      smallint,
    @proxy_id                   int,              
    @proxy_name                 sysname,          
    @schedule_uid               uniqueidentifier, 
    @schedule_name              sysname,          
    @logging_level              smallint,
    @description                nvarchar(4000),   
    @schedule_id                int,
    @old_collection_mode        smallint,
    @old_proxy_id               int,
    @old_collection_job_id      uniqueidentifier,
    @old_upload_job_id          uniqueidentifier    
AS
BEGIN
    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_update_collection_set
    ELSE
        BEGIN TRANSACTION
    BEGIN TRY
        DECLARE @old_upload_schedule_id    int
        DECLARE @old_upload_schedule_uid uniqueidentifier

        SELECT  @old_upload_schedule_id = sv.schedule_id,
                @old_upload_schedule_uid = cs.schedule_uid
        FROM dbo.syscollector_collection_sets cs 
        JOIN sysschedules_localserver_view sv ON (cs.schedule_uid = sv.schedule_uid)
        WHERE collection_set_id = @collection_set_id

        -- update job names, schedule, and collection mode in a transaction to maintain a consistent state in case of failures
        IF (@collection_mode IS NOT NULL AND @collection_mode != @old_collection_mode)
        BEGIN
            IF (@schedule_id IS NULL)
            BEGIN
                -- if no schedules is supplied as a parameter to this update SP, 
                -- we can use the one that is already in the collection set table
                SET @schedule_uid = @old_upload_schedule_uid
                
                SELECT @schedule_id = schedule_id 
                FROM sysschedules_localserver_view 
                WHERE @schedule_uid = schedule_uid
            END

            IF (@schedule_name IS NOT NULL AND @schedule_name = N'')
            BEGIN
                SET @schedule_id = NULL 
            END

            -- make sure there exists a schedule we can use
            IF (@old_collection_mode = 1 AND @schedule_id IS NULL) -- a switch from non-cached to cached mode require a schedule
            BEGIN
                -- no schedules specified in input or collection set table, raise error
                RAISERROR(14683, -1, -1)
                RETURN (1)
            END

            -- Only update the jobs if we have jobs already created. Otherwise the right
            -- jobs will be created when the collection set starts for the first time.
            IF (@old_collection_job_id IS NOT NULL AND @old_upload_job_id IS NOT NULL)
            BEGIN
                -- create new jobs 
                DECLARE @collection_job_id        uniqueidentifier 
                DECLARE @upload_job_id            uniqueidentifier 

                DECLARE @collection_set_name sysname;
                SET @collection_set_name = ISNULL(@new_name, @name);
                EXEC [dbo].[sp_syscollector_create_jobs] 
                    @collection_set_id        = @collection_set_id,
                    @collection_set_uid     = @collection_set_uid,
                    @collection_set_name    = @collection_set_name,
                    @proxy_id                = @proxy_id,
                    @schedule_id            = @schedule_id,
                    @collection_mode        = @collection_mode,
                    @collection_job_id        = @collection_job_id OUTPUT,
                    @upload_job_id            = @upload_job_id OUTPUT

                UPDATE [dbo].[syscollector_collection_sets_internal]
                SET
                    upload_job_id        = @upload_job_id,
                    collection_job_id    = @collection_job_id
                WHERE @collection_set_id = collection_set_id
                
                -- drop old upload and collection jobs
                EXEC dbo.sp_syscollector_delete_jobs 
                    @collection_job_id        = @old_collection_job_id,
                    @upload_job_id            = @old_upload_job_id,
                    @schedule_id            = @old_upload_schedule_id,
                    @collection_mode        = @old_collection_mode
            END
        END
        ELSE -- collection mode unchanged, we do not have to recreate the jobs
        BEGIN
            -- we need to update the proxy id for all job steps
            IF (@old_proxy_id <> @proxy_id) OR (@old_proxy_id IS NULL AND @proxy_id IS NOT NULL)
            BEGIN
                IF (@old_collection_job_id IS NOT NULL)
                BEGIN
                    EXEC dbo.sp_syscollector_update_job_proxy
                        @job_id    = @old_collection_job_id, 
                        @proxy_id  = @proxy_id
                END

                IF (@old_upload_job_id IS NOT NULL)
                BEGIN
                    EXEC dbo.sp_syscollector_update_job_proxy
                        @job_id    = @old_upload_job_id, 
                        @proxy_id  = @proxy_id
                END
            END
            IF (@proxy_name = N'' AND @old_proxy_id IS NOT NULL) 
            BEGIN
                IF (@old_collection_job_id IS NOT NULL)
                BEGIN
                    EXEC dbo.sp_syscollector_update_job_proxy
                        @job_id    = @old_collection_job_id, 
                        @proxy_name = @proxy_name
                END

                IF (@old_upload_job_id IS NOT NULL)
                BEGIN
                    EXEC dbo.sp_syscollector_update_job_proxy
                        @job_id    = @old_upload_job_id, 
                        @proxy_name = @proxy_name
                END
            END

            -- need to update the schedule
            IF (@old_upload_schedule_id <> @schedule_id) OR (@old_upload_schedule_id IS NULL AND @schedule_id IS NOT NULL)
            BEGIN
                -- detach the old schedule 
                IF (@old_upload_job_id IS NOT NULL) AND (@old_upload_schedule_id IS NOT NULL)
                BEGIN
                    EXEC dbo.sp_detach_schedule 
                        @job_id            = @old_upload_job_id,
                        @schedule_id    = @old_upload_schedule_id,
                        @delete_unused_schedule = 0
                END

                -- attach the new schedule
                IF (@old_upload_job_id IS NOT NULL)
                BEGIN
                    EXEC dbo.sp_attach_schedule
                        @job_id            = @old_upload_job_id,
                        @schedule_id    = @schedule_id
                END
            END

            -- special case - remove the existing schedule
            IF (@schedule_name = N'') AND (@old_upload_schedule_id IS NOT NULL)
            BEGIN
                EXEC dbo.sp_detach_schedule 
                    @job_id            = @old_upload_job_id,
                    @schedule_id    = @old_upload_schedule_id,
                    @delete_unused_schedule = 0
            END
        END

        -- after the all operations succeed, update the sollection_sets table
        DECLARE @new_proxy_id int
        SET @new_proxy_id = @proxy_id
        IF (@proxy_name    = N'')    SET @new_proxy_id = NULL

        UPDATE [dbo].[syscollector_collection_sets_internal]
        SET
            name                    = ISNULL(@new_name, name),
            target                    = ISNULL(@target, target),
            proxy_id                = @new_proxy_id,
            collection_mode            = ISNULL(@collection_mode, collection_mode),
            logging_level            = ISNULL(@logging_level, logging_level),
            days_until_expiration   = ISNULL(@days_until_expiration, days_until_expiration)
        WHERE @collection_set_id = collection_set_id

        IF (@schedule_uid IS NOT NULL OR @schedule_name IS NOT NULL)
        BEGIN
            IF (@schedule_name = N'')    SET @schedule_uid = NULL

            UPDATE [dbo].[syscollector_collection_sets_internal]
            SET schedule_uid = @schedule_uid
            WHERE @collection_set_id = collection_set_id
        END

        IF (@description IS NOT NULL)
        BEGIN
            IF (@description = N'')      SET @description = NULL

            UPDATE [dbo].[syscollector_collection_sets_internal]
            SET description = @description
            WHERE @collection_set_id = collection_set_id
        END

        IF (@TranCounter = 0)
        COMMIT TRANSACTION
        RETURN (0)
    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_update_collection_set

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
        
        RETURN (1)
    END CATCH
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_update_collection_set]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_update_collection_set]...'
    DROP PROCEDURE [dbo].[sp_syscollector_update_collection_set]
END
GO

-- Updates a collection set. These are the steps involved
-- 1- Security checks
-- 2- Stop the collection set if currently running (sp_syscollector_stop_collection_set)
-- 3- Perform the actual update transactionally (sp_syscollector_update_collection_set_internal)
-- 4- Restart the collection set if it was running (sp_syscollector_start_collection_set
PRINT 'Creating procedure [dbo].[sp_syscollector_update_collection_set]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_update_collection_set]
    @collection_set_id        int = NULL,
    @name                    sysname = NULL,
    @new_name                sysname = NULL,
    @target                    nvarchar(128) = NULL,
    @collection_mode        smallint = NULL,         -- 0: cached, 1: non-cached
    @days_until_expiration  smallint = NULL,
    @proxy_id               int = NULL,              -- mutual exclusive; must specify either proxy_id or proxy_name to identify the proxy
    @proxy_name             sysname = NULL,          -- @proxy_name = N'' is a special case to allow change of an existing proxy with NULL
    @schedule_uid           uniqueidentifier = NULL, -- mutual exclusive; must specify either schedule_uid or schedule_name to identify the schedule
    @schedule_name          sysname = NULL,          -- @schedule_name = N'' is a special case to allow change of an existing schedule with NULL
    @logging_level            smallint = NULL,
    @description            nvarchar(4000) = NULL   -- @description = N'' is a special case to allow change of an existing description with NULL
WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser'
AS
BEGIN
    -- Security checks will be performed against caller's security context
    EXECUTE AS CALLER;

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        REVERT;
        RAISERROR(14677, -1, -1, 'dc_operator')
        RETURN (1)
    END

    -- Security checks (restrict functionality for non-dc_admin-s)
    IF (((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1)) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
        AND (@new_name IS NOT NULL))
    BEGIN
        REVERT;
        RAISERROR(14676, -1, -1, '@new_name', 'dc_admin')
        RETURN (1)
    END

    IF (((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1)) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
        AND (@target IS NOT NULL))
    BEGIN
        REVERT;
        RAISERROR(14676, -1, -1, '@target', 'dc_admin')
        RETURN (1)
    END

    IF (((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1)) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
        AND (@proxy_id IS NOT NULL))
    BEGIN
        REVERT;
        RAISERROR(14676, -1, -1, '@proxy_id', 'dc_admin')
        RETURN (1)
    END

    IF (((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1)) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
        AND (@collection_mode IS NOT NULL))
    BEGIN
        REVERT;
        RAISERROR(14676, -1, -1, '@collection_mode', 'dc_admin')
        RETURN (1)
    END

    IF (((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1)) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
        AND (@description IS NOT NULL))
    BEGIN
        REVERT;
        RAISERROR(14676, -1, -1, '@description', 'dc_admin')
        RETURN (1)
    END

    IF (((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1)) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
        AND (@days_until_expiration IS NOT NULL))
    BEGIN
        REVERT;
        RAISERROR(14676, -1, -1, '@days_until_expiration', 'dc_admin')
        RETURN (1) -- Failure
    END

    -- Security checks done, reverting now to internal data collector user security context
    REVERT;

    -- check for inconsistencies/errors in the parameters
    DECLARE @retVal int
    EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT
    IF (@retVal <> 0)
        RETURN (1)

    IF (@collection_mode IS NOT NULL AND (@collection_mode < 0 OR @collection_mode > 1))
    BEGIN
        RAISERROR(14266, -1, -1, '@collection_mode', '0, 1')
        RETURN (1)
    END

    IF (@logging_level IS NOT NULL AND (@logging_level < 0 OR @logging_level > 2))
    BEGIN
        RAISERROR(14266, -1, -1, '@logging_level', '0, 1, or 2')
        RETURN(1)
    END

    IF (LEN(@new_name) = 0)
    BEGIN
      RAISERROR(21263, -1, -1, '@new_name')
      RETURN(1) -- Failure
    END    

    -- Remove any leading/trailing spaces from parameters
    SET @target                    = NULLIF(LTRIM(RTRIM(@target)), N'')
    SET @new_name                = NULLIF(LTRIM(RTRIM(@new_name)), N'')
    SET @description            = LTRIM(RTRIM(@description))
    
    DECLARE @is_system                    bit
    DECLARE @is_running                    bit
    DECLARE @collection_set_uid            uniqueidentifier
    DECLARE @old_collection_mode        smallint
    DECLARE @old_upload_job_id            uniqueidentifier
    DECLARE @old_collection_job_id        uniqueidentifier
    DECLARE @old_proxy_id                int

    SELECT    @is_running = is_running,
            @is_system = is_system,
            @collection_set_uid = collection_set_uid,
            @old_collection_mode = collection_mode,
            @old_collection_job_id = collection_job_id, 
            @old_upload_job_id = upload_job_id,
            @old_proxy_id = proxy_id
    FROM dbo.syscollector_collection_sets
    WHERE collection_set_id = @collection_set_id

    IF (@is_system = 1 AND (
            @new_name IS NOT NULL OR 
            @description IS NOT NULL))
    BEGIN
        -- cannot update, delete, or add new collection items to a system collection set
        RAISERROR(14696, -1, -1);
        RETURN (1)
    END
    
    IF (@proxy_id IS NOT NULL) OR (@proxy_name IS NOT NULL AND @proxy_name <> N'')
    BEGIN
        -- verify the proxy exists
        EXEC sp_verify_proxy_identifiers '@proxy_name',
                                         '@proxy_id',
                                         @proxy_name OUTPUT,
                                         @proxy_id   OUTPUT

        -- check if proxy_id is granted to dc_admin
        IF (@proxy_id NOT IN (SELECT proxy_id 
                              FROM sysproxylogin 
                              WHERE sid = USER_SID(USER_ID('dc_admin'))
                              )
            )
        BEGIN
            RAISERROR(14523, -1, -1, N'dc_admin', @proxy_name)
            RETURN (1)
        END
    END
    ELSE -- if no proxy_id provided, get the existing proxy_id, might need it later to create new jobs
    BEGIN
        SET @proxy_id = @old_proxy_id
    END

    -- can't have both uid and name passed for the schedule
    IF (@schedule_uid IS NOT NULL) AND (@schedule_name IS NOT NULL AND @schedule_name <> N'')
    BEGIN
        RAISERROR(14373, -1, -1, '@schedule_uid', '@schedule_name')
        RETURN (1)
    END

    -- check if it attempts to remove a schedule when the collection mode is cached
    IF    (@schedule_name = N'' AND @collection_mode = 0)    OR 
        (@collection_mode IS NULL AND @old_collection_mode = 0 AND @schedule_name = N'')
    BEGIN
        RAISERROR(14683, -1, -1)    
        RETURN (1)
    END    

    -- Execute the check for the schedule as caller to ensure only schedules owned by caller can be attached
    EXECUTE AS CALLER;

    DECLARE @schedule_id int
    SET @schedule_id = NULL
    IF (@schedule_uid IS NOT NULL)
    BEGIN
        SElECT @schedule_id = schedule_id FROM sysschedules_localserver_view WHERE @schedule_uid = schedule_uid
    
        IF (@schedule_id IS NULL)
        BEGIN
            DECLARE @schedule_uid_as_char VARCHAR(36)
            SELECT @schedule_uid_as_char = CONVERT(VARCHAR(36), @schedule_uid)
            REVERT;
            RAISERROR(14262, -1, -1, N'@schedule_uid', @schedule_uid_as_char)
            RETURN (1)
        END
    END
    ELSE IF (@schedule_name IS NOT NULL AND @schedule_name <> N'') -- @schedule_name is not null
    BEGIN
        SELECT @schedule_id = schedule_id, @schedule_uid = schedule_uid FROM sysschedules_localserver_view WHERE name = @schedule_name 
    
        IF (@schedule_id IS NULL)
        BEGIN
            REVERT;
            RAISERROR(14262, -1, -1, N'@schedule_name', @schedule_name)
            RETURN (1)
        END
    END

    REVERT;
    
    -- Stop the collection set if it is currently running
    IF (@is_running = 1 AND (
            @new_name IS NOT NULL OR 
            @target IS NOT NULL OR 
            @proxy_id IS NOT NULL OR 
            @logging_level IS NOT NULL OR 
            @collection_mode IS NOT NULL))
    BEGIN
        EXEC @retVal = sp_syscollector_stop_collection_set @collection_set_id = @collection_set_id
        IF (@retVal <> 0)
            RETURN (1)
    END

    -- Passed all necessary checks, go ahead with the update
    EXEC @retVal = sp_syscollector_update_collection_set_internal
        @collection_set_id = @collection_set_id,
        @collection_set_uid = @collection_set_uid,
        @name = @name,
        @new_name = @new_name,
        @target = @target,
        @collection_mode = @collection_mode,
        @days_until_expiration = @days_until_expiration,
        @proxy_id = @proxy_id,
        @proxy_name = @proxy_name,
        @schedule_uid = @schedule_uid,
        @schedule_name = @schedule_name,
        @logging_level = @logging_level,
        @description = @description,
        @schedule_id = @schedule_id,
        @old_collection_mode = @old_collection_mode,
        @old_proxy_id = @old_proxy_id,
        @old_collection_job_id = @old_collection_job_id,
        @old_upload_job_id = @old_upload_job_id
            
     IF (@retVal <> 0)
        RETURN (1)
        
     -- Restart the collection set if it has been already running
     IF (@is_running = 1)
     BEGIN
         EXEC @retVal = sp_syscollector_start_collection_set
                            @collection_set_id = @collection_set_id
         IF (@retVal <> 0)
            RETURN (1)
     END
        
     RETURN (0)
END
GO

IF (NOT OBJECT_ID('dbo.sp_syscollector_configure_sql_dumper', 'P') IS NULL)
BEGIN
    DROP PROCEDURE [dbo].[sp_syscollector_configure_sql_dumper]
END
GO

PRINT ''
PRINT 'Creating procedure [dbo].[sp_syscollector_configure_sql_dumper]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_configure_sql_dumper]
    @collection_set_id        int = NULL,
    @name                    sysname = NULL,
    @dump_on_any_error      bit = NULL,                -- configure SQL dumper to dump on any SSIS errors
    @dump_on_codes          nvarchar(max) = NULL    -- configure SQL dumper to dump when we hit one of the specified SSIS errors. Set to N'' to remove the codes.
AS
BEGIN
    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_admin')
        RETURN(1) -- Failure
    END

    DECLARE @retVal int
    EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT
    IF (@retVal <> 0)
        RETURN (1)

    DECLARE @is_running bit
    SELECT    @is_running = is_running
    FROM dbo.syscollector_collection_sets
    WHERE collection_set_id = @collection_set_id
    IF (@is_running = 1)
    BEGIN
        RAISERROR(14711, 0, 1)
    END

    IF (@dump_on_codes = N'')
    BEGIN
        UPDATE [dbo].[syscollector_collection_sets_internal]
        SET dump_on_codes = NULL
        WHERE @collection_set_id = collection_set_id
    END
    ELSE IF (@dump_on_codes IS NOT NULL)
    BEGIN
        UPDATE [msdb].[dbo].[syscollector_collection_sets_internal]
        SET dump_on_codes = @dump_on_codes
        WHERE @collection_set_id = collection_set_id
    END    

    IF (@dump_on_any_error IS NOT NULL)
    BEGIN
        UPDATE [msdb].[dbo].[syscollector_collection_sets_internal]
        SET dump_on_any_error = @dump_on_any_error
        WHERE @collection_set_id = collection_set_id
    END

    RETURN (0)
END
GO

---------------------------------------------------------------
-- Collector type
---------------------------------------------------------------
IF (OBJECT_ID(N'[dbo].[syscollector_collector_types_internal]', 'U') IS NULL)
BEGIN
    PRINT 'Creating table [dbo].[syscollector_collector_types_internal]...'
    CREATE TABLE [dbo].[syscollector_collector_types_internal] (
        collector_type_uid                uniqueidentifier NOT NULL,
        name                            sysname NOT NULL,
        parameter_schema                xml NULL,
        parameter_formatter                xml NULL,
        schema_collection                sysname NULL,
        collection_package_name            sysname NOT NULL,
        collection_package_folderid        uniqueidentifier NOT NULL,
        upload_package_name                sysname NOT NULL,
        upload_package_folderid            uniqueidentifier NOT NULL,
        is_system                        bit default 0 NOT NULL,
        CONSTRAINT [PK_syscollector_collector_types_internal] PRIMARY KEY CLUSTERED (collector_type_uid ASC),
        CONSTRAINT [UQ_syscollector_collection_types_internal_name] UNIQUE (name)
        )
    ALTER TABLE syscollector_collector_types_internal
        ADD CONSTRAINT [FK_syscollector_collector_types_internal_upload_sysssispackages] FOREIGN KEY(upload_package_folderid, upload_package_name)
        REFERENCES sysssispackages (folderid, [name])
    ALTER TABLE syscollector_collector_types_internal
        ADD CONSTRAINT [FK_syscollector_collector_types_internal_collection_sysssispackages] FOREIGN KEY(collection_package_folderid, collection_package_name)
        REFERENCES sysssispackages (folderid, [name])
END
GO

-- [fn_syscollector_get_package_path]
-- This function returns the full path of a SSIS package given the package id
IF (NOT OBJECT_ID('[dbo].[fn_syscollector_get_package_path]', 'FN') IS NULL)
BEGIN
    PRINT 'Dropping function [dbo].[fn_syscollector_get_package_path]...'
    DROP FUNCTION [dbo].[fn_syscollector_get_package_path]
END
GO

PRINT 'Creating function [dbo].[fn_syscollector_get_package_path]...'
GO
CREATE FUNCTION [dbo].[fn_syscollector_get_package_path] 
(
    @package_id uniqueidentifier
)
RETURNS NVARCHAR(4000)
AS
BEGIN
    IF @package_id IS NULL
        RETURN NULL

    DECLARE @package_path nvarchar(4000)
    DECLARE @prevfolderid uniqueidentifier
    DECLARE @folderid uniqueidentifier
    DECLARE @package_name sysname
    SET @package_path = ''

    SELECT @package_name = name, 
            @folderid = folderid 
    FROM dbo.sysssispackages
    WHERE id = @package_id

    WHILE (@folderid != '00000000-0000-0000-0000-000000000000')
    BEGIN
        SET @prevfolderid = @folderid

        DECLARE @foldername sysname
        SELECT @foldername = foldername, 
                @folderid = parentfolderid 
        FROM dbo.sysssispackagefolders
        WHERE folderid = @prevfolderid
        SET @package_path = @foldername + N'\\' + @package_path
    END

    SET @package_path = N'\\' + @package_path + @package_name
    RETURN @package_path
END
GO

IF (NOT OBJECT_ID(N'[dbo].[syscollector_collector_types]', 'V') IS NULL)
BEGIN
    PRINT 'Dropping view [dbo].[syscollector_collector_types]...'
    DROP VIEW [dbo].[syscollector_collector_types]
END
GO

PRINT 'Creating view [dbo].[syscollector_collector_types]...'
GO
CREATE VIEW [dbo].[syscollector_collector_types]
AS
    SELECT 
        t.collector_type_uid,
        t.name,
        t.parameter_schema,
        t.parameter_formatter,
        s1.id AS collection_package_id,
        dbo.fn_syscollector_get_package_path(s1.id) AS collection_package_path,
        s1.name AS collection_package_name,
        s2.id AS upload_package_id,
        dbo.fn_syscollector_get_package_path(s2.id) AS upload_package_path,
        s2.name AS upload_package_name,
        t.is_system
    FROM 
        [dbo].[syscollector_collector_types_internal] AS t,
        sysssispackages s1,
        sysssispackages s2
    WHERE t.collection_package_folderid = s1.folderid
      AND t.collection_package_name = s1.name
      AND t.upload_package_folderid = s2.folderid
      AND t.upload_package_name = s2.name
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_verify_collector_type]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_verify_collector_type]...'
    DROP PROCEDURE [dbo].[sp_syscollector_verify_collector_type]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_verify_collector_type]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_verify_collector_type]
    @collector_type_uid        uniqueidentifier = NULL OUTPUT,
    @name                    sysname = NULL OUTPUT
AS
BEGIN
    IF (@name IS NOT NULL)
    BEGIN
        -- Remove any leading/trailing spaces from parameters
        SET @name                    = NULLIF(LTRIM(RTRIM(@name)), N'')
    END

    IF (@collector_type_uid IS NULL AND @name IS NULL)
    BEGIN
        RAISERROR(14624, -1, -1, '@collector_type_uid, @name')
        RETURN(1)
    END

    IF (@collector_type_uid IS NOT NULL AND @name IS NOT NULL)
    BEGIN
        IF (NOT EXISTS(SELECT *
                        FROM dbo.syscollector_collector_types
                        WHERE collector_type_uid = @collector_type_uid
                        AND name = @name))
        BEGIN
            DECLARE @errMsg NVARCHAR(196)
            SELECT @errMsg = CONVERT(NVARCHAR(36), @collector_type_uid) + ', ' + @name
            RAISERROR(14262, -1, -1, '@collector_type_uid, @name', @errMsg)
            RETURN(1)
        END
    END
    -- Check id
    ELSE IF (@collector_type_uid IS NOT NULL)
    BEGIN
        SELECT @name = name
        FROM dbo.syscollector_collector_types
        WHERE (collector_type_uid = @collector_type_uid)
    
        -- the view would take care of all the permissions issues.
        IF (@name IS NULL) 
        BEGIN
            DECLARE @collector_type_uid_as_char VARCHAR(36)
            SELECT @collector_type_uid_as_char = CONVERT(VARCHAR(36), @collector_type_uid)
            RAISERROR(14262, -1, -1, '@collector_type_uid', @collector_type_uid_as_char)
            RETURN(1) -- Failure
        END
    END
    -- Check name
    ELSE IF (@name IS NOT NULL)
    BEGIN
        -- get the corresponding collector_type_uid (if the collector type exists)
        SELECT @collector_type_uid = collector_type_uid
        FROM dbo.syscollector_collector_types
        WHERE (name = @name)

        -- the view would take care of all the permissions issues.
        IF (@collector_type_uid IS NULL)
        BEGIN
            RAISERROR(14262, -1, -1, '@name', @name)
            RETURN(1) -- Failure
        END
    END
    RETURN (0)
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_create_collector_type]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_create_collector_type]...'
    DROP PROCEDURE [dbo].[sp_syscollector_create_collector_type]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_create_collector_type]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_create_collector_type]
    @collector_type_uid            uniqueidentifier = NULL OUTPUT,
    @name                        sysname,
    @parameter_schema            xml = NULL,
    @parameter_formatter        xml = NULL,
    @collection_package_id        uniqueidentifier,
    @upload_package_id            uniqueidentifier
AS
BEGIN
    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_create_collector_type
    ELSE
        BEGIN TRANSACTION
    BEGIN TRY

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_admin')
        RETURN (1)
    END

    SET @name                = NULLIF(LTRIM(RTRIM(@name)), N'')
    IF (@name IS NULL) 
    BEGIN
        RAISERROR(21263, -1, -1, '@name', @name)
        RETURN (1)
    END

    IF (@collector_type_uid IS NULL) 
    BEGIN
        SET @collector_type_uid = NEWID()
    END
    
    IF (NOT EXISTS(SELECT * from sysssispackages
        WHERE @collection_package_id = id))
    BEGIN
        DECLARE @collection_package_id_as_char VARCHAR(36)
        SELECT @collection_package_id_as_char = CONVERT(VARCHAR(36), @collection_package_id)
        RAISERROR(14262, -1, -1, '@collection_package_id', @collection_package_id_as_char)
        RETURN (1)
    END

    IF (NOT EXISTS(SELECT * from sysssispackages
        WHERE @upload_package_id = id))
    BEGIN
        DECLARE @upload_package_id_as_char VARCHAR(36)
        SELECT @upload_package_id_as_char = CONVERT(VARCHAR(36), @upload_package_id)
        RAISERROR(14262, -1, -1, '@upload_package_id', @upload_package_id_as_char)
        RETURN (1)
    END

    DECLARE @collection_package_name sysname
    DECLARE @collection_package_folderid uniqueidentifier
    DECLARE @upload_package_name sysname
    DECLARE @upload_package_folderid uniqueidentifier    

    SELECT 
        @collection_package_name = name,
        @collection_package_folderid = folderid
    FROM sysssispackages
    WHERE @collection_package_id = id

    SELECT 
        @upload_package_name = name,
        @upload_package_folderid = folderid
    FROM sysssispackages
    WHERE @upload_package_id = id

    DECLARE @schema_collection sysname
    IF (@parameter_schema IS NOT NULL)
    BEGIN
        SET @schema_collection = N'schema_collection_' + @name
        WHILE (EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = @schema_collection))
        BEGIN
            SET @schema_collection = LEFT(@schema_collection, 119) + '_' + RIGHT(STR(FLOOR(RAND() * 100000000)),8)
        END

        DECLARE @retVal int
        DECLARE @sql_string nvarchar(2048)
        DECLARE @param_definition nvarchar(16)
        SET @param_definition = N'@schema xml'
        SET @sql_string = N'CREATE XML SCHEMA COLLECTION ' + QUOTENAME(@schema_collection, '[') + N' AS @schema; '
        SET @sql_string = @sql_string + N'GRANT EXECUTE ON XML SCHEMA COLLECTION::[dbo].' + QUOTENAME(@schema_collection, '[') + N' TO dc_admin; ' 
        SET @sql_string = @sql_string + N'GRANT VIEW DEFINITION ON XML SCHEMA COLLECTION::[dbo].' + QUOTENAME(@schema_collection, '[') + N' TO dc_admin; '

        EXEC sp_executesql @sql_string, @param_definition, @schema = @parameter_schema
    END

    INSERT INTO [dbo].[syscollector_collector_types_internal]
    (
        collector_type_uid,
        name,
        parameter_schema,
        parameter_formatter,
        schema_collection,
        collection_package_name,
        collection_package_folderid,
        upload_package_name,
        upload_package_folderid
    )
    VALUES
    (
        @collector_type_uid,
        @name,
        @parameter_schema,
        @parameter_formatter,
        @schema_collection,
        @collection_package_name,
        @collection_package_folderid,
        @upload_package_name,
        @upload_package_folderid
    )

    IF (@TranCounter = 0)
        COMMIT TRANSACTION
    RETURN (0)
    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_create_collector_type

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);

        RETURN (1)    
    END CATCH
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_update_collector_type]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_update_collector_type]...'
    DROP PROCEDURE [dbo].[sp_syscollector_update_collector_type]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_update_collector_type]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_update_collector_type]
    @collector_type_uid            uniqueidentifier = NULL,
    @name                        sysname = NULL,
    @parameter_schema            xml = NULL,
    @parameter_formatter        xml = NULL,
    @collection_package_id        uniqueidentifier,
    @upload_package_id            uniqueidentifier
AS
BEGIN
    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_update_collector_type
    ELSE
        BEGIN TRANSACTION
    BEGIN TRY

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_admin')
        RETURN (1)
    END

    -- Check the validity of the name/uid pair
    DECLARE @retVal int
    EXEC @retVal = dbo.sp_syscollector_verify_collector_type @collector_type_uid OUTPUT, @name OUTPUT
    IF (@retVal <> 0)
        RETURN (1)
    
    DECLARE @old_parameter_schema       xml
    DECLARE @old_parameter_formatter    xml
    DECLARE @old_collection_package_id  uniqueidentifier
    DECLARE @old_upload_package_id      uniqueidentifier

    SELECT  @old_parameter_schema = parameter_schema,
            @old_parameter_formatter = parameter_formatter,
            @old_collection_package_id = collection_package_id,
            @old_upload_package_id = upload_package_id
    FROM [dbo].[syscollector_collector_types]
    WHERE name = @name
    AND collector_type_uid = @collector_type_uid

    IF (@collection_package_id IS NULL)
    BEGIN
        SET @collection_package_id = @old_collection_package_id
    END
    ELSE IF (NOT EXISTS(SELECT * from sysssispackages
                        WHERE @collection_package_id = id))
    BEGIN
        DECLARE @collection_package_id_as_char VARCHAR(36)
        SELECT @collection_package_id_as_char = CONVERT(VARCHAR(36), @collection_package_id)
        RAISERROR(14262, -1, -1, '@collection_package_id', @collection_package_id_as_char)
        RETURN (1)
    END

    IF (@upload_package_id IS NULL)
    BEGIN
        SET @upload_package_id = @old_upload_package_id
    END
    ELSE IF (NOT EXISTS(SELECT * from sysssispackages
                        WHERE @upload_package_id = id))
    BEGIN
        DECLARE @upload_package_id_as_char VARCHAR(36)
        SELECT @upload_package_id_as_char = CONVERT(VARCHAR(36), @upload_package_id)
        RAISERROR(14262, -1, -1, '@upload_package_id', @upload_package_id_as_char)
        RETURN (1)
    END

    DECLARE @collection_package_name sysname
    DECLARE @collection_package_folderid uniqueidentifier
    DECLARE @upload_package_name sysname
    DECLARE @upload_package_folderid uniqueidentifier    

    SELECT 
        @collection_package_name = name,
        @collection_package_folderid = folderid
    FROM sysssispackages
    WHERE @collection_package_id = id

    SELECT 
        @upload_package_name = name,
        @upload_package_folderid = folderid
    FROM sysssispackages
    WHERE @upload_package_id = id

    DECLARE @schema_collection sysname
    IF (@parameter_schema IS NULL)
    BEGIN
        SET @parameter_schema = @old_parameter_schema
    END
    ELSE
    BEGIN
        SELECT @schema_collection = schema_collection
        FROM [dbo].[syscollector_collector_types_internal]
        WHERE name = @name
        AND collector_type_uid = @collector_type_uid

        -- if a previous xml schema collection existed with the same name, drop it in favor of the new schema
        IF (EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = @schema_collection))
        BEGIN
            DECLARE @sql_drop_schema nvarchar(512)
            SET @sql_drop_schema = N'DROP XML SCHEMA COLLECTION ' + QUOTENAME(@schema_collection)
            EXECUTE sp_executesql @sql_drop_schema
        END

        -- recreate it with the new schema
        DECLARE @sql_create_schema nvarchar(2048)
        DECLARE @param_definition nvarchar(16)
        SET @param_definition = N'@schema xml'
        SET @sql_create_schema = N'CREATE XML SCHEMA COLLECTION ' + QUOTENAME(@schema_collection) + N' AS @schema; '
        SET @sql_create_schema = @sql_create_schema + N'GRANT EXECUTE ON XML SCHEMA COLLECTION::[dbo].' + QUOTENAME(@schema_collection) + N' TO dc_admin; ' 
        SET @sql_create_schema = @sql_create_schema + N'GRANT VIEW DEFINITION ON XML SCHEMA COLLECTION::[dbo].' + QUOTENAME(@schema_collection) + N' TO dc_admin; '
            
        EXEC sp_executesql @sql_create_schema, @param_definition, @schema = @parameter_schema
    END

    UPDATE [dbo].[syscollector_collector_types_internal]
    SET parameter_schema = @parameter_schema,
        parameter_formatter = @parameter_formatter,
        schema_collection = @schema_collection,
        collection_package_name = @collection_package_name,
        collection_package_folderid = @collection_package_folderid,
        upload_package_name = @upload_package_name,
        upload_package_folderid = @upload_package_folderid
    WHERE @collector_type_uid = collector_type_uid
    AND   @name = name

    IF (@TranCounter = 0)
        COMMIT TRANSACTION
    RETURN (0)
    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_update_collector_type

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);

        RETURN (1)    
    END CATCH
END
GO



IF (NOT OBJECT_ID('[dbo].[sp_syscollector_validate_xml]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_validate_xml]...'
    DROP PROCEDURE [dbo].[sp_syscollector_validate_xml]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_validate_xml]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_validate_xml]
    @collector_type_uid        uniqueidentifier = NULL,
    @name                    sysname = NULL,
    @parameters                xml
AS
BEGIN
    DECLARE @retVal int
    EXEC @retVal = dbo.sp_syscollector_verify_collector_type @collector_type_uid OUTPUT, @name OUTPUT
    IF (@retVal <> 0)
        RETURN (1)

    DECLARE @schema_collection sysname
    SET @schema_collection = (SELECT schema_collection 
                                FROM syscollector_collector_types_internal
                                WHERE @collector_type_uid = collector_type_uid)

    IF (@schema_collection IS NULL)
    BEGIN
        RAISERROR(21263, -1, -1, '@schema_collection')
        RETURN (1)
    END

        -- @sql_string should have enough buffer to include 2n+2 max size for QUOTENAME(@schema_collection).
    DECLARE @sql_string nvarchar(328)
    DECLARE @param_definition nvarchar(16)
    SET @param_definition = N'@param xml'
    SET @sql_string = N'DECLARE @validated_xml XML (DOCUMENT ' + QUOTENAME(@schema_collection, '[') + N'); SELECT @validated_xml = @param';
    EXEC @retVal = sp_executesql @sql_string, @param_definition, @param = @parameters

    IF (@retVal <> 0)
    BEGIN
        RETURN (1)
    END
END
GO

---------------------------------------------------------------
-- Collection item
---------------------------------------------------------------
IF (OBJECT_ID(N'[dbo].[syscollector_collection_items_internal]', 'U') IS NULL)
BEGIN
    PRINT 'Creating table [dbo].[syscollector_collection_items_internal]...'
    CREATE TABLE [dbo].[syscollector_collection_items_internal] (
        collection_set_id            int NOT NULL,
        collection_item_id            int IDENTITY NOT NULL,
        collector_type_uid            uniqueidentifier NOT NULL,
        name                        sysname NOT NULL,
        name_id                        int NULL,                        -- sysmessage id of the name of the item (for localizing system collection set)
        frequency                    int NOT NULL,
        parameters                    xml NULL,
        CONSTRAINT [PK_syscollector_collection_items_internal] PRIMARY KEY CLUSTERED (collection_set_id ASC, collection_item_id ASC),
        CONSTRAINT [UQ_syscollector_collection_items_internal_name] UNIQUE (name)
        )
    ALTER TABLE syscollector_collection_items_internal
        ADD CONSTRAINT [FK_syscollector_collection_items_internal_syscollector_collection_sets_internal] FOREIGN KEY(collection_set_id)
        REFERENCES syscollector_collection_sets_internal (collection_set_id) ON DELETE CASCADE
    ALTER TABLE syscollector_collection_items_internal
        ADD CONSTRAINT [FK_syscollector_collection_items_internal_syscollector_collector_types_internal] FOREIGN KEY(collector_type_uid)
        REFERENCES syscollector_collector_types_internal (collector_type_uid) ON DELETE CASCADE
END ELSE BEGIN   
    IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'name_id' AND id = OBJECT_ID(N'[dbo].[syscollector_collection_items_internal]')))
    BEGIN
        PRINT 'Altering table [dbo].[syscollector_collection_items_internal]...'
        ALTER TABLE [dbo].[syscollector_collection_items_internal] ADD name_id int NULL
    END    
END
GO

IF (NOT OBJECT_ID(N'[dbo].[syscollector_collection_items]', 'V') IS NULL)
BEGIN
    PRINT 'Dropping view [dbo].[syscollector_collection_items]...'
    DROP VIEW [dbo].[syscollector_collection_items]
END
GO

PRINT 'Creating view [dbo].[syscollector_collection_items]...'
GO
CREATE VIEW [dbo].[syscollector_collection_items]
AS
    SELECT
        i.collection_set_id,
        i.collection_item_id,
        i.collector_type_uid,
        CASE 
            WHEN i.name_id IS NULL THEN i.name 
            ELSE FORMATMESSAGE(i.name_id)
        END AS name,        
        i.frequency,
        i.parameters
    FROM 
        [dbo].[syscollector_collection_items_internal] i
GO

-- This is a stored procedure of collector_type, but it is created here because it 
-- makes references to the collection item view
IF (NOT OBJECT_ID('[dbo].[sp_syscollector_delete_collector_type]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_collector_type]...'
    DROP PROCEDURE [dbo].[sp_syscollector_delete_collector_type]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_delete_collector_type]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_delete_collector_type]
    @collector_type_uid            uniqueidentifier = NULL,
    @name                        sysname = NULL
AS
BEGIN
    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_delete_collector_type
    ELSE
        BEGIN TRANSACTION
    BEGIN TRY

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_admin')
        RETURN (1)
    END
    
    DECLARE @retVal int
    EXEC @retVal = dbo.sp_syscollector_verify_collector_type @collector_type_uid OUTPUT, @name OUTPUT
    IF (@retVal <> 0)
        RETURN (1)

    IF (EXISTS(SELECT * from dbo.syscollector_collection_items
        WHERE @collector_type_uid = collector_type_uid))
    BEGIN
        RAISERROR(14673, -1, -1, @name)
        RETURN (1)
    END
    
    DECLARE @schema_collection sysname
        -- @sql_string should have enough buffer to include 2n+2 max size for QUOTENAME(@schema_collection).
    DECLARE @sql_string nvarchar(285)
    SET @schema_collection = (SELECT schema_collection 
                                FROM syscollector_collector_types_internal
                                WHERE @collector_type_uid = collector_type_uid)
    SET @sql_string = N'DROP XML SCHEMA COLLECTION ' + QUOTENAME(@schema_collection, '[');
    EXEC sp_executesql @sql_string

    DELETE [dbo].[syscollector_collector_types_internal]
    WHERE collector_type_uid = @collector_type_uid

    IF (@TranCounter = 0)
        COMMIT TRANSACTION
    RETURN (0)
    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_delete_collector_type

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
        
        RETURN (1)
    END CATCH
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_verify_collection_item]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_verify_collection_item]...'
    DROP PROCEDURE [dbo].[sp_syscollector_verify_collection_item]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_verify_collection_item]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_verify_collection_item]
    @collection_item_id        int = NULL OUTPUT,
    @name                    sysname = NULL OUTPUT
AS
BEGIN
    IF (@name IS NOT NULL)
    BEGIN
        -- Remove any leading/trailing spaces from parameters
        SET @name                    = NULLIF(LTRIM(RTRIM(@name)), N'')
    END

    IF (@collection_item_id IS NULL AND @name IS NULL)
    BEGIN
        RAISERROR(14624, -1, -1, '@collection_item_id, @name')
        RETURN(1)
    END

    IF (@collection_item_id IS NOT NULL AND @name IS NOT NULL)
    BEGIN
        IF (NOT EXISTS(SELECT *
                        FROM dbo.syscollector_collection_items
                        WHERE collection_item_id = @collection_item_id
                        AND name = @name))
        BEGIN
            DECLARE @errMsg NVARCHAR(196)
            SELECT @errMsg = CONVERT(NVARCHAR(36), @collection_item_id) + ', ' + @name
            RAISERROR(14262, -1, -1, '@collection_item_id, @name', @errMsg)
            RETURN(1)
        END
    END
    -- Check id
    ELSE IF (@collection_item_id IS NOT NULL)
    BEGIN
        SELECT @name = name
        FROM dbo.syscollector_collection_items
        WHERE (collection_item_id = @collection_item_id)
    
        -- the view would take care of all the permissions issues.
        IF (@name IS NULL) 
        BEGIN
            DECLARE @collection_item_id_as_char VARCHAR(36)
            SELECT @collection_item_id_as_char = CONVERT(VARCHAR(36), @collection_item_id)
            RAISERROR(14262, -1, -1, '@collection_item_id', @collection_item_id_as_char)
            RETURN(1) -- Failure
        END
    END
    -- Check name
    ELSE IF (@name IS NOT NULL)
    BEGIN
        -- get the corresponding collection_item_id (if the collection_item exists)
        SELECT @collection_item_id = collection_item_id
        FROM dbo.syscollector_collection_items
        WHERE (name = @name)

        -- the view would take care of all the permissions issues.
        IF (@collection_item_id IS NULL)
        BEGIN
            RAISERROR(14262, -1, -1, '@name', @name)
            RETURN(1) -- Failure
        END
    END
    RETURN (0)
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_create_collection_item]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_create_collection_item]...'
    DROP PROCEDURE [dbo].[sp_syscollector_create_collection_item]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_create_collection_item]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_create_collection_item]
    @collection_set_id        int,
    @collector_type_uid        uniqueidentifier,
    @name                    sysname,
    @frequency                int = 5,                -- set by default to the minimum frequency
    @parameters                xml = NULL,
    @collection_item_id        int OUTPUT
AS
BEGIN
    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_create_collection_item
    ELSE
        BEGIN TRANSACTION
    BEGIN TRY
        -- Security check (role membership)
        IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
        BEGIN
            RAISERROR(14677, -1, -1, 'dc_admin')
            RETURN (1)
        END

        DECLARE @is_system bit
        SELECT    @is_system = is_system
        FROM dbo.syscollector_collection_sets
        WHERE collection_set_id = @collection_set_id
        
        IF (@is_system = 1)
        BEGIN
            -- cannot update, delete, or add new collection items to a system collection set
            RAISERROR(14696, -1, -1);
            RETURN (1)
        END

        SET @name            = NULLIF(LTRIM(RTRIM(@name)), N'')
        IF (@name IS NULL) 
        BEGIN
            RAISERROR(21263, -1, -1, '@name')
            RETURN (1)
        END
        
        IF (@frequency < 5)
        BEGIN
            DECLARE @frequency_as_char VARCHAR(36)
            SELECT @frequency_as_char = CONVERT(VARCHAR(36), @frequency)
            RAISERROR(21405, 16, -1, @frequency_as_char, '@frequency', 5)
            RETURN (1)
        END

        IF (NOT EXISTS(SELECT * from dbo.syscollector_collector_types
            WHERE @collector_type_uid = collector_type_uid))
        BEGIN
            DECLARE @collector_type_uid_as_char VARCHAR(36)
            SELECT @collector_type_uid_as_char = CONVERT(VARCHAR(36), @collector_type_uid)
            RAISERROR(14262, -1, -1, '@collector_type_uid', @collector_type_uid_as_char)
            RETURN (1)
        END
        
        IF (NOT EXISTS(SELECT * from dbo.syscollector_collection_sets
            WHERE @collection_set_id = collection_set_id))
        BEGIN
            DECLARE @collection_set_id_as_char VARCHAR(36)
            SELECT @collection_set_id_as_char = CONVERT(VARCHAR(36), @collection_set_id)
            RAISERROR(14262, -1, -1, '@collection_set_id', @collection_set_id_as_char)
            RETURN (1)
        END

        DECLARE @is_running bit
        SELECT    @is_running = is_running
        FROM dbo.syscollector_collection_sets
        WHERE collection_set_id = @collection_set_id
        IF (@is_running = 1)
        BEGIN
            RAISERROR(14675, -1, -1, @name)
            RETURN (1)
        END

        IF (@parameters IS NULL)
        BEGIN
            DECLARE @parameter_schema xml
            SELECT @parameter_schema = parameter_schema FROM syscollector_collector_types WHERE collector_type_uid = @collector_type_uid
            IF (@parameter_schema IS NOT NULL)    -- only allows parameters to be null if the collector type has a null schema
            BEGIN
                RAISERROR(21263, -1, -1, '@parameters')
                RETURN (1)
            END
        END
        ELSE IF (LTRIM(RTRIM(CONVERT(nvarchar(max), @parameters))) <> N'') -- don't check if the parameters are empty string
        BEGIN
            EXEC dbo.sp_syscollector_validate_xml @collector_type_uid = @collector_type_uid, @parameters = @parameters
        END

        INSERT INTO [dbo].[syscollector_collection_items_internal]
        (
            collection_set_id,
            collector_type_uid,
            name,
            frequency,
            parameters
        )
        VALUES
        (
            @collection_set_id,
            @collector_type_uid,
            @name,
            @frequency,
            @parameters
        )

        SET @collection_item_id = SCOPE_IDENTITY()

        IF (@collection_item_id IS NULL)
        BEGIN
            DECLARE @collection_item_id_as_char VARCHAR(36)
            SELECT @collection_item_id_as_char = CONVERT(VARCHAR(36), @collection_item_id)
            RAISERROR(14262, -1, -1, '@collection_item_id', @collection_item_id_as_char)
            RETURN (1)
        END

        IF (@TranCounter = 0)
            COMMIT TRANSACTION
        RETURN (0)
    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_create_collection_item

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
        
        RETURN (1)
    END CATCH
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_update_collection_item_internal]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_update_collection_item_internal]...'
    DROP PROCEDURE [dbo].[sp_syscollector_update_collection_item_internal]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_update_collection_item_internal]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_update_collection_item_internal]
    @collection_item_id        int = NULL,
    @name                    sysname = NULL,
    @new_name                sysname = NULL,
    @frequency                int = NULL,
    @parameters                xml = NULL
AS
BEGIN
    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_update_collection_item
    ELSE
        BEGIN TRANSACTION
    BEGIN TRY
        UPDATE [dbo].[syscollector_collection_items_internal]
        SET
            name                = ISNULL(@new_name, name),
            frequency            = ISNULL(@frequency, frequency),
            parameters            = ISNULL(@parameters, parameters)
        WHERE @collection_item_id = collection_item_id

        IF (@TranCounter = 0)
            COMMIT TRANSACTION
        RETURN (0)
    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_update_collection_item

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
        
        RETURN (1)
    END CATCH
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_update_collection_item]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_update_collection_item]...'
    DROP PROCEDURE [dbo].[sp_syscollector_update_collection_item]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_update_collection_item]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_update_collection_item]
    @collection_item_id        int = NULL,
    @name                    sysname = NULL,
    @new_name                sysname = NULL,
    @frequency                int = NULL,
    @parameters                xml = NULL
AS
BEGIN
    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_operator')
        RETURN(1) -- Failure
    END

    -- Security checks (restrict functionality for non-dc_admin-s)
    IF ((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) 
        AND (@new_name IS NOT NULL))
    BEGIN
        RAISERROR(14676, -1, -1, '@new_name', 'dc_admin')
        RETURN (1) -- Failure
    END
    IF ((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
        AND (@parameters IS NOT NULL))
    BEGIN
        RAISERROR(14676, -1, -1, '@parameters', 'dc_admin')
        RETURN (1) -- Failure
    END

    DECLARE @retVal int
    EXEC @retVal = dbo.sp_syscollector_verify_collection_item @collection_item_id OUTPUT, @name OUTPUT
    IF (@retVal <> 0)
        RETURN (@retVal)

    IF (@frequency < 5)
    BEGIN
        DECLARE @frequency_as_char VARCHAR(36)
        SELECT @frequency_as_char = CONVERT(VARCHAR(36), @frequency)
        RAISERROR(21405, 16, -1, @frequency_as_char, '@frequency', 5)
        RETURN (1)
    END

    IF (LEN(@new_name) = 0)  -- can't rename to an empty string
    BEGIN
      RAISERROR(21263, -1, -1, '@new_name')
      RETURN(1) -- Failure
    END    

    -- Remove any leading/trailing spaces from parameters
    SET @new_name            = LTRIM(RTRIM(@new_name))

    DECLARE @collection_set_name sysname
    DECLARE @is_system              bit
    DECLARE @is_running             bit
    DECLARE @collector_type_uid     uniqueidentifier
    DECLARE @collection_set_id      int
    SELECT @is_running = s.is_running,
           @is_system = s.is_system,
           @collection_set_name = s.name,
           @collector_type_uid = i.collector_type_uid,
           @collection_set_id = s.collection_set_id
    FROM dbo.syscollector_collection_sets s,
         dbo.syscollector_collection_items i
    WHERE s.collection_set_id = i.collection_set_id
    AND i.collection_item_id = @collection_item_id

    IF (@is_system = 1 AND (@new_name IS NOT NULL OR @parameters IS NOT NULL))
    BEGIN
        -- cannot update, delete, or add new collection items to a system collection set
        RAISERROR(14696, -1, -1);
        RETURN (1)
    END

    IF (@parameters IS NOT NULL)
    BEGIN
        EXEC @retVal = dbo.sp_syscollector_validate_xml @collector_type_uid = @collector_type_uid, @parameters = @parameters
        IF (@retVal <> 0)
            RETURN (@retVal)
    END

    -- if the collection item is running, stop it before update
    IF (@is_running = 1)
    BEGIN
        EXEC @retVal = sp_syscollector_stop_collection_set @collection_set_id = @collection_set_id
        IF (@retVal <> 0)
            RETURN(1)
    END

    -- all conditions go, perform the update
    EXEC @retVal = sp_syscollector_update_collection_item_internal     
                            @collection_item_id = @collection_item_id,
                            @name = @name,
                            @new_name = @new_name,
                            @frequency = @frequency,
                            @parameters = @parameters
                        
    -- if you stopped the collection set, restart it
    IF (@is_running = 1)
    BEGIN
        EXEC @retVal = sp_syscollector_start_collection_set @collection_set_id = @collection_set_id
        IF (@retVal <> 0)
            RETURN (1)
    END
    
    RETURN (0)
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_delete_collection_item_internal]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_collection_item_internal]...'
    DROP PROCEDURE [dbo].[sp_syscollector_delete_collection_item_internal]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_delete_collection_item_internal]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_delete_collection_item_internal]
    @collection_item_id         int,
    @name                       sysname
AS
BEGIN
    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_delete_collection_item
    ELSE
        BEGIN TRANSACTION
    BEGIN TRY
        DELETE [dbo].[syscollector_collection_items_internal]
        WHERE collection_item_id = @collection_item_id
          AND name = @name

        IF (@TranCounter = 0)
            COMMIT TRANSACTION
        RETURN (0)
    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_delete_collection_item

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
        
        RETURN (1)
    END CATCH
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_delete_collection_item]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_collection_item]...'
    DROP PROCEDURE [dbo].[sp_syscollector_delete_collection_item]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_delete_collection_item]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_delete_collection_item]
    @collection_item_id        int = NULL,
    @name                    sysname = NULL
AS
BEGIN
    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_admin')
        RETURN(1) -- Failure
    END

    DECLARE @retVal int
    EXEC @retVal = dbo.sp_syscollector_verify_collection_item @collection_item_id OUTPUT, @name OUTPUT
    IF (@retVal <> 0)
        RETURN (1)

    DECLARE @is_system          bit
    DECLARE @is_running         bit
    DECLARE @collection_set_id  int
    SELECT @is_running = s.is_running,
           @is_system = s.is_system,
           @collection_set_id = s.collection_set_id
    FROM dbo.syscollector_collection_sets s,
         dbo.syscollector_collection_items i
    WHERE i.collection_item_id = @collection_item_id
    AND s.collection_set_id = i.collection_set_id

    IF (@is_system = 1)
    BEGIN
        -- cannot update, delete, or add new collection items to a system collection set
        RAISERROR(14696, -1, -1);
        RETURN(1)
    END

    IF (@is_running = 1)
    BEGIN
        -- stop the collection set if it was running
        EXEC @retVal = sp_syscollector_stop_collection_set @collection_set_id = @collection_set_id
        IF (@retVal <> 0)
            RETURN (1)
    END

    -- all checks go, perform delete
    EXEC @retVal = sp_syscollector_delete_collection_item_internal @collection_item_id = @collection_item_id, @name = @name
    IF (@retVal <> 0)
        RETURN (1)
        
    RETURN (0)
END
GO

---------------------------------------------------------------
-- Collection Set runtime procedures
---------------------------------------------------------------

IF (NOT OBJECT_ID('dbo.sp_syscollector_start_collection_set', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_start_collection_set]...'
    DROP PROCEDURE [dbo].[sp_syscollector_start_collection_set]
END
GO

PRINT ''
PRINT 'Creating procedure [dbo].[sp_syscollector_start_collection_set]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_start_collection_set]
    @collection_set_id        int = NULL,
    @name                     sysname = NULL
WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser'
AS
BEGIN
    SET NOCOUNT ON

    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_start_collection_set
    ELSE
        BEGIN TRANSACTION

    BEGIN TRY

    -- Security check (role membership)
    EXECUTE AS CALLER;
    IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        REVERT;
        RAISERROR(14677, -1, -1, 'dc_operator')
        RETURN(1) -- Failure
    END
    REVERT;

    -- check if SQL Server Agent is enabled
    DECLARE @agent_enabled int
    SELECT @agent_enabled = CAST(value_in_use AS int) FROM sys.configurations WHERE name = N'Agent XPs'
    IF @agent_enabled <> 1
    BEGIN
        RAISERROR(14688, -1, -1)
        RETURN (1)
    END

    -- check if MDW is setup
    DECLARE @instance_name sysname
    SELECT @instance_name = CONVERT(sysname,parameter_value)
    FROM [msdb].[dbo].[syscollector_config_store_internal]
    WHERE parameter_name = N'MDWInstance'
    IF (@instance_name IS NULL)
    BEGIN
        RAISERROR(14689, -1, -1)
        RETURN (1)
    END    
    DECLARE @database_name sysname
    SELECT @database_name = CONVERT(sysname,parameter_value)
    FROM [msdb].[dbo].[syscollector_config_store_internal]
    WHERE parameter_name = N'MDWDatabase'
    IF (@database_name IS NULL)
    BEGIN
        RAISERROR(14689, -1, -1)
        RETURN (1)
    END

    -- Verify the input parameters
    DECLARE @retVal int
    EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT
    IF (@retVal <> 0)
        RETURN (1)

    -- Check if the collection set does not have any collection items
    IF NOT EXISTS(
        SELECT i.collection_item_id 
        FROM [dbo].[syscollector_collection_sets] AS s
        INNER JOIN [dbo].[syscollector_collection_items] AS i
            ON(s.collection_set_id = i.collection_set_id)
        WHERE s.collection_set_id = @collection_set_id
    )
    BEGIN
        RAISERROR(14685, 10, -1, @name) -- Raise a warning message
        IF (@TranCounter = 0)
            COMMIT TRANSACTION
        RETURN (0)
    END

    DECLARE @proxy_id int;
    DECLARE @collection_job_id uniqueidentifier
    DECLARE @upload_job_id uniqueidentifier
    DECLARE @schedule_uid uniqueidentifier;

    SELECT 
        @collection_job_id = collection_job_id, 
        @upload_job_id = upload_job_id, 
        @proxy_id = proxy_id,
        @schedule_uid = schedule_uid
    FROM [dbo].[syscollector_collection_sets_internal]
    WHERE collection_set_id = @collection_set_id;

    -- Check if the set does not have a proxy
    IF (@proxy_id IS NULL)
    BEGIN
        -- to start a collection set without a proxy, the caller has to be a sysadmin
        EXECUTE AS CALLER;
            IF (NOT (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1))
            BEGIN
                REVERT;
                RAISERROR(14692, -1, -1, @name)
                RETURN (1)
            END
        REVERT;
    END

    -- Starting a collection set requires a schedule
    IF @schedule_uid IS NULL
    BEGIN
        RAISERROR(14693, -1, -1)
        RETURN (1)
    END

    -- Check if we have jobs created, and if not, create them
    IF (@collection_job_id IS NULL AND @upload_job_id IS NULL)
    BEGIN
        -- Jobs not created yet, go and crete them
        -- We need to get some data from collection_sets table 
        -- before we do that.
        DECLARE @collection_set_uid uniqueidentifier;
        DECLARE @schedule_id int;
        DECLARE @collection_mode int;

        SELECT 
            @collection_set_uid = collection_set_uid,
            @collection_mode = collection_mode
        FROM
            [dbo].[syscollector_collection_sets_internal]
        WHERE
            collection_set_id = @collection_set_id;
        
        -- Sanity check
        -- Make sure the proxy and schedule are still there, someone could have
        -- remove them between when the collection set was created and now.
        IF (@proxy_id IS NOT NULL)
        BEGIN
            DECLARE @proxy_name sysname
            
            -- this will throw if the id does not exist
            EXEC @retVal = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT
            IF (@retVal <> 0)
                RETURN (1)
        END

        SELECT @schedule_id = schedule_id FROM sysschedules_localserver_view WHERE @schedule_uid = schedule_uid
        EXEC @retVal = sp_verify_schedule_identifiers  @name_of_name_parameter = '@schedule_name',
                                                       @name_of_id_parameter   = '@schedule_id',
                                                       @schedule_name          = NULL,
                                                       @schedule_id            = @schedule_id,
                                                       @owner_sid              = NULL,
                                                       @orig_server_id         = NULL 
        IF (@retVal <> 0)
            RETURN (1)

        -- Go add the jobs
        EXEC [dbo].[sp_syscollector_create_jobs]
            @collection_set_id    = @collection_set_id,
            @collection_set_uid = @collection_set_uid,
            @collection_set_name = @name,
            @proxy_id            = @proxy_id,
            @schedule_id        = @schedule_id,
            @collection_mode    = @collection_mode,
            @collection_job_id    = @collection_job_id OUTPUT,
            @upload_job_id        = @upload_job_id OUTPUT

        -- Finally, update the collection_sets table
        UPDATE [dbo].[syscollector_collection_sets_internal]
        SET
            upload_job_id        = @upload_job_id,
            collection_job_id    = @collection_job_id
        WHERE @collection_set_id = collection_set_id
    END

    -- Update the is_running column for this collection set
    -- There is a trigger defined for that table that turns on
    -- the collection and upload jobs in response to that bit
    -- changing.
    UPDATE [dbo].[syscollector_collection_sets_internal]
    SET is_running = 1
    WHERE collection_set_id = @collection_set_id

    IF (@TranCounter = 0)
        COMMIT TRANSACTION
    RETURN (0)

    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_start_collection_set

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');
        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);

        RETURN (1)        
    END CATCH
END
GO


IF (NOT OBJECT_ID('dbo.sp_syscollector_stop_collection_set', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_stop_collection_set]...'
    DROP PROCEDURE [dbo].[sp_syscollector_stop_collection_set]
END
GO

PRINT ''
PRINT 'Creating procedure [dbo].[sp_syscollector_stop_collection_set]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_stop_collection_set]
    @collection_set_id        int = NULL,
    @name                     sysname = NULL,
    @stop_collection_job      bit = 1           -- Do we need to stop the collection job, YES by default
AS
BEGIN
    SET NOCOUNT ON

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_operator')
        RETURN(1) -- Failure
    END

    -- Verify the input parameters
    DECLARE @retVal int
    EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT
    IF (@retVal <> 0)
        RETURN (1)

    IF (@stop_collection_job = 1)
    BEGIN
        DECLARE @collection_mode INT
        DECLARE @collection_job_id UNIQUEIDENTIFIER

        SELECT  @collection_mode = collection_mode, @collection_job_id = collection_job_id
        FROM    dbo.syscollector_collection_sets
        WHERE   collection_set_id = @collection_set_id
       
        DECLARE @is_collection_job_running INT
        EXECUTE [dbo].[sp_syscollector_get_collection_set_execution_status]
                @collection_set_id = @collection_set_id,
                @is_collection_running = @is_collection_job_running OUTPUT

        -- Stop the collection job if we are in cached mode, this should signal the runtime to exit
        IF (@is_collection_job_running = 1      -- Collection job is running
            AND @collection_mode = 0
            AND @stop_collection_job = 1)
        BEGIN
            EXEC sp_stop_job @job_id = @collection_job_id
        END
    END


    -- Update the is_running column for this collection set
    -- There is a trigger defined for that table that turns off
    -- the collection and uplaod jobs in response to that bit
    -- changing.
    UPDATE [dbo].[syscollector_collection_sets_internal]
    SET is_running = 0
    WHERE collection_set_id = @collection_set_id

    RETURN (0)
END
GO


IF (NOT OBJECT_ID('[dbo].[sp_syscollector_get_collection_set_execution_status]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_get_collection_set_execution_status]...'
    DROP PROCEDURE [dbo].[sp_syscollector_get_collection_set_execution_status]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_get_collection_set_execution_status]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_get_collection_set_execution_status]
    @collection_set_id            int,
    @is_running                    int = NULL OUTPUT,
    @is_collection_running        int = NULL OUTPUT,
    @collection_job_state        int = NULL OUTPUT,
    @is_upload_running            int = NULL OUTPUT,
    @upload_job_state            int = NULL OUTPUT
WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser'
AS
BEGIN
    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_get_execution_status
    ELSE
        BEGIN TRANSACTION

    BEGIN TRY

    DECLARE @xp_results TABLE (job_id             UNIQUEIDENTIFIER NOT NULL,
                            last_run_date         INT              NOT NULL,
                            last_run_time         INT              NOT NULL,
                            next_run_date         INT              NOT NULL,
                            next_run_time         INT              NOT NULL,
                            next_run_schedule_id  INT              NOT NULL,
                            requested_to_run      INT              NOT NULL, -- BOOL
                            request_source        INT              NOT NULL,
                            request_source_id     sysname          COLLATE database_default NULL,
                            running               INT              NOT NULL, -- BOOL
                            current_step          INT              NOT NULL,
                            current_retry_attempt INT              NOT NULL,
                            job_state             INT              NOT NULL)


    DECLARE @is_sysadmin INT
    SELECT @is_sysadmin = ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0)

    DECLARE @collection_job_id UNIQUEIDENTIFIER
    DECLARE @upload_job_id UNIQUEIDENTIFIER
    
    SELECT @collection_job_id = collection_job_id, @upload_job_id = upload_job_id 
    FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id

    DECLARE @agent_enabled int
    SELECT @agent_enabled = CAST(value_in_use AS int) FROM sys.configurations WHERE name = N'Agent XPs'

    IF (@agent_enabled <> 0)
    BEGIN
        INSERT  INTO @xp_results    
        EXECUTE master.dbo.xp_sqlagent_enum_jobs @is_sysadmin, N'', @upload_job_id

        INSERT  INTO @xp_results    
        EXECUTE master.dbo.xp_sqlagent_enum_jobs @is_sysadmin, N'', @collection_job_id
    END

    SELECT @is_running = is_running FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id
    SELECT @is_collection_running = running, @collection_job_state = job_state FROM @xp_results WHERE job_id = @collection_job_id
    SELECT @is_upload_running = running, @upload_job_state = job_state FROM @xp_results WHERE job_id = @upload_job_id

    IF (@TranCounter = 0)
        COMMIT TRANSACTION
    RETURN (0)

    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_get_execution_status

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');
        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);

        RETURN (1)        
    END CATCH
END
GO


IF (NOT OBJECT_ID('dbo.sp_syscollector_upload_collection_set', 'P') IS NULL)
BEGIN
    DROP PROCEDURE [dbo].[sp_syscollector_upload_collection_set]
END
GO

PRINT ''
PRINT 'Creating procedure [dbo].[sp_syscollector_upload_collection_set]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_upload_collection_set]
    @collection_set_id        int = NULL,
    @name                     sysname = NULL
WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser'
AS
BEGIN
    SET NOCOUNT ON

    -- Security check (role membership)
    EXECUTE AS CALLER;
    IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        REVERT;
        RAISERROR(14677, -1, -1, 'dc_operator')
        RETURN(1) -- Failure
    END
    REVERT;

    -- Verify the input parameters
    DECLARE @retVal int
    EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT
    IF (@retVal <> 0)
        RETURN (1)

    -- Make sure the collection set is running and is in the right mode
    DECLARE @is_running bit
    DECLARE @collection_mode smallint

    SELECT 
        @is_running = is_running,
        @collection_mode = collection_mode            
    FROM [dbo].[syscollector_collection_sets]
    WHERE collection_set_id = @collection_set_id

    IF (@collection_mode <> 0)
    BEGIN
        RAISERROR(14694, -1, -1, @name)
        RETURN(1)
    END

    IF (@is_running = 0)
    BEGIN
        RAISERROR(14674, -1, -1, @name)
        RETURN(1)
    END

    -- Make sure the collector is enabled
    EXEC @retVal = [dbo].[sp_syscollector_verify_collector_state] @desired_state = 1
    IF (@retVal <> 0)
        RETURN (1)

    -- Check if the upload job is currently running
    DECLARE @is_upload_job_running INT
    EXECUTE [dbo].[sp_syscollector_get_collection_set_execution_status]
        @collection_set_id = @collection_set_id,
        @is_upload_running = @is_upload_job_running OUTPUT

    IF (@is_upload_job_running = 0)
    BEGIN
        -- Job is not running, we can trigger it now
        DECLARE @job_id UNIQUEIDENTIFIER
        SELECT @job_id = upload_job_id 
            FROM [dbo].[syscollector_collection_sets] 
            WHERE collection_set_id = @collection_set_id

        EXEC @retVal = sp_start_job @job_id = @job_id
        IF (@retVal <> 0)
            RETURN (1)
    END

    RETURN (0)
END
GO


IF (NOT OBJECT_ID('dbo.sp_syscollector_run_collection_set', 'P') IS NULL)
BEGIN
    DROP PROCEDURE [dbo].[sp_syscollector_run_collection_set]
END
GO

PRINT ''
PRINT 'Creating procedure [dbo].[sp_syscollector_run_collection_set]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_run_collection_set]
    @collection_set_id        int = NULL,
    @name                     sysname = NULL
WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser'
AS
BEGIN
    SET NOCOUNT ON

    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_run_collection_set
    ELSE
        BEGIN TRANSACTION

    BEGIN TRY


    -- Security check (role membership)
    EXECUTE AS CALLER;
    IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        REVERT;
        RAISERROR(14677, -1, -1, 'dc_operator')
        RETURN(1) -- Failure
    END
    REVERT;

    -- Verify the input parameters
    DECLARE @retVal int
    EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT
    IF (@retVal <> 0)
        RETURN (1)

    -- Make sure the collection set is in the right mode
    DECLARE @collection_mode smallint
    DECLARE @collection_set_uid uniqueidentifier;

    SELECT 
        @collection_set_uid = collection_set_uid,
        @collection_mode = collection_mode            
    FROM [dbo].[syscollector_collection_sets]
    WHERE collection_set_id = @collection_set_id

    IF (@collection_mode <> 1)
    BEGIN
        RAISERROR(14695, -1, -1, @name)
        RETURN(1)
    END


    -- Make sure the collector is enabled
    EXEC @retVal = [dbo].[sp_syscollector_verify_collector_state] @desired_state = 1
    IF (@retVal <> 0)
        RETURN (1)

    -- check if SQL Server Agent is enabled
    DECLARE @agent_enabled int
    SELECT @agent_enabled = CAST(value_in_use AS int) FROM sys.configurations WHERE name = N'Agent XPs'
    IF @agent_enabled <> 1
    BEGIN
        RAISERROR(14688, -1, -1)
        RETURN (1)
    END

    -- check if MDW is setup
    DECLARE @instance_name sysname
    SELECT @instance_name = CONVERT(sysname,parameter_value)
    FROM [msdb].[dbo].[syscollector_config_store_internal]
    WHERE parameter_name = N'MDWInstance'
    IF (@instance_name IS NULL)
    BEGIN
        RAISERROR(14689, -1, -1)
        RETURN (1)
    END    
    DECLARE @database_name sysname
    SELECT @database_name = CONVERT(sysname,parameter_value)
    FROM [msdb].[dbo].[syscollector_config_store_internal]
    WHERE parameter_name = N'MDWDatabase'
    IF (@database_name IS NULL)
    BEGIN
        RAISERROR(14689, -1, -1)
        RETURN (1)
    END

    -- Make sure the jobs are created for the collection set
    -- Verify the input parameters
    EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT
    IF (@retVal <> 0)
        RETURN (1)

    -- Check if the collection set does not have any collection items
    IF NOT EXISTS(
        SELECT i.collection_item_id 
        FROM [dbo].[syscollector_collection_sets] AS s
        INNER JOIN [dbo].[syscollector_collection_items] AS i
            ON(s.collection_set_id = i.collection_set_id)
        WHERE s.collection_set_id = @collection_set_id
    )
    BEGIN
        RAISERROR(14685, 10, -1, @name) -- Raise a warning message
        IF (@TranCounter = 0)
            COMMIT TRANSACTION
        RETURN (0)
    END

    DECLARE @proxy_id int;
    DECLARE @collection_job_id uniqueidentifier
    DECLARE @upload_job_id uniqueidentifier

    SELECT @collection_job_id = collection_job_id, 
           @upload_job_id = upload_job_id, 
           @proxy_id = proxy_id
    FROM [dbo].[syscollector_collection_sets_internal]
    WHERE collection_set_id = @collection_set_id;

    -- Check if the set does not have a proxy
    IF (@proxy_id IS NULL)
    BEGIN
        -- to start a collection set without a proxy, the caller has to be a sysadmin
        EXECUTE AS CALLER;
            IF (NOT (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1))
            BEGIN
                REVERT;
                RAISERROR(14692, -1, -1, @name)
                RETURN (1)
            END
        REVERT;
    END

    -- Check if we have jobs created, and if not, create them
    DECLARE @jobs_just_created bit
    SET @jobs_just_created = 0  -- False until further notice
    IF (@collection_job_id IS NULL AND @upload_job_id IS NULL)
    BEGIN
        DECLARE @schedule_id int;
        DECLARE @schedule_uid uniqueidentifier;

        SELECT 
            @schedule_uid = schedule_uid
        FROM [dbo].[syscollector_collection_sets_internal]
        WHERE collection_set_id = @collection_set_id;
        
        IF (@schedule_uid IS NOT NULL)
        BEGIN
            SELECT @schedule_id = schedule_id FROM sysschedules_localserver_view WHERE @schedule_uid = schedule_uid
        END

        -- Sanity check
        -- Make sure the proxy and schedule are still there, someone could have
        -- remove them between when the collection set was created and now.
        IF (@proxy_id IS NOT NULL)
        BEGIN
            DECLARE @proxy_name sysname
            
            -- this will throw an error of proxy_id does not exist
            EXEC @retVal = msdb.dbo.sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT
            IF (@retVal <> 0)
                RETURN (0)
        END

        IF (@schedule_uid IS NOT NULL)
        BEGIN
            EXEC @retVal = sp_verify_schedule_identifiers  @name_of_name_parameter = '@schedule_name',
                                                           @name_of_id_parameter   = '@schedule_id',
                                                           @schedule_name          = NULL,
                                                           @schedule_id            = @schedule_id,
                                                           @owner_sid              = NULL,
                                                           @orig_server_id         = NULL 
            IF (@retVal <> 0)
                RETURN (1)
        END

        -- Go add the jobs
        EXEC [dbo].[sp_syscollector_create_jobs]
            @collection_set_id    = @collection_set_id,
            @collection_set_uid = @collection_set_uid,
            @collection_set_name = @name,
            @proxy_id            = @proxy_id,
            @schedule_id        = @schedule_id,
            @collection_mode    = @collection_mode,
            @collection_job_id    = @collection_job_id OUTPUT,
            @upload_job_id        = @upload_job_id OUTPUT

        -- Finally, update the collection_sets table
        UPDATE [dbo].[syscollector_collection_sets_internal]
        SET
            upload_job_id        = @upload_job_id,
            collection_job_id    = @collection_job_id
        WHERE @collection_set_id = collection_set_id

        SET @jobs_just_created = 1  -- Record the fact that we have just created the job here
    END

    IF (@jobs_just_created = 1)
    BEGIN  -- We created the jobs here in this transaction, post a request for agent to start as soon as we commit
        EXEC @retVal = sp_start_job @job_id = @upload_job_id
        IF (@retVal <> 0)
            RETURN (1)
    END
    ELSE   
    BEGIN
        -- The jobs were created previously, we need to guard against it already executing by the schedule
        -- So, check if the job is currently running before asking agent to start it
        DECLARE @is_upload_job_running INT
        EXECUTE [dbo].[sp_syscollector_get_collection_set_execution_status]
            @collection_set_id = @collection_set_id,
            @is_upload_running = @is_upload_job_running OUTPUT

        IF (@is_upload_job_running = 0)
        BEGIN
            -- Job is not running, we can trigger it now
            -- We run only one job because for this (non-cached) mode there is only one job. The same id is stored
            -- as collection and upload job id
            EXEC @retVal = sp_start_job @job_id = @upload_job_id
            IF (@retVal <> 0)
                RETURN (1)
        END
    END

    IF (@TranCounter = 0)
        COMMIT TRANSACTION
    RETURN (0)

    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_run_collection_set

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');
        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);

        RETURN (1)        
    END CATCH
END
GO


PRINT 'Creating trigger [dbo].[syscollector_collection_set_is_running_update_trigger] on [dbo].[syscollector_collection_sets_internal]'
GO
IF NOT OBJECT_ID('dbo.syscollector_collection_set_is_running_update_trigger', 'TR') IS NULL
    DROP TRIGGER [dbo].[syscollector_collection_set_is_running_update_trigger]
GO

CREATE TRIGGER [dbo].[syscollector_collection_set_is_running_update_trigger] on [dbo].[syscollector_collection_sets_internal]
WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser'
FOR UPDATE
AS
BEGIN
    DECLARE @collection_set_id INT
    DECLARE @is_running BIT
    DECLARE @old_is_running BIT
    DECLARE @collection_mode SMALLINT

    IF (NOT UPDATE (is_running))
       RETURN

    DECLARE @collector_enabled int
    SET @collector_enabled = CONVERT(int, (SELECT parameter_value FROM dbo.syscollector_config_store_internal
                            WHERE parameter_name = 'CollectorEnabled'))
    IF @collector_enabled = 0
    BEGIN
        -- flipping the is_running bit has no effect when the collector is disabled
        RAISERROR(14682, 10, -1) -- severity 10 emits a warning
    END
    ELSE
    BEGIN
        DECLARE inserted_cursor CURSOR LOCAL FOR 
            SELECT collection_set_id, is_running, collection_mode
            FROM inserted 

        OPEN inserted_cursor
        FETCH inserted_cursor INTO @collection_set_id, @is_running, @collection_mode
        
        WHILE @@FETCH_STATUS = 0 
        BEGIN
            SELECT @old_is_running = is_running FROM deleted WHERE collection_set_id = @collection_set_id

            -- If there is a change in the state, handle accordingly
            IF (@old_is_running <> @is_running)
            BEGIN
                IF (@is_running = 0)
                BEGIN
                    EXEC dbo.sp_syscollector_stop_collection_set_jobs @collection_set_id = @collection_set_id
                END
                ELSE IF (@is_running = 1)
                BEGIN              
                    EXEC dbo.sp_syscollector_start_collection_set_jobs @collection_set_id = @collection_set_id
                END
            END

            FETCH inserted_cursor INTO @collection_set_id, @is_running, @collection_mode
        END    

        CLOSE inserted_cursor
        DEALLOCATE inserted_cursor
    END
END
GO

PRINT ''
PRINT 'Creating stored procedure syscollector_stop_collection_set_jobs...'

IF (NOT OBJECT_ID('dbo.sp_syscollector_stop_collection_set_jobs', 'P') IS NULL)
    DROP PROCEDURE [dbo].[sp_syscollector_stop_collection_set_jobs]
GO

CREATE PROCEDURE [dbo].[sp_syscollector_stop_collection_set_jobs]
    @collection_set_id    int
AS
BEGIN
    SET NOCOUNT ON

    -- Collection set stopped. Make sure the following happens:
    -- 1. Detach upload schedule
    -- 2. Collection job is stopped
    -- 3. Upload job is kicked once if it is not running now
    -- 4. Collection and upload jobs are disabled
    -- 5. Attach upload schedule
    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_stop_collection_set_jobs
    ELSE
        BEGIN TRANSACTION
    
    BEGIN TRY
        DECLARE @collection_job_id    uniqueidentifier
        DECLARE @upload_job_id        uniqueidentifier
        DECLARE @schedule_uid        uniqueidentifier
        DECLARE @collection_mode    smallint

        SELECT    @collection_job_id = collection_job_id, 
                @upload_job_id = upload_job_id, 
                @collection_mode = collection_mode, 
                @schedule_uid = schedule_uid
        FROM dbo.syscollector_collection_sets
        WHERE collection_set_id = @collection_set_id

        DECLARE @schedule_id int
        IF (@collection_mode != 1)  -- detach schedule for continuous and snapshot modes
        BEGIN
            SELECT @schedule_id = schedule_id from sysschedules_localserver_view WHERE @schedule_uid = schedule_uid
            IF (@schedule_id IS NULL)
            BEGIN
                DECLARE @schedule_uid_as_char VARCHAR(36)
                SELECT @schedule_uid_as_char = CONVERT(VARCHAR(36), @schedule_uid)
                RAISERROR(14262, -1, -1, '@schedule_uid', @schedule_uid_as_char)
                RETURN (1)
            END

            -- Detach schedule
            EXEC dbo.sp_detach_schedule
                @job_id            = @upload_job_id,
                @schedule_id    = @schedule_id,
                @delete_unused_schedule = 0    -- do not delete schedule, might need to attach it back again
        END

        DECLARE @is_upload_job_running INT
        EXECUTE [dbo].[sp_syscollector_get_collection_set_execution_status]
                @collection_set_id = @collection_set_id,
                @is_upload_running = @is_upload_job_running OUTPUT

        -- Upload job (needs to be kicked off for continuous collection mode)
        IF (@is_upload_job_running = 0            -- If the upload job is not already in progress
            AND @collection_mode = 0)           -- don't do it for adhoc or snapshot, they will handle it on their own
        BEGIN
            EXEC sp_start_job @job_id = @upload_job_id, @error_flag = 0
        END

        -- Disable both jobs
        EXEC sp_update_job @job_id = @collection_job_id, @enabled = 0
        EXEC sp_update_job @job_id = @upload_job_id, @enabled = 0

        IF (@collection_mode != 1)    -- attach schedule for continuous and snapshot modes
        BEGIN
            -- Attach schedule
            EXEC dbo.sp_attach_schedule
                @job_id            = @upload_job_id,
                @schedule_id    = @schedule_id
        END

        -- Log the stop of the collection set
        EXEC sp_syscollector_event_oncollectionstop @collection_set_id = @collection_set_id

        IF (@TranCounter = 0)
            COMMIT TRANSACTION
        RETURN (0)
    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_stop_collection_set_jobs

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
        
        RETURN (1)
    END CATCH
END
GO


PRINT ''
PRINT 'Creating stored procedure syscollector_start_collection_set_jobs...'

IF (NOT OBJECT_ID('dbo.sp_syscollector_start_collection_set_jobs', 'P') IS NULL)
    DROP PROCEDURE [dbo].[sp_syscollector_start_collection_set_jobs]
GO

CREATE PROCEDURE [dbo].[sp_syscollector_start_collection_set_jobs]
    @collection_set_id    int
AS
BEGIN
    SET NOCOUNT ON

    -- Collection set started. Make sure the following happens:
    -- 1. Collection and upload jobs are enabled
    -- 2. Collection job is started if it is defined as running continously

    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_start_collection_set_jobs
    ELSE
        BEGIN TRANSACTION

    BEGIN TRY
        
        -- Log the start of the collection set
        DECLARE @log_id bigint
        EXEC sp_syscollector_event_oncollectionstart @collection_set_id = @collection_set_id, @log_id = @log_id OUTPUT

        -- Enable both jobs
        DECLARE @collection_job_id    uniqueidentifier
        DECLARE @upload_job_id        uniqueidentifier
        DECLARE @collection_mode    smallint

        SELECT    @collection_job_id = collection_job_id,
                @upload_job_id = upload_job_id,
                @collection_mode = collection_mode
        FROM dbo.syscollector_collection_sets
        WHERE collection_set_id = @collection_set_id

        EXEC sp_update_job @job_id = @collection_job_id, @enabled = 1
        EXEC sp_update_job @job_id = @upload_job_id, @enabled = 1

        -- Start the collection job if you are in ad hoc or continuous modes
        IF (@collection_mode = 1 OR @collection_mode = 0)
        BEGIN
            EXEC sp_start_job @job_id = @collection_job_id, @error_flag = 0
        END

        IF (@TranCounter = 0)
            COMMIT TRANSACTION
        RETURN (0)
    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_start_collection_set_jobs

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
        
        RETURN (1)
    END CATCH
END
GO


---------------------------------------------------------------
-- Collection Set execution log                                        
---------------------------------------------------------------

IF (OBJECT_ID('dbo.syscollector_execution_log_internal', 'U') IS NULL)
BEGIN
    PRINT ''
    PRINT 'Creating table syscollector_execution_log_internal...'

    CREATE TABLE [dbo].[syscollector_execution_log_internal]
    (
        log_id                  bigint          IDENTITY(1,1) NOT NULL,     -- Unique log entry id. Each execution log gets a new one
        parent_log_id           bigint          NULL,                       -- Id of the parent execution context. NULL for the root node. 
        collection_set_id       int             NOT NULL,                   -- Id of the collection set that owns this entry
        collection_item_id      int             NULL,                       -- Collection item id
        start_time              datetime        NOT NULL,                   -- Collection set or package execution start time
        last_iteration_time     datetime        NULL,                       -- For continously running packages, last time an interation has been started
        finish_time             datetime        NULL,                       -- Collection set or package execution end time
        runtime_execution_mode  smallint        NULL,                       -- 0 - Collection, 1 - Upload
        status                  smallint        NOT NULL,                   -- 0 - Running, 1 - Finished, 2 - Error, 3 - Warning
        operator                nvarchar(128)   NOT NULL,                   -- Name of the user running the collection set or package
        package_id              uniqueidentifier NULL,                      -- Id of a package, NULL for collection sets
        package_execution_id    uniqueidentifier NULL,                      -- Execution Id generated by SSIS for each package execution. Use to join events from sysssislog. NULL for collection sets
        failure_message         nvarchar(2048)   NULL                       -- Message that indicates package failure. NULL if no failure

        CONSTRAINT [PK_syscollector_execution_log] PRIMARY KEY CLUSTERED (log_id ASC),
        CONSTRAINT [FK_syscollector_execution_log_collection_set_id] FOREIGN KEY (collection_set_id) 
            REFERENCES [dbo].[syscollector_collection_sets_internal] (collection_set_id),
    )
END
go

PRINT ''
PRINT 'Creating view syscollector_execution_log...'

IF (NOT OBJECT_ID('dbo.syscollector_execution_log', 'V') IS NULL)
    DROP VIEW [dbo].[syscollector_execution_log]
go

CREATE VIEW [dbo].[syscollector_execution_log] AS
    SELECT 
        log_id, 
        ISNULL(parent_log_id, 0) as parent_log_id, 
        collection_set_id, 
        collection_item_id,
        start_time,
        last_iteration_time,
        finish_time,
        runtime_execution_mode,
        [status],
        operator,
        package_id,
        msdb.dbo.fn_syscollector_get_package_path(package_id) as package_name,
        package_execution_id,
        failure_message
    FROM dbo.syscollector_execution_log_internal;
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_delete_jobs]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_jobs]...'
    DROP PROCEDURE [dbo].[sp_syscollector_delete_jobs]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_delete_jobs]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_delete_jobs]
    @collection_job_id        uniqueidentifier,
    @upload_job_id            uniqueidentifier,
    @schedule_id            int = NULL,
    @collection_mode        smallint
AS
BEGIN
    -- delete the jobs corresponding to the collection set
    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_syscollector_delete_jobs
    ELSE
        BEGIN TRANSACTION
    
    BEGIN TRY

    IF (@collection_mode = 1) -- non-cached mode
    BEGIN
        IF (@upload_job_id IS NOT NULL)
        BEGIN
            -- note, upload job id = collection job id in this mode
            IF (@schedule_id IS NOT NULL)
            BEGIN
                EXEC dbo.sp_detach_schedule
                    @job_id            = @upload_job_id, 
                    @schedule_id    = @schedule_id,
                    @delete_unused_schedule = 0
            END

            EXEC dbo.sp_delete_jobserver
                @job_id            = @upload_job_id,
                @server_name    = N'(local)'

            EXEC dbo.sp_delete_job 
                @job_id            = @upload_job_id
        END
    END
    ELSE -- cached mode
    BEGIN
        -- detach schedules, delete job servers, then delete jobs
        IF (@upload_job_id IS NOT NULL)
        BEGIN
            EXEC dbo.sp_detach_schedule
                @job_id            = @upload_job_id, 
                @schedule_id    = @schedule_id,
                @delete_unused_schedule = 0

            EXEC dbo.sp_delete_jobserver
                @job_id            = @upload_job_id,
                @server_name    = N'(local)'

            EXEC dbo.sp_delete_job 
                @job_id            = @upload_job_id
        END

        IF (@collection_job_id IS NOT NULL)
        BEGIN
            EXEC dbo.sp_detach_schedule
                @job_id            = @collection_job_id, 
                @schedule_name    = N'RunAsSQLAgentServiceStartSchedule',
                @delete_unused_schedule = 0

            EXEC dbo.sp_delete_jobserver
                @job_id            = @collection_job_id,
                @server_name    = N'(local)'

            EXEC dbo.sp_delete_job 
                @job_id            = @collection_job_id
        END
    END

    IF (@TranCounter = 0)
        COMMIT TRANSACTION
    RETURN (0)
    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_syscollector_delete_jobs

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
        
        RETURN (1)
    END CATCH
END
GO


---------------------------------------------------------------
-- Collection Set execution stats                                      
---------------------------------------------------------------

IF (OBJECT_ID('dbo.syscollector_execution_stats_internal', 'U') IS NULL)
BEGIN
    PRINT ''
    PRINT 'Creating table syscollector_execution_stats_internal...'

    CREATE TABLE [dbo].[syscollector_execution_stats_internal]
    (
        log_id                  bigint          NOT NULL,                   -- Log_id of the package that inserts the row
        task_name               nvarchar(128)   NOT NULL,                   -- Name of the task/component in the package that reports the execution stats
        execution_row_count_in  int             NULL,                       -- Number of rows that entered the data flow from its source transformations
        execution_row_count_out int             NULL,                       -- Number of rows that exited the data flow into its destination transformations
        execution_row_count_errors int          NULL,                       -- Number of rows that were re-directed to an error output due to processing errors
        execution_time_ms       int             NULL,                       -- Execution time of the data flow
        log_time                datetime        NOT NULL                    -- Date and time when this entry was logged

        CONSTRAINT [PK_syscollector_execution_stats] PRIMARY KEY CLUSTERED (log_id ASC, task_name ASC, log_time DESC),
        CONSTRAINT [FK_syscollector_execution_stats_log_id] FOREIGN KEY (log_id) REFERENCES [dbo].[syscollector_execution_log_internal] (log_id) ON DELETE CASCADE
    )
END
go

PRINT ''
PRINT 'Creating view syscollector_execution_stats...'

IF (NOT OBJECT_ID('dbo.syscollector_execution_stats', 'V') IS NULL)
    DROP VIEW [dbo].[syscollector_execution_stats]
go

CREATE VIEW [dbo].[syscollector_execution_stats] AS
    SELECT
        log_id,
        task_name,
        execution_row_count_in,
        execution_row_count_out,
        execution_row_count_errors,
        execution_time_ms,
        log_time
    FROM dbo.syscollector_execution_stats_internal
go

---------------------------------------------------------------
-- Logging stored procedures  
---------------------------------------------------------------

PRINT ''
PRINT 'Creating stored procedure sp_syscollector_verify_event_log_id...'

IF (NOT OBJECT_ID('dbo.sp_syscollector_verify_event_log_id', 'P') IS NULL)
    DROP PROCEDURE [dbo].[sp_syscollector_verify_event_log_id]
go

CREATE PROCEDURE [dbo].[sp_syscollector_verify_event_log_id]
    @log_id bigint,
    @allow_collection_set_id bit = 0
AS
BEGIN
    SET NOCOUNT ON

    DECLARE @log_id_as_char VARCHAR(36)

    IF (@log_id IS NULL)
    BEGIN
        RAISERROR(14606, -1, -1, '@log_id')
        RETURN (1)
    END
    ELSE IF @allow_collection_set_id = 0
    BEGIN
        IF (NOT EXISTS (SELECT log_id FROM dbo.syscollector_execution_log WHERE log_id = @log_id AND package_id IS NOT NULL))
        BEGIN
            SELECT @log_id_as_char = CONVERT(VARCHAR(36), @log_id)

            RAISERROR(14262, -1, -1, '@log_id', @log_id_as_char)
            RETURN (1)
        END
    END
    ELSE
    BEGIN
        IF (NOT EXISTS (SELECT log_id FROM dbo.syscollector_execution_log WHERE log_id = @log_id))
        BEGIN
            SELECT @log_id_as_char = CONVERT(VARCHAR(36), @log_id)

            RAISERROR(14262, -1, -1, '@log_id', @log_id_as_char)
            RETURN (1)
        END
    END

    RETURN (0)
END
go

PRINT ''
PRINT 'Creating stored procedure sp_syscollector_event_oncollectionstart...'

IF (NOT OBJECT_ID('dbo.sp_syscollector_event_oncollectionstart', 'P') IS NULL)
    DROP PROCEDURE [dbo].[sp_syscollector_event_oncollectionstart]
go

CREATE PROCEDURE [dbo].[sp_syscollector_event_oncollectionstart]
    @collection_set_id int,
    @operator nvarchar(128) = NULL,
    @log_id bigint OUTPUT
AS
BEGIN
    SET NOCOUNT ON

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_operator')
        RETURN(1) -- Failure
    END

    -- Verify parameters
    --

    -- Check the collection_set_id
    IF (@collection_set_id IS NULL)
    BEGIN
        RAISERROR(14606, -1, -1, '@collection_set_id')
        RETURN (1)
    END
    ELSE IF (NOT EXISTS (SELECT collection_set_id FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id))
    BEGIN
        DECLARE @collection_set_id_as_char VARCHAR(36)
        SELECT @collection_set_id_as_char = CONVERT(VARCHAR(36), @collection_set_id)

        RAISERROR(14262, -1, -1, '@collection_set_id', @collection_set_id_as_char)
        RETURN (1)
    END


    -- Default operator to currently logged in user
    SET @operator = NULLIF(LTRIM(RTRIM(@operator)), '')
    SET @operator = ISNULL(@operator, suser_sname())

    -- Insert the log record
    --
    INSERT INTO dbo.syscollector_execution_log_internal (
        parent_log_id, 
        collection_set_id, 
        start_time,
        last_iteration_time,
        finish_time,
        runtime_execution_mode,
        [status],
        operator,
        package_id,
        package_execution_id,
        failure_message
    ) VALUES (
        NULL,
        @collection_set_id,
        GETDATE(),
        NULL,
        NULL,
        NULL,
        0, -- Running
        @operator,
        NULL,
        NULL,
        NULL
    )

    SET @log_id = SCOPE_IDENTITY()                
    
    RETURN (0)
END
go

PRINT ''
PRINT 'Creating stored procedure sp_syscollector_event_oncollectionstop...'

IF (NOT OBJECT_ID('dbo.sp_syscollector_event_oncollectionstop', 'P') IS NULL)
    DROP PROCEDURE [dbo].[sp_syscollector_event_oncollectionstop]
go

CREATE PROCEDURE [dbo].[sp_syscollector_event_oncollectionstop]
    @collection_set_id int
AS
BEGIN
    SET NOCOUNT ON

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_proxy')
        RETURN(1) -- Failure
    END

    -- Check the collection_set_id
    IF (@collection_set_id IS NULL)
    BEGIN
        RAISERROR(14606, -1, -1, '@collection_set_id')
        RETURN (1)
    END
    ELSE IF (NOT EXISTS (SELECT collection_set_id FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id))
    BEGIN
        DECLARE @collection_set_id_as_char VARCHAR(36)
        SELECT @collection_set_id_as_char = CONVERT(VARCHAR(36), @collection_set_id)

        RAISERROR(14262, -1, -1, '@collection_set_id', @collection_set_id_as_char)
        RETURN (1)
    END

    -- Find the log_id
    -- It will be a log entry for the same collection set, with no parent and not finished
    DECLARE @log_id bigint
    SELECT TOP 1 @log_id = log_id FROM dbo.syscollector_execution_log_internal 
        WHERE collection_set_id = @collection_set_id 
        AND parent_log_id IS NULL
        AND finish_time IS NULL
        ORDER BY start_time DESC

    IF (@log_id IS NULL)
    BEGIN
        -- Raise a warning message
        RAISERROR(14606, 9, -1, '@log_id')
    END
    ELSE
    BEGIN
        -- Mark the log as finished
        UPDATE dbo.syscollector_execution_log_internal SET
            finish_time = GETDATE(),
            [status] = CASE
                WHEN [status] = 0 THEN 1 -- Mark complete if it was running
                ELSE [status]            -- Leave the error status unchanged
            END
        WHERE log_id = @log_id
    END

    RETURN (0)
END
go


PRINT ''
PRINT 'Creating stored procedure sp_syscollector_event_oncollectionbegin...'

IF (NOT OBJECT_ID('dbo.sp_syscollector_event_oncollectionbegin', 'P') IS NULL)
    DROP PROCEDURE [dbo].[sp_syscollector_event_oncollectionbegin]
go

CREATE PROCEDURE [dbo].[sp_syscollector_event_oncollectionbegin]
    @collection_set_id int,
    @mode smallint = NULL,
    @operator nvarchar(128) = NULL,
    @log_id bigint OUTPUT
AS
BEGIN
    SET NOCOUNT ON

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_proxy')
        RETURN(1) -- Failure
    END

    -- Verify parameters
    --

    -- Check the collection_set_id
    IF (@collection_set_id IS NULL)
    BEGIN
        RAISERROR(14606, -1, -1, '@collection_set_id')
        RETURN (1)
    END
    ELSE IF (NOT EXISTS (SELECT collection_set_id FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id))
    BEGIN
        DECLARE @collection_set_id_as_char VARCHAR(36)
        SELECT @collection_set_id_as_char = CONVERT(VARCHAR(36), @collection_set_id)

        RAISERROR(14262, -1, -1, '@collection_set_id', @collection_set_id_as_char)
        RETURN (1)
    END


    -- Default operator to currently logged in user
    SET @operator = NULLIF(LTRIM(RTRIM(@operator)), '')
    SET @operator = ISNULL(@operator, suser_sname())

    -- Default mode to Collection
    SET @mode = ISNULL(@mode, 0)

    -- Find the parent log id.
    -- It will be a log entry for the same collection set, with no parent and not finished
    DECLARE @parent_log_id bigint
    SELECT TOP 1 @parent_log_id = log_id FROM dbo.syscollector_execution_log_internal 
        WHERE collection_set_id = @collection_set_id 
        AND parent_log_id IS NULL
        AND (@mode = 1 OR finish_time IS NULL)
        ORDER BY start_time DESC

    -- Insert the log record
    --
    INSERT INTO dbo.syscollector_execution_log_internal (
        parent_log_id, 
        collection_set_id, 
        collection_item_id,
        start_time,
        last_iteration_time,
        finish_time,
        runtime_execution_mode,
        [status],
        operator,
        package_id,
        package_execution_id,
        failure_message
    ) VALUES (
        @parent_log_id,
        @collection_set_id,
        NULL,
        GETDATE(),
        NULL,
        NULL,
        @mode,
        0, -- Running
        @operator,
        NULL,
        NULL,
        NULL
    )

    SET @log_id = SCOPE_IDENTITY()                
    
    RETURN (0)
END
go

PRINT ''
PRINT 'Creating stored procedure sp_syscollector_event_oncollectionend...'

IF (NOT OBJECT_ID('dbo.sp_syscollector_event_oncollectionend', 'P') IS NULL)
    DROP PROCEDURE [dbo].[sp_syscollector_event_oncollectionend]
go

CREATE PROCEDURE [dbo].[sp_syscollector_event_oncollectionend]
    @log_id bigint
AS
BEGIN
    SET NOCOUNT ON

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_proxy')
        RETURN(1) -- Failure
    END

    -- Check the log_id
    DECLARE @retVal INT
    EXEC @retVal = dbo.sp_syscollector_verify_event_log_id @log_id, 1
    IF (@retVal <> 0)
        RETURN (@retVal)

    -- Mark the log as finished
    UPDATE dbo.syscollector_execution_log_internal SET
        finish_time = GETDATE(),
        [status] = CASE
            WHEN [status] = 0 THEN 1 -- Mark complete if it was running
            ELSE [status]            -- Leave the error status unchanged
        END
    WHERE log_id = @log_id

    RETURN (0)
END
go

PRINT ''
PRINT 'Creating stored procedure sp_syscollector_event_onpackagebegin...'

IF (NOT OBJECT_ID('dbo.sp_syscollector_event_onpackagebegin', 'P') IS NULL)
    DROP PROCEDURE [dbo].[sp_syscollector_event_onpackagebegin]
go

CREATE PROCEDURE [dbo].[sp_syscollector_event_onpackagebegin]
    @parent_log_id bigint,
    @package_id uniqueidentifier,
    @package_execution_id uniqueidentifier,
    @collection_item_id int = NULL,
    @mode smallint = NULL,
    @operator nvarchar(128) = NULL,
    @log_id bigint OUTPUT
AS
BEGIN
    SET NOCOUNT ON

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_proxy')
        RETURN(1) -- Failure
    END

    -- Verify parameters
    --

    -- Check the @parent_log_id
    IF (@parent_log_id IS NULL)
    BEGIN
        RAISERROR(14606, -1, -1, '@parent_log_id')
        RETURN (1)
    END
    ELSE IF (NOT EXISTS (SELECT log_id FROM dbo.syscollector_execution_log WHERE log_id = @parent_log_id))
    BEGIN
        DECLARE @parent_log_id_as_char VARCHAR(36)
        SELECT @parent_log_id_as_char = CONVERT(VARCHAR(36), @parent_log_id)

        RAISERROR(14262, -1, -1, '@parent_log_id', @parent_log_id_as_char)
        RETURN (1)
    END

    -- Check the @package_id
    IF (@package_id IS NULL)
    BEGIN
        RAISERROR(14606, -1, -1, '@package_id')
        RETURN (1)
    END
    -- The 84CEC861... package is an id of our special Master package that is allowed to start 
    -- the log without being saved to sysssispackages
    ELSE IF (@package_id != N'84CEC861-D619-433D-86FB-0BB851AF454A' AND NOT EXISTS (SELECT id FROM dbo.sysssispackages WHERE id = @package_id))
    BEGIN
        DECLARE @package_id_as_char VARCHAR(50)
        SELECT @package_id_as_char = CONVERT(VARCHAR(50), @package_id)

        RAISERROR(14262, -1, -1, '@package_id', @package_id_as_char)
        RETURN (1)
    END

    -- Default operator to currently logged in user
    SET @operator = NULLIF(LTRIM(RTRIM(@operator)), '')
    SET @operator = ISNULL(@operator, suser_sname())

    -- Default mode to Collection
    SET @mode = ISNULL(@mode, 0)

    -- Find out the collection_set_id from the parent
    DECLARE @collection_set_id INT
    SELECT @collection_set_id = collection_set_id FROM dbo.syscollector_execution_log WHERE log_id = @parent_log_id

    -- Check the @package_execution_id
    IF (@package_execution_id IS NULL)
    BEGIN
        RAISERROR(14606, -1, -1, '@package_execution_id')
        RETURN (1)
    END
    

    -- Insert the log record
    --
    INSERT INTO dbo.syscollector_execution_log_internal (
        parent_log_id, 
        collection_set_id, 
        collection_item_id,
        start_time,
        last_iteration_time,
        finish_time,
        runtime_execution_mode,
        [status],
        operator,
        package_id,
        package_execution_id,
        failure_message
    ) VALUES (
        @parent_log_id,
        @collection_set_id,
        @collection_item_id,        
        GETDATE(),
        NULL,
        NULL,
        @mode,
        0, -- Running
        @operator,
        @package_id,
        @package_execution_id,        
        NULL
    )

    SET @log_id = SCOPE_IDENTITY()                

    RETURN (0)
END
go


PRINT ''
PRINT 'Creating stored procedure sp_syscollector_event_onpackageend...'

IF (NOT OBJECT_ID('dbo.sp_syscollector_event_onpackageend', 'P') IS NULL)
    DROP PROCEDURE [dbo].[sp_syscollector_event_onpackageend]
go

CREATE PROCEDURE [dbo].[sp_syscollector_event_onpackageend]
    @log_id bigint
AS
BEGIN
    SET NOCOUNT ON

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_proxy')
        RETURN(1) -- Failure
    END

    -- Check the log_id
    DECLARE @retVal INT
    EXEC @retVal = dbo.sp_syscollector_verify_event_log_id @log_id
    IF (@retVal <> 0)
        RETURN (@retVal)

    -- Mark the log as finished
    UPDATE dbo.syscollector_execution_log_internal SET
        finish_time = GETDATE(),
        [status] = CASE
            WHEN [status] = 0 THEN 1 -- Mark complete if it was running
            ELSE [status]            -- Leave the error status unchanged
        END
    WHERE log_id = @log_id

    DECLARE @runtime_execution_mode smallint
    DECLARE @status smallint
    SELECT @status = [status], @runtime_execution_mode = runtime_execution_mode
    FROM dbo.syscollector_execution_log_internal
    WHERE log_id = @log_id

    -- status was successful and this is logged by an upload package
    IF @status = 1 AND @runtime_execution_mode = 1
    BEGIN
        -- if the package ended succesfully, update the top most log to warning if it had failure
        -- this is because if there were a previous upload failure but the latest upload were successful, 
        -- we want indicated a warning rather than a failure throughout the lifetime of this collection set
        DECLARE @parent_log_id BIGINT
        SELECT @parent_log_id = parent_log_id FROM dbo.syscollector_execution_log_internal WHERE log_id = @log_id;
        WHILE @parent_log_id IS NOT NULL
        BEGIN
            -- get the next parent
            SET @log_id = @parent_log_id
            SELECT @parent_log_id = parent_log_id FROM dbo.syscollector_execution_log_internal WHERE log_id = @log_id;
        END

        UPDATE dbo.syscollector_execution_log_internal SET
            [status] = CASE
                WHEN [status] = 2 THEN 3 -- Mark warning if it indicated a failure
                ELSE [status]            -- Leave the original status unchanged
            END
        WHERE
            log_id = @log_id
    END

    RETURN (0)
END
go


PRINT ''
PRINT 'Creating stored procedure sp_syscollector_event_onpackageupdate...'

IF (NOT OBJECT_ID('dbo.sp_syscollector_event_onpackageupdate', 'P') IS NULL)
    DROP PROCEDURE [dbo].[sp_syscollector_event_onpackageupdate]
go

CREATE PROCEDURE [dbo].[sp_syscollector_event_onpackageupdate]
    @log_id bigint
AS
BEGIN
    SET NOCOUNT ON

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_proxy')
        RETURN(1) -- Failure
    END

    -- Check the log_id
    DECLARE @retVal INT
    EXEC @retVal = dbo.sp_syscollector_verify_event_log_id @log_id
    IF (@retVal <> 0)
        RETURN (@retVal)

    -- Update the log
    UPDATE dbo.syscollector_execution_log_internal SET
        last_iteration_time = GETDATE()
    WHERE log_id = @log_id

    RETURN (0)
END
go


PRINT ''
PRINT 'Creating stored procedure sp_syscollector_event_onerror...'

IF (NOT OBJECT_ID('dbo.sp_syscollector_event_onerror', 'P') IS NULL)
    DROP PROCEDURE [dbo].[sp_syscollector_event_onerror]
go

CREATE PROCEDURE [dbo].[sp_syscollector_event_onerror]
    @log_id bigint,
    @message nvarchar(2048) = NULL
AS
BEGIN
    SET NOCOUNT ON
    
    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_proxy')
        RETURN(1) -- Failure
    END

    DECLARE @TranCounter INT
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_event_onerror
    ELSE
        BEGIN TRANSACTION
    
    BEGIN TRY
    -- Check the log_id
    -- If @message is passed, we can allow to enter the error for a collection set
    -- otherwise we will rely on the entries in sysssislog table to get the error message.
    DECLARE @retVal INT
    IF (@message IS NULL)
    BEGIN
        EXEC @retVal = dbo.sp_syscollector_verify_event_log_id @log_id, 0
    END
    ELSE
    BEGIN
        EXEC @retVal = dbo.sp_syscollector_verify_event_log_id @log_id, 1
    END
    IF (@retVal <> 0)
        RETURN (@retVal)


    DECLARE
         @failure_message   NVARCHAR(2048)
        ,@execution_id        UNIQUEIDENTIFIER

    IF @message IS NULL 
    BEGIN
        -- If no message is provided, find the last task that has failed
        -- for this package in the sysssislog table.
        -- Store the message as the failure_message for our package log.
        SELECT 
            @execution_id = package_execution_id
        FROM dbo.syscollector_execution_log
        WHERE log_id = @log_id

        SELECT TOP 1 
            @failure_message = [message]
        FROM dbo.sysssislog
        WHERE executionid = @execution_id
            AND (UPPER([event] COLLATE SQL_Latin1_General_CP1_CS_AS) = 'ONERROR')
        ORDER BY endtime DESC
    END 
    ELSE 
    BEGIN
        -- Otherwise use the provided message
        SET @failure_message = @message
    END

    -- Update the execution log
    UPDATE dbo.syscollector_execution_log_internal SET
         [status] = 2                    -- Mark as Failed
        ,failure_message = @failure_message
    WHERE
        log_id = @log_id

    -- Update all parent logs with the failure status
    SELECT @log_id = parent_log_id FROM dbo.syscollector_execution_log_internal WHERE log_id = @log_id;
    WHILE @log_id IS NOT NULL
    BEGIN
        UPDATE dbo.syscollector_execution_log_internal SET
            [status] = 2                    -- Mark as Failed
        WHERE
            log_id = @log_id;

        -- get the next parent
        SELECT @log_id = parent_log_id FROM dbo.syscollector_execution_log_internal WHERE log_id = @log_id;
    END

    IF (@TranCounter = 0)
        COMMIT TRANSACTION
    RETURN (0)

    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_event_onerror

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
        
        RETURN (1)    
    END CATCH
END
go
  
  
PRINT ''
PRINT 'Creating stored procedure sp_syscollector_event_onstatsupdate...'

IF (NOT OBJECT_ID('dbo.sp_syscollector_event_onstatsupdate', 'P') IS NULL)
    DROP PROCEDURE [dbo].[sp_syscollector_event_onstatsupdate]
go

CREATE PROCEDURE [dbo].[sp_syscollector_event_onstatsupdate]
    @log_id bigint,
    @task_name nvarchar(128),
    @row_count_in int = NULL,
    @row_count_out int = NULL,
    @row_count_error int = NULL,
    @execution_time_ms int = NULL
AS
BEGIN
    SET NOCOUNT ON

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_proxy')
        RETURN(1) -- Failure
    END

    -- Check the log_id
    DECLARE @retVal INT
    EXEC @retVal = dbo.sp_syscollector_verify_event_log_id @log_id
    IF (@retVal <> 0)
        RETURN (@retVal)
    
    -- Check task name
    IF (@task_name IS NOT NULL)
    BEGIN
        SET @task_name = NULLIF(LTRIM(RTRIM(@task_name)), N'')
    END
    IF (@task_name IS NULL)
    BEGIN
        RAISERROR(14606, -1, -1, '@task_name')
        RETURN (1)
    END
    
    -- Insert the log entry
    INSERT INTO dbo.syscollector_execution_stats_internal (
        log_id,
        task_name,
        execution_row_count_in,
        execution_row_count_out,
        execution_row_count_errors,
        execution_time_ms,
        log_time
    ) VALUES (
        @log_id,
        @task_name,
        @row_count_in,
        @row_count_out,
        @row_count_error,
        NULLIF(@execution_time_ms, 0),
        GETDATE()
    )

    RETURN (0)
END
go

---------------------------------------------------------------
-- Data Collector log viewing functions and views
---------------------------------------------------------------

-- [fn_syscollector_find_collection_set_root]
-- This function finds the first log entry for a given collection set
-- run. It retunrs log_id of that entry. 
PRINT ''
PRINT 'Creating function [dbo].[fn_syscollector_find_collection_set_root] ...'
IF (NOT OBJECT_ID(N'dbo.fn_syscollector_find_collection_set_root', 'FN') IS NULL)
  DROP FUNCTION [dbo].[fn_syscollector_find_collection_set_root]
go

CREATE FUNCTION [dbo].[fn_syscollector_find_collection_set_root]
(
    @log_id BIGINT
)
RETURNS BIGINT
WITH RETURNS NULL ON NULL INPUT
AS
BEGIN
    DECLARE @root_id BIGINT;

    -- Derive result using a CTE as the table is self-referencing
    WITH graph AS
    (
        -- select the anchor (specified) node
        SELECT log_id, parent_log_id FROM dbo.syscollector_execution_log WHERE log_id = @log_id
        UNION ALL
        -- select the parent node recursively
        SELECT node.log_id, node.parent_log_id FROM dbo.syscollector_execution_log node
        INNER JOIN graph AS leaf ON (node.log_id = leaf.parent_log_id)
    )
    SELECT @root_id = log_id FROM graph WHERE parent_log_id = 0;
    
    --Return result
    RETURN ISNULL(@root_id, @log_id)
END 
go

-- [fn_syscollector_get_execution_log_tree]
-- This function returns a set of log entries related to a single run 
-- of a collection set. The entries are ordered in a hierarchy, starting from
-- the collection set first log entry and then down through all packages 
-- that were executed as part of the collection set.
PRINT ''
PRINT 'Creating function [dbo].[fn_syscollector_get_execution_log_tree] ...'
IF (NOT OBJECT_ID(N'dbo.fn_syscollector_get_execution_log_tree', 'IF') IS NULL)
    DROP FUNCTION [dbo].[fn_syscollector_get_execution_log_tree]
go

CREATE FUNCTION [dbo].[fn_syscollector_get_execution_log_tree] 
(
     @log_id                BIGINT,
     @from_collection_set    BIT = 1
) 
RETURNS TABLE
AS
RETURN
(
    -- Derive result using a CTE as the table is self-referencing
    WITH graph AS 
    (
        -- select the anchor (specified) node
        SELECT 
            log_id,
            parent_log_id,
            collection_set_id,
            start_time,
            last_iteration_time,
            finish_time,
            runtime_execution_mode,
            operator,
            [status],
            package_id,
            package_execution_id,
            failure_message,
            0 AS depth 
        FROM dbo.syscollector_execution_log
        WHERE log_id = CASE @from_collection_set
            WHEN 1 THEN dbo.fn_syscollector_find_collection_set_root(@log_id)
            ELSE @log_id
        END 
        -- select the child nodes recursively
        UNION ALL
        SELECT 
            leaf.log_id,
            leaf.parent_log_id,
            leaf.collection_set_id,
            leaf.start_time,
            leaf.last_iteration_time,
            leaf.finish_time,
            leaf.runtime_execution_mode,
            leaf.operator,
            leaf.[status],
            leaf.package_id,
            leaf.package_execution_id,
            leaf.failure_message,
            node.depth + 1 AS depth
        FROM dbo.syscollector_execution_log AS leaf
        INNER JOIN graph AS node ON (node.log_id = leaf.parent_log_id)
    )
    SELECT 
        log_id,
        parent_log_id,
        collection_set_id,
        start_time,
        last_iteration_time,
        finish_time,
        CASE 
            WHEN finish_time IS NOT NULL THEN DATEDIFF(ss, start_time, finish_time) 
            WHEN last_iteration_time IS NOT NULL THEN DATEDIFF(ss, start_time, last_iteration_time) 
            ELSE 0
        END AS duration,
        runtime_execution_mode,
        operator,
        [status],
        package_id,
        package_execution_id,
        failure_message,
        depth 
    FROM graph
) 
go


-- [fn_syscollector_get_execution_stats]
-- This function returns stats for each execution of a package.
-- The stats should be logged for each iteration within the package.
PRINT ''
PRINT 'Creating function [dbo].[fn_syscollector_get_execution_stats] ...'
IF (NOT OBJECT_ID(N'dbo.fn_syscollector_get_execution_stats', 'IF') IS NULL)
    DROP FUNCTION [dbo].[fn_syscollector_get_execution_stats]
go

CREATE FUNCTION [dbo].[fn_syscollector_get_execution_stats] 
(
     @log_id                BIGINT
) 
RETURNS TABLE
AS
RETURN
(
    SELECT 
        log_id,
        task_name,
        AVG(execution_row_count_in) AS avg_row_count_in,
        MIN(execution_row_count_in) AS min_row_count_in,
        MAX(execution_row_count_in) AS max_row_count_in,
        AVG(execution_row_count_out) AS avg_row_count_out,
        MIN(execution_row_count_out) AS min_row_count_out,
        MAX(execution_row_count_out) AS max_row_count_out,
        AVG(execution_row_count_errors) AS avg_row_count_errors,
        MIN(execution_row_count_errors) AS min_row_count_errors,
        MAX(execution_row_count_errors) AS max_row_count_errors,
        AVG(execution_time_ms) AS avg_duration,
        MIN(execution_time_ms) AS min_duration,
        MAX(execution_time_ms) AS max_duration
    FROM dbo.syscollector_execution_stats
    WHERE log_id = @log_id
    GROUP BY log_id, task_name
)
go


-- [fn_syscollector_get_execution_details]
-- This function returns detailed log entries from SSIS log for each package
-- execution. The log id passed as input is an id of a log entry for that package
-- from syscollector_execution_log view.
PRINT ''
PRINT 'Creating function [dbo].[fn_syscollector_get_execution_details] ...'
IF (NOT OBJECT_ID(N'dbo.fn_syscollector_get_execution_details', 'IF') IS NULL)
    DROP FUNCTION [dbo].[fn_syscollector_get_execution_details]
go
CREATE FUNCTION [dbo].[fn_syscollector_get_execution_details] 
(
     @log_id                BIGINT
)
RETURNS TABLE
AS
RETURN
(
    SELECT TOP (100) PERCENT
        l.source,
        l.event,
        l.message,
        l.starttime AS start_time,
        l.endtime AS finish_time,
        l.datacode,
        l.databytes
    FROM sysssislog l
    JOIN dbo.syscollector_execution_log e ON (e.package_execution_id = l.executionid)
    WHERE e.log_id = @log_id
    ORDER BY l.starttime
)
go            


-- [syscollector_execution_log_full]
-- An expanded log view that shows the log entries in a hierarchical order.
PRINT ''
PRINT 'Creating view syscollector_execution_log_full...'
IF (NOT OBJECT_ID('dbo.syscollector_execution_log_full', 'V') IS NULL)
    DROP VIEW [dbo].[syscollector_execution_log_full]
go

CREATE VIEW [dbo].[syscollector_execution_log_full]
AS
SELECT 
        t.log_id,
        ISNULL(t.parent_log_id, 0) as parent_log_id,
        CASE 
            WHEN t.package_id IS NULL THEN SPACE(t.depth * 4) + c.name
            WHEN t.package_id = N'84CEC861-D619-433D-86FB-0BB851AF454A' THEN SPACE(t.depth * 4) + N'Master'
            ELSE SPACE(t.depth * 4) + p.name 
        END AS [name],
        t.[status],
        t.runtime_execution_mode,
        t.start_time,
        t.last_iteration_time,
        t.finish_time,
        t.duration,
        t.failure_message,
        t.operator,
        t.package_execution_id,
        t.collection_set_id
    FROM dbo.syscollector_execution_log_internal l
    CROSS APPLY dbo.fn_syscollector_get_execution_log_tree(l.log_id, 0) t
    LEFT OUTER JOIN dbo.syscollector_collection_sets c ON( c.collection_set_id = t.collection_set_id)
    LEFT OUTER JOIN dbo.sysssispackages p ON (p.id = t.package_id AND p.id != N'84CEC861-D619-433D-86FB-0BB851AF454A')
    WHERE l.parent_log_id IS NULL
go
        
---------------------------------------------------------------
-- Data Collection log clean-up
---------------------------------------------------------------

-- [sp_syscollector_delete_execution_log_tree]
-- This stored procedure removes all log entries related to a single
-- run of a collection set. It also removes corresponding log entries
-- from SSIS log tables.
PRINT ''
IF (NOT OBJECT_ID(N'dbo.sp_syscollector_delete_execution_log_tree', 'P') IS NULL)
BEGIN
  PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_execution_log_tree] ...'
  DROP PROCEDURE [dbo].[sp_syscollector_delete_execution_log_tree]
END
go

PRINT 'Creating procedure [dbo].[sp_syscollector_delete_execution_log_tree] ...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_delete_execution_log_tree]
    @log_id BIGINT,
    @from_collection_set    BIT = 1
AS
BEGIN
    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_operator')
        RETURN(1) -- Failure
    END

    SET NOCOUNT ON;
    CREATE TABLE #log_ids (log_id BIGINT);
    
    WITH graph AS
    (
        SELECT log_id FROM dbo.syscollector_execution_log
        WHERE log_id = CASE @from_collection_set
            WHEN 1 THEN dbo.fn_syscollector_find_collection_set_root(@log_id)
            ELSE @log_id
        END
        UNION ALL
        SELECT leaf.log_id FROM dbo.syscollector_execution_log AS leaf
        INNER JOIN graph AS node ON (node.log_id = leaf.parent_log_id)
    )
    INSERT INTO #log_ids
    SELECT log_id
    FROM graph
    
    -- Delete all ssis log records pertaining to the selected logs
    DELETE FROM dbo.sysssislog
        FROM dbo.sysssislog AS s
        INNER JOIN dbo.syscollector_execution_log_internal AS l ON (l.package_execution_id = s.executionid)
        INNER JOIN #log_ids AS i ON i.log_id = l.log_id
        
    -- Then delete the actual logs
    DELETE FROM syscollector_execution_log_internal
        FROM syscollector_execution_log_internal AS l
        INNER Join #log_ids AS i ON i.log_id = l.log_id

    DROP TABLE #log_ids
    RETURN (0)
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_delete_collection_set_internal]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_collection_set_internal]...'
    DROP PROCEDURE [dbo].[sp_syscollector_delete_collection_set_internal]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_delete_collection_set_internal]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_delete_collection_set_internal]
    @collection_set_id      int,
    @name                   sysname,
    @collection_job_id      uniqueidentifier,
    @upload_job_id          uniqueidentifier,
    @collection_mode        smallint
AS
BEGIN
    DECLARE @TranCounter int
    SET @TranCounter = @@TRANCOUNT
    IF (@TranCounter > 0)
        SAVE TRANSACTION tran_delete_collection_set
    ELSE
        BEGIN TRANSACTION
    
    BEGIN TRY
        -- clean log before deleting collection set
        DECLARE @log_id bigint
        SET @log_id = (SELECT TOP(1) log_id  FROM dbo.syscollector_execution_log WHERE collection_set_id = @collection_set_id)
        WHILE (@log_id IS NOT NULL)
        BEGIN
            EXEC dbo.sp_syscollector_delete_execution_log_tree @log_id = @log_id
            SET @log_id = (SELECT TOP(1) log_id  FROM dbo.syscollector_execution_log WHERE collection_set_id = @collection_set_id)
        END

        DECLARE @schedule_id    int
        SELECT @schedule_id = schedule_id
        FROM dbo.syscollector_collection_sets cs JOIN sysschedules_localserver_view sv
        ON (cs.schedule_uid = sv.schedule_uid)
        WHERE collection_set_id = @collection_set_id

        DELETE [dbo].[syscollector_collection_sets_internal]
        WHERE collection_set_id = @collection_set_id

        EXEC dbo.sp_syscollector_delete_jobs 
            @collection_job_id        = @collection_job_id,
            @upload_job_id            = @upload_job_id,
            @schedule_id            = @schedule_id,
            @collection_mode        = @collection_mode

        IF (@TranCounter = 0)
            COMMIT TRANSACTION
        RETURN (0)
    END TRY
    BEGIN CATCH
        IF (@TranCounter = 0 OR XACT_STATE() = -1)
            ROLLBACK TRANSACTION
        ELSE IF (XACT_STATE() = 1)
            ROLLBACK TRANSACTION tran_delete_collection_set

        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

        RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
        
        RETURN (1)    
    END CATCH
END
GO

-- This is a stored procedure of collection_set, but it is created here because it 
-- makes references to the collection item view, execution log, 
-- and the [sp_syscollector_delete_execution_log_tree] stored proc
IF (NOT OBJECT_ID('[dbo].[sp_syscollector_delete_collection_set]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_collection_set]...'
    DROP PROCEDURE [dbo].[sp_syscollector_delete_collection_set]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_delete_collection_set]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_delete_collection_set]
    @collection_set_id            int = NULL,
    @name                        sysname = NULL
WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser'
AS
BEGIN
    -- Security check (role membership)
    EXECUTE AS CALLER;
    IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        REVERT;
        RAISERROR(14677, -1, -1, 'dc_admin')
        RETURN (1)
    END
    REVERT;

    DECLARE @retVal int
    EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT
    IF (@retVal <> 0)
        RETURN (1)

    DECLARE @is_system            bit
    DECLARE @is_running            bit
    DECLARE @upload_job_id        uniqueidentifier
    DECLARE @collection_job_id    uniqueidentifier
    DECLARE @collection_mode    smallint
    SELECT    @is_running = is_running,
            @is_system = is_system,
            @upload_job_id = upload_job_id, 
            @collection_job_id = collection_job_id,
            @collection_mode = collection_mode
    FROM [dbo].[syscollector_collection_sets]
    WHERE collection_set_id = @collection_set_id

    IF (@is_system = 1)
    BEGIN
        -- cannot update, delete, or add new collection items to a system collection set
        RAISERROR(14696, -1, -1);
        RETURN (1)
    END

    IF (@is_running = 1)
    BEGIN
        EXEC @retVal = sp_syscollector_stop_collection_set @collection_set_id = @collection_set_id
        IF (@retVal <> 0)
            RETURN (1)
    END

    -- All checks are go
    -- Do the actual delete
    EXEC @retVal = sp_syscollector_delete_collection_set_internal
                        @collection_set_id = @collection_set_id, 
                        @name = @name,
                        @collection_job_id = @collection_job_id,
                        @upload_job_id = @upload_job_id,
                        @collection_mode = @collection_mode
    RETURN (0)
END
GO

IF (NOT OBJECT_ID('dbo.sp_syscollector_purge_collection_logs', 'P') IS NULL)
BEGIN
    PRINT ''
    PRINT 'Dropping stored procedure sp_syscollector_purge_collection_logs...'
    DROP PROCEDURE [dbo].[sp_syscollector_purge_collection_logs]
END
GO

-- [sp_syscollector_purge_collection_logs]
-- The sp cleans any log record with an expired finish date
-- Expiration is measured from the date provided to the sp
-- and defaults to TODAY.
PRINT ''
PRINT 'Creating stored procedure sp_syscollector_purge_collection_logs...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_purge_collection_logs]
    @reference_date datetime = NULL
AS
BEGIN
    SET NOCOUNT ON

    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_proxy')
        RETURN(1) -- Failure
    END

    IF (@reference_date IS NULL)
    BEGIN
        SET @reference_date = GETDATE()
    END
    
    -- An expired log record is any record of a collection set that is older than 
    -- the reference date minus the collection set's days_until_expiration
    CREATE TABLE #purged_log_ids (log_id BIGINT)
    
    INSERT INTO #purged_log_ids
    SELECT log_id
    FROM syscollector_execution_log_internal as l
    INNER JOIN syscollector_collection_sets s ON l.collection_set_id = s.collection_set_id
    WHERE s.days_until_expiration > 0
    AND @reference_date >= DATEADD(DAY, s.days_until_expiration, l.finish_time)

    -- Delete all ssis log records pertaining to expired logs
    DELETE FROM dbo.sysssislog
        FROM dbo.sysssislog AS s
        INNER JOIN dbo.syscollector_execution_log_internal AS l ON (l.package_execution_id = s.executionid)
        INNER JOIN #purged_log_ids AS i ON i.log_id = l.log_id
        
    -- Then delete the actual logs
    DELETE FROM syscollector_execution_log_internal
        FROM syscollector_execution_log_internal AS l
        INNER Join #purged_log_ids AS i ON i.log_id = l.log_id

    DROP TABLE #purged_log_ids
    -- Go for another round to cleanup the orphans
    -- Ideally, the log heirarchy guarantees that a finish time by a parent log will always
    -- be higher than the finish time of any of its descendants.
    -- The purge step however does not delete log records with a null finish time
    -- A child log can have a null finish time while its parent is closed if there is an
    -- error in execution that causes the log to stay open.
    -- If such a child log exists, its parent will be purged leaving it as an orphan
    
    -- get orphan records and all their descendants in a cursor and purge them
    DECLARE orphaned_log_cursor INSENSITIVE CURSOR FOR
            SELECT log_id 
            FROM syscollector_execution_log_internal
            WHERE parent_log_id NOT IN (
                SELECT log_id FROM syscollector_execution_log_internal
            )
            FOR READ ONLY
            
    DECLARE @log_id BIGINT

    -- for every orphan, delete all its remaining tree
    -- this is supposedly a very small fraction of the entire log
    OPEN orphaned_log_cursor    
    FETCH orphaned_log_cursor INTO @log_id
    WHILE @@FETCH_STATUS = 0
    BEGIN
        EXEC sp_syscollector_delete_execution_log_tree @log_id = @log_id, @from_collection_set = 0
        FETCH orphaned_log_cursor INTO @log_id
    END
    
    CLOSE orphaned_log_cursor
    DEALLOCATE orphaned_log_cursor
END
GO

---------------------------------------------------------------
-- Start and stop Data Collector
---------------------------------------------------------------

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_enable_collector]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_enable_collector]...'
    DROP PROCEDURE [dbo].[sp_syscollector_enable_collector]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_enable_collector]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_enable_collector]
WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser'
AS
BEGIN
    -- Security check (role membership)
    EXECUTE AS CALLER;
    IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        REVERT;
        RAISERROR(14677, -1, -1, 'dc_operator')
        RETURN(1) -- Failure
    END
    REVERT;

    BEGIN TRANSACTION

    DECLARE @was_enabled int;

    SELECT @was_enabled = ISNULL(CONVERT(int, parameter_value),0)
    FROM [dbo].[syscollector_config_store_internal]
    WHERE parameter_name = 'CollectorEnabled'

    IF (@was_enabled = 0)
    BEGIN

        UPDATE [dbo].[syscollector_config_store_internal]
        SET parameter_value = 1
        WHERE parameter_name = 'CollectorEnabled'

        DECLARE @collection_set_id int

        DECLARE collection_set_cursor CURSOR LOCAL FOR
            SELECT collection_set_id
            FROM dbo.syscollector_collection_sets
            WHERE is_running = 1

        OPEN collection_set_cursor
        FETCH collection_set_cursor INTO @collection_set_id

        WHILE @@FETCH_STATUS = 0 
        BEGIN
            EXEC dbo.sp_syscollector_start_collection_set_jobs @collection_set_id = @collection_set_id
            FETCH collection_set_cursor INTO @collection_set_id
        END

        CLOSE collection_set_cursor
        DEALLOCATE collection_set_cursor

    END

    COMMIT TRANSACTION

END
GO


IF (NOT OBJECT_ID('[dbo].[sp_syscollector_disable_collector]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_disable_collector]...'
    DROP PROCEDURE [dbo].[sp_syscollector_disable_collector]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_disable_collector]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_disable_collector]
WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser'
AS
BEGIN
    -- Security check (role membership)
    EXECUTE AS CALLER;
    IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        REVERT;
        RAISERROR(14677, -1, -1, 'dc_operator')
        RETURN(1) -- Failure
    END
    REVERT;

    BEGIN TRANSACTION

    DECLARE @was_enabled int;

    SELECT @was_enabled = ISNULL(CONVERT(int, parameter_value),0)
    FROM [dbo].[syscollector_config_store_internal]
    WHERE parameter_name = 'CollectorEnabled'

    IF (@was_enabled <> 0)
    BEGIN

        UPDATE [dbo].[syscollector_config_store_internal]
        SET parameter_value = 0
        WHERE parameter_name = 'CollectorEnabled'

        DECLARE @collection_set_id INT
        DECLARE @collection_mode SMALLINT
        DECLARE @collection_job_id UNIQUEIDENTIFIER

        DECLARE collection_set_cursor CURSOR LOCAL FOR
            SELECT collection_set_id, collection_mode, collection_job_id
            FROM dbo.syscollector_collection_sets
            WHERE is_running = 1

        OPEN collection_set_cursor
        FETCH collection_set_cursor INTO @collection_set_id, @collection_mode, @collection_job_id

        WHILE @@FETCH_STATUS = 0 
        BEGIN
            -- If this collection set is running in cached mode, and the collection job is running, we need to stop the job explicitly here
            DECLARE @is_collection_job_running INT
            EXECUTE [dbo].[sp_syscollector_get_collection_set_execution_status]
                    @collection_set_id = @collection_set_id,
                    @is_collection_running = @is_collection_job_running OUTPUT    

            IF (@is_collection_job_running = 1
                AND @collection_mode = 0)           -- Cached mode
            BEGIN
                EXEC sp_stop_job @job_id = @collection_job_id
            END

            -- Now, disable the jobs and detach them from the upload schedules
            EXEC dbo.sp_syscollector_stop_collection_set_jobs @collection_set_id = @collection_set_id
            FETCH collection_set_cursor INTO @collection_set_id, @collection_mode, @collection_job_id
        END
        CLOSE collection_set_cursor
        DEALLOCATE collection_set_cursor

    END

    COMMIT TRANSACTION

END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_get_trace_info]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_get_trace_info]'
    DROP PROCEDURE [dbo].[sp_syscollector_get_trace_info]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_get_trace_info]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_get_trace_info]
    @trace_path  nvarchar(512),
    @use_default int
AS
BEGIN
    SELECT 
        CONVERT(nvarchar(30), t.start_time, 126) as start_time,
        CASE t.status 
            WHEN 1 THEN 1 
            ELSE 0 
        END AS is_running, 
        ISNULL(t.dropped_event_count,0) as dropped_event_count,
        t.id
    FROM sys.traces t
    WHERE (@use_default=1 and t.is_default=1)
          OR (@use_default=0 AND t.path LIKE (@trace_path + N'%.trc'))
END
GO

---------------------------------------------------------------
-- Data Collector: Helper procedures
---------------------------------------------------------------

-- Procedure to retrieve a query plan from cache
IF (NOT OBJECT_ID('[dbo].[sp_syscollector_text_query_plan_lookpup]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_text_query_plan_lookpup]'
    DROP PROCEDURE [dbo].[sp_syscollector_text_query_plan_lookpup]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_text_query_plan_lookpup]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_text_query_plan_lookpup]
    @plan_handle varbinary(64),
    @statement_start_offset int,
    @statement_end_offset int
AS
BEGIN
    SET NOCOUNT ON
    SELECT    
        @plan_handle AS plan_handle,
        @statement_start_offset AS statement_start_offset,
        @statement_end_offset AS statement_end_offset,
        [dbid] AS database_id,
        [objectid] AS object_id,
        OBJECT_NAME(objectid, dbid) AS object_name,
        [query_plan] AS query_plan
    FROM    
        [sys].[dm_exec_text_query_plan](@plan_handle, @statement_start_offset, @statement_end_offset) dm
END
GO

-- Procedure to retrieve a query text from cache
IF (NOT OBJECT_ID('[dbo].[sp_syscollector_sql_text_lookup]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_sql_text_lookup]'
    DROP PROCEDURE [dbo].[sp_syscollector_sql_text_lookup]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_sql_text_lookup]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_sql_text_lookup]
    @sql_handle varbinary(64)
AS
BEGIN
    SET NOCOUNT ON
    SELECT    
        @sql_handle as sql_handle,
        dm.[dbid] AS database_id,
        dm.[objectid] AS object_id,
        OBJECT_NAME(objectid, dbid) AS object_name,
        CASE dm.[encrypted]
            WHEN 1 THEN N'Query SQL Text Encrypted'
            ELSE dm.[text]
        END AS sql_text
        FROM    
            [sys].[dm_exec_sql_text](@sql_handle) dm
END
GO

---------------------------------------------------------------
-- Install out-of-the-box objects
---------------------------------------------------------------
PRINT 'Installing out of the box Collector objects'
PRINT ''

-- We need agent XP's to be on for many parts of the coming installation script
-- Enable them here once and return them to their original state when done

DECLARE @advopt_old_value int
DECLARE @comp_old_value int
EXEC #sp_enable_component 'Agent XPs', @advopt_old_value out, @comp_old_value out

-- We need the old values to endure beyond batches
-- insert them into temp tables

IF (OBJECT_ID('tempdb..#advopt_old_value', 'U') IS NOT NULL)
BEGIN
	DROP TABLE #advopt_old_value
END

SELECT @advopt_old_value AS advopt_old_value
INTO #advopt_old_value

IF (OBJECT_ID('tempdb..#comp_old_value', 'U') IS NOT NULL)
BEGIN
	DROP TABLE #comp_old_value
END

SELECT @comp_old_value AS comp_old_value
INTO #comp_old_value
GO

-- disable the collector first
EXEC sp_syscollector_disable_collector
GO

---------------------------------------------------------------
-- Out-of-the-box SSIS folders for Data Collector
---------------------------------------------------------------

PRINT 'Creating SSIS folders...'
-- create 'Data Collector' folder under the root
IF(NOT EXISTS(SELECT * 
                FROM dbo.sysssispackagefolders
                WHERE folderid = '8877FE4B-A938-4a51-84B9-C5BDAD74B0AD'))
BEGIN
    EXEC dbo.sp_ssis_addfolder
        @parentfolderid = '00000000-0000-0000-0000-000000000000',
        @name = 'Data Collector',
        @folderid = '8877FE4B-A938-4a51-84B9-C5BDAD74B0AD'
END
GO
-- create 'Generated' folder under 'Data Collector'
IF(NOT EXISTS(SELECT * 
                FROM dbo.sysssispackagefolders
                WHERE folderid = '39163C42-602B-42C9-B4F7-1843614F9625'))
BEGIN
    EXEC dbo.sp_ssis_addfolder
        @parentfolderid = '8877FE4B-A938-4a51-84B9-C5BDAD74B0AD',
        @name = 'Generated',
        @folderid = '39163C42-602B-42c9-B4F7-1843614F9625'
END
GO

---------------------------------------------------------------
-- Loading instmdw.sql
---------------------------------------------------------------

-- a data collector table to store BLOB
IF (OBJECT_ID(N'[dbo].[syscollector_blobs_internal]', 'U') IS NULL)
BEGIN
    PRINT 'Creating table [dbo].[syscollector_blobs_internal]...'
    CREATE TABLE [dbo].[syscollector_blobs_internal] (
        parameter_name                nvarchar(128) NOT NULL,
        parameter_value               varbinary(max) NOT NULL,
        CONSTRAINT [PK_syscollector_blobs_internal_paremeter_name] PRIMARY KEY CLUSTERED (parameter_name ASC)
        )
END
GO

-- an SP to read a parameter value from the syscollector BLOB table
-- this stored procedure is called by the wizard to retrieve the instmdw.sql when setting up MDW
IF (NOT OBJECT_ID('[dbo].[sp_syscollector_get_instmdw]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_get_instmdw]'
    DROP PROCEDURE [dbo].[sp_syscollector_get_instmdw]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_get_instmdw]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_get_instmdw]
AS
BEGIN
    -- only dc_admin and dbo can setup MDW
    IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14712, -1, -1) WITH LOG
        RETURN(1) -- Failure
    END

    -- if the script has not been loaded, load it now
    IF (NOT EXISTS(SELECT parameter_name 
                   FROM syscollector_blobs_internal
                   WHERE parameter_name = N'InstMDWScript'))
    BEGIN
        EXECUTE sp_syscollector_upload_instmdw
    END
               
    SELECT cast(parameter_value as nvarchar(max)) 
    FROM syscollector_blobs_internal
    WHERE parameter_name = N'InstMDWScript'
END
GO

-- the script that would upload or update instmdw.sql
-- this is used when a hotfix, service pack, or upgrade is issued in instmdw.sql
-- user can specify the path to the new instmdw.sql to be installed or updated to.
IF (NOT OBJECT_ID('[dbo].[sp_syscollector_upload_instmdw]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_upload_instmdw]'
    DROP PROCEDURE [dbo].[sp_syscollector_upload_instmdw]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_upload_instmdw]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_upload_instmdw]
    @installpath              nvarchar(2048) = NULL
AS
BEGIN
    -- only dc_admin and dbo can setup MDW
    IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14712, -1, -1) WITH LOG
        RETURN(1) -- Failure
    END

    IF (@installpath IS NULL)
    BEGIN
        EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @installpath OUTPUT;
    
        IF RIGHT(@installpath, 1) != N'\' set @installpath = @installpath + N'\'
        SET @installpath  = @installpath + N'Install\'
    END

    DECLARE @filename nvarchar(2048);
    SET @filename = @installpath + N'instmdw.sql'
    PRINT 'Uploading instmdw.sql from disk: ' + @filename

    CREATE TABLE #bulkuploadinstmdwscript
        (
            [instmdwscript] nvarchar(max) NOT NULL
        );


    DECLARE @stmt_bulkinsert nvarchar(2048);
    SET @stmt_bulkinsert = N'
    BULK INSERT #bulkuploadinstmdwscript
    FROM ' 
        -- Escape any embedded single quotes (we can't use QUOTENAME here b/c it can't handle strings > 128 chars)
        + '''' + REPLACE(@filename COLLATE database_default, N'''', N'''''') + '''
    WITH
        (
            DATAFILETYPE = ''char'',
            FIELDTERMINATOR = ''dc:stub:ft'',
            ROWTERMINATOR = ''dc:stub:rt'',
            CODEPAGE = ''RAW''
        );
    ';

    EXECUTE sp_executesql @stmt_bulkinsert;

    DECLARE @bytesLoaded int
    SELECT @bytesLoaded = ISNULL (DATALENGTH ([instmdwscript]), 0) FROM #bulkuploadinstmdwscript
    PRINT 'Loaded ' + CONVERT (nvarchar, @bytesLoaded)
            + ' bytes from ' + '''' + REPLACE(@filename COLLATE database_default, N'''', N'''''') + ''''

    DECLARE @scriptdata varbinary(max)
    SELECT @scriptdata = convert(varbinary(max), [instmdwscript]) FROM #bulkuploadinstmdwscript;

    IF (EXISTS(SELECT * FROM [dbo].[syscollector_blobs_internal]
        WHERE parameter_name = N'InstMDWScript'))
    BEGIN
        UPDATE [dbo].[syscollector_blobs_internal]
        SET parameter_value = @scriptdata
        WHERE parameter_name = N'InstMDWScript'
    END
    ELSE
    BEGIN
        INSERT INTO [dbo].[syscollector_blobs_internal] (
            parameter_name, 
            parameter_value
        )
        VALUES
        (
            N'InstMDWScript',
            @scriptdata
        )
    END

    DROP TABLE #bulkuploadinstmdwscript
END
GO


---------------------------------------------------------------
-- Out-of-the-box data collector packages - loading SSIS packages
---------------------------------------------------------------

CREATE PROCEDURE #syscollector_upload_package_from_file 
    @filename nvarchar(2048),
    @packagename sysname,
    @packageid uniqueidentifier,
    @versionid uniqueidentifier
AS
BEGIN
    RAISERROR ('Uploading data collector package from disk: %s', 0, 1, @filename) WITH NOWAIT;

    CREATE TABLE #bulkpackage
        (
            [packagexml] xml NOT NULL
        );

    DECLARE @stmt_bulkinsert nvarchar(2048);
    SET @stmt_bulkinsert = N'
    BULK INSERT #bulkpackage
    FROM ' 
    -- Escape any embedded single quotes (we can't use QUOTENAME here b/c it can't handle strings > 128 chars)
    + '''' + REPLACE(@filename COLLATE database_default, N'''', N'''''') + '''
    WITH
        (
            DATAFILETYPE = ''char'',
            FIELDTERMINATOR = ''dc:stub:ft'',
            ROWTERMINATOR = ''dc:stub:rt'',
            CODEPAGE = ''RAW''
        );
    ';

    EXECUTE sp_executesql @stmt_bulkinsert;

    DECLARE @bytesLoaded int
    SELECT @bytesLoaded = ISNULL (DATALENGTH ([packagexml]), 0) FROM #bulkpackage
    PRINT 'Loaded ' + CONVERT (varchar, @bytesLoaded)
            + ' bytes from ' 
            + '''' + REPLACE(@filename COLLATE database_default, N'''', N'''''') + ''''

    DECLARE @packagebin varbinary(max);
    SELECT @packagebin = convert(varbinary(max),[packagexml]) FROM #bulkpackage;

    DECLARE @loadtime datetime;
    SET @loadtime = getdate();

    DROP TABLE #bulkpackage

    EXECUTE sp_ssis_putpackage
            @name = @packagename
        ,    @id = @packageid
        ,    @description = N'System Data Collector Package'
        ,    @createdate = @loadtime
        ,    @folderid = '8877FE4B-A938-4a51-84B9-C5BDAD74B0AD'
        ,    @packagedata = @packagebin
        ,    @packageformat = 1
        ,    @packagetype = 5 -- DTSPKT_DTSDESIGNER100
        ,    @vermajor = 1
        ,    @verminor = 0
        ,    @verbuild = 0
        ,    @vercomments = N''
        ,    @verid = @versionid
    ;
END;
GO

CREATE PROCEDURE #syscollector_upload_package 
    @packagename sysname,
    @packageid uniqueidentifier,
    @versionid uniqueidentifier
AS
BEGIN
    DECLARE @installpath nvarchar(2048);
    EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @installpath OUTPUT;
    IF RIGHT(@installpath,1) != N'\' set @installpath = @installpath + N'\'
    SET @installpath  = @installpath + N'Install\'

    DECLARE @filename nvarchar(2048);
    SET @filename = @installpath + @packagename + N'.dtsx'
    RAISERROR ('Uploading data collector package from disk: %s', 0, 1, @filename) WITH NOWAIT;

    EXEC #syscollector_upload_package_from_file @filename=@filename, @packagename=@packagename, @packageid=@packageid, @versionid=@versionid;    
END;
GO

--
-- Data Collector placeholder comment, dont move or remove.
-- 875f7de1-9e83-4be1-8b96-2df4a8533e88
--

-- Load SSIS packages needed by collector types

-- Temporarily enable the 'Agent XPs' config option so that sp_ssis_putpackage can 
-- succeed when SQLAgent is stopped. 

EXECUTE #syscollector_upload_package 
        @packagename='SqlTraceCollect'
    ,    @packageid='0E149FC9-1046-4DE6-98BF-4B22ED6F6C42'
    ,    @versionid='244F0904-5CC6-49B8-AE90-905AEEA8BAF3';

EXECUTE #syscollector_upload_package 
        @packagename='SqlTraceUpload'
    ,    @packageid='F389A8E6-5A17-4056-ABFD-C8B823F2092E'
    ,    @versionid='2D32AB4C-9929-4A85-A94E-7A01D8F40016';

EXECUTE #syscollector_upload_package 
        @packagename='TSQLQueryCollect'
    ,    @packageid='292B1476-0F46-4490-A9B7-6DB724DE3C0B'
    ,    @versionid='E24C6D00-94C6-457B-BED4-1F9F018F3273';

EXECUTE #syscollector_upload_package 
        @packagename='TSQLQueryUpload'
    ,    @packageid='6EB73801-39CF-489C-B682-497350C939F0'
    ,    @versionid='DA1210BC-C31B-43C6-B255-D8DDEB288CA1';

EXECUTE #syscollector_upload_package 
        @packagename='PerfCountersCollect'
    ,    @packageid='C2EAABC1-5BF3-4127-BEB3-26E94D026E7D'
    ,    @versionid='09A5B959-21B3-44E1-A37F-4A62BE5D6244';

EXECUTE #syscollector_upload_package 
        @packagename='PerfCountersUpload'
    ,    @packageid='08D854CB-0D45-4E96-92C6-227A5DCD7066'
    ,    @versionid='22A676DD-2025-493A-AD6B-C0186ABD556F';

EXECUTE #syscollector_upload_package 
        @packagename='QueryActivityCollect'
    ,    @packageid='0B68FC9D-23DC-48F3-A937-90A0A8943D0E'
    ,    @versionid='75A3A143-2059-433B-A11C-C8E0C80A83CF';

EXECUTE #syscollector_upload_package 
        @packagename='QueryActivityUpload'
    ,    @packageid='833DB628-8E19-47A3-92C5-FB1779B52E76'
    ,    @versionid='B1D79132-C6E6-46AA-8B14-E0AE4C4BA7BB';
GO

-- Cleanup the temp stored proc that we used to upload the SSIS packages
DROP PROCEDURE #syscollector_upload_package_from_file
DROP PROCEDURE #syscollector_upload_package
GO

---------------------------------------------------------------
-- Out-of-the-box collector type objects - definition for types
---------------------------------------------------------------

PRINT 'Creating or updating Collection Types...'
GO
-- Performance counters collector type
DECLARE @collector_type_uid uniqueidentifier
DECLARE @name sysname
DECLARE @parameter_schema xml
DECLARE @parameter_formatter xml
DECLARE @collection_package_id uniqueidentifier
DECLARE @upload_package_id uniqueidentifier

SET @collector_type_uid = '294605dd-21de-40b2-b20f-f3e170ea1ec3'
SET @name = 'Performance Counters Collector Type'
SET @parameter_schema =
		'<?xml version="1.0" encoding="utf-8"?>
			<xs:schema targetNamespace="DataCollectorType" xmlns:xs="http://www.w3.org/2001/XMLSchema">
			 <xs:element name="PerformanceCountersCollector">
			  <xs:complexType>
				  <xs:sequence>
					  <xs:element minOccurs="0" maxOccurs="unbounded" name="PerformanceCounters">
						  <xs:complexType>
							  <xs:attribute name="Objects" type="xs:string" use="required" />
							  <xs:attribute name="Counters" type="xs:string" use="required" />
							  <xs:attribute name="Instances" type="xs:string" use="optional" />
						  </xs:complexType>
					  </xs:element>
				  </xs:sequence>
			  	  <xs:attribute name="StoreLocalizedCounterNames" type="xs:boolean" use="optional" default="false" />
			  </xs:complexType>
			</xs:element>
			</xs:schema>
		'
SET @parameter_formatter = 
        N'<xsl:stylesheet 
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            version="1.0"
            xmlns:z="#RowsetSchema"
            >
        <xsl:template match="/PerformanceCountersCollector">
            <HTML>
            <HEAD>
            <TITLE></TITLE>
            </HEAD>
            <BODY>
            <UL>
            <xsl:apply-templates select="PerformanceCounters"/>
            </UL>
            <HR/>
            </BODY>
            </HTML>
        </xsl:template>
        <xsl:template match="PerformanceCounters">
            <LI>
            \<xsl:value-of select="@Objects"/>
            <xsl:if test="@Instances">(<xsl:value-of select="@Instances"/>)</xsl:if>
            \<xsl:value-of select="@Counters"/>
            </LI>
        </xsl:template>
        </xsl:stylesheet>'
SET @collection_package_id = 'C2EAABC1-5BF3-4127-BEB3-26E94D026E7D'
SET @upload_package_id = '08D854CB-0D45-4E96-92C6-227A5DCD7066'

IF(EXISTS (SELECT * FROM dbo.syscollector_collector_types 
            WHERE collector_type_uid = @collector_type_uid))
BEGIN
    PRINT 'Updating Performance counters collector type'
    EXEC sp_syscollector_update_collector_type 
            @collector_type_uid = @collector_type_uid,
            @name = @name,
            @parameter_schema = @parameter_schema,
            @parameter_formatter = @parameter_formatter,
            @collection_package_id = @collection_package_id,
            @upload_package_id = @upload_package_id
END
ELSE
BEGIN
    PRINT 'Creating Performance Counters collector type'
    EXEC sp_syscollector_create_collector_type
            @collector_type_uid = @collector_type_uid,
            @name = @name,
            @parameter_schema = @parameter_schema,
            @parameter_formatter = @parameter_formatter,
            @collection_package_id = @collection_package_id,
            @upload_package_id = @upload_package_id
END

UPDATE syscollector_collector_types 
SET is_system = 1
WHERE collector_type_uid = @collector_type_uid
GO

-- TSQL query collector type
DECLARE @collector_type_uid uniqueidentifier
DECLARE @name sysname
DECLARE @parameter_schema xml
DECLARE @parameter_formatter xml
DECLARE @collection_package_id uniqueidentifier
DECLARE @upload_package_id uniqueidentifier

SET @collector_type_uid = '302E93D1-3424-4be7-AA8E-84813ECF2419'
SET @name = 'Generic T-SQL Query Collector Type'
SET @parameter_schema = '<?xml version="1.0" encoding="utf-8"?>
        <xs:schema targetNamespace="DataCollectorType" xmlns:xs="http://www.w3.org/2001/XMLSchema">
         <xs:element name="TSQLQueryCollector">
          <xs:complexType>
           <xs:sequence>
            <xs:element name="Query" minOccurs="1" maxOccurs="unbounded">
             <xs:complexType>
              <xs:sequence>
               <xs:element name="Value" type="xs:string" />
               <xs:element name="OutputTable" type="xs:string" />
              </xs:sequence>
             </xs:complexType>
            </xs:element>
            <xs:element name="Databases" minOccurs="0" maxOccurs="1">
             <xs:complexType>
              <xs:sequence>
               <xs:element name="Database" minOccurs="0" maxOccurs="unbounded" type="xs:string" />
              </xs:sequence>
              <xs:attribute name="UseSystemDatabases" type="xs:boolean" use="optional" />
              <xs:attribute name="UseUserDatabases" type="xs:boolean" use="optional" />
             </xs:complexType>
            </xs:element>
           </xs:sequence>
          </xs:complexType>
         </xs:element>
        </xs:schema>'
SET @parameter_formatter = 
        N'<xsl:stylesheet 
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            version="1.0"
            xmlns:z="#RowsetSchema"
            >
        <xsl:template match="/TSQLQueryCollector">
            <HTML>
            <HEAD>
            <TITLE></TITLE>
            </HEAD>
            <BODY>
            <xsl:apply-templates select="Query"/>
            </BODY>
            </HTML>
        </xsl:template>
        <xsl:template match="Query">
            <I>
            <xsl:value-of select="OutputTable"/>
            </I> <BR/>
            <PRE>
            <xsl:value-of select="Value"/>
            </PRE>
            <HR/>
        </xsl:template>
        </xsl:stylesheet>'
SET @collection_package_id = '292B1476-0F46-4490-A9B7-6DB724DE3C0B'
SET @upload_package_id = '6EB73801-39CF-489C-B682-497350C939F0'

IF(EXISTS (SELECT * FROM dbo.syscollector_collector_types 
            WHERE collector_type_uid = @collector_type_uid))
BEGIN
    PRINT 'Updating TSQL Query collector type'
    EXEC sp_syscollector_update_collector_type 
            @collector_type_uid = @collector_type_uid,
            @name = @name,
            @parameter_schema = @parameter_schema,
            @parameter_formatter = @parameter_formatter,
            @collection_package_id = @collection_package_id,
            @upload_package_id = @upload_package_id
END
ELSE
BEGIN
    PRINT 'Creating T-SQL Query collector type'
    EXEC sp_syscollector_create_collector_type
            @collector_type_uid = @collector_type_uid,
            @name = @name,
            @parameter_schema = @parameter_schema,
            @parameter_formatter = @parameter_formatter,
            @collection_package_id = @collection_package_id,
            @upload_package_id = @upload_package_id
END

-- mark the collector type as system
UPDATE syscollector_collector_types 
SET is_system = 1
WHERE collector_type_uid = @collector_type_uid
GO

---------------------------------------------------------------
-- Database objects for TSQL query collector type 
---------------------------------------------------------------

IF (OBJECT_ID(N'[dbo].[syscollector_tsql_query_collector]', 'U') IS NULL)
BEGIN
    PRINT 'Creating table [dbo].[syscollector_tsql_query_collector]...'
    CREATE TABLE [dbo].[syscollector_tsql_query_collector] (
        collection_set_uid            uniqueidentifier NOT NULL,
        collection_set_id            int NOT NULL,
        collection_item_id            int NOT NULL,
        collection_package_id        uniqueidentifier NOT NULL,
        upload_package_id            uniqueidentifier NOT NULL,
        )
    ALTER TABLE syscollector_tsql_query_collector
        ADD CONSTRAINT [FK_syscollector_tsql_query_collector_syscollector_collection_items_internal] FOREIGN KEY(collection_set_id, collection_item_id)
        REFERENCES syscollector_collection_items_internal (collection_set_id, collection_item_id) ON DELETE CASCADE
END
GO

IF (OBJECT_ID('dbo.syscollector_collection_item_parameter_update_trigger', 'TR') IS NOT NULL)
BEGIN
    PRINT 'Dropping trigger [dbo].[syscollector_collection_item_parameter_update_trigger] on [dbo].[syscollector_collection_items_internal]'
    DROP TRIGGER [dbo].[syscollector_collection_item_parameter_update_trigger]
END
GO

PRINT 'Creating trigger [dbo].[syscollector_collection_item_parameter_update_trigger] on [dbo].[syscollector_collection_items_internal]'
GO
CREATE TRIGGER [dbo].[syscollector_collection_item_parameter_update_trigger] on [dbo].[syscollector_collection_items_internal]
FOR UPDATE
AS
BEGIN
    DECLARE @collection_set_id int
    DECLARE @collection_item_id int

    -- remove the TSQL query collection item that was updated so packages will be regenerated 
    -- base on the new parameters
    IF (NOT UPDATE (parameters))
       RETURN

    -- clean up the SSIS packages that are left behind
    DECLARE inserted_cursor CURSOR LOCAL FOR
        SELECT collection_set_id, collection_item_id
        FROM inserted
    
    OPEN inserted_cursor
    FETCH inserted_cursor INTO @collection_set_id, @collection_item_id

    WHILE @@FETCH_STATUS = 0
    BEGIN
        DELETE FROM dbo.syscollector_tsql_query_collector 
        WHERE collection_set_id = @collection_set_id
        AND collection_item_id = @collection_item_id

        FETCH inserted_cursor INTO @collection_set_id, @collection_item_id
    END

    CLOSE inserted_cursor
    DEALLOCATE inserted_cursor
END
GO

IF (OBJECT_ID('dbo.syscollector_tsql_query_collector_delete_trigger', 'TR') IS NOT NULL)
BEGIN
    PRINT 'Dropping trigger [dbo].[syscollector_tsql_query_collector_delete_trigger] on [dbo].[syscollector_tsql_query_collector]'
    DROP TRIGGER [dbo].[syscollector_tsql_query_collector_delete_trigger]
END
GO

PRINT 'Creating trigger [dbo].[syscollector_tsql_query_collector_delete_trigger] on [dbo].[syscollector_tsql_query_collector]'
GO
CREATE TRIGGER [dbo].[syscollector_tsql_query_collector_delete_trigger] on [dbo].[syscollector_tsql_query_collector]
FOR DELETE
AS
BEGIN
    -- remove the SSIS packages left behind when the collection item is deleted 
    DECLARE @collection_package_id uniqueidentifier
    DECLARE @collection_package_folderid uniqueidentifier
    DECLARE @collection_package_name sysname

    DECLARE @upload_package_id  uniqueidentifier
    DECLARE @upload_package_folderid  uniqueidentifier
    DECLARE @upload_package_name  sysname

    DECLARE deleted_cursor CURSOR LOCAL FOR
        SELECT collection_package_id, upload_package_id
        FROM deleted
    
    OPEN deleted_cursor
    FETCH deleted_cursor INTO @collection_package_id, @upload_package_id

    WHILE @@FETCH_STATUS = 0
    BEGIN
        SELECT 
            @collection_package_name = name,
            @collection_package_folderid = folderid
        FROM sysssispackages
        WHERE @collection_package_id = id

        SELECT 
            @upload_package_name = name,
            @upload_package_folderid = folderid
        FROM sysssispackages
        WHERE @upload_package_id = id

        EXEC dbo.sp_ssis_deletepackage
            @name = @collection_package_name,
            @folderid = @collection_package_folderid

        EXEC dbo.sp_ssis_deletepackage
            @name = @upload_package_name,
            @folderid = @upload_package_folderid

        FETCH deleted_cursor INTO @collection_package_id, @upload_package_id
    END

    CLOSE deleted_cursor
    DEALLOCATE deleted_cursor
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_create_tsql_query_collector]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_create_tsql_query_collector]...'
    DROP PROCEDURE [dbo].[sp_syscollector_create_tsql_query_collector]
END
GO

PRINT 'Creating procedure [dbo].[sp_syscollector_create_tsql_query_collector]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_create_tsql_query_collector]
    @collection_set_uid            uniqueidentifier,
    @collection_item_id            int,
    @collection_package_id        uniqueidentifier,
    @upload_package_id            uniqueidentifier
AS
BEGIN
    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND 
        NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND 
        NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_operator'' or ''dc_proxy')
        RETURN(1) -- Failure
    END

    DECLARE @errMsg VARCHAR(256)
    DECLARE @collection_set_id int
    SELECT @collection_set_id = s.collection_set_id
    FROM dbo.syscollector_collection_items i, dbo.syscollector_collection_sets s
    WHERE i.collection_item_id = @collection_item_id
    AND i.collector_type_uid = '302E93D1-3424-4be7-AA8E-84813ECF2419'
    AND s.collection_set_uid = @collection_set_uid

    -- Verify that the collection item exists of the correct type
    IF (@collection_set_id IS NULL)
    BEGIN        
        SELECT @errMsg = CONVERT(VARCHAR(36), @collection_set_uid) + ', ' + CONVERT(VARCHAR(36), @collection_item_id)
        RAISERROR(14262, -1, -1, '@collection_set_uid, @collection_item_id', @errMsg)
        RETURN(1)
    END

    -- Get the names and folder ids for the generated packages
    DECLARE @upload_package_name sysname
    DECLARE @upload_package_folder_id uniqueidentifier
    SELECT @upload_package_name = name, @upload_package_folder_id = folderid
    FROM sysssispackages
    WHERE id = @upload_package_id
    
    IF (@upload_package_name IS NULL) 
    BEGIN
        SELECT @errMsg = @upload_package_name + ', ' + CONVERT(VARCHAR(36), @upload_package_folder_id)
        RAISERROR(14262, -1, -1, '@upload_package_name, @upload_package_folder_id', @errMsg)
        RETURN(1)
    END

    DECLARE @collection_package_name sysname
    DECLARE @collection_package_folder_id uniqueidentifier
    SELECT @collection_package_name = name, @collection_package_folder_id = folderid
    FROM sysssispackages
    WHERE id = @collection_package_id
    
    IF (@collection_package_name IS NULL) 
    BEGIN
        SELECT @errMsg = @collection_package_name + ', ' + CONVERT(VARCHAR(36), @collection_package_folder_id)
        RAISERROR(14262, -1, -1, '@collection_package_name, @collection_package_folder_id', @errMsg)
        RETURN(1)
    END

    -- we need to allow dc_admin to delete these packages along with the collection set when 
    -- the set is deleted
    EXEC sp_ssis_setpackageroles @name = @upload_package_name, @folderid = @upload_package_folder_id, @readrole = NULL, @writerole = N'dc_admin'
    EXEC sp_ssis_setpackageroles @name = @collection_package_name, @folderid = @collection_package_folder_id, @readrole = NULL, @writerole = N'dc_admin'

    INSERT INTO [dbo].[syscollector_tsql_query_collector]
    (
        collection_set_uid,
        collection_set_id, 
        collection_item_id,
        collection_package_id,
        upload_package_id
    )
    VALUES
    (
        @collection_set_uid,
        @collection_set_id,
        @collection_item_id,
        @collection_package_id,
        @upload_package_id
    )
END
GO

IF (NOT OBJECT_ID('[dbo].[sp_syscollector_get_tsql_query_collector_package_ids]', 'P') IS NULL)
BEGIN
    PRINT 'Dropping procedure [dbo].[sp_syscollector_get_tsql_query_collector_package_ids]...'
    DROP PROCEDURE [dbo].[sp_syscollector_get_tsql_query_collector_package_ids]
END
GO

-- get and return the collection and upload package IDs
-- if they do not exist, return empty IDs
PRINT 'Creating procedure [dbo].[sp_syscollector_get_tsql_query_collector_package_ids]...'
GO
CREATE PROCEDURE [dbo].[sp_syscollector_get_tsql_query_collector_package_ids]
    @collection_set_uid            uniqueidentifier,
    @collection_item_id            int,
    @collection_package_id        uniqueidentifier OUTPUT,
    @upload_package_id            uniqueidentifier OUTPUT,
    @collection_package_name    sysname OUTPUT,
    @upload_package_name        sysname OUTPUT    
AS
BEGIN
    -- Security check (role membership)
    IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND 
        NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND
        NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1))
    BEGIN
        RAISERROR(14677, -1, -1, 'dc_operator'' or ''dc_proxy')
        RETURN(1) -- Failure
    END

    SELECT @collection_package_id = collection_package_id,
        @upload_package_id = upload_package_id
    FROM dbo.syscollector_tsql_query_collector
    WHERE @collection_item_id = collection_item_id 
      AND @collection_set_uid = collection_set_uid

    IF(@collection_package_id IS NOT NULL AND @upload_package_id IS NOT NULL)
    BEGIN
        SELECT @collection_package_name = name
        FROM dbo.sysssispackages
        WHERE @collection_package_id = id

        SELECT @upload_package_name = name
        FROM dbo.sysssispackages
        WHERE @upload_package_id = id
    END
END
GO

-- SQLTrace collector type
DECLARE @collector_type_uid uniqueidentifier
DECLARE @name sysname
DECLARE @parameter_schema xml
DECLARE @parameter_formatter xml
DECLARE @collection_package_id uniqueidentifier
DECLARE @upload_package_id uniqueidentifier

SET @collector_type_uid = '0E218CF8-ECB5-417B-B533-D851C0251271'
SET @name = 'Generic SQL Trace Collector Type'
SET @parameter_schema = '<?xml version="1.0" encoding="utf-8"?>
                        <xs:schema targetNamespace="DataCollectorType" xmlns:xs="http://www.w3.org/2001/XMLSchema">
                         <xs:element name="SqlTraceCollector">
                          <xs:complexType>
                           <xs:sequence>
                            <xs:element name="Events">
                             <xs:complexType>
                              <xs:sequence>
                               <xs:element minOccurs="0" maxOccurs="unbounded" name="EventType">
                                <xs:complexType>
                                 <xs:sequence>
                                  <xs:element maxOccurs="unbounded" name="Event">
                                   <xs:complexType>
                                    <xs:attribute name="id" type="xs:unsignedByte" use="required" />
                                    <xs:attribute name="name" type="xs:string" use="required" />
                                    <xs:attribute name="columnslist" type="xs:string" use="optional" />
                                   </xs:complexType>
                                  </xs:element>
                                 </xs:sequence>
                                 <xs:attribute name="id" type="xs:unsignedByte" use="optional" />
                                 <xs:attribute name="name" type="xs:string" use="required" />
                                </xs:complexType>
                               </xs:element>
                              </xs:sequence>
                             </xs:complexType>
                            </xs:element>
                            <xs:element name="Filters">
                             <xs:complexType>
                              <xs:sequence>
                               <xs:element name="Filter" minOccurs="0" maxOccurs="unbounded">
                                <xs:complexType>
                                 <xs:attribute name="columnid" type="xs:unsignedByte" use="required" />
                                 <xs:attribute name="columnname" type="xs:string" use="required" />
                                 <xs:attribute name="logical_operator" type="xs:string" use="required" />
                                 <xs:attribute name="comparison_operator" type="xs:string" use="required" />
                                 <xs:attribute name="value" type="xs:string" use="required" />
                                </xs:complexType>
                               </xs:element>
                              </xs:sequence>
                             </xs:complexType>
                            </xs:element>
                           </xs:sequence>
                           <xs:attribute name="use_default" type="xs:boolean" />
                          </xs:complexType>
                         </xs:element>
                        </xs:schema>'
SET @parameter_formatter = 
        N'<xsl:stylesheet 
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            version="1.0"
            xmlns:z="#RowsetSchema"
            >
        <xsl:template match="/SqlTraceCollector">
            <HTML>
            <HEAD>
            <TITLE></TITLE>
            </HEAD>
            <BODY>
            <xsl:apply-templates select="Events"/>
            <HR/>
            <xsl:apply-templates select="Filters"/>
            </BODY>
            </HTML>
        </xsl:template>
        <xsl:template match="Events">
            <xsl:apply-templates select="EventType"/>
            <BR/>
        </xsl:template>
        <xsl:template match="EventType">
            <I> <PRE> ID = <xsl:value-of select="@id"/> - <xsl:value-of select="@name"/> </PRE> </I> 
            <UL>
            <xsl:apply-templates select="Event"/>
            </UL>
        </xsl:template>
        <xsl:template match="Event">
            <LI>
            <PRE> ID = <xsl:value-of select="@id"/> - <xsl:value-of select="@name"/> </PRE>
            </LI>
        </xsl:template>
        <xsl:template match="Filters">
            <UL>
            <xsl:apply-templates select="Filter" />
            </UL>
        </xsl:template>
        <xsl:template match="Filter">
            <PRE> <xsl:value-of select="@logical_operator"/> - <xsl:value-of select="@columnname"/> - <xsl:value-of select="@comparison_operator"/> - <xsl:value-of select="@value"/> </PRE>
        </xsl:template>
        </xsl:stylesheet>'
SET @collection_package_id = '0E149FC9-1046-4DE6-98BF-4B22ED6F6C42'
SET @upload_package_id = 'F389A8E6-5A17-4056-ABFD-C8B823F2092E' 

IF(EXISTS (SELECT * FROM dbo.syscollector_collector_types 
            WHERE collector_type_uid = @collector_type_uid))
BEGIN
    PRINT 'Updating SQL Trace collector type'
    EXEC sp_syscollector_update_collector_type 
            @collector_type_uid = @collector_type_uid,
            @name = @name,
            @parameter_schema = @parameter_schema,
            @parameter_formatter = @parameter_formatter,
            @collection_package_id = @collection_package_id,
            @upload_package_id = @upload_package_id
END
ELSE
BEGIN
    PRINT 'Creating SQL Trace collector type'
    EXEC sp_syscollector_create_collector_type
            @collector_type_uid = @collector_type_uid,
            @name = @name,
            @parameter_schema = @parameter_schema,
            @parameter_formatter = @parameter_formatter,
            @collection_package_id = @collection_package_id,
            @upload_package_id = @upload_package_id
END

-- mark the collector type as system
UPDATE syscollector_collector_types 
SET is_system = 1
WHERE collector_type_uid = @collector_type_uid
GO

-- Query Activity Collector Type
DECLARE @collector_type_uid uniqueidentifier
DECLARE @name sysname
DECLARE @collection_package_id uniqueidentifier
DECLARE @upload_package_id uniqueidentifier

SET @collector_type_uid = '14AF3C12-38E6-4155-BD29-F33E7966BA23'
SET @name = 'Query Activity Collector Type'
SET @collection_package_id = '0B68FC9D-23DC-48F3-A937-90A0A8943D0E'
SET @upload_package_id = '833DB628-8E19-47A3-92C5-FB1779B52E76'

IF(EXISTS (SELECT * FROM dbo.syscollector_collector_types 
            WHERE collector_type_uid = @collector_type_uid))
BEGIN
    PRINT 'Updating Query Activity Collector Type'
    EXEC sp_syscollector_update_collector_type
        @collector_type_uid = @collector_type_uid,
        @name = @name,
        @parameter_schema = NULL,
        @parameter_formatter = NULL,
        @collection_package_id = @collection_package_id,
        @upload_package_id = @upload_package_id
END
ELSE
BEGIN
    PRINT 'Creating Query Activity Collector Type'
    EXEC sp_syscollector_create_collector_type
        @collector_type_uid = @collector_type_uid,
        @name = @name,
        @parameter_schema = NULL,
        @parameter_formatter = NULL,
        @collection_package_id = @collection_package_id,
        @upload_package_id = @upload_package_id
END

-- mark the collector type as system
UPDATE syscollector_collector_types
SET is_system = 1
WHERE collector_type_uid = @collector_type_uid
GO

---------------------------------------------------------------
-- Out-of-the-box collector objects - Generic schedules for system collection sets
---------------------------------------------------------------
PRINT 'Creating data collector schedules'

DECLARE @schedule_name sysname
SET @schedule_name = N'CollectorSchedule_Every_5min'
IF  NOT EXISTS (SELECT name FROM msdb.dbo.sysschedules_localserver_view WHERE name = @schedule_name)
BEGIN
    EXEC dbo.sp_add_schedule
        @schedule_name = @schedule_name,    -- Schedule name
        @freq_type = 4,                        -- Daily
        @freq_interval = 1,                    -- Recurs every 1 day
        @freq_subday_type = 0x4,            -- Frequency type is "minutes"
        @freq_subday_interval = 5            -- Occurs every 5 minutes
END

SET @schedule_name = N'CollectorSchedule_Every_10min'
IF  NOT EXISTS (SELECT name FROM msdb.dbo.sysschedules_localserver_view WHERE name = @schedule_name)
BEGIN
    EXEC dbo.sp_add_schedule
        @schedule_name = @schedule_name,    -- Schedule name
        @freq_type = 4,                        -- Daily
        @freq_interval = 1,                    -- Recurs every 1 day
        @freq_subday_type = 0x4,            -- Frequency type is "minutes"
        @freq_subday_interval = 10            -- Occurs every 10 minutes
END

SET @schedule_name = N'CollectorSchedule_Every_15min'
IF  NOT EXISTS (SELECT name FROM msdb.dbo.sysschedules_localserver_view WHERE name = @schedule_name)
BEGIN
    EXEC dbo.sp_add_schedule
        @schedule_name = @schedule_name,    -- Schedule name
        @freq_type = 4,                        -- Daily
        @freq_interval = 1,                    -- Recurs every 1 day
        @freq_subday_type = 0x4,            -- Frequency type is "minutes"
        @freq_subday_interval = 15            -- Occurs every 15 minutes
END

SET @schedule_name = N'CollectorSchedule_Every_30min'
IF  NOT EXISTS (SELECT name FROM msdb.dbo.sysschedules_localserver_view WHERE name = @schedule_name)
BEGIN
    EXEC dbo.sp_add_schedule
        @schedule_name = @schedule_name,    -- Schedule name
        @freq_type = 4,                        -- Daily
        @freq_interval = 1,                    -- Recurs every 1 day
        @freq_subday_type = 0x4,            -- Frequency type is "minutes"
        @freq_subday_interval = 30            -- Occurs every 30 minutes
END

SET @schedule_name = N'CollectorSchedule_Every_60min'
IF  NOT EXISTS (SELECT name FROM msdb.dbo.sysschedules_localserver_view WHERE name = @schedule_name)
BEGIN
    EXEC dbo.sp_add_schedule
        @schedule_name = @schedule_name,    -- Schedule name
        @freq_type = 4,                        -- Daily
        @freq_interval = 1,                    -- Recurs every 1 day
        @freq_subday_type = 0x4,            -- Frequency type is "minutes"
        @freq_subday_interval = 60            -- Occurs every 60 minutes
END

SET @schedule_name = N'CollectorSchedule_Every_6h'
IF  NOT EXISTS (SELECT name FROM msdb.dbo.sysschedules_localserver_view WHERE name = @schedule_name)
BEGIN
    EXEC dbo.sp_add_schedule
        @schedule_name = @schedule_name,    -- Schedule name
        @freq_type = 4,                        -- Daily
        @freq_interval = 1,                    -- Recurs every 1 day
        @freq_subday_type = 0x8,            -- Frequency type is "hours"
        @freq_subday_interval = 6            -- Occurs every 6 hours
END
GO




/**************************************************************/
/*                     BEGIN DAC SCRIPTS                     */
/**************************************************************/
-- Note: These should be located before the sysutility objects in instmsdb.sql, 
-- because some of the Utility objects depend on the DAC objects. 

/**********************************************************************/
/* DAC Functions                                                      */
/**********************************************************************/

IF (NOT OBJECT_ID ('[dbo].[fn_sysdac_is_dac_creator]', 'FN') IS NULL) 
BEGIN
    RAISERROR('Dropping Function [dbo].[fn_sysdac_is_dac_creator]', 0, 1) WITH NOWAIT;
    DROP FUNCTION [dbo].[fn_sysdac_is_dac_creator];
END;
GO

IF (NOT OBJECT_ID ('[dbo].[fn_sysdac_is_login_creator]', 'FN') IS NULL) 
BEGIN
    RAISERROR('Dropping Function [dbo].[fn_sysdac_is_login_creator]', 0, 1) WITH NOWAIT;
    DROP FUNCTION [dbo].[fn_sysdac_is_login_creator];
END;
GO

IF (NOT OBJECT_ID ('[dbo].[fn_sysdac_is_currentuser_sa]', 'FN') IS NULL) 
BEGIN
    RAISERROR('Dropping Function [dbo].[fn_sysdac_is_currentuser_sa]', 0, 1) WITH NOWAIT;
    DROP FUNCTION [dbo].[fn_sysdac_is_currentuser_sa];
END;
GO

IF (NOT OBJECT_ID ('[dbo].[fn_sysdac_get_currentusername]', 'FN') IS NULL) 
BEGIN
    RAISERROR('Dropping Function [dbo].[fn_sysdac_get_currentusername]', 0, 1) WITH NOWAIT;
    
    --
    -- Drop the constraints that reference this function
    --
    DECLARE @dac_constraint_name SYSNAME
    DECLARE @sql nvarchar(1000)

    -- Find the Default constraint system name on the DAC tables for created_by column. 
    -- If it's fn_sysdac_get_currentusername, drop this constraint since we are about to drop the function

    -- sysdac_instances_internal
    SELECT @dac_constraint_name=dc.name
    FROM sys.default_constraints dc
    JOIN sys.columns c 
	    ON dc.parent_object_id = c.object_id 
	    AND dc.parent_column_id = c.column_id
    WHERE dc.parent_object_id = object_id('[dbo].[sysdac_instances_internal]', 'U') 
      AND dc.definition ='([dbo].[fn_sysdac_get_currentusername]())' 
      AND c.name='created_by'

    IF @dac_constraint_name IS NOT NULL
    BEGIN
	    SELECT @sql = 'ALTER TABLE [dbo].[sysdac_instances_internal] DROP CONSTRAINT ' + QUOTENAME(@dac_constraint_name)
	    EXEC (@sql)
    END

    -- sysdac_history_internal    
    SELECT @dac_constraint_name=dc.name
    FROM sys.default_constraints dc
    JOIN sys.columns c 
	    ON dc.parent_object_id = c.object_id 
	    AND dc.parent_column_id = c.column_id
    WHERE dc.parent_object_id = object_id('[dbo].[sysdac_history_internal]', 'U') 
      AND dc.definition ='([dbo].[fn_sysdac_get_currentusername]())' 
      AND c.name='created_by'


    IF @dac_constraint_name IS NOT NULL
    BEGIN
	    SELECT @sql = 'ALTER TABLE [dbo].[sysdac_history_internal] DROP CONSTRAINT ' + QUOTENAME(@dac_constraint_name)
	    EXEC (@sql)
    END
	
    --
    -- Drop the function
    --
    DROP FUNCTION [dbo].[fn_sysdac_get_currentusername];

END;
GO

IF (NOT OBJECT_ID ('[dbo].[fn_sysdac_get_username]', 'FN') IS NULL) 
BEGIN
    RAISERROR('Dropping Function [dbo].[fn_sysdac_get_username]', 0, 1) WITH NOWAIT;
    DROP FUNCTION [dbo].[fn_sysdac_get_username];
END;
GO

/*
Scalar Function:  dbo.fn_sysdac_is_dac_creator
Returns a one (1) if the current user is allowed to create DACs

This is generalized in a function for reuse and maintenance
*/
RAISERROR('Creating Function [dbo].[fn_sysdac_is_dac_creator]...', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysdac_is_dac_creator]()
RETURNS int
BEGIN
    DECLARE @engineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int);
    DECLARE @isdaccreator int;

    -- Check the engine edition
    IF (@engineEdition = 5)
    BEGIN
        -- Sql Azure:
        --   is member of dbmanager or is superuser.

        SET @isdaccreator = COALESCE(IS_MEMBER('dbmanager'), 0) | 
            dbo.fn_sysdac_is_currentuser_sa()

    END ELSE
    BEGIN
        -- Standalone, default:
        --  is member of dbcreator

        /*
        We should only require CREATE ANY DATABASE but the database rename 
        step of creating a DAC requires that we have dbcreator.
    
        If that changes use the code below
    
        -- CREATE ANY DATABASE is what makes somebody a creator
        Set @isdaccreator = HAS_PERMS_BY_NAME(null, null, 'CREATE ANY DATABASE')
        */

        SET @isdaccreator = COALESCE(is_srvrolemember('dbcreator'), 0)
        
    END

    RETURN @isdaccreator;
END
GO

/*
Scalar Function:  dbo.fn_sysdac_is_login_creator
Returns a one (1) if the current user is allowed to create Logins

This is generalized in a function for reuse and maintenance
*/
RAISERROR('Creating Function [dbo].[fn_sysdac_is_login_creator]...', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysdac_is_login_creator]()
RETURNS int
BEGIN
    DECLARE @engineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int);
    DECLARE @islogincreator int;

    -- Check the engine edition
    IF (@engineEdition = 5)
    BEGIN
        -- Sql Azure:
        --   is member of loginmanager or is superuser.

        SET @islogincreator = COALESCE(IS_MEMBER('loginmanager'), 0) | 
            dbo.fn_sysdac_is_currentuser_sa()

    END ELSE
    BEGIN
        -- Standalone, default:
        --   has ALTER ANY LOGIN permision

        SET @islogincreator = HAS_PERMS_BY_NAME(null, null, 'ALTER ANY LOGIN')
        
    END

    RETURN @islogincreator;
END
GO

/*
Scalar Function:  dbo.fn_sysdac_is_currentuser_sa
Returns a one (1) if the current user is super user

This is generalized in a function for reuse and maintenance
*/
RAISERROR('Creating Function [dbo].[fn_sysdac_is_currentuser_sa]...', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysdac_is_currentuser_sa]()
RETURNS int
BEGIN
    DECLARE @engineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int);
    DECLARE @is_sa int;
    
    -- Check the engine edition
    IF (@engineEdition = 5)
    BEGIN
        -- Sql Azure:
    --   SID matches with the reserved offset.
        -- NOTE: We should get an inbuilt user function from Azure team instead of us querying based on SID.    
        SET @is_sa = 0
    
        IF((CONVERT(varchar(85), suser_sid(), 2) LIKE '0106000000000164%'))
            SET @is_sa = 1
            
    END ELSE
    BEGIN
        -- Standalone, default:
        --   is member of the serverrole 'sysadmin'
        SET @is_sa = COALESCE(is_srvrolemember('sysadmin'), 0)
    
    END
   
    RETURN @is_sa;
END
GO

/*
Scalar Function:  dbo.fn_sysdac_get_username
Returns the login name of the user with given sid

EXECUTE AS OWNER : This function needs access to sys.sql_logins

This is generalized in a function for reuse and maintenance
*/
RAISERROR('Creating Function [dbo].[fn_sysdac_get_username]...', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysdac_get_username](@user_sid varbinary(85))
RETURNS sysname
WITH EXECUTE AS OWNER
BEGIN	
    DECLARE @engineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int);
    DECLARE @current_user_name sysname;

    IF (@engineEdition = 5)
    BEGIN 
        --SQL Azure does not have syslogins. All the logins reside in sql_logins
        SELECT @current_user_name = name FROM sys.sql_logins where sid = @user_sid 
    END ELSE
    BEGIN
        --OnPremise engine has both sql and windows logins in syslogins
        SELECT @current_user_name = name FROM sys.syslogins where sid = @user_sid 
    END

    RETURN @current_user_name;
END
GO

/*
Scalar Function:  dbo.fn_sysdac_get_currentusername
Returns the login name of the current user

This is generalized in a function for reuse and maintenance
*/
RAISERROR('Creating Function [dbo].[fn_sysdac_get_currentusername]...', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysdac_get_currentusername]()
RETURNS sysname
BEGIN
    DECLARE @engineEdition int;
    DECLARE @current_user_name sysname;

    SET @engineEdition = CAST(SERVERPROPERTY('EngineEdition') AS int);

    IF (@engineEdition = 5)
    BEGIN 
        --SQL Azure does not have SUSER_SNAME. We need to look in sql_logins to get the user name.
        SELECT @current_user_name = dbo.fn_sysdac_get_username(SUSER_SID()) 
    END ELSE
    BEGIN
        --OnPremise engine has both sql and windows logins - We rely on SUSER_SNAME to find the current user name.
        SELECT @current_user_name = SUSER_SNAME()
    END

    RETURN @current_user_name;
END
GO

/**********************************************************************/
/* DAC TABLES                                                         */
/**********************************************************************/


/*
Table dbo.sysdac_instances_internal
Creates one row per one installed DacInstance
*/
IF (OBJECT_ID(N'dbo.sysdac_instances_internal', 'U') IS NULL)
BEGIN
RAISERROR('Creating table [dbo].[sysdac_instances_internal]...', 0, 1) WITH NOWAIT;
CREATE TABLE [dbo].[sysdac_instances_internal] 
(
    instance_id UniqueIdentifier NOT NULL, --unique DAC instance ID of the DacInstance
    instance_name sysname NOT NULL, --DacInstance name
    type_name sysname NOT NULL,  --DacType name
    type_version nvarchar(64) NOT NULL,--DacType version 
    description nvarchar(4000) NULL default (''),
    type_stream varbinary(max) NOT NULL, --Package Blob
    date_created datetime NOT NULL default GETDATE(),
    created_by sysname NOT NULL default dbo.fn_sysdac_get_currentusername(),
    
    CONSTRAINT [PK_sysdac_instances_internal]
        PRIMARY KEY CLUSTERED (instance_id), 
    
    --instance name is unique
    CONSTRAINT [UQ_sysdac_instances_internal]
        UNIQUE (instance_name),
);
END
GO

/*
Table dbo.sysdac_history_internal
Holds the information of deployment/deletion steps of DAC instances
*/
IF (OBJECT_ID(N'dbo.sysdac_history_internal', 'U') IS NULL)
BEGIN
RAISERROR('Creating table [dbo].[sysdac_history_internal]...', 0, 1) WITH NOWAIT;
CREATE TABLE [dbo].[sysdac_history_internal] 
(
    action_id int NOT NULL, --action identifier for each attempt of deploy/delete/detach/upgrade
    sequence_id int NOT NULL, --step # of deployment/deletion process
    instance_id UniqueIdentifier NOT NULL, --unique transaction ID = DAC package id
    action_type tinyint NOT NULL, --type of the action being performed 
    action_type_name AS CASE action_type
        WHEN 0 THEN 'deploy'
        WHEN 1 THEN 'create'
        WHEN 2 THEN 'rename'
        WHEN 3 THEN 'register'
        WHEN 4 THEN 'create objects'
        WHEN 5 THEN 'detach'
        WHEN 6 THEN 'delete'
        WHEN 7 THEN 'data transfer'
        WHEN 8 THEN 'disable constraints'
        WHEN 9 THEN 'move data'
        WHEN 10 THEN 'enable constraints'
        WHEN 11 THEN 'copy permissions'
        WHEN 12 THEN 'set readonly'
        WHEN 13 THEN 'upgrade'
        WHEN 14 THEN 'unregister'
        WHEN 15 THEN 'update registration'
        WHEN 16 THEN 'set readwrite'
        WHEN 17 THEN 'disconnect users'
        END,        
    dac_object_type tinyint NOT NULL, --type of the object affected 0-dacpac,1-login,2-database
    dac_object_type_name AS CASE dac_object_type
        WHEN 0 THEN 'dacpac'
        WHEN 1 THEN 'login'
        WHEN 2 THEN 'database'
        END,
    action_status tinyint NOT NULL, --pending/success/fail
        action_status_name AS CASE action_status
        WHEN 0 THEN 'not started'
        WHEN 1 THEN 'pending'
        WHEN 2 THEN 'success'
        WHEN 3 THEN 'fail'
        WHEN 4 THEN 'rollback'
        ELSE NULL
        END,
    required bit NULL, --indicates if the current step is needed to be committed for success of the action
    dac_object_name_pretran sysname, --name of the  object before the current step completes
    dac_object_name_posttran sysname, --name of the  object after the current step completes
    sqlscript nvarchar(max) NULL,  --sql script for performing the associated object
    payload varbinary(max) NULL, --payload data associated with an object. Mostly used for Dacpac
    comments varchar(max) NOT NULL, --comments associated with the operation
    error_string nvarchar(max) NULL,  --error while running associated sql
    created_by sysname NOT NULL default dbo.fn_sysdac_get_currentusername(),  --login of the user creating the step
    date_created datetime NOT NULL default GETDATE(), --datetime at which the step is created
    date_modified datetime NOT NULL default GETDATE() --datetime at while the step was last modified
    
    CONSTRAINT [PK_sysdac_history_internal]
    PRIMARY KEY CLUSTERED (action_id, sequence_id),
    
    CONSTRAINT [UQ_sysdac_history_internal]
        UNIQUE (action_id, dac_object_type, action_type, 
                dac_object_name_pretran, dac_object_name_posttran),    
);

CREATE NONCLUSTERED INDEX IX_sysdac_history_internal 
ON sysdac_history_internal(sequence_id, action_status) 
END
GO

--
-- Recreate the constraints that reference fn_sysdac_get_currentusername
--
-- sysdac_instances_internal
DECLARE @dac_constraint_name SYSNAME
DECLARE @sql nvarchar(1000)
DECLARE @tablename varchar(50)
SET @tablename = '[dbo].[sysdac_instances_internal]'
SELECT @dac_constraint_name=dc.name
FROM sys.default_constraints dc
JOIN sys.columns c 
	ON dc.parent_object_id = c.object_id 
	AND dc.parent_column_id = c.column_id
WHERE dc.parent_object_id = object_id(@tablename, 'U') 
  AND c.name='created_by'

IF @dac_constraint_name IS NULL
BEGIN
	SELECT @sql = 'ALTER TABLE ' + @tablename + ' ADD DEFAULT dbo.fn_sysdac_get_currentusername() FOR created_by';
	EXEC (@sql)
END
GO

-- sysdac_history_internal
DECLARE @dac_constraint_name SYSNAME
DECLARE @sql nvarchar(1000)
DECLARE @tablename varchar(50)
SET @tablename = '[dbo].[sysdac_history_internal]'
SELECT @dac_constraint_name=dc.name
FROM sys.default_constraints dc
JOIN sys.columns c 
	ON dc.parent_object_id = c.object_id 
	AND dc.parent_column_id = c.column_id
WHERE dc.parent_object_id = object_id(@tablename, 'U') 
  AND c.name='created_by'
  
IF @dac_constraint_name IS NULL
BEGIN
	SELECT @sql = 'ALTER TABLE ' + @tablename + ' ADD DEFAULT dbo.fn_sysdac_get_currentusername() FOR created_by';
	EXEC (@sql)
END
GO



/**********************************************************************/
/* DAC VIEWS                                                          */
/**********************************************************************/

/* Clean up any existing views */
IF(NOT OBJECT_ID(N'[dbo].[sysdac_instances]', 'V') IS NULL)
BEGIN
    RAISERROR ('Dropping view dbo.sysdac_instances', 0, 1) WITH NOWAIT;
    DROP VIEW dbo.sysdac_instances;
END;
GO

/*
View :  dbo.sysdac_instances
Returns one row for each package installed in the system. The list is filtered
based on the callers permisions.
sysadmin: sees all information for all instances
dbo's: sees all information for dacs in databases they own, limited information for all other instances
public: sees limited information for all packges
*/
RAISERROR('Creating View [dbo].[sysdac_instances]...', 0, 1) WITH NOWAIT;
GO
CREATE VIEW [dbo].[sysdac_instances]
AS
    SELECT
        -- this must be locked down because we use instance_id visability as a security gate
        case 
            when (dbo.fn_sysdac_is_currentuser_sa() = 1) then dac_instances.instance_id
            when sd.owner_sid = SUSER_SID() then dac_instances.instance_id
            else NULL
        end as instance_id,
        dac_instances.instance_name,
        dac_instances.type_name,
        dac_instances.type_version,
        dac_instances.description,
        case 
            when (dbo.fn_sysdac_is_currentuser_sa() = 1) then dac_instances.type_stream
            when sd.owner_sid = SUSER_SID() then dac_instances.type_stream
            else NULL
        end as type_stream,
        dac_instances.date_created,
        dac_instances.created_by,
        dac_instances.instance_name as database_name
    FROM sysdac_instances_internal dac_instances
    LEFT JOIN sys.databases sd
		ON dac_instances.instance_name = sd.name
GO

/**********************************************************************/
/* DAC Stored Procedures                                              */
/**********************************************************************/

/*Drop existing Stored Procedures*/
IF (NOT OBJECT_ID ('dbo.sp_sysdac_add_history_entry', 'P') IS NULL)
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_add_history_entry', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_add_history_entry; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_add_instance', 'P') IS NULL)
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_add_instance', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_add_instance; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_update_instance', 'P') IS NULL)
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_update_instance', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_update_instance; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_upgrade_instance', 'P') IS NULL)
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_upgrade_instance', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_upgrade_instance; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_update_history_entry', 'P') IS NULL) 
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_update_history_entry', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_update_history_entry; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_delete_instance', 'P') IS NULL) 
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_delete_instance', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_delete_instance; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_rename_database', 'P') IS NULL) 
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_rename_database', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_rename_database; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_drop_database', 'P') IS NULL) 
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_drop_database', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_drop_database; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_drop_login', 'P') IS NULL) 
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_drop_login', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_drop_login; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_ensure_dac_creator', 'P') IS NULL) 
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_ensure_dac_creator', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_ensure_dac_creator; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_rollback_all_pending_objects', 'P') IS NULL)
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_rollback_all_pending_objects', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_rollback_all_pending_objects; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_rollback_pending_object', 'P') IS NULL)
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_rollback_pending_object', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_rollback_pending_object; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_setreadonly_database', 'P') IS NULL)
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_setreadonly_database', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_setreadonly_database; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_resolve_pending_entry', 'P') IS NULL)
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_resolve_pending_entry', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_resolve_pending_entry; 
END;
GO

IF (NOT OBJECT_ID ('dbo.sp_sysdac_rollback_committed_step', 'P') IS NULL)
BEGIN
    RAISERROR('Dropping Stored Procedure sp_sysdac_rollback_committed_step', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysdac_rollback_committed_step; 
END;
GO

/*
Procedure [sp_sysdac_ensure_dac_creator]
This sproc checks the caller is a dac creator and raise's an error if not
*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_ensure_dac_creator]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE dbo.sp_sysdac_ensure_dac_creator
as
BEGIN
    -- only users that can create a dac can add parts
    IF (dbo.fn_sysdac_is_dac_creator() != 1)
    BEGIN
        RAISERROR(36010, -1, -1);
        RETURN(1); -- failure
    END
END
go

/*
Procedure [sp_sysdac_add_instance]
This proc creates a new DacInstance in dbo.sysdac_instances_internal table
 
Parameters: 
    @type_name - Name of the Dac type
    @instance_id - Guid identification of a Dac instance
    @instance_name - Name of the Dac Instance
    @type_version - Version of the Dac type
    @description - Dac type description
    @type_stream - package content of the dac type

*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_add_instance]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysdac_add_instance]  
    @type_name sysname,
    @instance_id UniqueIdentifier = NULL,            
    @instance_name sysname,
    @type_version NVARCHAR(64) = NULL,
    @description nvarchar(4000) = N'',
    @type_stream varbinary(max)
AS  
SET NOCOUNT ON;
BEGIN  
    DECLARE @retval INT  

    DECLARE @null_column sysname    
    SET @null_column = NULL

    IF (@type_name IS NULL OR @type_name = N'')
        SET @null_column = '@type_name'
    ELSE IF (@instance_name IS NULL OR @instance_name = N'')
        SET @null_column = '@instance_name'
    ELSE IF (@instance_id IS NULL )
        SET @null_column = '@instance_id'
    ELSE IF( @type_version = N'')
        SET @null_column = '@type_version'
    ELSE IF( @type_stream IS NULL)
        SET @null_column = '@type_stream'
      

    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysdac_add_instance')
        RETURN(1)
    END

    -- only users that can create a dac can add instances
    if (dbo.fn_sysdac_is_dac_creator() != 1)
    BEGIN
        RAISERROR(36010, -1, -1);
        RETURN(1); -- failure
    END
    
    --instance_name is unique
    IF EXISTS (SELECT * FROM dbo.sysdac_instances_internal WHERE instance_name = @instance_name) 
    BEGIN
        RAISERROR(36001, -1, -1, 'DacInstance', @instance_name)
        RETURN(1)
    END

    --Ensure that the database being referred exists
    IF NOT EXISTS (SELECT * from sys.sysdatabases WHERE name = @instance_name)
    BEGIN
        RAISERROR(36005, -1, -1, @instance_name)
        RETURN(1)
    END
  
    INSERT INTO [dbo].[sysdac_instances_internal]
        (instance_id, type_name, instance_name, type_version, description, type_stream)
    VALUES
        (@instance_id, @type_name, @instance_name, @type_version, @description, @type_stream)

    SELECT @retval = @@error
    RETURN(@retval)
END
GO

/*
Procedure [sp_sysdac_update_instance]
This proc updates an existing DacInstance in dbo.sysdac_instances_internal table

Parameters: 
    @type_name - Name of the Dac type. If NULL, then type_name will not be updated.
    @type_version - Version of the Dac type
    @instance_name - Name of the Dac Instance. If NULL, then instance_name will not be updated.
    @instance_id - Guid identification of a Dac instance
    @description - Dac type description
    @type_stream - package content of the dac type

*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_update_instance]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysdac_update_instance]  
    @type_name sysname,
    @instance_id UniqueIdentifier = NULL,            
    @instance_name sysname,
    @type_version NVARCHAR(64) = NULL,
    @description nvarchar(4000) = N'',
    @type_stream varbinary(max)
AS  
SET NOCOUNT ON;
BEGIN  
    DECLARE @retval INT  

    DECLARE @null_column sysname    
    SET @null_column = NULL

   IF (@type_name = N'')
        SET @null_column = '@type_name'
    ELSE IF (@instance_name = N'')
        SET @null_column = '@instance_name'
    ELSE IF (@instance_id IS NULL )
        SET @null_column = '@instance_id'
    ELSE IF( @type_version = N'')
        SET @null_column = '@type_version'
    ELSE IF( @type_stream IS NULL)
        SET @null_column = '@type_stream'
      
    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysdac_add_instance')
        RETURN(1)
    END

    -- Ensure that the package being referred to exists by using the package view. We only continue if we can see 
    -- the specified package. The package will only be visible if we are the associated dbo or sysadmin and it exists
    IF NOT EXISTS (SELECT * from dbo.sysdac_instances WHERE instance_id = @instance_id)
    BEGIN
        RAISERROR(36004, -1, -1)
        RETURN(1)
    END
    
    UPDATE sysdac_instances_internal SET
        type_name = ISNULL(@type_name, type_name),
        instance_name = ISNULL(@instance_name, instance_name),
		type_version = @type_version,
		description = @description, 
		type_stream = @type_stream
    WHERE instance_id = @instance_id 
    
    SELECT @retval = @@error
    RETURN(@retval)
END
GO

/*
Procedure [sp_sysdac_add_history_entry]
This proc creates a new row in dbo.sysdac_history_internal table

Parameters: 
    @sequence_id --step # of deployment/deletion process
    @instance_id --unique transaction ID = DAC package id
    @part_name --name of the dacpart corresponding to this part
    @action_type --type of the action being performed 0-deploy 1-detach 2-delete
    @dac_object_type --type of the object affected 0-dacpac,1-login,2-database
    @required -- indicates if the steps is needed(value of 1) to be committed for the success of the action 
    @dac_object_name_pretran --name of the  object before the current step completes
    @dac_object_name_posttran --name of the  object after the current step completes
    @sqlscript --sql script for performing the associated object
    @payload --payload data associated with an object. Mostly used for Dacpac
    @comments --comments associated with the history
    @error_string --error while running associated sql
    @action_id --action identifier for each attempt of deploy/delete/detach/upgrade
*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_add_history_entry]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysdac_add_history_entry]  
    @sequence_id int,
    @instance_id UniqueIdentifier = NULL,
    @action_type tinyint = NULL,
    @action_status tinyint = NULL,
    @dac_object_type tinyint = NULL,
    @required bit = NULL,
    @dac_object_name_pretran sysname = N'',
    @dac_object_name_posttran sysname = N'',
    @sqlscript nvarchar(max) = N'',
    @payload varbinary(max) = NULL,
    @comments varchar(max) = N'',
    @error_string nvarchar(max) = N'',
    @action_id int = NULL OUTPUT
AS  
SET NOCOUNT ON;
BEGIN  
    DECLARE @retval INT  

    DECLARE @null_column sysname    
    SET @null_column = NULL

    IF (@instance_id IS NULL)
        SET @null_column = '@instance_id'
    ELSE IF (@action_type IS NULL)
        SET @null_column = '@action_type'
    ELSE IF (@action_status IS NULL)
        SET @null_column = '@action_status'
    ELSE IF (@dac_object_type IS NULL)
        SET @null_column = '@dac_object_type'
    ELSE IF (@required IS NULL)
        SET @null_column = '@required'

    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysdac_add_history_entry')
        RETURN(1)
    END

    -- comments is optional. make sure it is non-null
    IF (@comments IS NULL)
    BEGIN
        SET @comments = N''
    END

    -- INSTALL service: only users that can create a dac can add history entries
    IF (@action_type <= 4)
    BEGIN
        IF (dbo.fn_sysdac_is_dac_creator() != 1)
        BEGIN
            RAISERROR(36010, -1, -1);
            RETURN(1); -- failure
        END
    END
    -- UNINSTALL service : Ensure that the package being referred to exists by using the package view. We only continue if we can see 
    -- the specified package. The package will only be visible if we are the associated dbo or sysadmin and it exists
    ELSE IF NOT EXISTS (SELECT * from dbo.sysdac_instances WHERE instance_id = @instance_id)
    BEGIN
        RAISERROR(36004, -1, -1)
        RETURN(1)
    END
    
    BEGIN TRAN

    --If the action_id value is not set by the user, this is a new entry and the proc
    --should calculate the next value which is one more than the current max
    IF (@action_id IS NULL)
    BEGIN
        SET @action_id = (
            SELECT ISNULL(MAX(action_id) + 1, 0) 
            FROM dbo.sysdac_history_internal WITH (UPDLOCK, HOLDLOCK))        
    END

    INSERT INTO [dbo].[sysdac_history_internal]
        (action_id, sequence_id, instance_id, action_type, dac_object_type, action_status, required,
         dac_object_name_pretran, dac_object_name_posttran, sqlscript, payload, comments, error_string)
    VALUES
        (@action_id, @sequence_id, @instance_id, @action_type, @dac_object_type, @action_status, @required,
         @dac_object_name_pretran, @dac_object_name_posttran, @sqlscript, @payload, @comments, @error_string)

    COMMIT
    
    
    SELECT @retval = @@error
    RETURN(@retval)
END
GO

/*
Procedure [sp_sysdac_delete_instance]
This proc deletes DacInstance with the input instance_id in dbo.sysdac_instances_internal table
 
Parameters: 
    @instance_id - Guid identifier of a Dac Package
*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_delete_instance]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysdac_delete_instance]  
    @instance_id UniqueIdentifier
AS  
SET NOCOUNT ON;
BEGIN  
    DECLARE @retval INT  
    DECLARE @partId INT

    IF @instance_id IS NULL
    BEGIN
        RAISERROR(14043, -1, -1, 'instance_id', 'sp_sysdac_delete_instance')
        RETURN(1)
    END
  
    -- Ensure that the package being referred to exists by using the package view. We only continue if we can see 
    -- the specified package. The package will only be visible if we are the associated dbo or sysadmin and it exists
    IF NOT EXISTS (SELECT * from dbo.sysdac_instances WHERE instance_id = @instance_id)
    BEGIN
        RAISERROR(36004, -1, -1)
        RETURN(1)
    END
    
    --Delete the entry of DacInstance
    DELETE FROM sysdac_instances_internal WHERE instance_id=@instance_id 

    SELECT @retval = @@error
    RETURN(@retval)
END
GO

/*
Procedure [sp_sysdac_upgrade_instance]
This proc upgrades existing DacInstance metadata with the new version DacInstance.
 
Parameters: 
    @source_instance_id - Instance identifier of the original Dac Instance
    @instance_id - identifer of the new Dac Instance that's installed as a temporary instance
    @instance_name -  name of the new Dac Instance that's installed as a temporary instance
    @database_name - database name of the original DAC 
*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_upgrade_instance]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysdac_upgrade_instance]  
    @source_instance_id UniqueIdentifier = NULL,   
    @instance_id UniqueIdentifier = NULL,            
    @instance_name sysname,
    @database_name sysname
AS  
SET NOCOUNT ON;
BEGIN  
    DECLARE @retval INT  

    DECLARE @null_column sysname    
    SET @null_column = NULL

    IF (@source_instance_id IS NULL)
        SET @null_column = '@source_instance_id'
    ELSE IF (@instance_id IS NULL )
        SET @null_column = '@instance_id'
    ELSE IF( @database_name IS NULL)
        SET @null_column = '@database_name'

    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysdac_upgrade_instance')
        RETURN(1)
    END
    
    -- Ensure that the package being referred to exists by using the package view. We only continue if we can see 
    -- the specified package. The package will only be visible if we are the associated dbo or sysadmin and it exists
    IF NOT EXISTS (SELECT * from dbo.sysdac_instances WHERE instance_id = @instance_id)
    BEGIN
        RAISERROR(36004, -1, -1)
        RETURN(1)
    END
    
    --Ensure that the package being referred exists
    IF NOT EXISTS (SELECT * from dbo.sysdac_instances_internal WHERE instance_id = @instance_id)
    BEGIN
        RAISERROR(36004, -1, -1)
        RETURN(1)
    END

    BEGIN TRAN 
    
    --Delete the source DacInstance first
    EXEC dbo.sp_sysdac_delete_instance @instance_id = @instance_id
    
    --Update the new version DacInstance metadata with the original DacInstance
    UPDATE [dbo].[sysdac_instances_internal]
    SET instance_id   = @instance_id, 
        instance_name = @instance_name
    WHERE instance_id = @source_instance_id

    COMMIT
    
    SELECT @retval = @@error
    RETURN(@retval)
END
GO

/*
Procedure [sp_sysdac_drop_database]
This proc drops DAC databases
 
 Parameters
    @database_name : Name of the Database to be dropped
*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_drop_database]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysdac_drop_database]  
        @database_name sysname
AS  
SET NOCOUNT ON;
BEGIN  
    IF EXISTS(SELECT name FROM sys.databases WHERE name = @database_name)
    BEGIN
        DECLARE @engineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int);

        DECLARE @quoteddbname nvarchar(258)
        SET @quoteddbname = QUOTENAME(@database_name)
        
        DECLARE @sqlstatement nvarchar(1000)
        
        IF (@engineEdition != 5)
        BEGIN
            SET @sqlstatement = 'ALTER DATABASE ' + @quoteddbname + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE'
            EXEC (@sqlstatement)
        END 
        
        SET @sqlstatement = 'DROP DATABASE ' + @quoteddbname
        
        IF (@engineEdition = 5)
        BEGIN
			DECLARE @dbname SYSNAME 
			SET @dbname = db_name()
			
            RAISERROR (36012, 0, 1, @dbname, @sqlstatement);
            SELECT @dbname as databasename, @sqlstatement as sqlscript
        END
        ELSE
        BEGIN
            EXEC (@sqlstatement)
        END    
    END
    
    RETURN(@@error)
END
GO



/*
Procedure [sp_sysdac_rename_database]
This proc renames DAC databases
 
 Parameters
    @@database_name : original name of the database 
    @new_name : new name of the database
*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_rename_database]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysdac_rename_database]  
        @database_name sysname,
        @new_name sysname
AS  
SET NOCOUNT ON;
BEGIN  
    DECLARE @sqlstatement nvarchar(1000)

    -- Alter the database to single user mode    
    DECLARE @quoted_database_name nvarchar(258)
    SET @quoted_database_name = QUOTENAME(@database_name)
    SET @sqlstatement = 'ALTER DATABASE ' + @quoted_database_name + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE'
    EXEC (@sqlstatement)

    -- Rename the database
    EXEC sp_rename @objname=@quoted_database_name, @newname=@new_name, @objtype='DATABASE'

    -- Revert the database back to multi user mode
    DECLARE @quoted_new_name nvarchar(258)
    SET @quoted_new_name = QUOTENAME(@new_name)
    SET @sqlstatement = 'ALTER DATABASE ' + @quoted_new_name + ' SET MULTI_USER WITH ROLLBACK IMMEDIATE'
    EXEC (@sqlstatement)
            
    RETURN(@@error)
END
GO

/*
Procedure [sp_sysdac_setreadonly_database]
This proc renames DAC databases
 
 Parameters
    @database_name : original name of the database 
    @readonly : (0) to set readonly, (1) to set readwrite. Default value is 0
*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_setreadonly_database]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysdac_setreadonly_database]  
        @database_name sysname,
        @readonly bit = 0
AS  
SET NOCOUNT ON;
BEGIN  
    DECLARE @sqlstatement nvarchar(1000)
    
    DECLARE @quoted_database_name nvarchar(258)   
    SET @quoted_database_name = QUOTENAME(@database_name)
    
    IF (@readonly = 0)
        SET @sqlstatement = 'ALTER DATABASE ' + @quoted_database_name + ' SET READ_ONLY WITH ROLLBACK IMMEDIATE'
    ELSE IF (@readonly = 1)
        SET @sqlstatement = 'ALTER DATABASE ' + @quoted_database_name + ' SET READ_WRITE WITH ROLLBACK IMMEDIATE'    

    EXEC (@sqlstatement)
            
    RETURN(@@error)
END
GO


/*
Procedure [sp_sysdac_update_history_entry]
This proc update entry in dbo.sysdac_history_internal table

Parameters: 
    @action_id --action identifier for each attempt of deploy/delete/detach/upgrade
    @instance_id --unique transaction ID = DAC package id
    @action_type --type of the action being performed 0-deploy 1-detach 2-delete
    @dac_object_type --type of the object affected 0-dacpac,1-login,2-database
    @action_status --pending/success/fail
    @dac_object_name_pretran --name of the  object before the current step completes
    @dac_object_name_posttran --name of the  object after the current step completes
    @sqlscript --sql script for performing the associated object
    @error_string --error while running associated sql
*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_update_history_entry]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysdac_update_history_entry]
    @action_id int,
    @instance_id UniqueIdentifier = NULL,
    @action_type tinyint = NULL,
    @dac_object_type tinyint = NULL,
    @action_status tinyint = NULL,
    @dac_object_name_pretran sysname = N'',
    @dac_object_name_posttran sysname = N'',
    @sqlscript nvarchar(max) = N'',
    @error_string nvarchar(max) = N''
AS  
SET NOCOUNT ON;
BEGIN  
    DECLARE @retval INT  

    DECLARE @null_column sysname    
    SET @null_column = NULL

    IF (@instance_id IS NULL)
        SET @null_column = '@instance_id'
    ELSE IF (@action_type IS NULL)
        SET @null_column = '@action_type'
    ELSE IF (@dac_object_type IS NULL)
        SET @null_column = '@dac_object_type'
    ELSE IF (@action_status IS NULL) --action_status should be non-pending (success/failure)
        SET @null_column = '@action_status'

    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysdac_update_history_entry')
        RETURN(1)
    END
     
    -- Only allow users who created history entry or 'sysadmins' to update the row
    DECLARE @username SYSNAME
    SET @username = (SELECT created_by 
                     FROM dbo.sysdac_history_internal 
                     WHERE instance_id              = @instance_id AND 
                           action_id                = @action_id AND 
                           action_type              = @action_type AND
                           dac_object_type          = @dac_object_type AND
                           dac_object_name_pretran  = @dac_object_name_pretran AND
                           dac_object_name_posttran = @dac_object_name_posttran)

    IF ((@username != [dbo].[fn_sysdac_get_currentusername]()) AND ([dbo].[fn_sysdac_is_currentuser_sa]() != 1))
    BEGIN
        RAISERROR(36011, -1, -1);
        RETURN(1); -- failure
    END

    UPDATE [dbo].[sysdac_history_internal] 
    SET            
                action_status           = @action_status,
                sqlscript               = @sqlscript,
                error_string            = @error_string,
                date_modified           = (SELECT GETDATE()) 
    WHERE
                action_id               = @action_id AND
                action_type             = @action_type AND
                dac_object_type         = @dac_object_type AND
                dac_object_name_pretran = @dac_object_name_pretran AND
                dac_object_name_posttran = @dac_object_name_posttran
    
    SELECT @retval = @@error
    RETURN(@retval)
END
GO



/*
Procedure [sp_sysdac_resolve_pending_entry]
This proc resolves a pending entry to either completed or failed

Parameters: 
    @action_id --action identifier for each attempt of deploy/delete/detach/upgrade
    @sequence_id --step # of deployment/deletion process
*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_resolve_pending_entry]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysdac_resolve_pending_entry]  
    @action_id INT,
    @sequence_id INT
AS  
SET NOCOUNT ON;
BEGIN  
    DECLARE @null_column sysname    
    SET @null_column = NULL

    IF (@action_id IS NULL)
        SET @null_column = '@action_id'
    ELSE IF (@sequence_id IS NULL)
        SET @null_column = '@sequence_id'

    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysdac_resolve_pending_entry')
        RETURN(1)
    END

    DECLARE @instance_id UNIQUEIDENTIFIER
    DECLARE @action_type TINYINT
    DECLARE @dac_object_type TINYINT        
    DECLARE @action_status TINYINT
    DECLARE @dac_object_name_pretran SYSNAME
    DECLARE @dac_object_name_posttran SYSNAME

    SELECT @instance_id = instance_id, 
            @action_type = action_type, 
            @dac_object_type = dac_object_type,
            @dac_object_name_pretran = dac_object_name_pretran, 
            @dac_object_name_posttran = dac_object_name_posttran
    FROM sysdac_history_internal
    WHERE action_id = @action_id AND sequence_id = @sequence_id

   
    --Below are the constants set based on history table    
    DECLARE @create TINYINT
    DECLARE @rename TINYINT
    DECLARE @database TINYINT
    DECLARE @success TINYINT
    DECLARE @rollback TINYINT
    DECLARE @fail TINYINT
    DECLARE @register TINYINT
    DECLARE @unregister TINYINT
    DECLARE @upgrade TINYINT
    DECLARE @readonly TINYINT
    DECLARE @readwrite TINYINT
    DECLARE @disconnectusers TINYINT
    DECLARE @readonlymode INT

    SET @create = 1
    SET @rename = 2
    SET @database = 2
    SET @success = 2
    SET @rollback = 4
    SET @fail = 3
    SET @register = 3
    SET @unregister = 14
    SET @upgrade = 15
    SET @readonly = 12
    SET @readwrite = 16
    SET @disconnectusers = 17
    SET @readonlymode = 1024
    
    SET @action_status = @fail --initialize result of the action to failure and adjust if below cases succeed!
    
    IF @action_type = @create AND @dac_object_type = @database --database create
    BEGIN
        IF EXISTS(SELECT 1 FROM sys.sysdatabases WHERE name = @dac_object_name_pretran)
            SET @action_status = @success
    END
    ELSE IF @action_type = @rename AND @dac_object_type = @database --database rename
    BEGIN
        IF (EXISTS(SELECT 1 FROM sys.sysdatabases WHERE name = @dac_object_name_posttran)) AND 
            (NOT EXISTS(SELECT 1 FROM sys.sysdatabases WHERE name = @dac_object_name_pretran))
            SET @action_status = @success 
    END
    ELSE IF @action_type = @register --register DAC
    BEGIN
        IF (EXISTS(SELECT 1 FROM dbo.sysdac_instances_internal WHERE instance_name = @dac_object_name_pretran))
            SET @action_status = @success
    END
    ELSE IF @action_type = @unregister --unregister DAC
    BEGIN
        IF (NOT EXISTS(SELECT 1 FROM dbo.sysdac_instances_internal WHERE instance_name = @dac_object_name_pretran))
            SET @action_status = @success
    END
    ELSE IF @action_type = @upgrade --upgrade DAC
    BEGIN
        IF (EXISTS(SELECT 1 FROM dbo.sysdac_instances_internal WHERE instance_name = @dac_object_name_posttran)) AND 
            (NOT EXISTS(SELECT 1 FROM dbo.sysdac_instances_internal WHERE instance_name = @dac_object_name_pretran))
            SET @action_status = @success     
    END
    ELSE IF @action_type = @readonly OR @action_type = @disconnectusers -- readonly/disconnect users state
    BEGIN
        IF (EXISTS(SELECT 1 FROM sys.sysdatabases 
                            WHERE ((status & @readonlymode) = @readonlymode) AND name=@dac_object_name_pretran))
            SET @action_status = @success
    END
    ELSE IF @action_type = @readwrite -- readwrite state
    BEGIN
        IF (EXISTS(SELECT 1 FROM sys.sysdatabases 
                            WHERE ((status & @readonlymode) != @readonlymode) AND name=@dac_object_name_pretran))
            SET @action_status = @success
    END

    UPDATE sysdac_history_internal
    SET action_status = @action_status
    WHERE action_id = @action_id AND sequence_id = @sequence_id
    
END
GO


/*
Procedure [sp_sysdac_rollback_committed_step]
This proc rollsback a given committed step.

Parameters: 
    @action_id --action identifier for each attempt of deploy/delete/detach/upgrade
    @sequence_id --step # of deployment/deletion process
*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_rollback_committed_step]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysdac_rollback_committed_step]  
    @action_id INT,
    @sequence_id INT
AS  
SET NOCOUNT ON;
BEGIN  
    DECLARE @retval INT  

    DECLARE @null_column sysname    
    SET @null_column = NULL

    IF (@action_id IS NULL)
        SET @null_column = '@action_id'
    ELSE IF (@sequence_id IS NULL)
        SET @null_column = '@sequence_id'

    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysdac_rollback_committed_step')
        RETURN(1)
    END

    DECLARE @instance_id UNIQUEIDENTIFIER
    DECLARE @part_name NVARCHAR(128)
    DECLARE @action_type TINYINT
    DECLARE @dac_object_type TINYINT        
    DECLARE @action_status TINYINT
    DECLARE @dac_object_name_pretran SYSNAME
    DECLARE @dac_object_name_posttran SYSNAME
    DECLARE @sqlstatement NVARCHAR(1000)        

    SELECT @instance_id = instance_id, 
            @action_id = action_id, 
            @action_type = action_type, 
            @sequence_id = sequence_id,
            @dac_object_type = dac_object_type,
            @action_status = action_status, 
            @dac_object_name_pretran = dac_object_name_pretran, 
            @dac_object_name_posttran = dac_object_name_posttran
    FROM sysdac_history_internal
    WHERE action_id = @action_id AND sequence_id = @sequence_id
    
    --Below are the constants set based on history table    
    DECLARE @create TINYINT
    DECLARE @rename TINYINT
    DECLARE @register TINYINT
    DECLARE @database TINYINT
    DECLARE @rollback TINYINT
    DECLARE @rollback_pending TINYINT
    DECLARE @rollback_success TINYINT
    DECLARE @setreadonly TINYINT
    DECLARE @setreadwrite TINYINT

    SET @create = 1
    SET @rename = 2
    SET @register = 3
    SET @database = 2
    SET @rollback = 4
    SET @rollback_pending = 0
    SET @rollback_success = 1
    SET @setreadonly = 12
    SET @setreadwrite = 16
    
    IF @action_type = @create AND @dac_object_type = @database --database create
    BEGIN
        RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_pending, NULL) WITH NOWAIT

        EXEC dbo.sp_sysdac_drop_database @database_name = @dac_object_name_pretran
        
        RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_success, NULL) WITH NOWAIT
    END
    ELSE IF @action_type = @rename AND @dac_object_type = @database --database rename
    BEGIN
        RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_pending, NULL) WITH NOWAIT

        EXEC dbo.sp_sysdac_rename_database @dac_object_name_posttran, @dac_object_name_pretran

        RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_success, NULL) WITH NOWAIT
    END
    ELSE IF @action_type = @register --register DAC
    BEGIN
        SET @instance_id = (
            SELECT instance_id 
            FROM dbo.sysdac_instances_internal 
            WHERE instance_name = @dac_object_name_pretran)

        RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_pending, NULL) WITH NOWAIT

        EXEC dbo.sp_sysdac_delete_instance @instance_id = @instance_id

        RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_success, NULL) WITH NOWAIT
    END
    ELSE IF @action_type = @setreadonly  --readonly step
    BEGIN
        RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_pending, NULL) WITH NOWAIT

        EXEC dbo.sp_sysdac_setreadonly_database @database_name = @dac_object_name_pretran, @readonly = 1

        RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_success, NULL) WITH NOWAIT        
    END
    ELSE IF @action_type = @setreadwrite  --readonly step
    BEGIN
        RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_pending, NULL) WITH NOWAIT

        EXEC dbo.sp_sysdac_setreadonly_database @database_name = @dac_object_name_pretran, @readonly = 0

        RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_success, NULL) WITH NOWAIT        
    END
    
    --mark the entry as rolledback
    UPDATE sysdac_history_internal
    SET action_status = @rollback
    WHERE action_id = @action_id AND sequence_id = @sequence_id
    
    SELECT @retval = @@error
    RETURN(@retval)
END
GO


/*
Procedure [sp_sysdac_rollback_pending_object]
This proc rollsback the pending object of a given action id. 

Parameters: 
    @action_id --action identifier for each attempt of deploy/delete/detach/upgrade
*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_rollback_pending_object]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysdac_rollback_pending_object]  
    @action_id INT
AS  
SET NOCOUNT ON;
BEGIN  
    IF (@action_id IS NULL)
    BEGIN
        RAISERROR(14043, -1, -1, '@action_id', 'sp_sysdac_rollback_pending_object')
        RETURN(1)
    END

    DECLARE @sequence_id INT
    DECLARE @action_status TINYINT

    --Below are the constants set based on history table    
    DECLARE @header_id bit
    DECLARE @pending TINYINT
    DECLARE @success TINYINT
    DECLARE @true bit
    DECLARE @rollback TINYINT
    DECLARE @fail TINYINT
    DECLARE @rollback_failure TINYINT

    SET @header_id = 0
    SET @pending = 1
    SET @success = 2
    SET @true = 1
    SET @rollback = 4
    SET @fail = 3
    SET @rollback_failure = 2
    
    --if step 0 is not pending, exit
    IF ((SELECT action_status 
        FROM sysdac_history_internal 
        WHERE action_id = @action_id AND sequence_id = @header_id) != @pending)
        RETURN;

    
    --STEP 1. Resolve pending entry 
    SET @sequence_id = (SELECT TOP 1 sequence_id 
                        FROM sysdac_history_internal 
                        WHERE sequence_id != @header_id AND action_id = @action_id AND action_status = @pending)

    IF (@sequence_id IS NOT NULL)
        EXEC dbo.sp_sysdac_resolve_pending_entry @action_id = @action_id, @sequence_id = @sequence_id
    
    --check if all required steps are committed(success). If so, mark the action success and return!
    IF NOT EXISTS (SELECT 1
                    FROM sysdac_history_internal 
                    WHERE action_id = @action_id AND sequence_id != @header_id AND required = @true AND action_status != @success)
    BEGIN
        UPDATE dbo.sysdac_history_internal
        SET action_status = @success
        WHERE action_id = @action_id AND sequence_id = @header_id
    
        RETURN
    END
     
    BEGIN TRY
        
        --STEP 2. rollback commit entries
        WHILE EXISTS( SELECT 1 
                        FROM sysdac_history_internal 
                        WHERE action_status = @success AND action_id = @action_id AND sequence_id > 0)
        BEGIN
            SELECT TOP 1 @sequence_id = sequence_id,
                        @action_status = action_status
            FROM sysdac_history_internal
            WHERE action_status = @success AND action_id = @action_id AND sequence_id != @header_id
            ORDER BY sequence_id DESC

            EXEC dbo.sp_sysdac_rollback_committed_step @action_id = @action_id, @sequence_id = @sequence_id
            
        END

        --Mark the header entry as rolledback
        SET @action_status = @rollback    

    END TRY
    BEGIN CATCH
        DECLARE @error_message NVARCHAR(4000);
        
        SELECT @error_message = ERROR_MESSAGE()

        RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_failure, @error_message) WITH NOWAIT

        --Mark the header entry as failed
        SET @action_status = @fail
    END CATCH

    --STEP 3. Mark the header entry with final action status
    UPDATE dbo.sysdac_history_internal
    SET action_status = @action_status
    WHERE action_id = @action_id AND sequence_id = @header_id    

END
GO



/*
Procedure [sp_sysdac_rollback_all_pending_objects]

This proc rollsback all the pending actions.

Parameters: 
    @return_scripts -- if set to 1, stored procedure will return as a resultset 
    a set of sql scripts needed to be additionally executed to complete the rollback.
    
    Rollback procedure might not be able to execute them due to sql server limitations.
*/
RAISERROR('Creating procedure [dbo].[sp_sysdac_rollback_all_pending_objects]...', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysdac_rollback_all_pending_objects] (@return_scripts TINYINT = 0)
AS  
SET NOCOUNT ON;
BEGIN  
    DECLARE @action_id INT
    DECLARE @sequence_id INT

    --Below are the constants set based on history table    
    DECLARE @header_id bit
    DECLARE @pending TINYINT

    SET @header_id = 0
    SET @pending = 1
    
    CREATE TABLE #additional_scripts(databasename sysname, sqlscript VARCHAR(MAX))
    
    WHILE EXISTS (SELECT 1 FROM sysdac_history_internal WHERE sequence_id = @header_id AND action_status = @pending)
    BEGIN
        SET @action_id = (SELECT TOP 1 action_id FROM sysdac_history_internal WHERE sequence_id = @header_id AND action_status = @pending)

        INSERT INTO #additional_scripts
        EXEC dbo.sp_sysdac_rollback_pending_object @action_id = @action_id
    END
    
    IF (@return_scripts = 1)
    BEGIN
        SELECT databasename, sqlscript FROM #additional_scripts
    END
END
GO




-----------------------------------------------------------
-- Security for Dac objects
-----------------------------------------------------------
-- everybody can see the dacs view. These are filtered views so no info disclosure by it being granted to public
GRANT SELECT ON dbo.sysdac_instances to PUBLIC

-- everybody can see if they are a dac creator or not
GRANT EXECUTE ON dbo.fn_sysdac_is_dac_creator to PUBLIC
GRANT EXECUTE ON dbo.fn_sysdac_is_login_creator to PUBLIC
GRANT EXECUTE ON dbo.fn_sysdac_is_currentuser_sa to PUBLIC

GRANT EXECUTE ON dbo.sp_sysdac_ensure_dac_creator to PUBLIC

-- we allow anybody to execute the sprocs, they are guarded by the is_dac_creator function
GRANT EXECUTE ON dbo.sp_sysdac_rename_database to PUBLIC
GRANT EXECUTE ON dbo.sp_sysdac_drop_database to PUBLIC
GRANT EXECUTE ON dbo.sp_sysdac_upgrade_instance to PUBLIC
GRANT EXECUTE ON dbo.sp_sysdac_delete_instance to PUBLIC
GRANT EXECUTE ON dbo.sp_sysdac_add_instance to PUBLIC
GRANT EXECUTE ON dbo.sp_sysdac_add_history_entry to PUBLIC
GRANT EXECUTE ON dbo.sp_sysdac_update_history_entry to PUBLIC

--End DAC Script
RAISERROR('', 0, 1) WITH NOWAIT;
RAISERROR('------------------------------------', 0, 1) WITH NOWAIT;
RAISERROR('Execution of InstDac.SQL complete', 0, 1) WITH NOWAIT;
RAISERROR('------------------------------------', 0, 1) WITH NOWAIT;
GO


/**************************************************************/
/*                     END DAC SCRIPTS                     */
/**************************************************************/

---------------------------------------------------------------
-- Out-of-the-box system Collection Sets 
---------------------------------------------------------------
PRINT 'Creating system Collection Sets...'

------------------------------------------------
-- System collection set: Disk Usage
------------------------------------------------
DECLARE @collection_set_name NVARCHAR(128);
DECLARE @description NVARCHAR(4000);
DECLARE @collection_set_id int;
DECLARE @collection_set_uid uniqueidentifier;
DECLARE @collection_mode smallint;
DECLARE @schedule_name sysname;
DECLARE @days_until_expiration smallint;
DECLARE @name_id int;
DECLARE @description_id int;

-- The GUID below identifies this collection set and is used to locate the data collected by this collection set. 
-- The name and description retrived via FORMATMESSAGE will not be localized for the initial mkmastr build of msdb, 
-- but that is OK; the public interface to collection set detils (syscollector_collection_sets) is a view that will 
-- dynamically pull the correct localized strings via its own FORMATMESSAGE calls.  We just need a unique string to 
-- store in the table.
SET @name_id = 14701;
SET @description_id = 14700;
SET @collection_set_uid = N'7B191952-8ECF-4E12-AEB2-EF646EF79FEF';
SET @collection_mode = 1; -- Non-cached
SET @schedule_name = N'CollectorSchedule_Every_6h';
SET @days_until_expiration = 730;

SET @description = FORMATMESSAGE(@description_id);
SET @collection_set_name = ISNULL (FORMATMESSAGE(@name_id), 'Disk Usage');

IF EXISTS (SELECT * FROM dbo.syscollector_collection_sets_internal WHERE collection_set_uid = @collection_set_uid)
BEGIN
    RAISERROR ('Updating system Collection Set "%s"...', 0, 1, @collection_set_name) WITH NOWAIT;
    -- We are updating an existing collection set -- get its ID 
    SELECT @collection_set_id = collection_set_id
    FROM syscollector_collection_sets 
    WHERE collection_set_uid = @collection_set_uid;
    
    -- Temporarily clear the is_system flag so that we can modify the collection set definition
    UPDATE syscollector_collection_sets
    SET is_system = 0
    WHERE collection_set_id = @collection_set_id
    
    -- Don't override the current expiration period or schedule settings, since the user may have customized these
    EXEC dbo.sp_syscollector_update_collection_set
        @collection_set_id = @collection_set_id, 
        @new_name = @collection_set_name, 
        @schedule_name = @schedule_name, -- avoid mismatch in uid if schedule was deleted and recreated
        @collection_mode = @collection_mode, 
        @logging_level = 0,
        @description = @description;
END
ELSE
BEGIN    
    RAISERROR ('Creating system Collection Set "%s"...', 0, 1, @collection_set_name) WITH NOWAIT;
    EXEC dbo.sp_syscollector_create_collection_set
        @collection_set_uid = @collection_set_uid,
        @name = @collection_set_name, 
        @schedule_name = @schedule_name, 
        @collection_mode = @collection_mode, 
        @days_until_expiration = @days_until_expiration, 
        @description = @description,
        @logging_level = 0, 
        @collection_set_id = @collection_set_id OUTPUT;
END

-- for localization of collection set name and description
UPDATE syscollector_collection_sets_internal
SET name_id = @name_id, description_id = @description_id
WHERE collection_set_uid = @collection_set_uid;

-- Add collection items
DECLARE @collection_item_name NVARCHAR(128);
DECLARE @collection_item_old_name NVARCHAR(128);
DECLARE @collection_item_id int;
DECLARE @frequency int;

-- Item 1: disk_usage DMV query
DECLARE @parameters xml;
SELECT @parameters = convert(xml, N'<ns:TSQLQueryCollector xmlns:ns="DataCollectorType">
<Query>
<Value>
DECLARE @dbsize bigint 
DECLARE @logsize bigint 
DECLARE @ftsize bigint 
DECLARE @reservedpages bigint 
DECLARE @pages bigint 
DECLARE @usedpages bigint

SELECT @dbsize = SUM(convert(bigint,case when type = 0 then size else 0 end)) 
      ,@logsize = SUM(convert(bigint,case when type = 1 then size else 0 end)) 
      ,@ftsize = SUM(convert(bigint,case when type = 4 then size else 0 end)) 
FROM sys.database_files

SELECT @reservedpages = SUM(a.total_pages) 
       ,@usedpages = SUM(a.used_pages) 
       ,@pages = SUM(CASE 
                        WHEN it.internal_type IN (202,204) THEN 0 
                        WHEN a.type != 1 THEN a.used_pages 
                        WHEN p.index_id &lt; 2 THEN a.data_pages 
                        ELSE 0 
                     END) 
FROM sys.partitions p  
JOIN sys.allocation_units a ON p.partition_id = a.container_id 
LEFT JOIN sys.internal_tables it ON p.object_id = it.object_id 

SELECT 
        @dbsize as ''dbsize'',
        @logsize as ''logsize'',
        @ftsize as ''ftsize'',
        @reservedpages as ''reservedpages'',
        @usedpages as ''usedpages'',
        @pages as ''pages''
</Value>
<OutputTable>disk_usage</OutputTable>
</Query>
<Databases UseSystemDatabases="true" UseUserDatabases="true" />
</ns:TSQLQueryCollector>');

-- The name and description retrived via FORMATMESSAGE will not be localized for the initial mkmastr build of msdb, 
-- but that is OK; the public interface to collection item detils (syscollector_collection_items) is a view that will 
-- dynamically pull the correct localized strings via its own FORMATMESSAGE calls.  We just need a unique string to 
-- store in the table.
SET @name_id = 14702;
SET @collection_item_name = FORMATMESSAGE(@name_id);
SET @collection_item_old_name = 'Disk Usage - Data Files';
SET @frequency = 60;  -- Ignored (this collection set uses non-cached collection mode)

IF ISNULL (@collection_item_name, '') = '' 
BEGIN
    SET @collection_item_name = @collection_item_old_name;
END;

SET @collection_item_id = NULL;
SELECT @collection_item_id = collection_item_id
FROM syscollector_collection_items_internal 
WHERE collection_set_id = @collection_set_id 
    AND (name = @collection_item_name OR name = @collection_item_old_name);

IF (@collection_item_id IS NOT NULL)
BEGIN
    RAISERROR ('Updating Collection Item "%s"...', 0, 1, @collection_item_name);
    EXEC dbo.sp_syscollector_update_collection_item 
        @collection_item_id = @collection_item_id, 
        @new_name = @collection_item_name, 
        @frequency = @frequency, 
        @parameters = @parameters;
END
ELSE
BEGIN
    RAISERROR ('Creating Collection Item "%s"...', 0, 1, @collection_item_name);
    EXEC dbo.sp_syscollector_create_collection_item
        @collection_set_id = @collection_set_id,
        @collector_type_uid = N'302E93D1-3424-4BE7-AA8E-84813ECF2419',
        @name = @collection_item_name,
        @parameters = @parameters,
        @frequency = @frequency, 
        @collection_item_id = @collection_item_id output;
END;

-- for localization of collection item name
UPDATE syscollector_collection_items_internal
SET name_id = @name_id
WHERE collection_item_id = @collection_item_id;

-- Item 2: log_usage DMV query
SELECT @parameters = convert(xml, N'<ns:TSQLQueryCollector xmlns:ns="DataCollectorType">
<Query>
<Value>
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE @tran_log_space_usage table( 
        database_name sysname
,       log_size_mb float
,       log_space_used float
,       status int
); 
INSERT INTO @tran_log_space_usage 
EXEC(''DBCC SQLPERF (LOGSPACE) WITH NO_INFOMSGS'');
 
SELECT 
    database_name,
    log_size_mb,
    log_space_used,
    status    
FROM @tran_log_space_usage
</Value>
<OutputTable>log_usage</OutputTable>
</Query>
</ns:TSQLQueryCollector>');

-- The name and description retrived via FORMATMESSAGE will not be localized for the initial mkmastr build of msdb, 
-- but that is OK; the public interface to collection item detils (syscollector_collection_items) is a view that will 
-- dynamically pull the correct localized strings via its own FORMATMESSAGE calls.  We just need a unique string to 
-- store in the table.
SET @name_id = 14703;
SET @collection_item_name = FORMATMESSAGE(@name_id);
SET @collection_item_old_name = 'Disk Usage - Log Files';
SET @frequency = 60;  -- Ignored (this collection set uses non-cached collection mode)

IF ISNULL (@collection_item_name, '') = '' 
BEGIN
    SET @collection_item_name = @collection_item_old_name;
END;

SET @collection_item_id = NULL;
SELECT @collection_item_id = collection_item_id
FROM syscollector_collection_items_internal 
WHERE collection_set_id = @collection_set_id 
    AND (name = @collection_item_name OR name = @collection_item_old_name);

IF (@collection_item_id IS NOT NULL)
BEGIN
    RAISERROR ('Updating Collection Item "%s"...', 0, 1, @collection_item_name);
    EXEC dbo.sp_syscollector_update_collection_item 
        @collection_item_id = @collection_item_id, 
        @new_name = @collection_item_name, 
        @frequency = @frequency, 
        @parameters = @parameters;
END
ELSE
BEGIN
    RAISERROR ('Creating Collection Item "%s"...', 0, 1, @collection_item_name);
    EXEC dbo.sp_syscollector_create_collection_item
        @collection_set_id = @collection_set_id,
        @collector_type_uid = N'302E93D1-3424-4BE7-AA8E-84813ECF2419',
        @name = @collection_item_name,
        @parameters = @parameters,
        @frequency = @frequency, 
        @collection_item_id = @collection_item_id output;
END;

-- for localization of collection item name
UPDATE syscollector_collection_items_internal
SET name_id = @name_id
WHERE collection_item_id = @collection_item_id;

-- Turn the is_system flag on so users can't change the definition of this collection set 
UPDATE syscollector_collection_sets
SET is_system = 1
WHERE collection_set_id = @collection_set_id
GO


------------------------------------------------
-- System collection set: Server Activity
------------------------------------------------
DECLARE @collection_set_name NVARCHAR(128);
DECLARE @description NVARCHAR(4000);
DECLARE @collection_set_id int;
DECLARE @collection_set_uid uniqueidentifier;
DECLARE @collection_mode smallint;
DECLARE @schedule_name sysname;
DECLARE @days_until_expiration smallint;
DECLARE @name_id int;
DECLARE @description_id int;

-- The GUID below identifies this collection set and is used to locate the data collected by this collection set. 
-- The name and description retrived via FORMATMESSAGE will not be localized for the initial mkmastr build of msdb, 
-- but that is OK; the public interface to collection set detils (syscollector_collection_sets) is a view that will 
-- dynamically pull the correct localized strings via its own FORMATMESSAGE calls.  We just need a unique string to 
-- store in the table.
SET @name_id = 14705;
SET @description_id = 14704;
SET @collection_set_uid = N'49268954-4FD4-4EB6-AA04-CD59D9BB5714';
SET @collection_mode = 0; -- Cached
SET @schedule_name = N'CollectorSchedule_Every_15min';
SET @days_until_expiration = 14;

SET @description = FORMATMESSAGE(@description_id);
SET @collection_set_name = ISNULL (FORMATMESSAGE(@name_id), 'Server Activity');

IF EXISTS (SELECT * FROM dbo.syscollector_collection_sets_internal WHERE collection_set_uid = @collection_set_uid)
BEGIN
    RAISERROR ('Updating system Collection Set "%s"...', 0, 1, @collection_set_name) WITH NOWAIT;
    -- We are updating an existing collection set -- get its ID 
    SELECT @collection_set_id = collection_set_id
    FROM syscollector_collection_sets 
    WHERE collection_set_uid = @collection_set_uid;
    
    -- Temporarily clear the is_system flag so that we can modify the collection set definition
    UPDATE syscollector_collection_sets
    SET is_system = 0
    WHERE collection_set_id = @collection_set_id
    
    -- Don't override the current expiration period or schedule settings, since the user may have customized these
    EXEC dbo.sp_syscollector_update_collection_set
        @collection_set_id = @collection_set_id, 
        @new_name = @collection_set_name, 
        @schedule_name = @schedule_name, -- avoid mismatch in uid if schedule was deleted and recreated
        @collection_mode = @collection_mode, 
        @logging_level = 0,
        @description = @description;
END
ELSE
BEGIN    
    RAISERROR ('Creating system Collection Set "%s"...', 0, 1, @collection_set_name) WITH NOWAIT;
    EXEC dbo.sp_syscollector_create_collection_set
        @collection_set_uid = @collection_set_uid,
        @name = @collection_set_name, 
        @schedule_name = @schedule_name, 
        @collection_mode = @collection_mode, 
        @days_until_expiration = @days_until_expiration, 
        @description = @description, 
        @logging_level = 0,
        @collection_set_id = @collection_set_id OUTPUT;
END

-- for localization of collection set name and description
UPDATE syscollector_collection_sets_internal
SET name_id = @name_id, description_id = @description_id
WHERE collection_set_uid = @collection_set_uid;

-- Add collection items
DECLARE @collection_item_name NVARCHAR(128);
DECLARE @collection_item_old_name NVARCHAR(128);
DECLARE @collection_item_id int;
DECLARE @frequency int;
DECLARE @parameters xml;

-- Item 1 - DMV SNAPSHOTS
SELECT @parameters = convert(xml, N'<ns:TSQLQueryCollector xmlns:ns="DataCollectorType">
<Query>
<Value>
SET NOCOUNT ON
SELECT 
    LEFT (wait_type, 45) AS wait_type, 
    SUM (waiting_tasks_count) AS waiting_tasks_count, 
    SUM (wait_time_ms) AS wait_time_ms, 
    SUM (signal_wait_time_ms) AS signal_wait_time_ms
FROM 
(
    SELECT 
        LEFT (wait_type, 45) AS wait_type, 
    waiting_tasks_count, 
    wait_time_ms,  
    signal_wait_time_ms
FROM sys.dm_os_wait_stats 
WHERE waiting_tasks_count &gt; 0 OR wait_time_ms &gt; 0 OR signal_wait_time_ms &gt; 0
    UNION ALL 
    SELECT 
        LEFT (wait_type, 45) AS wait_type, 
        1 AS waiting_tasks_count, 
        wait_duration_ms AS wait_time_ms, 
        0 AS signal_wait_time_ms
    FROM sys.dm_os_waiting_tasks
    WHERE wait_duration_ms &gt; 60000
) AS merged_wait_stats
GROUP BY wait_type
</Value>
<OutputTable>os_wait_stats</OutputTable>
</Query>
<Query>
<Value>
SET NOCOUNT ON
SELECT 
  LEFT(latch_class,45) as latch_class,
  waiting_requests_count,
  wait_time_ms
FROM sys.dm_os_latch_stats 
WHERE waiting_requests_count &gt; 0 OR wait_time_ms &gt; 0
</Value>
<OutputTable>os_latch_stats</OutputTable>
</Query>
<Query>
<Value>
SET NOCOUNT ON
SELECT 
    pm.physical_memory_in_use_kb            AS sql_physical_memory_in_use_kb, 
    pm.large_page_allocations_kb            AS sql_large_page_allocations_kb, 
    pm.locked_page_allocations_kb           AS sql_locked_page_allocations_kb, 
    pm.total_virtual_address_space_kb       AS sql_total_virtual_address_space_kb, 
    pm.virtual_address_space_reserved_kb    AS sql_virtual_address_space_reserved_kb, 
    pm.virtual_address_space_committed_kb   AS sql_virtual_address_space_committed_kb, 
    pm.virtual_address_space_available_kb   AS sql_virtual_address_space_available_kb, 
    pm.page_fault_count                     AS sql_page_fault_count, 
    pm.memory_utilization_percentage        AS sql_memory_utilization_percentage, 
    pm.available_commit_limit_kb            AS sql_available_commit_limit_kb, 
    pm.process_physical_memory_low          AS sql_process_physical_memory_low, 
    pm.process_virtual_memory_low           AS sql_process_virtual_memory_low, 
    
    sm.total_physical_memory_kb             AS system_total_physical_memory_kb, 
    sm.available_physical_memory_kb         AS system_available_physical_memory_kb, 
    sm.total_page_file_kb                   AS system_total_page_file_kb, 
    sm.available_page_file_kb               AS system_available_page_file_kb, 
    sm.system_cache_kb                      AS system_cache_kb, 
    sm.kernel_paged_pool_kb                 AS system_kernel_paged_pool_kb, 
    sm.kernel_nonpaged_pool_kb              AS system_kernel_nonpaged_pool_kb, 
    sm.system_high_memory_signal_state      AS system_high_memory_signal_state, 
    sm.system_low_memory_signal_state       AS system_low_memory_signal_state, 
    
    si.bpool_commit_target                  AS bpool_commit_target, 
    si.bpool_committed                      AS bpool_committed, 
    si.bpool_visible                        AS bpool_visible
FROM sys.dm_os_process_memory AS pm
CROSS JOIN sys.dm_os_sys_memory AS sm   -- single-row DMV
CROSS JOIN sys.dm_os_sys_info AS si;    -- single-row DMV
</Value>
<OutputTable>sql_process_and_system_memory</OutputTable>
</Query>
<Query>
<Value>
SET NOCOUNT ON
SELECT 
    memory_node_id, 
    virtual_address_space_reserved_kb, 
    virtual_address_space_committed_kb, 
    locked_page_allocations_kb, 
    single_pages_kb, 
    multi_pages_kb, 
    shared_memory_reserved_kb, 
    shared_memory_committed_kb
FROM sys.dm_os_memory_nodes
</Value>
<OutputTable>os_memory_nodes</OutputTable>
</Query>
<Query>
<Value>
SET NOCOUNT ON
SELECT 
    type,
    memory_node_id as memory_node_id,
    SUM(single_pages_kb) as single_pages_kb,
    SUM(multi_pages_kb) as multi_pages_kb,
    SUM(virtual_memory_reserved_kb) as virtual_memory_reserved_kb,
    SUM(virtual_memory_committed_kb) as virtual_memory_committed_kb,
    SUM(awe_allocated_kb) as awe_allocated_kb,
    SUM(shared_memory_reserved_kb) as shared_memory_reserved_kb,
    SUM(shared_memory_committed_kb) as shared_memory_committed_kb
FROM sys.dm_os_memory_clerks
GROUP BY type, memory_node_id</Value>
<OutputTable>os_memory_clerks</OutputTable>
</Query>
<Query>
<Value>
SET NOCOUNT ON
SELECT 
    [parent_node_id],
    [scheduler_id],
    [cpu_id],
    [status],
    [is_idle],
    [preemptive_switches_count],
    [context_switches_count],
    [yield_count],
    [current_tasks_count],
    [runnable_tasks_count],
    [work_queue_count],
    [pending_disk_io_count]
FROM sys.dm_os_schedulers
WHERE scheduler_id &lt; 128
</Value>
<OutputTable>os_schedulers</OutputTable>
</Query>
<Query>
<Value>
SELECT 
    DB_NAME (f.database_id) AS database_name, f.database_id, f.name AS logical_file_name, f.[file_id], f.type_desc, 
    CAST (CASE 
        -- Handle UNC paths (e.g. ''\\fileserver\readonlydbs\dept_dw.ndf'' --&gt; ''\\fileserver\readonlydbs'')
        WHEN LEFT (LTRIM (f.physical_name), 2) = ''\\'' 
            THEN LEFT (LTRIM (f.physical_name), CHARINDEX (''\'', LTRIM (f.physical_name), CHARINDEX (''\'', LTRIM (f.physical_name), 3) + 1) - 1)
        -- Handle local paths (e.g. ''C:\Program Files\...\master.mdf'' --&gt; ''C:'') 
        WHEN CHARINDEX (''\'', LTRIM(f.physical_name), 3) &gt; 0 
            THEN UPPER (LEFT (LTRIM (f.physical_name), CHARINDEX (''\'', LTRIM (f.physical_name), 3) - 1))
        ELSE f.physical_name
    END AS nvarchar(255)) AS logical_disk, 
    fs.num_of_reads, fs.num_of_bytes_read, fs.io_stall_read_ms, fs.num_of_writes, fs.num_of_bytes_written, 
    fs.io_stall_write_ms, fs.size_on_disk_bytes
FROM sys.dm_io_virtual_file_stats (default, default) AS fs
INNER JOIN sys.master_files AS f ON fs.database_id = f.database_id AND fs.[file_id] = f.[file_id]
</Value>
<OutputTable>io_virtual_file_stats</OutputTable>
</Query>
</ns:TSQLQueryCollector>');

-- The name and description retrived via FORMATMESSAGE will not be localized for the initial mkmastr build of msdb, 
-- but that is OK; the public interface to collection item detils (syscollector_collection_items) is a view that will 
-- dynamically pull the correct localized strings via its own FORMATMESSAGE calls.  We just need a unique string to 
-- store in the table.
SET @name_id = 14706;
SET @collection_item_name = FORMATMESSAGE(@name_id);
SET @collection_item_old_name = 'Server Activity - DMV Snapshots';
SET @frequency = 60;  

IF ISNULL (@collection_item_name, '') = '' 
BEGIN
    SET @collection_item_name = @collection_item_old_name;
END;

SET @collection_item_id = NULL;
SELECT @collection_item_id = collection_item_id
FROM syscollector_collection_items_internal 
WHERE collection_set_id = @collection_set_id 
    AND (name = @collection_item_name OR name = @collection_item_old_name);

IF (@collection_item_id IS NOT NULL)
BEGIN
    RAISERROR ('Updating Collection Item "%s"...', 0, 1, @collection_item_name);
    EXEC dbo.sp_syscollector_update_collection_item 
        @collection_item_id = @collection_item_id, 
        @new_name = @collection_item_name, 
        @frequency = @frequency, 
        @parameters = @parameters;
END
ELSE
BEGIN
    RAISERROR ('Creating Collection Item "%s"...', 0, 1, @collection_item_name);
    EXEC dbo.sp_syscollector_create_collection_item
        @collection_set_id = @collection_set_id,
        @collector_type_uid = N'302E93D1-3424-4BE7-AA8E-84813ECF2419',
        @name = @collection_item_name,
        @parameters = @parameters,
        @frequency = @frequency, 
        @collection_item_id = @collection_item_id output;
END;

-- for localization of collection item name
UPDATE syscollector_collection_items_internal
SET name_id = @name_id
WHERE collection_item_id = @collection_item_id;

-- Item 2 - PERFORMANCE COUNTERS
SELECT @parameters = convert(xml, N'<ns:PerformanceCountersCollector xmlns:ns="DataCollectorType">
 <PerformanceCounters Objects="Memory" Counters="% Committed Bytes In Use" />
 <PerformanceCounters Objects="Memory" Counters="Available Bytes" />
 <PerformanceCounters Objects="Memory" Counters="Cache Bytes" />
 <PerformanceCounters Objects="Memory" Counters="Cache Faults/sec" />
 <PerformanceCounters Objects="Memory" Counters="Committed Bytes" />
 <PerformanceCounters Objects="Memory" Counters="Free &amp; Zero Page List Bytes" />
 <PerformanceCounters Objects="Memory" Counters="Modified Page List Bytes" />
 <PerformanceCounters Objects="Memory" Counters="Pages/sec" />
 <PerformanceCounters Objects="Memory" Counters="Page Reads/sec" />
 <PerformanceCounters Objects="Memory" Counters="Page Write/sec" />
 <PerformanceCounters Objects="Memory" Counters="Page Faults/sec" />
 <PerformanceCounters Objects="Memory" Counters="Pool Nonpaged Bytes" />
 <PerformanceCounters Objects="Memory" Counters="Pool Paged Bytes" />
 <PerformanceCounters Objects="Memory" Counters="Standby Cache Core Bytes" />
 <PerformanceCounters Objects="Memory" Counters="Standby Cache Normal Priority Bytes" />
 <PerformanceCounters Objects="Memory" Counters="Standby Cache Reserve Bytes" />
 <PerformanceCounters Objects="Memory" Counters="Pool Paged Bytes" />
 <PerformanceCounters Objects="Memory" Counters="Write Copies/sec" />
 <PerformanceCounters Objects="Process" Counters="*" Instances="_Total" />
 <PerformanceCounters Objects="Process" Counters="*" Instances="$(TARGETPROCESS)" />
 <PerformanceCounters Objects="Process" Counters="Thread Count" Instances="*" />
 <PerformanceCounters Objects="Process" Counters="% Processor Time" Instances="*" />
 <PerformanceCounters Objects="Process" Counters="IO Read Bytes/sec" Instances="*" />
 <PerformanceCounters Objects="Process" Counters="IO Write Bytes/sec" Instances="*" />
 <PerformanceCounters Objects="Process" Counters="Private Bytes" Instances="*" />
 <PerformanceCounters Objects="Process" Counters="Working Set" Instances="*" />
 <PerformanceCounters Objects="Processor" Counters="% Processor Time" Instances="*" />
 <PerformanceCounters Objects="Processor" Counters="% User Time" Instances="*" />
 <PerformanceCounters Objects="Processor" Counters="% Privileged Time" Instances="*" />
 <PerformanceCounters Objects="Server Work Queues" Counters="Queue Length" Instances="*" />
 <PerformanceCounters Objects="LogicalDisk" Counters="% Disk Time" Instances="*" />
 <PerformanceCounters Objects="LogicalDisk" Counters="Avg. Disk Queue Length" Instances="*" />
 <PerformanceCounters Objects="LogicalDisk" Counters="Avg. Disk Read Queue Length" Instances="*" />
 <PerformanceCounters Objects="LogicalDisk" Counters="Avg. Disk Write Queue Length" Instances="*" />
 <PerformanceCounters Objects="LogicalDisk" Counters="Avg. Disk sec/Read" Instances="*" />
 <PerformanceCounters Objects="LogicalDisk" Counters="Avg. Disk sec/Write" Instances="*" />
 <PerformanceCounters Objects="LogicalDisk" Counters="Avg. Disk sec/Transfer" Instances="*" />
 <PerformanceCounters Objects="LogicalDisk" Counters="Disk Reads/sec" Instances="*" />
 <PerformanceCounters Objects="LogicalDisk" Counters="Disk Bytes/sec" Instances="*" />
 <PerformanceCounters Objects="LogicalDisk" Counters="Disk Writes/sec" Instances="*" />
 <PerformanceCounters Objects="LogicalDisk" Counters="Split IO/sec" Instances="*" />
 <PerformanceCounters Objects="System" Counters="Processor Queue Length" />
 <PerformanceCounters Objects="System" Counters="File Read Operations/sec" />
 <PerformanceCounters Objects="System" Counters="File Write Operations/sec" />
 <PerformanceCounters Objects="System" Counters="File Control Operations/sec" />
 <PerformanceCounters Objects="System" Counters="File Read Bytes/sec" />
 <PerformanceCounters Objects="System" Counters="File Write Bytes/sec" />
 <PerformanceCounters Objects="System" Counters="File Control Bytes/sec" />
 <PerformanceCounters Objects="Network Interface" Counters="Bytes Total/sec" Instances="*" />
 <PerformanceCounters Objects="Network Interface" Counters="Output Queue Length" Instances="*" />
 <PerformanceCounters Objects="$(INSTANCE):Buffer Manager" Counters="Stolen pages"/>
 <PerformanceCounters Objects="$(INSTANCE):Buffer Manager" Counters="Page life expectancy"/>
 <PerformanceCounters Objects="$(INSTANCE):Memory Manager" Counters="Memory Grants Outstanding"/>
 <PerformanceCounters Objects="$(INSTANCE):Memory Manager" Counters="Memory Grants Pending"/>
 <PerformanceCounters Objects="$(INSTANCE):Databases" Counters="Transactions/sec"  Instances="_Total"/>
 <PerformanceCounters Objects="$(INSTANCE):Databases" Counters="Transactions/sec"  Instances="tempdb"/>
 <PerformanceCounters Objects="$(INSTANCE):Databases" Counters="Active Transactions"  Instances="*"/>
 <PerformanceCounters Objects="$(INSTANCE):General Statistics" Counters="Logins/sec"  />
 <PerformanceCounters Objects="$(INSTANCE):General Statistics" Counters="Logouts/sec"  />
 <PerformanceCounters Objects="$(INSTANCE):General Statistics" Counters="User Connections"  />
 <PerformanceCounters Objects="$(INSTANCE):General Statistics" Counters="Logical Connections"  />
 <PerformanceCounters Objects="$(INSTANCE):General Statistics" Counters="Transactions"  />
 <PerformanceCounters Objects="$(INSTANCE):General Statistics" Counters="Processes blocked"  />
 <PerformanceCounters Objects="$(INSTANCE):General Statistics" Counters="Active Temp Tables"  />
 <PerformanceCounters Objects="$(INSTANCE):SQL Statistics" Counters="Batch Requests/sec" />
 <PerformanceCounters Objects="$(INSTANCE):SQL Statistics" Counters="SQL Compilations/sec" />
 <PerformanceCounters Objects="$(INSTANCE):SQL Statistics" Counters="SQL Re-Compilations/sec" />
 <PerformanceCounters Objects="$(INSTANCE):SQL Statistics" Counters="SQL Attention rate" />
 <PerformanceCounters Objects="$(INSTANCE):SQL Statistics" Counters="Auto-Param Attempts/sec" />
 <PerformanceCounters Objects="$(INSTANCE):SQL Statistics" Counters="Failed Auto-Params/sec" />
 <PerformanceCounters Objects="$(INSTANCE):Plan Cache" Counters="Cache Hit Ratio" Instances="_Total" />
 <PerformanceCounters Objects="$(INSTANCE):Plan Cache" Counters="Cache Hit Ratio" Instances="Object Plans" />
 <PerformanceCounters Objects="$(INSTANCE):Plan Cache" Counters="Cache Hit Ratio" Instances="SQL Plans" />
 <PerformanceCounters Objects="$(INSTANCE):Plan Cache" Counters="Cache Hit Ratio" Instances="Temporary Tables &amp; Table Variables" />
 <PerformanceCounters Objects="$(INSTANCE):Transactions" Counters="Free Space in tempdb (KB)"/>
 <PerformanceCounters Objects="$(INSTANCE):Workload Group Stats" Counters="Active requests" Instances="*"/>
 <PerformanceCounters Objects="$(INSTANCE):Workload Group Stats" Counters="Blocked tasks" Instances="*"/>
 <PerformanceCounters Objects="$(INSTANCE):Workload Group Stats" Counters="CPU usage %" Instances="*"/>
</ns:PerformanceCountersCollector>');

-- The name and description retrived via FORMATMESSAGE will not be localized for the initial mkmastr build of msdb, 
-- but that is OK; the public interface to collection item detils (syscollector_collection_items) is a view that will 
-- dynamically pull the correct localized strings via its own FORMATMESSAGE calls.  We just need a unique string to 
-- store in the table.
SET @name_id = 14707;
SET @collection_item_name = FORMATMESSAGE(@name_id);
SET @collection_item_old_name = 'Server Activity - Performance Counters';
SET @frequency = 60;  

IF ISNULL (@collection_item_name, '') = '' 
BEGIN
    SET @collection_item_name = @collection_item_old_name;
END;

SET @collection_item_id = NULL;
SELECT @collection_item_id = collection_item_id
FROM syscollector_collection_items_internal 
WHERE collection_set_id = @collection_set_id 
    AND (name = @collection_item_name OR name = @collection_item_old_name);

IF (@collection_item_id IS NOT NULL)
BEGIN
    RAISERROR ('Updating Collection Item "%s"...', 0, 1, @collection_item_name);
    EXEC dbo.sp_syscollector_update_collection_item 
        @collection_item_id = @collection_item_id, 
        @new_name = @collection_item_name, 
        @frequency = @frequency, 
        @parameters = @parameters;
END
ELSE
BEGIN
    RAISERROR ('Creating Collection Item "%s"...', 0, 1, @collection_item_name);
    EXEC dbo.sp_syscollector_create_collection_item
        @collection_set_id = @collection_set_id,
        @collector_type_uid = N'294605DD-21DE-40B2-B20F-F3E170EA1EC3',
        @name = @collection_item_name,
        @parameters = @parameters,
        @frequency = @frequency, 
        @collection_item_id = @collection_item_id output;
END;

-- for localization of collection item name
UPDATE syscollector_collection_items_internal
SET name_id = @name_id
WHERE collection_item_id = @collection_item_id;

-- Turn the is_system flag on so users can't change the definition of this collection set 
UPDATE syscollector_collection_sets
SET is_system = 1
WHERE collection_set_id = @collection_set_id
GO


------------------------------------------------
-- System collection set: Query Statistics
------------------------------------------------
DECLARE @collection_set_name NVARCHAR(128);
DECLARE @description NVARCHAR(4000);
DECLARE @collection_set_id int;
DECLARE @collection_set_uid uniqueidentifier;
DECLARE @collection_mode smallint;
DECLARE @schedule_name sysname;
DECLARE @days_until_expiration smallint;
DECLARE @name_id int;
DECLARE @description_id int;

-- The GUID below identifies this collection set and is used to locate the data collected by this collection set. 
-- The name and description retrived via FORMATMESSAGE will not be localized for the initial mkmastr build of msdb, 
-- but that is OK; the public interface to collection set detils (syscollector_collection_sets) is a view that will 
-- dynamically pull the correct localized strings via its own FORMATMESSAGE calls.  We just need a unique string to 
-- store in the table.
SET @name_id = 14709;
SET @description_id = 14708;
SET @collection_set_uid = N'2DC02BD6-E230-4C05-8516-4E8C0EF21F95';
SET @collection_mode = 0; -- Cached
SET @schedule_name = N'CollectorSchedule_Every_15min';
SET @days_until_expiration = 14;

SET @description = FORMATMESSAGE(@description_id);
SET @collection_set_name = ISNULL (FORMATMESSAGE(@name_id), 'Query Statistics');

IF EXISTS (SELECT * FROM dbo.syscollector_collection_sets_internal WHERE collection_set_uid = @collection_set_uid)
BEGIN
    RAISERROR ('Updating system Collection Set "%s"...', 0, 1, @collection_set_name) WITH NOWAIT;
    -- We are updating an existing collection set -- get its ID 
    SELECT @collection_set_id = collection_set_id
    FROM syscollector_collection_sets 
    WHERE collection_set_uid = @collection_set_uid;
    
    -- Temporarily clear the is_system flag so that we can modify the collection set definition
    UPDATE syscollector_collection_sets
    SET is_system = 0
    WHERE collection_set_id = @collection_set_id
    
    -- Don't override the current expiration period or schedule settings, since the user may have customized these
    EXEC dbo.sp_syscollector_update_collection_set
        @collection_set_id = @collection_set_id, 
        @new_name = @collection_set_name, 
        @schedule_name = @schedule_name, -- avoid mismatch in uid if schedule was deleted and recreated
        @collection_mode = @collection_mode, 
        @logging_level = 0,
        @description = @description;
END
ELSE
BEGIN    
    RAISERROR ('Creating system Collection Set "%s"...', 0, 1, @collection_set_name) WITH NOWAIT;
    EXEC dbo.sp_syscollector_create_collection_set
        @collection_set_uid = @collection_set_uid,
        @name = @collection_set_name, 
        @schedule_name = @schedule_name,
        @collection_mode = @collection_mode, 
        @days_until_expiration = @days_until_expiration, 
        @description = @description, 
        @logging_level = 0,
        @collection_set_id = @collection_set_id OUTPUT;
END

-- for localization of collection set name and description
UPDATE syscollector_collection_sets_internal
SET name_id = @name_id, description_id = @description_id
WHERE collection_set_uid = @collection_set_uid;

-- Add collection items
DECLARE @collection_item_name NVARCHAR(128);
DECLARE @collection_item_old_name NVARCHAR(128);
DECLARE @collection_item_id int;
DECLARE @frequency int;
DECLARE @parameters xml;

-- Item 1 - Query activity collection type
-- The name and description retrived via FORMATMESSAGE will not be localized for the initial mkmastr build of msdb, 
-- but that is OK; the public interface to collection item detils (syscollector_collection_items) is a view that will 
-- dynamically pull the correct localized strings via its own FORMATMESSAGE calls.  We just need a unique string to 
-- store in the table.
SET @name_id = 14710;
SET @collection_item_name = FORMATMESSAGE(@name_id);
SET @collection_item_old_name = 'Query Statistics - Query Activity';
SET @frequency = 10;  

IF ISNULL (@collection_item_name, '') = '' 
BEGIN
    SET @collection_item_name = @collection_item_old_name;
END;

SET @collection_item_id = NULL;
SELECT @collection_item_id = collection_item_id
FROM syscollector_collection_items_internal 
WHERE collection_set_id = @collection_set_id 
    AND (name = @collection_item_name OR name = @collection_item_old_name);

IF (@collection_item_id IS NOT NULL)
BEGIN
    RAISERROR ('Updating Collection Item "%s"...', 0, 1, @collection_item_name);
    EXEC dbo.sp_syscollector_update_collection_item 
        @collection_item_id = @collection_item_id, 
        @new_name = @collection_item_name, 
        @frequency = @frequency, 
        @parameters = @parameters;
END
ELSE
BEGIN
    RAISERROR ('Creating Collection Item "%s"...', 0, 1, @collection_item_name);
    EXEC dbo.sp_syscollector_create_collection_item
        @collection_set_id = @collection_set_id,
        @collector_type_uid = N'14AF3C12-38E6-4155-BD29-F33E7966BA23',
        @name = @collection_item_name,
        @parameters = @parameters,
        @frequency = @frequency, 
        @collection_item_id = @collection_item_id output;
END;

-- for localization of collection item name
UPDATE syscollector_collection_items_internal
SET name_id = @name_id
WHERE collection_item_id = @collection_item_id;

-- Turn the is_system flag on so users can't change the definition of this collection set 
UPDATE syscollector_collection_sets
SET is_system = 1
WHERE collection_set_id = @collection_set_id
GO

-- End of installation of out-of-the-box collector components
-- Restore agent xp settings to original state
DECLARE @advopt_old_value int
DECLARE @comp_old_value int
SELECT @advopt_old_value = advopt_old_value FROM #advopt_old_value
SELECT @comp_old_value = comp_old_value FROM #comp_old_value

EXECUTE #sp_restore_component_state 'Agent XPs', @advopt_old_value, @comp_old_value

DROP TABLE #advopt_old_value
DROP TABLE #comp_old_value
---------------------------------------------------------------
-- Data Collector: Security: Permissions
---------------------------------------------------------------

PRINT ''
PRINT 'Granting permissions to data collector roles...'

GRANT SELECT  ON [dbo].[syscollector_config_store]                                TO [dc_operator], [dc_proxy]
GRANT EXECUTE ON [dbo].[sp_syscollector_enable_collector]                        TO [dc_operator]
GRANT EXECUTE ON [dbo].[sp_syscollector_disable_collector]                        TO [dc_operator]
GRANT EXECUTE ON [dbo].[sp_syscollector_set_warehouse_instance_name]            TO [dc_admin]
GRANT EXECUTE ON [dbo].[sp_syscollector_set_warehouse_database_name]            TO [dc_admin]
GRANT EXECUTE ON [dbo].[sp_syscollector_set_cache_window]                        TO [dc_admin]
GRANT EXECUTE ON [dbo].[sp_syscollector_set_cache_directory]                    TO [dc_admin]
GRANT EXECUTE ON [dbo].[sp_syscollector_get_warehouse_connection_string]        TO [dc_proxy]
GRANT EXECUTE ON [dbo].[fn_syscollector_highest_incompatible_mdw_version]		TO [dc_admin], [dc_proxy]

GRANT SELECT  ON [dbo].[syscollector_collector_types]                            TO [dc_operator], [dc_proxy]
GRANT EXECUTE ON [dbo].[sp_syscollector_create_collector_type]                    TO [dc_admin]
GRANT EXECUTE ON [dbo].[sp_syscollector_delete_collector_type]                    TO [dc_admin]

GRANT SELECT  ON [dbo].[syscollector_collection_sets]                            TO [dc_operator], [dc_proxy]
GRANT EXECUTE ON [dbo].[sp_syscollector_create_collection_set]                    TO [dc_admin]
GRANT EXECUTE ON [dbo].[sp_syscollector_delete_collection_set]                    TO [dc_admin]
GRANT EXECUTE ON [dbo].[sp_syscollector_update_collection_set]                    TO [dc_operator]
GRANT EXECUTE ON [dbo].[sp_syscollector_start_collection_set]                    TO [dc_operator]
GRANT EXECUTE ON [dbo].[sp_syscollector_stop_collection_set]                    TO [dc_operator]
GRANT EXECUTE ON [dbo].[sp_syscollector_upload_collection_set]                    TO [dc_operator]
GRANT EXECUTE ON [dbo].[sp_syscollector_run_collection_set]                        TO [dc_operator]

GRANT SELECT  ON [dbo].[syscollector_collection_items]                            TO [dc_operator], [dc_proxy]
GRANT EXECUTE ON [dbo].[sp_syscollector_create_collection_item]                    TO [dc_admin]
GRANT EXECUTE ON [dbo].[sp_syscollector_delete_collection_item]                    TO [dc_admin]
GRANT EXECUTE ON [dbo].[sp_syscollector_update_collection_item]                    TO [dc_operator]

GRANT SELECT  ON [dbo].[syscollector_execution_log]                                TO [dc_operator]
GRANT SELECT  ON [dbo].[syscollector_execution_log_full]                        TO [dc_operator]
GRANT SELECT  ON [dbo].[syscollector_execution_stats]                            TO [dc_operator]

GRANT EXECUTE ON [dbo].[fn_syscollector_find_collection_set_root]                TO [dc_operator]
GRANT SELECT  ON [dbo].[fn_syscollector_get_execution_log_tree]                    TO [dc_operator]
GRANT SELECT  ON [dbo].[fn_syscollector_get_execution_stats]                    TO [dc_operator]
GRANT SELECT  ON [dbo].[fn_syscollector_get_execution_details]                    TO [dc_operator]
GRANT EXECUTE ON [dbo].[sp_syscollector_delete_execution_log_tree]                TO [dc_operator]

GRANT EXECUTE ON [dbo].[sp_syscollector_event_oncollectionbegin]                TO [dc_proxy]
GRANT EXECUTE ON [dbo].[sp_syscollector_event_oncollectionend]                    TO [dc_proxy]
GRANT EXECUTE ON [dbo].[sp_syscollector_event_onpackagebegin]                    TO [dc_proxy]
GRANT EXECUTE ON [dbo].[sp_syscollector_event_onpackageend]                        TO [dc_proxy]
GRANT EXECUTE ON [dbo].[sp_syscollector_event_onpackageupdate]                    TO [dc_proxy]
GRANT EXECUTE ON [dbo].[sp_syscollector_event_onerror]                            TO [dc_proxy]
GRANT EXECUTE ON [dbo].[sp_syscollector_event_onstatsupdate]                    TO [dc_proxy]

GRANT EXECUTE ON [dbo].[sp_syscollector_create_tsql_query_collector]            TO [dc_operator], [dc_proxy]
GRANT EXECUTE ON [dbo].[sp_syscollector_get_tsql_query_collector_package_ids]   TO [dc_operator], [dc_proxy]

GRANT EXECUTE ON [dbo].[sp_verify_subsystems]                                   TO [dc_operator]

---------------------------------------------------------------
-- Relational storage for DMF objects
---------------------------------------------------------------

CREATE TABLE #objects_to_drop(
    type nvarchar(128),
    name sysname
)

-- List of shared registered server objects to be deleted from msdb. Note: order is important!
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_sysmanagement_verify_shared_server_type]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_sysmanagement_update_shared_registered_server]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_sysmanagement_rename_shared_registered_server]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_sysmanagement_update_shared_server_group]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_sysmanagement_rename_shared_server_group]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_sysmanagement_move_shared_registered_server]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_sysmanagement_delete_shared_registered_server]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_sysmanagement_move_shared_server_group]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_sysmanagement_delete_shared_server_group]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_sysmanagement_add_shared_registered_server]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_sysmanagement_add_shared_server_group]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',              '[dbo].[sysmanagement_shared_registered_servers]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',              '[dbo].[sysmanagement_shared_server_groups]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',         '[dbo].[sysmanagement_delete_shared_server_group_trigger]')

-- List of policy management objects to be deleted from msdb. Note: order is important!
INSERT INTO #objects_to_drop VALUES ('TRIGGER',           '[dbo].[syspolicy_insert_target_set_level_trigger]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',           '[dbo].[syspolicy_update_target_set_level_trigger]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',           '[dbo].[syspolicy_insert_target_set_trigger]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',           '[dbo].[syspolicy_delete_target_set_trigger]' )
INSERT INTO #objects_to_drop VALUES ('FUNCTION',        '[dbo].[syspolicy_fn_filter_complete]' )
INSERT INTO #objects_to_drop VALUES ('FUNCTION',        '[dbo].[syspolicy_fn_eventing_filter]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_syspolicy_add_target_set_condition_reference]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_syspolicy_update_target_set_condition_reference]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',             '[dbo].[syspolicy_target_set_condition_references]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_syspolicy_add_target_set_level]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_syspolicy_update_target_set_level]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',             '[dbo].[syspolicy_target_set_levels]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_syspolicy_verify_object_set_identifiers]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_syspolicy_verify_object_set_references]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_syspolicy_add_target_set]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_syspolicy_delete_target_set]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_syspolicy_update_target_set]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_syspolicy_add_object_set]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',          '[dbo].[sp_syspolicy_delete_object_set]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',             '[dbo].[syspolicy_target_sets]' )

INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_events_reader]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_dispatch_event]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_delete_policy_execution_history]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_log_policy_execution_detail]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_log_policy_execution_end]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_log_policy_execution_start]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',      		'[dbo].[syspolicy_policy_execution_history_details]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',      		'[dbo].[syspolicy_policy_execution_history]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',    		'[dbo].[syspolicy_update_system_health_state]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',      		'[dbo].[syspolicy_system_health_state]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_delete_policy_category_subscription]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_update_policy_category_subscription]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_add_policy_category_subscription]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',      		'[dbo].[syspolicy_policy_category_subscriptions]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_delete_policy]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_update_policy]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_rename_policy]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_verify_policy_identifiers]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_add_policy]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_rename_policy_category]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_update_policy_category]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_delete_policy_category]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_add_policy_category]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_verify_policy_category_identifiers]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',      		'[dbo].[syspolicy_policies]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_create_job]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',   		'[dbo].[syspolicy_delete_job_delete_trigger]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',   		'[dbo].[syspolicy_update_policy_trigger]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',   		'[dbo].[syspolicy_insert_policy_trigger]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',   		'[dbo].[syspolicy_update_job_update_trigger]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',   		'[dbo].[syspolicy_insert_job_create_trigger]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',   		'[dbo].[syspolicy_instead_delete_policy_trigger]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',      		'[dbo].[syspolicy_policy_categories]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_rename_condition]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_delete_condition]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_update_condition]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_verify_condition_identifiers]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE', 		'[dbo].[sp_syspolicy_add_condition]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',      		'[dbo].[syspolicy_conditions]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',    		'[dbo].[syspolicy_insert_condition_trigger]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',    		'[dbo].[syspolicy_update_condition_trigger]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',   		'[dbo].[syspolicy_for_update_condition_trigger]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',   		'[dbo].[syspolicy_after_update_condition_trigger]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',     		'[dbo].[syspolicy_object_sets]' )
INSERT INTO #objects_to_drop VALUES ('FUNCTION',  		'[dbo].[syspolicy_fn_get_type_name]' )
INSERT INTO #objects_to_drop VALUES ('INLINE FUNCTION',  		'[dbo].[syspolicy_fn_get_bad_filters]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',  		'[dbo].[sp_syspolicy_check_membership]' )
INSERT INTO #objects_to_drop VALUES ('FUNCTION',  		'[dbo].[fn_syspolicy_get_ps_command]' )
INSERT INTO #objects_to_drop VALUES ('VIEW',            '[dbo].[syspolicy_configuration]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',  	    '[dbo].[sp_syspolicy_configure]' )
INSERT INTO #objects_to_drop VALUES ('FUNCTION',  	    '[dbo].[fn_syspolicy_is_automation_enabled]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',  	    '[dbo].[sp_syspolicy_set_config_enabled]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',  	    '[dbo].[sp_syspolicy_repair_policy_automation]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',  	    '[dbo].[sp_syspolicy_purge_history]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',  	    '[dbo].[sp_syspolicy_set_config_history_retention]' )
INSERT INTO #objects_to_drop VALUES ('TRIGGER',  	    '[dbo].[syspolicy_validate_events]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',  	    '[dbo].[sp_syspolicy_create_purge_job]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',  	    '[dbo].[sp_syspolicy_purge_health_state]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',  	    '[dbo].[sp_syspolicy_set_log_on_success]' )
INSERT INTO #objects_to_drop VALUES ('PROCEDURE',  	    '[dbo].[sp_syspolicy_mark_system]' )

GO

DECLARE @object_name sysname
DECLARE @object_type nvarchar(128)

DECLARE object_to_drop_cursor CURSOR LOCAL FOR SELECT type, name FROM #objects_to_drop

OPEN object_to_drop_cursor
FETCH object_to_drop_cursor INTO @object_type, @object_name

WHILE @@FETCH_STATUS = 0 
BEGIN
    DECLARE @stmt nvarchar(128)
    DECLARE @object_kind nvarchar(2)

    SET @object_kind = 
        CASE @object_type
            WHEN 'VIEW'       THEN 'V'
            WHEN 'PROCEDURE'  THEN 'P'
            WHEN 'TRIGGER'    THEN 'TR'
            WHEN 'FUNCTION'    THEN 'FN'
            WHEN 'INLINE FUNCTION'    THEN 'IF'
            WHEN 'TABLE TYPE'    THEN 'TT'
            ELSE NULL
        END

    IF @object_kind IS NULL
    BEGIN
        DECLARE @errtxt nvarchar(max)
        SET @errtxt = 'Incorrect object type "' + @object_type + '" for object "' + @object_name + '", check "INSERT INTO #objects_to_drop..." statement'
        RAISERROR(@errtxt, 20, 127) WITH LOG
    END

    IF (OBJECT_ID(@object_name, @object_kind) IS NULL)
        PRINT 'Object "' + @object_name + N'" does not exist, will not drop'
    ELSE
    BEGIN
        IF( @object_type = 'INLINE FUNCTION')
            SET @object_type = 'FUNCTION'
        SET @stmt = N'drop ' + @object_type + N' ' + @object_name
        PRINT 'Executing "' + @stmt + N'"'
        EXECUTE(@stmt)
    END
    FETCH object_to_drop_cursor INTO @object_type, @object_name
END

CLOSE object_to_drop_cursor
DEALLOCATE object_to_drop_cursor
DROP TABLE #objects_to_drop

PRINT 'Done dropping all DMF and Shared Registered Server procedures'
GO


--------------------------------------------------------------
-- This section contains shared registered servers information
--------------------------------------------------------------

IF NOT EXISTS (SELECT * FROM sys.tables where name ='sysmanagement_shared_server_groups_internal')
BEGIN
	PRINT 'Creating table [msdb].[dbo].[sysmanagement_shared_server_groups_internal]...'
	CREATE TABLE [msdb].[dbo].[sysmanagement_shared_server_groups_internal]
	(
		server_group_id INT NOT NULL IDENTITY PRIMARY KEY NONCLUSTERED,
		name sysname NOT NULL,
		description NVARCHAR (2048) NOT NULL,
		-- You can only have a registered server of the same type within a group of the same type
		-- So the group needs to have knowledge of its type
		server_type INT NOT NULL,
		-- Explicitly allow NULLs for this column, so we are independent of server configuration
		parent_id INT NULL,
		-- this flag indicates whether the group is a system builtin group
		is_system_object BIT DEFAULT 0,
		-- Make sure each name is unique per parent
		CONSTRAINT [UQ_sysmanagement_unique_group_name_per_parent] UNIQUE(parent_id, name)
	);

    CREATE CLUSTERED    INDEX [IX_sysmanagement_shared_server_groups_clustParentGroupID] ON
                [dbo].[sysmanagement_shared_server_groups_internal] (parent_id);
    CREATE NONCLUSTERED INDEX [IX_sysmanagement_shared_server_groups_name] ON
                [dbo].[sysmanagement_shared_server_groups_internal] (name)

    -- populate with the builtin server groups
    -- Note: server_type_id values must match the ServerType enumeration
    INSERT INTO [msdb].[dbo].[sysmanagement_shared_server_groups_internal] (name, description, server_type, parent_id, is_system_object)
    VALUES (N'DatabaseEngineServerGroup', N'Builtin group that contains the DatabaseEngine servers', 0, null, 1);

    INSERT INTO [msdb].[dbo].[sysmanagement_shared_server_groups_internal] (name, description, server_type, parent_id, is_system_object)
    VALUES (N'AnalysisServicesServerGroup', N'Builtin group that contains the AnalysisServices servers', 1, null, 1);

    INSERT INTO [msdb].[dbo].[sysmanagement_shared_server_groups_internal] (name, description, server_type, parent_id, is_system_object)
    VALUES (N'ReportingServicesServerGroup', N'Builtin group that contains the ReportingServices servers', 2, null, 1);

    INSERT INTO [msdb].[dbo].[sysmanagement_shared_server_groups_internal] (name, description, server_type, parent_id, is_system_object)
    VALUES (N'IntegrationServicesServerGroup', N'Builtin group that contains the IntegrationServices servers', 3, null, 1);

    INSERT INTO [msdb].[dbo].[sysmanagement_shared_server_groups_internal] (name, description, server_type, parent_id, is_system_object)
    VALUES (N'SqlServerCompactEditionServerGroup', N'Builtin group that contains the SqlServerCompactEdition servers', 4, null, 1);

END
ELSE
BEGIN
	UPDATE 
		[msdb].[dbo].[sysmanagement_shared_server_groups_internal]
	SET 
		name = N'SqlServerCompactEditionServerGroup',
		description = N'Builtin group that contains the SqlServerCompactEdition servers'
	WHERE
		name = N'SqlServerEverywhereServerGroup' and
		server_type = 4 and
		is_system_object = 1;
END

GO
PRINT 'Creating trigger [sysmanagement_delete_shared_server_group_trigger]...'
GO
CREATE TRIGGER [sysmanagement_delete_shared_server_group_trigger] on [msdb].[dbo].[sysmanagement_shared_server_groups_internal] 
FOR DELETE
AS
BEGIN
    -- system server groups should not be deleted
    IF EXISTS (SELECT * FROM deleted where is_system_object = 1)
    BEGIN
        RAISERROR (35008, 1, 1)
        ROLLBACK TRANSACTION
    END
END
GO

IF NOT EXISTS ( SELECT * FROM sys.tables WHERE name = 'sysmanagement_shared_registered_servers_internal')
BEGIN
    PRINT 'Creating table [msdb].[dbo].[sysmanagement_shared_registered_servers_internal]...'
    CREATE TABLE [msdb].[dbo].[sysmanagement_shared_registered_servers_internal]
    (
        server_id INT NOT NULL IDENTITY PRIMARY KEY NONCLUSTERED,
        server_group_id INT FOREIGN KEY REFERENCES [msdb].[dbo].[sysmanagement_shared_server_groups_internal] (server_group_id) ON DELETE CASCADE,
        name sysname NOT NULL,
        server_name sysname NOT NULL,
        description NVARCHAR(2048) NOT NULL,
        -- While the server group has the knowledge of the server type,
        -- you also need the Server Type here, because you can have a root registered server with no parent group
        server_type INT NOT NULL,
        -- Make sure each registered name is unique in each group
        CONSTRAINT [UQ_sysmanagement_unique_server_name_per_group] UNIQUE(server_group_id, name)
    )

    CREATE CLUSTERED    INDEX [IX_sysmanagement_shared_registered_servers_clustGroupID] ON
                [dbo].[sysmanagement_shared_registered_servers_internal] (server_group_id);
    CREATE NONCLUSTERED INDEX [IX_sysmanagement_shared_registered_servers_name] ON
                [dbo].[sysmanagement_shared_registered_servers_internal] (name)
END
GO

PRINT 'Creating view [dbo].[sysmanagement_shared_server_groups]...'
GO
CREATE VIEW [dbo].[sysmanagement_shared_server_groups]
AS
(
    SELECT server_group_id, name, description, server_type, parent_id, is_system_object,
    (select COUNT(*) from [msdb].[dbo].[sysmanagement_shared_server_groups_internal] sgChild where sgChild.parent_id = sg.server_group_id) as num_server_group_children,
    (select COUNT(*) from [msdb].[dbo].[sysmanagement_shared_registered_servers_internal] rsChild where rsChild.server_group_id = sg.server_group_id) as num_registered_server_children
    FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal] sg
)
GO

PRINT 'Creating view [dbo].[sysmanagement_shared_registered_servers]...'
GO
CREATE VIEW [dbo].[sysmanagement_shared_registered_servers]
AS
(
    SELECT server_id, server_group_id, name, server_name, description, server_type
    FROM [msdb].[dbo].[sysmanagement_shared_registered_servers_internal]
)
GO

PRINT 'Creating procedure [dbo].[sp_sysmanagement_verify_shared_server_type]...'
GO
CREATE PROCEDURE [dbo].[sp_sysmanagement_verify_shared_server_type]
    @server_type INT
AS
BEGIN
    IF (@server_type IS NULL)
    BEGIN
        RAISERROR (35009, -1, -1)
        RETURN(1)
    END
    
    -- 0 --> DatabaseEngineServerGroup, 1 --> AnalysisServicesServerGroup, 2 --> ReportingServicesServerGroup, 3 --> IntegrationServicesServerGroup, 4 --> SqlServerCompactEditionServerGroup
    IF (@server_type < 0 OR @server_type > 4)
    BEGIN
        RAISERROR (35010, -1, -1, @server_type)
        RETURN (1)
    END
    
    RETURN (0)
END
GO


PRINT 'Creating procedure [dbo].[sp_sysmanagement_add_shared_server_group]...'
GO
CREATE PROCEDURE [dbo].[sp_sysmanagement_add_shared_server_group]
    @name sysname,
    @description NVARCHAR (2048) = N'',
    @parent_id INT,
    @server_type INT,
    @server_group_id INT OUTPUT
AS
BEGIN
    DECLARE @retval INT

    EXECUTE @retval = sp_sysmanagement_verify_shared_server_type @server_type

    IF (@retval <> 0)
        RETURN(1) -- Failure
    
    -- user created server groups should have a valid parent 
    IF( (@parent_id IS NULL) OR 
        (@parent_id NOT IN (SELECT sg.server_group_id FROM msdb.dbo.sysmanagement_shared_server_groups_internal sg)))
    BEGIN
        RAISERROR (35001, -1, -1)
        RETURN (1)
    END

    IF EXISTS (SELECT * FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal]  sg 
                WHERE @parent_id = sg.server_group_id AND @server_type <> sg.server_type)
    BEGIN
        RAISERROR (35002, -1, -1)
        RETURN (1)
    END    
    
    INSERT INTO [msdb].[dbo].[sysmanagement_shared_server_groups_internal]
        (name, description, parent_id, server_type)
    VALUES
        (
        @name, 
        @description, 
        @parent_id, 
        @server_type
        )

    SELECT @server_group_id = SCOPE_IDENTITY()
    RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_sysmanagement_add_shared_registered_server]...'
GO
CREATE PROCEDURE [dbo].[sp_sysmanagement_add_shared_registered_server]
    @name sysname,
    @server_group_id INT,
    @server_name sysname,
    @description NVARCHAR(2048) = N'',
    @server_type INT,
    @server_id INT OUTPUT
AS
BEGIN
    DECLARE @retval INT

    EXECUTE @retval = sp_sysmanagement_verify_shared_server_type @server_type

    IF (@retval <> 0)
        RETURN(1) -- Failure
    
    IF( (@server_group_id IS NULL) OR 
        (@server_group_id NOT IN (SELECT sg.server_group_id FROM msdb.dbo.sysmanagement_shared_server_groups_internal sg)))
    BEGIN
        RAISERROR (35001, -1, -1)
        RETURN (1)
    END

    IF EXISTS (SELECT * FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal]  sg 
                WHERE @server_group_id = sg.server_group_id AND @server_type <> sg.server_type)
    BEGIN
        RAISERROR (35002, -1, -1)
        RETURN (1)
    END    

    IF (@server_name IS NULL)
    BEGIN
        RAISERROR(14618, -1, 1, '@server_name')
        RETURN(1)   
    END

    set @server_name = LTRIM(@server_name)
    set @server_name = RTRIM(@server_name)

    -- Disallow relative names
    IF ('.' = @server_name) OR
        (1 = CHARINDEX(N'.\', @server_name)) OR
        (1 = CHARINDEX(N'LOCALHOST\', UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS))) OR
        (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) = 'LOCALHOST') OR
        (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) = '(LOCAL)')
    BEGIN
        RAISERROR (35011, -1, -1)
        RETURN (1)
    END

    IF (UPPER(@@SERVERNAME collate SQL_Latin1_General_CP1_CS_AS) = UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS))
    BEGIN
        RAISERROR (35012, -1, -1)
        RETURN (1)
    END

    INSERT INTO [msdb].[dbo].[sysmanagement_shared_registered_servers_internal]
        (server_group_id, name, server_name, description, server_type)
    VALUES
        (@server_group_id, @name, @server_name, @description, @server_type)
        
    SELECT @server_id = SCOPE_IDENTITY()
    RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_sysmanagement_delete_shared_server_group]...'
GO
CREATE PROCEDURE [dbo].[sp_sysmanagement_delete_shared_server_group]
    @server_group_id INT
AS
BEGIN
    IF NOT EXISTS (SELECT * FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal] WHERE server_group_id = @server_group_id)
    BEGIN
        RAISERROR (35004, -1, -1)
        RETURN(1)
    END;

    WITH ChildGroups (parent_id, server_group_id, name, server_type, server_level)
    AS
    (
        -- Anchor
        SELECT g.parent_id, g.server_group_id, g.name, g.server_type, 0 AS server_level
        FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal] AS g
        WHERE g.server_group_id = @server_group_id
        UNION ALL
        -- Recursive definition
        SELECT r.parent_id, r.server_group_id, r.name, r.server_type, server_level + 1
        FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal] AS r
        INNER JOIN ChildGroups AS children ON r.parent_id = children.server_group_id
    )
    -- Execute CTE to delete the hierarchy of server groups
    DELETE FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal]
        FROM ChildGroups children
        JOIN [msdb].[dbo].[sysmanagement_shared_server_groups_internal] ServerGroups
            ON children.server_group_id = ServerGroups.server_group_id
    RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_sysmanagement_delete_shared_registered_server]...'
GO
CREATE PROCEDURE [dbo].[sp_sysmanagement_delete_shared_registered_server]
    @server_id INT
AS
BEGIN
    IF NOT EXISTS (SELECT * FROM [msdb].[dbo].[sysmanagement_shared_registered_servers_internal] WHERE server_id = @server_id)
    BEGIN
        RAISERROR (35007, -1, -1)
        RETURN(1)
    END
    
    DELETE FROM [msdb].[dbo].[sysmanagement_shared_registered_servers_internal]
    WHERE
            server_id = @server_id
    RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_sysmanagement_move_shared_server_group]...'
GO
CREATE PROCEDURE [dbo].[sp_sysmanagement_move_shared_server_group]
    @server_group_id INT,
    @new_parent_id INT
AS
BEGIN
    IF (@new_parent_id IS NULL) OR 
        NOT EXISTS (SELECT * FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal] WHERE server_group_id = @new_parent_id)
    BEGIN
        RAISERROR (35001, -1, -1)
        RETURN(1)
    END

    IF (@new_parent_id IS NOT NULL)
        AND ((SELECT server_type FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal] WHERE server_group_id = @new_parent_id)
                <>
             (SELECT server_type FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal] WHERE server_group_id = @server_group_id))
    BEGIN
        RAISERROR (35002, -1, -1)
        RETURN(1)
    END

    DECLARE @DeletedGroups TABLE
    (
        server_group_id int
    );

    -- Check if the destination group you're moving to isn't already a descendant of the current group
    WITH ChildGroups (parent_id, server_group_id, server_level)
    AS
    (
        -- Anchor
        SELECT g.parent_id, g.server_group_id, 0 AS server_level
        FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal] AS g
        WHERE g.server_group_id = @server_group_id
        UNION ALL
        -- Recursive definition
        SELECT r.parent_id, r.server_group_id, server_level + 1
        FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal] AS r
        INNER JOIN ChildGroups AS children ON r.parent_id = children.server_group_id
    )
    -- Execute CTE
    INSERT INTO @DeletedGroups
    SELECT server_group_id FROM ChildGroups

    IF (SELECT COUNT(*) FROM @DeletedGroups WHERE server_group_id = @new_parent_id) > 0
    BEGIN
        RAISERROR (35003, -1, -1)
        RETURN(1)
    END
    
    UPDATE [msdb].[dbo].[sysmanagement_shared_server_groups_internal]
        SET parent_id = @new_parent_id
    WHERE
        server_group_id = @server_group_id
    RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_sysmanagement_move_shared_registered_server]...'
GO
CREATE PROCEDURE [dbo].[sp_sysmanagement_move_shared_registered_server]
    @server_id INT,
    @new_parent_id INT
AS
BEGIN
    IF (@server_id IS NULL)
    BEGIN
        RAISERROR (35006, -1, -1)
        RETURN(1)
    END
    
    IF NOT EXISTS (SELECT * FROM [msdb].[dbo].[sysmanagement_shared_registered_servers_internal] WHERE server_id = @server_id)
    BEGIN
        RAISERROR (35007, -1, -1)
        RETURN(1)
    END
    
    IF (@new_parent_id IS NULL) OR 
        NOT EXISTS (SELECT * FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal] WHERE server_group_id = @new_parent_id)
    BEGIN
        RAISERROR (35001, -1, -1)
        RETURN(1)
    END

    IF (@new_parent_id IS NOT NULL)
        AND ((SELECT server_type FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal] WHERE server_group_id = @new_parent_id)
                <>
             (SELECT server_type FROM [msdb].[dbo].[sysmanagement_shared_registered_servers_internal] WHERE server_id = @server_id))
    BEGIN
        RAISERROR (35002, -1, -1)
        RETURN(1)
    END
    
    UPDATE [msdb].[dbo].[sysmanagement_shared_registered_servers_internal]
        SET server_group_id = @new_parent_id
    WHERE
        server_id = @server_id
    RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_sysmanagement_update_shared_server_group]...'
GO
CREATE PROCEDURE [dbo].[sp_sysmanagement_update_shared_server_group]
    @server_group_id INT,
    @description NVARCHAR (2048) = NULL
AS
BEGIN
    IF (@server_group_id IS NULL)
    BEGIN
        RAISERROR (35005, -1, -1)
        RETURN(1)
    END

    IF NOT EXISTS (SELECT * FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal] WHERE server_group_id = @server_group_id)
    BEGIN
        RAISERROR (35004, -1, -1)
        RETURN(1)
    END

    UPDATE [msdb].[dbo].[sysmanagement_shared_server_groups_internal]
        SET description = ISNULL(@description, description)
    WHERE
        server_group_id = @server_group_id
    
    RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_sysmanagement_update_shared_registered_server]...'
GO
CREATE PROCEDURE [dbo].[sp_sysmanagement_update_shared_registered_server]
    @server_id INT,
    @server_name sysname = NULL,
    @description NVARCHAR(2048) = NULL
AS
BEGIN
    IF (@server_id IS NULL)
    BEGIN
        RAISERROR (35006, -1, -1)
        RETURN(1)
    END
    
    IF NOT EXISTS (SELECT * FROM [msdb].[dbo].[sysmanagement_shared_registered_servers_internal] WHERE server_id = @server_id)
    BEGIN
        RAISERROR (35007, -1, -1)
        RETURN(1)
    END

    IF (@server_name IS NULL)
    BEGIN
        SET @server_name = (select server_name FROM [msdb].[dbo].[sysmanagement_shared_registered_servers_internal] WHERE server_id = @server_id)
    END

    set @server_name = LTRIM(@server_name)
    set @server_name = RTRIM(@server_name)

    -- Disallow relative names
    IF ('.' = @server_name) OR
        (1 = CHARINDEX(N'.\', @server_name)) OR
        (1 = CHARINDEX(N'LOCALHOST\', UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS))) OR
        (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) = 'LOCALHOST') OR
        (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) = '(LOCAL)')
    BEGIN
        RAISERROR (35011, -1, -1)
        RETURN (1)
    END

    IF (UPPER(@@SERVERNAME collate SQL_Latin1_General_CP1_CS_AS) = UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS))
    BEGIN
        RAISERROR (35012, -1, -1)
        RETURN (1)
    END

    UPDATE [msdb].[dbo].[sysmanagement_shared_registered_servers_internal]
        SET server_name = ISNULL(@server_name, server_name),
            description = ISNULL(@description, description)
    WHERE
        server_id = @server_id
    RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_sysmanagement_rename_shared_server_group]...'
GO
CREATE PROCEDURE [dbo].[sp_sysmanagement_rename_shared_server_group]
    @server_group_id INT,
    @new_name sysname
AS
BEGIN
    IF (@server_group_id IS NULL)
    BEGIN
        RAISERROR (35006, -1, -1)
        RETURN(1)
    END
    
    IF NOT EXISTS (SELECT * FROM [msdb].[dbo].[sysmanagement_shared_server_groups_internal] WHERE server_group_id = @server_group_id)
    BEGIN
        RAISERROR (35007, -1, -1)
        RETURN(1)
    END
    
    IF (@new_name IS NULL or LEN(@new_name) = 0)
    BEGIN
      RAISERROR(21263, -1, -1, '@new_name')
      RETURN(1) -- Failure
    END

    UPDATE [msdb].[dbo].[sysmanagement_shared_server_groups_internal]
        SET name = @new_name
    WHERE
        server_group_id = @server_group_id

    RETURN (0)
END
GO


PRINT 'Creating procedure [dbo].[sp_sysmanagement_rename_shared_registered_server]...'
GO
CREATE PROCEDURE [dbo].[sp_sysmanagement_rename_shared_registered_server]
    @server_id INT,
    @new_name sysname
AS
BEGIN
    IF (@server_id IS NULL)
    BEGIN
        RAISERROR (35006, -1, -1)
        RETURN(1)
    END
    
    IF NOT EXISTS (SELECT * FROM [msdb].[dbo].[sysmanagement_shared_registered_servers_internal] WHERE server_id = @server_id)
    BEGIN
        RAISERROR (35007, -1, -1)
        RETURN(1)
    END
    
    IF (@new_name IS NULL or LEN(@new_name) = 0)
    BEGIN
      RAISERROR(21263, -1, -1, '@new_name')
      RETURN(1) -- Failure
    END

    UPDATE [msdb].[dbo].[sysmanagement_shared_registered_servers_internal]
        SET name = @new_name
    WHERE
        server_id = @server_id

    RETURN (0)
END
GO


-----------------------------------------------------------
-- This section contains facet information
-----------------------------------------------------------
IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_management_facets')
BEGIN
    PRINT 'Creating table [dbo].[syspolicy_management_facets]...';
    CREATE TABLE [dbo].[syspolicy_management_facets] (
        management_facet_id int NOT NULL IDENTITY PRIMARY KEY,
        name nvarchar(MAX) NOT NULL,    -- this is the name of the management facet
        execution_mode int NOT NULL
    );

END

GO

-- Create a temp table for the facets that are supposed to ship out of the box
-- Later the script will use the temp table to add any new facets to the syspolicy_management_facets table
declare @temp_syspolicy_management_facets TABLE(
    name nvarchar(MAX) NOT NULL,    -- this is the name of the management facet
    execution_mode int NOT NULL);
                
-- populate the temp table with facets shipping out of the box
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ApplicationRole', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('AsymmetricKey', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Audit', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('BackupDevice', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('BrokerPriority', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('BrokerService', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Certificate', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Computer', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Credential', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('CryptographicProvider', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Database', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('DatabaseAuditSpecification', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('DatabaseDdlTrigger', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('DatabaseRole', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('DataFile', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Default', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('DeployedDac', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Endpoint', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Utility', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('FileGroup', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('FullTextCatalog', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('FullTextIndex', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('FullTextStopList', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IDatabaseMaintenanceFacet', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IDatabaseOptions', 6);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IDatabasePerformanceFacet', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IDatabaseSecurityFacet', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ILoginOptions', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IMultipartNameFacet', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('INameFacet', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ITableOptions', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IUserOptions', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IViewOptions', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Index', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerAuditFacet', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerConfigurationFacet', 6);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerInformation', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerPerformanceFacet', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerProtocolSettingsFacet', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerSecurityFacet', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerSetupFacet', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerSelectionFacet', 0);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerSettings', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ISurfaceAreaConfigurationForAnalysisServer', 0);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ISurfaceAreaConfigurationForReportingServices', 0);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ISurfaceAreaFacet', 6);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('LinkedServer', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('LogFile', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Login', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('MessageType', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('PartitionFunction', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('PartitionScheme', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Processor', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('PlanGuide', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('RemoteServiceBinding', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ResourceGovernor', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ResourcePool', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Rule', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Schema', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Server', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ServerAuditSpecification', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ServerDdlTrigger', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ServiceContract', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ServiceQueue', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ServiceRoute', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Statistic', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('StoredProcedure', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('SymmetricKey', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Synonym', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Table', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Trigger', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('User', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('UserDefinedAggregate', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('UserDefinedDataType', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('UserDefinedFunction', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('UserDefinedTableType', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('UserDefinedType', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('View', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Volume', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('WorkloadGroup', 7);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('XmlSchemaCollection', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IDataFilePerformanceFacet', 4);
INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ILogFilePerformanceFacet', 4);

-- Facets can be updated and inserted, however deleting a facet is dangerous because of references from conditions

-- Update the modes on the facets
UPDATE facets
SET facets.execution_mode = tempFacets.execution_mode 
FROM @temp_syspolicy_management_facets tempFacets, [msdb].[dbo].[syspolicy_management_facets] facets
WHERE tempFacets.name = facets.name AND tempFacets.execution_mode <> facets.execution_mode;

-- Now populate the syspolicy_management_facets table with the facets that are missing in the table
INSERT [msdb].[dbo].[syspolicy_management_facets] (name, execution_mode)
    SELECT tempFacets.name, tempFacets.execution_mode
    FROM @temp_syspolicy_management_facets tempFacets
    WHERE tempFacets.name NOT IN (SELECT distinct facets.name FROM [msdb].[dbo].[syspolicy_management_facets] facets)
    


GO

---------------------------------------------------------------
-- Relational storage for policy objects
---------------------------------------------------------------
IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_facet_events')
BEGIN
    PRINT 'Creating table [dbo].[syspolicy_facet_events]...';
    CREATE TABLE [dbo].[syspolicy_facet_events] (
        management_facet_id int NOT NULL REFERENCES [dbo].[syspolicy_management_facets],
        event_name sysname NOT NULL,                -- this is the name of the event
        target_type sysname NOT NULL,                -- type of the target (TABLE, PROCEDURE etc.)
        target_type_alias sysname NOT NULL);        -- this is an alias for the type of the target
                                                    -- it can happen that the same target type 
                                                    -- shows up as different strings in the event

    CREATE CLUSTERED INDEX [IX_facet_events_target_type_alias] ON 
        [dbo].[syspolicy_facet_events] (target_type_alias);
    CREATE UNIQUE INDEX [UX_facet_events] ON 
        [dbo].[syspolicy_facet_events] (management_facet_id, event_name, target_type, target_type_alias);
END
GO
-- The facet events are unique to an installation, thus delete what is there and then insert the new events
-- This technique will work for both a new install and an upgrade.
DELETE FROM [msdb].[dbo].[syspolicy_facet_events]

-- this is a pseudo event, so we're inserting it before the
-- consistency check is in place
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'SAC_ENDPOINT_CHANGE',N'SERVER',N'SERVER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ISurfaceAreaFacet'
 

GO
CREATE TRIGGER [syspolicy_validate_events] on [dbo].[syspolicy_facet_events]
AFTER INSERT, UPDATE
AS
BEGIN
    -- make sure that caller is dbo and all events inserted are real events.
    IF  (USER_ID() != 1) OR
        EXISTS (SELECT event_name FROM inserted 
                    WHERE event_name NOT IN(SELECT type_name from sys.event_notification_event_types))
    BEGIN
        RAISERROR(N'Unknown event name inserted into [dbo].[syspolicy_facet_events]', 1,1)
        ROLLBACK TRANSACTION
    END
END
GO

-- Insert the facet events               
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_APPLICATION_ROLE',N'APPLICATION ROLE',N'APPLICATION ROLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ApplicationRole'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_APPLICATION_ROLE',N'APPLICATION ROLE',N'APPLICATION ROLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ApplicationRole'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_ASYMMETRIC_KEY',N'ASYMMETRICKEY',N'ASYMMETRIC KEY'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'AsymmetricKey'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_ASYMMETRIC_KEY',N'ASYMMETRICKEY',N'ASYMMETRIC KEY'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'AsymmetricKey'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'ASYMMETRICKEY',N'ASYMMETRIC KEY'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'AsymmetricKey'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_ROLE',N'ROLE',N'ROLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'DatabaseRole'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_ROLE',N'ROLE',N'ROLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'DatabaseRole'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'ROLE',N'ROLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'DatabaseRole'

INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_ENDPOINT',N'ENDPOINT',N'ENDPOINT'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'Endpoint'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_ENDPOINT',N'ENDPOINT',N'ENDPOINT'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'Endpoint'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_AUTHORIZATION_SERVER',N'ENDPOINT',N'ENDPOINT'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'Endpoint'
   
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_DATABASE',N'DATABASE',N'DATABASE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IDatabaseOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_DATABASE',N'DATABASE',N'DATABASE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IDatabaseOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'DATABASE',N'DATABASE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IDatabaseOptions'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_FUNCTION',N'FUNCTION',N'FUNCTION'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_FUNCTION',N'FUNCTION',N'FUNCTION'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'RENAME',N'FUNCTION',N'FUNCTION'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_PROCEDURE',N'PROCEDURE',N'PROCEDURE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_PROCEDURE',N'PROCEDURE',N'PROCEDURE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'RENAME',N'PROCEDURE',N'PROCEDURE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_SYNONYM',N'SYNONYM',N'SYNONYM'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_TABLE',N'TABLE',N'TABLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_TABLE',N'TABLE',N'TABLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'RENAME',N'TABLE',N'TABLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_TYPE',N'TYPE',N'TYPE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'RENAME',N'TYPE',N'TYPE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_VIEW',N'VIEW',N'VIEW'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_VIEW',N'VIEW',N'VIEW'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'RENAME',N'VIEW',N'VIEW'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_XML_SCHEMA_COLLECTION',N'XMLSCHEMACOLLECTION',N'XMLSCHEMACOLLECTION'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_XML_SCHEMA_COLLECTION',N'XMLSCHEMACOLLECTION',N'XMLSCHEMACOLLECTION'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'RENAME',N'XMLSCHEMACOLLECTION',N'XMLSCHEMACOLLECTION'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_SCHEMA',N'TABLE',N'TABLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_SCHEMA',N'VIEW',N'VIEW'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_SCHEMA',N'FUNCTION',N'FUNCTION'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_SCHEMA',N'PROCEDURE',N'PROCEDURE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_SCHEMA',N'SYNONYM',N'SYNONYM'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_SCHEMA',N'TYPE',N'TYPE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_SCHEMA',N'XMLSCHEMACOLLECTION',N'XMLSCHEMACOLLECTION'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IMultipartNameFacet'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'AUDIT_SERVER_OPERATION_EVENT',N'SERVER',N'SERVER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IServerConfigurationFacet'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'AUDIT_SERVER_OPERATION_EVENT',N'SERVER',N'SERVER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ISurfaceAreaFacet'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_ENDPOINT',N'SERVER',N'SERVER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ISurfaceAreaFacet'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_ENDPOINT',N'SERVER',N'SERVER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ISurfaceAreaFacet'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'DROP_ENDPOINT',N'SERVER',N'SERVER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ISurfaceAreaFacet'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_LOGIN',N'LOGIN',N'LOGIN'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ILoginOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_LOGIN',N'LOGIN',N'LOGIN'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ILoginOptions'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_RESOURCE_POOL',N'RESOURCEPOOL',N'RESOURCE POOL'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ResourcePool'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_RESOURCE_POOL',N'RESOURCEPOOL',N'RESOURCE POOL'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ResourcePool'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_SCHEMA',N'SCHEMA',N'SCHEMA'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'Schema'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_SCHEMA',N'SCHEMA',N'SCHEMA'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'Schema'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'SCHEMA',N'SCHEMA'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'Schema'
  
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_PROCEDURE',N'PROCEDURE',N'PROCEDURE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'StoredProcedure'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_PROCEDURE',N'PROCEDURE',N'PROCEDURE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'StoredProcedure'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'RENAME',N'PROCEDURE',N'PROCEDURE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'StoredProcedure'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'PROCEDURE',N'PROCEDURE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'StoredProcedure'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_SCHEMA',N'PROCEDURE',N'PROCEDURE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'StoredProcedure'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_TABLE',N'TABLE',N'TABLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ITableOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_TABLE',N'TABLE',N'TABLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ITableOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'RENAME',N'TABLE',N'TABLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ITableOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'TABLE',N'TABLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ITableOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_SCHEMA',N'TABLE',N'TABLE'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'ITableOptions'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_USER',N'USER',N'ASYMMETRIC KEY USER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IUserOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_USER',N'USER',N'CERTIFICATE USER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IUserOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_USER',N'USER',N'GROUP USER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IUserOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_USER',N'USER',N'SQL USER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IUserOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_USER',N'USER',N'WINDOWS USER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IUserOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_USER',N'USER',N'ASYMMETRIC KEY USER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IUserOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_USER',N'USER',N'CERTIFICATE USER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IUserOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_USER',N'USER',N'GROUP USER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IUserOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_USER',N'USER',N'SQL USER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IUserOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_USER',N'USER',N'WINDOWS USER'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IUserOptions'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_FUNCTION',N'FUNCTION',N'FUNCTION'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'UserDefinedFunction'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_FUNCTION',N'FUNCTION',N'FUNCTION'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'UserDefinedFunction'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'RENAME',N'FUNCTION',N'FUNCTION'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'UserDefinedFunction'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'FUNCTION',N'FUNCTION'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'UserDefinedFunction'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_SCHEMA',N'FUNCTION',N'FUNCTION'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'UserDefinedFunction'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_VIEW',N'VIEW',N'VIEW'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IViewOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_VIEW',N'VIEW',N'VIEW'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IViewOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'RENAME',N'VIEW',N'VIEW'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IViewOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'VIEW',N'VIEW'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IViewOptions'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_SCHEMA',N'VIEW',N'VIEW'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'IViewOptions'
 
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'CREATE_WORKLOAD_GROUP',N'WORKLOADGROUP',N'WORKLOAD GROUP'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'WorkloadGroup'
INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) 
 SELECT management_facet_id,N'ALTER_WORKLOAD_GROUP',N'WORKLOADGROUP',N'WORKLOAD GROUP'
 FROM [msdb].[dbo].[syspolicy_management_facets]
 WHERE name = 'WorkloadGroup'

        
GO
---------------------------------------------------------------
-- Condition object
---------------------------------------------------------------

IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_conditions_internal')
BEGIN
PRINT 'Creating table [dbo].[syspolicy_conditions_internal]...'
CREATE TABLE [dbo].[syspolicy_conditions_internal] (
    condition_id int IDENTITY(1,1),
    name sysname NOT NULL,
    date_created datetime default GETDATE(),
    description nvarchar(max) NOT NULL default (''),
    created_by sysname NOT NULL default SUSER_SNAME(),
    modified_by sysname NULL,
    date_modified datetime NULL,
    facet_id int,
    expression nvarchar(max),
    is_name_condition smallint default (0),
    obj_name sysname NULL,
    is_system bit NOT NULL default (0),
    CONSTRAINT [PK_syspolicy_conditions] PRIMARY KEY CLUSTERED (condition_id ASC),
    CONSTRAINT [UQ_syspolicy_conditions_name] UNIQUE(name)
    );
ALTER TABLE [dbo].[syspolicy_conditions_internal] 
    ADD CONSTRAINT [FK_syspolicy_conditions_internal_facet] FOREIGN KEY(facet_id)
    REFERENCES [dbo].[syspolicy_management_facets] (management_facet_id);
END
GO

PRINT 'Creating view [dbo].[syspolicy_conditions]...'
GO
CREATE VIEW [dbo].[syspolicy_conditions]
AS
    SELECT
        c.condition_id, c.name, c.date_created, c.description, c.created_by, 
        c.modified_by, c.date_modified, c.is_name_condition, mf.name AS facet, c.expression, c.obj_name, c.is_system 
    FROM [dbo].[syspolicy_conditions_internal] c 
    LEFT OUTER JOIN [dbo].[syspolicy_management_facets] mf ON c.facet_id = mf.management_facet_id
GO

CREATE PROCEDURE [dbo].[sp_syspolicy_check_membership]
@role sysname,
@raiserror bit = 1
AS
BEGIN
	-- make sure that the caller is dbo or @role
	IF ( IS_MEMBER(@role) != 1 AND USER_ID() != 1)
	BEGIN
		IF (@raiserror = 1)
		BEGIN
			RAISERROR(15003, -1, -1, @role);
		END
		RETURN 15003;
	END
	
	RETURN 0;
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_add_condition]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_add_condition] 
@name sysname,
@description nvarchar(max) = N'',
@facet nvarchar(max),
@expression nvarchar(max),
@is_name_condition smallint = 0,
@obj_name sysname = NULL,
@condition_id int = NULL OUTPUT 
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DECLARE @retval              INT
	DECLARE @facet_id            INT
	DECLARE @null_column	sysname
	
	SET @null_column = NULL

    IF (@name IS NULL OR @name = N'')
        SET @null_column = '@name'
    ELSE IF( @description IS NULL)
        SET @null_column = '@description'
    ELSE IF( @facet IS NULL)
        SET @null_column = '@facet'
    ELSE IF( @expression IS NULL)
        SET @null_column = '@expression'

    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_syspolicy_add_condition')
        RETURN(1)
    END

    IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_conditions WHERE name = @name)
    BEGIN
        RAISERROR(34010, -1, -1, 'Condition', @name)
        RETURN(1)
    END

    SET @facet_id = (SELECT management_facet_id FROM [dbo].[syspolicy_management_facets] WHERE name = @facet)
    IF (@facet_id IS NULL)
    BEGIN
        RAISERROR (34014, -1, -1)
        RETURN(1)
    END

    INSERT INTO msdb.dbo.syspolicy_conditions_internal(name, description,facet_id,expression,is_name_condition,obj_name) 
        VALUES(@name, @description,@facet_id,@expression,@is_name_condition,@obj_name)
    SELECT @retval = @@error
    SET @condition_id = SCOPE_IDENTITY()
    RETURN(@retval)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_verify_condition_identifiers]...'
GO
-----------------------------------------------------------
-- This procedure verifies if a condition exists
-- The caller can pass either the condition name or the id
-----------------------------------------------------------
CREATE PROCEDURE [dbo].[sp_syspolicy_verify_condition_identifiers]
@condition_name sysname = NULL OUTPUT, 
@condition_id int = NULL OUTPUT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

  IF ((@condition_name IS NULL)     AND (@condition_id IS NULL)) OR
     ((@condition_name IS NOT NULL) AND (@condition_id IS NOT NULL))
  BEGIN
    RAISERROR(14524, -1, -1, '@condition_name', '@condition_id')
    RETURN(1) -- Failure
  END

  -- Check id
  IF (@condition_id IS NOT NULL)
  BEGIN
    SELECT @condition_name = name
    FROM msdb.dbo.syspolicy_conditions
    WHERE (condition_id = @condition_id)
    
    -- the view would take care of all the permissions issues.
    IF (@condition_name IS NULL) 
    BEGIN
      DECLARE @condition_id_as_char VARCHAR(36)
      SELECT @condition_id_as_char = CONVERT(VARCHAR(36), @condition_id)
      RAISERROR(14262, -1, -1, '@condition_id', @condition_id_as_char)
      RETURN(1) -- Failure
    END
  END
  ELSE
  -- Check name
  IF (@condition_name IS NOT NULL)
  BEGIN
    -- get the corresponding condition_id (if the condition exists)
    SELECT @condition_id = condition_id
    FROM msdb.dbo.syspolicy_conditions
    WHERE (name = @condition_name)
    
    -- the view would take care of all the permissions issues.
    IF (@condition_id IS NULL) 
    BEGIN
      RAISERROR(14262, -1, -1, '@condition_name', @condition_name)
      RETURN(1) -- Failure
    END
  END

  RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_update_condition]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_update_condition] 
@name sysname = NULL,
@condition_id int = NULL,
@facet nvarchar(max) = NULL,
@expression nvarchar(max) = NULL,
@description nvarchar(max) = NULL,
@is_name_condition smallint = NULL,
@obj_name sysname = NULL
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DECLARE @retval              INT
	DECLARE @facet_id            INT

    EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @name, @condition_id OUTPUT
    IF (@retval <> 0)
        RETURN (1)

    IF (@facet IS NOT NULL)
    BEGIN
        SET @facet_id = (SELECT management_facet_id FROM [dbo].[syspolicy_management_facets] WHERE name = @facet)
        IF (@facet_id IS NULL)
        BEGIN
            RAISERROR (34014, -1, -1)
            RETURN(1)
        END
    END

    UPDATE msdb.[dbo].[syspolicy_conditions_internal] 
    SET
        description = ISNULL(@description, description),
        facet_id = ISNULL(@facet_id, facet_id),
        expression = ISNULL(@expression, expression),
        is_name_condition = ISNULL(@is_name_condition, is_name_condition),
        obj_name = ISNULL(@obj_name, obj_name)
    WHERE condition_id = @condition_id
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_condition]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_delete_condition] 
@name sysname = NULL,
@condition_id int = NULL
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DECLARE @retval              INT

    EXEC @retval = sp_syspolicy_verify_condition_identifiers @name, @condition_id OUTPUT
    IF (@retval <> 0)
        RETURN (1)

    IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_policies WHERE condition_id = @condition_id)
    BEGIN
        RAISERROR(34012,-1,-1,'Condition','Policy')
        RETURN (1)
    END

    DELETE msdb.dbo.syspolicy_conditions_internal
    WHERE condition_id = @condition_id
    
    RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_rename_condition]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_rename_condition] 
@name sysname = NULL,
@condition_id int = NULL,
@new_name sysname = NULL
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

    IF (@new_name IS NULL or LEN(@new_name) = 0)
    BEGIN
      RAISERROR(21263, -1, -1, '@new_name')
      RETURN(1) -- Failure
    END

    DECLARE @retval              INT

    EXEC @retval = sp_syspolicy_verify_condition_identifiers @name, @condition_id OUTPUT
    IF (@retval <> 0)
        RETURN (1)

    UPDATE msdb.[dbo].[syspolicy_conditions_internal] 
    SET name = @new_name
    WHERE condition_id = @condition_id

    SELECT @retval = @@error
    RETURN(@retval)
END
GO

---------------------------------------------------------------
-- table for Policy category
---------------------------------------------------------------
IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_policy_categories_internal')
BEGIN
    PRINT 'Creating table [dbo].[syspolicy_policy_categories_internal]...';
    CREATE TABLE [dbo].[syspolicy_policy_categories_internal] (
        policy_category_id int IDENTITY(1,1),
        name sysname,
        mandate_database_subscriptions bit default 1 NOT NULL,
        CONSTRAINT [PK_syspolicy_policy_categories] PRIMARY KEY CLUSTERED (policy_category_id ASC),
        CONSTRAINT [UQ_syspolicy_policy_categories_name] UNIQUE(name)
        );
END
GO

PRINT 'Creating view [dbo].[syspolicy_policy_categories]...'
GO
CREATE VIEW [dbo].[syspolicy_policy_categories]
AS
    SELECT     
        policy_category_id,
        name,
        mandate_database_subscriptions
    FROM [dbo].[syspolicy_policy_categories_internal]
GO

---------------------------------------------------------------
-- ObjectSet object
---------------------------------------------------------------
IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_object_sets_internal')
BEGIN
    PRINT 'Creating table [dbo].[syspolicy_object_sets_internal]...'
CREATE TABLE [dbo].[syspolicy_object_sets_internal] (
    object_set_id int NOT NULL IDENTITY(1,1),
    object_set_name sysname NOT NULL,
    facet_id int,
    is_system bit NOT NULL default (0),
    CONSTRAINT [PK_syspolicy_object_sets] PRIMARY KEY CLUSTERED (object_set_id),
    CONSTRAINT [UQ_syspolicy_object_sets_name] UNIQUE(object_set_name)
)

ALTER TABLE [dbo].[syspolicy_object_sets_internal]
    ADD CONSTRAINT [FK_syspolicy_object_sets_syspolicy_management_facets] FOREIGN KEY(facet_id)
    REFERENCES [dbo].[syspolicy_management_facets] (management_facet_id)
    ON DELETE CASCADE
END
GO

PRINT 'Creating view [dbo].[syspolicy_object_sets]...'
GO
CREATE VIEW [dbo].[syspolicy_object_sets]
AS
    SELECT     
        os.object_set_id,
        os.object_set_name,
        os.facet_id,
        facet.name as facet_name,
        os.is_system
    FROM [dbo].[syspolicy_object_sets_internal] AS os INNER JOIN [dbo].[syspolicy_management_facets] AS facet
    ON os.facet_id = facet.management_facet_id
GO

-----------------------------------------------------------
-- This procedure verifies if a object set definition exists
-- The caller can pass either the name or the id
-----------------------------------------------------------
PRINT 'Creating procedure [dbo].[sp_syspolicy_verify_object_set_identifiers]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_verify_object_set_identifiers]
@name sysname = NULL OUTPUT, 
@object_set_id int = NULL OUTPUT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

  IF ((@name IS NULL)     AND (@object_set_id IS NULL)) OR
     ((@name IS NOT NULL) AND (@object_set_id IS NOT NULL))
  BEGIN
    -- TODO: Verify the error message is appropriate for object sets as well and not specific to policies
    RAISERROR(14524, -1, -1, '@name', '@object_set_id')
    RETURN(1) -- Failure
  END

  -- Check id
  IF (@object_set_id IS NOT NULL)
  BEGIN
    SELECT @name = object_set_name
    FROM msdb.dbo.syspolicy_object_sets
    WHERE (object_set_id = @object_set_id)
    
    -- the view would take care of all the permissions issues.
    IF (@name IS NULL) 
    BEGIN
        -- TODO: Where did 36 come from? Is this the total lenght of characters for an int?
      DECLARE @object_set_id_as_char VARCHAR(36)
      SELECT @object_set_id_as_char = CONVERT(VARCHAR(36), @object_set_id)
      RAISERROR(14262, -1, -1, '@object_set_id', @object_set_id_as_char)
      RETURN(1) -- Failure
    END
  END
  ELSE
  -- Check name
  IF (@name IS NOT NULL)
  BEGIN
    -- get the corresponding object_set_id (if the object_set exists)
    SELECT @object_set_id = object_set_id
    FROM msdb.dbo.syspolicy_object_sets
    WHERE (object_set_name = @name)
    
    -- the view would take care of all the permissions issues.
    IF (@object_set_id IS NULL) 
    BEGIN
    -- TODO: Verify the error message is appropriate for object sets as well and not specific to policies
      RAISERROR(14262, -1, -1, '@name', @name)
      RETURN(1) -- Failure
    END
  END

  RETURN (0)
END
GO

-----------------------------------------------------------
-- This procedure verifies if an object set is refernced (cannot be deleted)
-----------------------------------------------------------
PRINT 'Creating procedure [dbo].[sp_syspolicy_verify_object_set_references]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_verify_object_set_references]
@object_set_id int,
@is_referenced int OUTPUT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	SELECT @is_referenced = count(*) FROM dbo.syspolicy_policies WHERE object_set_id = @object_set_id
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_add_object_set]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_add_object_set]
@object_set_name sysname,
@facet nvarchar (max),
@object_set_id int OUTPUT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DECLARE @retval             INT
	
	DECLARE @facet_id           INT
	DECLARE @null_column		sysname
	
	IF( @facet IS NULL)
		SET @null_column = '@facet'
		
	IF @null_column IS NOT NULL
	BEGIN
		RAISERROR(14043, -1, -1, @null_column, 'sp_syspolicy_add_object_set')
		RETURN(1)
	END

    SET @facet_id = (SELECT management_facet_id FROM [dbo].[syspolicy_management_facets] WHERE name = @facet)
        
    IF (@facet_id IS NULL)
    BEGIN
        RAISERROR (34014, -1, -1)
        RETURN(1)
    END

    INSERT INTO msdb.[dbo].[syspolicy_object_sets_internal]
                                        (object_set_name,
                                        facet_id)
    VALUES                            
                                        (@object_set_name,
                                        @facet_id)

    SELECT @retval = @@error
    SET @object_set_id = SCOPE_IDENTITY()
    RETURN(@retval)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_object_set]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_delete_object_set]
@object_set_name sysname = NULL,
@object_set_id int = NULL
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DECLARE @retval              INT

    EXEC @retval = sp_syspolicy_verify_object_set_identifiers @object_set_name, @object_set_id OUTPUT
    IF (@retval <> 0)
        RETURN (1)

    DELETE msdb.[dbo].[syspolicy_object_sets_internal] 
        WHERE object_set_id = @object_set_id

    RETURN (0)
END
GO


---------------------------------------------------------------
-- PolicyDefinition object
---------------------------------------------------------------

IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_policies_internal')
BEGIN
    PRINT 'Creating table [dbo].[syspolicy_policies_internal]...';
CREATE TABLE [dbo].[syspolicy_policies_internal] (
	policy_id int IDENTITY(1,1),
	name sysname NOT NULL,
	condition_id int NOT NULL,
	root_condition_id int NULL,
	date_created datetime  NOT NULL default GETDATE(),
	execution_mode int NOT NULL default (0),
	policy_category_id int NULL,
	schedule_uid uniqueidentifier NULL,
	description nvarchar(max) NOT NULL default (''),
	help_text nvarchar(4000) NOT NULL default (''),
	help_link nvarchar(2083) NOT NULL default (''),
	object_set_id INT NULL,
	is_enabled bit default 0 NOT NULL,
	job_id uniqueidentifier NULL,
	created_by sysname NOT NULL default SUSER_SNAME(),
	modified_by sysname NULL,
	date_modified datetime NULL,
	is_system bit NOT NULL default (0),
	CONSTRAINT [PK_syspolicy_policies] PRIMARY KEY CLUSTERED (policy_id ASC),
	CONSTRAINT [UQ_syspolicy_policies_name] UNIQUE(name)
	);
ALTER TABLE [dbo].[syspolicy_policies_internal] 
    ADD CONSTRAINT [FK_syspolicy_policies_syspolicy_conditions] FOREIGN KEY(condition_id)
    REFERENCES [dbo].[syspolicy_conditions_internal] (condition_id);

    ALTER TABLE [dbo].[syspolicy_policies_internal] 
        ADD CONSTRAINT [FK_syspolicy_policies_syspolicy_policy_categories] FOREIGN KEY(policy_category_id)
        REFERENCES [dbo].[syspolicy_policy_categories_internal] (policy_category_id);


ALTER TABLE [dbo].[syspolicy_policies_internal] 
    ADD CONSTRAINT [FK_syspolicy_policies_syspolicy_root_conditions] FOREIGN KEY(root_condition_id)
    REFERENCES [dbo].[syspolicy_conditions_internal] (condition_id);


    ALTER TABLE [dbo].[syspolicy_policies_internal] 
        ADD CONSTRAINT [FK_syspolicy_policies_sysjobs] FOREIGN KEY(job_id)
        REFERENCES [dbo].[sysjobs] (job_id);

ALTER TABLE [dbo].[syspolicy_policies_internal]
    ADD CONSTRAINT [FK_syspolicy_policies_syspolicy_object_sets] FOREIGN KEY(object_set_id)
    REFERENCES [dbo].[syspolicy_object_sets_internal] (object_set_id);
    
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_create_job]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_create_job] 
@schedule_uid uniqueidentifier,
@is_enabled bit = 0,
@jobID uniqueidentifier OUTPUT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DECLARE @job_name sysname

	-- create unique job name
	SET @job_name = N'syspolicy_check_schedule_' + LEFT(CONVERT(nvarchar(100), @schedule_uid), 100) 
	WHILE (EXISTS (SELECT * FROM msdb..sysjobs WHERE name = @job_name))
	BEGIN
		SET @job_name = N'syspolicy_check_schedule_' + LEFT(CONVERT(nvarchar(91), @schedule_uid), 91) + '_' + RIGHT(STR(FLOOR(RAND() * 100000000)),8) 
	END

	EXEC  msdb.dbo.sp_add_job @job_name=@job_name, 
			@enabled=@is_enabled, 
			@notify_level_eventlog=0, 
			@notify_level_email=2, 
			@notify_level_netsend=2, 
			@notify_level_page=2, 
			@delete_level=0, 
			@category_id=0, -- [Uncategorized (Local)]
			@job_id = @jobID OUTPUT

	EXEC msdb.dbo.sp_add_jobserver @job_name=@job_name, @server_name = @@servername

    EXEC msdb.dbo.sp_add_jobstep 
            @job_id=@jobID, 
			@step_name=N'Verify that automation is enabled.', 
		    @step_id=1, 
		    @cmdexec_success_code=0, 
		    @on_fail_action=1, 
		    @on_fail_step_id=0, 
		    @retry_attempts=0, 
		    @retry_interval=0, 
		    @os_run_priority=0, 
		    @subsystem=N'TSQL', 
		    @command=N'IF (msdb.dbo.fn_syspolicy_is_automation_enabled() != 1)
        BEGIN
            RAISERROR(34022, 16, 1)
        END', 
		    @database_name=N'master', 
		    @flags=0

	DECLARE @command nvarchar(max)
	SET @command = [dbo].[fn_syspolicy_get_ps_command] (@schedule_uid)

	EXEC msdb.dbo.sp_add_jobstep 
            @job_id=@jobID, 
			@step_name=N'Evaluate policies.', 
			@step_id=2, 
			@cmdexec_success_code=0, 
			@on_success_action=1, 
			@on_fail_action=2, 
			@retry_attempts=0, 
			@retry_interval=0, 
			@os_run_priority=0, 
			@subsystem=N'PowerShell', 
			@command=@command, 
			@flags=0

    EXEC msdb.dbo.sp_update_jobstep 
            @job_id = @jobID, 
            @step_id = 1, 
            @on_success_action=4, 
            @on_success_step_id=2 

	DECLARE @schedule_id int
	SELECT @schedule_id = schedule_id from msdb.dbo.sysschedules where schedule_uid = @schedule_uid

	EXEC msdb.dbo.sp_attach_schedule @job_name = @job_name, @schedule_id = @schedule_id
END
GO

PRINT 'Creating function [dbo].[fn_syspolicy_get_ps_command] ...'
GO

CREATE FUNCTION [dbo].[fn_syspolicy_get_ps_command] (@schedule_uid uniqueidentifier)
RETURNS nvarchar(max)
AS
BEGIN
	
	DECLARE @schedule_uid_string nvarchar(max);
	SET @schedule_uid_string = CONVERT(nvarchar(36), @schedule_uid);
	
	-- translate to PSPath root name, for default instances 
	-- we need to add \default as instance name
	DECLARE @root_name nvarchar(100);
	SET @root_name = @@SERVERNAME
	IF( 0 = CHARINDEX('\', @@SERVERNAME))
		SET @root_name = @root_name + N'\default';
	
	DECLARE @command nvarchar(max);
	SET @command = N'dir SQLSERVER:\SQLPolicy\' + @root_name + 
				N'\Policies | where { $_.ScheduleUid -eq "' + @schedule_uid_string + 
				N'" } |  where { $_.Enabled -eq 1} | where {$_.AutomatedPolicyEvaluationMode -eq 4} | Invoke-PolicyEvaluation -AdHocPolicyEvaluationMode 2 -TargetServerName ' + @@SERVERNAME
				
	RETURN @command
END
GO

PRINT 'Creating trigger [dbo].[syspolicy_insert_job_create_trigger] on [dbo].[syspolicy_policies_internal]'
GO

CREATE TRIGGER [dbo].[syspolicy_insert_job_create_trigger] on [dbo].[syspolicy_policies_internal]
FOR INSERT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END

	DECLARE @jobID uniqueidentifier
	DECLARE @schedule_uid uniqueidentifier
	DECLARE @is_enabled bit

	-- verify that values in inserted.schedule_uid are valid
	IF EXISTS (
		SELECT * FROM inserted i 
		WHERE i.schedule_uid NOT IN 
				(SELECT schedule_uid FROM msdb.dbo.sysschedules ) AND 
		((i.execution_mode & 4) = 4))
	BEGIN
		ROLLBACK -- Failure
		RAISERROR (14365, -1, -1)
		RETURN
	END

	-- find all schedules referenced by the inserted policies for which 
	-- there is no agent job that executes the policies
	DECLARE schedule_cursor CURSOR LOCAL FOR
	    SELECT DISTINCT i.schedule_uid
	    FROM inserted i
	    WHERE 
		    ((i.execution_mode & 4) = 4) AND
	        NOT EXISTS (SELECT * 
	                        FROM msdb.dbo.syspolicy_policies p 
	                        WHERE 
	                            p.policy_id NOT IN (SELECT policy_id FROM inserted) AND
	                            p.schedule_uid = i.schedule_uid AND 
	                            ((p.execution_mode & 4) = 4) )
	
	-- iterate through the cursor and create a job for every schedule		
	OPEN schedule_cursor
	FETCH schedule_cursor INTO @schedule_uid
	WHILE @@FETCH_STATUS = 0
	BEGIN
		-- figure out if the job is enabled or not
		SELECT @is_enabled = COUNT(*) 
		FROM inserted i 
		WHERE i.schedule_uid = @schedule_uid AND i.is_enabled = 1
		
		-- explicitly nullify jobID, 
		-- (if we need to create more than 1 job, it will not be null and sp_add_job will think we're getting job from MSX)
		SET @jobID = NULL
		
		-- create the job that is going to execute the schedule
		EXEC [msdb].[dbo].[sp_syspolicy_create_job] @schedule_uid, @is_enabled, @jobID OUTPUT

		-- update the job_id back into the policies table
		UPDATE p SET p.job_id = @jobID 
			FROM msdb.dbo.syspolicy_policies_internal p
			INNER JOIN inserted i ON p.policy_id = i.policy_id
		WHERE 
			i.schedule_uid = @schedule_uid AND
			(i.execution_mode & 4) = 4

		FETCH schedule_cursor INTO @schedule_uid
	END
	
	CLOSE schedule_cursor
	DEALLOCATE schedule_cursor
	
	-- in case we haven't created the job we still need to update 
	-- the policies with their jobID
	UPDATE p
		SET p.job_id = ( SELECT TOP 1 p2.job_id 
						FROM msdb.dbo.syspolicy_policies p2 
						WHERE 
							p2.schedule_uid = p.schedule_uid AND 
							p2.job_id IS NOT NULL)
		FROM msdb.dbo.syspolicy_policies_internal p
		INNER JOIN inserted i ON p.policy_id = i.policy_id
		WHERE 
			((p.execution_mode & 4) = 4) AND
			p.job_id IS NULL
	
	-- See what jobs we need to enable.
	-- This can happen because we might create a new policy that 
	-- is enabled and there is already a job for it, but the existing
	-- job is disabled
	DECLARE jobs_to_enable CURSOR LOCAL FOR
		SELECT DISTINCT j.job_id
		FROM dbo.sysjobs_view j 
		JOIN msdb.dbo.syspolicy_policies p ON p.job_id = j.job_id
		JOIN inserted i ON p.policy_id = i.policy_id
		WHERE 
			((i.execution_mode & 4) = 4) AND
			j.enabled = 0 AND
			EXISTS ( SELECT * from msdb.dbo.syspolicy_policies p2 
						WHERE p2.job_id = j.job_id AND p2.is_enabled = 1)
	
	OPEN jobs_to_enable
	FETCH jobs_to_enable INTO @jobID
	WHILE @@FETCH_STATUS = 0
	BEGIN
		EXEC msdb.dbo.sp_update_job @job_id = @jobID, @enabled = 1
		
		FETCH jobs_to_enable INTO @jobID
	END
	CLOSE jobs_to_enable
	DEALLOCATE jobs_to_enable
	
	-- enable events infrastructure
	IF EXISTS ( SELECT * FROM inserted i WHERE ((i.execution_mode & 1) = 1))
	BEGIN
		EXEC sys.sp_syspolicy_update_ddl_trigger 
	END

	IF EXISTS (SELECT * FROM inserted i WHERE ((i.execution_mode & 2) = 2))
	BEGIN
		EXEC sys.sp_syspolicy_update_event_notification 
	END

	-- update owner information
	UPDATE msdb.dbo.syspolicy_policies_internal
	SET created_by = original_login(),
		date_created = getdate (),
		date_modified = NULL,
		modified_by = NULL
	FROM inserted i,
	   msdb.dbo.syspolicy_policies_internal policies
	WHERE i.policy_id = policies.policy_id

	-- protect against non-scheduled automation jobs
	-- that have expressions that execute script
	DECLARE @row_count int

    SELECT @row_count = count(*) 
        FROM syspolicy_conditions c  
        INNER JOIN inserted i ON (i.condition_id = c.condition_id OR i.root_condition_id = c.condition_id)
        WHERE    i.is_enabled != 0 AND
                i.execution_mode != 4 AND
                (1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteSql"]' ) OR
                1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteWql"]' ) ) 
        OPTION (FORCE ORDER);

    SELECT @row_count = @row_count + count(*)
        FROM dbo.syspolicy_target_set_levels l 
        INNER JOIN dbo.syspolicy_target_sets s ON s.target_set_id = l.target_set_id
        INNER JOIN syspolicy_conditions c on c.condition_id = l.condition_id
        INNER JOIN syspolicy_object_sets_internal os ON os.object_set_id = s.object_set_id
        INNER JOIN inserted i ON os.object_set_id = i.object_set_id
        WHERE    i.is_enabled != 0 AND
                i.execution_mode != 4 AND
                (1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteSql"]' ) OR
                1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteWql"]' ) )
        OPTION (FORCE ORDER);

    IF (@row_count > 0)
    BEGIN
        RAISERROR(34017, -1, -1);
        ROLLBACK TRANSACTION;
    END

END -- create trigger
GO

PRINT 'Creating trigger [dbo].[syspolicy_update_job_update_trigger] on [dbo].[syspolicy_policies_internal]'
GO

CREATE TRIGGER [dbo].[syspolicy_update_job_update_trigger] on [dbo].[syspolicy_policies_internal]
FOR UPDATE
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END

	-- This is to prevent indirect entrance of the trigger
	IF (TRIGGER_NESTLEVEL() > 1) RETURN

	-- verify that values in inserted.schedule_uid are valid
	IF EXISTS (
		SELECT * FROM inserted i 
		WHERE i.schedule_uid NOT IN 
				(SELECT schedule_uid FROM msdb.dbo.sysschedules ) AND 
		((i.execution_mode & 4) = 4))
	BEGIN
		ROLLBACK -- Failure
		RAISERROR (14365, -1, -1)
		RETURN
	END

	-- update eventing infrastructure
	IF(UPDATE(execution_mode) OR UPDATE(is_enabled))
	BEGIN
		IF  EXISTS (SELECT * 
					FROM inserted i
					INNER JOIN deleted d ON i.policy_id = d.policy_id
					WHERE 
						(((i.execution_mode & 1) = 1) OR ((d.execution_mode & 1) = 1)) AND 
						(i.is_enabled != d.is_enabled OR i.execution_mode != d.execution_mode))
		BEGIN
			EXEC sys.sp_syspolicy_update_ddl_trigger 
		END

		IF  EXISTS (SELECT * 
					FROM inserted i
					INNER JOIN deleted d ON i.policy_id = d.policy_id
					WHERE 
						(((i.execution_mode & 2) = 2) OR ((d.execution_mode & 2) = 2)) AND 
						(i.is_enabled != d.is_enabled OR i.execution_mode != d.execution_mode))
		BEGIN
			EXEC sys.sp_syspolicy_update_event_notification 
		END
	END

	DECLARE @jobID uniqueidentifier
	DECLARE @is_enabled bit
	DECLARE @schedule_uid uniqueidentifier

    -- set the job_id to NULL for all policies whose schedule_uid has changed
    -- so that we can create a job if needed
    UPDATE p
        SET p.job_id = NULL
        FROM msdb.dbo.syspolicy_policies p
        INNER JOIN inserted i ON p.policy_id = i.policy_id
        INNER JOIN deleted d ON d.policy_id = p.policy_id
        WHERE i.schedule_uid != d.schedule_uid

	-- find all schedules referenced by the inserted policies for which 
	-- there is no agent job that executes the policies
	DECLARE schedule_cursor CURSOR LOCAL FOR
	    SELECT DISTINCT i.schedule_uid
	    FROM inserted i
	    WHERE 
		    ((i.execution_mode & 4) = 4) AND
	        NOT EXISTS (SELECT * 
	                        FROM msdb.dbo.syspolicy_policies p 
	                        WHERE 
	                            p.schedule_uid = i.schedule_uid AND 
	                            ((p.execution_mode & 4) = 4) AND
	                            p.job_id IS NOT NULL)
	                    
	-- iterate through the cursor and create a job for every schedule		
	OPEN schedule_cursor
	FETCH schedule_cursor INTO @schedule_uid
	WHILE @@FETCH_STATUS = 0
	BEGIN
		-- figure out if the job is enabled or not
		SELECT @is_enabled = COUNT(*) 
		FROM inserted i 
		WHERE i.schedule_uid = @schedule_uid AND i.is_enabled = 1
		
		-- create the job that is going to execute the schedule
		EXEC [msdb].[dbo].[sp_syspolicy_create_job] @schedule_uid, @is_enabled, @jobID OUTPUT

		-- update the job_id back into the policies table
		UPDATE p SET p.job_id = @jobID 
			FROM msdb.dbo.syspolicy_policies_internal p
			INNER JOIN inserted i ON p.policy_id = i.policy_id
		WHERE 
			i.schedule_uid = @schedule_uid AND
			(i.execution_mode & 4) = 4
	
		FETCH schedule_cursor INTO @schedule_uid
	END
	
	CLOSE schedule_cursor
	DEALLOCATE schedule_cursor
	
	-- in case we haven't created the job we still need to update 
	-- the policies with their jobID
	UPDATE p 
		SET p.job_id = ( SELECT TOP 1 p2.job_id 
						FROM msdb.dbo.syspolicy_policies p2 
						WHERE 
							p2.schedule_uid = p.schedule_uid AND
							p2.job_id IS NOT NULL)
		FROM msdb.dbo.syspolicy_policies p
		INNER JOIN inserted i ON p.policy_id = i.policy_id
		WHERE 
			((p.execution_mode & 4) = 4) AND
			p.job_id IS NULL
	
	-- if the execution_mode has changed then we need to clear the job references
	UPDATE p
		SET p.job_id = NULL
		FROM msdb.dbo.syspolicy_policies_internal p
		INNER JOIN inserted i ON p.policy_id = i.policy_id 
		INNER JOIN deleted d ON p.policy_id = d.policy_id
		WHERE 
			((i.execution_mode & 4) != 4) AND
			((d.execution_mode & 4) = 4) AND
			p.job_id IS NOT NULL
	
	-- See what jobs we need to enable.
	-- This can happen because we might create a new policy that 
	-- is enabled and there is already a job for it, but the existing
	-- job is disabled
	DECLARE jobs_to_enable CURSOR LOCAL FOR
		SELECT DISTINCT j.job_id
		FROM dbo.sysjobs_view j 
		JOIN msdb.dbo.syspolicy_policies p ON p.job_id = j.job_id
		JOIN inserted i ON p.policy_id = i.policy_id
		WHERE 
			((i.execution_mode & 4) = 4) AND
			j.enabled = 0 AND
			EXISTS ( SELECT * from msdb.dbo.syspolicy_policies p2 
						WHERE p2.job_id = j.job_id AND p2.is_enabled = 1)
	
	OPEN jobs_to_enable
	FETCH jobs_to_enable INTO @jobID
	WHILE @@FETCH_STATUS = 0
	BEGIN
		EXEC msdb.dbo.sp_update_job @job_id = @jobID, @enabled = 1
		
		FETCH jobs_to_enable INTO @jobID
	END
	CLOSE jobs_to_enable
	DEALLOCATE jobs_to_enable

	-- Find out what jobs have to be deleted because the policy's schedule 
	-- has changed
	IF (UPDATE(schedule_uid))
	BEGIN
		DECLARE deleted_cursor CURSOR LOCAL FOR 
			SELECT DISTINCT d.job_id
			FROM deleted d
			WHERE 
				((d.execution_mode & 4) = 4) AND
				d.job_id NOT IN (SELECT job_id FROM msdb.dbo.syspolicy_policies p 
								WHERE 
								((p.execution_mode & 4) = 4))

		OPEN deleted_cursor
		FETCH deleted_cursor INTO @jobID
		
		WHILE (@@FETCH_STATUS=0)
		BEGIN
			-- delete the job(s), but do not delete the shared schedule
			IF (@jobID IS NOT NULL)
				EXEC msdb.dbo.sp_delete_job @job_id = @jobID, @delete_unused_schedule = 0

			FETCH deleted_cursor INTO @jobID
		END -- while (@@FETCH_STATUS=0)

		CLOSE deleted_cursor
		DEALLOCATE deleted_cursor
	END	-- UPDATE(schedule_uid)


	-- See what jobs we need to disable.
	-- This can happen because we do not need to delete the job, but
	-- all policies that reference it are disabled.
	DECLARE jobs_to_disable CURSOR LOCAL FOR
		SELECT DISTINCT j.job_id
		FROM dbo.sysjobs_view j 
		JOIN msdb.dbo.syspolicy_policies p ON p.job_id = j.job_id
		WHERE 
			j.enabled = 1 AND
			NOT EXISTS ( SELECT * from msdb.dbo.syspolicy_policies p2 
						WHERE p2.job_id = j.job_id AND p2.is_enabled = 1 AND ((p2.execution_mode & 4) = 4))
	
	OPEN jobs_to_disable
	FETCH jobs_to_disable INTO @jobID
	WHILE @@FETCH_STATUS = 0
	BEGIN
		EXEC msdb.dbo.sp_update_job @job_id = @jobID, @enabled = 0
		
		FETCH jobs_to_disable INTO @jobID
	END
	CLOSE jobs_to_disable
	DEALLOCATE jobs_to_disable

    UPDATE msdb.dbo.syspolicy_policies_internal
    SET modified_by = original_login(),
        date_modified = GETDATE()
    FROM inserted i,
       msdb.dbo.syspolicy_policies_internal policies
    WHERE i.policy_id = policies.policy_id
END -- update trigger
GO

PRINT 'Creating trigger [dbo].[syspolicy_insert_policy_trigger] on [dbo].[syspolicy_policies_internal]'
GO

CREATE TRIGGER [dbo].[syspolicy_insert_policy_trigger] on [dbo].[syspolicy_policies_internal]
FOR INSERT
AS
BEGIN
    DECLARE @object_set_id int, @name sysname

	SELECT TOP 1 @object_set_id = i.object_set_id, @name = i.name 
	FROM inserted i 
	WHERE 1 < (SELECT count(*) FROM syspolicy_policies p WHERE p.object_set_id = i.object_set_id)

	IF @@ROWCOUNT > 0
	BEGIN
				DECLARE @os_name sysname, @policy_name sysname
				SELECT TOP 1 @os_name = os.object_set_name, @policy_name = p.name
				 FROM syspolicy_object_sets os 
					INNER JOIN syspolicy_policies p ON (os.object_set_id = p.object_set_id)
				WHERE os.object_set_id = @object_set_id AND p.name <> @name
				
				RAISERROR(34013, -1, -1, 'ObjectSet', @os_name, @policy_name) 
				ROLLBACK TRANSACTION
	END
END
GO

PRINT 'Creating trigger [dbo].[syspolicy_update_policy_trigger] on [dbo].[syspolicy_policies_internal]'
GO

CREATE TRIGGER [dbo].[syspolicy_update_policy_trigger] on [dbo].[syspolicy_policies_internal]
FOR UPDATE
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END
	
	-- This is to prevent indirect execution of the trigger
	IF (TRIGGER_NESTLEVEL() > 1) RETURN

    IF( UPDATE(condition_id) )
    BEGIN
        -- delete all health state records for active policies whose 
        -- condition has changed
        DELETE FROM [dbo].[syspolicy_system_health_state_internal] 
            FROM [dbo].[syspolicy_system_health_state_internal] phs
            INNER JOIN inserted i ON phs.policy_id = i.policy_id
            INNER JOIN deleted d ON phs.policy_id = d.policy_id
            WHERE d.condition_id != i.condition_id AND i.is_enabled = 1
    END

    IF( UPDATE(object_set_id) )
    BEGIN
        DECLARE @object_set_id int, @numref int, @new_object_set_id int, @name sysname
        
        DECLARE os_cursor CURSOR LOCAL FOR 
        SELECT i.object_set_id, d.object_set_id, i.name
        FROM inserted i INNER JOIN deleted d ON (i.policy_id = d.policy_id) 
        WHERE (d.object_set_id IS NOT NULL AND i.object_set_id IS NULL)
			OR (i.object_set_id IS NOT NULL AND d.object_set_id IS NULL)
			OR (d.object_set_id != i.object_set_id)

        OPEN os_cursor
        FETCH os_cursor INTO @new_object_set_id, @object_set_id, @name
    
        WHILE @@FETCH_STATUS = 0 
        BEGIN
			IF (@object_set_id IS NOT NULL)
			BEGIN
				EXEC sp_syspolicy_verify_object_set_references @object_set_id, @numref OUTPUT
				IF (@numref = 0)
					EXEC sp_syspolicy_delete_object_set @object_set_id=@object_set_id
            END
            
			IF (@new_object_set_id IS NOT NULL)
			BEGIN
				EXEC sp_syspolicy_verify_object_set_references @new_object_set_id, @numref OUTPUT
				IF (@numref > 1)
				BEGIN
					DECLARE @os_name sysname, @policy_name sysname
					SELECT TOP 1 @os_name = os.object_set_name, @policy_name = p.name
					 FROM syspolicy_object_sets os 
						INNER JOIN syspolicy_policies p ON (os.object_set_id = p.object_set_id)
					WHERE os.object_set_id = @object_set_id AND p.name <> @name
					
					RAISERROR(34013, -1, -1, 'ObjectSet', @os_name, @policy_name) 
					ROLLBACK TRANSACTION
				END
            END
                
            FETCH os_cursor INTO @new_object_set_id, @object_set_id, @name
        END
            
        CLOSE os_cursor
        DEALLOCATE os_cursor    

    END

    IF( UPDATE(is_enabled) )
    BEGIN
        -- delete all health state records for policies that 
        -- have been disabled
        DELETE FROM [dbo].[syspolicy_system_health_state_internal] 
            FROM [dbo].[syspolicy_system_health_state_internal] phs
            INNER JOIN inserted i ON phs.policy_id = i.policy_id
            INNER JOIN deleted d ON phs.policy_id = d.policy_id
            WHERE d.is_enabled = 1 AND i.is_enabled = 0
    END

    IF( UPDATE(is_enabled) )
    BEGIN
        DECLARE @row_count int

        SELECT @row_count = count(*) 
            FROM syspolicy_conditions c  
            INNER JOIN inserted i ON (i.condition_id = c.condition_id OR i.root_condition_id = c.condition_id)
            WHERE    i.is_enabled != 0 AND
                    i.execution_mode != 4 AND
                    (1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteSql"]' ) OR
                    1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteWql"]' ) ) 
            OPTION (FORCE ORDER)

        SELECT @row_count = @row_count + count(*)
            FROM dbo.syspolicy_target_set_levels l 
            INNER JOIN dbo.syspolicy_target_sets s ON s.target_set_id = l.target_set_id
            INNER JOIN syspolicy_conditions c on c.condition_id = l.condition_id
            INNER JOIN syspolicy_object_sets_internal os on os.object_set_id = s.object_set_id
            INNER JOIN inserted i ON os.object_set_id = i.object_set_id
            WHERE    i.is_enabled != 0 AND
                    i.execution_mode != 4 AND
                    (1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteSql"]' ) OR
                    1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteWql"]' ) )
            OPTION (FORCE ORDER)

        IF (@row_count > 0)
        BEGIN
            RAISERROR(34017, -1, -1) 
            ROLLBACK TRANSACTION
        END
    END
END
GO

PRINT 'Creating trigger [dbo].[syspolicy_delete_job_delete_trigger] on [dbo].[syspolicy_policies_internal]'
GO

CREATE TRIGGER [dbo].[syspolicy_delete_job_delete_trigger] on [dbo].[syspolicy_policies_internal]
FOR DELETE
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END

	DECLARE @jobID uniqueidentifier 

	-- Declare the cursor to iterate over the jobs that are only referenced 
	-- by deleted policies. The jobs that are still referenced by active policies 
	-- should not be deleted.
	DECLARE deleted_cursor CURSOR LOCAL FOR 
		SELECT DISTINCT d.job_id
		FROM deleted d
		WHERE 
			((d.execution_mode & 4) = 4) AND
			NOT EXISTS (SELECT * FROM msdb.dbo.syspolicy_policies p 
							WHERE 
							p.job_id = d.job_id AND 
							((p.execution_mode & 4) = 4) AND
							p.policy_id NOT IN (SELECT d2.policy_id FROM deleted d2))

	OPEN deleted_cursor
	FETCH deleted_cursor INTO @jobID
	
	WHILE (@@FETCH_STATUS=0)
	BEGIN
		-- delete the job(s), but do not delete the shared schedule
		IF (@jobID IS NOT NULL)
			EXEC msdb.dbo.sp_delete_job @job_id = @jobID, @delete_unused_schedule = 0

		FETCH deleted_cursor INTO @jobID
	END -- while (@@FETCH_STATUS=0)

    CLOSE deleted_cursor
    DEALLOCATE deleted_cursor


	-- See what jobs we need to disable.
	-- This can happen because we do not need to delete the job, but
	-- all policies that reference it are disabled.
	DECLARE jobs_to_disable CURSOR LOCAL FOR
		SELECT DISTINCT j.job_id
		FROM dbo.sysjobs_view j 
		JOIN deleted d ON d.job_id = j.job_id
		WHERE 
			j.enabled = 1 AND
			NOT EXISTS ( SELECT * from msdb.dbo.syspolicy_policies p2 
						WHERE p2.job_id = j.job_id AND p2.is_enabled = 1 AND ((p2.execution_mode & 4) = 4))
	
	OPEN jobs_to_disable
	FETCH jobs_to_disable INTO @jobID
	WHILE @@FETCH_STATUS = 0
	BEGIN
		EXEC msdb.dbo.sp_update_job @job_id = @jobID, @enabled = 0
		
		FETCH jobs_to_disable INTO @jobID
	END
	CLOSE jobs_to_disable
	DEALLOCATE jobs_to_disable

	-- update eventing infrastructure
	IF EXISTS ( SELECT * FROM deleted d WHERE ((d.execution_mode & 1) = 1))
	BEGIN
		EXEC sys.sp_syspolicy_update_ddl_trigger 
	END

	IF EXISTS (SELECT * FROM deleted d WHERE ((d.execution_mode & 2) = 2))
	BEGIN
		EXEC sys.sp_syspolicy_update_event_notification 
	END

END
GO

PRINT 'Creating trigger [dbo].[syspolicy_instead_delete_policy_trigger] on [dbo].[syspolicy_policies_internal]'
GO

CREATE TRIGGER [dbo].[syspolicy_instead_delete_policy_trigger] on [dbo].[syspolicy_policies_internal]
INSTEAD OF DELETE 
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END

	-- This trigger deletes references in given order to protect from deadlocks
	DELETE msdb.dbo.syspolicy_policy_execution_history_internal	WHERE policy_id in (SELECT policy_id FROM deleted)
	DELETE msdb.dbo.syspolicy_system_health_state_internal		WHERE policy_id in (SELECT policy_id FROM deleted)
	DELETE msdb.dbo.syspolicy_policies_internal		WHERE policy_id in (SELECT policy_id FROM deleted)
END
GO

PRINT 'Creating view [dbo].[syspolicy_policies]...'
GO
CREATE VIEW [dbo].[syspolicy_policies]
AS
    SELECT     
        policy_id,
        name,
        condition_id,
        root_condition_id,
        date_created,
        execution_mode,
        policy_category_id,
        schedule_uid,
        description,
        help_text,
        help_link,
        object_set_id,
        is_enabled,
        job_id,
        created_by,
        modified_by,
        date_modified,
        is_system
    FROM [dbo].[syspolicy_policies_internal]
GO

PRINT ''
PRINT 'Creating trigger syspolicy_insert_condition_trigger...'
GO

CREATE TRIGGER dbo.syspolicy_insert_condition_trigger
ON msdb.dbo.syspolicy_conditions_internal
AFTER INSERT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END

  UPDATE msdb.dbo.syspolicy_conditions_internal
  SET created_by = original_login()
  FROM inserted i INNER JOIN
       msdb.dbo.syspolicy_conditions_internal conditions
  ON i.condition_id = conditions.condition_id

END
GO


PRINT ''
PRINT 'Creating trigger dbo.syspolicy_for_update_condition_trigger...'
GO

CREATE TRIGGER dbo.syspolicy_for_update_condition_trigger
ON msdb.dbo.syspolicy_conditions_internal
FOR UPDATE
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END

	-- This is to prevent indirect entrance of the trigger
	IF (TRIGGER_NESTLEVEL() > 1) 
		RETURN

    -- do not allow expression to be changed to a script 
    -- if the policy is enabled
    IF UPDATE(expression)
    BEGIN
        DECLARE @row_count int

        SELECT @row_count = count(*) 
            FROM inserted i 
            INNER JOIN syspolicy_policies p ON (i.condition_id = p.condition_id OR p.root_condition_id = i.condition_id)
            WHERE    p.is_enabled != 0 AND
                    p.execution_mode != 4 AND
                    (1 = CONVERT(xml, i.expression).exist('//FunctionType/text()[.="ExecuteSql"]' ) OR
                    1 = CONVERT(xml, i.expression).exist('//FunctionType/text()[.="ExecuteWql"]' ) ) 
            OPTION (FORCE ORDER)

        SELECT @row_count = @row_count + count(*)
            FROM dbo.syspolicy_target_set_levels l 
            INNER JOIN dbo.syspolicy_target_sets s ON s.target_set_id = l.target_set_id
            INNER JOIN inserted i on i.condition_id = l.condition_id
            INNER JOIN syspolicy_object_sets_internal os ON s.object_set_id = os.object_set_id
            INNER JOIN syspolicy_policies p ON os.object_set_id = p.object_set_id
            WHERE    p.is_enabled != 0 AND
                    p.execution_mode != 4 AND
                    (1 = CONVERT(xml, i.expression).exist('//FunctionType/text()[.="ExecuteSql"]' ) OR
                    1 = CONVERT(xml, i.expression).exist('//FunctionType/text()[.="ExecuteWql"]' ) )
            OPTION (FORCE ORDER)

        IF (@row_count > 0)
        BEGIN
            RAISERROR(34017, -1, -1) 
            ROLLBACK TRANSACTION
        END

    END
END
GO

PRINT ''
PRINT 'Creating trigger syspolicy_after_update_condition_trigger...'
GO

CREATE TRIGGER dbo.syspolicy_after_update_condition_trigger
ON msdb.dbo.syspolicy_conditions_internal
AFTER UPDATE
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END

	-- This is to prevent indirect entrance of the trigger
	IF (TRIGGER_NESTLEVEL() > 1) 
		RETURN

    UPDATE msdb.dbo.syspolicy_conditions_internal
        SET modified_by = original_login(), date_modified = GETDATE()
        FROM inserted i 
        INNER JOIN msdb.dbo.syspolicy_conditions_internal c ON i.condition_id = c.condition_id

    -- update health state table by deleting all the records for 
    -- policies whose expression has been modified
    IF UPDATE(expression)
    BEGIN
        DELETE FROM dbo.syspolicy_system_health_state_internal 
            FROM dbo.syspolicy_system_health_state_internal phs
            INNER JOIN dbo.syspolicy_policies p ON phs.policy_id = p.policy_id
            INNER JOIN inserted i ON p.condition_id = i.condition_id
            INNER JOIN deleted d ON p.condition_id = d.condition_id
            WHERE d.expression != i.expression 
    END
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_verify_policy_category_identifiers]...'
GO
-----------------------------------------------------------
-- This procedure verifies if a policy category exists
-- The caller can pass either the policy category name or the id
-----------------------------------------------------------
CREATE PROCEDURE [dbo].[sp_syspolicy_verify_policy_category_identifiers]
@policy_category_name sysname = NULL OUTPUT, 
@policy_category_id int = NULL OUTPUT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

  IF ((@policy_category_name IS NULL)     AND (@policy_category_id IS NULL)) OR
     ((@policy_category_name IS NOT NULL) AND (@policy_category_id IS NOT NULL))
  BEGIN
    RAISERROR(14524, -1, -1, '@policy_category_name', '@policy_category_id')
    RETURN(1) -- Failure
  END

  -- Check id
  IF (@policy_category_id IS NOT NULL)
  BEGIN
    SELECT @policy_category_name = name
    FROM msdb.dbo.syspolicy_policy_categories
    WHERE (policy_category_id = @policy_category_id)
    
    -- the view would take care of all the permissions issues.
    IF (@policy_category_name IS NULL) 
    BEGIN
      DECLARE @policy_category_id_as_char VARCHAR(36)
      SELECT @policy_category_id_as_char = CONVERT(VARCHAR(36), @policy_category_id)
      RAISERROR(14262, -1, -1, '@policy_category_id', @policy_category_id_as_char)
      RETURN(1) -- Failure
    END
  END
  ELSE
  -- Check name
  IF (@policy_category_name IS NOT NULL)
  BEGIN
    -- get the corresponding policy_category_id (if the condition exists)
    SELECT @policy_category_id = policy_category_id
    FROM msdb.dbo.syspolicy_policy_categories
    WHERE (name = @policy_category_name)
    
    -- the view would take care of all the permissions issues.
    IF (@policy_category_id IS NULL) 
    BEGIN
      RAISERROR(14262, -1, -1, '@policy_category_name', @policy_category_name)
      RETURN(1) -- Failure
    END
  END

  RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_add_policy_category]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_add_policy_category]
@name sysname,
@mandate_database_subscriptions bit = 1,
@policy_category_id int OUTPUT 
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DECLARE @retval			INT
	DECLARE @null_column	sysname
	
	SET @null_column = NULL
	
	IF(@name IS NULL OR @name = N'')
		SET @null_column = '@name'

    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_syspolicy_add_policy_category')
        RETURN(1)
    END

    IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_policy_categories_internal WHERE name = @name)
    BEGIN
        RAISERROR(34010, -1, -1, 'Policy Category', @name)
        RETURN(1)
    END

    INSERT INTO msdb.dbo.syspolicy_policy_categories_internal(name, mandate_database_subscriptions) VALUES (@name, @mandate_database_subscriptions)
    SELECT @retval = @@error
    SET @policy_category_id = SCOPE_IDENTITY()
    RETURN(@retval)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_policy_category]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_delete_policy_category]
@name sysname = NULL,
@policy_category_id int = NULL
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DECLARE @retval              INT

    EXEC @retval = sp_syspolicy_verify_policy_category_identifiers @name, @policy_category_id OUTPUT
    IF (@retval <> 0)
        RETURN (1)

    IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_policy_category_subscriptions WHERE policy_category_id = @policy_category_id)
    BEGIN
        RAISERROR(34012,-1,-1,'Policy Category','Policy Subscription')
        RETURN (1)
    END


    IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_policies WHERE policy_category_id = @policy_category_id)
    BEGIN
        RAISERROR(34012,-1,-1,'Policy Category','Policy')
        RETURN (1)
    END

    DELETE msdb.dbo.syspolicy_policy_categories_internal
    WHERE policy_category_id = @policy_category_id
    
    SET @retval = @@error
    RETURN @retval
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_rename_policy_category]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_rename_policy_category] 
@name sysname = NULL,
@policy_category_id int = NULL,
@new_name sysname = NULL
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

    IF (@new_name IS NULL or LEN(@new_name) = 0)
    BEGIN
      RAISERROR(21263, -1, -1, '@new_name')
      RETURN(1) -- Failure
    END

    DECLARE @retval              INT

    EXEC @retval = sp_syspolicy_verify_policy_category_identifiers @name, @policy_category_id OUTPUT
    IF (@retval <> 0)
        RETURN (1)

    UPDATE msdb.[dbo].[syspolicy_policy_categories_internal ]
    SET name = @new_name
    WHERE policy_category_id = @policy_category_id

    SELECT @retval = @@error
    RETURN(@retval)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_update_policy_category]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_update_policy_category] 
@name sysname = NULL,
@policy_category_id int = NULL,
@mandate_database_subscriptions bit = NULL
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

    DECLARE @retval              INT

    EXEC @retval = sp_syspolicy_verify_policy_category_identifiers @name, @policy_category_id OUTPUT
    IF (@retval <> 0)
        RETURN (1)

    UPDATE msdb.[dbo].[syspolicy_policy_categories_internal ]
    SET mandate_database_subscriptions = ISNULL(@mandate_database_subscriptions, mandate_database_subscriptions)
    WHERE policy_category_id = @policy_category_id

    SELECT @retval = @@error
    RETURN(@retval)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_add_policy]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_add_policy] 
@name sysname,
@condition_id int = NULL,
@condition_name sysname = NULL,
@schedule_uid uniqueidentifier = NULL,
@policy_category sysname = NULL,
@description nvarchar(max) = N'',
@help_text nvarchar(4000) = N'',
@help_link nvarchar(2083) = N'',
@execution_mode int,
@is_enabled bit = 0,
@root_condition_id int = NULL,
@root_condition_name sysname = NULL,
@object_set sysname = NULL,
@policy_id int = NULL OUTPUT
WITH EXECUTE AS OWNER
AS
BEGIN
    DECLARE @retval_check int;
    EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
    IF ( 0!= @retval_check)
    BEGIN
        RETURN @retval_check
    END

    DECLARE @retval         INT
    DECLARE @null_column    sysname
    
    SET @null_column = NULL

    IF (@name IS NULL OR @name = N'')
        SET @null_column = '@name'
    ELSE IF (@execution_mode IS NULL )
        SET @null_column = '@execution_mode'
    ELSE IF( @is_enabled IS NULL)
        SET @null_column = '@is_enabled'
    ELSE IF( @description IS NULL)
        SET @null_column = '@description'
    ELSE IF( @help_text IS NULL)
        SET @null_column = '@help_text'
    ELSE IF( @help_link IS NULL)
        SET @null_column = '@help_link'
    

    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_syspolicy_add_policy')
        RETURN(1)
    END

    IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_policies WHERE name = @name)
    BEGIN
        RAISERROR(34010, -1, -1, 'Policy', @name)
        RETURN(1)
    END

    SET @schedule_uid = ISNULL (@schedule_uid, '{00000000-0000-0000-0000-000000000000}')

    --Check for the execution mode value
    IF (@execution_mode NOT IN (0,1,2,4,5,6))
    BEGIN 
        RAISERROR(34004, -1, -1, @execution_mode)
        RETURN (1)
    END

    IF (@schedule_uid = '{00000000-0000-0000-0000-000000000000}' AND (@execution_mode & 4) = 4)
    BEGIN
        RAISERROR (34011, -1, -1, 'schedule_uid', 4)
        RETURN(1)
    END

    IF (@is_enabled = 1 AND @execution_mode = 0)
    BEGIN
        RAISERROR (34011, -1, -1, 'is_enabled', @execution_mode)
        RETURN(1)
    END

    -- Turn [nullable] empty string parameters into NULLs
    IF @condition_name = '' SELECT @condition_name = NULL
    IF @policy_category = ''   SELECT @policy_category = NULL
    IF @root_condition_name = '' SELECT @root_condition_name = NULL

    -- verify that the condition exists
    EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @condition_name = @condition_name OUTPUT, @condition_id = @condition_id OUTPUT
    IF (@retval <> 0)
        RETURN(1)
        
    -- convert @object_set into id if needed
    DECLARE @object_set_id INT
    DECLARE @object_set_facet_id INT
    IF (@object_set IS NOT NULL)
    BEGIN
        SELECT @object_set_id = object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name = @object_set
        IF @object_set_id IS NULL
        BEGIN
            -- TODO: RAISERROR that specified object set doesn't exist
            RAISERROR(N'specified object set does not exists', -1, -1)
            RETURN(1) -- Failure
        END
        ELSE
        BEGIN
            SELECT @object_set_facet_id = facet_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name = @object_set
            -- Ensure the object set has been created from the same facet that the policy condition has been created
            IF (@object_set_facet_id <> (SELECT facet_id FROM msdb.dbo.syspolicy_conditions_internal WHERE condition_id = @condition_id))
            BEGIN
                -- TODO: RAISEERROR that specified object_set isn't created from the facet that the policy condition has been created from
                RAISERROR(N'specified object set does not match facet the policy condition was created off', -1, -1)
                RETURN(1) -- Failure
            END
        END
    END

    IF (@root_condition_name IS NOT NULL) OR (@root_condition_id IS NOT NULL)
    BEGIN
        -- verify that the root condition exists
        EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @condition_name = @root_condition_name OUTPUT, @condition_id = @root_condition_id OUTPUT
        IF (@retval <> 0)
            RETURN(1)
            
        -- Check execution mode for compatibility with root_condition
        IF (@execution_mode = 1) OR (@execution_mode = 2) -- Enforce or Check on Change
        BEGIN
            RAISERROR (34011, -1, -1, 'root_condition', @execution_mode)
            RETURN(1)
        END

    END

    -- verify schedule
    IF (@schedule_uid != '{00000000-0000-0000-0000-000000000000}')
    BEGIN
        IF NOT EXISTS (SELECT * FROM msdb.dbo.sysschedules WHERE schedule_uid = @schedule_uid)
        BEGIN
            RAISERROR(14365, -1, -1)
            RETURN(1) -- Failure
        END
    END

    -- convert group_name into id if needed
    DECLARE @policy_category_id INT
    IF ( (@policy_category IS NOT NULL) )
    BEGIN 
        IF NOT EXISTS (SELECT * from msdb.dbo.syspolicy_policy_categories WHERE name = @policy_category)
        BEGIN
            RAISERROR(34015, -1, -1,@policy_category)
            RETURN(1) -- Failure
        END
        ELSE
            SELECT @policy_category_id = policy_category_id FROM msdb.dbo.syspolicy_policy_categories WHERE name = @policy_category
    END
    
    INSERT INTO msdb.dbo.syspolicy_policies_internal
                                        (name, 
                                        execution_mode, 
                                        schedule_uid,
                                        policy_category_id,
                                        description,
                                        help_text,
                                        help_link,
                                        condition_id,
                                        root_condition_id,
                                        object_set_id,
                                        is_enabled)
    VALUES                            
                                        (@name, 
                                        @execution_mode, 
                                        @schedule_uid,
                                        @policy_category_id,
                                        @description,
                                        @help_text,
                                        @help_link,
                                        @condition_id,
                                        @root_condition_id,
                                        @object_set_id,
                                        @is_enabled)

    SELECT @retval = @@error
    SET @policy_id = SCOPE_IDENTITY()
    RETURN(@retval)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_verify_policy_identifiers]...'
GO
-----------------------------------------------------------
-- This procedure verifies if a policy definition exists
-- The caller can pass either the name or the id
-----------------------------------------------------------
CREATE PROCEDURE [dbo].[sp_syspolicy_verify_policy_identifiers]
@name sysname = NULL OUTPUT, 
@policy_id int = NULL OUTPUT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

  IF ((@name IS NULL)     AND (@policy_id IS NULL)) OR
     ((@name IS NOT NULL) AND (@policy_id IS NOT NULL))
  BEGIN
    RAISERROR(14524, -1, -1, '@name', '@policy_id')
    RETURN(1) -- Failure
  END

  -- Check id
  IF (@policy_id IS NOT NULL)
  BEGIN
    SELECT @name = name
    FROM msdb.dbo.syspolicy_policies
    WHERE (policy_id = @policy_id)
    
    -- the view would take care of all the permissions issues.
    IF (@name IS NULL) 
    BEGIN
      DECLARE @policy_id_as_char VARCHAR(36)
      SELECT @policy_id_as_char = CONVERT(VARCHAR(36), @policy_id)
      RAISERROR(14262, -1, -1, '@policy_id', @policy_id_as_char)
      RETURN(1) -- Failure
    END
  END
  ELSE
  -- Check name
  IF (@name IS NOT NULL)
  BEGIN
    -- get the corresponding policy_id (if the policy exists)
    SELECT @policy_id = policy_id
    FROM msdb.dbo.syspolicy_policies
    WHERE (name = @name)
    
    -- the view would take care of all the permissions issues.
    IF (@policy_id IS NULL) 
    BEGIN
      RAISERROR(14262, -1, -1, '@name', @name)
      RETURN(1) -- Failure
    END
  END

  RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_rename_policy]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_rename_policy] 
@name sysname = NULL,
@policy_id int = NULL,
@new_name sysname = NULL
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

    IF (@new_name IS NULL or LEN(@new_name) = 0)
    BEGIN
      RAISERROR(21263, -1, -1, '@new_name')
      RETURN(1) -- Failure
    END

    DECLARE @retval              INT

    EXEC @retval = sp_syspolicy_verify_policy_identifiers @name, @policy_id OUTPUT
    IF (@retval <> 0)
        RETURN (1)

    UPDATE msdb.[dbo].[syspolicy_policies_internal] 
    SET name = @new_name
    WHERE policy_id = @policy_id

    SELECT @retval = @@error
    RETURN(@retval)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_update_policy]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_update_policy] 
@name sysname = NULL,
@policy_id int = NULL,
@condition_id int=NULL,
@condition_name sysname = NULL,
@execution_mode int=NULL,
@policy_category sysname = NULL,
@schedule_uid uniqueidentifier = NULL,
@description nvarchar(max) = NULL,
@help_text nvarchar(4000) = NULL,
@help_link nvarchar(2083) = NULL,
@root_condition_id int = -1,
@root_condition_name sysname = NULL,
@object_set_id int = -1,
@object_set sysname = NULL,
@is_enabled bit = NULL
WITH EXECUTE AS OWNER
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	--Check for the execution mode value
	IF (((@execution_mode IS NOT NULL)) AND (@execution_mode NOT IN (0,1,2,4,5,6)))
	BEGIN 
		RAISERROR(34004, -1, -1, @execution_mode)
		RETURN (1)
	END

    -- Turn [nullable] empty string parameters into NULLs
    IF @name = ''           SELECT @name = NULL
    IF @condition_name = '' SELECT @condition_name = NULL
    IF @root_condition_name = '' 
        BEGIN
        SELECT @root_condition_name = NULL
        IF @root_condition_id = -1
            -- root_condition is being reset
            SELECT @root_condition_id = NULL
        END
    IF @object_set = '' 
        BEGIN
        SELECT @object_set = NULL
        IF @object_set_id = -1
            -- object_set is being reset
            SELECT @object_set_id = NULL
        END

    DECLARE @retval              INT

    EXEC @retval = msdb.dbo.sp_syspolicy_verify_policy_identifiers @name, @policy_id OUTPUT
    IF (@retval <> 0)
        RETURN (1)

    SELECT  @execution_mode = ISNULL(@execution_mode, execution_mode),
            @is_enabled = ISNULL(@is_enabled, is_enabled) 
        FROM msdb.dbo.syspolicy_policies WHERE policy_id = @policy_id

    IF(@condition_id IS NOT NULL or @condition_name IS NOT NULL)
    BEGIN
        EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @condition_name = @condition_name OUTPUT, @condition_id = @condition_id OUTPUT
        IF (@retval <> 0)
            RETURN (1)
    END

    IF((@root_condition_id IS NOT NULL and @root_condition_id != -1) or @root_condition_name IS NOT NULL)
    BEGIN
        IF (@root_condition_id = -1 and @root_condition_name IS NOT NULL)
            SET @root_condition_id = NULL
        EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @condition_name = @root_condition_name OUTPUT, @condition_id = @root_condition_id OUTPUT
        IF (@retval <> 0)
            RETURN (1)
    END

    SET @schedule_uid = ISNULL (@schedule_uid, '{00000000-0000-0000-0000-000000000000}')

    IF (@schedule_uid = '{00000000-0000-0000-0000-000000000000}' AND (@execution_mode & 4) = 4)
    BEGIN
        RAISERROR (34011, -1, -1, 'schedule_uid', 4)
        RETURN(1)
    END

    IF (@is_enabled = 1 AND @execution_mode = 0)
    BEGIN
        RAISERROR (34011, -1, -1, 'is_enabled', @execution_mode)
        RETURN(1)
    END

    IF (@schedule_uid != '{00000000-0000-0000-0000-000000000000}')
    BEGIN
        -- verify the schedule exists
        IF NOT EXISTS (SELECT schedule_id FROM msdb.dbo.sysschedules WHERE schedule_uid = @schedule_uid)
        BEGIN
            RAISERROR (14365, -1, -1)
            RETURN(1)
        END
    END

    DECLARE @object_set_facet_id INT
    IF ((@object_set_id IS NOT NULL and @object_set_id != -1) or @object_set IS NOT NULL)
    BEGIN
        IF (@object_set_id = -1 and @object_set IS NOT NULL)
            SET @object_set_id = NULL
        EXEC @retval = msdb.dbo.sp_syspolicy_verify_object_set_identifiers @name = @object_set OUTPUT, @object_set_id = @object_set_id OUTPUT
        IF (@retval <> 0)
            RETURN (1)

        SELECT @object_set_facet_id = facet_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name = @object_set
        -- Ensure the object set has been created from the same facet that the policy condition has been created
        IF (@object_set_facet_id <> (SELECT facet_id FROM msdb.dbo.syspolicy_conditions_internal WHERE condition_id = @condition_id))
        BEGIN
            -- TODO: RAISEERROR that specified object_set isn't created from the facet that the policy condition has been created from
            RAISERROR(N'specified object set does not match facet the policy condition was created off', -1, -1)
            RETURN(1) -- Failure
        END
    END

    DECLARE @policy_category_id INT
    SET @policy_category_id = NULL
    BEGIN TRANSACTION 

    DECLARE @old_policy_category_id INT
    SELECT @old_policy_category_id = policy_category_id 
        FROM syspolicy_policies 
        WHERE policy_id = @policy_id 

    IF ( (@policy_category IS NOT NULL and @policy_category != '') )
    BEGIN
        IF NOT EXISTS (SELECT * from syspolicy_policy_categories WHERE name = @policy_category)
        BEGIN
            RAISERROR(34015, -1, -1,@policy_category)
            RETURN(1) -- Failure
        END
        ELSE
            SELECT @policy_category_id = policy_category_id FROM msdb.dbo.syspolicy_policy_categories WHERE name = @policy_category
    END

        -- If the caller gave us an empty string for the
        -- @policy_category, then that means to remove the group.
    DECLARE @new_policy_category_id INT
        SELECT  @new_policy_category_id = @old_policy_category_id
    IF ( (@policy_category = '') )
                SELECT @new_policy_category_id = NULL
    ELSE IF (@policy_category_id IS NOT NULL)
                SELECT @new_policy_category_id = @policy_category_id

    UPDATE msdb.dbo.syspolicy_policies_internal
    SET 
        condition_id = ISNULL(@condition_id, condition_id),
        root_condition_id = CASE @root_condition_id WHEN -1 THEN root_condition_id ELSE @root_condition_id END,
        execution_mode = ISNULL(@execution_mode, execution_mode ),
        schedule_uid = @schedule_uid,
        policy_category_id = @new_policy_category_id, 
        description = ISNULL(@description, description),
        help_text = ISNULL(@help_text, help_text),
        help_link = ISNULL(@help_link, help_link),
        is_enabled = ISNULL(@is_enabled, is_enabled),
        object_set_id = CASE @object_set_id WHEN -1 THEN object_set_id ELSE @object_set_id END
    WHERE policy_id = @policy_id

    COMMIT TRANSACTION
    RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_policy]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_delete_policy] 
@name sysname = NULL,
@policy_id int = NULL
WITH EXECUTE AS OWNER
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DECLARE @retval              INT

    EXEC @retval = sp_syspolicy_verify_policy_identifiers @name, @policy_id OUTPUT
    IF (@retval <> 0)
        RETURN (1)

    DELETE msdb.dbo.syspolicy_policies_internal 
        WHERE policy_id = @policy_id

    RETURN (0)
END
GO


IF NOT EXISTS (SELECT * FROM sys.types where name = 'syspolicy_target_filters_type')
BEGIN
    PRINT 'Creating type [dbo].[syspolicy_target_filters_type]...'
    CREATE TYPE [dbo].[syspolicy_target_filters_type]
    AS
    TABLE (
        target_filter_id int,
        policy_id int,
        type sysname NOT NULL,
        filter nvarchar(max) NOT NULL,
        type_skeleton sysname NOT NULL
        )
END
GO

PRINT 'Creating function [dbo].[syspolicy_fn_get_bad_filters]...'
GO

-- This function returns filters that are not supported
-- It is used to prevent unsupported filters from being
-- created. It will only reject well formed filters, in 
-- other words it will not perform a full syntax check.
CREATE FUNCTION [dbo].[syspolicy_fn_get_bad_filters] (
    @inserted [dbo].[syspolicy_target_filters_type] READONLY
)
RETURNS TABLE
AS
    RETURN 
    (
        SELECT filter FROM @inserted 
        WHERE    
            -- do not accept filters for the next level 
            filter LIKE N'Server/%/%\[@%=%\]%' ESCAPE '\' AND 
            -- take out cases when the property contains the pattern
            filter NOT LIKE 'Server/%\[%\[%\]%\]%' ESCAPE '\'
    )
GO

---------------------------------------------------------------
-- Target Set object
---------------------------------------------------------------

IF OBJECT_ID ('[dbo].[syspolicy_target_sets_internal]') IS NULL
BEGIN
    PRINT 'Creating table [dbo].[syspolicy_target_sets_internal]...'
    CREATE TABLE [dbo].[syspolicy_target_sets_internal] (
        target_set_id int NOT NULL IDENTITY(1,1),
        object_set_id int NOT NULL,
        type_skeleton nvarchar(440) NOT NULL,
        type sysname NOT NULL,
        enabled bit NOT NULL,
        -- TODO: Verify if the primary access method of this table is based on policy_id then perhaps the clustered intdex should be on the policy id?
        CONSTRAINT [PK_syspolicy_target_sets] PRIMARY KEY CLUSTERED (target_set_id),
        )
    ALTER TABLE [dbo].[syspolicy_target_sets_internal] 
        ADD CONSTRAINT [FK_syspolicy_target_sets_syspolicy_object_sets] FOREIGN KEY(object_set_id)
        REFERENCES [dbo].[syspolicy_object_sets_internal] (object_set_id)
        ON DELETE CASCADE
    
    CREATE UNIQUE INDEX [UX_syspolicy_target_sets] ON [dbo].[syspolicy_target_sets_internal](object_set_id, type_skeleton)
END
GO

PRINT 'Creating view [dbo].[syspolicy_target_sets]...'
GO
CREATE VIEW [dbo].[syspolicy_target_sets]
AS
    SELECT     
        target_set_id,
        object_set_id,
        type_skeleton,
        type,
        enabled
    FROM [dbo].[syspolicy_target_sets_internal]
GO

IF OBJECT_ID ('[dbo].[syspolicy_target_set_levels_internal]') IS NULL
BEGIN
    PRINT 'Creating table [dbo].[syspolicy_target_set_levels_internal]...'
    CREATE TABLE [dbo].[syspolicy_target_set_levels_internal] (
        target_set_level_id int NOT NULL IDENTITY(1,1),
        target_set_id int NOT NULL,
        type_skeleton nvarchar(440) NOT NULL,
        condition_id int NULL,
        level_name sysname NOT NULL,
        CONSTRAINT [PK_syspolicy_target_set_levels_internal] PRIMARY KEY CLUSTERED (target_set_level_id),
        )
    ALTER TABLE [dbo].[syspolicy_target_set_levels_internal] 
        ADD CONSTRAINT [FK_syspolicy_levels_target_sets] FOREIGN KEY(target_set_id)
        REFERENCES [dbo].[syspolicy_target_sets_internal] (target_set_id)
        ON DELETE CASCADE
    ALTER TABLE [dbo].[syspolicy_target_set_levels_internal] 
        ADD CONSTRAINT [FK_syspolicy_levels_conditions] FOREIGN KEY(condition_id)
        REFERENCES [dbo].[syspolicy_conditions_internal] (condition_id)
    
    CREATE UNIQUE INDEX [UX_syspolicy_levels] ON [dbo].[syspolicy_target_sets_internal](target_set_id, type_skeleton)
END
GO

PRINT 'Creating view [dbo].[syspolicy_target_set_levels]...'
GO
CREATE VIEW [dbo].[syspolicy_target_set_levels]
AS
    SELECT     
        target_set_level_id,
        target_set_id,
        type_skeleton,
        condition_id,
        level_name
    FROM [dbo].[syspolicy_target_set_levels_internal]
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_add_target_set]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_add_target_set] 
@object_set_id int = NULL,
@object_set_name sysname = NULL,
@type_skeleton nvarchar(max),
@type sysname,
@enabled bit,
@target_set_id int OUTPUT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DECLARE @retval              INT

    EXEC @retval = dbo.sp_syspolicy_verify_object_set_identifiers @name = @object_set_name OUTPUT, @object_set_id = @object_set_id OUTPUT
    if( @retval <> 0)
        RETURN(1)
    
    INSERT INTO msdb.[dbo].[syspolicy_target_sets_internal]
                                        (object_set_id,
                                        type_skeleton,
                                        type,
                                        enabled)
    VALUES                            
                                        (@object_set_id, 
                                        @type_skeleton,
                                        @type,
                                        @enabled)

    SELECT @retval = @@error
    SET @target_set_id = SCOPE_IDENTITY()
    RETURN(@retval)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_update_target_set]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_update_target_set]
@target_set_id int,
@enabled bit
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	UPDATE [msdb].[dbo].[syspolicy_target_sets_internal]
	SET
		enabled = @enabled
	WHERE
		target_set_id = @target_set_id
	
	IF (@@ROWCOUNT = 0)
	BEGIN
		DECLARE @target_set_id_as_char VARCHAR(36)
		SELECT @target_set_id_as_char = CONVERT(VARCHAR(36), @target_set_id)
		RAISERROR(14262, -1, -1, '@target_set_id', @target_set_id_as_char)
		RETURN (1)
	END

    RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_target_set]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_delete_target_set] 
@target_set_id int
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DELETE msdb.[dbo].[syspolicy_target_sets_internal] 
		WHERE target_set_id = @target_set_id
	
	IF (@@ROWCOUNT = 0)
	BEGIN
		DECLARE @target_set_id_as_char VARCHAR(36)
		SELECT @target_set_id_as_char = CONVERT(VARCHAR(36), @target_set_id)
		RAISERROR(14262, -1, -1, '@target_set_id', @target_set_id_as_char)
		RETURN (1)
	END

    RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_add_target_set_level]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_add_target_set_level] 
@target_set_id int,
@type_skeleton nvarchar(max),
@condition_id int = NULL,
@condition_name sysname = NULL,
@level_name sysname,
@target_set_level_id int OUTPUT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DECLARE @retval              INT

    IF NOT EXISTS (SELECT * FROM syspolicy_target_sets WHERE target_set_id = @target_set_id)
            RETURN (1)

    IF (@condition_name = '')
        SET @condition_name = NULL

    IF(@condition_id IS NOT NULL or @condition_name IS NOT NULL)
    BEGIN
        EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @condition_name = @condition_name OUTPUT, @condition_id = @condition_id OUTPUT
        IF (@retval <> 0)
            RETURN (1)
    END

    
    INSERT INTO msdb.[dbo].[syspolicy_target_set_levels_internal]
                                        (target_set_id,
                                        type_skeleton,
                                        condition_id,
                                        level_name)
    VALUES                            
                                        (@target_set_id,
                                        @type_skeleton,
                                        @condition_id,
                                        @level_name)

    SELECT @retval = @@error
    SET @target_set_level_id = SCOPE_IDENTITY()
    RETURN(@retval)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_update_target_set_level]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_update_target_set_level] 
@target_set_id int,
@type_skeleton nvarchar(max),
@condition_id int = NULL,
@condition_name sysname = NULL
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DECLARE @retval int

    IF @condition_name = '' SET @condition_name = NULL

    IF(@condition_id IS NOT NULL or @condition_name IS NOT NULL)
    BEGIN
        EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @condition_name = @condition_name OUTPUT, @condition_id = @condition_id OUTPUT
        IF (@retval <> 0)
            RETURN (1)
    END

    UPDATE msdb.[dbo].[syspolicy_target_set_levels_internal] 
        SET condition_id = @condition_id
        WHERE target_set_id = @target_set_id AND type_skeleton = @type_skeleton
    
    IF (@@ROWCOUNT = 0)
    BEGIN
        DECLARE @id nvarchar(max)
        SET @id = '@target_set_id='+LTRIM(STR(@target_set_id))+' @type_skeleton='''+@type_skeleton+''''
        RAISERROR(14262, -1, -1, 'Target Set Level', @id)
        RETURN (1)
    END

    RETURN (0)
END
GO

PRINT 'Creating function [dbo].[syspolicy_fn_eventing_filter]'
GO

CREATE FUNCTION [dbo].[syspolicy_fn_eventing_filter] (@target_set_id INT)
RETURNS INT
AS
BEGIN
    DECLARE @cnt int, @level sysname, @condition_id int, @ret int

    SELECT @cnt = count(*) FROM msdb.dbo.syspolicy_target_set_levels 
        WHERE target_set_id = @target_set_id AND condition_id IS NOT NULL
    IF @cnt = 0 
        RETURN 1
    ELSE IF @cnt > 1
        RETURN 0
    ELSE
        BEGIN
        SELECT @level = level_name, @condition_id = condition_id FROM msdb.dbo.syspolicy_target_set_levels 
            WHERE target_set_id = @target_set_id AND condition_id IS NOT NULL
        IF @level != 'Database'
            RETURN 0

		IF @condition_id IS NOT NULL
			BEGIN
			IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_conditions  
				WHERE condition_id = @condition_id AND   
				(1 = CONVERT(xml, expression).exist('//FunctionType/text()[.="ExecuteSql"]') OR
				1 = CONVERT(xml, expression).exist('//FunctionType/text()[.="ExecuteWql"]') ) )
				RETURN 0
			END

        SELECT @ret = is_name_condition 
        FROM msdb.dbo.syspolicy_conditions    
        WHERE condition_id = @condition_id
        END

    RETURN @ret
END
GO

PRINT 'Creating function [dbo].[syspolicy_fn_filter_complete]'
GO

CREATE FUNCTION [dbo].[syspolicy_fn_filter_complete] (@target_set_id INT)
RETURNS INT
AS
BEGIN
    DECLARE @target_set_skeleton nvarchar(max), @skeleton nvarchar(max), @level sysname, @dummy nvarchar(max), @ret int, 
            @i int, @p int
    
    SELECT @target_set_skeleton = type_skeleton, @i=0, @p=CHARINDEX('/',type_skeleton)
        FROM msdb.dbo.syspolicy_target_sets 
        WHERE target_set_id = @target_set_id

    IF @@ROWCOUNT != 1 RETURN 0
    
    IF @target_set_skeleton = 'Server' RETURN 1    

    -- Count the number of levels in the skeleton past the root
    WHILE (@p <> 0)
        BEGIN
            SET @i = @i + 1
        SET @p = CHARINDEX('/', @target_set_skeleton, @p + 1)
        END

    -- Compare the number of levels in the skeleton with those in TSL
    IF (@i = (SELECT COUNT(*) FROM msdb.dbo.syspolicy_target_set_levels 
             WHERE target_set_id = @target_set_id))
        RETURN 1

    RETURN 0
END
GO

PRINT 'Creating trigger [dbo].[syspolicy_insert_target_set_level_trigger] on [dbo].[syspolicy_target_set_levels_internal]'
GO

CREATE TRIGGER [dbo].[syspolicy_insert_target_set_level_trigger] 
    ON [dbo].[syspolicy_target_set_levels_internal]
FOR INSERT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END

    DECLARE @update_notifications INT
    DECLARE @update_ddl_trigger   INT

    SET @update_notifications = 0
    SET @update_ddl_trigger = 0

	DECLARE @level sysname
	SET @level = NULL
	
	-- Don't allow setting non-db levels for runtime policies
    SELECT TOP 1 @level = i.level_name
		FROM inserted i
		JOIN dbo.syspolicy_target_sets s ON (i.target_set_id = s.target_set_id)
		JOIN msdb.dbo.syspolicy_object_sets os ON s.object_set_id = os.object_set_id
		JOIN msdb.dbo.syspolicy_policies p ON (os.object_set_id = p.object_set_id)    
		WHERE 1 = dbo.syspolicy_fn_filter_complete (i.target_set_id) AND
			((p.execution_mode & 3) > 0 AND 0 = dbo.syspolicy_fn_eventing_filter (i.target_set_id))
	IF @level IS NOT NULL
    BEGIN
        RAISERROR(34016, -1, -1, @level) 
        ROLLBACK TRANSACTION
    END


    SELECT @update_notifications = SUM (p.execution_mode & 2), @update_ddl_trigger = SUM (p.execution_mode & 1)
        FROM inserted i
        JOIN dbo.syspolicy_target_sets s ON (i.target_set_id = s.target_set_id)
        JOIN msdb.dbo.syspolicy_object_sets_internal os ON (s.object_set_id = os.object_set_id)
        JOIN msdb.dbo.syspolicy_policies p ON (os.object_set_id = p.object_set_id)    
        WHERE 1 = dbo.syspolicy_fn_filter_complete (i.target_set_id) AND
            ((p.execution_mode & 3) > 0 AND p.is_enabled = 1 AND 1 = dbo.syspolicy_fn_eventing_filter (i.target_set_id))

    IF (@update_ddl_trigger > 0)
        EXEC sys.sp_syspolicy_update_ddl_trigger 

    IF    (@update_notifications > 0)    
        EXEC sys.sp_syspolicy_update_event_notification 
END
GO

PRINT 'Creating trigger [dbo].[syspolicy_update_target_set_level_trigger] on [dbo].[syspolicy_target_set_levels_internal]'
GO

CREATE TRIGGER [dbo].[syspolicy_update_target_set_level_trigger] ON [dbo].[syspolicy_target_set_levels_internal]
FOR UPDATE
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END

    DECLARE @update_notifications INT
    DECLARE @update_ddl_trigger   INT

    SET @update_notifications = 0
    SET @update_ddl_trigger = 0

    IF UPDATE(condition_id)
    BEGIN
		DECLARE @level sysname
		SET @level = NULL
		
		-- Don't allow setting non-db levels for runtime policies
        SELECT TOP 1 @level = i.level_name
			FROM inserted i
			JOIN dbo.syspolicy_target_sets s ON (i.target_set_id = s.target_set_id)
			JOIN msdb.dbo.syspolicy_object_sets os ON s.object_set_id = os.object_set_id
			JOIN msdb.dbo.syspolicy_policies p ON (os.object_set_id = p.object_set_id)    
			WHERE 1 = dbo.syspolicy_fn_filter_complete (i.target_set_id) AND
				((p.execution_mode & 3) > 0 AND 0 = dbo.syspolicy_fn_eventing_filter (i.target_set_id))
		IF @level IS NOT NULL
        BEGIN
            RAISERROR(34016, -1, -1, @level) 
            ROLLBACK TRANSACTION
        END
    END

    SELECT @update_notifications = SUM (p.execution_mode & 2), @update_ddl_trigger = SUM (p.execution_mode & 1)
        FROM inserted i
        JOIN dbo.syspolicy_target_sets s ON (i.target_set_id = s.target_set_id)
        JOIN msdb.dbo.syspolicy_object_sets os ON s.object_set_id = os.object_set_id
        JOIN msdb.dbo.syspolicy_policies p ON (os.object_set_id = p.object_set_id)    
        WHERE 1 = dbo.syspolicy_fn_filter_complete (i.target_set_id) AND
            ((p.execution_mode & 3) > 0 AND p.is_enabled = 1 AND 1 = dbo.syspolicy_fn_eventing_filter (i.target_set_id))

    IF (@update_ddl_trigger > 0)
        EXEC sys.sp_syspolicy_update_ddl_trigger 

    IF    (@update_notifications > 0)    
        EXEC sys.sp_syspolicy_update_event_notification 

END
GO


PRINT 'Creating trigger [dbo].[syspolicy_insert_target_set_trigger] on [dbo].[syspolicy_target_sets_internal]'
GO

CREATE TRIGGER [dbo].[syspolicy_insert_target_set_trigger] 
    ON [dbo].[syspolicy_target_sets_internal]
FOR INSERT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END

    DECLARE @update_notifications INT
    DECLARE @update_ddl_trigger   INT

    SET @update_notifications = 0
    SET @update_ddl_trigger = 0

    -- Only need to check Server TargetSets, as they don't have levels
    SELECT @update_notifications = SUM (p.execution_mode & 2), @update_ddl_trigger = SUM (p.execution_mode & 1)
        FROM inserted i
        JOIN msdb.dbo.syspolicy_object_sets_internal os ON i.object_set_id = os.object_set_id
        JOIN msdb.dbo.syspolicy_policies p ON (os.object_set_id = p.object_set_id)    
        WHERE i.type = 'SERVER' AND ((p.execution_mode & 3) > 0 AND p.is_enabled = 1)

    IF (@update_ddl_trigger > 0)
        EXEC sys.sp_syspolicy_update_ddl_trigger 

    IF    (@update_notifications > 0)    
        EXEC sys.sp_syspolicy_update_event_notification 
END
GO

PRINT 'Creating trigger [dbo].[syspolicy_delete_target_set_trigger] on [dbo].[syspolicy_target_sets_internal]'
GO

CREATE TRIGGER [dbo].[syspolicy_delete_target_set_trigger] ON [dbo].[syspolicy_target_sets_internal]
FOR DELETE
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END

    DECLARE @update_notifications INT
    DECLARE @update_ddl_trigger   INT

    SET @update_notifications = 0
    SET @update_ddl_trigger = 0

    -- If this is cascade delete, there will be no policies to join
    SELECT @update_notifications = SUM (p.execution_mode & 2), @update_ddl_trigger = SUM (p.execution_mode & 1)
        FROM deleted d
        JOIN msdb.dbo.syspolicy_object_sets_internal os ON d.object_set_id = os.object_set_id
        JOIN msdb.dbo.syspolicy_policies p ON (os.object_set_id = p.object_set_id)    
        WHERE ((p.execution_mode & 3) > 0 AND p.is_enabled = 1)

    IF (@update_ddl_trigger > 0)
        EXEC sys.sp_syspolicy_update_ddl_trigger 

    IF    (@update_notifications > 0)    
        EXEC sys.sp_syspolicy_update_event_notification 
END
GO


---------------------------------------------------------------
-- Policy category subscription object
---------------------------------------------------------------

IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_policy_category_subscriptions_internal')
BEGIN
    PRINT 'Creating table [dbo].[syspolicy_policy_category_subscriptions_internal]...';
    CREATE TABLE [dbo].[syspolicy_policy_category_subscriptions_internal] (
        policy_category_subscription_id int IDENTITY(1,1),
        target_type sysname NOT NULL,
        target_object sysname NOT NULL, 
        policy_category_id int NOT NULL,
        CONSTRAINT [PK_syspolicy_policy_category_subscriptions] PRIMARY KEY CLUSTERED (policy_category_subscription_id ASC)
        );

    CREATE UNIQUE INDEX [UX_syspolicy_policy_category_subscriptions] ON [dbo].[syspolicy_policy_category_subscriptions_internal](policy_category_id, target_object, target_type)

    ALTER TABLE [dbo].[syspolicy_policy_category_subscriptions_internal] 
        ADD CONSTRAINT [FK_syspolicy_policy_category_subscriptions_syspolicy_policy_categories] FOREIGN KEY(policy_category_id)
        REFERENCES [dbo].[syspolicy_policy_categories_internal] (policy_category_id)
        ON DELETE CASCADE;
END
GO

PRINT 'Creating view [dbo].[syspolicy_policy_category_subscriptions]...'
GO
CREATE VIEW [dbo].[syspolicy_policy_category_subscriptions]
AS
    SELECT     
        policy_category_subscription_id,
        target_type,
        target_object,
        policy_category_id
    FROM [dbo].[syspolicy_policy_category_subscriptions_internal]
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_add_policy_category_subscription]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_add_policy_category_subscription] 
    @target_type sysname,
    @target_object sysname,
    @policy_category sysname,
    @policy_category_subscription_id int = NULL OUTPUT
WITH EXECUTE AS OWNER
AS
BEGIN
    DECLARE @retval int    

 IF(@target_type IS NOT NULL)
	BEGIN
		IF(@target_type <> 'DATABASE')
		BEGIN
			RAISERROR(34018,-1,-1,@target_type);
			RETURN(1)
		END
	END


 IF(NOT EXISTS(SELECT * FROM sys.databases WHERE name=@target_object))
	BEGIN
		RAISERROR(34019,-1,-1,@target_object);
		RETURN(1)
	END

    -- convert category_name into id if needed
    DECLARE @policy_category_id INT
    BEGIN TRANSACTION 
    IF ( (@policy_category IS NOT NULL AND @policy_category != '') )
    BEGIN 
        IF NOT EXISTS (SELECT * from syspolicy_policy_categories WHERE name = @policy_category)
        BEGIN
            INSERT INTO syspolicy_policy_categories_internal(name) VALUES (@policy_category)
            SELECT @policy_category_id = SCOPE_IDENTITY()
        END
        ELSE
            SELECT @policy_category_id = policy_category_id FROM syspolicy_policy_categories WHERE name = @policy_category
    END
    
    INSERT INTO msdb.[dbo].[syspolicy_policy_category_subscriptions_internal]
                                            (target_type, 
                                            target_object,
                                            policy_category_id)
        VALUES                            
                                            (@target_type, 
                                            @target_object, 
                                            @policy_category_id)

    SELECT @retval = @@error
    SET @policy_category_subscription_id = SCOPE_IDENTITY()

    COMMIT TRANSACTION
    RETURN(@retval)

END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_update_policy_category_subscription]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_update_policy_category_subscription] 
@policy_category_subscription_id int,
@target_type sysname = NULL,
@target_object sysname = NULL,
@policy_category sysname = NULL
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	-- Turn [nullable] empty string parameters into NULLs
	IF @target_type = ''      SELECT @target_type = NULL
	IF @target_object = ''    SELECT @target_object = NULL
	IF @policy_category = ''  SELECT @policy_category = NULL

    IF(@target_type IS NOT NULL)
	BEGIN
		IF(LOWER(@target_type) <> 'database')
		BEGIN
			RAISERROR(34018,-1,-1,@target_type);
			RETURN(1)
		END
	END
	
	IF(@target_object IS NOT NULL AND NOT EXISTS(SELECT * FROM sys.databases WHERE name=@target_object))
	BEGIN
		RAISERROR(34019,-1,-1,@target_object);
		RETURN(1)
	END

    DECLARE @policy_category_id INT
    SET @policy_category_id = NULL
    BEGIN TRANSACTION 

    DECLARE @old_policy_category_id INT
    SET @old_policy_category_id = NULL

    IF ( (@policy_category IS NOT NULL) )
    BEGIN 
        IF NOT EXISTS (SELECT * from syspolicy_policy_categories WHERE name = @policy_category)
        BEGIN
            -- add a new policy category
            INSERT INTO syspolicy_policy_categories_internal(name) VALUES (@policy_category)
            SELECT @policy_category_id = SCOPE_IDENTITY()

            SELECT @old_policy_category_id = policy_category_id 
                FROM syspolicy_policy_category_subscriptions 
                WHERE policy_category_subscription_id = @policy_category_subscription_id 
        END
        ELSE
            SELECT @policy_category_id = policy_category_id FROM syspolicy_policy_categories WHERE name = @policy_category
    END
    
    DECLARE @group_usage_count INT
    SELECT @group_usage_count = COUNT(*) 
        FROM syspolicy_policy_category_subscriptions  
        WHERE policy_category_id = @old_policy_category_id

    SELECT @group_usage_count = @group_usage_count + COUNT(*) 
        FROM syspolicy_policies  
        WHERE policy_category_id = @old_policy_category_id

    UPDATE msdb.[dbo].[syspolicy_policy_category_subscriptions_internal] 
        SET
            target_type            = ISNULL(@target_type, target_type),
            target_object       = ISNULL(@target_object, target_object),
            policy_category_id        = ISNULL(@policy_category_id, policy_category_id)
        WHERE policy_category_subscription_id = @policy_category_subscription_id

    IF (@@ROWCOUNT = 0)
    BEGIN
        DECLARE @policy_category_subscription_id_as_char VARCHAR(36)
        SELECT @policy_category_subscription_id_as_char = CONVERT(VARCHAR(36), @policy_category_subscription_id)
        RAISERROR(14262, -1, -1, '@policy_category_subscription_id', @policy_category_subscription_id_as_char)
        ROLLBACK TRANSACTION
        RETURN(1) -- Failure
    END

    -- delete the old entry if it was used only by this policy
    DELETE syspolicy_policy_categories_internal WHERE 
        policy_category_id = @old_policy_category_id
        AND 1 = @group_usage_count

    COMMIT TRANSACTION
    RETURN (0)
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_policy_category_subscription]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_delete_policy_category_subscription] 
@policy_category_subscription_id int
WITH EXECUTE AS OWNER
AS
BEGIN
    DECLARE @old_policy_category_id INT
    SELECT @old_policy_category_id = policy_category_id 
        FROM dbo.syspolicy_policy_category_subscriptions 
        WHERE policy_category_subscription_id = @policy_category_subscription_id

    DECLARE @group_usage_count INT
    SELECT @group_usage_count = COUNT(name) 
        FROM syspolicy_policies pd 
        WHERE pd.policy_category_id = @old_policy_category_id

    DECLARE @subscription_group_usage_count INT
    SELECT @subscription_group_usage_count = COUNT(*) 
        FROM syspolicy_policy_category_subscriptions  
        WHERE policy_category_id = @old_policy_category_id

    SELECT @group_usage_count = @group_usage_count + @subscription_group_usage_count

    DELETE msdb.dbo.syspolicy_policy_category_subscriptions_internal 
        WHERE policy_category_subscription_id = @policy_category_subscription_id

    IF (@@ROWCOUNT = 0)
    BEGIN
        DECLARE @policy_category_subscription_id_as_char VARCHAR(36)
        SELECT @policy_category_subscription_id_as_char = CONVERT(VARCHAR(36), @policy_category_subscription_id)
        RAISERROR(14262, -1, -1, '@policy_category_subscription_id', @policy_category_subscription_id_as_char)
        RETURN(1) -- Failure
    END

    RETURN (0)
END
GO

GO
IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_system_health_state_internal')
BEGIN
	PRINT 'Creating table [dbo].[syspolicy_system_health_state_internal]...'
	CREATE TABLE [dbo].[syspolicy_system_health_state_internal](
			health_state_id bigint IDENTITY PRIMARY KEY CLUSTERED,
			policy_id int NOT NULL REFERENCES [dbo].[syspolicy_policies_internal],
			last_run_date datetime NOT NULL, 
			target_query_expression_with_id nvarchar(400) NOT NULL, 
			target_query_expression nvarchar(max) NOT NULL, 
			result bit NOT NULL);
	CREATE INDEX IX_syspolicy_system_health_state_internal_policy_id ON 
		[dbo].[syspolicy_system_health_state_internal](policy_id);
	CREATE INDEX IX_syspolicy_system_health_state_internal_target_query_expression_with_id ON
		[dbo].[syspolicy_system_health_state_internal](target_query_expression_with_id);
END
GO

CREATE VIEW [dbo].[syspolicy_system_health_state]
AS
    SELECT 
        health_state_id,
        policy_id,
        last_run_date,
        target_query_expression_with_id,
        target_query_expression,
        result
    FROM [dbo].[syspolicy_system_health_state_internal]
GO

IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_policy_execution_history_internal')
BEGIN
	PRINT 'Creating table [dbo].[syspolicy_policy_execution_history_internal]...';
	CREATE TABLE syspolicy_policy_execution_history_internal (
		history_id bigint IDENTITY PRIMARY KEY CLUSTERED,
		policy_id int NOT NULL REFERENCES syspolicy_policies_internal,
		start_date datetime NOT NULL DEFAULT (GETDATE()),
		end_date datetime NULL,
		result bit NOT NULL DEFAULT (0),
		is_full_run bit NOT NULL DEFAULT (1),
		exception_message nvarchar(max) NULL,
		exception nvarchar(max) NULL
	);
	CREATE INDEX IX_syspolicy_policy_execution_history_internal_end_date_policy_id 
		ON [dbo].[syspolicy_policy_execution_history_internal](policy_id, end_date);
	CREATE INDEX IX_syspolicy_policy_execution_history_internal_policy_id 
		ON [dbo].[syspolicy_policy_execution_history_internal](policy_id);
END

PRINT 'Creating trigger [syspolicy_update_system_health_state]...'
GO
CREATE TRIGGER [syspolicy_update_system_health_state] ON [dbo].[syspolicy_policy_execution_history_internal]
AFTER UPDATE
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END

    -- if the entire policy has been checked delete all entries 
    -- regarding that policy
    DELETE FROM [dbo].[syspolicy_system_health_state_internal] 
        WHERE policy_id in (SELECT policy_id FROM inserted WHERE is_full_run = 1)

    -- Note: in the queries below new records are added only for
    -- policies that are enabled for automation

    -- if the policy is evaluated against a single target
    -- delete the old entry
    DELETE FROM [dbo].[syspolicy_system_health_state_internal] 
        WHERE policy_id in 
            (SELECT i.policy_id FROM inserted i WHERE i.is_full_run = 0) AND
                target_query_expression_with_id in (SELECT target_query_expression_with_id 
                                                    FROM [dbo].[syspolicy_policy_execution_history_details_internal] d 
                                                    INNER JOIN inserted i2 ON i2.history_id = d.history_id 
                                                    WHERE i2.is_full_run = 0)


    -- insert the detail rows, but only for failures
    -- this is done both for the full runs and for the partial runs
    -- we will not insert anything if this is a ghost record, i.e. 
    -- target_query_expression_with_id is null 
    -- this will happen when we log prevent policies
    INSERT INTO [dbo].[syspolicy_system_health_state_internal] 
        (policy_id, last_run_date, target_query_expression_with_id, target_query_expression, result)
    SELECT i.policy_id, d.execution_date, d.target_query_expression_with_id, d.target_query_expression, d.result
        FROM inserted i 
        INNER JOIN [dbo].[syspolicy_policy_execution_history_details_internal] d on i.history_id = d.history_id
        INNER JOIN [dbo].[syspolicy_policies] p on i.policy_id = p.policy_id
        WHERE d.result = 0 AND p.is_enabled = 1 AND d.target_query_expression_with_id != N''
        
    -- delete all the success detail rows with no expression
    -- these are rows inserted so that we can update the system health state
    -- make an exception if the global switch says we should keep those records
    IF( 0 = (SELECT ISNULL(convert(bit, current_value), 0) FROM msdb.dbo.syspolicy_configuration WHERE name = 'LogOnSuccess'))
    BEGIN
        DELETE FROM d
        FROM [dbo].[syspolicy_policy_execution_history_details_internal] d
        INNER JOIN inserted i ON i.history_id = d.history_id
            WHERE d.result_detail = N''
    END
END
GO

CREATE VIEW [dbo].[syspolicy_policy_execution_history]
AS
    SELECT 
        history_id,
        policy_id,
        start_date,
        end_date,
        result,
        exception_message,
        exception
    FROM [dbo].[syspolicy_policy_execution_history_internal]
GO

IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_policy_execution_history_details_internal')
BEGIN
	PRINT 'Creating table [dbo].[syspolicy_policy_execution_history_details_internal]...';
    CREATE TABLE syspolicy_policy_execution_history_details_internal 
    (
          detail_id bigint IDENTITY,
          history_id bigint NOT NULL REFERENCES syspolicy_policy_execution_history_internal ON DELETE CASCADE,
          target_query_expression nvarchar(4000) NOT NULL,
          target_query_expression_with_id nvarchar(4000) NOT NULL,
          execution_date datetime NOT NULL DEFAULT (GETDATE()),
          result bit NOT NULL,
          result_detail nvarchar(max) NULL, 
          exception_message nvarchar(max) NULL,
          exception nvarchar(max) NULL,
          CONSTRAINT [PK_syspolicy_policy_execution_history_details_id] PRIMARY KEY CLUSTERED(history_id, detail_id)
    )
END
GO

CREATE VIEW [dbo].[syspolicy_policy_execution_history_details]
AS
    SELECT 
        detail_id,
        history_id,
        target_query_expression,
        execution_date,
        result,
        result_detail,
        exception_message,
        exception
    FROM [dbo].[syspolicy_policy_execution_history_details_internal]
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_log_policy_execution_start]...'
GO
CREATE PROC [dbo].[sp_syspolicy_log_policy_execution_start] 
    @policy_id int,
    @is_full_run bit,
    @history_id bigint OUTPUT 
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole', 0
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END
    DECLARE @ret int

    SET @history_id = 0

    EXEC @ret = dbo.sp_syspolicy_verify_policy_identifiers NULL, @policy_id
    IF @ret <> 0 RETURN -1

    INSERT syspolicy_policy_execution_history_internal (policy_id, is_full_run) VALUES (@policy_id, @is_full_run) 
    SET @history_id = SCOPE_IDENTITY ()
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_log_policy_execution_end]...'
GO
CREATE PROC [dbo].[sp_syspolicy_log_policy_execution_end] 
    @history_id bigint, 
    @result bit,
    @exception_message nvarchar(max) = NULL,
    @exception nvarchar(max) = NULL
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole', 0
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

    UPDATE syspolicy_policy_execution_history_internal 
      SET result = @result,
          end_date = GETDATE(),
          exception_message = @exception_message,
          exception = @exception
      WHERE history_id = @history_id
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_log_policy_execution_detail]...'
GO
CREATE PROC [dbo].[sp_syspolicy_log_policy_execution_detail] 
 @history_id bigint, 
 @target_query_expression nvarchar(4000), 
 @target_query_expression_with_id nvarchar(4000), 
 @result bit, 
 @result_detail nvarchar(max),
 @exception_message nvarchar(max) = NULL,
 @exception nvarchar(max) = NULL
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole', 0
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END
    BEGIN TRANSACTION
    DECLARE @is_valid_entry INT
    -- take an update lock on this table first to prevent deadlock
    SELECT @is_valid_entry = count(*) FROM syspolicy_policy_execution_history_internal
        WITH (UPDLOCK) 
        WHERE history_id = @history_id

    INSERT syspolicy_policy_execution_history_details_internal (
                                history_id, 
                                target_query_expression, 
                                target_query_expression_with_id, 
                                result, 
                                result_detail,
                                exception_message,
                                exception) 
                        VALUES (
                                @history_id, 
                                @target_query_expression, 
                                @target_query_expression_with_id, 
                                @result, 
                                @result_detail,
                                @exception_message,
                                @exception) 
    IF( @@TRANCOUNT > 0)
        COMMIT
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_policy_execution_history]...'
GO
CREATE PROC [dbo].[sp_syspolicy_delete_policy_execution_history] 
 @policy_id int,
 @oldest_date datetime
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

    IF @oldest_date IS NULL
        BEGIN
        IF (@policy_id IS NULL)
            DELETE syspolicy_policy_execution_history_internal
        ELSE
            DELETE syspolicy_policy_execution_history_internal WHERE policy_id = @policy_id
        END
    ELSE
        BEGIN
        IF (@policy_id IS NULL)
            DELETE syspolicy_policy_execution_history_internal WHERE start_date < @oldest_date
        ELSE
            DELETE syspolicy_policy_execution_history_internal WHERE policy_id = @policy_id AND start_date < @oldest_date
        END
END
GO


PRINT 'Creating procedure [dbo].[sp_syspolicy_mark_system]...'
GO
CREATE PROC dbo.sp_syspolicy_mark_system @type sysname, @name sysname=NULL, @object_id int=NULL, @marker bit=NULL 
AS
BEGIN
	-- If @marker IS NULL simple return the state

    DECLARE @retval_check int;
    EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
    IF ( 0!= @retval_check)
    BEGIN
        RETURN @retval_check
    END

	DECLARE @retval int
	
	IF (UPPER(@type  collate SQL_Latin1_General_CP1_CS_AS) = N'POLICY')
	BEGIN
	    EXEC @retval = sp_syspolicy_verify_policy_identifiers @name, @object_id OUTPUT
		IF (@retval <> 0)
			RETURN (1)

		IF @marker IS NULL
		BEGIN
			SELECT policy_id, name, is_system FROM syspolicy_policies_internal WHERE policy_id = @object_id
		END
		ELSE
		BEGIN
			UPDATE msdb.dbo.syspolicy_policies_internal
			SET is_system = @marker 
			WHERE policy_id = @object_id
		END
	END
	ELSE IF (UPPER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'CONDITION')
	BEGIN
	    EXEC @retval = sp_syspolicy_verify_condition_identifiers @name, @object_id OUTPUT
		IF (@retval <> 0)
			RETURN (1)

		IF @marker IS NULL
		BEGIN
			SELECT condition_id, name, is_system FROM syspolicy_conditions_internal WHERE condition_id = @object_id
		END
		ELSE
		BEGIN
			UPDATE msdb.dbo.syspolicy_conditions_internal
			SET is_system = @marker 
			WHERE condition_id = @object_id
		END
	END
	ELSE IF (UPPER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'OBJECTSET')
	BEGIN
	    EXEC @retval = sp_syspolicy_verify_object_set_identifiers @name, @object_id OUTPUT
		IF (@retval <> 0)
			RETURN (1)

		IF @marker IS NULL
		BEGIN
			SELECT object_set_id, object_set_name, is_system FROM syspolicy_object_sets_internal WHERE object_set_id = @object_id
		END
		ELSE
		BEGIN
			UPDATE msdb.dbo.syspolicy_object_sets_internal
			SET is_system = @marker 
			WHERE object_set_id = @object_id
		END
	END
    ELSE
    BEGIN
		RAISERROR(14262, -1, -1, '@type', @type)
		RETURN(1) -- Failure
	END
	
    SELECT @retval = @@error
    RETURN(@retval)
END
GO 




-----------------------------------------------------------
-- event processing
-----------------------------------------------------------

PRINT 'Creating function [dbo].[syspolicy_fn_get_type_name]...'
GO
CREATE FUNCTION [dbo].[syspolicy_fn_get_type_name](@event_type_name sysname)
RETURNS sysname
AS
BEGIN
    RETURN 
    (CASE LOWER(@event_type_name)
        WHEN 'procedure' THEN 'StoredProcedure'
        WHEN 'function' THEN 'UserDefinedFunction'
        WHEN 'type' THEN 'UserDefinedType'
        WHEN 'sql user' THEN 'User'
        WHEN 'certificate user' THEN 'User'
        WHEN 'asymmetric key user' THEN 'User'
        WHEN 'windows user' THEN 'User'
        WHEN 'group user' THEN 'User'
        WHEN 'application role' THEN 'ApplicationRole'
        ELSE UPPER(SUBSTRING(@event_type_name, 1,1)) + LOWER(SUBSTRING(@event_type_name, 2,LEN(@event_type_name)))
    END)
END
GO

IF OBJECT_ID ('[dbo].[syspolicy_execution_internal]') IS NULL
BEGIN
    PRINT 'Creating table [dbo].[syspolicy_execution_internal]...'
    CREATE TABLE [dbo].[syspolicy_execution_internal] (
        policy_id int,
        synchronous bit,
        event_data xml)
END
GO

IF OBJECT_ID ('[dbo].[syspolicy_execution_trigger]') IS NOT NULL
BEGIN
    DROP TRIGGER [dbo].[syspolicy_execution_trigger] 
END
GO
CREATE TRIGGER [dbo].[syspolicy_execution_trigger] ON [dbo].[syspolicy_execution_internal]
INSTEAD OF INSERT
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN;
	END

	IF NOT EXISTS (SELECT * FROM inserted)
		RETURN

    DECLARE @policy_id int
    DECLARE @synchronous bit
    DECLARE @event_data xml
    DECLARE affected_policies CURSOR LOCAL FOR 
        SELECT policy_id, synchronous, event_data FROM inserted
    
    OPEN affected_policies
    FETCH NEXT FROM affected_policies INTO @policy_id, @synchronous, @event_data
    DECLARE @err int
    SET @err = 0
    WHILE (@@FETCH_STATUS = 0 AND (@synchronous = 0 OR @err = 0)) 
    BEGIN
        DECLARE @pol_name sysname
        SELECT @pol_name = p.name
            FROM dbo.syspolicy_policies p 
            WHERE p.policy_id = @policy_id

        IF (@synchronous = 0)
        BEGIN
            -- trace what policy is processing this event
            DECLARE @msg nvarchar(1000)
            SET @msg = N'Policy ''' + @pol_name + ''' was activated by an event.'
            RAISERROR(@msg, 1, 1) with log
        END

        -- execute the policy
        EXEC @err = msdb.sys.sp_syspolicy_execute_policy @policy_name =@pol_name, @event_data = @event_data, @synchronous = @synchronous
    
        -- move to the next policy if we're checking the policy
        -- or if we are in enforce mode and we haven't failed
        IF( @synchronous = 0 OR @err = 0)
            FETCH NEXT FROM affected_policies INTO @policy_id, @synchronous, @event_data
    END

    CLOSE affected_policies
    DEALLOCATE affected_policies

END
GO
    

PRINT 'Creating procedure [dbo].[sp_syspolicy_dispatch_event]...'
GO
-- These settings are necessary to read XML.
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
SET ARITHABORT ON
SET CONCAT_NULL_YIELDS_NULL ON
SET NUMERIC_ROUNDABORT OFF
SET QUOTED_IDENTIFIER ON
GO

-- procedure that processes an event and decides 
-- what binding should handle it
CREATE PROCEDURE [dbo].[sp_syspolicy_dispatch_event]  @event_data xml, @synchronous bit
AS
BEGIN
	-- disable these as the caller may not have SHOWPLAN permission
	SET STATISTICS XML OFF
	SET STATISTICS PROFILE OFF

	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	IF ( @synchronous = 0)
		PRINT CONVERT(nvarchar(max), @event_data)
	DECLARE @event_type sysname
	DECLARE @object_type sysname
	DECLARE @database sysname
	DECLARE @mode int
	DECLARE @filter_expression nvarchar(4000)
	DECLARE @filter_expression_skeleton nvarchar(4000)

    SET @mode = (case @synchronous when 1 then 1 else 2 end)

    -- These settings are necessary to read XML.
    SET ANSI_NULLS ON
    SET ANSI_PADDING ON
    SET ANSI_WARNINGS ON
    SET ARITHABORT ON
    SET CONCAT_NULL_YIELDS_NULL ON
    SET NUMERIC_ROUNDABORT OFF
    SET QUOTED_IDENTIFIER ON

    SET NOCOUNT ON 

    SELECT 
        @event_type = T.c.value('(EventType/text())[1]', 'sysname')
        , @database = T.c.value('(DatabaseName/text())[1]', 'sysname')
        , @object_type = T.c.value('(ObjectType/text())[1]', 'sysname')
    FROM   @event_data.nodes('/EVENT_INSTANCE') T(c)
    
    -- we are going to ignore events that affect subobjects
    IF  (@event_type = N'ALTER_DATABASE' AND 
        1 = @event_data.exist('EVENT_INSTANCE/AlterDatabaseActionList')) OR
        (@event_type = N'ALTER_TABLE' AND 
        1 = @event_data.exist('EVENT_INSTANCE/AlterTableActionList'))
    BEGIN
        RETURN;
    END

    -- convert trace numerical objecttypes to string
    IF (ISNUMERIC(@object_type) = 1)
        select @object_type = name from master.dbo.spt_values where type = 'EOB' and number = @object_type

    -- these events do not have ObjectType and ObjectName
    IF ((@object_type IS NULL) AND @event_type IN ('CREATE_DATABASE', 'DROP_DATABASE', 'ALTER_DATABASE'))
    BEGIN
        SET @object_type = 'DATABASE'
    END

    INSERT msdb.dbo.syspolicy_execution_internal
        SELECT p.policy_id , @synchronous, @event_data
        FROM dbo.syspolicy_policies p  -- give me all the policies
        INNER JOIN dbo.syspolicy_conditions_internal c ON c.condition_id = p.condition_id  -- and their conditions
        INNER JOIN dbo.syspolicy_facet_events fe ON c.facet_id = fe.management_facet_id  -- and the facet events that are affected by the condition
        INNER JOIN dbo.syspolicy_target_sets ts ON ts.object_set_id = p.object_set_id AND ts.type = fe.target_type  -- and the target sets in the object set of the policy, with event types that are affected by the condition
        LEFT JOIN dbo.syspolicy_policy_category_subscriptions pgs ON pgs.policy_category_id = p.policy_category_id -- and the policy category subscriptions, if any
        LEFT JOIN dbo.syspolicy_target_set_levels tsl ON tsl.target_set_id = ts.target_set_id AND tsl.level_name = 'Database' -- and the database target set levels associated with any of the target sets, if any
        LEFT JOIN dbo.syspolicy_conditions_internal lc ON lc.condition_id = tsl.condition_id -- and any conditions on the target set level, if any
        LEFT JOIN dbo.syspolicy_policy_categories cat ON p.policy_category_id = cat.policy_category_id -- and the policy categories, if any
        WHERE fe.event_name=@event_type AND -- event type matches the fired event
            p.is_enabled = 1 AND -- policy is enabled
            fe.target_type_alias = @object_type AND -- target type matches the object in the event
            ts.enabled = 1 AND -- target set is enabled
            -- 1 means Enforce, 2 means CheckOnChange
            (p.execution_mode & @mode) = @mode AND -- policy mode matches the requested mode
            ((p.policy_category_id IS NULL) OR (cat.mandate_database_subscriptions = 1) OR ( ts.type_skeleton NOT LIKE 'Server/Database%') OR (@database IS NOT NULL AND pgs.target_object = @database)) AND
            ((@database IS NULL) OR 
             (@database IS NOT NULL AND 
              (tsl.condition_id IS NULL OR 
               (tsl.condition_id IS NOT NULL AND 
                ((lc.is_name_condition=1 AND @database = lc.obj_name) OR
                 (lc.is_name_condition=2 AND @database LIKE lc.obj_name) OR
                 (lc.is_name_condition=3 AND @database != lc.obj_name) OR
                 (lc.is_name_condition=4 AND @database NOT LIKE lc.obj_name))
               )
              )
             )
            ) 

    -- NOTE: if we haven't subscribed via an Endpoint facet on those events 
    -- we know for sure they will not be processed by the ServerAreaFacet policies 
    -- because syspolicy_facet_events expects @target_type to be SERVER
    -- so the filter will leave them out, and we are going to generate a fake 
    -- event to make those policies run
    IF( @synchronous = 0 AND 
        (@event_type IN ('ALTER_ENDPOINT', 'CREATE_ENDPOINT', 'DROP_ENDPOINT')))
    BEGIN
        DECLARE @fake_event_data xml
        SET @fake_event_data = CONVERT(xml, '<EVENT_INSTANCE><EventType>SAC_ENDPOINT_CHANGE</EventType><ObjectType>21075</ObjectType><ObjectName/><DatabaseName>master</DatabaseName></EVENT_INSTANCE>')
        
        EXEC [dbo].[sp_syspolicy_dispatch_event]  @event_data = @fake_event_data, @synchronous = 0
    END
            
END              
GO 

/* 
 * Asynchronous events collection via event notifications
 */

PRINT N'Dropping service [syspolicy_event_listener]...'
GO
IF EXISTS (SELECT * FROM sys.services WHERE name = N'syspolicy_event_listener')
    DROP SERVICE [syspolicy_event_listener]
GO
PRINT N'Dropping queue [syspolicy_event_queue]...'
GO
IF EXISTS (SELECT * FROM sys.service_queues WHERE name = N'syspolicy_event_queue')
    DROP QUEUE [syspolicy_event_queue]
GO
PRINT N'Creating queue [syspolicy_event_queue]...'
GO
CREATE QUEUE [syspolicy_event_queue]
GO

PRINT N'Creating service [syspolicy_event_listener]...'
GO
CREATE SERVICE [syspolicy_event_listener]
 ON QUEUE  [syspolicy_event_queue]
    ([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]);
GO

IF EXISTS (SELECT * FROM sys.procedures WHERE name = N'sp_syspolicy_events_reader')
    DROP PROCEDURE [sp_syspolicy_events_reader] 
GO
PRINT N'Creating procedure [sp_syspolicy_events_reader]...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_events_reader] 
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

	DECLARE @dh uniqueidentifier;
	DECLARE @mt sysname;
	DECLARE @body varbinary(max);
	DECLARE @msg nvarchar(max)

    BEGIN TRANSACTION;
    WAITFOR (RECEIVE TOP (1)
        @dh = conversation_handle,
        @mt = message_type_name,
        @body = message_body
        FROM [syspolicy_event_queue]), timeout 5000;
    WHILE (@dh is not null)
    BEGIN
        IF (@mt = N'http://schemas.microsoft.com/SQL/ServiceBroker/Error')
        BEGIN
            -- @body contains the error
            DECLARE @bodyStr nvarchar(max)
            SET @bodyStr = convert(nvarchar(max), @body)
            RAISERROR (34001, 1,1, @bodyStr) with log;
            END CONVERSATION @dh;
        END
        IF (@mt = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog')
        BEGIN
            RAISERROR (34002, 1,1) with log;
            END CONVERSATION @dh;
        END
        IF (@mt = N'http://schemas.microsoft.com/SQL/Notifications/EventNotification')
        BEGIN
            -- process the event
            BEGIN TRY
                EXEC [dbo].[sp_syspolicy_dispatch_event]  @event_data = @body, @synchronous = 0
            END TRY
            BEGIN CATCH
                -- report the error

                DECLARE @errorNumber int
                DECLARE @errorMessage nvarchar(max)
                SET @errorNumber = ERROR_NUMBER()
                SET @errorMessage = ERROR_MESSAGE()

                RAISERROR (34003, 1,1, @errorNumber, @errorMessage ) with log;
            END CATCH
        END
        -- every message is handled in its own transaction
        COMMIT TRANSACTION;
        SELECT @dh = null;
        BEGIN TRANSACTION;
        WAITFOR (RECEIVE TOP (1)
            @dh = conversation_handle,
            @mt = message_type_name,
            @body = message_body
            FROM [syspolicy_event_queue]), TIMEOUT 5000;
    END
    COMMIT;
END
GO

IF OBJECT_ID('[dbo].[syspolicy_configuration_internal]') IS NULL
BEGIN
    PRINT 'Creating table [dbo].[syspolicy_configuration_internal]...'
    CREATE TABLE [dbo].[syspolicy_configuration_internal] (
        name sysname PRIMARY KEY CLUSTERED NOT NULL,
        current_value sql_variant NOT NULL);
        
    INSERT INTO [dbo].[syspolicy_configuration_internal] (name, current_value)
    VALUES (N'Enabled', 1);
    
    INSERT INTO [dbo].[syspolicy_configuration_internal] (name, current_value)
    VALUES (N'HistoryRetentionInDays', 0);
    
    INSERT INTO [dbo].[syspolicy_configuration_internal] (name, current_value)
    VALUES (N'LogOnSuccess', 0);
END
GO


PRINT 'Creating view [dbo].[syspolicy_configuration] ...'
GO
CREATE VIEW [dbo].[syspolicy_configuration]
AS
    SELECT 
        name,
        CASE WHEN name = N'Enabled' and SERVERPROPERTY('EngineEdition') = 4 THEN 0 ELSE current_value END AS current_value
    FROM [dbo].[syspolicy_configuration_internal] 
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_set_config_history_retention] ...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_set_config_history_retention] 
	@value sql_variant
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

    UPDATE [msdb].[dbo].[syspolicy_configuration_internal]
        SET current_value = @value
        WHERE name = N'HistoryRetentionInDays';
    
END
GO
PRINT 'Creating procedure [dbo].[sp_syspolicy_set_log_on_success] ...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_set_log_on_success] 
	@value sql_variant
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END

    UPDATE [msdb].[dbo].[syspolicy_configuration_internal]
        SET current_value = @value
        WHERE name = N'LogOnSuccess';
    
END
GO
PRINT 'Creating procedure [dbo].[sp_syspolicy_set_config_enabled] ...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_set_config_enabled] 
	@value sql_variant
AS
BEGIN
	DECLARE @retval_check int;
	
	SET NOCOUNT ON
	
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check
	END
    
    DECLARE @val bit;
    SET @val = CONVERT(bit, @value);
    IF (@val = 1)
    BEGIN
	    DECLARE @engine_edition INT
		SELECT @engine_edition = CAST(SERVERPROPERTY('EngineEdition') AS INT)
		IF @engine_edition = 4 -- All SQL Express types + embedded
		BEGIN
			RAISERROR (34054, 16, 1)			
            RETURN 34054
		END
		
	    UPDATE [msdb].[dbo].[syspolicy_configuration_internal]
	    SET current_value = @value
	    WHERE name = N'Enabled';
    
        -- enable policy automation
        ALTER QUEUE [syspolicy_event_queue] WITH ACTIVATION (STATUS = ON)

        EXEC sys.sp_syspolicy_update_ddl_trigger;
        EXEC sys.sp_syspolicy_update_event_notification;
    END
    ELSE
    BEGIN
	    UPDATE [msdb].[dbo].[syspolicy_configuration_internal]
	    SET current_value = @value
	    WHERE name = N'Enabled';

        -- disable policy automation
        ALTER QUEUE [syspolicy_event_queue] WITH ACTIVATION (STATUS = OFF)

        IF EXISTS (SELECT * FROM sys.server_event_notifications WHERE name = N'syspolicy_event_notification')
	        DROP EVENT NOTIFICATION [syspolicy_event_notification] ON SERVER 

        IF EXISTS (SELECT * FROM sys.server_triggers WHERE name = N'syspolicy_server_trigger')
            DISABLE TRIGGER [syspolicy_server_trigger] ON ALL SERVER 
    END

END
GO
PRINT 'Creating procedure [dbo].[sp_syspolicy_configure] ...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_configure]
    @name sysname,
    @value sql_variant
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole'
	IF (0 != @retval_check)
	BEGIN
		RETURN @retval_check
	END

    DECLARE @value_type sysname;
    
    IF (@name=N'Enabled')
    BEGIN
        SET @value_type = CONVERT(sysname, SQL_VARIANT_PROPERTY(@value, 'BaseType'));
        IF (@value_type != 'int')
        BEGIN
            RAISERROR (34021, -1, -1, @name, @value_type);
            RETURN 34021;
        END
       
        EXEC msdb.[dbo].[sp_syspolicy_set_config_enabled] @value;
    END
    ELSE 
    IF (@name = N'HistoryRetentionInDays')
    BEGIN
        SET @value_type = CONVERT(sysname, SQL_VARIANT_PROPERTY(@value, 'BaseType'));
        IF (@value_type != 'int')
        BEGIN
            RAISERROR (34021, -1, -1, @name, @value_type);
            RETURN 34021;
        END
        
        EXEC msdb.[dbo].[sp_syspolicy_set_config_history_retention] @value;
    END
    ELSE
    IF (@name=N'LogOnSuccess')
    BEGIN
        SET @value_type = CONVERT(sysname, SQL_VARIANT_PROPERTY(@value, 'BaseType'));
        IF (@value_type != 'int')
        BEGIN
            RAISERROR (34021, -1, -1, @name, @value_type);
            RETURN 34021;
        END
       
        EXEC msdb.[dbo].[sp_syspolicy_set_log_on_success] @value;
    END
    ELSE 
    BEGIN
        RAISERROR(34020, -1, -1, @name);
        RETURN 34020;
    END
    
    RETURN 0;
END
GO
PRINT 'Creating function [dbo].[fn_syspolicy_is_automation_enabled] ...'
GO
CREATE FUNCTION fn_syspolicy_is_automation_enabled()
RETURNS bit
AS
BEGIN
    DECLARE @ret bit;
    SELECT @ret = CONVERT(bit, current_value)
        FROM msdb.dbo.syspolicy_configuration 
        WHERE name = 'Enabled' 

    RETURN @ret;
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_repair_policy_automation] ...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_repair_policy_automation]
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole';
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check;
	END

    BEGIN TRANSACTION;

    BEGIN TRY
        SET NOCOUNT ON
        
        DECLARE @policies_copy TABLE (
                            policy_id int ,
                            name sysname NOT NULL,
                            condition_id int NOT NULL,
                            root_condition_id int NULL,
                            execution_mode int NOT NULL,
                            policy_category_id int NULL,
                            schedule_uid uniqueidentifier NULL,
                            description nvarchar(max) NOT NULL ,
                            help_text nvarchar(4000) NOT NULL ,
                            help_link nvarchar(2083) NOT NULL ,
                            object_set_id INT NULL,
                            is_enabled bit NOT NULL,
                           	is_system bit NOT NULL);

        INSERT INTO @policies_copy 
            SELECT          policy_id,
                            name, 
                            condition_id, 
                            root_condition_id, 
                            execution_mode, 
                            policy_category_id, 
                            schedule_uid, 
                            description,
                            help_text,
                            help_link,
                            object_set_id,
                            is_enabled,
                            is_system
            FROM msdb.dbo.syspolicy_policies_internal;
        
        DELETE FROM syspolicy_policies_internal;
        
        SET IDENTITY_INSERT msdb.dbo.syspolicy_policies_internal ON
        INSERT INTO msdb.dbo.syspolicy_policies_internal (
                            policy_id,
                            name, 
                            condition_id, 
                            root_condition_id, 
                            execution_mode, 
                            policy_category_id, 
                            schedule_uid, 
                            description,
                            help_text,
                            help_link,
                            object_set_id,
                            is_enabled,
                            is_system)
            SELECT 
                            policy_id,
                            name, 
                            condition_id, 
                            root_condition_id, 
                            execution_mode, 
                            policy_category_id, 
                            schedule_uid, 
                            description,
                            help_text,
                            help_link,
                            object_set_id,
                            is_enabled,
                            is_system
            FROM @policies_copy;
            
        SET IDENTITY_INSERT msdb.dbo.syspolicy_policies_internal OFF;
        
        SET NOCOUNT OFF;
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
        RAISERROR (14351, -1, -1);
        RETURN 1;
    END CATCH

    -- commit the transaction we started
    COMMIT TRANSACTION;
    
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_purge_history] ...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_purge_history] @include_system bit=0
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole';
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check;
	END

    DECLARE @retention_interval_in_days_variant sql_variant
    SET @retention_interval_in_days_variant = (SELECT current_value 
                                        FROM msdb.dbo.syspolicy_configuration
                                        WHERE name = N'HistoryRetentionInDays');
                                        
    DECLARE @retention_interval_in_days int;
    SET @retention_interval_in_days = CONVERT(int, @retention_interval_in_days_variant);
    
    IF( @retention_interval_in_days <= 0)
        RETURN 0;
	
	DECLARE @cutoff_date datetime;
	SET @cutoff_date = DATEADD(day, -@retention_interval_in_days, GETDATE());

    -- delete old policy history records
    BEGIN TRANSACTION
    
    DELETE d 
    FROM msdb.dbo.syspolicy_policy_execution_history_details_internal d
    INNER JOIN msdb.dbo.syspolicy_policy_execution_history_internal h ON d.history_id = h.history_id
    INNER JOIN msdb.dbo.syspolicy_policies p ON h.policy_id = p.policy_id
    WHERE h.end_date < @cutoff_date AND (p.is_system = 0 OR p.is_system = @include_system)
    
    DELETE h
    FROM msdb.dbo.syspolicy_policy_execution_history_internal h
    INNER JOIN msdb.dbo.syspolicy_policies p ON h.policy_id = p.policy_id
    WHERE h.end_date < @cutoff_date AND (p.is_system = 0 OR p.is_system = @include_system)
    
    COMMIT TRANSACTION
    
    -- delete policy subscriptions that refer to the nonexistent databases
    DELETE s
    FROM msdb.dbo.syspolicy_policy_category_subscriptions_internal s
    LEFT OUTER JOIN master.sys.databases d ON s.target_object = d.name
    WHERE s.target_type = 'DATABASE' AND d.database_id IS NULL
    
    RETURN 0;
END
GO
PRINT 'Creating procedure [dbo].[sp_syspolicy_create_purge_job] ...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_create_purge_job]
AS
BEGIN
DECLARE @retval_check int;
EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole';
IF ( 0!= @retval_check)
BEGIN
	RETURN @retval_check;
END

-- create a policy history retention maintenance job
-- first check if this job already exists 
IF EXISTS (SELECT * 
            FROM msdb.dbo.syspolicy_configuration c
            WHERE c.name = 'PurgeHistoryJobGuid')
BEGIN
    RETURN;
END

BEGIN TRANSACTION;
DECLARE @ReturnCode INT;
SELECT @ReturnCode = 0;
DECLARE @job_name sysname;
-- create unique job name
SET @job_name = N'syspolicy_purge_history';
WHILE (EXISTS (SELECT * FROM msdb..sysjobs WHERE name = @job_name))
BEGIN
	SET @job_name = N'syspolicy_purge_history_' + RIGHT(STR(FLOOR(RAND() * 100000000)),8);
END

DECLARE @sa_account_name sysname
SET @sa_account_name = SUSER_Name(0x1)

DECLARE @jobId BINARY(16);
EXEC @ReturnCode =  msdb.dbo.sp_add_job 
        @job_name=@job_name, 
		@enabled=1, 
		@notify_level_eventlog=0, 
		@notify_level_email=0, 
		@notify_level_netsend=0, 
		@notify_level_page=0, 
		@delete_level=0, 
		@owner_login_name=@sa_account_name, 
		@job_id = @jobId OUTPUT;
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;

EXEC @ReturnCode = msdb.dbo.sp_add_jobstep 
        @job_id=@jobId, 
        @step_name=N'Verify that automation is enabled.', 
		@step_id=1, 
		@cmdexec_success_code=0, 
		@on_success_action=3, 
		@on_success_step_id=0, 
		@on_fail_action=1, 
		@on_fail_step_id=0, 
		@retry_attempts=0, 
		@retry_interval=0, 
		@os_run_priority=0, 
		@subsystem=N'TSQL', 
		@command=N'IF (msdb.dbo.fn_syspolicy_is_automation_enabled() != 1)
        BEGIN
            RAISERROR(34022, 16, 1)
        END', 
		@database_name=N'master', 
		@flags=0;
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback

EXEC @ReturnCode = msdb.dbo.sp_add_jobstep 
        @job_id=@jobId, 
        @step_name=N'Purge history.', 
		@step_id=2, 
		@cmdexec_success_code=0, 
		@on_success_action=3, 
		@on_success_step_id=0, 
		@on_fail_action=2, 
		@on_fail_step_id=0, 
		@retry_attempts=0, 
		@retry_interval=0, 
		@os_run_priority=0, 
		@subsystem=N'TSQL', 
		@command=N'EXEC msdb.dbo.sp_syspolicy_purge_history', 
		@database_name=N'master', 
		@flags=0;
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;

DECLARE @command nvarchar(1000);

SET @command = N'if (''$(ESCAPE_SQUOTE(INST))'' -eq ''MSSQLSERVER'') {$a = ''\DEFAULT''} ELSE {$a = ''''};
(Get-Item SQLSERVER:\SQLPolicy\$(ESCAPE_NONE(SRVR))$a).EraseSystemHealthPhantomRecords()'

EXEC @ReturnCode = msdb.dbo.sp_add_jobstep 
        @job_id=@jobId, 
        @step_name=N'Erase Phantom System Health Records.', 
		@step_id=3, 
		@cmdexec_success_code=0, 
		@on_success_action=1, 
		@on_success_step_id=0, 
		@on_fail_action=2, 
		@on_fail_step_id=0, 
		@retry_attempts=0, 
		@retry_interval=0, 
		@os_run_priority=0, 
		@subsystem=N'PowerShell', 
		@command=@command, 
		@database_name=N'master', 
		@flags=0;
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;

EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1;
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;

EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = @@SERVERNAME;
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;

-- run this job every day at 2AM
EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule 
        @job_id=@jobId, 
        @name=N'syspolicy_purge_history_schedule', 
		@enabled=1, 
		@freq_type=4, 
		@freq_interval=1, 
		@freq_subday_type=1, 
		@freq_subday_interval=0, 
		@freq_relative_interval=0, 
		@freq_recurrence_factor=0, 
		@active_start_date=20080101, 
		@active_end_date=99991231, 
		@active_start_time=20000, 
		@active_end_time=235959;
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;

INSERT INTO [msdb].[dbo].[syspolicy_configuration_internal] (name, current_value)
VALUES (N'PurgeHistoryJobGuid', @jobId);

COMMIT TRANSACTION;
RETURN;

QuitWithRollback:
    IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION;
END
GO

PRINT 'Creating procedure [dbo].[sp_syspolicy_purge_health_state] ...'
GO
CREATE PROCEDURE [dbo].[sp_syspolicy_purge_health_state]
    @target_tree_root_with_id nvarchar(400) = NULL
AS
BEGIN
	DECLARE @retval_check int;
	EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole';
	IF ( 0!= @retval_check)
	BEGIN
		RETURN @retval_check;
	END
	
	IF (@target_tree_root_with_id IS NULL)
	BEGIN
	    DELETE FROM msdb.dbo.syspolicy_system_health_state_internal;
	END
	ELSE
	BEGIN
	    DECLARE @target_mask nvarchar(801);
	    SET @target_mask = @target_tree_root_with_id;
	    -- we need to escape all the characters that can be part of the 
	    -- LIKE pattern
	    SET @target_mask = REPLACE(@target_mask, '[', '\[');
	    SET @target_mask = REPLACE(@target_mask, ']', '\]');
	    SET @target_mask = REPLACE(@target_mask, '_', '\_');
	    SET @target_mask = REPLACE(@target_mask, '%', '\%');
	    SET @target_mask = @target_mask + '%';
	    DELETE FROM msdb.dbo.syspolicy_system_health_state_internal
	        WHERE target_query_expression_with_id LIKE @target_mask ESCAPE '\';
	END
	
	RETURN 0;
END
GO

-----------------------------------------------------------
-- Security for policy objects
-----------------------------------------------------------
IF ( NOT EXISTS (SELECT * FROM sys.database_principals 
                    WHERE name = N'PolicyAdministratorRole' AND type = 'R'))
BEGIN
    CREATE ROLE [PolicyAdministratorRole]
END
ELSE -- if the role exists check to see if it has members
BEGIN
    IF NOT EXISTS (SELECT rm.member_principal_id
                FROM sys.database_principals dp 
                INNER JOIN sys.database_role_members rm ON rm.role_principal_id = dp.principal_id
                WHERE name = N'PolicyAdministratorRole' AND type = 'R')
    BEGIN
        -- if the role has no members drop and recreate it
        DROP ROLE [PolicyAdministratorRole]
        CREATE ROLE [PolicyAdministratorRole]
    END
END
GO
-- Policy administrator is also an agent operator
-- because we need to create jobs automatically
EXECUTE sp_addrolemember @rolename = 'SQLAgentOperatorRole' , 
                   @membername = 'PolicyAdministratorRole' 
GO
IF ( NOT EXISTS (SELECT * FROM sys.database_principals 
                    WHERE name = N'ServerGroupAdministratorRole' AND type = 'R'))
BEGIN
    CREATE ROLE [ServerGroupAdministratorRole]
END

GO
IF ( NOT EXISTS (SELECT * FROM sys.database_principals 
                    WHERE name = N'ServerGroupReaderRole' AND type = 'R'))
BEGIN
    CREATE ROLE [ServerGroupReaderRole]
END
GO
EXECUTE sp_addrolemember @rolename = 'ServerGroupReaderRole' , 
                   @membername = 'ServerGroupAdministratorRole' 

GO
CREATE PROCEDURE #provision_table @short_name sysname, @role_name sysname, @grant_public_select bit
AS
BEGIN
    DECLARE @stmt nvarchar(max)
    -- revoke table permissions
    SELECT @stmt = N'REVOKE DELETE, INSERT, REFERENCES, SELECT, UPDATE, ALTER, CONTROL, TAKE OWNERSHIP 
        ON [dbo].' + QUOTENAME(@short_name + N'_internal') + ' FROM [public] CASCADE'
    EXEC sp_executesql @stmt

    -- revoke view permissions
    SELECT @stmt = N'REVOKE ALTER, CONTROL, DELETE, INSERT, REFERENCES, SELECT, TAKE OWNERSHIP, UPDATE
        ON [dbo].' + QUOTENAME(@short_name ) + ' FROM [public] CASCADE'
    EXEC sp_executesql @stmt

    -- grant select on view 
    SELECT @stmt = N'GRANT SELECT ON [dbo].' + QUOTENAME(@short_name)+ ' TO ' + QUOTENAME(@role_name)
    EXEC sp_executesql @stmt

    if (@grant_public_select != 0)
    BEGIN
        SELECT @stmt = N'GRANT SELECT ON [dbo].' + QUOTENAME(@short_name)+ ' TO [public]'
        EXEC sp_executesql @stmt
    END
    
END
GO

-- public role can view all policy metadata
EXEC #provision_table N'syspolicy_conditions', N'PolicyAdministratorRole', 1
EXEC #provision_table N'syspolicy_policies', N'PolicyAdministratorRole', 1
EXEC #provision_table N'syspolicy_policy_categories', N'PolicyAdministratorRole', 1
EXEC #provision_table N'syspolicy_object_sets', N'PolicyAdministratorRole', 1
EXEC #provision_table N'syspolicy_target_sets', N'PolicyAdministratorRole', 1
EXEC #provision_table N'syspolicy_target_set_levels', N'PolicyAdministratorRole', 1
EXEC #provision_table N'syspolicy_policy_category_subscriptions', N'PolicyAdministratorRole', 1
EXEC #provision_table N'syspolicy_system_health_state', N'PolicyAdministratorRole', 1
EXEC #provision_table N'syspolicy_policy_execution_history', N'PolicyAdministratorRole', 1
EXEC #provision_table N'syspolicy_policy_execution_history_details', N'PolicyAdministratorRole', 1
EXEC #provision_table N'syspolicy_configuration', N'PolicyAdministratorRole', 1

-- Registered Server information is limited to the ServerGroupReaderRole, with no public access
EXEC #provision_table N'sysmanagement_shared_registered_servers', N'ServerGroupReaderRole', 0
EXEC #provision_table N'sysmanagement_shared_server_groups', N'ServerGroupReaderRole', 0
GO
DROP PROCEDURE #provision_table

GO
CREATE PROCEDURE #provision_sp @name sysname, @role_name sysname
AS
BEGIN
    DECLARE @stmt nvarchar(max)
    SELECT @stmt = N'REVOKE ALTER, CONTROL, EXECUTE, TAKE OWNERSHIP, VIEW DEFINITION
    ON [dbo].' + QUOTENAME(@name) + ' FROM [public] CASCADE'
    EXEC sp_executesql @stmt

    SELECT @stmt = N'GRANT EXECUTE ON [dbo].' + QUOTENAME(@name)+ ' TO ' + QUOTENAME(@role_name)
    EXEC sp_executesql @stmt
    
END
GO
EXEC #provision_sp N'sp_syspolicy_add_condition', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_update_condition', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_rename_condition', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_delete_condition', N'PolicyAdministratorRole'

EXEC #provision_sp N'sp_syspolicy_add_policy', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_update_policy', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_rename_policy', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_delete_policy', N'PolicyAdministratorRole'

EXEC #provision_sp N'sp_syspolicy_add_target_set', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_update_target_set', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_add_target_set_level', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_update_target_set_level', N'PolicyAdministratorRole'

EXEC #provision_sp N'sp_syspolicy_add_policy_category_subscription', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_update_policy_category_subscription', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_delete_policy_category_subscription', N'PolicyAdministratorRole'

EXEC #provision_sp N'sp_syspolicy_log_policy_execution_start', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_log_policy_execution_end', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_log_policy_execution_detail', N'PolicyAdministratorRole'

EXEC #provision_sp N'sp_syspolicy_add_policy_category', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_rename_policy_category', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_update_policy_category', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_delete_policy_category', N'PolicyAdministratorRole'

EXEC #provision_sp N'sp_syspolicy_add_object_set', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_delete_object_set', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_verify_object_set_identifiers', N'PolicyAdministratorRole'

EXEC #provision_sp N'sp_syspolicy_dispatch_event', N'PolicyAdministratorRole'

EXEC #provision_sp N'sp_syspolicy_configure', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_purge_history', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_repair_policy_automation', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_purge_health_state', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_create_purge_job', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_set_log_on_success', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_set_config_enabled', N'PolicyAdministratorRole'
EXEC #provision_sp N'sp_syspolicy_set_config_history_retention', N'PolicyAdministratorRole'

EXEC #provision_sp N'sp_sysmanagement_update_shared_registered_server', N'ServerGroupAdministratorRole'
EXEC #provision_sp N'sp_sysmanagement_rename_shared_registered_server', N'ServerGroupAdministratorRole'
EXEC #provision_sp N'sp_sysmanagement_update_shared_server_group', N'ServerGroupAdministratorRole'
EXEC #provision_sp N'sp_sysmanagement_rename_shared_server_group', N'ServerGroupAdministratorRole'
EXEC #provision_sp N'sp_sysmanagement_move_shared_registered_server', N'ServerGroupAdministratorRole'
EXEC #provision_sp N'sp_sysmanagement_delete_shared_registered_server', N'ServerGroupAdministratorRole'
EXEC #provision_sp N'sp_sysmanagement_move_shared_server_group', N'ServerGroupAdministratorRole'
EXEC #provision_sp N'sp_sysmanagement_delete_shared_server_group', N'ServerGroupAdministratorRole'
EXEC #provision_sp N'sp_sysmanagement_add_shared_registered_server', N'ServerGroupAdministratorRole'
EXEC #provision_sp N'sp_sysmanagement_add_shared_server_group', N'ServerGroupAdministratorRole'

GO
DROP PROCEDURE #provision_sp
GO
GRANT EXECUTE ON [dbo].[fn_syspolicy_is_automation_enabled] TO PUBLIC
GO 

IF EXISTS (SELECT * from sys.database_principals where name = N'##MS_PolicyEventProcessingLogin##')
    DROP USER [##MS_PolicyEventProcessingLogin##]
GO
use master
GO
IF EXISTS (SELECT * from sys.server_principals WHERE name = '##MS_PolicyEventProcessingLogin##')
BEGIN
    IF EXISTS (SELECT * from sys.server_triggers WHERE name = N'syspolicy_server_trigger')
        DROP TRIGGER [syspolicy_server_trigger] ON ALL SERVER
    DROP LOGIN [##MS_PolicyEventProcessingLogin##]
END
go
-- create event processing login with random password
DECLARE @newid uniqueidentifier
SET @newid = NEWID()
DECLARE @password varchar(255)
SELECT @password = QUOTENAME(CONVERT(varchar(255), @newid), '''')
DBCC TRACEON(4606,-1)
EXECUTE( N'CREATE LOGIN [##MS_PolicyEventProcessingLogin##] WITH PASSWORD=N' + @password + '')
DBCC TRACEOFF(4606,-1)
go

-- create t-sql execution login with random password
DECLARE @newid uniqueidentifier
SET @newid = NEWID()
DECLARE @password varchar(255)
SELECT @password = QUOTENAME(CONVERT(varchar(255), @newid), '''')
IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = '##MS_PolicyTsqlExecutionLogin##')
BEGIN
    DBCC TRACEON(4606,-1)
    EXECUTE( N'CREATE LOGIN [##MS_PolicyTsqlExecutionLogin##] WITH PASSWORD=N' + @password + '')
    DBCC TRACEOFF(4606,-1)

    -- this login is used just for impersonation, no one should be able to connect
    ALTER LOGIN [##MS_PolicyTsqlExecutionLogin##] DISABLE

    GRANT VIEW ANY DEFINITION TO [##MS_PolicyTsqlExecutionLogin##]
    GRANT VIEW SERVER STATE TO [##MS_PolicyTsqlExecutionLogin##]
END
ELSE
BEGIN
    ALTER LOGIN [##MS_PolicyTsqlExecutionLogin##] WITH CHECK_POLICY = OFF;
    EXECUTE( N'ALTER LOGIN [##MS_PolicyTsqlExecutionLogin##] WITH PASSWORD=N' + @password + '')
    ALTER LOGIN [##MS_PolicyTsqlExecutionLogin##] WITH CHECK_POLICY = ON;
END
go

-- this login is used just for impersonation, no one should be able to connect
ALTER LOGIN [##MS_PolicyEventProcessingLogin##] DISABLE

go
USE [msdb]
go
CREATE USER [##MS_PolicyEventProcessingLogin##] FROM LOGIN [##MS_PolicyEventProcessingLogin##]
go
EXEC msdb.sys.sp_addrolemember @rolename = 'PolicyAdministratorRole', @membername = '##MS_PolicyEventProcessingLogin##'
go
GRANT EXECUTE ON [sp_syspolicy_events_reader] TO [##MS_PolicyEventProcessingLogin##]
go

IF NOT EXISTS ( SELECT * FROM sys.database_principals WHERE name = '##MS_PolicyTsqlExecutionLogin##')
BEGIN
    CREATE USER [##MS_PolicyTsqlExecutionLogin##] FROM LOGIN [##MS_PolicyTsqlExecutionLogin##]
    EXEC msdb.sys.sp_addrolemember @rolename = 'PolicyAdministratorRole', @membername = '##MS_PolicyTsqlExecutionLogin##'
END
GO

USE [master]
go
IF EXISTS (SELECT * from sys.database_principals where name = N'##MS_PolicyEventProcessingLogin##')
    DROP USER [##MS_PolicyEventProcessingLogin##]
go
CREATE USER [##MS_PolicyEventProcessingLogin##]
go
GRANT EXECUTE ON [sp_syspolicy_execute_policy] TO [##MS_PolicyEventProcessingLogin##]
go

USE [msdb]
go

PRINT N'Hook up the activation procedure to the queue...'
ALTER QUEUE [syspolicy_event_queue]
    WITH ACTIVATION (
        STATUS = ON,
        MAX_QUEUE_READERS = 1,
        PROCEDURE_NAME = [sp_syspolicy_events_reader],
        EXECUTE AS N'##MS_PolicyEventProcessingLogin##');
GO

-- if this script runs on an existing installation (e.g in an upgrade) we need to recreate 
-- the event notifications and the ddl triggers used for policy automation 
exec sys.sp_syspolicy_update_event_notification
exec sys.sp_syspolicy_update_ddl_trigger
GO


-------------------------------------------------------------------------------
-- End policy objects
-------------------------------------------------------------------------------

USE [msdb]
GO

IF OBJECT_ID('[dbo].[sp_read_settings]', 'P') IS NOT NULL
  DROP PROC [dbo].[sp_read_settings]
GO

CREATE PROCEDURE [dbo].[sp_read_settings]
@name sysname = NULL OUTPUT, 
@setting_id int = NULL OUTPUT
AS
BEGIN
  IF ((@name IS NULL)     AND (@setting_id IS NULL)) OR
     ((@name IS NOT NULL) AND (@setting_id IS NOT NULL))
  BEGIN
    RAISERROR(14524, -1, -1, '@name', '@setting_id')
    RETURN(1) -- Failure
  END

  IF (@setting_id IS NOT NULL)
  BEGIN
    SELECT @name = CASE @setting_id
      WHEN 1 THEN 'ExtendedProtection'
      WHEN 2 THEN 'ForceEncryption'
      WHEN 3 THEN 'AcceptedSPNs'
      ELSE NULL
      END
      
      IF (@name IS NULL) RETURN (2) -- Unknown key
  END
  ELSE
  BEGIN
    IF (@name collate SQL_Latin1_General_CP1_CI_AS) != 'ExtendedProtection'
      AND (@name collate SQL_Latin1_General_CP1_CI_AS) != 'ForceEncryption'
      AND (@name collate SQL_Latin1_General_CP1_CI_AS) != 'AcceptedSPNs'
      RETURN (2) -- Unknown key
  END
  
  DECLARE @hive nvarchar(32), @key nvarchar(256)
  SET @hive=N'HKEY_LOCAL_MACHINE' 
  SET @key=N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\SuperSocketNetLib'
  
  Execute master.sys.xp_instance_regread @hive, @key, @name
  
  RETURN (0)
END
GO

EXEC sp_MS_marksystemobject 'dbo.sp_read_settings'
GO

/**************************************************************/
/*                   BEGIN UTILITY SCRIPTS                    */
/**************************************************************/

-- These settings are necessary for the Utility.
SET ANSI_NULLS ON
SET ANSI_NULL_DFLT_ON ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
SET ARITHABORT ON
SET CONCAT_NULL_YIELDS_NULL ON
SET NUMERIC_ROUNDABORT OFF
SET QUOTED_IDENTIFIER ON
GO

IF (OBJECT_ID ('tempdb..#configuration_original_values') IS NOT NULL)
BEGIN 
    DROP TABLE #configuration_original_values;
END;
GO

-- We call SQL Agent procs that require the Agent XPs config value to be enabled.  Normally, Agent XPs is 
-- only enabled when Agent is running, but there are cases (e.g. upgrade) where this script is executed but 
-- Agent will never be running.  Force the configuration setting on so that we can run even in these cases. 
DECLARE @advanced_options_original_value INT;
DECLARE @configuration_original_value    INT;
DECLARE @configuration_option            VARCHAR(128) = 'Agent XPs';
EXEC #sp_enable_component 
    @comp_name = @configuration_option, 
    @advopt_old_value = @advanced_options_original_value OUTPUT, 
    @comp_old_value = @configuration_original_value OUTPUT; 

-- Save the original value of the config options that we just changed.  We'll restore the original values 
-- when Utility DDL is done. 
SELECT 
   @configuration_option AS configuration_option, 
   @advanced_options_original_value AS advanced_options_original_value, 
   @configuration_original_value AS configuration_original_value 
INTO #configuration_original_values;
GO


-----------------------------------------------------------
-- Security for Utility objects
-----------------------------------------------------------
IF ( NOT EXISTS (SELECT * FROM sys.database_principals 
                    WHERE name = N'UtilityCMRReader' AND type = 'R'))
BEGIN
    CREATE ROLE [UtilityCMRReader]
END
ELSE -- if the role exists check to see if it has members
BEGIN
    IF NOT EXISTS (SELECT rm.member_principal_id
                FROM sys.database_principals dp 
                INNER JOIN sys.database_role_members rm ON rm.role_principal_id = dp.principal_id
                WHERE name = N'UtilityCMRReader' AND type = 'R')
    BEGIN
        -- if the role has no members drop and recreate it
        DROP ROLE [UtilityCMRReader]
        CREATE ROLE [UtilityCMRReader]
    END
END
GO



IF ( NOT EXISTS (SELECT * FROM sys.database_principals 
                    WHERE name = N'UtilityIMRWriter' AND type = 'R'))
BEGIN
    CREATE ROLE [UtilityIMRWriter]
END
ELSE -- if the role exists check to see if it has members
BEGIN
    IF NOT EXISTS (SELECT rm.member_principal_id
                FROM sys.database_principals dp 
                INNER JOIN sys.database_role_members rm ON rm.role_principal_id = dp.principal_id
                WHERE name = N'UtilityIMRWriter' AND type = 'R')
    BEGIN
        -- if the role has no members drop and recreate it
        DROP ROLE [UtilityIMRWriter]
        CREATE ROLE [UtilityIMRWriter]
    END
END
GO



IF ( NOT EXISTS (SELECT * FROM sys.database_principals 
                    WHERE name = N'UtilityIMRReader' AND type = 'R'))
BEGIN
    CREATE ROLE [UtilityIMRReader]
END
ELSE -- if the role exists check to see if it has members
BEGIN
    IF NOT EXISTS (SELECT rm.member_principal_id
                FROM sys.database_principals dp 
                INNER JOIN sys.database_role_members rm ON rm.role_principal_id = dp.principal_id
                WHERE name = N'UtilityIMRReader' AND type = 'R')
    BEGIN
        -- if the role has no members drop and recreate it
        DROP ROLE [UtilityIMRReader]
        CREATE ROLE [UtilityIMRReader]
    END
END
GO

-- A UtilityIMRWriter is also a UtilityIMRReader
EXECUTE sp_addrolemember @rolename = 'UtilityIMRReader' , 
                   @membername = 'UtilityIMRWriter' 
GO


/**************************************************************************/
/* Create the Utility Control Point configuration table                   */
/* This table stores configuration values for the UCP.                    */
/**************************************************************************/

IF(OBJECT_ID(N'[dbo].[sysutility_ucp_configuration_internal]', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table [dbo].[sysutility_ucp_configuration_internal]', 0, 1) WITH NOWAIT;
    CREATE TABLE [dbo].[sysutility_ucp_configuration_internal] (
        name sysname PRIMARY KEY CLUSTERED NOT NULL,
        current_value sql_variant NULL);

    INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value)
    VALUES (N'UtilityName', '');
    
    INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value)
    VALUES (N'MdwDatabaseName', '');
    
    INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value)
    VALUES (N'UtilityDescription', '');
    
    INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value)
    VALUES (N'UtilityDateCreated', '');
    
    INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value)
    VALUES (N'UtilityCreatedBy', '');

    INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value)
    VALUES (N'OverUtilizationTrailingWindow', 1);

    INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value)
    VALUES (N'OverUtilizationOccurenceFrequency', 25);

    INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value)
    VALUES (N'UnderUtilizationTrailingWindow', 168);

    INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value)
    VALUES (N'UnderUtilizationOccurenceFrequency', 90);

    INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value)
    VALUES (N'MdwRetentionLengthInDaysForMinutesHistory', 2);

    INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value)
    VALUES (N'MdwRetentionLengthInDaysForHoursHistory', 31);

    INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value)
    VALUES (N'MdwRetentionLengthInDaysForDaysHistory', 366);

    INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value)
    VALUES (N'UtilityVersion', N'1.0.0.0');
    
END
GO


/**********************************************************************/
/* Create the Utility Control Point configuration view                */
/**********************************************************************/
IF(OBJECT_ID(N'[dbo].[sysutility_ucp_configuration]', 'V') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping view [dbo].[sysutility_ucp_configuration]', 0, 1) WITH NOWAIT;
    DROP VIEW [dbo].[sysutility_ucp_configuration]
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_configuration]...', 0, 1) WITH NOWAIT;
GO
CREATE VIEW [dbo].[sysutility_ucp_configuration]
AS
    SELECT     
        name,
        current_value
    FROM [dbo].[sysutility_ucp_configuration_internal]
GO


/**********************************************************************/
/* Create the utility control point policy configuration view         */
/* This view returns the occurrence frequency and trailing window for */
/* both over and under utilization resource health policy types       */
/**********************************************************************/
IF (OBJECT_ID(N'[dbo].[sysutility_ucp_policy_configuration]', 'V') IS NOT NULL) 
BEGIN
    RAISERROR ('Dropping view [dbo].[sysutility_ucp_policy_configuration]', 0, 1) WITH NOWAIT;
    DROP VIEW dbo.sysutility_ucp_policy_configuration
END	
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_policy_configuration]...', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_policy_configuration AS
(    
    SELECT 1 AS utilization_type
        , CAST(UnderUtilizationOccurenceFrequency AS INT) AS occurence_frequency
        , CAST(UnderUtilizationTrailingWindow AS INT) AS trailing_window
    FROM (SELECT name, current_value FROM msdb.dbo.sysutility_ucp_configuration) config
        PIVOT (MAX(current_value) FOR name IN (UnderUtilizationOccurenceFrequency, UnderUtilizationTrailingWindow)) pvt

    UNION ALL

    SELECT 2 AS utilization_type
        , CAST(OverUtilizationOccurenceFrequency AS INT) AS occurence_frequency
        , CAST(OverUtilizationTrailingWindow AS INT) AS trailing_window
    FROM (SELECT name, current_value FROM msdb.dbo.sysutility_ucp_configuration) config
        PIVOT (MAX(current_value) FOR name IN (OverUtilizationOccurenceFrequency, OverUtilizationTrailingWindow)) pvt
) 
GO


/********************************************************************
Function fn_sysutility_get_is_instance_ucp 
   Description: Returns 1 if the local instance is a UCP, 0 otherwise
********************************************************************/
IF OBJECT_ID(N'[dbo].[fn_sysutility_get_is_instance_ucp]') IS NOT NULL
BEGIN
   RAISERROR('Dropping [dbo].[fn_sysutility_get_is_instance_ucp]', 0, 1)  WITH NOWAIT;
   DROP FUNCTION [dbo].[fn_sysutility_get_is_instance_ucp];
END
GO
RAISERROR('Creating [dbo].[fn_sysutility_get_is_instance_ucp]', 0, 1)  WITH NOWAIT;
GO

CREATE FUNCTION [dbo].[fn_sysutility_get_is_instance_ucp]()
RETURNS BIT 
AS
BEGIN
   RETURN (
      SELECT 
         CASE 
            WHEN ISNULL ((SELECT CAST (current_value as sysname) FROM msdb.dbo.sysutility_ucp_configuration_internal WHERE name = 'UtilityName'), '') = ''
            THEN 0
            ELSE 1
         END)
END;
GO


/********************************************************************
Function fn_sysutility_get_culture_invariant_conversion_style_internal
   Description: Returns an integer that can be passed to CONVERT's "style" parameter 
   in order to round-trip a value of the specified type to or from a string without 
   data loss and in a culture-invariant way.  
********************************************************************/
IF OBJECT_ID('dbo.fn_sysutility_get_culture_invariant_conversion_style_internal') IS NOT NULL
BEGIN
   DROP FUNCTION dbo.fn_sysutility_get_culture_invariant_conversion_style_internal;
END;
GO
CREATE FUNCTION dbo.fn_sysutility_get_culture_invariant_conversion_style_internal (@data_type varchar(30))
RETURNS tinyint
AS 
BEGIN
   RETURN 
      CASE 
         -- ISO8601, e.g. "yyyy-mm-ddThh:mi:ss.mmm"
         WHEN @data_type IN ('datetime', 'datetimeoffset', 'smalldatetime', 'datetime2', 'date', 'time') THEN 126
         -- scientific notation, 16 digits
         WHEN @data_type IN ('real', 'float') THEN 2
         -- e.g. "0x12AB"
         WHEN @data_type IN ('binary', 'varbinary') THEN 1 
         -- all other types including bit, integer types, (n)varchar, decimal/numeric
         ELSE 0 
      END;
END;
GO



/********************************************************************
   Create "stub" objects that stand in for Utility MDW objects. 
   
   We have views in msdb that wrap tables, views, and functions in the MDW database.  The 
   sysutility_mdw database is only created when an instance is made a UCP, so at the time of 
   instmsdb.sql execution sysutility_mdw doesn't yet exist.  This is a problem because views 
   and inline functions in msdb cannot be created unless all of the objects (and columns) 
   that they reference do exist.  The current solution is to create these "stub" objects in 
   msdb, create synonyms pointing to the stub objects, then reference the synonyms within any 
   functions or views in msdb that need to reference MDW tables.  If the instance is made a 
   UCP, the Create UCP process executes the sp_sysutility_ucp_initialize_mdw stored proc, 
   which will redirect the synonyms to reference the true MDW tables after the sysutility_mdw 
   database has been created.  
   
   These stub objects should only be modified when the structure of one of the corresponding 
   MDW objects is modified.  Note that these objects will never hold any data, so there is no 
   reason to bother with build-to-build schema upgrade steps; we can safely drop and recreate 
   them whenever instmsdb.sql is executed.  
********************************************************************/

------ BEGIN UTILITY MDW STUB OBJECTS ------

-- Stub for MDW object [snapshots].[sysutility_ucp_core.latest_dacs]
IF OBJECT_ID(N'[dbo].[sysutility_ucp_dacs_stub]') IS NOT NULL
BEGIN
   RAISERROR('Dropping table [dbo].[sysutility_ucp_dacs_stub]', 0, 1) WITH NOWAIT;
   DROP TABLE [dbo].[sysutility_ucp_dacs_stub];
END;
GO
RAISERROR('Creating table [sysutility_ucp_dacs_stub]', 0, 1)  WITH NOWAIT;
CREATE TABLE [sysutility_ucp_dacs_stub]
(
   [dac_id] INT IDENTITY,   -- todo (VSTS #345036): This column will be removed
   
   [physical_server_name] SYSNAME,
   [server_instance_name] SYSNAME,  -- the server-qualified instance name
   [dac_name] SYSNAME,
   [dac_deploy_date] DATETIME,
   [dac_description] NVARCHAR(4000) NULL,
   [urn] NVARCHAR(4000),
   [powershell_path] NVARCHAR(4000),
   
   [processing_time] DATETIMEOFFSET(7),               
   batch_time DATETIMEOFFSET(7),
   
   -- todo (VSTS #345040) (no measure columns in dimension tables)
   [dac_percent_total_cpu_utilization] REAL 
)
GO

-- Stub for MDW object [snapshots].[sysutility_ucp_core.latest_volumes]

IF OBJECT_ID(N'[dbo].[sysutility_ucp_volumes_stub]') IS NOT NULL
BEGIN
   RAISERROR('Dropping table [dbo].[sysutility_ucp_volumes_stub]', 0, 1) WITH NOWAIT;
   DROP TABLE [dbo].[sysutility_ucp_volumes_stub];
END;
GO
RAISERROR('Creating table [sysutility_ucp_latest_volumes_stub]', 0, 1)  WITH NOWAIT;
CREATE TABLE [sysutility_ucp_volumes_stub]
(
     [ID] INT IDENTITY,    -- todo (VSTS #345036): This column will be removed
   
     virtual_server_name SYSNAME,
     physical_server_name SYSNAME,
   
     volume_device_id SYSNAME,
     volume_name SYSNAME,

     -- todo (VSTS #345040) (no measure columns in dimension tables)
     total_space_available real,
     free_space  real,
     total_space_utilized real,
     percent_total_space_utilization real,
  
     processing_time DATETIMEOFFSET(7),
     batch_time DATETIMEOFFSET(7)
)

-- Stub for MDW object [snapshots].[sysutility_ucp_core.latest_computers]
IF OBJECT_ID(N'[dbo].[sysutility_ucp_computers_stub]') IS NOT NULL
BEGIN
   RAISERROR('Dropping table [dbo].[sysutility_ucp_computers_stub]', 0, 1) WITH NOWAIT;
   DROP TABLE [dbo].[sysutility_ucp_computers_stub];
END;
GO
RAISERROR('Creating table [sysutility_ucp_computers_stub]', 0, 1)  WITH NOWAIT;
CREATE TABLE [sysutility_ucp_computers_stub]
(
   [id] INT IDENTITY,    -- todo (VSTS #345036): This column will be removed 
   
   virtual_server_name SYSNAME,
   physical_server_name SYSNAME,    -- differs from server_name for clustered servers
   is_clustered_server INT,
   
   num_processors INT,
   cpu_name NVARCHAR(128),
   cpu_caption NVARCHAR(128),
   cpu_family NVARCHAR(128),
   cpu_architecture NVARCHAR(64),
   cpu_max_clock_speed DECIMAL(10),
   cpu_clock_speed DECIMAL(10),
   l2_cache_size DECIMAL(10),
   l3_cache_size DECIMAL(10),

   urn NVARCHAR(4000),
   powershell_path NVARCHAR(4000),
      
   processing_time DATETIMEOFFSET(7),
   batch_time DATETIMEOFFSET(7),

   percent_total_cpu_utilization REAL    -- todo (VSTS #345040) (no measure columns in dimension tables)
   
)

-- Stub for MDW object [snapshots].[sysutility_ucp_latest_smo_servers]
IF OBJECT_ID(N'[dbo].[sysutility_ucp_smo_servers_stub]') IS NOT NULL
BEGIN
   RAISERROR('Dropping table [dbo].[sysutility_ucp_smo_servers_stub]', 0, 1) WITH NOWAIT;
   DROP TABLE [dbo].[sysutility_ucp_smo_servers_stub];
END;
GO

RAISERROR ('Creating table [dbo].[sysutility_ucp_smo_servers_stub]', 0, 1) WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_ucp_smo_servers_stub]
(
   [urn] NVARCHAR(512),
   [powershell_path] NVARCHAR(4000),
   [processing_time] DATETIMEOFFSET(7),
   [batch_time] DATETIMEOFFSET(7),
   [AuditLevel] SMALLINT,
   [BackupDirectory] NVARCHAR(260),
   [BrowserServiceAccount] NVARCHAR(128),
   [BrowserStartMode] SMALLINT,
   [BuildClrVersionString] NVARCHAR(20),
   [BuildNumber] INT,
   [Collation] NVARCHAR(128),
   [CollationID] INT,
   [ComparisonStyle] INT,
   [ComputerNamePhysicalNetBIOS] NVARCHAR(128),
   [DefaultFile] NVARCHAR(260),
   [DefaultLog] NVARCHAR(260),
   [Edition] NVARCHAR(64),
   [EngineEdition] SMALLINT,
   [ErrorLogPath] NVARCHAR(260),
   [FilestreamShareName] NVARCHAR(260),
   [InstallDataDirectory] NVARCHAR(260),
   [InstallSharedDirectory] NVARCHAR(260),
   [InstanceName] NVARCHAR(128),
   [IsCaseSensitive] BIT,
   [IsClustered] BIT,
   [IsFullTextInstalled] BIT,
   [IsSingleUser] BIT,
   [Language] NVARCHAR(64),
   [MailProfile] NVARCHAR(128),
   [MasterDBLogPath] NVARCHAR(260),
   [MasterDBPath] NVARCHAR(260),
   [MaxPrecision] TINYINT,
   [Name] NVARCHAR(128),
   [NamedPipesEnabled] BIT,
   [NetName] NVARCHAR(128),
   [NumberOfLogFiles] INT,
   [OSVersion] NVARCHAR(32),
   [PerfMonMode] SMALLINT,
   [PhysicalMemory] INT,
   [Platform] NVARCHAR(32),
   [Processors] SMALLINT,
   [ProcessorUsage] INT,
   [Product] NVARCHAR(48),
   [ProductLevel] NVARCHAR(32),
   [ResourceVersionString] NVARCHAR(32),
   [RootDirectory] NVARCHAR(260),
   [ServerType] SMALLINT,
   [ServiceAccount] NVARCHAR(128),
   [ServiceInstanceId] NVARCHAR(64),
   [ServiceName] NVARCHAR(64),
   [ServiceStartMode] SMALLINT,
   [SqlCharSet] SMALLINT,
   [SqlCharSetName] NVARCHAR(32),
   [SqlDomainGroup] NVARCHAR(260),
   [SqlSortOrder] SMALLINT,
   [SqlSortOrderName] NVARCHAR(64),
   [Status] SMALLINT,
   [TapeLoadWaitTime] INT,
   [TcpEnabled] BIT,
   [VersionMajor] INT,
   [VersionMinor] INT,
   [VersionString] NVARCHAR(32)
);
GO


-- Stub for MDW object [snapshots].[sysutility_ucp_databases]
IF OBJECT_ID(N'[dbo].[sysutility_ucp_databases_stub]') IS NOT NULL
BEGIN
   RAISERROR('Dropping table [dbo].[sysutility_ucp_databases_stub]', 0, 1) WITH NOWAIT;
   DROP TABLE [dbo].[sysutility_ucp_databases_stub];
END;
GO

RAISERROR ('Creating table [dbo].[sysutility_ucp_databases_stub]', 0, 1) WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_ucp_databases_stub]
(
     [urn] NVARCHAR(512)
     , [powershell_path]  NVARCHAR(MAX)
     , [processing_time] DATETIMEOFFSET(7)
     , [batch_time] DATETIMEOFFSET(7)
     , [server_instance_name]    SYSNAME
     , [parent_urn] NVARCHAR(320)
     , [Collation] NVARCHAR(128)
     , [CompatibilityLevel] SMALLINT
     , [CreateDate] DATETIME
     , [EncryptionEnabled] BIT
     , [Name] NVARCHAR(128)
     , [RecoveryModel] SMALLINT
     , [Trustworthy]	BIT
);
GO

-- Stub for MDW object [snapshots].[sysutility_ucp_filegroups]
IF OBJECT_ID(N'[dbo].[sysutility_ucp_filegroups_stub]') IS NOT NULL
BEGIN
   RAISERROR('Dropping table [dbo].[sysutility_ucp_filegroups_stub]', 0, 1) WITH NOWAIT;
   DROP TABLE [dbo].[sysutility_ucp_filegroups_stub];
END;
GO

RAISERROR ('Creating table [dbo].[sysutility_ucp_filegroups_stub]', 0, 1) WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_ucp_filegroups_stub]
(
    [urn] NVARCHAR(780)
    , [powershell_path]  NVARCHAR(MAX)
    , [processing_time] DATETIMEOFFSET(7)
    , [batch_time] DATETIMEOFFSET(7)
    , [server_instance_name]    SYSNAME
    , [database_name] SYSNAME
    , [parent_urn] NVARCHAR(512)
    , [Name] NVARCHAR(128)
);
GO

-- Stub for MDW object [snapshots].[sysutility_ucp_datafiles]
IF OBJECT_ID(N'[dbo].[sysutility_ucp_datafiles_stub]') IS NOT NULL
BEGIN
   RAISERROR('Dropping table [dbo].[sysutility_ucp_datafiles_stub]', 0, 1) WITH NOWAIT;
   DROP TABLE [dbo].[sysutility_ucp_datafiles_stub];
END;
GO

RAISERROR ('Creating table [dbo].[sysutility_ucp_datafiles_stub]', 0, 1) WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_ucp_datafiles_stub]
(
     [urn] NVARCHAR(1500)
     , [powershell_path]  NVARCHAR(MAX)
     , [processing_time] DATETIMEOFFSET(7)
     , [batch_time] DATETIMEOFFSET(7)    
     , [server_instance_name]    SYSNAME
     , [database_name] SYSNAME
     , [filegroup_name] SYSNAME	
     , [parent_urn] NVARCHAR(780)
     , [physical_server_name] SYSNAME
     , [volume_name] SYSNAME  
     , [volume_device_id] SYSNAME
     , [Growth]  REAL
     , [GrowthType] SMALLINT
     , [MaxSize]  REAL
     , [Name] NVARCHAR(128)
     , [Size] REAL
     , [UsedSpace]  REAL
     , [FileName] NVARCHAR(260)
     , [VolumeFreeSpace] BIGINT
     , [available_space] REAL
);
GO


-- Stub for MDW object [snapshots].[sysutility_ucp_logfiles]
IF OBJECT_ID(N'[dbo].[sysutility_ucp_logfiles_stub]') IS NOT NULL
BEGIN
   RAISERROR('Dropping table [dbo].[sysutility_ucp_logfiles_stub]', 0, 1) WITH NOWAIT;
   DROP TABLE [dbo].[sysutility_ucp_logfiles_stub];
END;
GO

RAISERROR ('Creating table [dbo].[sysutility_ucp_logfiles_stub]', 0, 1) WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_ucp_logfiles_stub]
(
     [urn] NVARCHAR(1500)
     , [powershell_path]  NVARCHAR(MAX)
     , [processing_time] DATETIMEOFFSET(7)
     , [batch_time] DATETIMEOFFSET(7)
     , [server_instance_name]    SYSNAME
     , [database_name] SYSNAME
     , [parent_urn] NVARCHAR(780)
     , [physical_server_name] SYSNAME
     , [volume_name] SYSNAME
     , [volume_device_id] SYSNAME
     , [Growth]  REAL
     , [GrowthType] SMALLINT
     , [MaxSize]  REAL
     , [Name] NVARCHAR(128)
     , [Size] REAL
     , [UsedSpace]  REAL
     , [FileName] NVARCHAR(260)
     , [VolumeFreeSpace] BIGINT
     , [available_space] REAL
)
GO


-- Stub for MDW object [snapshots].[sysutility_ucp_cpu_utilization]
IF OBJECT_ID(N'[dbo].[sysutility_ucp_cpu_utilization_stub]') IS NOT NULL
BEGIN
   RAISERROR('Dropping table [dbo].[sysutility_ucp_cpu_utilization_stub]', 0, 1) WITH NOWAIT;
   DROP TABLE [dbo].[sysutility_ucp_cpu_utilization_stub];
END;
GO
RAISERROR ('Creating table [dbo].[sysutility_ucp_cpu_utilization_stub]', 0, 1) WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_ucp_cpu_utilization_stub]
(
       [processing_time] DATETIMEOFFSET(7),
       [aggregation_type] TINYINT NOT NULL,
       [object_type]      TINYINT NOT NULL, 
       
       -- Dimension keys
       [physical_server_name] SYSNAME DEFAULT '',
       [server_instance_name] SYSNAME DEFAULT '',
       [database_name]        SYSNAME DEFAULT '',
      
       -- The actual measure columns.
       percent_total_cpu_utilization REAL

)
GO

-- Stub for MDW object [snapshots].[sysutility_ucp_logfiles]
IF OBJECT_ID(N'[dbo].[sysutility_ucp_space_utilization_stub]') IS NOT NULL
BEGIN
   RAISERROR('Dropping table [dbo].[sysutility_ucp_space_utilization_stub]', 0, 1) WITH NOWAIT;
   DROP TABLE [dbo].[sysutility_ucp_space_utilization_stub];
END;
GO
RAISERROR ('Creating table [dbo].[sysutility_ucp_space_utilization_stub]', 0, 1) WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_ucp_space_utilization_stub]
(
       [processing_time] DATETIMEOFFSET(7) NOT NULL,
       [aggregation_type] TINYINT NOT NULL,
       [object_type]      TINYINT NOT NULL, 
          
       -- The dimension columns
       [virtual_server_name] SYSNAME DEFAULT '',
       [server_instance_name] SYSNAME DEFAULT '',
       [volume_device_id]          SYSNAME DEFAULT '',
       [database_name]        SYSNAME DEFAULT '',
       [filegroup_name]       SYSNAME DEFAULT '',
       [dbfile_name]          SYSNAME DEFAULT '',
      
       [used_space_bytes]      REAL,
       [allocated_space_bytes] REAL,
       [total_space_bytes]     REAL,
       [available_space_bytes] REAL
)
GO

------ END UTILITY MDW STUB OBJECTS ------



/********************************************************************
Procedure [sp_sysutility_ucp_recreate_synonym_internal]
   Description: Helper proc to create/recreate a synonym
********************************************************************/
IF OBJECT_ID(N'[dbo].[sp_sysutility_ucp_recreate_synonym_internal]') IS NOT NULL
BEGIN
   RAISERROR('Dropping [dbo].[sp_sysutility_ucp_recreate_synonym_internal] procedure', 0, 1)  WITH NOWAIT;
   DROP PROCEDURE [dbo].[sp_sysutility_ucp_recreate_synonym_internal];
END
GO

RAISERROR('Creating [dbo].[sp_sysutility_ucp_recreate_synonym_internal] procedure', 0, 1)  WITH NOWAIT;
GO

CREATE PROCEDURE [dbo].[sp_sysutility_ucp_recreate_synonym_internal]
   @synonym_name sysname, @database_name sysname, @schema_name sysname, @object_name sysname
WITH EXECUTE AS CALLER
AS
BEGIN
   DECLARE @null_column nvarchar(100)
   SET @null_column = NULL

   IF (@synonym_name IS NULL OR @synonym_name = N'')
        SET @null_column = '@synonym_name'
   ELSE IF (@object_name IS NULL OR @object_name = N'')
        SET @null_column = '@object_name'

   IF @null_column IS NOT NULL
   BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_recreate_synonym')
        RETURN(1)
   END

   IF  EXISTS (SELECT name FROM sys.objects WHERE object_id = OBJECT_ID(@synonym_name) )
   BEGIN
      RAISERROR('Dropping @synonym_name synonym', 0, 1)  WITH NOWAIT;   

      DECLARE @drop_statement nvarchar(600)
      SET @drop_statement = N'DROP SYNONYM [dbo].' + QUOTENAME(@synonym_name)
      RAISERROR ('Executing: %s', 0, 1, @drop_statement);
      EXEC  sp_executesql @drop_statement
   END

   DECLARE @full_object_name nvarchar(776) = QUOTENAME(@database_name) + '.' + QUOTENAME(@schema_name) + '.' + QUOTENAME(@object_name)
   DECLARE @create_statement nvarchar(1060)
   SET @create_statement = N'CREATE SYNONYM [dbo].' + QUOTENAME(@synonym_name) + ' FOR ' + @full_object_name
   RAISERROR ('Executing: %s', 0, 1, @create_statement);
   EXEC  sp_executesql @create_statement
END
GO


/********************************************************************
Procedure [sp_sysutility_ucp_initialize_mdw]
   Description: This procedure will create the Utility synonyms.  This proc is called 
   by the Utility OM as part of the Create UCP process. It is also called whenever 
   instmsdb.sql is executed. If the instance is a UCP and if sysutility_mdw exists, the 
   synonyms will be created to reference objects in the Utility MDW database.  
   Otherwise, the synonyms will reference stub objects in msdb.  This allows the 
   Utility msdb objects to be created even when sysutility_mdw does not exist. 

   Parameters: 
      @mdw_database_name - Name of the Utility MDW database ('sysutility_mdw')
      @require_mdw - If set to 1, the sysutility_mdw database must exist or an error is 
         thrown.  When this procedure is executed within instmsdb.sql, the param is set 
         to 0 so that it tolerates a missing MDW db.  When the procedure is executed by 
         the Utility OM, this parameter is not passed (and defaults to 1), so the MDW 
         database must exist.  
********************************************************************/
IF OBJECT_ID ('[dbo].[sp_sysutility_ucp_initialize_mdw]') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_initialize_mdw]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE [dbo].[sp_sysutility_ucp_initialize_mdw]
END;
GO

RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_initialize_mdw]', 0, 1) WITH NOWAIT;
GO
 
CREATE PROCEDURE [dbo].[sp_sysutility_ucp_initialize_mdw]
   @mdw_database_name sysname, @require_mdw bit = 1
WITH EXECUTE AS OWNER
AS
BEGIN

   -- Check if @mdw_database_name is NULL or empty
   IF (@mdw_database_name IS NULL OR @mdw_database_name = N'')
   BEGIN
       RAISERROR(14043, -1, -1, 'mdw_database_name', 'sp_sysutility_ucp_initialize_mdw')
       RETURN(1)
   END


   IF (@require_mdw = 1) AND NOT EXISTS (SELECT * FROM master.dbo.sysdatabases WHERE name = @mdw_database_name)
   BEGIN
        RAISERROR(37002, -1, -1, @mdw_database_name)
        RETURN(1)
   END

   DECLARE @database sysname;
   DECLARE @schema sysname;
   DECLARE @is_ucp bit;

   -- If the sysutility_mdw database has been installed and the instance appears to be a UCP, we should 
   -- point the synonyms at the MDW objects.  Otherwise, the synonyms should reference the stub objects 
   -- (with a "_stub" suffix) that we just created.  Note that during UCP creation, this proc is called 
   -- at an interim step before fn_sysutility_get_is_instance_ucp returns 1.  However, @require_mdw will 
   -- be set to 1 in this case, telling us that we should redirect the synonyms to the MDW db even though . 
   -- the instance is not (yet) completely set up as a UCP. 
   IF (DB_ID (@mdw_database_name) IS NOT NULL) 
      AND ((@require_mdw = 1) OR (dbo.fn_sysutility_get_is_instance_ucp() = 1))
   BEGIN
       -- This instance is a UCP; synonyms should reference objects in sysutility_mdw
       SET @database = @mdw_database_name;
       SET @schema = 'sysutility_ucp_core';
      
       -- Dimensions
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_computers', @database, @schema, 'latest_computers';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_volumes', @database, @schema, 'latest_volumes';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_dacs', @database, @schema, 'latest_dacs';	   
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_smo_servers', @database, @schema, 'latest_smo_servers';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_databases', @database, @schema, 'latest_databases';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_filegroups', @database, @schema, 'latest_filegroups';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_datafiles', @database, @schema, 'latest_datafiles';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_logfiles', @database, @schema, 'latest_logfiles';

       -- Measures
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_cpu_utilization', @database, @schema, 'cpu_utilization';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_space_utilization', @database, @schema, 'space_utilization';

      -- Now that msdb is set up, call a setup proc in MDW to do any runtime initialization that is 
      -- needed in that database.  Only exec the proc if it exists -- it won't exist yet when instmsdb.sql 
      -- is run on upgrade from CTP3 to RTM, because the MDW initialization proc was added post-CTP3 and 
      -- upgrade executes instmsdb.sql prior to instmdw.sql.  This proc will be re-executed by the post-upgrade 
      -- script post_upgrade_ucp_cmdw.sql, and at that time the MDW proc will have been created. 
      DECLARE @sql nvarchar(max);
      DECLARE @mdw_proc_name nvarchar(max);
      SET @mdw_proc_name = QUOTENAME(@mdw_database_name) + '.sysutility_ucp_core.sp_initialize_mdw_internal';
      SET @sql = 'EXEC ' + @mdw_proc_name;
      IF OBJECT_ID (@mdw_proc_name) IS NOT NULL 
      BEGIN
         RAISERROR ('Executing %s', 0, 1, @mdw_proc_name) WITH NOWAIT;
         EXEC (@sql);
      END
      ELSE BEGIN
         RAISERROR ('Skipping execution of %s', 0, 1, @mdw_proc_name) WITH NOWAIT;
      END;
                 
   END
   ELSE BEGIN
       -- This instance is not a UCP; synonyms should reference msdb stub objects
       SET @database = 'msdb';
       SET @schema = 'dbo';
               
       -- Dimensions
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_computers', @database, @schema, 'sysutility_ucp_computers_stub';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_volumes', @database, @schema, 'sysutility_ucp_volumes_stub';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_dacs', @database, @schema, 'sysutility_ucp_dacs_stub';
       
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_smo_servers', @database, @schema, 'sysutility_ucp_smo_servers_stub';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_databases', @database, @schema, 'sysutility_ucp_databases_stub';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_filegroups', @database, @schema, 'sysutility_ucp_filegroups_stub';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_datafiles', @database, @schema, 'sysutility_ucp_datafiles_stub';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_logfiles', @database, @schema, 'sysutility_ucp_logfiles_stub';
       
       -- Measures
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_cpu_utilization', @database, @schema, 'sysutility_ucp_cpu_utilization_stub';
       EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_space_utilization', @database, @schema, 'sysutility_ucp_space_utilization_stub';		   
                   
   END;

END
GO


-- Create the Utility synonyms.  For details, see comments prior to "BEGIN UTILITY MDW STUB OBJECTS", 
-- above. If instmsdb is being run as part of mkmaster or for QFE/SP installation on an existing 
-- non-UCP SQL instance, this will redirect the synonyms to the msdb stub objects.  If the script 
-- is being executed on a UCP, the synonyms will be redirected to the sysutility_mdw objects.  
EXEC msdb.dbo.sp_sysutility_ucp_initialize_mdw @mdw_database_name = 'sysutility_mdw', @require_mdw = 0;
GO


---------------------------------------------------------------------
-- SP to setup the managed instance proxy for a network account
---------------------------------------------------------------------
IF OBJECT_ID ('dbo.sp_sysutility_mi_configure_proxy_account') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_configure_proxy_account]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE [dbo].[sp_sysutility_mi_configure_proxy_account]
END;
GO

RAISERROR ('Creating procedure [dbo].[sp_sysutility_mi_configure_proxy_account]', 0, 1) WITH NOWAIT;
GO


CREATE PROCEDURE [dbo].[sp_sysutility_mi_configure_proxy_account]
   @proxy_name sysname,
   @credential_name sysname,
   @network_account sysname,
   @password sysname
AS
BEGIN
   DECLARE @retval INT
   DECLARE @null_column    sysname
   DECLARE @expression NVARCHAR(MAX) = N''
   DECLARE @network_account_sid varbinary(85)
   
   SET @null_column = NULL

   IF (@proxy_name IS NULL OR @proxy_name = N'')
       SET @null_column = '@proxy_name'
   ELSE IF (@credential_name IS NULL OR @credential_name = N'')
       SET @null_column = '@credential_name'
   ELSE IF (@network_account IS NULL OR @network_account = N'')
       SET @null_column = '@network_account'
   ELSE IF (@password IS NULL OR @password = N'')
       SET @null_column = '@password'

   IF @null_column IS NOT NULL
   BEGIN
       RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_mi_configure_proxy_account')
       RETURN(1)
   END

   SET @network_account_sid = SUSER_SID(@network_account, 0) -- case insensensitive lookup
   SET @network_account = SUSER_SNAME(@network_account_sid)  -- get the caseing of the user that the server recognizes
   IF NOT EXISTS (SELECT sid FROM msdb.sys.syslogins WHERE sid = @network_account_sid)
   BEGIN
        SET @expression = N'CREATE LOGIN '+ QUOTENAME(@network_account) +' FROM WINDOWS;'
        EXEC sp_executesql @expression
   END 
   
   DECLARE @create_credential nvarchar(4000)
   DECLARE @print_credential nvarchar(4000)
   
   IF EXISTS(SELECT * FROM master.sys.credentials WHERE name = @credential_name)
   BEGIN
      set @create_credential = 'DROP CREDENTIAL ' + QUOTENAME(@credential_name)
      RAISERROR (@create_credential, 0, 1) WITH NOWAIT;
      EXEC sp_executesql @create_credential
   END


   set @create_credential = 'CREATE CREDENTIAL ' + QUOTENAME(@credential_name) + ' WITH IDENTITY=N' + QUOTENAME(@network_account, '''') + ', SECRET=N' + QUOTENAME(@password, '''')
   set @print_credential = 'CREATE CREDENTIAL ' + QUOTENAME(@credential_name) + ' WITH IDENTITY=N' + QUOTENAME(@network_account, '''')
   RAISERROR (@print_credential, 0, 1) WITH NOWAIT;
   EXEC sp_executesql @create_credential

   
   IF EXISTS(SELECT * FROM dbo.sysproxies WHERE (name = @proxy_name))
   BEGIN
      EXEC dbo.sp_delete_proxy @proxy_name=@proxy_name
   END
   
   EXEC dbo.sp_add_proxy @proxy_name=@proxy_name, @credential_name=@credential_name, @enabled=1

   EXEC dbo.sp_grant_login_to_proxy @msdb_role=N'dc_admin', @proxy_name=@proxy_name

   -- Grant the cmdexec subsystem to the proxy.  This is the subsystem that DC uses to perform upload.
   EXEC dbo.sp_grant_proxy_to_subsystem @proxy_name=@proxy_name, @subsystem_id=3
      
   -- Allow the account to see the table schemas.  This is because DC checks to make sure the mdw
   -- schema matches the schema on the client.   

   -- One cannot grant privledges to oneself.
   -- Since the caller is creating users by virtue of this sproc, it already can view server state
   -- So, only grant veiw server state if the network_account is not the caller
   IF( SUSER_SID() <> @network_account_sid )
   BEGIN       
       -- GRANT VIEW SERVER STATE requires the expression to be executed in master.
       SET @expression = N'use master; GRANT VIEW SERVER STATE TO ' + QUOTENAME(@network_account)
       RAISERROR (@expression, 0, 1) WITH NOWAIT;
       EXEC sp_executesql @expression
   END
       
   -- Add a user to the msdb database so that the proxy can be associated with the appropriate roles.
   
   -- The user might already be associated with a user in msdb.  If so, find that user name so that
   -- roles can be assigned to it.
   DECLARE @user_name SYSNAME = (SELECT name FROM msdb.sys.database_principals WHERE sid = @network_account_sid)
   
   -- The "special principles" are not allowed to have roles added to them.
   -- The database Users in the "special" category are dbo, sys, and INFORMATION_SCHEMA.  
   -- dbo is the only one that can have logins associated with it.
   -- The following only checks dbo because the network_account has an associated login.
   -- The else case (the user is msdb dbo), then they are effectively sysadmin in msdb and have 
   -- the required permissions for the proxy, and there is not need to grant roles anyway.
   IF ((@user_name IS NULL) OR (@user_name <> N'dbo'))
   BEGIN
        
        -- This login doesn't have a user associated with it.
        -- Go ahead and create a user for it in msdb
       IF( @user_name IS NULL )
       BEGIN
          SET @user_name = @network_account
          SET @expression = N'CREATE USER ' + QUOTENAME(@user_name)
          EXEC sp_executesql @expression 
       END; 
       
       -- Allow the user to view the msdb database metadata.  This allows DC (and ssis) to verify
       -- the proxy's privledges.
       -- One cannot grant privledges to oneself.
       IF( SUSER_SID() <> @network_account_sid )
       BEGIN
           SET @expression = N'GRANT VIEW DEFINITION TO ' + QUOTENAME(@network_account)
           RAISERROR (@expression, 0, 1) WITH NOWAIT;
           EXEC sp_executesql @expression
       END

       -- Adding roles is idempotent, so go ahead and add them.
       
       -- This role necessary for the proxy
       EXEC sp_addrolemember @rolename=N'dc_proxy', @membername=@user_name

       -- It needs to read the Utility tables.  It requires execute permissions on the dac performance sp, so writer role is required.
       EXEC sp_addrolemember @rolename=N'UtilityIMRWriter', @membername=@user_name
   END

END
GO


---------------------------------------------------------------------
-- SP to provision the proxy for a network account on the CMR
---------------------------------------------------------------------
IF OBJECT_ID ('dbo.sp_sysutility_ucp_provision_proxy_account') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_provision_proxy_account]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE [dbo].[sp_sysutility_ucp_provision_proxy_account]
END;
GO

RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_provision_proxy_account]', 0, 1) WITH NOWAIT;
GO


CREATE PROCEDURE [dbo].[sp_sysutility_ucp_provision_proxy_account]
   @network_account sysname,
   @mdw_database_name sysname
AS
BEGIN
   DECLARE @retval INT
   DECLARE @null_column    sysname
   DECLARE @expression NVARCHAR(MAX) = N''
   DECLARE @network_account_sid varbinary(85)
    
   SET @null_column = NULL

   IF (@network_account IS NULL OR @network_account = N'')
       SET @null_column = '@network_account'
   ELSE IF (@mdw_database_name IS NULL OR @mdw_database_name = N'')
       SET @null_column = '@mdw_database_name'

   IF @null_column IS NOT NULL
   BEGIN
       RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_provision_proxy_account')
       RETURN(1)
   END

   IF NOT EXISTS (SELECT * FROM master.dbo.sysdatabases WHERE name = @mdw_database_name)
   BEGIN
        RAISERROR(37002, -1, -1, @mdw_database_name)
        RETURN(1)
   END

   SET @network_account_sid = SUSER_SID(@network_account, 0) -- case insensensitive lookup
   SET @network_account = SUSER_SNAME(@network_account_sid)  -- get the caseing of the user that the server recognizes
   IF NOT EXISTS (SELECT sid FROM msdb.sys.syslogins WHERE sid = @network_account_sid)
   BEGIN
        SET @expression = N'USE msdb; CREATE LOGIN '+ QUOTENAME(@network_account) + ' FROM WINDOWS;'
        EXEC sp_executesql @expression
   END 

   DECLARE @is_sysadmin INT
   SELECT @is_sysadmin = 0
   EXECUTE msdb.dbo.sp_sqlagent_has_server_access @login_name = @network_account, @is_sysadmin_member = @is_sysadmin OUTPUT

   IF (@is_sysadmin = 0)
   BEGIN
      DECLARE @print_expression nvarchar(500)
      SET @print_expression = @network_account + ' is NOT a SQL sysadmin'
      RAISERROR (@print_expression, 0, 1) WITH NOWAIT;

      IF NOT EXISTS(SELECT * FROM msdb.sys.database_principals WHERE sid = @network_account_sid)
      BEGIN
         SET @expression = N'USE msdb; CREATE USER ' + QUOTENAME(@network_account) +';'
         EXEC sp_executesql @expression    
      END;

      EXEC msdb.dbo.sp_addrolemember @rolename='dc_proxy', @membername=@network_account

      DECLARE @grant_expression nvarchar(4000)

      IF NOT EXISTS
      (SELECT name from master.sys.databases
       WHERE @network_account_sid = owner_sid
       AND database_id = DB_ID(@mdw_database_name))
      BEGIN
         set @grant_expression =
         'IF NOT EXISTS(SELECT * FROM ' + QUOTENAME(@mdw_database_name) +'.[sys].[database_principals] WHERE sid = SUSER_SID(' + QUOTENAME(@network_account, '''') +', 0))

         BEGIN
            RAISERROR (''Creating user ' + QUOTENAME(@network_account) + ' in ' + QUOTENAME(@mdw_database_name) + ''', 0, 1) WITH NOWAIT;
            USE ' + QUOTENAME(@mdw_database_name) + '; CREATE USER ' + QUOTENAME(@network_account) + ';
         END;

         RAISERROR (''Add to UtilityMDWWriter role'', 0, 1) WITH NOWAIT;
         EXEC ' + QUOTENAME(@mdw_database_name) + '.[dbo].[sp_addrolemember] @rolename=''UtilityMDWWriter'', @membername=' + QUOTENAME(@network_account) + ';'

         RAISERROR (@grant_expression, 0, 1) WITH NOWAIT;
         EXEC sp_executesql @grant_expression
      END

   END

END
GO



----------------------------------------------------------------------
-- SP to create the proxy account validator job
----------------------------------------------------------------------
IF OBJECT_ID ('dbo.sp_sysutility_mi_validate_proxy_account') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_validate_proxy_account]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE [dbo].[sp_sysutility_mi_validate_proxy_account]
END;
GO

RAISERROR ('Creating procedure [dbo].[sp_sysutility_mi_validate_proxy_account]', 0, 1) WITH NOWAIT;
GO


CREATE PROCEDURE [dbo].[sp_sysutility_mi_validate_proxy_account]
   @proxy_name sysname,
   @credential_name sysname,
   @network_account sysname,
   @password sysname
AS
BEGIN
   DECLARE @retval INT
   DECLARE @null_column    sysname
    
   SET @null_column = NULL

   IF (@proxy_name IS NULL OR @proxy_name = N'')
       SET @null_column = '@proxy_name'
   ELSE IF (@credential_name IS NULL OR @credential_name = N'')
       SET @null_column = '@credential_name'
   ELSE IF (@network_account IS NULL OR @network_account = N'')
       SET @null_column = '@network_account'
   ELSE IF (@password IS NULL OR @password = N'')
       SET @null_column = '@password'

   IF @null_column IS NOT NULL
   BEGIN
       RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_mi_validate_proxy_account')
       RETURN(1)
   END


   DECLARE @instance_name  nvarchar(128)
   SET @instance_name = ISNULL(CONVERT(nvarchar(128), SERVERPROPERTY('InstanceName')), N'MSSQLSERVER')

   DECLARE @job_name sysname
   DECLARE @job_id uniqueidentifier        
   DECLARE @description nvarchar(512)

   -- Delete the job if it already exists
   SET @job_name = N'sysutility_check_proxy_credentials'
   WHILE (EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @job_name))
   BEGIN
      EXEC sp_delete_job @job_name=@job_name
   END



   DECLARE @credential_statement nvarchar(4000)
   DECLARE @print_credential nvarchar(4000)
   
   IF EXISTS(select * from master.sys.credentials where name = @credential_name)
   BEGIN
      set @credential_statement = 'DROP CREDENTIAL ' + QUOTENAME(@credential_name)
      RAISERROR (@credential_statement, 0, 1) WITH NOWAIT;
      EXEC sp_executesql @credential_statement
   END


   set @credential_statement = 'CREATE CREDENTIAL ' + QUOTENAME(@credential_name) + ' WITH IDENTITY=N' + QUOTENAME(@network_account, '''') + ', SECRET=N' + QUOTENAME(@password, '''')
   set @print_credential = 'CREATE CREDENTIAL ' + QUOTENAME(@credential_name) + ' WITH IDENTITY=N' + QUOTENAME(@network_account, '''')
   RAISERROR (@print_credential, 0, 1) WITH NOWAIT;
   EXEC sp_executesql @credential_statement

   
   IF EXISTS(SELECT * FROM dbo.sysproxies WHERE (name = @proxy_name))
   BEGIN
      EXEC dbo.sp_delete_proxy @proxy_name=@proxy_name
   END
   
   
   -- Create the proxy and grant it to the cmdExec subsytem
   EXEC dbo.sp_add_proxy @proxy_name=@proxy_name, @credential_name=@credential_name, @enabled=1

   EXEC dbo.sp_grant_proxy_to_subsystem @proxy_name=@proxy_name, @subsystem_id=3


   -- Create the job
	EXEC  msdb.dbo.sp_add_job @job_name=@job_name, 
			@enabled=1,
			@notify_level_eventlog=0, 
			@notify_level_email=2, 
			@notify_level_netsend=2, 
			@notify_level_page=2, 
			@delete_level=0, 
			@category_id=0,
			@job_id = @job_id OUTPUT

	DECLARE @server_name SYSNAME = CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))
	EXEC msdb.dbo.sp_add_jobserver @job_name=@job_name, @server_name = @server_name

   DECLARE @collection_step_command nvarchar(512)
   SET @collection_step_command = N'time /T'

   EXEC msdb.dbo.sp_add_jobstep 
          @job_id=@job_id, 
          @step_name=N'Validate proxy account', 
		    @step_id=1, 
		    @cmdexec_success_code=0, 
		    @on_fail_action=2, 
		    @on_fail_step_id=0,
		    @on_success_action=1, 
		    @retry_attempts=0, 
		    @retry_interval=0, 
		    @os_run_priority=0, 
		    @subsystem=N'CMDEXEC',
		    @command=@collection_step_command,
		    @proxy_name=@proxy_name,
		    @flags=16

END
GO

----------------------------------------------------------------------
-- A function that returns a powershell script which is used for 
-- ensuring that the WMI queries necessary for data collection in the
-- Utility work correctly.  The function is used for validation of the
-- Managed Instance and is run within a job step.  It is exposed as a 
-- to ease testing and troublshooting.
----------------------------------------------------------------------
IF  (OBJECT_ID(N'[dbo].[fn_sysutility_mi_get_validate_wmi_script]') IS NOT NULL)
BEGIN
   RAISERROR('Dropping [dbo].[fn_sysutility_mi_get_validate_wmi_script] function', 0, 1)  WITH NOWAIT;
   DROP FUNCTION [dbo].[fn_sysutility_mi_get_validate_wmi_script];
END
GO
RAISERROR('Creating [dbo].[fn_sysutility_mi_get_validate_wmi_script] function', 0, 1)  WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysutility_mi_get_validate_wmi_script]()
   RETURNS NVARCHAR(MAX)
AS
BEGIN     
   RETURN 
'# This script verifies that the following WMI objects are queriable
$objectsToValidate = "Win32_MountPoint", 
                "Win32_PerfRawData_PerfProc_Process", 
                "Win32_PerfRawData_PerfOS_Processor",
                "Win32_Processor",
                "Win32_Volume",
                "Win32_LogicalDisk"
                
# The errorHappend variable keeps track of whether any class failed the check
$errorHappened=$false

# The SQL Agent Powershell subsystem does not have an interactive host associated
# with it, thus standard Write-Host and other host-based cmdlets have no place
# to write to.  This knowledge is used to tell if the script is in an Agent
# or if it is running on a standard PowerShell host.
$isNotConsole = ($host.Name -ne "ConsoleHost")
function Get-IsAgentStep
{
   $global:isNotConsole
}

# Writing to the agent logs is easiest to achieve with [Console]::Error.WriteLine
# If the script is in Agent, write through the Console directly.  If the script
# is not in Agent (someone is using it to debug), then just output to the pipeline.
function Write-AgentLog($object)
{
     if(Get-IsAgentStep)
     {
        [Console]::Error.WriteLine($object)
     } 
     else 
     {
        $object
     }
}

# Query the given WMI object and report pass or fail on the object.
function Validate-WmiObject([string] $wmiObject)
{
   process
   {
      Write-AgentLog "#Running Command:"
      Write-AgentLog "Get-WmiObject $wmiObject | Out-Null"
      
      # Use ErrorVariable and ErrorAction SilentlyContinue so that all of the 
      # objects can be tested without stopping the script or having spurrious messages
      # in the Agent logs.  
      Get-WmiObject $wmiObject -ErrorVariable wmiError -ErrorAction SilentlyContinue | Out-Null
      
      # Check the error message and report pass or fail
      if($wmiError)
      {
         $global:errorHappened=$true
         Write-AgentLog "#Command FAILED. Exception : $wmiError"
      }
      else
      {
         Write-AgentLog "#Command PASSED."
      }

   }
}

# Validate all of the Wmi objects.  If any one of them fail, then
# report an error.
function Validate-AllWmiObjects
{
   $objectsToValidate | %{ 
      Validate-WmiObject $_ 
   }
   
   if($global:errorHappened)
   {
      Write-Error -ErrorAction Stop "One or more WMI classes failed the test"
   }
}

# Automatically check the status of the objects if the script is running in Agent
# Otherwise, allow the user to call the Validate functions interactively.
if(Get-IsAgentStep)
{ 
   Validate-AllWmiObjects
}
'
END
GO

----------------------------------------------------------------------
-- A sp that creates a job that ensures that WMI works appropreately for
-- Utility data collection.  
----------------------------------------------------------------------
IF OBJECT_ID ('dbo.sp_sysutility_mi_create_job_validate_wmi') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_create_job_validate_wmi]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE [dbo].[sp_sysutility_mi_create_job_validate_wmi]
END;
GO

RAISERROR ('Creating procedure [dbo].[sp_sysutility_mi_create_job_validate_wmi]', 0, 1) WITH NOWAIT;
GO


CREATE PROCEDURE [dbo].[sp_sysutility_mi_create_job_validate_wmi]
AS
BEGIN

   DECLARE @job_name sysname = N'sysutility_mi_validate_wmi'
   DECLARE @job_id uniqueidentifier
   DECLARE @description nvarchar(512) = N''
   DECLARE @psScript NVARCHAR(MAX) = (SELECT [dbo].[fn_sysutility_mi_get_validate_wmi_script]());

   -- Delete the job if it already exists
   WHILE (EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @job_name))
   BEGIN
      EXEC sp_delete_job @job_name=@job_name
   END

   -- Create the job
   EXEC  msdb.dbo.sp_add_job @job_name=@job_name, 
			@enabled=1,
			@notify_level_eventlog=0, 
			@notify_level_email=2, 
			@notify_level_netsend=2, 
			@notify_level_page=2, 
			@delete_level=0, 
			@category_id=0,
			@job_id = @job_id OUTPUT

   EXEC msdb.dbo.sp_add_jobserver @job_name=@job_name, @server_name = @@SERVERNAME

   -- Add the validation step
   EXEC msdb.dbo.sp_add_jobstep 
          @job_id=@job_id, 
          @step_name=N'Validate WMI configuration', 
          @step_id=1, 
          @cmdexec_success_code=0, 
          @on_fail_action=2, 
          @on_fail_step_id=0,
          @on_success_action=1, 
          @retry_attempts=0, 
          @retry_interval=0, 
          @os_run_priority=0, 
          @subsystem=N'Powershell',
          @command=@psScript

END
GO


------------------------------------------------------------------------
-- SP to create the job which creates the UtilityCacheDirectory
------------------------------------------------------------------------
IF OBJECT_ID ('dbo.sp_sysutility_mi_create_cache_directory') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_create_cache_directory]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE [dbo].[sp_sysutility_mi_create_cache_directory]
END;
GO

RAISERROR ('Creating procedure [dbo].[sp_sysutility_mi_create_cache_directory]', 0, 1) WITH NOWAIT;
GO

CREATE PROCEDURE [dbo].[sp_sysutility_mi_create_cache_directory]
   @network_account sysname,
   @agent_service_account sysname
AS
BEGIN
    DECLARE @instance_name  nvarchar(128)
    DECLARE @null_column    sysname
    
    SET @null_column = NULL
    
    IF (@network_account IS NULL OR @network_account = N'')
       SET @null_column = '@network_account'
    ELSE IF (@agent_service_account IS NULL OR @agent_service_account = N'')
       SET @null_column = '@agent_service_account'
       
    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_create_cache_directory_job')
        RETURN(1)
    END


    SET @instance_name = ISNULL(CONVERT(nvarchar(128), SERVERPROPERTY('InstanceName')), N'MSSQLSERVER')
 

    DECLARE @job_name sysname
    DECLARE @job_id uniqueidentifier        
    DECLARE @description nvarchar(512)

    DECLARE @cachepath nvarchar(2048);  -- SQL Eye reports that xp_instance_regread can truncate the cachepath
									    -- but xp_instance_regread doesn't handle LOB types to use nvarchar(MAX)

    EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'WorkingDirectory', @cachepath OUTPUT;

    set @cachepath=@cachepath + '\UtilityDC'

    RAISERROR (@cachepath, 0, 1) WITH NOWAIT;

    -- create unique job name
    SET @job_name = N'sysutility_create_cache_directory'

    WHILE (EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @job_name))
    BEGIN
       EXEC sp_delete_job @job_name=@job_name
    END
 

    EXEC  msdb.dbo.sp_add_job @job_name=@job_name, 
                @enabled=1,
                @notify_level_eventlog=0, 
                @notify_level_email=2, 
                @notify_level_netsend=2, 
                @notify_level_page=2, 
                @delete_level=0, 
                @category_id=0,
                @job_id = @job_id OUTPUT

    DECLARE @server_name SYSNAME = CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))      
    EXEC msdb.dbo.sp_add_jobserver @job_name=@job_name, @server_name = @server_name

    DECLARE @command nvarchar(MAX)
    SET @command = N'if exist "' + @cachepath + '"  rmdir /S /Q "' + @cachepath + '"'

    EXEC msdb.dbo.sp_add_jobstep 
          @job_id=@job_id, 
                @step_name=N'Delete existing cache directory', 
                @step_id=1, 
                @cmdexec_success_code=0, 
                @on_fail_action=2, 
                @on_fail_step_id=0,
                @on_success_action=3, 
                @retry_attempts=0, 
                @retry_interval=0, 
                @os_run_priority=0, 
                @subsystem=N'CMDEXEC',
                @command=@command, 
                @flags=16
 
    IF NOT (@network_account LIKE @agent_service_account)
    BEGIN
        -- If network_account (proxy) and agent_service_account are different, we shall ACL the cache
        RAISERROR ('network_account is different from agent_service_account', 0, 1) WITH NOWAIT;
        SET @command = N'md "' + @cachepath + '"'

        EXEC msdb.dbo.sp_add_jobstep 
                @job_id=@job_id, 
                @step_name=N'Create cache directory', 
                @step_id=2, 
                @cmdexec_success_code=0, 
                @on_fail_action=2, 
                @on_fail_step_id=0,
                @on_success_action=3, 
                @retry_attempts=0, 
                @retry_interval=0, 
                @os_run_priority=0, 
                @subsystem=N'CMDEXEC',
                @command=@command,
                @flags=16

        SET @command = N'cacls "' + @cachepath + '" /E /G ' + @network_account + ':C'

        EXEC msdb.dbo.sp_add_jobstep 
                @job_id=@job_id, 
                @step_name=N'ACL cache directory', 
                @step_id=3, 
                @cmdexec_success_code=0, 
                @on_fail_action=2, 
                @on_fail_step_id=0, 
                @on_success_action=1,
                @retry_attempts=0, 
                @retry_interval=0, 
                @os_run_priority=0, 
                @subsystem=N'CMDEXEC',
                @command=@command, 
                @flags=16
    END
    ELSE
    BEGIN
        -- If network_account (proxy) and agent_service_account are the same, then there is no need to ACL the cache
        -- as the account already has write access to it courtesy the agent service account provisioning.
        -- In this case explicit provisioning of cache with the account leads to error.
        RAISERROR ('network_account is the same as the agent_service_account', 0, 1) WITH NOWAIT;
        SET @command = N'md "' + @cachepath + '"'

        EXEC msdb.dbo.sp_add_jobstep 
                @job_id=@job_id, 
                @step_name=N'Create cache directory', 
                @step_id=2, 
                @cmdexec_success_code=0, 
                @on_fail_action=2, 
                @on_fail_step_id=0,
                @on_success_action=1, 
                @retry_attempts=0, 
                @retry_interval=0, 
                @os_run_priority=0, 
                @subsystem=N'CMDEXEC',
                @command=@command,
                @flags=16

    END

END
GO

---------------------------------------------------------------------
-- Table to hold information about a managed instance and its
-- configuration
---------------------------------------------------------------------

IF NOT EXISTS (SELECT name FROM [sys].[objects] WHERE object_id = OBJECT_ID(N'[dbo].[sysutility_mi_configuration_internal]') AND type in (N'U'))
BEGIN
RAISERROR('Creating [dbo].[sysutility_mi_configuration_internal] table', 0, 1)  WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_mi_configuration_internal]
(
   -- configuration_id + pk enforces a single row in the table
   configuration_id              AS (1), 
   
   ucp_instance_name             SYSNAME NULL, 
   mdw_database_name             SYSNAME NULL,
   
   CONSTRAINT pk_sysutility_mi_configuration_internal_configuration_id PRIMARY KEY (configuration_id)
   -- this table should only contain one row
);
END
GO

IF (NOT OBJECT_ID('[dbo].[sysutility_mi_configuration]', 'V') IS NULL)
BEGIN
    RAISERROR('Dropping [dbo].[sysutility_mi_configuration] view', 0, 1)  WITH NOWAIT;
    DROP VIEW [dbo].[sysutility_mi_configuration]
END
GO

RAISERROR('Creating [dbo].[sysutility_mi_configuration] view', 0, 1)  WITH NOWAIT;
GO
CREATE VIEW [dbo].[sysutility_mi_configuration]
AS
    SELECT config.ucp_instance_name, config.mdw_database_name, t.upload_schema_version
    FROM 
    -- The upload_schema_version represents the contract between the UCP and MI for data upload
    -- Change this value when a breaking change with a (downlevel) UCP may be introduced in the MI
    -- upload code.
    (SELECT 100 AS upload_schema_version) t
    LEFT OUTER JOIN
    [dbo].[sysutility_mi_configuration_internal] config
    ON 1=1
GO

----------------------------------------------------------
-- Stored PROCs to add/removed/check the UCP configuration
----------------------------------------------------------
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_sysutility_mi_add_ucp_registration]') AND type in (N'P', N'PC'))
BEGIN
   RAISERROR('Dropping [dbo].[sp_sysutility_mi_add_ucp_registration] procedure', 0, 1)  WITH NOWAIT;
   DROP PROCEDURE [dbo].[sp_sysutility_mi_add_ucp_registration];
END
GO
RAISERROR('Creating [dbo].[sp_sysutility_mi_add_ucp_registration] procedure', 0, 1)  WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysutility_mi_add_ucp_registration]
    @ucp_instance_name SYSNAME,
    @mdw_database_name SYSNAME
WITH EXECUTE AS OWNER
AS
BEGIN
   
   DECLARE @null_column SYSNAME = NULL
   SET NOCOUNT ON;
   SET XACT_ABORT ON;

   IF (@ucp_instance_name IS NULL)
     SET @null_column = '@ucp_instance_name'
   ELSE IF (@mdw_database_name IS NULL)
     SET @null_column = '@mdw_database_name'

   IF @null_column IS NOT NULL
   BEGIN
     RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_mi_add_ucp_registration')
     RETURN(1)
   END
    
   BEGIN TRANSACTION;
    
     IF EXISTS (SELECT * FROM [msdb].[dbo].[sysutility_mi_configuration_internal])
    BEGIN
      UPDATE [msdb].[dbo].[sysutility_mi_configuration_internal]
      SET
         ucp_instance_name          = @ucp_instance_name,
         mdw_database_name          = @mdw_database_name
    END
    ELSE
    BEGIN
         INSERT INTO [msdb].[dbo].[sysutility_mi_configuration_internal] (ucp_instance_name, mdw_database_name)
         VALUES (@ucp_instance_name, @mdw_database_name);
    END          
    
   COMMIT TRANSACTION;

   ---- Add the MiUcpName registry key values.
   ---- If the value is already present this XP will update them.
   EXEC master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                        N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility',
                                        N'MiUcpName',
                                        N'REG_SZ',
                                        @ucp_instance_name
                                         
END
GO


IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_sysutility_mi_remove_ucp_registration]') AND type in (N'P', N'PC'))
BEGIN
   RAISERROR('Dropping [dbo].[sp_sysutility_mi_remove_ucp_registration] procedure', 0, 1)  WITH NOWAIT;
   DROP PROCEDURE [dbo].[sp_sysutility_mi_remove_ucp_registration];
END
GO
RAISERROR('Creating [dbo].[sp_sysutility_mi_remove_ucp_registration] procedure', 0, 1)  WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysutility_mi_remove_ucp_registration]
WITH EXECUTE AS OWNER
AS
BEGIN
   SET NOCOUNT ON;
   SET XACT_ABORT ON;
 
   BEGIN TRANSACTION;
    
    IF EXISTS (SELECT * FROM [msdb].[dbo].[sysutility_mi_configuration_internal])
    BEGIN
      UPDATE [msdb].[dbo].[sysutility_mi_configuration_internal]
      SET
         ucp_instance_name          = NULL,
         mdw_database_name          = NULL
    END
    ELSE
    BEGIN
         INSERT INTO [msdb].[dbo].[sysutility_mi_configuration_internal] (ucp_instance_name, mdw_database_name)
         VALUES (NULL, NULL);
    END     

   COMMIT TRANSACTION;

   ---- If the above part fails it will not execute the following XPs.
   ---- The following XP calls are not transactional, so they are put outside
   ---- the transaction.
   ---- Remove the MiUcpName registry key if it is present
   DECLARE @mi_ucp_name nvarchar(1024)
   EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                       N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility',
                                       N'MiUcpName',
                                       @mi_ucp_name OUTPUT

   IF @mi_ucp_name IS NOT NULL
   BEGIN
       EXEC master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE',
                                                  N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility',
                                                  N'MiUcpName'
   END

   ---- Remove the registry key if this instance is NOT a UCP.
   ---- If this instance is a UCP we cannot remove the key entirely as
   ---- the version number is still stored under the key.
   IF (msdb.dbo.fn_sysutility_get_is_instance_ucp() = 0)
   BEGIN
       EXEC master.dbo.xp_instance_regdeletekey N'HKEY_LOCAL_MACHINE',
                                                  N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility'
   END
   
END
GO


IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fn_sysutility_ucp_get_instance_is_mi]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
BEGIN
   RAISERROR('Dropping [dbo].[fn_sysutility_ucp_get_instance_is_mi] function', 0, 1)  WITH NOWAIT;
   DROP FUNCTION [dbo].[fn_sysutility_ucp_get_instance_is_mi];
END
GO
RAISERROR('Creating [dbo].[fn_sysutility_ucp_get_instance_is_mi] function', 0, 1)  WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysutility_ucp_get_instance_is_mi]()
RETURNS BIT
WITH EXECUTE AS OWNER
AS
BEGIN
    DECLARE @status BIT = (SELECT 
                            CASE  
                            WHEN ((ucp_instance_name IS NOT NULL) AND (mdw_database_name IS NOT NULL)) THEN CAST(1 AS BIT)
                            ELSE CAST(0 AS BIT)
                            END
                           FROM sysutility_mi_configuration_internal config)
    RETURN ISNULL(@status,0)
END	
GO



----------------------------------------------------------------------------------
-- Table sysutility_mi_dac_execution_statistics_internal
--    Staging table used to store execution statistics for DACs on the local instance.  This table stores 
--    relatively transient data; in the worst case, dropping and recreating it will result in the loss of 
--    up to one upload interval (15 minutes) of DAC execution statistics.  The data in this table is created 
--    and updated by sp_sysutility_mi_collect_dac_execution_statistics_internal, which runs every 15 seconds 
--    on each MI.  The data is harvested every 15 min by sp_sysutility_mi_get_dac_execution_statistics_internal, 
--    then moved to the UCP's CMDW database by the Utility collection set. 
----------------------------------------------------------------------------------
IF OBJECT_ID ('dbo.sysutility_mi_dac_execution_statistics_internal', 'U') IS NOT NULL
BEGIN
   RAISERROR('Dropping [dbo].[sysutility_mi_dac_execution_statistics_internal] table', 0, 1)  WITH NOWAIT;
   DROP TABLE [dbo].[sysutility_mi_dac_execution_statistics_internal];
END
GO
RAISERROR('Creating [dbo].[sysutility_mi_dac_execution_statistics_internal] table', 0, 1)  WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_mi_dac_execution_statistics_internal]
(
   dac_instance_name sysname NOT NULL,
   database_name sysname UNIQUE NOT NULL,     -- database name, from sysdac_instances
   database_id int UNIQUE NOT NULL,           -- database ID, from sysdac_instances
   date_created datetime NULL,                -- DAC creation date, from sysdac_instances
   [description] nvarchar(4000) NULL,         -- DAC description, from sysdac_instances
   first_collection_time datetimeoffset NULL, -- date and time of the first update by [sp_sysutility_mi_collect_dac_execution_statistics_internal]
   last_collection_time datetimeoffset NULL,  -- date and time of the last update by [sp_sysutility_mi_collect_dac_execution_statistics_internal]
   last_upload_time datetimeoffset NULL,      -- date and time of the last upload of this data by DC
   lifetime_cpu_time_ms bigint NULL,          -- cumulative CPU time observed within this DAC since [first_collection_time]
   cpu_time_ms_at_last_upload bigint NULL,    -- [lifetime_cpu_time_ms] value at the last DC upload
   
   CONSTRAINT PK_sysutility_mi_dac_execution_statistics_internal PRIMARY KEY CLUSTERED (dac_instance_name)
);
GO


----------------------------------------------------------------------------------
-- Table dbo.sysutility_mi_session_statistics_internal
--    This table is effectively part of the implementation of sp_sysutility_mi_collect_dac_execution_statistics_internal. 
--    We can safely drop it because it holds ephemeral data that does not need to be preserved across 
--    an msdb upgrade.  The only reason we need a separate table here is because we must temporarily 
--    store execution statistics at the session level in order to approximate the database/DAC execution 
--    statistics (SQL doesn't automatically roll execution stats up to the database level). 
----------------------------------------------------------------------------------
IF (OBJECT_ID ('dbo.sysutility_mi_session_statistics_internal', 'U') IS NOT NULL)
BEGIN
   RAISERROR('Dropping table [dbo].[sysutility_mi_session_statistics_internal]', 0, 1)  WITH NOWAIT;
   DROP TABLE [dbo].[sysutility_mi_session_statistics_internal];
END;
GO

IF (OBJECT_ID ('dbo.sysutility_mi_session_statistics_internal', 'U') IS NULL)
BEGIN
   RAISERROR('Creating table [dbo].[sysutility_mi_session_statistics_internal]', 0, 1)  WITH NOWAIT;
   CREATE TABLE dbo.sysutility_mi_session_statistics_internal (
      collection_time datetimeoffset NOT NULL, 
      session_id int NOT NULL, 
      dac_instance_name sysname NOT NULL, 
      database_name sysname NOT NULL, 
      login_time datetime NOT NULL, 
      cumulative_cpu_ms int NOT NULL, 
      
      CONSTRAINT PK_sysutility_mi_session_statistics_internal PRIMARY KEY CLUSTERED 
         (collection_time, session_id, dac_instance_name, database_name, login_time)
   );
END;
GO


-------------------------------------------------------------------------------------
-- Configuration table to snapshot partitioning to enhance the performance of caching job 
-- time_id = 1 means the previous snapshot cut, time_id = 0, means the current snapshot_id cut
-- Preserve the table during upgrade scenarios (create if it doesn't exist)
-------------------------------------------------------------------------------------
IF(OBJECT_ID(N'[dbo].[sysutility_ucp_snapshot_partitions_internal]', 'U') IS NULL)
BEGIN
RAISERROR('Creating [dbo].[sysutility_ucp_snapshot_partitions_internal] table', 0, 1)  WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_ucp_snapshot_partitions_internal]
(
   time_id INT,
   latest_consistent_snapshot_id INT,
);
END
GO



----------------------------------------------------------------------------------
-- Procedure dbo.sp_sysutility_mi_collect_dac_execution_statistics_internal
--    Captures execution statistics (currently, just CPU usage) by sessions within each DAC since the last 
--    execution of this sp. The incremental new CPU usage is added to the the lifetime DAC total CPU usage 
--    recorded in the cache table [sysutility_mi_dac_execution_statistics_internal].  This sp is executed 
--    by the Agent job that runs every 15 seconds on a Utility managed instance.  The output (the execution 
--    stats in the cache table) is harvested by sp_sysutility_mi_get_dac_execution_statistics_internal as 
--    part of Utility's Data Collector collection set. 
--    
-- Parameters: None
-- Output Resultsets: None
----------------------------------------------------------------------------------
IF OBJECT_ID('dbo.sp_sysutility_mi_collect_dac_execution_statistics_internal', 'P') IS NOT NULL
BEGIN
   RAISERROR('Dropping [dbo].[sp_sysutility_mi_collect_dac_execution_statistics_internal] procedure', 0, 1) WITH NOWAIT;
   DROP PROCEDURE [dbo].[sp_sysutility_mi_collect_dac_execution_statistics_internal];
END
GO

RAISERROR('Creating [dbo].[sp_sysutility_mi_collect_dac_execution_statistics_internal] procedure', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysutility_mi_collect_dac_execution_statistics_internal]
AS
BEGIN
   DECLARE @current_collection_time datetimeoffset = SYSDATETIMEOFFSET();
   DECLARE @previous_collection_time datetimeoffset;
  
   -- At this point the session stats table should contain only rows from the prior collection time (or no 
   -- rows, in which case @previous_collection_time will be set to NULL). Retrieve the prior collection time. 
   SELECT TOP 1 @previous_collection_time = collection_time 
   FROM dbo.sysutility_mi_session_statistics_internal
   ORDER BY collection_time DESC; 

   -- Get a list of the DACs deployed on this instance.  In the sysdac_instances view, some of these values 
   -- are unindexed computed columns.  Store them in a temp table so that we get indexed retrieval by dbid 
   -- or db/instance name and stats on the columns we'll use as join columns. 
   CREATE TABLE #dacs (
      dac_instance_name sysname PRIMARY KEY, 
      database_name sysname UNIQUE, 
      database_id int UNIQUE, 
      date_created datetime, 
      [description] nvarchar(4000));
      
   INSERT INTO #dacs 
   SELECT DISTINCT 
      instance_name, 
      database_name, 
      DB_ID(database_name), 
      date_created, 
      [description]
   FROM dbo.sysdac_instances
   -- Filter out "orphaned" DACs that have had their database deleted or renamed
   WHERE DB_ID(database_name) IS NOT NULL;

   -- Step 1: Capture execution stats for all current sessions in DAC databases. Now this table should 
   -- hold two snapshots: one that we just inserted (referred to as the "current" data from here on), and 
   -- the immediately prior snapshot of the same data from ~15 seconds ago ("previous").  Note that we 
   -- include a dummy row with a fake spid number for any DACs that don't have any active sessions.  This 
   -- is because of a downstream requirement that we return a row for all DACs, even if there are no stats 
   -- to report for the DAC.  
   INSERT INTO dbo.sysutility_mi_session_statistics_internal 
      (collection_time, session_id, dac_instance_name, database_name, login_time, cumulative_cpu_ms)
   SELECT 
      @current_collection_time, 
      ISNULL (sp.spid, -10) AS session_id, 
      #dacs.dac_instance_name, 
      #dacs.database_name, 
      ISNULL (sp.login_time, GETDATE()) AS login_time, 
      ISNULL (SUM(sp.cpu), 0) AS cumulative_cpu_ms
   FROM #dacs 
   LEFT OUTER JOIN sys.sysprocesses AS sp ON #dacs.database_id = sp.[dbid]
   GROUP BY ISNULL (sp.spid, -10), #dacs.dac_instance_name, #dacs.database_name, ISNULL (sp.login_time, GETDATE()); 

   -- Step 2: If this is the first execution, set @prev_collection_time to @cur_collection_time. 
   -- This has the effect of generating stats that indicate no work done for all DACs.  This is 
   -- the best we can do on the first execution because we need to snapshots in order to calculate 
   -- correct resource consumption over a time interval.  We can't just return here, because we 
   -- need at least a "stub" row for each DAC, so they all DACs will appear in the UI if a DC 
   -- upload runs before our next collection time. 
   IF (@previous_collection_time IS NULL)
   BEGIN
      SET @previous_collection_time = @current_collection_time;
   END;

   -- Step 3: Determine the amount of new CPU time used by each DAC in the just-completed ~15 second interval 
   -- (this defines a CTE that is used in the following step). 
   WITH interval_dac_statistics AS (
      SELECT 
         #dacs.dac_instance_name, 
         -- Add up the approximate CPU time used by each session since the last execution of this proc. 
         -- The [current CPU] - [previous CPU] calculation for a session will return NULL if 
         -- [previous CPU] is NULL ([current CPU] should never be NULL).  Previous CPU might be 
         -- NULL if the session is new.  Previous CPU could also be NULL if an existing session 
         -- changed database context.  In either case, we will not charge any of the session's 
         -- CPU time to the database for this interval.  We will start charging any new session 
         -- CPU time to the session's current database on the next execution of this procedure, 
         -- assuming that the session doesn't change database context between now and then.  
         SUM (ISNULL (cur.cumulative_cpu_ms - prev.cumulative_cpu_ms, 0)) AS interval_cpu_time_ms, 
         #dacs.database_name, 
         #dacs.database_id, 
         #dacs.date_created, 
         #dacs.[description]
      FROM #dacs 
      LEFT OUTER JOIN dbo.sysutility_mi_session_statistics_internal AS cur   -- current snapshot, "right" side of time interval
         ON #dacs.dac_instance_name = cur.dac_instance_name AND cur.collection_time = @current_collection_time
      LEFT OUTER JOIN dbo.sysutility_mi_session_statistics_internal AS prev  -- previous snapshot, "left" side of time interval
         ON cur.dac_instance_name = prev.dac_instance_name AND prev.collection_time = @previous_collection_time 
            AND cur.session_id = prev.session_id AND cur.login_time = prev.login_time 
      GROUP BY #dacs.dac_instance_name, #dacs.database_name, #dacs.database_id, #dacs.date_created, #dacs.[description]
   )
   
   -- Step 4: Do an "upsert" to the staging table.  If the DAC is already represented in this table, we will 
   -- add our interval CPU time to that row's [lifetime_cpu_time_ms] value. If the DAC does not yet exist 
   -- in the staging table, we will insert a new row.  
   -- 
   -- A note about overflow risk for the [lifetime_cpu_time_ms] column (bigint).  A DAC that used 100% of 
   -- every CPU on a 500 processor machine 24 hours a day could run for more than half a million years without 
   -- overflowing this column.  
   MERGE [dbo].[sysutility_mi_dac_execution_statistics_internal] AS [target]
   USING interval_dac_statistics AS [source] 
      ON [source].dac_instance_name = [target].dac_instance_name 
         -- Filter out "orphaned" DACs that have had their database deleted or renamed
         AND DB_ID([target].database_name) IS NOT NULL 
   
   WHEN MATCHED THEN UPDATE 
      SET [target].lifetime_cpu_time_ms = ISNULL([target].lifetime_cpu_time_ms, 0) + [source].interval_cpu_time_ms, 
         [target].last_collection_time = @current_collection_time, 
         [target].first_collection_time = ISNULL ([target].first_collection_time, @previous_collection_time), 
         [target].database_name = [source].database_name, 
         [target].database_id = [source].database_id, 
         [target].date_created = [source].date_created, 
         [target].[description] = [source].[description] 
   
   WHEN NOT MATCHED BY TARGET THEN INSERT (  -- new DAC
      dac_instance_name, 
      first_collection_time, 
      last_collection_time, 
      last_upload_time, 
      lifetime_cpu_time_ms, 
      cpu_time_ms_at_last_upload, 
      database_name, 
      database_id, 
      date_created, 
      [description])
      VALUES (
         [source].dac_instance_name, 
         @previous_collection_time, 
         @current_collection_time, 
         @previous_collection_time, 
         [source].interval_cpu_time_ms, 
         0, 
         [source].database_name, 
         [source].database_id, 
         [source].date_created, 
         [source].[description])
   
   WHEN NOT MATCHED BY SOURCE THEN DELETE;   -- deleted or orphaned DAC

   -- Step 5: Delete the session stats from the previous collection time as we no longer need them (but keep the 
   -- current stats we just collected in Step 1; at the next collection time these will be the "previous" 
   -- stats that we'll use to calculate the stats for the next interval). 
   DELETE FROM dbo.sysutility_mi_session_statistics_internal WHERE collection_time != @current_collection_time;
END;
GO

----------------------------------------------------------------------------------
-- Procedure dbo.sp_sysutility_mi_get_dac_execution_statistics_internal
--    Retrieves execution statistics (currently, just CPU usage) for each DAC on the local managed instance. 
--    The CPU usage values are updated in the staging table [sysutility_mi_dac_execution_statistics_internal] 
--    by the [sp_sysutility_mi_collect_dac_execution_statistics_internal] stored proc.  This stored proc is 
--    executed by Utility's Data Collector collection set.  This sp returns an output resultset, but cannot 
--    be implemented as a TVF because it has side effects (records its last upload time in the staging table). 
--    
-- Parameters: None
-- Output Resultsets: A data set containing metadata and execution statistics for each DAC on this managed 
--    instance
----------------------------------------------------------------------------------
IF OBJECT_ID('dbo.sp_sysutility_mi_get_dac_execution_statistics_internal', 'P') IS NOT NULL
BEGIN
   RAISERROR('Dropping [dbo].[sp_sysutility_mi_get_dac_execution_statistics_internal] procedure', 0, 1) WITH NOWAIT;
   DROP PROCEDURE [dbo].[sp_sysutility_mi_get_dac_execution_statistics_internal];
END;
GO

RAISERROR('Creating [dbo].[sp_sysutility_mi_get_dac_execution_statistics_internal] procedure', 0, 1) WITH NOWAIT;
GO

CREATE PROCEDURE [dbo].[sp_sysutility_mi_get_dac_execution_statistics_internal]
AS
BEGIN
   SET NOCOUNT ON;   -- Required for SSIS to retrieve the proc's output rowset 
   DECLARE @logical_processor_count int;
   SELECT @logical_processor_count = cpu_count FROM sys.dm_os_sys_info;
   
   -- Get the shared "batch time" that will be a part of all data collection query rowsets.  On the UCP, this 
   -- will be used to tie together all of the data from one execution of the MI data collection job. 
   
   -- Check for the existance of the temp table.  If it is there, then the Utility is
   -- set up correctly.  If it is not there, do not fail the upload.  This handles the
   -- case when a user might run the collection set out-of-band from the Utility.
   -- The data may not be staged, but no sporratic errors should occur
   DECLARE @current_batch_time datetimeoffset(7) = SYSDATETIMEOFFSET();
   IF OBJECT_ID ('[tempdb].[dbo].[sysutility_batch_time_internal]') IS NOT NULL
   BEGIN
      SELECT @current_batch_time = latest_batch_time FROM tempdb.dbo.sysutility_batch_time_internal;
   END

   -- Temp storage for the DAC execution statistics for this data collection interval (typically, a 15 minute window). 
   -- This and the following table variable would be better as a temp table (b/c of the potentially large number 
   -- of rows), but this proc is run by DC with SET FMTONLY ON to get the output rowset schema.  That means no 
   -- temp tables.  
   DECLARE @upload_interval_dac_stats TABLE (
      dac_instance_name sysname PRIMARY KEY, 
      lifetime_cpu_time_ms bigint NULL,   -- amount of CPU time consumed since we started tracking this DAC
      interval_cpu_time_ms bigint NULL,   -- amount of CPU time used by the DAC in this ~15 min upload interval
      interval_start_time datetimeoffset NULL, 
      interval_end_time datetimeoffset NULL
   );
   
   -- We use an update with an OUTPUT clause to atomically update the staging table and retrieve data from it.  
   -- The use of the "inserted"/"deleted" pseudo-tables in this query ensures that we don't lose any data if the 
   -- collection job happens to be running at the same time. 
   UPDATE dbo.sysutility_mi_dac_execution_statistics_internal
   SET last_upload_time = SYSDATETIMEOFFSET(), 
       cpu_time_ms_at_last_upload = lifetime_cpu_time_ms 
   OUTPUT 
      inserted.dac_instance_name, 
      inserted.lifetime_cpu_time_ms, 
      -- Calculate the amount of CPU time consumed by this DAC since the last time we did an upload. 
      (inserted.cpu_time_ms_at_last_upload - ISNULL (deleted.cpu_time_ms_at_last_upload, 0)) AS interval_cpu_time_ms, 
      deleted.last_upload_time AS interval_start_time, 
      inserted.last_upload_time AS interval_end_time
   INTO @upload_interval_dac_stats;
   
   -- Return the data to the collection set
   SELECT 
      CONVERT (sysname, SERVERPROPERTY('ComputerNamePhysicalNetBIOS')) AS physical_server_name, 
      CONVERT (sysname, SERVERPROPERTY('ServerName')) AS server_instance_name, 
      CONVERT (sysname, dacs.database_name) AS dac_db, 
      dacs.date_created AS dac_deploy_date, 
      dacs.[description] AS dac_description, 
      dacs.dac_instance_name AS dac_name, 
      dac_stats.interval_start_time, 
      dac_stats.interval_end_time, 
      dac_stats.interval_cpu_time_ms, 
      CONVERT (real, CASE 
         WHEN dac_stats.interval_cpu_time_ms IS NOT NULL 
            AND DATEDIFF (second, dac_stats.interval_start_time, dac_stats.interval_end_time) > 0
            -- % CPU calculation is: [avg seconds of cpu time used per processor] / [interval duration in sec]
            -- The percentage value is returned as an int (e.g. 76 for 76%, not 0.76)
            THEN 100 * (dac_stats.interval_cpu_time_ms / @logical_processor_count) / 1000 / 
               DATEDIFF (second, dac_stats.interval_start_time, dac_stats.interval_end_time)
         ELSE 0
      END) AS interval_cpu_pct, 
      dac_stats.lifetime_cpu_time_ms, 
      @current_batch_time AS batch_time
   FROM dbo.sysutility_mi_dac_execution_statistics_internal AS dacs 
   LEFT OUTER JOIN @upload_interval_dac_stats AS dac_stats ON dacs.dac_instance_name = dac_stats.dac_instance_name;
END;
GO


----------------------------------------------------------------------------
--- Function to get a readable processor architecture from stored architecture number
----------------------------------------------------------------------------
IF EXISTS (SELECT name FROM [sys].[objects] WHERE object_id = OBJECT_ID(N'[dbo].[fn_sysutility_mi_get_cpu_architecture_name]'))
   DROP FUNCTION [dbo].[fn_sysutility_mi_get_cpu_architecture_name];
GO
CREATE FUNCTION [dbo].[fn_sysutility_mi_get_cpu_architecture_name](@architecture INT)
RETURNS NVARCHAR(64)
AS
BEGIN
   DECLARE @architecture_name NVARCHAR(64) = N''
   SELECT @architecture_name = 
      CASE 
         WHEN @architecture = 0 THEN 'x86'
         WHEN @architecture = 1 THEN 'MIPS'
         WHEN @architecture = 2 THEN 'Alpha'
         WHEN @architecture = 3 THEN 'PowerPC'
         WHEN @architecture = 6 THEN 'Intel Itanium Processor Family (IPF)'
         WHEN @architecture = 9 THEN 'x64'
      END
   RETURN @architecture_name
END
GO



----------------------------------------------------------------------------
--- Function to get a readable processor family from stored family number
----------------------------------------------------------------------------
IF EXISTS (SELECT name FROM [sys].[objects] WHERE object_id = OBJECT_ID(N'[dbo].[fn_sysutility_mi_get_cpu_family_name]'))
   DROP FUNCTION [dbo].[fn_sysutility_mi_get_cpu_family_name];
GO
CREATE FUNCTION [dbo].[fn_sysutility_mi_get_cpu_family_name](@family INT)
RETURNS NVARCHAR(128)
AS
BEGIN
   DECLARE @family_name NVARCHAR(128) = N''
   SELECT @family_name = 
      CASE 
         WHEN @family = 1 THEN 'Other'
         WHEN @family = 2 THEN 'Unknown'
         WHEN @family = 3 THEN '8086'
         WHEN @family = 4 THEN '80286'
         WHEN @family = 5 THEN 'Intel386 Processor'
         WHEN @family = 6 THEN 'Intel486 Processor'
         WHEN @family = 7 THEN '8087'
         WHEN @family = 8 THEN '80287'
         WHEN @family = 9 THEN '80387'
         WHEN @family = 10 THEN '80487'
         WHEN @family = 11 THEN 'Pentium Brand'
         WHEN @family = 12 THEN 'Pentium Pro'
         WHEN @family = 13 THEN 'Pentium II'
         WHEN @family = 14 THEN 'Pentium Processor with MMX Technology'
         WHEN @family = 15 THEN 'Celeron'
         WHEN @family = 16 THEN 'Pentium II Xeon'
         WHEN @family = 17 THEN 'Pentium III'
         WHEN @family = 18 THEN 'M1 Family'
         WHEN @family = 19 THEN 'M2 Family'
         WHEN @family = 24 THEN 'AMD Duron Processor Family'
         WHEN @family = 25 THEN 'K5 Family'
         WHEN @family = 26 THEN 'K6 Family'
         WHEN @family = 27 THEN 'K6-2'
         WHEN @family = 28 THEN 'K6-3'
         WHEN @family = 29 THEN 'AMD Athlon Processor Family'
         WHEN @family = 30 THEN 'AMD2900 Family'
         WHEN @family = 31 THEN 'K6-2+'
         WHEN @family = 32 THEN 'Power PC Family'
         WHEN @family = 33 THEN 'Power PC 601'
         WHEN @family = 34 THEN 'Power PC 603'
         WHEN @family = 35 THEN 'Power PC 603+'
         WHEN @family = 36 THEN 'Power PC 604'
         WHEN @family = 37 THEN 'Power PC 620'
         WHEN @family = 38 THEN 'Power PC X704'
         WHEN @family = 39 THEN 'Power PC 750'
         WHEN @family = 48 THEN 'Alpha Family'
         WHEN @family = 49 THEN 'Alpha 21064'
         WHEN @family = 50 THEN 'Alpha 21066'
         WHEN @family = 51 THEN 'Alpha 21164'
         WHEN @family = 52 THEN 'Alpha 21164PC'
         WHEN @family = 53 THEN 'Alpha 21164a'
         WHEN @family = 54 THEN 'Alpha 21264'
         WHEN @family = 55 THEN 'Alpha 21364'
         WHEN @family = 64 THEN 'MIPS Family'
         WHEN @family = 65 THEN 'MIPS R4000'
         WHEN @family = 66 THEN 'MIPS R4200'
         WHEN @family = 67 THEN 'MIPS R4400'
         WHEN @family = 68 THEN 'MIPS R4600'
         WHEN @family = 69 THEN 'MIPS R10000'
         WHEN @family = 80 THEN 'SPARC Family'
         WHEN @family = 81 THEN 'SuperSPARC'
         WHEN @family = 82 THEN 'microSPARC II'
         WHEN @family = 83 THEN 'microSPARC IIep'
         WHEN @family = 84 THEN 'UltraSPARC'
         WHEN @family = 85 THEN 'UltraSPARC II'
         WHEN @family = 86 THEN 'UltraSPARC IIi'
         WHEN @family = 87 THEN 'UltraSPARC III'
         WHEN @family = 88 THEN 'UltraSPARC IIIi'
         WHEN @family = 96 THEN '68040'
         WHEN @family = 97 THEN '68xxx Family'
         WHEN @family = 98 THEN '68000'
         WHEN @family = 99 THEN '68010'
         WHEN @family = 100 THEN '68020'
         WHEN @family = 101 THEN '68030'
         WHEN @family = 112 THEN 'Hobbit Family'
         WHEN @family = 120 THEN 'Crusoe TM5000 Family'
         WHEN @family = 121 THEN 'Crusoe TM3000 Family'
         WHEN @family = 122 THEN 'Efficeon TM8000 Family'
         WHEN @family = 128 THEN 'Weitek'
         WHEN @family = 130 THEN 'Itanium Processor'
         WHEN @family = 131 THEN 'AMD Athlon 64 Processor Family'
         WHEN @family = 132 THEN 'AMD Opteron Processor Family'
         WHEN @family = 144 THEN 'PA-RISC Family'
         WHEN @family = 145 THEN 'PA-RISC 8500'
         WHEN @family = 146 THEN 'PA-RISC 8000'
         WHEN @family = 147 THEN 'PA-RISC 7300LC'
         WHEN @family = 148 THEN 'PA-RISC 7200'
         WHEN @family = 149 THEN 'PA-RISC 7100LC'
         WHEN @family = 150 THEN 'PA-RISC 7100'
         WHEN @family = 160 THEN 'V30 Family'
         WHEN @family = 176 THEN 'Pentium III Xeon Processor'
         WHEN @family = 177 THEN 'Pentium III Processor with Intel SpeedStep Technology'
         WHEN @family = 178 THEN 'Pentium 4'
         WHEN @family = 179 THEN 'Intel Xeon'
         WHEN @family = 180 THEN 'AS400 Family'
         WHEN @family = 181 THEN 'Intel Xeon Processor MP'
         WHEN @family = 182 THEN 'AMD Athlon XP Family'
         WHEN @family = 183 THEN 'AMD Athlon MP Family'
         WHEN @family = 184 THEN 'Intel Itanium 2'
         WHEN @family = 185 THEN 'Intel Pentium M Processor'
         WHEN @family = 190 THEN 'K7'
         WHEN @family = 200 THEN 'IBM390 Family'
         WHEN @family = 201 THEN 'G4'
         WHEN @family = 202 THEN 'G5'
         WHEN @family = 203 THEN 'G6'
         WHEN @family = 204 THEN 'z/Architecture Base'
         WHEN @family = 250 THEN 'i860'
         WHEN @family = 251 THEN 'i960'
         WHEN @family = 260 THEN 'SH-3'
         WHEN @family = 261 THEN 'SH-4'
         WHEN @family = 280 THEN 'ARM'
         WHEN @family = 281 THEN 'StrongARM'
         WHEN @family = 300 THEN '6x86'
         WHEN @family = 301 THEN 'MediaGX'
         WHEN @family = 302 THEN 'MII'
         WHEN @family = 320 THEN 'WinChip'
         WHEN @family = 350 THEN 'DSP'
         WHEN @family = 500 THEN 'Video Processor'
      END
   RETURN @family_name
END
GO

IF (OBJECT_ID(N'[msdb].[dbo].[sysutility_mi_volumes_stage_internal]', N'U') IS NULL)
BEGIN
RAISERROR('Creating [dbo].[sysutility_mi_volumes_stage_internal] table', 0, 1)  WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_mi_volumes_stage_internal]
(  
   -- Collected columns
   [volume_device_id]     SYSNAME          NOT NULL DEFAULT N'',   -- Collected as String
   [volume_name]   NVARCHAR(260)	NOT NULL DEFAULT N'',   -- Collected as String

   [capacity_mb]    DECIMAL(20,0)   NOT NULL DEFAULT 0.0,   -- Collected as UInt64
   [free_space_mb]  DECIMAL(20,0)   NOT NULL DEFAULT 0.0,   -- Collected as UInt64
   
   -- Computed columns
   [server_instance_name]     AS (CAST(SERVERPROPERTY('ServerName') AS SYSNAME)),
   [virtual_server_name]      AS (CAST(SERVERPROPERTY('MachineName') AS SYSNAME)),
   [physical_server_name]     AS (CAST(SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS SYSNAME))
);
END
GO


IF (OBJECT_ID(N'[dbo].[sysutility_mi_cpu_stage_internal]', 'U') IS NULL)
BEGIN
RAISERROR('Creating [dbo].[sysutility_mi_cpu_stage_internal]', 0, 1) WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_mi_cpu_stage_internal]
(
   -- Collected columns
   [num_processors]          INT            NOT NULL DEFAULT -1,  -- Collected as Int32, -1 represents unknown
   [cpu_name]                NVARCHAR(128)  NOT NULL DEFAULT N'', -- Collected as String
   [cpu_caption]             NVARCHAR(128)  NOT NULL DEFAULT N'', -- Collected as String
   [cpu_family_id]           DECIMAL(5,0)   NOT NULL DEFAULT -1,  -- Collected as UInt16, -1 represents unknown
   [cpu_architecture_id]     DECIMAL(5,0)   NOT NULL DEFAULT -1,  -- Collected as UInt16, -1 represents unknown
   [cpu_max_clock_speed]     DECIMAL(10,0)  NOT NULL DEFAULT 0.0, -- Collected as UInt32
   [cpu_clock_speed]         DECIMAL(10,0)  NOT NULL DEFAULT 0.0, -- Collected as UInt32
   [l2_cache_size]           DECIMAL(10,0)  NOT NULL DEFAULT 0.0, -- Collected as UInt32
   [l3_cache_size]           DECIMAL(10,0)  NOT NULL DEFAULT 0.0, -- Collected as UInt32
   
   [instance_processor_usage_start_ticks]   DECIMAL(20,0) NOT NULL DEFAULT 0.0, -- Collected as UInt64
   [instance_collect_time_start_ticks]      DECIMAL(20,0) NOT NULL DEFAULT 0.0, -- Collected as Int64, but should not be negative
   
   [computer_processor_idle_start_ticks]    DECIMAL(20,0) NOT NULL DEFAULT 0.0, -- Collected as UInt64
   [computer_collect_time_start_ticks]      DECIMAL(20,0) NOT NULL DEFAULT 0.0, -- Collected as UInt64
   
   [instance_processor_usage_end_ticks]     DECIMAL(20,0) NOT NULL DEFAULT 0.0, -- Collected as UInt64
   [instance_collect_time_end_ticks]        DECIMAL(20,0) NOT NULL DEFAULT 0.0, -- Collected as Int64, but should not be negative
   
   [computer_processor_idle_end_ticks]      DECIMAL(20,0) NOT NULL DEFAULT 0.0, -- Collected as UInt64
   [computer_collect_time_end_ticks]        DECIMAL(20,0) NOT NULL DEFAULT 0.0, -- Collected as UInt64

   -- Computed columns
   [server_instance_name]     AS (CAST(SERVERPROPERTY('ServerName') AS SYSNAME)),
   [virtual_server_name]      AS (CAST(SERVERPROPERTY('MachineName') AS SYSNAME)),
   [physical_server_name]     AS (CAST(SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS SYSNAME)),
   
   [instance_processor_usage_percentage] AS 
                                (-- negative elapsed usage indicates instance reboot.  use 0 as usage ticks in this case
                                CAST(
                                   CASE WHEN 
                                           -- usage time can be 0 when the instance never got a timeslice (very unlikely)
                                          (0 > instance_processor_usage_end_ticks - instance_processor_usage_start_ticks)
                                       OR (0 >= instance_collect_time_end_ticks - instance_collect_time_start_ticks) 
                                       THEN 0.0  
                                   ELSE 
                                   -- we divide by num_processors since we're getting this info from the process' overall consumption. 
                                   -- not the same for the computer-processor information below
                                   ((instance_processor_usage_end_ticks - instance_processor_usage_start_ticks) 
                                   / (instance_collect_time_end_ticks - instance_collect_time_start_ticks)
                                   / num_processors) * 100.0
                                   END
                                AS REAL)),
       
   [computer_processor_usage_percentage] AS								
                                (-- negative elapsed usage indicates instance reboot.  use 0 as usage ticks in this case
                                CAST(
                                   CASE WHEN 
                                           -- idle time can be 0 when the processor is completely saturated
                                          (0 > computer_processor_idle_end_ticks - computer_processor_idle_start_ticks)
                                       OR (0 >= computer_collect_time_end_ticks - computer_collect_time_start_ticks) 
                                       THEN 0.0  
                                   ELSE 
                                   -- 1.0 - PercentProcessorTime because using idle time and want percent utilized time
                                   (1.0 - 
                                   ((computer_processor_idle_end_ticks - computer_processor_idle_start_ticks)  
                                   / (computer_collect_time_end_ticks - computer_collect_time_start_ticks))) * 100.0
                                   END
                                AS REAL))
)
END
GO

IF NOT EXISTS (SELECT name FROM [sys].[objects] WHERE object_id = OBJECT_ID(N'[dbo].[sysutility_mi_smo_stage_internal]'))
BEGIN
RAISERROR('Creating [dbo].[sysutility_mi_smo_stage_internal]', 0, 1) WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_mi_smo_stage_internal]
(
   -- Collected columns
   [object_type]           INT        NOT NULL,          -- FK reference to sysutility_mi_smo_objects_to_collect_internal.object_type
                                                         -- FK is not defined on the table to keep table a light-weight staging table
   [urn]                   NVARCHAR(4000) NOT NULL,      -- Collected as String
                                                         -- Needs to hold /Server[@Name='']/Database[@Name='']/Filegroup[@Name='']/DataFile[@Name='']
                                                         -- where value within '' is SYSNAME, so 600 is enough thus 4000 is plenty
   [property_name]         NVARCHAR(128)   NOT NULL,     -- Collected as String, no property should be longer than 80 characters, but we'll be safe
   [property_value]        SQL_VARIANT    NULL,          -- Collected as Object

   
   -- Computed columns
   [server_instance_name]     AS (CAST(SERVERPROPERTY('ServerName') AS SYSNAME)),
   [physical_server_name]     AS (CAST(SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS SYSNAME))
);
END
GO

-------------------------------------------------------------------------
-- Create function: fn_sysutility_mi_get_batch_manifest
-- This function returns the manifest information for the most recent batch collected 
-- and is included as a part of the data uploaded from the MI to UCP. 
-- The purpose of the manifest is to qualify the consistency of the data uploaded on the UCP.
-- The batch manifest primarily includes:
-- 1. server_instance_name: the server\instance name of the MI
-- 2. batch_time: the batch creation date-time stamp.  
-- 3. xx_row_count: row count for each of the table collected/uploaded by the utility T-SQL collection item query
-- Note: a. The server_instance_name & batch_time make the composite key for batch_manifest
--       b. The parameter name is of type string and value is of type sql_variant.	
-------------------------------------------------------------------------
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fn_sysutility_mi_get_batch_manifest]') AND type in (N'IF'))
BEGIN
   RAISERROR('Dropping [dbo].[fn_sysutility_mi_get_batch_manifest] function', 0, 1)  WITH NOWAIT;
   DROP FUNCTION [dbo].[fn_sysutility_mi_get_batch_manifest];
END
GO
RAISERROR('Creating [dbo].[fn_sysutility_mi_get_batch_manifest] function', 0, 1)  WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysutility_mi_get_batch_manifest]()
RETURNS TABLE
AS
RETURN
(
    -- DAC execution statistics row count
    SELECT N'dac_packages_row_count' AS parameter_name
    , CONVERT(SQL_VARIANT, COUNT(*)) AS parameter_value
    FROM [msdb].[dbo].[sysutility_mi_dac_execution_statistics_internal] 

    UNION ALL
    
    -- MI CPU and memory configurations row count
    SELECT N'cpu_memory_configurations_row_count' AS parameter_name
    , CONVERT(SQL_VARIANT, COUNT(*)) AS parameter_value
    FROM [msdb].[dbo].[sysutility_mi_cpu_stage_internal]

    UNION ALL

    -- MI volumes row count
    SELECT N'volumes_row_count' AS parameter_name
    , CONVERT(SQL_VARIANT, COUNT(*)) AS parameter_value 
    FROM [msdb].[dbo].[sysutility_mi_volumes_stage_internal]

    UNION ALL
    
    -- SMO properties row count
    SELECT N'smo_properties_row_count' AS parameter_name
    , CONVERT(SQL_VARIANT, COUNT(*)) AS parameter_value 
    FROM [msdb].[dbo].[sysutility_mi_smo_stage_internal]
)
GO
-------------------------------------------------------------------------------
-- Stores the Smo sfc query that is used during data collection
-------------------------------------------------------------------------------
IF (OBJECT_ID(N'[dbo].[sysutility_mi_smo_objects_to_collect_internal]', 'U') IS NULL)
BEGIN
RAISERROR('Creating [dbo].[sysutility_mi_smo_objects_to_collect_internal]', 0, 1) WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_mi_smo_objects_to_collect_internal]
(
   [object_type]              INT       NOT NULL,
   [sfc_query]                NVARCHAR(MAX) NOT NULL,
   PRIMARY KEY (object_type)
);
END
GO
 

-------------------------------------------------------------------------------
--Script for filling the smo configurations tables
-------------------------------------------------------------------------------
DELETE FROM [dbo].[sysutility_mi_smo_objects_to_collect_internal]

INSERT INTO [dbo].[sysutility_mi_smo_objects_to_collect_internal] VALUES(1, N'Server'); 
INSERT INTO [dbo].[sysutility_mi_smo_objects_to_collect_internal] VALUES(2, N'Server/Database');
INSERT INTO [dbo].[sysutility_mi_smo_objects_to_collect_internal] VALUES(3, N'Server/Database[@IsAccessible=1]/LogFile');
-- for SQL10/10.5 file stat dmv support for filestream isn't there, thus we do not collect filestream related
-- file groups and data files. VSTS 351631.
INSERT INTO [dbo].[sysutility_mi_smo_objects_to_collect_internal] VALUES(4, N'Server/Database[@IsAccessible=1]/FileGroup[@IsFileStream=0]');
INSERT INTO [dbo].[sysutility_mi_smo_objects_to_collect_internal] VALUES(5, N'Server/Database[@IsAccessible=1]/FileGroup[@IsFileStream=0]/File');

-------------------------------------------------------------------------------
-- Stores SMO properties to collect
-------------------------------------------------------------------------------
IF (OBJECT_ID(N'[dbo].[sysutility_mi_smo_properties_to_collect_internal]', 'U') IS NULL)
BEGIN
RAISERROR('Creating [dbo].[sysutility_mi_smo_properties_to_collect_internal]', 0, 1) WITH NOWAIT;
CREATE TABLE [dbo].[sysutility_mi_smo_properties_to_collect_internal]
(
   [object_type]         INT NOT NULL,
   [property_name]       NVARCHAR(80) NOT NULL,
   PRIMARY KEY (object_type, property_name)
);

ALTER TABLE [dbo].[sysutility_mi_smo_properties_to_collect_internal]  WITH CHECK ADD  CONSTRAINT [FK_sysutility_mi_smo_properties] FOREIGN KEY([object_type])
REFERENCES [dbo].[sysutility_mi_smo_objects_to_collect_internal] ([object_type])
ON DELETE CASCADE

END
GO

DELETE FROM [dbo].[sysutility_mi_smo_properties_to_collect_internal]
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'AuditLevel');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'BackupDirectory');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'BrowserServiceAccount');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'BrowserStartMode');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'BuildClrVersionString');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'BuildNumber');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'Collation');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'CollationID');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'ComparisonStyle');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'ComputerNamePhysicalNetBIOS');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'DefaultFile');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'DefaultLog');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'Edition');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'EngineEdition');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'ErrorLogPath');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'FilestreamShareName');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'InstallDataDirectory');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'InstallSharedDirectory');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'InstanceName');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'IsCaseSensitive');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'IsClustered');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'IsFullTextInstalled');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'IsSingleUser');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'Language');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'MailProfile');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'MasterDBLogPath');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'MasterDBPath');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'MaxPrecision');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'Name');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'NamedPipesEnabled');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'NetName');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'NumberOfLogFiles');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'OSVersion');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'PerfMonMode');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'PhysicalMemory');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'Platform');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'Processors');
-- note for this 'ProcessorUsage' property this is just a placeholder, we in fact collect its value
-- by ourselves instead of relying on SMO.Server.ProcessorUsage property for two reasons
-- 1) our collection mechanism is more accurate because we're doing averaging over
--    our entire collection cycle versus, SMO property value was very instantaneous.
-- 2) this SMO property isn't available downlevel (before 10.5)
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'ProcessorUsage');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'Product');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'ProductLevel');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'ResourceVersionString');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'RootDirectory');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'ServerType');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'ServiceAccount');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'ServiceInstanceId');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'ServiceName');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'ServiceStartMode');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'SqlCharSet');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'SqlCharSetName');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'SqlDomainGroup');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'SqlSortOrder');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'SqlSortOrderName');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'Status');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'TapeLoadWaitTime');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'TcpEnabled');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'VersionMajor');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'VersionMinor');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(1, N'VersionString');

INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(2, N'CreateDate');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(2, N'Collation');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(2, N'CompatibilityLevel');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(2, N'EncryptionEnabled');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(2, N'ID');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(2, N'Name');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(2, N'RecoveryModel');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(2, N'Trustworthy');

INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(3, N'FileName');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(3, N'Growth');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(3, N'GrowthType');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(3, N'ID');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(3, N'MaxSize');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(3, N'Name');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(3, N'Size');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(3, N'UsedSpace');

INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(4, N'ID');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(4, N'Name');

INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(5, N'FileName');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(5, N'Growth');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(5, N'GrowthType');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(5, N'ID');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(5, N'MaxSize');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(5, N'Name');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(5, N'Size');
INSERT INTO [dbo].[sysutility_mi_smo_properties_to_collect_internal] VALUES(5, N'UsedSpace');
GO

-----------------------------------------------------------------------
--  Script for creating Utility Collection sets and Collection set items.
-----------------------------------------------------------------------

   DECLARE @collection_set_number INT = 0 ;
   DECLARE @running_state BIT;    
    DECLARE @collection_set_name NVARCHAR(128);
   DECLARE @description NVARCHAR(4000);
   DECLARE @collection_set_uid uniqueidentifier;
   DECLARE @collection_mode smallint;
   DECLARE @schedule_name sysname;
   DECLARE @days_until_expiration smallint;
   DECLARE @name_id int;
   DECLARE @description_id int;
   DECLARE @collection_set_id INT
   DECLARE @proxy_id int = NULL;
    
    
   SET @name_id = 14716;
   SET @description_id = 14715;
   SET @collection_set_uid = N'ABA37A22-8039-48C6-8F8F-39BFE0A195DF';
   SET @collection_mode = 1; -- Non-cached
   SET @schedule_name = N''; -- Non-scheduled.  The Utility collection runs on-demand through the Utility-owned job.
   DECLARE @frequency int = 900; -- Collection is on demand, this value is ignored
   
   SET @days_until_expiration = 1;
    
   SET @description = FORMATMESSAGE(@description_id);
   SET @collection_set_name = ISNULL (FORMATMESSAGE(@name_id), 'Utility Information');
    
   -- Unlike the MDW system collection sets, we don't need to retain any customized schedule added by the user.  The 
   -- Utility collection set is not scheduled and we want it to stay that way.  To simplify this code, we just drop 
   -- the collection set if it exists, then recreate it.      
   IF EXISTS (SELECT * FROM dbo.syscollector_collection_sets_internal WHERE collection_set_uid = @collection_set_uid)
   BEGIN
      RAISERROR ('Deleting collection set "%s"...', 0, 1, @collection_set_name) WITH NOWAIT;

      -- Get the collection set's collection set ID and save off the current proxy ID.  If the collection set was 
      -- configured to run under a proxy account, this is the one user-specified attribute that we need to preserve 
      -- when we recreate it.  If the collection set has not been configured, proxy_id will be NULL.  When we pass 
      -- NULL to sp_syscollector_create_collection_set when recreating the collection set, a NULL value for the 
      -- @proxy_id parameter also means "don't use a proxy".  
      SELECT 
         @collection_set_id = collection_set_id, 
         @proxy_id = proxy_id 
      FROM syscollector_collection_sets 
      WHERE collection_set_uid = @collection_set_uid;
      
      -- Temporarily clear the is_system flag so that we can modify the collection set definition
      UPDATE syscollector_collection_sets
      SET is_system = 0
      WHERE collection_set_id = @collection_set_id
      
      EXEC sp_syscollector_delete_collection_set @collection_set_id = @collection_set_id;
   END

   RAISERROR ('Creating system Collection Set "%s"...', 0, 1, @collection_set_name) WITH NOWAIT;
   EXEC dbo.sp_syscollector_create_collection_set
      @collection_set_uid = @collection_set_uid,
      @name = @collection_set_name, 
      @schedule_name = @schedule_name, 
      @collection_mode = @collection_mode, 
      @days_until_expiration = @days_until_expiration, 
      @description = @description,
      @logging_level = 0, 
      @proxy_id = @proxy_id, 
      @collection_set_id = @collection_set_id OUTPUT;
   
   IF(FORMATMESSAGE(@name_id) IS NOT NULL)
   BEGIN
      -- for localization of collection set name and description
      UPDATE syscollector_collection_sets_internal
      SET name_id = @name_id, description_id = @description_id
      WHERE collection_set_uid = @collection_set_uid;
   END

   DECLARE @collector_type_uid_3 UNIQUEIDENTIFIER
    SELECT @collector_type_uid_3 = collector_type_uid 
   FROM [dbo].[syscollector_collector_types] 
   WHERE name = N'Generic T-SQL Query Collector Type';
   
   DECLARE @collection_item_loc_name NVARCHAR(128);
   DECLARE @collection_item_enu_name NVARCHAR(128);
   DECLARE @collection_item_id int;

   SET @name_id = 14718;
   SET @collection_item_enu_name = 'Utility Information - Managed Instance';
   SET @collection_item_loc_name = ISNULL (FORMATMESSAGE(@name_id), @collection_item_enu_name);


   DECLARE @parameters xml;
   SELECT @parameters = convert(xml, N'<ns:TSQLQueryCollector xmlns:ns="DataCollectorType">
   <Query>
      <Value>
         EXEC [msdb].[dbo].[sp_sysutility_mi_get_dac_execution_statistics_internal];
      </Value><OutputTable>sysutility_ucp_dac_collected_execution_statistics_internal</OutputTable>
   </Query>
   
   <Query>
      <Value>
      -- Check for the existance of the temp table.  If it is there, then the Utility is
      -- set up correctly.  If it is not there, do not fail the upload.  This handles the
      -- case when a user might run the collection set out-of-band from the Utility.
      -- The data may not be staged, but no sporratic errors should occur
      DECLARE @batch_time datetimeoffset(7) = SYSDATETIMEOFFSET()
      IF OBJECT_ID (''[tempdb].[dbo].[sysutility_batch_time_internal]'') IS NOT NULL
      BEGIN
          SELECT @batch_time = latest_batch_time FROM tempdb.dbo.sysutility_batch_time_internal
      END
      SELECT 
         [server_instance_name],
         CAST(clustered_check.is_clustered_server AS SMALLINT) AS [is_clustered_server], 
         [virtual_server_name],
         [physical_server_name],
         [num_processors],
         [computer_processor_usage_percentage]  AS [server_processor_usage],
         [instance_processor_usage_percentage]  AS [instance_processor_usage],
         [cpu_name],
           [cpu_caption],
         [msdb].[dbo].[fn_sysutility_mi_get_cpu_family_name](cpu_family_id) AS [cpu_family],
         [msdb].[dbo].[fn_sysutility_mi_get_cpu_architecture_name](cpu_architecture_id) AS [cpu_architecture],
         [cpu_max_clock_speed],
         [cpu_clock_speed],
         [l2_cache_size],
         [l3_cache_size], 
         @batch_time AS [batch_time]
         FROM [msdb].[dbo].[sysutility_mi_cpu_stage_internal],
           (SELECT TOP 1 CAST (CASE WHEN COUNT(*) = 0 THEN 0 ELSE 1 END AS bit) AS is_clustered_server
            FROM msdb.sys.dm_os_cluster_nodes 
            WHERE NodeName = SERVERPROPERTY(''ComputerNamePhysicalNetBIOS'')) AS clustered_check
      </Value><OutputTable>sysutility_ucp_cpu_memory_configurations_internal</OutputTable>
   </Query>

   <Query>
      <Value>
         -- Check for the existance of the temp table.  If it is there, then the Utility is
         -- set up correctly.  If it is not there, do not fail the upload.  This handles the
         -- case when a user might run the collection set out-of-band from the Utility.
         -- The data may not be staged, but no sporratic errors should occur
         DECLARE @batch_time datetimeoffset(7) = SYSDATETIMEOFFSET()
         IF OBJECT_ID (''[tempdb].[dbo].[sysutility_batch_time_internal]'') IS NOT NULL
         BEGIN
             SELECT @batch_time = latest_batch_time FROM tempdb.dbo.sysutility_batch_time_internal
         END
         SELECT 
            [volume_device_id],
            [volume_name],
            CAST([capacity_mb] AS REAL)   AS [total_space_available],
            CAST([free_space_mb] AS REAL) AS [free_space],
            [virtual_server_name],
            [physical_server_name],
            [server_instance_name], 
            @batch_time AS [batch_time]
         FROM [msdb].[dbo].[sysutility_mi_volumes_stage_internal]
      </Value>
      <OutputTable>sysutility_ucp_volumes_internal</OutputTable>
   </Query>
         
   <Query>
   <Value>
      -- Check for the existance of the temp table.  If it is there, then the Utility is
      -- set up correctly.  If it is not there, do not fail the upload.  This handles the
      -- case when a user might run the collection set out-of-band from the Utility.
      -- The data may not be staged, but no sporratic errors should occur
      DECLARE @batch_time datetimeoffset(7) = SYSDATETIMEOFFSET()
      IF OBJECT_ID (''[tempdb].[dbo].[sysutility_batch_time_internal]'') IS NOT NULL
      BEGIN
          SELECT @batch_time = latest_batch_time FROM tempdb.dbo.sysutility_batch_time_internal
      END
      
      SELECT 
         smo.[physical_server_name],
         smo.[server_instance_name],
         [object_type],
         [urn],
         [property_name],
         -- DC (SSIS, really) does not support sql_variant.  It will implicitly convert all variant columns to nvarchar(256), 
         -- which can cause data loss.  To avoid this we explicitly convert to nvarchar(4000) so that nothing gets truncated. 
         -- On the UCP, we reverse this conversion in sp_copy_live_table_data_into_cache_tables.  In order to round-trip the 
         -- data through nvarchar successfully, we must use the same language-independent conversion style on MI and UCP. We 
         -- use the shared fn_sysutility_get_culture_invariant_conversion_style_internal function to get a consistent 
         -- language-independent conversion style for each property data type.  (References: VSTS 361531, 359504, 12967)
         CONVERT 
         (
            nvarchar(4000), 
            CASE [property_name] 
               WHEN N''ProcessorUsage'' -- Hijack the ProcessorUsage property and insert our own value
               THEN CAST(cpu.[instance_processor_usage_percentage] AS INT)  -- loss of decimal places
               ELSE [property_value] 
            END, 
            msdb.dbo.fn_sysutility_get_culture_invariant_conversion_style_internal(CONVERT (varchar(30), SQL_VARIANT_PROPERTY (property_value, ''BaseType''))) 
         ) AS [property_value], 
         @batch_time AS [batch_time]
      FROM [msdb].[dbo].[sysutility_mi_smo_stage_internal] AS smo
      INNER JOIN [msdb].[dbo].[sysutility_mi_cpu_stage_internal] AS cpu 
         ON smo.[server_instance_name] = cpu.[server_instance_name]
      </Value><OutputTable>sysutility_ucp_smo_properties_internal</OutputTable>
  </Query>
              
  <!-- Query to collect/upload the batch manifest -->
  <Query>
    <Value>
        -- Check for the existance of the temp table.  If it is there, then the Utility is
        -- set up correctly.  If it is not there, do not fail the upload.  This handles the
        -- case when a user might run the collection set out-of-band from the Utility.
        -- The data may not be staged, but no sporratic errors should occur
        DECLARE @batch_time datetimeoffset(7) = SYSDATETIMEOFFSET()
        IF OBJECT_ID (''[tempdb].[dbo].[sysutility_batch_time_internal]'') IS NOT NULL
        BEGIN
          SELECT @batch_time = latest_batch_time FROM tempdb.dbo.sysutility_batch_time_internal
        END 
        SELECT CONVERT(SYSNAME, SERVERPROPERTY(N''ServerName'')) AS [server_instance_name],
            @batch_time AS [batch_time],
            bm.parameter_name,
            bm.parameter_value
        FROM [msdb].[dbo].[fn_sysutility_mi_get_batch_manifest]() bm  
    </Value>
    <OutputTable>sysutility_ucp_batch_manifests_internal</OutputTable>
  </Query> 
  </ns:TSQLQueryCollector>');
         
   SET @collection_item_id = NULL;
   SELECT @collection_item_id = collection_item_id
   FROM syscollector_collection_items_internal 
   WHERE collection_set_id = @collection_set_id 
      AND (name = @collection_item_loc_name OR name = @collection_item_enu_name);

   IF (@collection_item_id IS NOT NULL)
   BEGIN
      RAISERROR ('Updating Collection Item "%s"...', 0, 1, @collection_item_loc_name);
      EXEC dbo.sp_syscollector_update_collection_item 
         @collection_item_id = @collection_item_id, 
         @new_name = @collection_item_loc_name, 
         @frequency = @frequency, 
         @parameters = @parameters;
   END
   ELSE
   BEGIN
      RAISERROR ('Creating Collection Item "%s"...', 0, 1, @collection_item_loc_name);
      EXEC dbo.sp_syscollector_create_collection_item
         @collection_set_id = @collection_set_id,
         @collector_type_uid = N'302E93D1-3424-4BE7-AA8E-84813ECF2419',
         @name = @collection_item_loc_name,
         @parameters = @parameters,
         @frequency = @frequency, 
         @collection_item_id = @collection_item_id output;
   END;
   
   IF(FORMATMESSAGE(@name_id) IS NOT NULL)
   BEGIN
      -- for localization of collection item name
      UPDATE syscollector_collection_items_internal
      SET name_id = @name_id
      WHERE collection_item_id = @collection_item_id;
   END

   -- Turn the is_system flag on so users can't change the definition of this collection set 
   UPDATE syscollector_collection_sets
   SET is_system = 1
   WHERE collection_set_id = @collection_set_id

GO

----------------------------------------------------------------
-- An SP to get whether the instance is already running DC
-- OR not
----------------------------------------------------------------

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fn_sysutility_mi_get_data_collector_status]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
BEGIN
   RAISERROR('Dropping [dbo].[fn_sysutility_mi_get_data_collector_status] procedure', 0, 1)  WITH NOWAIT;
   DROP FUNCTION [dbo].[fn_sysutility_mi_get_data_collector_status];
END
GO
RAISERROR('Creating [dbo].[fn_sysutility_mi_get_data_collector_status] procedure', 0, 1)  WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysutility_mi_get_data_collector_status]()
   RETURNS BIT
AS
BEGIN
   RETURN 
   (
    SELECT CAST (ISNULL (parameter_value, 0) AS bit) 
       FROM [msdb].[dbo].[syscollector_config_store_internal]
       WHERE parameter_name = 'CollectorEnabled'
   );
END
GO



--------------------------------------------------------
-- Utility collect and upload procedures and functions
--------------------------------------------------------


-------------------------------------------------------------------------------
-- Upload the collected data to the utility control point.
--
-- Specifics: 
--  The Utility uses the data collector to upload data to the UCP.  The
--  collection set corresponding to the Utility is set to on-demand mode.
--  The Utility directs the data collector to run the collect and upload cycle
--  through a Utility-owned job.  The Utility-owned job first stages data,
--  and in the final job step calls this procedure to direct DC to pick up
--  the staged data and upload it to the UCP.  
-------------------------------------------------------------------------------
IF OBJECT_ID ('[dbo].[sp_sysutility_mi_upload]') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_upload]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE [dbo].[sp_sysutility_mi_upload] 
END;
RAISERROR ('Creating procedure [dbo].[sp_sysutility_mi_upload]', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysutility_mi_upload]
WITH EXECUTE AS OWNER
AS
BEGIN
   SET NOCOUNT ON;
   
   -- Check if the instance is enrolled
   IF ( 0 = (select [dbo].[fn_sysutility_ucp_get_instance_is_mi]()) )
   BEGIN
	  RAISERROR(37006, -1, -1)
     RETURN(1)
   END
   
   -- Check if Data Collector is enabled
   -- The following sproc will throw the correct error DC is disabled
   DECLARE @return_code INT;
   EXEC @return_code = [dbo].[sp_syscollector_verify_collector_state] @desired_state = 1
   IF (@return_code <> 0)
        RETURN (1)

   DECLARE @poll_delay_hh_mm_ss char(8)  = '00:00:10';
   DECLARE @start_delay_hh_mm_ss char(8)  = '00:00:10';
   
   DECLARE @start_time datetime2 = SYSUTCDATETIME();
   DECLARE @elapsed_time_ss INT
   
   DECLARE @collection_set_uid UNIQUEIDENTIFIER = N'ABA37A22-8039-48C6-8F8F-39BFE0A195DF';  
   DECLARE @collection_set_id INT = (SELECT collection_set_id FROM [dbo].[syscollector_collection_sets_internal]
                                    WHERE collection_set_uid = @collection_set_uid);
           
   DECLARE @is_upload_running INT;
   DECLARE @is_collection_running INT;

   -- If the collection set is running for some reason, wait for it
   -- to complete before instructing it to upload again.
   -- Assume that the collection is running before the loop starts
   SET @is_upload_running = 1;
   SET @is_collection_running = 1;
   
   -- Wait for the collection set to finish its previous execution        
   WHILE(1 = @is_collection_running OR 1 = @is_upload_running)
   BEGIN  
      -- Reset the while loop variables.  The sp will not update the values, if the collection
      -- is currently not running
      SET @is_upload_running = NULL;
      SET @is_collection_running = NULL;
      EXEC @return_code = [dbo].[sp_syscollector_get_collection_set_execution_status] 
            @collection_set_id = @collection_set_id,
            @is_collection_running = @is_collection_running OUTPUT,
            @is_upload_running = @is_upload_running OUTPUT
      IF (@@ERROR <> 0 OR @return_code <> 0) GOTO QuitWithError;

      -- Check to see if the collection is running before calling wait.
      -- It is more likely that it is not running, thus it is not optimal to wait.
      IF (1 = @is_collection_running OR 1 = @is_upload_running)
      BEGIN    
         SET @elapsed_time_ss = DATEDIFF(second, @start_time, SYSUTCDATETIME())
         RAISERROR ('Waiting for collection set to finish its previous run.  Total seconds spent waiting : %i', 0, 1, @elapsed_time_ss) WITH NOWAIT;
         WAITFOR DELAY @poll_delay_hh_mm_ss
      END

   END

   -- Grab the time before running the collection.  Use local time because later this value is used
   -- to find failures in the DC logs, which use local time.
   DECLARE @run_start_time datetime = SYSDATETIME();

   -- Start the collect and upload by invoking the run command
   RAISERROR ('Starting collection set.', 0, 1) WITH NOWAIT;
   EXEC @return_code = [msdb].[dbo].[sp_syscollector_run_collection_set] @collection_set_id = @collection_set_id
   IF (@@ERROR <> 0 OR @return_code <> 0) GOTO QuitWithError;    

   -- Allow the collection set to start
   RAISERROR ('Waiting for the collection set to kick off jobs.', 0, 1) WITH NOWAIT;
   WAITFOR DELAY @start_delay_hh_mm_ss
   
   -- Assume that the collection is running before the loop starts
   SET @is_upload_running = 1;
   SET @is_collection_running = 1;
   
   -- Wait for the collection set to finish it's previous execution        
   WHILE(1 = @is_collection_running OR 1 = @is_upload_running)
   BEGIN
      -- Reset the while loop variables.  The sp will not update the values, if the collection
      -- is currently not running
      SET @is_upload_running = NULL;
      SET @is_collection_running = NULL;
      
      -- Go ahead and wait on entry to the loop because it takes a 
      -- while for the collection set to finish collection
      SET @elapsed_time_ss = DATEDIFF(second, @start_time, SYSUTCDATETIME())
      RAISERROR ('Waiting for collection set to finish its previous run.  Total seconds spent waiting : %i', 0, 1, @elapsed_time_ss) WITH NOWAIT;
      WAITFOR DELAY @poll_delay_hh_mm_ss

      EXEC @return_code = [dbo].[sp_syscollector_get_collection_set_execution_status] 
            @collection_set_id = @collection_set_id,
            @is_collection_running = @is_collection_running OUTPUT,
            @is_upload_running = @is_upload_running OUTPUT
      IF (@@ERROR <> 0 OR @return_code <> 0) GOTO QuitWithError;
   END
   
   DECLARE @status_failure smallint = 2
   DECLARE @last_reported_status smallint = NULL
   
   -- Check if the collect/upload failed anytime after the call to run
   -- This is not precise in finding our exact run, but most of the time it will find our run
   -- What we really need to know is if the collect/upload failed
   -- There is a possibility that there are false positives (report failure, when our call to run passed)
   -- However, we are willing to risk it for simplicity.
   SELECT TOP 1 @last_reported_status = status 
   FROM msdb.dbo.syscollector_execution_log_internal 
      WHERE collection_set_id = @collection_set_id 
      AND parent_log_id IS NULL
      AND finish_time IS NOT NULL   
      AND start_time >= @run_start_time
      ORDER BY finish_time DESC

    IF (@last_reported_status = @status_failure)
    BEGIN
        RAISERROR(37007, -1, -1)
        RETURN(1) -- Failure
    END

   Return(0);
    
   QuitWithError:
      RAISERROR ('An error occurred during execution.', 0, 1) WITH NOWAIT;
      RETURN (1);
END
GO


IF  (OBJECT_ID(N'[dbo].[fn_sysutility_mi_get_collect_script]') IS NOT NULL)
BEGIN
   RAISERROR('Dropping [dbo].[fn_sysutility_mi_get_collect_script] function', 0, 1)  WITH NOWAIT;
   DROP FUNCTION [dbo].[fn_sysutility_mi_get_collect_script];
END
GO
RAISERROR('Creating [dbo].[fn_sysutility_mi_get_collect_script] function', 0, 1)  WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysutility_mi_get_collect_script]()
   RETURNS NVARCHAR(MAX)
AS
BEGIN     
   RETURN '[Void] [System.Reflection.Assembly]::LoadWithPartialName("System.Data")
[Void] [System.Reflection.Assembly]::LoadWithPartialName("System.Diagnostics")
[Void] [System.Reflection.Assembly]::LoadWithPartialName("System.Collections")

###############################################################################
# Powershell settings
###############################################################################

# Generate an error if attempt to access a nonexisting variable
Set-PsDebug -Strict

# Global settings for what to do on a error, warning, or verbose call
# Change these settings to change how this script writes output in the agent logs
# Settings also affects how SQL Agent reports success or failure in the script
# Options are:
#      Continue - Continue processing and notify the user 
#             - Agent reaction: step will report success, and
#                           log will include message
#      Inquire - Stop processing and ask the user how it should proceed 
#             - Agent reaction: step fails with "cannot invoke this function"
#                           the Agent PS provider does not implement this
#      SilentlyContinue - Continue processing without notifying the user
#                    - Agent reaction:  will not fail step 
#                                 and will not log any message
#      Stop - Stop processing when an action occurs
#          - Agent reaction: step will fail with message in log
$VerbosePreference = "SilentlyContinue"
$WarningPreference = "Continue"
$ErrorActionPreference = "Stop"

###############################################################################
# Global Variables
###############################################################################

# The following line uses SQL Agent tokens to set the server name
# ESCAPE_SQUOTE(SRVR) with a $ sign in front is a special token to SQL Agent
# When the job is run, SQL Agent will expand the string to the server name
# Use single quotes so that PS considers the string a literal and will not
# try to expand the $ reference and the script will not fail in a test environment
$serverName = ''$(ESCAPE_SQUOTE(SRVR))''

# Currently the best way to tell if the script is running in Agent
# is to check if the console is not the ConsoleHost.  The Powershell
# subsystem for Agent has no console and thus writing to the host directly
# does not show up in the Agent logs.
$isNotConsole = ($host.Name -ne "ConsoleHost")

$connection = $null
$transaction = $null
$isVistaOrXPSp2OrHigher = $null
$sleepTimeoutSeconds = 5
$directoryNameToDeviceId=$null

$cpuStageTableName = "[msdb].[dbo].[sysutility_mi_cpu_stage_internal]"
$cpuStageDataTable = $null
$cpuNumProcessorsColumnName = "num_processors"
$cpuNameColumnName = "cpu_name"
$cpuCaptionColumnName = "cpu_caption"
$cpuFamilyIdColumnName = "cpu_family_id"  
$cpuArchitectureIdColumnName = "cpu_architecture_id"
$cpuMaxClockSpeedColumnName = "cpu_max_clock_speed"
$cpuClockSpeedColumnName = "cpu_clock_speed"
$cpuL2CacheSizeColumnName = "l2_cache_size"
$cpuL3CacheSizeColumnName = "l3_cache_size"
# Start of collection column names
$cpuInstanceProcessorUsageStartTicks = "instance_processor_usage_start_ticks"
$cpuInstanceCollectTimeStartTicks = "instance_collect_time_start_ticks"
$cpuComputerProcessorIdleStartTicks = "computer_processor_idle_start_ticks"
$cpuComputerCollectTimeStartTicks = "computer_collect_time_start_ticks"
# End of collection column names
$cpuInstanceProcessorUsageEndTicks = "instance_processor_usage_end_ticks"
$cpuInstanceCollectTimeEndTicks = "instance_collect_time_end_ticks"
$cpuComputerProcessorIdleEndTicks = "computer_processor_idle_end_ticks"
$cpuComputerCollectTimeEndTicks = "computer_collect_time_end_ticks"


$volumeStageTableName = "[msdb].[dbo].[sysutility_mi_volumes_stage_internal]"
$volumeStageDataTable = $null
$volumeDeviceIdColumnName = "volume_device_id"
$volumeNameColumnName = "volume_name"
$volumeCapacityColumnName = "capacity_mb"
$volumeFreeSpaceColumnName = "free_space_mb"

$smoStageTableName = "[msdb].[dbo].[sysutility_mi_smo_stage_internal]"
$smoStageDataTable = $null
$smoTypeColumnName = "object_type"
$smoUrnColumnName = "urn"
$smoPropertyNameColumnName = "property_name"
$smoPropertyValueColumnName = "property_value"

###############################################################################
# Functions that help with handling output to SQL Agent
#
# Sql Agent PS provider does not write output to the log from
# the warnings, errors, and verbose Write cmdlets.  The following
# functions wrap these cmdlets for execution as an agent job step.
###############################################################################

# This function is a helper function throws an exception if the passed in object 
# is null or empty.  The intent is to mimic the PowerShell version 2.0 parameter 
# validation function with the same name. The paramter validation is available 
# in 2.0 or higher, but this script can run in 1.0 or 2.0 runtime environment.
function ValidateNotNullOrEmpty($object)
{
    if(($object -eq $null) -or ($object -eq ""))
    {
        throw "The argument is null or empty."
    }
}

# This function helps control control flow for the agent step context
# When running within agent, there are different semantics for writing
# errors, warnings, and messages.  In addition, when running inside an
# agent step, the script will automatically collect and stage data.
# However, if the script is loaded in a PS environment outside of 
# agent, the script will not automatically start to collect and stage data
#
# Returns True if the script is run inside an agent step
#         False if the script is run outside an agent step 
function Get-IsAgentStep
{
   $global:isNotConsole
}

function Write-AgentLog([String] $prefix, [String] $printString, [String] $preference)
{
   if((Get-IsAgentStep) -and ($preference -ne "SilentlyContinue"))
   {
      [Console]::Error.WriteLine($prefix + $printString)
   }
}

function Get-PrintString ($object)
{
   ValidateNotNullOrEmpty $object
   
   $date = Get-Date -DisplayHint Time
   $printString = $date.ToString() + " : " +  $object.ToString()
   $printString
}

function Write-ScriptVerbose ($object)
{
   $printString = Get-PrintString $object
   
   Write-AgentLog "VERBOSE : " $printString $VerbosePreference 
   Write-Verbose $printString
}


function Write-ScriptWarning ($object)
{
   $printString = Get-PrintString $object
   
   Write-AgentLog "WARNING : " $printString $WarningPreference 
   Write-Warning $printString
}

function Write-ScriptError ($object)
{
   $printString = Get-PrintString $object
   
   Write-AgentLog "ERROR : " $printString $ErrorActionPreference 
   Write-Error $printString   
}

function Resolve-Error ($ErrorRecord=$Error[0])
{
   $errorString = $ErrorRecord | Format-List * -Force  | Out-String
   Write-ScriptWarning $errorString
   $errorString = $ErrorRecord.InvocationInfo | Format-List * | Out-String
   Write-ScriptWarning $errorString
   $Exception = $ErrorRecord.Exception
   
   # Print the entire stack of exceptions
   for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException))
   {   Write-ScriptWarning ("$i" * 80)
       $errorString = $Exception | Format-List * -Force  | Out-String
      Write-ScriptWarning $errorString
   }
}


###############################################################################
# Connection Functions help to send queries to and manage the connection 
# to the server .
###############################################################################
function Get-Connection
{
   if($global:serverName.Contains(''ESCAPE_SQUOTE(SRVR)''))
   {
      throw "The global variable serverName has not been set."
   }
   if($global:connection -eq $null)
   {
      Write-ScriptVerbose "Opening connection to $global:serverName"
      $connString="Application Name=SQL Server Utility Managed Instance;Server=$global:serverName;Database=msdb;Trusted_Connection=True;"
      
      $global:connection = New-Object System.Data.SqlClient.SqlConnection
      $global:connection.ConnectionString = $connString
      [Void]$global:connection.Open()
      Write-ScriptVerbose "Opened connection with connection string:`n $connString"
   }
   $global:connection
}

function Remove-Connection
{
   if($global:connection -ne $null)
   {
      $dataSource=$global:connection.DataSource
      Write-ScriptVerbose "Closing and disposing connection to $dataSource"
      [Void]$global:connection.Close()
      [Void]$global:connection.Dispose()
      Write-ScriptVerbose "Connection is closed and disposed"
   }
   $global:connection = $null
}

function Invoke-BeginTransaction([string] $tranName)
{
   Write-ScriptVerbose "Opening transaction"
   $sqlConnection = Get-Connection
   $global:transaction = $sqlConnection.BeginTransaction($tranName)
}

function Invoke-CommitTransaction
{
   if($global:transaction -ne $null)
   {
      Write-ScriptVerbose "Committing transaction"
      $global:transaction.Commit()
      $global:transaction.Dispose()
      $global:transaction = $null
   }
}

function Invoke-RollbackTransaction
{
   if($global:transaction -ne $null)
   {
      Write-ScriptVerbose "Rolling back transaction"
      $global:transaction.Rollback() 
      $global:transaction.Dispose()
      $global:transaction = $null
   }
}

function Invoke-SubmitSqlCommandNonQuery([string] $query)
{
   ValidateNotNullOrEmpty $query
      
   Write-ScriptVerbose "Submitting as NonQuery : $query"
   $TsqlCommand = New-Object System.Data.SqlClient.SqlCommand;
   $TsqlCommand.CommandText = $query
   $TsqlCommand.CommandType = "Text";
   $TsqlCommand.Transaction = $global:transaction
   
   $TsqlCommand.Connection = Get-Connection
   $TsqlCommand.CommandTimeout = 0
   [Void] $TsqlCommand.ExecuteNonQuery()
}

function Get-SqlDataTable([string] $query)
{
   ValidateNotNullOrEmpty $query
   
   Write-ScriptVerbose "Requesting data table for : $query"
   $sqlConnection = Get-Connection
    $dataAdapter = New-Object System.Data.SqlClient.SqlDataAdapter($query, $sqlConnection)
    $dataTable = New-Object System.Data.DataTable
    $rowsFilled = $dataAdapter.Fill($dataTable)
   Write-ScriptVerbose "Query added $rowsFilled rows to the data table"
    # return the data table.  We need to wrap the variable because PS will
   # return data rows otherwise.
   return @(,($dataTable))
} 

function Invoke-BulkCopyCommand([System.Data.DataTable] $dataTableData)
{
   ValidateNotNullOrEmpty $dataTableData
   
   $opt = [System.Data.SqlClient.SqlBulkCopyOptions] 

   # Obtain a TableLock
   # But do not (use) Default (options), KeepIdentity, CheckConstraints, KeepNulls
   #  FireTriggers,  UseInternalTransaction
   $bulkOptions = $opt::none -bxor ("TableLock" -as $opt) 

   $tabName=$dataTableData.TableName
   Write-ScriptVerbose "Bulk copying data table : $tabName"
   $sqlConnection = Get-Connection
   $bulkCopy = new-object Data.SqlClient.SqlBulkCopy $sqlConnection, $bulkOptions,  $global:transaction
   $bulkCopy.DestinationTableName = $dataTableData.TableName
   
   #Map the columns so that the computed columns are skipped in the upload
   foreach($col in $dataTableData.Columns)
   {
      [Void] $bulkCopy.ColumnMappings.Add($col.ColumnName, 
                                 $col.ColumnName)
   }
   [Void] $bulkCopy.WriteToServer($dataTableData)
   
}

###############################################################################
# Short Helper Functions 
###############################################################################

function Get-DefaultIfNull($object, $default)
{
    if($object -eq $null) 
    {
        $default
    } 
    else
    {
        $object
    }
}

function Get-StringDefaultIfNull([String] $object)
{
    Get-DefaultIfNull $object ""
}

function Get-NumericDefaultIfNull($object)
{
    Get-DefaultIfNull $object 0
}

function Get-ProcessId
{
   $result = Get-SqlDataTable "SELECT SERVERPROPERTY(''ProcessID'') AS ProcessId"  | %{ $_.Rows }
   $result.ProcessId
}

function Get-IsWmiVolumeQueryAvailable
{
   if($global:isVistaOrXPSp2OrHigher -eq $null)
   {
      $osVersion = [System.Environment]::OsVersion.Version
      $global:isVistaOrXPSp2OrHigher = ($osVersion.Major -ge 6 -or ($osVersion.Major -ge 5 -and $osVersion.Minor -ge 2))
   }
   Write-ScriptVerbose "This computer is Vista or XP Sp2 or higher value is $global:isVistaOrXPSp2OrHigher"
   $global:isVistaOrXPSp2OrHigher
}

# Trims the volume name to <drive_letter>: format. 
# Reason: Data collection using WMI on different OS returns diffrent volume formats
# E.g. Win32_LogicalDisk on WIN2K3 returns c: and Win32_Volume on WIN2K8 returns c:\

function Get-FormattedVolumeName([String] $volumeName)
{
   [String] $volumeName = Get-StringDefaultIfNull $volumeName
   
   Write-ScriptVerbose "Formatting volume name $volumeName"
   if($volumeName.EndsWith("\"))
   {
      $volumeName = $volumeName.SubString(0,$volumeName.Length - 1)
   }
   
   Write-ScriptVerbose "Formatted volume name to $volumeName"
   $volumeName
}

function Get-MountPointDictionary()
{
	if($global:directoryNameToDeviceId -eq $null)
	{
		$global:directoryNameToDeviceId=@{}
		(Get-Wmiobject Win32_MountPoint) | 
		%{ $directory=$_.Directory.Replace("Win32_Directory.Name=", "").Replace("`"", "").Replace("\\", "\")
		   $deviceId=$_.Volume.Replace("Win32_Volume.DeviceID=`"", "").Replace("`"", "").Replace("\\", "\")
		   $global:directoryNameToDeviceId[$directory]=$deviceId
		}
	}
	return $global:directoryNameToDeviceId
}

# The following function returns a directory name that maps to a volume device
# based on longest match.  It is not exact because a file can have a long
# convoluted path that pass through many mount point references
# However, it will find the most common use case for mount points
function Get-MountPointName([String] $fileName)
{
    [String] $fileName = Get-StringDefaultIfNull $fileName

    $longestMatch = ""
    $dict = Get-MountPointDictionary
    foreach($directory in $dict.Keys)
    {
        if($fileName.StartsWith($directory, [System.StringComparison]::OrdinalIgnoreCase))
        {
            if($directory.Length -gt $longestMatch.Length)
            {
                $longestMatch = $directory
            }
        }
    }
    return $longestMatch
}


function Get-DeviceIdFromMountPointName([String] $mountPointDirectory)
{
    [String] $mountPointDirectory = Get-StringDefaultIfNull $mountPointDirectory

    $dict = Get-MountPointDictionary
    
    $dict[$mountPointDirectory]
} 

function Get-MegabytesFromBytes ([Uint64] $bytes)
{
   [Uint64] $bytes = Get-NumericDefaultIfNull $bytes
      
   Write-ScriptVerbose "Converting $bytes bytes to megabytes"
   $oneMB = 1048576
   [UInt64] ($bytes / $oneMB)  # No fractional MBs
}

function Get-ShouldCollectCpu
{   
   if( ($global:cpuStageDataTable -eq $null) -or ($global:cpuStageDataTable.Rows.Count -eq 0))
   {
      Write-ScriptVerbose "The cpu staging table is null or empty.  Get-ShouldCollectCpu returning true"
      # return True and exit early
      return $true
   }
   else
   {
      $dataRow = $global:cpuStageDataTable.Rows[0]
      
      # return the value of the disjunction
      $dataRow[$cpuInstanceProcessorUsageStartTicks] -eq 0 -or
         $dataRow[$cpuInstanceCollectTimeStartTicks] -eq 0 -or
         $dataRow[$cpuComputerProcessorIdleStartTicks] -eq 0 -or
         $dataRow[$cpuComputerCollectTimeStartTicks] -eq 0
   }
}

###############################################################################
# Staging Functions that construct DataTables based on the different types of 
# data collection
###############################################################################
function Add-StageCpuRow
{
   param ([Int32] $numProcessors, [String] $cpuName, [String] $cpuCaption, [UInt16] $cpuFamily, 
      [UInt16] $architecture, [UInt32] $cpuMaxClockSpeed, [UInt32] $clockSpeed, 
      [UInt32] $l2CacheSize, [UInt32] $l3CacheSize, 
      [UInt64] $instanceProcessorUsage, [Int64] $instanceCollectTime, 
      [UInt64] $computerIdleTime, [UInt64] $computerCollectTime)

   begin
   {
      # This function update the Cpu table in-place by
      # first querying the server for the previous collection
      # information
      if($global:cpuStageDataTable -eq $null)
      {
         $query = "SELECT  
                  $cpuNumProcessorsColumnName,
                  $cpuNameColumnName,
                  $cpuCaptionColumnName,
                  $cpuFamilyIdColumnName,
                  $cpuArchitectureIdColumnName,
                  $cpuMaxClockSpeedColumnName,
                  $cpuClockSpeedColumnName,
                  $cpuL2CacheSizeColumnName,
                  $cpuL3CacheSizeColumnName,
                  $cpuInstanceProcessorUsageStartTicks,
                  $cpuInstanceCollectTimeStartTicks,
                  $cpuComputerProcessorIdleStartTicks,
                  $cpuComputerCollectTimeStartTicks,
                  $cpuInstanceProcessorUsageEndTicks,
                  $cpuInstanceCollectTimeEndTicks,
                  $cpuComputerProcessorIdleEndTicks,
                  $cpuComputerCollectTimeEndTicks
               FROM $global:cpuStageTableName"
         $global:cpuStageDataTable = Get-SqlDataTable $query
                  
         # If the data table is null, then there is no
         # data on the server and the table needs to be initialized
         if($global:cpuStageDataTable -eq $null)
         {
            Write-ScriptVerbose "Database returned no rows for cpu table. Creating table definition"
            $global:cpuStageDataTable = New-Object System.Data.DataTable ($global:cpuStageTableName)
            
            ($cpuNumProcessorsColumnName, [UInt16]), 
            ($cpuNameColumnName,[string]), 
            ($cpuCaptionColumnName,[string]), 
            ($cpuFamilyIdColumnName, [UInt16]), 
            ($cpuArchitectureIdColumnName, [UInt16]), 
            ($cpuMaxClockSpeedColumnName, [UInt32]), 
            ($cpuClockSpeedColumnName, [UInt32]), 
            ($cpuL2CacheSizeColumnName, [UInt32]), 
            ($cpuL3CacheSizeColumnName, [UInt32]), 
            ($cpuInstanceProcessorUsageStartTicks, [UInt64]), 
            ($cpuInstanceCollectTimeStartTicks, [Int64]), 
            ($cpuComputerProcessorIdleStartTicks, [UInt64]), 
            ($cpuComputerCollectTimeStartTicks, [UInt64]), 
            ($cpuInstanceProcessorUsageEndTicks, [UInt64]), 
            ($cpuInstanceCollectTimeEndTicks, [Int64]),
            ($cpuComputerProcessorIdleEndTicks, [UInt64]), 
            ($cpuComputerCollectTimeEndTicks, [UInt64]) | 
            foreach { ,
               $column =  new-object Data.DataColumn ($_)
               $global:cpuStageDataTable.Columns.Add($column) 
            }
         }
         $global:cpuStageDataTable.TableName = $global:cpuStageTableName
      }
      
      
      # If there is one row in the table, it is the data that the query returned
      # update the start values to be the old end values
      if ($global:cpuStageDataTable.Rows.Count -eq 1)
      {
         Write-ScriptVerbose "Stage table contains one row. Swapping end to start values."
         $dataRow = [System.Data.DataRow] $global:cpuStageDataTable.Rows[0]
   
         # The previous end values become the start values
         $dataRow[$cpuInstanceProcessorUsageStartTicks] = $dataRow[$cpuInstanceProcessorUsageEndTicks]
         $dataRow[$cpuInstanceCollectTimeStartTicks] = $dataRow[$cpuInstanceCollectTimeEndTicks]
         $dataRow[$cpuComputerProcessorIdleStartTicks] = $dataRow[$cpuComputerProcessorIdleEndTicks]
         $dataRow[$cpuComputerCollectTimeStartTicks] = $dataRow[$cpuComputerCollectTimeEndTicks]
      } 
      else
      {
         # There were no rows in the table or too many rows
         # Either way, the data needs to be cleared and updated
         # with the new information
         $rowCount = $global:cpuStageDataTable.Rows.Count
         Write-ScriptVerbose "Number of rows in data table is $rowCount"   
         
         Write-ScriptVerbose "Clearing stage table and marking start values with 0"
         [Void] $global:cpuStageDataTable.Clear()
         $dataRow = [System.Data.DataRow] $global:cpuStageDataTable.NewRow()
         $global:cpuStageDataTable.Rows.Add($dataRow)
         
         # There are no start values
         $dataRow[$cpuInstanceProcessorUsageStartTicks] = 0
         $dataRow[$cpuInstanceCollectTimeStartTicks] = 0
         $dataRow[$cpuComputerProcessorIdleStartTicks] = 0
         $dataRow[$cpuComputerCollectTimeStartTicks] = 0
      }
   }
   
   process
   {
      # Powershell 2.0 does not default typed parameters that are $null
      # So, the function has to set the defaults for the null parameters
      [Int32] $numProcessors = Get-NumericDefaultIfNull $numProcessors
      [String] $cpuName = Get-StringDefaultIfNull $cpuName
      [String] $cpuCaption = Get-StringDefaultIfNull $cpuCaption
      [UInt16] $cpuFamily = Get-NumericDefaultIfNull $cpuFamily
      [UInt16] $architecture = Get-NumericDefaultIfNull $architecture
      [UInt32] $cpuMaxClockSpeed = Get-NumericDefaultIfNull $cpuMaxClockSpeed
      [UInt32] $clockSpeed = Get-NumericDefaultIfNull $clockSpeed
      [UInt32] $l2CacheSize = Get-NumericDefaultIfNull $l2CacheSize
      [UInt32] $l3CacheSize = Get-NumericDefaultIfNull $l3CacheSize
      [UInt64] $instanceProcessorUsage = Get-NumericDefaultIfNull $instanceProcessorUsage
      [Int64] $instanceCollectTime = Get-NumericDefaultIfNull $instanceCollectTime
      [UInt64] $computerIdleTime = Get-NumericDefaultIfNull $computerIdleTime
      [UInt64] $computerCollectTime = Get-NumericDefaultIfNull $computerCollectTime
      
   
      # instanceCollectTime comes in as an signed int, make sure it is not neg
      if($instanceCollectTime -lt 0)
      {
         $instanceCollectTime = 0
      }
      
      # numProcessors comes in as an signed int, make sure it is not neg
      if($numProcessors -lt 0)
      {
         $numProcessors = 0
      }
   
      # Add the collected information
      Write-ScriptVerbose "Adding collected information to data table"
      $dataRow[$cpuNumProcessorsColumnName] = $numProcessors
      $dataRow[$cpuNameColumnName] = $cpuName
      $dataRow[$cpuCaptionColumnName] = $cpuCaption
      $dataRow[$cpuFamilyIdColumnName] = $cpuFamily
      $dataRow[$cpuArchitectureIdColumnName] = $architecture
      $dataRow[$cpuMaxClockSpeedColumnName] = $cpuMaxClockSpeed
      $dataRow[$cpuClockSpeedColumnName] = $clockSpeed
      $dataRow[$cpuL2CacheSizeColumnName] = $l2CacheSize
      $dataRow[$cpuL3CacheSizeColumnName] = $l3CacheSize
      $dataRow[$cpuInstanceProcessorUsageEndTicks] = $instanceProcessorUsage
      $dataRow[$cpuInstanceCollectTimeEndTicks] = $instanceCollectTime
      $dataRow[$cpuComputerProcessorIdleEndTicks] = $computerIdleTime
      $dataRow[$cpuComputerCollectTimeEndTicks] = $computerCollectTime
   }
}



function Add-StageVolumeRow
{ 
   param ([String]$deviceId,
         [String] $volumeNameRaw, 
         [UInt64] $capacityBytes, 
         [UInt64] $freeSpaceBytes)
   begin
   {
      # Initialize the stage table
      if($global:volumeStageDataTable -eq $null)
      {   
         Write-ScriptVerbose "Volume data table is null, creating table definition."
         $global:volumeStageDataTable = New-Object System.Data.DataTable ($global:volumeStageTableName)
            
         ($global:volumeDeviceIdColumnName, [String]), 
         ($global:volumeNameColumnName, [String]), 
         ($global:volumeCapacityColumnName, [UInt64]), 
         ($global:volumeFreeSpaceColumnName, [UInt64])| 
         foreach { ,
            $column =  new-object Data.DataColumn ($_)
            $global:volumeStageDataTable.Columns.Add($column) 
         }
      }
   }
   process
   {
      [String] $deviceId = Get-StringDefaultIfNull $deviceId
      [String] $formattedName = Get-FormattedVolumeName $volumeNameRaw
      [UInt64] $freeSpaceMB = Get-MegabytesFromBytes $freeSpaceBytes
      [UInt64] $capacityMB = Get-MegabytesFromBytes $capacityBytes
      
      if ( ($formattedName -eq "") -or ($deviceId -eq ""))
      {
         Write-ScriptWarning "DeviceId is empty string, or volume name formatting results in empty string.  Skipping this row."
         Write-ScriptWarning "Device Id = $deviceId. Volume name raw = $volumeNameRaw."
         return # return early
      }

      Write-ScriptVerbose "Adding collected information to data table"
      $dataRow = [System.Data.DataRow] $global:volumeStageDataTable.NewRow()
      $dataRow[$global:volumeNameColumnName] = $formattedName
      $dataRow[$global:volumeFreeSpaceColumnName] = $freeSpaceMB
      $dataRow[$global:volumeCapacityColumnName] = $capacityMB
      $dataRow[$global:volumeDeviceIdColumnName] = $deviceId
      Write-ScriptVerbose "Adding row to table"
      
      [Void] $global:volumeStageDataTable.Rows.Add($dataRow)
   }
}


function Add-StageSmoRow
{
   param ([Int32] $type, [String] $objUrn, [String] $propertyName, [object] $value)
   begin
   {      
      # Initialize the stage table
      if($global:smoStageDataTable -eq $null)
      {   
         Write-ScriptVerbose "Smo data table is null, creating table definition."
         $global:smoStageDataTable = New-Object System.Data.DataTable ($global:smoStageTableName)
         
         ($global:smoTypeColumnName, [Int32]), 
         ($global:smoUrnColumnName, [String]), 
         ($global:smoPropertyNameColumnName, [String]), 
         ($global:smoPropertyValueColumnName, [Object]) | 
         foreach { ,
            $column =  new-object Data.DataColumn ($_)
            $global:smoStageDataTable.Columns.Add($column) 
         }   
      }
   }
   process
   {  
      # if the type, propertyName, or Urn is null, something is wrong, throw an exception
      ValidateNotNullOrEmpty $type          
      ValidateNotNUllOrEmpty $propertyName
      ValidateNotNUllOrEmpty $objUrn
      
      # value can be null sometimes, which is fine.  Just throw the row out.
      if ( $value -eq $null )
      {
         Write-ScriptWarning "The value for property $propertyName is null. This property will not be added."
         Write-ScriptWarning "(objUrn = $objUrn)) (type = $type)) (propertyName = $propertyName)) (value = $value))"
         return # return early
      }
      
      Write-ScriptVerbose "Adding collected information for $propertyName to data table"
      $dataRow = [System.Data.DataRow] $global:smoStageDataTable.NewRow()
      $dataRow[$global:smoTypeColumnName] = $type
      $dataRow[$global:smoUrnColumnName] = $objUrn
      $dataRow[$global:smoPropertyNameColumnName] = $propertyName
      $dataRow[$global:smoPropertyValueColumnName] = $value
      
      $global:smoStageDataTable.Rows.Add($dataRow)
      
   }
}
   

###############################################################################
# Collection functions
###############################################################################
function Collect-CpuData
{

   &{ # PS Try
      # Get the Instance-level Performance Data.  An instance is identified 
      # by its process-id
      $processId = Get-ProcessId;
      
      Write-ScriptVerbose "Get WMI percent cpu time for process id = $processId"
      # Get the total processor time from the wmi object
      # PercentProcessorTime is bad property name, it is actually counting the 
      # total number of ticks (100NS based)
      # the instance has spent on processors.
      (Get-WmiObject Win32_PerfRawData_PerfProc_Process -filter "IDProcess = ''$processId''") | 
      %{ $instanceProcessorUsage = $_.PercentProcessorTime };
      
      Write-ScriptVerbose "Get current time for collection time"
      # Find the current number of ticks
      $instanceCollectTime = [DateTime]::UtcNow.Ticks
      
      Write-ScriptVerbose "Get WMI machine cpu time and time stamp"
      # Get the Machine-level Performance Data
      (Get-WmiObject Win32_PerfRawData_PerfOS_Processor -filter "Name = ''_Total''") |
      %{ $computerIdleTime = $_.PercentProcessorTime; 
      $computerCollectTime = $_.TimeStamp_Sys100NS };
      
      Write-ScriptVerbose "Get WMI cpu details"
      # Get the processor details
      (Get-WmiObject Win32_Processor) | 
      %{$cpuName = $_.Name;
      $cpuCaption = $_.Caption;
      $cpuFamily = $_.Family; 
      $architecture = $_.Architecture; 
      $cpuMaxClockSpeed = $_.MaxClockSpeed;
      $clockSpeed = $_.CurrentClockSpeed; 
      $l2CacheSize = $_.L2CacheSize; 
      $l3CacheSize = $_.L3CacheSize };
      
      [Int32] $numProcessors = [System.Environment]::ProcessorCount
      
      Write-ScriptVerbose "Add row to cpu information"
      Add-StageCpuRow   $numProcessors $cpuName $cpuCaption $cpuFamily $architecture $cpuMaxClockSpeed $clockSpeed $l2CacheSize $l3CacheSize $instanceProcessorUsage $instanceCollectTime $computerIdleTime $computerCollectTime
      
      $global:cpuStageDataTable
   }
   # PS Catch
   trap [Exception]
   {
      Resolve-Error
      Write-ScriptError "Caught exception while collecting cpu properties.  A WMI query might have failed."
   }   
   
}

function Collect-VolumeData
{
   &{ # PS Try
      if( Get-IsWmiVolumeQueryAvailable )
      {
         # A null DriveLetter indicates that the volume is a mount point
         # Casting DriveLetter to [Boolean] results in False if it is null 
         Write-ScriptVerbose "Collecting volume information using Win32_Volume"
         (Get-Wmiobject Win32_Volume -filter "DriveType = 3") | 
         %{ Add-StageVolumeRow $_.DeviceId $_.Name $_.Capacity $_.FreeSpace }
      }
      else
      {
         # logical disk only collects disk information, not mount point information
         # hence passing in false as is_mount_point parameter
         Write-ScriptVerbose "Collecting volume information using Win32_LogicalDisk"
         (Get-Wmiobject Win32_LogicalDisk -filter "DriveType = 3") | 
         %{ Add-StageVolumeRow $_.DeviceId $_.Name $_.Size $_.FreeSpace }
      }
      
      $global:volumeStageDataTable
   }
   # PS Catch
   trap [Exception]
   {
      Resolve-Error
      Write-ScriptError "Caught exception while collecting volume properties.  A WMI query might have failed."
   }   
}

function Collect-SmoData
{
   
   &{ # PS try
      $sqlConnection = Get-Connection
      $serverConnection = New-Object Microsoft.SqlServer.Management.Common.ServerConnection $sqlConnection
      $server = New-Object Microsoft.SqlServer.Management.Smo.Server($serverConnection);
      
      # remove configurations from this table
      $objectsQuery = "SELECT object_type, sfc_query 
               FROM [msdb].[dbo].[sysutility_mi_smo_objects_to_collect_internal] AS sfc_queries";
      
      $sfcQueries = Get-SqlDataTable $objectsQuery | %{ $_.Rows }
      
      foreach ($sfcQueryRow in $sfcQueries)
      {
         [Int32] $object_type = $sfcQueryRow.object_type;
         $sfcQueryString = $sfcQueryRow.sfc_query.ToString();
   
         Write-ScriptVerbose "Retrieving list of properties to collect"
         $propertiesQuery = "SELECT property_name 
                        FROM [msdb].[dbo].[sysutility_mi_smo_properties_to_collect_internal] 
                        WHERE object_type ="+ $object_type.ToString();
         $properties = Get-SqlDataTable $propertiesQuery | %{ $_.Rows } | foreach { $_.property_name };
         
         Write-ScriptVerbose "Collecting smo information for sfc query $sfcQueryString"
         $oq = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SfcObjectQuery($server);
         $exp = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SfcQueryExpression($sfcQueryString);
         
         &{ # PS try
         
            # The following call is not itempotent.  The code does not run the same
            # in debug mode. If you are running in debug mode, any value display
            # invalidates the foreach statement.
            $en = $oq.ExecuteIterator($exp, $null, $null);   
            
            foreach($obj in $en)
            {
               $objUrn = $obj.Urn.ToString();
               Write-ScriptVerbose "Collecting smo information for urn $objUrn"
               
               # For each property get the value and insert it into the smo stage data table
               # the statment $obj.$_ retrieves the propety value from the object
               # going through the PS provider.  If the property is not found or throws an
               # exception from the SMO side, the PS provider wraps the property and returns
               # an empty value.
               $properties | 
               %{ 
                  if ($_ -eq "ProcessorUsage")
                  {
                      # for ProcessorUsage, we are in fact collecting the
                      # the data by ourselves in our own staging table.
                      # and we do not want to call SMO as this property
                      # may not exist on downlevel server.
                      # so here, we put a dummy value and later during upload
                      # we replace it with our real value.
                      # Note that we a similar situation for VolumeFreeSpace
                      # but the solution is different. For VolumeFreeSpace property
                      # it is not put in the sysutility_mi_smo_properties_to_collect_internal
                      # and we collect through other means and then do a join on the UCP
                      # side, versus for ProcessorUsage, we put the property in the list
                      # and during MI collection, we replace it with our own value.
                      # The difference is inconsistent and we should change them to behave
                      # the same in future releases.
                      Add-StageSmoRow $object_type $objUrn $_ [object]0
                  }
                  else
                  {
                      Add-StageSmoRow $object_type $objUrn $_ $obj.$_ 
                  }
                  
                  # if this property is FileName, we append volume/mount point info.
                  if($_ -eq "FileName")
                  {
                     Write-ScriptVerbose "Property is FileName, getting volume information"
                     [String] $mountPointName = Get-MountPointName $obj.FileName
                     Add-StageSmoRow $object_type $objUrn "mount_point_name" $mountPointName 
                     
                     [String] $deviceId = Get-DeviceIdFromMountPointName $mountPointName
                     Add-StageSmoRow $object_type $objUrn "volume_device_id" $deviceId
                  }
               }
               
               $psPath = Convert-UrnToPath $objUrn
               ("powershell_path", $psPath),
               ("parent_name", $obj.Parent.Name),              # If no Parent exists, Ps will return null
               ("grandparent_name", $obj.Parent.Parent.Name) | # If no Parent.Parent exists, Ps will return null
                  %{ ,
                     $propertyName = $_[0]                     
                     [String] $value = $_[1]    # Cast to string results in $null values becoming ""
                     if($value -ne "")
                     {
                        Add-StageSmoRow $object_type $objUrn $propertyName $value
                     }
                  }  
            }
         } # PS catch exception
         trap [Exception]
         {
            Resolve-Error
            Write-ScriptError "Caught exception while collecting smo properties."
         }   
      }
      $global:smoStageDataTable
   } # PS catch exception
   trap [Exception]
   {
      Resolve-Error
          Write-ScriptError "Caught exception while collecting smo properties."
   }   
}

###############################################################################
# Functions that mange the server tables by clearing and loading collected data 
###############################################################################
function Clear-AllStagedData
{
   # TRUNCATE TABLE removes all rows from a table without logging the 
   # individual row deletes.

   $cpuClearQuery = "TRUNCATE TABLE $global:cpuStageTableName; "
   $volumeClearQuery = "TRUNCATE TABLE $global:volumeStageTableName; "
   $smoClearQuery = "TRUNCATE TABLE $global:smoStageTableName; "
   
   Invoke-SubmitSqlCommandNonQuery "$cpuClearQuery $volumeClearQuery $smoClearQuery"
}

function Collect-AllStagedData
{

   Collect-CpuData | Out-Null
      
   # Should we collect cpu data again?
   # This will happen if the script is
   # run when there is no data yet in
   # the cpu staging table.
   if(Get-ShouldCollectCpu)
   {
      #Wait for some time to pass
      Write-ScriptVerbose "Waiting $sleepTimeoutSeconds seconds to collect cpu data."
      Start-Sleep -Seconds $sleepTimeoutSeconds
      #Collect the data again
      Collect-CpuData | Out-Null
   }
   
   Collect-SmoData | Out-Null
   Collect-VolumeData | Out-Null
}

function Save-AllStagedData
{
   Invoke-BulkCopyCommand $global:cpuStageDataTable
   Invoke-BulkCopyCommand $global:volumeStageDataTable
   Invoke-BulkCopyCommand $global:smoStageDataTable
}

function Invoke-StageData
{

   &{ # Try 
      
      Collect-AllStagedData
      
      Invoke-BeginTransaction
      
      Clear-AllStagedData
      Save-AllStagedData
      
      Invoke-CommitTransaction
      Remove-Connection
   }
   trap [Exception] # Catch
   {
      Write-ScriptWarning "Error occurred during execution of script."
      Write-ScriptWarning "Transaction will be rolled back."
      
      Resolve-Error
      
      Invoke-RollbackTransaction
      Remove-Connection
      
      # With ErrorActionPreference=Stop the following line will stop the script
      Write-ScriptError "Error.  Transaction was rolled back"
   }
}

if(Get-IsAgentStep)
{
   Invoke-StageData
}'
END
GO


-------------------------------------------------------------------------------
-- Initialize the collection for the managed instance by creating the
-- sysutility_mi jobs that do data collection and upload to the UCP.
--
-- Specifics: 
--  If the job does not exists, create the job with the schedule
--  If a utility job exists
--     keep the job so that schedule and history are retained
--     drop the job steps and then recreate them
-------------------------------------------------------------------------------
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_sysutility_mi_initialize_collection]') AND type in (N'P', N'PC'))
BEGIN
   RAISERROR('Dropping [dbo].[sp_sysutility_mi_initialize_collection] procedure', 0, 1)  WITH NOWAIT;
   DROP PROCEDURE [dbo].[sp_sysutility_mi_initialize_collection];
END
GO
RAISERROR('Creating [dbo].[sp_sysutility_mi_initialize_collection] procedure', 0, 1)  WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysutility_mi_initialize_collection]
WITH EXECUTE AS OWNER
AS
BEGIN
   SET NOCOUNT ON;

   DECLARE @null_column sysname = NULL  

   IF ( 0 = (select [dbo].[fn_sysutility_ucp_get_instance_is_mi]()) )
   BEGIN
	  RAISERROR(37006, -1, -1)
     RETURN(1)
   END

   BEGIN TRY
   
   DECLARE @tran_name NVARCHAR(32) = N'sysutility_mi_initialize_collection' -- transaction names can be no more than 32 characters

   BEGIN TRANSACTION @tran_name

      -- Common variables
      DECLARE @job_category sysname       = N'Utility - Managed Instance';
      DECLARE @job_category_id INT        = (SELECT category_id FROM msdb.dbo.syscategories WHERE name=@job_category AND category_class=1)
      DECLARE @server_name sysname        = N'(local)';
      DECLARE @step_id INT;
      DECLARE @step_name sysname;

      -- Collect and upload job variables
      DECLARE @collect_and_upload_job_name sysname              = N'sysutility_mi_collect_and_upload';
      DECLARE @collect_and_upload_job_description nvarchar(max) = N'Collect configuration and performance information';
      DECLARE @collect_and_upload_schedule_name sysname         = N'sysutility_mi_collect_and_upload';
      DECLARE @collect_and_upload_schedule_minutes int          = 15;                                                              
      DECLARE @collect_and_upload_job_id uniqueidentifier       = (SELECT jobs.job_id
                                                                   FROM [msdb].[dbo].[sysjobs] jobs
                                                                   WHERE jobs.name = @collect_and_upload_job_name 
                                                                   AND jobs.category_id = @job_category_id);
                                                                     
      -- start the job one minute past midnight + some random set of minutes between the schedule interval
      -- for agent jobs, a schedule's time is encoded in an integer.  The minutes portion
      -- are stored in the the 100s and 1000s digits.
      DECLARE @collect_and_upload_schedule_start_time int       = CAST((1 + RAND() * (@collect_and_upload_schedule_minutes + 1)) AS INT) * 100; 
      -- end the job one minute before the start time
      DECLARE @collect_and_upload_schedule_end_time int         = @collect_and_upload_schedule_start_time - 100;
      
      -- Dac performance collection job variables
      DECLARE @dac_perf_job_name sysname              = N'sysutility_mi_collect_performance';
      DECLARE @dac_perf_job_description nvarchar(max) = N'Collect performance information';
      DECLARE @dac_perf_schedule_name sysname         = N'sysutility_mi_collect_performance';
      DECLARE @dac_perf_schedule_seconds int          = 15;
      DECLARE @dac_perf_job_id uniqueidentifier       = (SELECT jobs.job_id
                                                         FROM [msdb].[dbo].[sysjobs] jobs
                                                         WHERE jobs.name = @dac_perf_job_name 
                                                         AND jobs.category_id = @job_category_id);

      -------------------------------------------------------------------------
      -- Create the category for the jobs
      -------------------------------------------------------------------------
      IF (@job_category_id IS NULL)
      BEGIN
         RAISERROR('Creating utility job category ... %s', 0, 1, @job_category)  WITH NOWAIT;
         EXEC msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=@job_category
      END
                   
      -------------------------------------------------------------------------
      -- Prepare the jobs
      -------------------------------------------------------------------------
      IF (@collect_and_upload_job_id IS NULL)
      BEGIN
         RAISERROR('Creating utility job ... %s', 0, 1, @collect_and_upload_job_name)  WITH NOWAIT;
         -- The job doesn't exist yet, create the job
         EXEC msdb.dbo.sp_add_job 
            @job_name=@collect_and_upload_job_name,     
            @enabled=0,                               -- create the job disabled
            @notify_level_eventlog=0, 
            @notify_level_email=0, 
            @notify_level_netsend=0, 
            @notify_level_page=0, 
            @delete_level=0,
            @description=@collect_and_upload_job_description, 
            @category_name=@job_category, 
            @job_id = @collect_and_upload_job_id OUTPUT
      
         RAISERROR('Adding job to jobserver ... %s' , 0, 1, @collect_and_upload_job_name)  WITH NOWAIT;
         EXEC msdb.dbo.sp_add_jobserver @job_id = @collect_and_upload_job_id, @server_name = @server_name
      END
      ELSE
      BEGIN
         
         RAISERROR('Disabling utility job ... %s', 0, 1, @collect_and_upload_job_name)  WITH NOWAIT;
         -- Disable the job for now.  Disable is itempotent
         EXEC msdb.dbo.sp_update_job @job_id=@collect_and_upload_job_id, @enabled=0
         
         RAISERROR('Clearing job steps for utility job ... %s', 0, 1, @collect_and_upload_job_name)  WITH NOWAIT;
         -- The job exists, delete all of the job steps prior to recreating them
         -- Passing step_id = 0 to sp_delete_jobstep deletes all job steps for the job
         EXEC msdb.dbo.sp_delete_jobstep @job_id=@collect_and_upload_job_id, @step_id = 0
      END
      
      IF (@dac_perf_job_id IS NULL)
      BEGIN
         RAISERROR('Creating utility job ... %s', 0, 1, @dac_perf_job_name)  WITH NOWAIT;
         -- The job doesn't exist yet, create the job
         EXEC msdb.dbo.sp_add_job 
            @job_name=@dac_perf_job_name, 
            @enabled=0,                                -- create the job disabled
            @notify_level_eventlog=0, 
            @notify_level_email=0, 
            @notify_level_netsend=0, 
            @notify_level_page=0, 
            @delete_level=0,
            @description=@dac_perf_job_description, 
            @category_name=@job_category, 
            @job_id = @dac_perf_job_id OUTPUT
      
         RAISERROR('Adding job to jobserver ... %s' , 0, 1, @dac_perf_job_name)  WITH NOWAIT;
         EXEC msdb.dbo.sp_add_jobserver @job_id = @dac_perf_job_id, @server_name = @server_name
      END
      ELSE
      BEGIN
         RAISERROR('Disabling utility job ... %s', 0, 1, @dac_perf_job_name)  WITH NOWAIT;
         -- Disable the job for now.  Disable is itempotent
         EXEC msdb.dbo.sp_update_job @job_id=@dac_perf_job_id, @enabled=0
         
         RAISERROR('Clearing job steps for utility job ... %s', 0, 1, @dac_perf_job_name)  WITH NOWAIT;
         -- The job exists, delete all of the job steps prior to recreating them
         -- Passing step_id = 0 to sp_delete_jobstep deletes all job steps for the job
         EXEC msdb.dbo.sp_delete_jobstep @job_id=@dac_perf_job_id, @step_id = 0
      END
      
      -------------------------------------------------------------------------
      -- Add the schedules for the jobs
      -------------------------------------------------------------------------

      IF  NOT EXISTS (SELECT name FROM msdb.dbo.sysschedules_localserver_view WHERE name = @collect_and_upload_schedule_name)
      BEGIN
         RAISERROR('Creating schedule ... %s', 0, 1, @collect_and_upload_schedule_name)  WITH NOWAIT;
         EXEC dbo.sp_add_schedule
            @schedule_name = @collect_and_upload_schedule_name,            -- Schedule name
            @enabled=1,                                                    -- Enabled
            @freq_type = 4,                                                -- Daily
            @freq_interval = 1,                                            -- Recurs every 1 day
            @freq_subday_type = 0x4,                                       -- Frequency type is "minutes"
            @freq_subday_interval = @collect_and_upload_schedule_minutes,  -- Occurs every x minutes
            @active_start_time = @collect_and_upload_schedule_start_time,  -- Time to start the job
            @active_end_time = @collect_and_upload_schedule_end_time       -- Time to end the job
      END
      
      -- attach the schedule.  attach_schedule is itempotent if the job already has the schedule attached
      RAISERROR('Attaching schedule %s to job %s ...'  , 0, 1, @collect_and_upload_schedule_name, @collect_and_upload_job_name)  WITH NOWAIT;
      EXEC msdb.dbo.sp_attach_schedule @job_id=@collect_and_upload_job_id,@schedule_name=@collect_and_upload_schedule_name
      
      IF  NOT EXISTS (SELECT name FROM msdb.dbo.sysschedules_localserver_view WHERE name = @dac_perf_schedule_name)
      BEGIN
         RAISERROR('Creating schedule ... %s', 0, 1, @dac_perf_schedule_name)  WITH NOWAIT;
         EXEC dbo.sp_add_schedule
            @schedule_name = @dac_perf_schedule_name,            -- Schedule name
            @enabled=1,                                          -- Enabled
            @freq_type = 4,                                      -- Daily
            @freq_interval = 1,                                  -- Recurs every 1 day
            @freq_subday_type = 0x2,                             -- Frequency type is "seconds"
            @freq_subday_interval = @dac_perf_schedule_seconds   -- Occurs every x seconds
      END   
      
      -- attach the schedule.  attach_schedule is itempotent if the job already has the schedule attached
      RAISERROR('Attaching schedule %s to job %s ...'  , 0, 1, @dac_perf_schedule_name, @dac_perf_job_name)  WITH NOWAIT;
      EXEC msdb.dbo.sp_attach_schedule @job_id=@dac_perf_job_id,@schedule_name=@dac_perf_schedule_name     

      -------------------------------------------------------------------------
      -- Add the steps 
      -------------------------------------------------------------------------
      
      -------------------------------------------------------------------------      
      -- Steps for dac performance job
      -------------------------------------------------------------------------
      
      SET @step_id = 1;
      SET @step_name = N'Collect DAC execution statistics';
      RAISERROR('Adding step %i name %s to job %s', 0, 1, @step_id, @step_name, @dac_perf_job_name)  WITH NOWAIT;
      EXEC msdb.dbo.sp_add_jobstep 
         @job_id=@dac_perf_job_id, 
         @step_name=@step_name, 
         @step_id=1, 
         @cmdexec_success_code=0, 
         @on_success_action=1, 
         @on_fail_action=3, 
         @retry_attempts=0, 
         @retry_interval=0, 
         @os_run_priority=0, @subsystem=N'TSQL', 
         @command=N'EXEC [msdb].[dbo].[sp_sysutility_mi_collect_dac_execution_statistics_internal]', 
         @database_name=N'msdb', 
         @flags=0
            
      -------------------------------------------------------------------------      
      -- Steps for collect and upload job
      -------------------------------------------------------------------------
      
      -- Job step to record the current time on the managed instance.  This value will be included in the output of all of 
      -- the queries executed by the Utility collection set.  It will be used on the UCP to tie together all of the data from 
      -- a single execution of the data collection job. 
      -- 
      -- We create a table in tempdb to hold last batch start time and other transient data that does not 
      -- need to survive a service cycle.  Nothing uses this table except subsequent steps in this job; 
      -- it is safe to drop and recreate it here so that we do not need to worry about build-to-build 
      -- schema changes.
      
      SET @step_id = 1;
      SET @step_name = N'Record batch start time';
      RAISERROR('Adding step %i name %s to job %s', 0, 1, @step_id, @step_name, @collect_and_upload_job_name)  WITH NOWAIT;
      EXEC msdb.dbo.sp_add_jobstep @job_id=@collect_and_upload_job_id, @step_name=@step_name, 
            @step_id=@step_id, 
            @cmdexec_success_code=0, 
            @on_success_action=3, -- Go to next step
            @on_fail_action=2,    -- Quit the job reporting failure.  If something goes wrong here, something is messed up
            @retry_attempts=0, 
            @retry_interval=0, 
            @os_run_priority=0, 
            @subsystem=N'TSQL', 
            @command='
               USE tempdb
               
               IF OBJECT_ID (''[tempdb].[dbo].[sysutility_batch_time_internal]'') IS NOT NULL
               BEGIN
                  DROP TABLE [tempdb].[dbo].[sysutility_batch_time_internal];
               END;
               
               CREATE TABLE [tempdb].[dbo].[sysutility_batch_time_internal] (
                  latest_batch_time datetimeoffset(7) PRIMARY KEY NOT NULL
               );
                  
               -- The DC job needs to access the timestamp in this table, and it may not run under a login that 
               -- is mapped to a user in tempdb, so grant SELECT permissions to public.  The table contains no 
               -- sensitive data (only a single datetimeoffset value), so granting read permission to public 
               -- does create a security problem. 
               GRANT SELECT ON [tempdb].[dbo].[sysutility_batch_time_internal] TO PUBLIC;

               -- Save the start time for the current execution of the managed instance data collection job
               INSERT INTO [tempdb].[dbo].[sysutility_batch_time_internal] (latest_batch_time) VALUES (SYSDATETIMEOFFSET());', 
            @database_name=N'tempdb', 
            @flags=0
      
      DECLARE @psScript NVARCHAR(MAX) = (SELECT [dbo].[fn_sysutility_mi_get_collect_script]());
      SET @step_id = 2;
      SET @step_name = N'Stage Data Collected from PowerShell Script';  
      RAISERROR('Adding step %i name %s to job %s', 0, 1, @step_id, @step_name, @collect_and_upload_job_name)  WITH NOWAIT;
      EXEC msdb.dbo.sp_add_jobstep @job_id=@collect_and_upload_job_id, @step_name=@step_name, 
         @step_id=@step_id, 
         @cmdexec_success_code=0, 
         @on_success_action=3,   -- Go to next step
         @on_fail_action=2,      -- Quit the job reporting failure
         @retry_attempts=0, 
         @retry_interval=0, 
         @os_run_priority=0, 
         @subsystem=N'PowerShell', 
         @command=@psScript, 
         @database_name=N'master', 
         @flags=0

      SET @step_id = 3;
      SET @step_name = N'Upload to Utility Control Point';
      RAISERROR('Adding step %i name %s to job %s', 0, 1, @step_id, @step_name, @collect_and_upload_job_name)  WITH NOWAIT;
      EXEC msdb.dbo.sp_add_jobstep @job_id=@collect_and_upload_job_id, @step_name=@step_name, 
         @step_id=@step_id, 
         @cmdexec_success_code=0, 
         @on_success_action=1, -- Quit the job reporting success
         @on_fail_action=2, -- Quit the job reporting failure
         @retry_attempts=0, 
         @retry_interval=0, 
         @os_run_priority=0, 
         @subsystem=N'TSQL', 
         @command=N'EXEC [msdb].[dbo].[sp_sysutility_mi_upload]', 
         @database_name=N'msdb', 
         @flags=0
            
      -- Capture an initial snapshot of DAC statistics. This is not strictly necessary, but it will ensure that we 
      -- can calculate interval statistics immediately on the first execution of the every-15-second scheduled job. 
      RAISERROR('Collecting dac execution statistics for the first time ...', 0, 1, @collect_and_upload_job_name)  WITH NOWAIT;
      EXEC [msdb].[dbo].[sp_sysutility_mi_collect_dac_execution_statistics_internal]

      -- Enable the jobs
      RAISERROR('Enabling job ... %s', 0, 1, @collect_and_upload_job_name)  WITH NOWAIT;
      EXEC msdb.dbo.sp_update_job @job_id=@collect_and_upload_job_id, @enabled=1
      
      RAISERROR('Enabling job ... %s', 0, 1, @dac_perf_job_name)  WITH NOWAIT;   
      EXEC msdb.dbo.sp_update_job @job_id=@dac_perf_job_id, @enabled=1     
      
      -- Start the jobs
      RAISERROR('Starting job ... %s', 0, 1, @collect_and_upload_job_name)  WITH NOWAIT;
      EXEC msdb.dbo.sp_start_job @job_id=@collect_and_upload_job_id
      
      RAISERROR('Starting job ... %s', 0, 1, @dac_perf_job_name)  WITH NOWAIT;   
      EXEC msdb.dbo.sp_start_job @job_id=@dac_perf_job_id

  COMMIT TRANSACTION @tran_name
  
  END TRY
  
  BEGIN CATCH
        -- Roll back our transaction if it's still open
        IF (@@TRANCOUNT > 0)
        BEGIN
            ROLLBACK TRANSACTION;
        END;
 
        -- Rethrow the error.  Unfortunately, we can't retrow the exact same error number b/c RAISERROR 
        -- does not allow you to use error numbers below 13000.  We rethrow error 14684: 
        -- Caught error#: %d, Level: %d, State: %d, in Procedure: %s, Line: %d, with Message: %s
        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');
        RAISERROR (14684, -1, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
    END CATCH;

END
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_sysutility_mi_disable_collection]') AND type in (N'P', N'PC'))
BEGIN
   RAISERROR('Dropping [dbo].[sp_sysutility_mi_disable_collection] procedure', 0, 1)  WITH NOWAIT;
   DROP PROCEDURE [dbo].[sp_sysutility_mi_disable_collection];
END
GO
RAISERROR('Creating [dbo].[sp_sysutility_mi_disable_collection] procedure', 0, 1)  WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysutility_mi_disable_collection]
WITH EXECUTE AS OWNER
AS
BEGIN
   SET NOCOUNT ON;
   
   BEGIN TRY
   DECLARE @tran_name NVARCHAR(32) = N'sysutility_mi_disable_colle' -- transaction names can be no more than 32 characters

   BEGIN TRANSACTION @tran_name
      DECLARE @job_category sysname       = N'Utility - Managed Instance';
      DECLARE @job_category_id INT        = (SELECT category_id FROM msdb.dbo.syscategories WHERE name=@job_category AND category_class=1)
      
      DECLARE @collect_and_upload_job_name sysname              = N'sysutility_mi_collect_and_upload';
      DECLARE @collect_and_upload_job_id uniqueidentifier       = (SELECT jobs.job_id
                                                                   FROM [msdb].[dbo].[sysjobs] jobs
                                                                   WHERE jobs.name = @collect_and_upload_job_name 
                                                                   AND jobs.category_id = @job_category_id);
                                                                     
      -- Dac performance collection job varaibles
      DECLARE @dac_perf_job_name sysname              = N'sysutility_mi_collect_performance';
      DECLARE @dac_perf_job_id uniqueidentifier       = (SELECT jobs.job_id
                                                         FROM [msdb].[dbo].[sysjobs] jobs
                                                         WHERE jobs.name = @dac_perf_job_name 
                                                         AND jobs.category_id = @job_category_id);

      IF(@collect_and_upload_job_id IS NOT NULL)
      BEGIN
         EXEC msdb.dbo.sp_update_job @job_id=@collect_and_upload_job_id, @enabled=0;
      END
      
      IF(@dac_perf_job_id IS NOT NULL)
      BEGIN
         EXEC msdb.dbo.sp_update_job @job_id=@dac_perf_job_id, @enabled=0;
      END
     		   
   COMMIT TRANSACTION @tran_name
   END TRY
   
   BEGIN CATCH
        -- Roll back our transaction if it's still open
        IF (@@TRANCOUNT > 0)
        BEGIN
            ROLLBACK TRANSACTION;
        END;
 
        -- Rethrow the error.  Unfortunately, we can't retrow the exact same error number b/c RAISERROR 
        -- does not allow you to use error numbers below 13000.  We rethrow error 14684: 
        -- Caught error#: %d, Level: %d, State: %d, in Procedure: %s, Line: %d, with Message: %s
        DECLARE @ErrorMessage   NVARCHAR(4000);
        DECLARE @ErrorSeverity  INT;
        DECLARE @ErrorState     INT;
        DECLARE @ErrorNumber    INT;
        DECLARE @ErrorLine      INT;
        DECLARE @ErrorProcedure NVARCHAR(200);
        SELECT @ErrorLine = ERROR_LINE(),
               @ErrorSeverity = ERROR_SEVERITY(),
               @ErrorState = ERROR_STATE(),
               @ErrorNumber = ERROR_NUMBER(),
               @ErrorMessage = ERROR_MESSAGE(),
               @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');
        RAISERROR (14684, -1, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage);
    END CATCH;
   
END
GO

/**********************************************************************/
/*                                                                    */
/* Validate the instance can be used as a UCP.                        */
/*                                                                    */
/**********************************************************************/

/*
Function [fn_sysutility_ucp_get_edition_is_ucp_capable_internal]
Returns 1 if this SQL instance's edition allows it to become a UCP. 
*/
IF OBJECT_ID ('dbo.fn_sysutility_ucp_get_edition_is_ucp_capable_internal') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping function [dbo].[fn_sysutility_ucp_get_edition_is_ucp_capable_internal]', 0, 1) WITH NOWAIT;
    DROP FUNCTION dbo.fn_sysutility_ucp_get_edition_is_ucp_capable_internal; 
END;
GO
RAISERROR ('Creating function [dbo].[fn_sysutility_ucp_get_edition_is_ucp_capable_internal]', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION dbo.fn_sysutility_ucp_get_edition_is_ucp_capable_internal ()
RETURNS bit
AS
BEGIN
   DECLARE @is_instance_ucp_capable bit = 1;
   -- The integer value below corresponds to a SQLBOOT property that identifies whether 
   -- the SKU supports the UCP feature.  
   DECLARE @sqlbootvalue int;
   EXEC @sqlbootvalue = master.dbo.xp_qv '1675385081', @@SERVICENAME;
   IF (@sqlbootvalue != 2)
   BEGIN
      SET @is_instance_ucp_capable = 0;
   END;
   RETURN @is_instance_ucp_capable;
END 
GO


/*
Procedure [sp_sysutility_ucp_validate_prerequisites]
The procedure validates that the local instance can be used as a UCP
*/
IF OBJECT_ID ('dbo.sp_sysutility_ucp_validate_prerequisites') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_validate_prerequisites]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_validate_prerequisites 
END;
GO
RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_validate_prerequisites]', 0, 1) WITH NOWAIT;
GO

CREATE PROCEDURE [dbo].[sp_sysutility_ucp_validate_prerequisites]
WITH EXECUTE AS OWNER
AS
BEGIN
   IF (dbo.fn_sysutility_ucp_get_edition_is_ucp_capable_internal() = 1)
   BEGIN
      RAISERROR ('Instance is able to be used as a Utility Control Point.', 0, 1) WITH NOWAIT;
   END
   ELSE BEGIN
      DECLARE @edition nvarchar(128);
      SELECT @edition = CONVERT(nvarchar(128), SERVERPROPERTY('Edition'));
      RAISERROR(37004, -1, -1, @edition);
      RETURN(1);
   END;
END 
GO


/**********************************************************************/
/*                                                                    */
/* Run the process to turn the local instance into a UCP              */
/*                                                                    */
/**********************************************************************/
/*
Procedure [sp_sysutility_ucp_create]
The procedure runs the process that turns the local instance into a UCP
*/
IF OBJECT_ID ('dbo.sp_sysutility_ucp_create') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_create]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_create 
END;
GO


RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_create]', 0, 1) WITH NOWAIT;
GO

 
CREATE PROCEDURE [dbo].[sp_sysutility_ucp_create]
WITH EXECUTE AS OWNER
AS
BEGIN
    /* Validate that the UCP can be created on the local instance. */
    EXEC [dbo].[sp_sysutility_ucp_validate_prerequisites]
END 
GO


/**********************************************************************/
/*                                                                    */
/* Validate the instance can be managed.                              */
/*                                                                    */
/**********************************************************************/
/*
Procedure [sp_sysutility_mi_validate_enrollment_preconditions]
The procedure validates that the local instance can be made managed
*/
IF OBJECT_ID ('dbo.sp_sysutility_mi_validate_enrollment_preconditions') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_validate_enrollment_preconditions]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_mi_validate_enrollment_preconditions 
END;
GO


RAISERROR ('Creating procedure [dbo].[sp_sysutility_mi_validate_enrollment_preconditions]', 0, 1) WITH NOWAIT;
GO

 
CREATE PROCEDURE [dbo].[sp_sysutility_mi_validate_enrollment_preconditions]
WITH EXECUTE AS OWNER
AS
BEGIN
    /* Get the Edition value */
    DECLARE @edition NVARCHAR(64)
    SELECT @edition = Convert(NVARCHAR, SERVERPROPERTY('edition'))

    /* Check SQLBOOT to ensure this instance edition can be used as a UCP. */
    DECLARE @sqlbootvalue int

    EXEC @sqlbootvalue = master.dbo.xp_qv '3090395820', @@SERVICENAME
    IF (@sqlbootvalue = 2)
        RAISERROR ('Instance can be managed by a Utility Control Point.', 0, 1) WITH NOWAIT;
    ELSE
        RAISERROR(37005, -1, -1, @edition)
        RETURN(1)
END 
GO


/**********************************************************************/
/*                                                                    */
/* Run the process to make the local instance managed by a UCP.       */
/*                                                                    */
/**********************************************************************/
/*
Procedure [sp_sysutility_mi_enroll]
The procedure runs the process that makes the local instance managed by a UCP.
*/
IF OBJECT_ID ('dbo.sp_sysutility_mi_enroll') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_enroll]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_mi_enroll
END;
GO


RAISERROR ('Creating procedure [dbo].[sp_sysutility_mi_enroll]', 0, 1) WITH NOWAIT;
GO

 
CREATE PROCEDURE [dbo].[sp_sysutility_mi_enroll]
WITH EXECUTE AS OWNER
AS
BEGIN
    /* Validate that the local instance can be managed by a UCP. */
    EXEC [dbo].[sp_sysutility_mi_validate_enrollment_preconditions]
END 
GO

/**********************************************************************/
/* Object types handled by the UCP                                    */
/*                                                                    */
/* We treat DACs and Databases as synonymous for this purpose         */
/**********************************************************************/
IF(OBJECT_ID(N'[dbo].[sysutility_ucp_supported_object_types_internal]', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table [dbo].[sysutility_ucp_supported_object_types_internal]', 0, 1) WITH NOWAIT;
    CREATE TABLE [sysutility_ucp_supported_object_types_internal] (
       [object_type]   INT,
       [object_name]   NVARCHAR(32),
       
       CONSTRAINT PK_sysutility_ucp_supported_object_types_internal
         PRIMARY KEY([object_type])
    )
    
    INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (0, 'Utility')    
    INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (1, 'Computer')
    INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (2, 'Volume')
    INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (3, 'Instance')
    INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (4, 'Database')
    INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (5, 'FileGroup')
    INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (6, 'DataFile')
    INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (7, 'LogFile')
            
END
GO

/**********************************************************************/
/* Create the managed instance table                                  */
/*                                                                    */
/*                                                                    */
/**********************************************************************/

IF(OBJECT_ID(N'[dbo].[sysutility_ucp_managed_instances_internal]', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table [dbo].[sysutility_ucp_managed_instances_internal]', 0, 1) WITH NOWAIT;
    CREATE TABLE [sysutility_ucp_managed_instances_internal] (
       instance_id int IDENTITY(1,1),
       instance_name sysname,
       virtual_server_name sysname,
              
       date_created datetimeoffset(7) NOT NULL default SYSDATETIMEOFFSET(),
       created_by sysname NOT NULL default SUSER_SNAME(),
       agent_proxy_account sysname NOT NULL,
       cache_directory nvarchar(520),
       management_state int NOT NULL default (0),
       
       CONSTRAINT [UQ_sysutility_ucp_mi_id] UNIQUE (instance_id ASC),
       CONSTRAINT [PK_sysutility_ucp_mi_name] PRIMARY KEY CLUSTERED (instance_name)
      ); 
END
GO


/**********************************************************************/
/* create the managed instance view                                   */
/*                                                                    */
/**********************************************************************/
IF(OBJECT_ID(N'[dbo].[sysutility_ucp_managed_instances]', 'V') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping view [dbo].[sysutility_ucp_managed_instances]', 0, 1) WITH NOWAIT;
    DROP VIEW [dbo].[sysutility_ucp_managed_instances]
END
GO


RAISERROR ('Creating view [dbo].[sysutility_ucp_managed_instances]...', 0, 1) WITH NOWAIT;
GO
CREATE VIEW [dbo].[sysutility_ucp_managed_instances]
AS
    SELECT     
        instance_id,
        instance_name,
        virtual_server_name,
        date_created,
        created_by,
        agent_proxy_account,
        cache_directory,
        management_state
    FROM [dbo].[sysutility_ucp_managed_instances_internal]
GO

/**********************************************************************/
/* Add managed instance to the store.                               */
/*                                                                    */
/*                                                                    */
/**********************************************************************/
/*
Procedure [sp_sysutility_ucp_add_mi]
This proc creates a new ManagedInstance in dbo.sysutility_ucp_managed_instances_internal table
 
Parameters: 
    @instance_name - 
    @agent_proxy_account - 
    @cache_directory - 
    @management_state - 
    @instance_id
*/


IF OBJECT_ID ('dbo.sp_sysutility_ucp_add_mi') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_add_mi]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_add_mi 
END;
GO


RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_add_mi]', 0, 1) WITH NOWAIT;
GO

 
CREATE PROCEDURE [dbo].[sp_sysutility_ucp_add_mi]
   @instance_name sysname,
   @virtual_server_name sysname,
   @agent_proxy_account sysname,
   @cache_directory nvarchar(520),
   @management_state int,
   @instance_id int = NULL OUTPUT
WITH EXECUTE AS OWNER
AS
BEGIN
   SET NOCOUNT ON
   
   DECLARE @retval INT
   
   DECLARE @null_column nvarchar(600)
   SET @null_column = NULL

    IF (@instance_name IS NULL OR @instance_name = N'')
        SET @null_column = '@instance_name'
    ELSE IF (@virtual_server_name IS NULL OR @virtual_server_name = N'')
        SET @null_column = '@virtual_server_name'
    ELSE IF (@management_state IS NULL)
        SET @null_column = '@management_state'
    ELSE IF (@agent_proxy_account IS NULL OR @agent_proxy_account = N'')
        SET @null_column = '@agent_proxy_account'    

    -- @cache_directory can be null or empty
    

   IF @null_column IS NOT NULL
   BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_add_mi')
        RETURN(1)
   END
   
   
    IF EXISTS (SELECT * FROM dbo.sysutility_ucp_managed_instances_internal WHERE (instance_name = @instance_name))
    BEGIN
        RAISERROR(34010, -1, -1, 'Managed_Instance', @instance_name)
        RETURN(1)
    END
       
    INSERT INTO [dbo].[sysutility_ucp_managed_instances_internal]
      (instance_name, virtual_server_name, agent_proxy_account, cache_directory, management_state)
    VALUES
      (@instance_name, @virtual_server_name, @agent_proxy_account, @cache_directory, @management_state)
      
       
    SELECT @retval = @@error
    SET @instance_id = SCOPE_IDENTITY()
    RETURN(@retval)
    
END 
GO


/**************************************************************************/
/* create the Utility Processing State table                              */
/* This table is a single-row table containing internal state information */
/* for UCP processing. The two columns that it stores currently are       */
/*   latest_processing_time: the time at which data from the "live" tables */
/*                          is copied over to the "cache" tables".        */
/*   latest_health_state_id: a sequence number that all the health_state  */
/*                          tables use to represent the "latest" health   */
/*                          state calculation.                            */
/*                                                                        */
/**************************************************************************/
IF(OBJECT_ID(N'[dbo].[sysutility_ucp_processing_state_internal]', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table [dbo].[sysutility_ucp_processing_state_internal]', 0, 1) WITH NOWAIT;
    CREATE TABLE [dbo].[sysutility_ucp_processing_state_internal] (
        latest_processing_time DATETIMEOFFSET(7),
        latest_health_state_id INT,
        next_health_state_id INT,
        
        [id] AS 1,
        
        CONSTRAINT CK_sysutility_ucp_processing_state_internal 
           CHECK (latest_health_state_id <= next_health_state_id),
        CONSTRAINT PK_sysutility_ucp_processing_state_internal 
           PRIMARY KEY([id])   -- enforce single row in this table
    );
    INSERT INTO [dbo].[sysutility_ucp_processing_state_internal](latest_processing_time, latest_health_state_id, next_health_state_id)
      VALUES (SYSDATETIMEOFFSET(), 0, 1);
END;
GO

/**********************************************************************/
/* create the Utility registration creation stored procedure           */
/*                                                                    */
/**********************************************************************/
IF OBJECT_ID ('dbo.sp_sysutility_ucp_initialize') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_initialize]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE [dbo].[sp_sysutility_ucp_initialize]
END;
GO

RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_initialize]', 0, 1) WITH NOWAIT;
GO


CREATE PROCEDURE [dbo].[sp_sysutility_ucp_initialize] 
   @utility_name sysname,
   @mdw_database_name sysname,
   @description nvarchar(1024) = N''
WITH EXECUTE AS OWNER
AS
BEGIN
    DECLARE @retval INT
    DECLARE @null_column    sysname
    
    SET @null_column = NULL

    IF (@utility_name IS NULL OR @utility_name = N'')
        SET @null_column = '@utility_name'
    ELSE IF (@mdw_database_name IS NULL OR @mdw_database_name = N'')
        SET @null_column = '@mdw_database_name'



    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_initialize')
        RETURN(1)
    END


    -- Make sure that the Utility wasn't already created
    DECLARE @utilityName sysname
    set @utilityName = (SELECT CAST (current_value as sysname) FROM msdb.dbo.sysutility_ucp_configuration_internal where name = 'UtilityName')
    

    IF (@utilityName IS NOT NULL AND @utilityName != N'')
    BEGIN
        RAISERROR(37003, -1, -1)
        RETURN(1)
    END


    IF NOT EXISTS (SELECT * FROM master.dbo.sysdatabases WHERE name = @mdw_database_name)
    BEGIN
        RAISERROR(37002, -1, -1, @mdw_database_name)
        RETURN(1)
    END

    UPDATE dbo.sysutility_ucp_configuration_internal
       SET current_value = @utility_name WHERE name = N'UtilityName'

    UPDATE dbo.sysutility_ucp_configuration_internal
       SET current_value = @mdw_database_name WHERE name = N'MdwDatabaseName'

    UPDATE dbo.sysutility_ucp_configuration_internal
       SET current_value = SYSDATETIMEOFFSET() WHERE name = N'UtilityDateCreated'

    UPDATE dbo.sysutility_ucp_configuration_internal
       SET current_value = SUSER_SNAME() WHERE name = N'UtilityCreatedBy'

    IF (@description IS NOT NULL AND @description != N'')
    BEGIN
       UPDATE dbo.sysutility_ucp_configuration_internal
          SET current_value = @description WHERE name = N'UtilityDescription'
    END

    DECLARE @utility_version SYSNAME
    set @utility_version = (SELECT CAST(current_value AS SYSNAME) FROM
                              [msdb].[dbo].[sysutility_ucp_configuration_internal]
                              WHERE name = N'UtilityVersion')

    ---- Add the UtilityVersion, UcpName and the UcpFriendlyName registry key values.
    EXEC master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility',
                                         N'UtilityVersion',
                                         N'REG_SZ',
                                         @utility_version

    EXEC master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility',
                                         N'UcpName',
                                         N'REG_SZ',
                                         @@SERVERNAME

    EXEC master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE',
                                         N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility',
                                         N'UcpFriendlyName',
                                         N'REG_SZ',
                                         @utility_name

END
GO


/**********************************************************************/
/* Create procedure sp_sysutility_ucp_update_utility_configuration           */
/**********************************************************************/
IF OBJECT_ID ('[dbo].sp_sysutility_ucp_update_utility_configuration') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_update_utility_configuration]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE [dbo].sp_sysutility_ucp_update_utility_configuration 
END;
GO
RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_update_utility_configuration]', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysutility_ucp_update_utility_configuration] 
   @name SYSNAME,
   @value SQL_VARIANT
WITH EXECUTE AS OWNER
AS
BEGIN

    DECLARE @retval INT
    DECLARE @null_column    SYSNAME
    
    SET @null_column = NULL

    IF (@name IS NULL OR @name = N'')
        SET @null_column = '@name'
    ELSE IF (@value IS NULL)
        SET @null_column = '@value'
    
    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_update_utility_configuration')
        RETURN(1)
    END

    IF NOT EXISTS (SELECT 1 FROM dbo.sysutility_ucp_configuration_internal WHERE name = @name)
    BEGIN
        RAISERROR(14027, -1, -1, @name)
        RETURN(1)
    END

    UPDATE dbo.sysutility_ucp_configuration_internal SET current_value = @value WHERE name = @name
    
    SELECT @retval = @@error
    RETURN(@retval)
END
GO



/**********************************************************************/
/* Create procedure fn_sysutility_ucp_accepts_upload_schema_version   */
/*   This procedure is used to identify whether an MI's upload schema */
/*   is compatible with this UCP.  If a breaking change is introduced */
/*   that would cause an incompatibility between an MI and UCP.  This */
/*   function should be updated accordingly along with the            */
/*   mi_configuration view                                            */
/*                                                                    */
/* REASON CODES:                                                      */
/* -1  : Schema version is too low, upgrade MI                        */
/*  0  : Schema is accepted                                           */
/*  1  : Schema version is too high, upgrade UCP                      */
/**********************************************************************/
IF OBJECT_ID ('[dbo].fn_sysutility_ucp_accepts_upload_schema_version') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[fn_sysutility_ucp_accepts_upload_schema_version]', 0, 1) WITH NOWAIT;
    DROP FUNCTION [dbo].fn_sysutility_ucp_accepts_upload_schema_version 
END;
GO
RAISERROR ('Creating procedure [dbo].[fn_sysutility_ucp_accepts_upload_schema_version]', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysutility_ucp_accepts_upload_schema_version] 
(
    @upload_schema_version INT
)
RETURNS INT
AS
BEGIN

   DECLARE @accepted_min_version INT = 100;
   DECLARE @accepted_max_version INT = 100;
   
   -- Assume that the version is compatable
   DECLARE @retvalue INT = 0;
   
   IF(@upload_schema_version < @accepted_min_version)
      SET @retvalue = -1
   ELSE IF(@upload_schema_version > @accepted_max_version)
      SET @retvalue = 1
      
   RETURN @retvalue
   
END
GO




/**********************************************************************/
/* Create the resource health policies table                          */
/**********************************************************************/

IF(OBJECT_ID(N'[dbo].[sysutility_ucp_health_policies_internal]', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table [dbo].[sysutility_ucp_health_policies_internal]', 0, 1) WITH NOWAIT;
    CREATE TABLE [dbo].[sysutility_ucp_health_policies_internal] (
        health_policy_id INT IDENTITY(1,1),
        policy_name SYSNAME NOT NULL,
        rollup_object_urn NVARCHAR(4000) NOT NULL,
        rollup_object_type INT NOT NULL,
        target_type INT NOT NULL,
        resource_type INT NOT NULL,
        utilization_type INT NOT NULL,
        utilization_threshold FLOAT NOT NULL,
        is_global_policy BIT DEFAULT 0
        
       CONSTRAINT [PK_sysutility_ucp_policies_internal_id] PRIMARY KEY CLUSTERED (health_policy_id ASC),
      ); 
      CREATE NONCLUSTERED INDEX [NCI_sysutility_resource_health_policies_urn_types] ON
                                [dbo].[sysutility_ucp_health_policies_internal]([rollup_object_type],
                                                                                     [target_type],
                                                                                     [resource_type],
                                                                                     [utilization_type],
                                                                                     [policy_name]) INCLUDE([rollup_object_urn]);
      
END
GO

/**********************************************************************/
/* Create the resource health policies view                           */
/**********************************************************************/
IF object_id(N'dbo.sysutility_ucp_policies', 'V') IS NOT NULL
    DROP VIEW dbo.sysutility_ucp_policies
GO
CREATE VIEW dbo.sysutility_ucp_policies 
AS
SELECT
    rhp.health_policy_id AS health_policy_id,
    p.policy_id AS policy_id,
    rhp.policy_name AS policy_name,
    rhp.rollup_object_type AS rollup_object_type,
    rhp.rollup_object_urn AS rollup_object_urn,
    rhp.target_type AS target_type,
    rhp.resource_type AS resource_type,
    rhp.utilization_type AS utilization_type,
    rhp.utilization_threshold AS utilization_threshold,
    rhp.is_global_policy AS is_global_policy
FROM [msdb].[dbo].[sysutility_ucp_health_policies_internal] rhp
INNER JOIN msdb.dbo.syspolicy_policies p ON p.name = rhp.policy_name
GO

/**********************************************************************/
/* Create procedure sp_sysutility_ucp_add_policy           */
/* Creates the resource health policy record for specified as input details */
/**********************************************************************/
IF OBJECT_ID ('dbo.sp_sysutility_ucp_add_policy') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_add_policy]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE sp_sysutility_ucp_add_policy 
END;
GO
RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_add_policy]', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysutility_ucp_add_policy] 
   @policy_name SYSNAME,
   @rollup_object_type INT,
   @rollup_object_urn NVARCHAR(4000),
   @target_type INT,
   @resource_type INT,
   @utilization_type INT,
   @utilization_threshold FLOAT,
   @resource_health_policy_id INT = NULL OUTPUT
WITH EXECUTE AS OWNER
AS
BEGIN

    DECLARE @retval INT
    DECLARE @null_column    SYSNAME
    
    SET @null_column = NULL

    IF (@policy_name IS NULL OR @policy_name = N'')
        SET @null_column = '@policy_name'
    ELSE IF (@rollup_object_type IS NULL OR @rollup_object_type < 1 OR @rollup_object_type > 3)
        SET @null_column = '@rollup_object_type'
    ELSE IF (@rollup_object_urn IS NULL OR @rollup_object_urn = N'')
        SET @null_column = '@rollup_object_urn'
    ELSE IF (@target_type IS NULL OR @target_type < 1 OR @target_type > 6)
        SET @null_column = '@target_type'
    ELSE IF (@resource_type IS NULL OR @resource_type < 1 OR @resource_type > 5)
        SET @null_column = '@resource_type'
    ELSE IF (@utilization_type IS NULL OR @utilization_type < 1 OR @utilization_type > 2)
        SET @null_column = '@utilization_type'
    ELSE IF (@utilization_threshold IS NULL OR @utilization_threshold < 0 OR @utilization_threshold > 100)
        SET @null_column = '@utilization_threshold'       
    
    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_add_policy')
        RETURN(1)
    END

    IF NOT EXISTS (SELECT * FROM dbo.syspolicy_policies WHERE name = @policy_name)
    BEGIN
        RAISERROR(14027, -1, -1, @policy_name)
        RETURN(1)
    END

    INSERT INTO dbo.sysutility_ucp_health_policies_internal(policy_name, rollup_object_type, rollup_object_urn, target_type, resource_type, utilization_type, utilization_threshold)
    VALUES(@policy_name, @rollup_object_type, @rollup_object_urn, @target_type, @resource_type, @utilization_type, @utilization_threshold)
    
    SELECT @retval = @@error
    SET @resource_health_policy_id = SCOPE_IDENTITY()
    RETURN(@retval)
END
GO

/**********************************************************************/
/* Create procedure sp_sysutility_ucp_delete_policy        */
/* Deletes the resource health policy record for the id specified as input */
/**********************************************************************/
IF OBJECT_ID ('dbo.sp_sysutility_ucp_delete_policy') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_delete_policy]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE sp_sysutility_ucp_delete_policy 
END;
GO
RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_delete_policy]', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysutility_ucp_delete_policy] 
   @resource_health_policy_id INT
WITH EXECUTE AS OWNER
AS
BEGIN

    DECLARE @retval INT
    DECLARE @null_column    SYSNAME
    
    SET @null_column = NULL

    IF (@resource_health_policy_id IS NULL OR @resource_health_policy_id = 0)
        SET @null_column = '@resource_health_policy_id'

    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_delete_policy')
        RETURN(1)
    END

    IF NOT EXISTS (SELECT * FROM dbo.sysutility_ucp_health_policies_internal WHERE health_policy_id = @resource_health_policy_id AND is_global_policy = 0)
    BEGIN
        RAISERROR(22981, -1, -1)
        RETURN(1)
    END

    DELETE dbo.sysutility_ucp_health_policies_internal
    WHERE health_policy_id = @resource_health_policy_id
    
    SELECT @retval = @@error
    RETURN(@retval)
END
GO

/**********************************************************************/
/* Create procedure sp_sysutility_ucp_update_policy         */
/* Updates the resource health policy record with input utilization threshold */
/**********************************************************************/
IF OBJECT_ID ('dbo.sp_sysutility_ucp_update_policy') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_update_policy]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE sp_sysutility_ucp_update_policy 
END;
GO
RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_update_policy]', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysutility_ucp_update_policy] 
   @resource_health_policy_id INT
   , @utilization_threshold INT
WITH EXECUTE AS OWNER
AS
BEGIN

    DECLARE @retval INT
    DECLARE @null_column    SYSNAME
    
    SET @null_column = NULL

    IF (@resource_health_policy_id IS NULL OR @resource_health_policy_id = 0)
        SET @null_column = '@resource_health_policy_id'
    ELSE IF (@utilization_threshold IS NULL OR @utilization_threshold < 0 OR @utilization_threshold > 100)
        SET @null_column = '@utilization_threshold'    

    IF @null_column IS NOT NULL
    BEGIN
        RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_update_policy')
        RETURN(1)
    END

    IF NOT EXISTS (SELECT * FROM dbo.sysutility_ucp_health_policies_internal WHERE health_policy_id = @resource_health_policy_id)
    BEGIN
        RAISERROR(22981, -1, -1)
        RETURN(1)
    END
    
    UPDATE dbo.sysutility_ucp_health_policies_internal
    SET utilization_threshold = @utilization_threshold
    WHERE health_policy_id = @resource_health_policy_id
    
    SELECT @retval = @@error
    RETURN(@retval)
END
GO

/**********************************************************************/
/* Create the sysutility_ucp_policy_check_conditions_internal table         */
/* This table contains metadata information to build the check condition */
/* Following are the values supported by each of the below attributes */
/* target_type: Computer - 1, DataFile - 2, LogFile - 3, Server - 4, DeployedDac - 5, Volume - 6 */ 
/* resource_type: StorageSpace - 1, StorageIO - 2, Processor - 3, Memory - 4, NetworkIO - 5 */
/* utilization_type: UnderUtilization - 1, OverUtilization - 2 */
/* operator_type: = AND - 1, OR - 2, - 3, != - 4, < - 5, > - 6, <= - 7, >= - 8 */ 
/**********************************************************************/
IF(OBJECT_ID(N'[dbo].[sysutility_ucp_policy_check_conditions_internal]', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table [dbo].[sysutility_ucp_policy_check_conditions_internal]', 0, 1) WITH NOWAIT;
    CREATE TABLE [dbo].[sysutility_ucp_policy_check_conditions_internal] (
        target_type INT NOT NULL,
        resource_type INT NOT NULL,
        utilization_type INT NOT NULL,
        facet_name SYSNAME NOT NULL,
        attribute_name SYSNAME NOT NULL,
        operator_type INT NOT NULL,
        property_name SYSNAME NOT NULL
        
       CONSTRAINT [PK_sysutility_ucp_policy_check_condition_internal_type] PRIMARY KEY CLUSTERED (resource_type, target_type, utilization_type, facet_name, attribute_name  ASC),
      ); 
END
GO

/**********************************************************************/
/* Create the sysutility_ucp_policy_check_conditions                   */
/**********************************************************************/
IF object_id(N'dbo.sysutility_ucp_policy_check_conditions', 'V') IS NOT NULL
    DROP VIEW dbo.sysutility_ucp_policy_check_conditions
GO
CREATE VIEW dbo.sysutility_ucp_policy_check_conditions 
AS
SELECT
    cc.target_type AS target_type,
    cc.resource_type AS resource_type,
    cc.utilization_type AS utilization_type,
    cc.facet_name AS facet_name,
    cc.attribute_name AS attribute_name,
    cc.operator_type AS operator_type,
    cc.property_name AS property_name
FROM msdb.[dbo].[sysutility_ucp_policy_check_conditions_internal] cc
GO

DELETE FROM msdb.dbo.sysutility_ucp_policy_check_conditions_internal
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(1, 3, 1, 'Computer', 'ProcessorUtilization', 8, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(1, 3, 2, 'Computer', 'ProcessorUtilization', 7, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(2, 1, 1, 'IDataFilePerformanceFacet', 'SpaceUtilization', 8, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(2, 1, 2, 'IDataFilePerformanceFacet', 'SpaceUtilization', 7, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(3, 1, 1, 'ILogFilePerformanceFacet', 'SpaceUtilization', 8, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(3, 1, 2, 'ILogFilePerformanceFacet', 'SpaceUtilization', 7, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(4, 3, 1, 'Server', 'ProcessorUsage', 8, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(4, 3, 2, 'Server', 'ProcessorUsage', 7, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(5, 3, 1, 'DeployedDac', 'ProcessorUtilization', 8, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(5, 3, 2, 'DeployedDac', 'ProcessorUtilization', 7, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(6, 1, 1, 'Volume', 'TotalSpaceUtilization', 8, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(6, 1, 2, 'Volume', 'TotalSpaceUtilization', 7, 'UtilizationThreshold')
GO

/**********************************************************************/
/* Create the sysutility_ucp_policy_target_conditions_internal table        */
/* This table contains metadata information to build the target set condition */
/* Following are the values supported by each of the below attributes */
/* rollup_object_type: DeployedDac - 1, ManagedInstance - 2, Computer - 3 */
/* target_type: Computer - 1, DataFile - 2, LogFile - 3, Server - 4, DeployedDac - 5, Volume - 6 */ 
/* resource_type: StorageSpace - 1, StorageIO - 2, Processor - 3, Memory - 4, NetworkIO - 5 */
/* utilization_type: UnderUtilization - 1, OverUtilization - 2 */
/* operator_type: = AND - 1, OR - 2, - 3, != - 4, < - 5, > - 6, <= - 7, >= - 8 */ 
/**********************************************************************/
IF(OBJECT_ID(N'[dbo].[sysutility_ucp_policy_target_conditions_internal]', 'U') IS NULL)
BEGIN
    RAISERROR ( 'Creating table [dbo].[sysutility_ucp_policy_target_conditions_internal]', 0, 1) WITH NOWAIT;
    CREATE TABLE [dbo].[sysutility_ucp_policy_target_conditions_internal] (
        rollup_object_type INT NOT NULL,
        target_type INT NOT NULL,
        resource_type INT NOT NULL,
        utilization_type INT NOT NULL,
        facet_name SYSNAME NOT NULL,
        attribute_name SYSNAME NOT NULL,
        operator_type INT NOT NULL,
        property_name SYSNAME NOT NULL
        
       CONSTRAINT [PK_sysutility_ucp_policy_target_condition_internal_type] PRIMARY KEY CLUSTERED (rollup_object_type, resource_type, target_type, utilization_type, facet_name, attribute_name  ASC),
      ); 
END
GO

/**********************************************************************/
/* Create the sysutility_ucp_policy_target_conditions                  */
/**********************************************************************/
IF object_id(N'dbo.sysutility_ucp_policy_target_conditions', 'V') IS NOT NULL
    DROP VIEW dbo.sysutility_ucp_policy_target_conditions
GO
CREATE VIEW dbo.sysutility_ucp_policy_target_conditions 
AS
SELECT
    tc.rollup_object_type AS rollup_object_type,
    tc.target_type AS target_type,
    tc.resource_type AS resource_type,
    tc.utilization_type AS utilization_type, 
    tc.facet_name AS facet_name,
    tc.attribute_name AS attribute_name,
    tc.operator_type as operator_type,
    tc.property_name as property_name
FROM msdb.[dbo].[sysutility_ucp_policy_target_conditions_internal] tc
GO

DELETE FROM msdb.dbo.sysutility_ucp_policy_target_conditions_internal
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 5, 3, 0, 'DeployedDac', 'ServerInstanceName', 3, 'DacServerInstanceName')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 5, 3, 0, 'DeployedDac',	'Name', 3, 'DacName')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 5, 3, 1, 'DeployedDac', 'ProcessorUtilization', 5, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 5, 3, 2, 'DeployedDac', 'ProcessorUtilization', 6, 'UtilizationThreshold')
GO
					
INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 2, 1, 0, 'Server', 'InstanceName', 3, 'DacInstanceName')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 2, 1, 0, 'Server', 'NetName', 3, 'DacComputerName')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 2, 1, 0, 'Database', 'Name', 3, 'DacDatabaseName')
GO
					
INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 3, 1, 0, 'Server', 'InstanceName', 3, 'DacInstanceName')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 3, 1, 0, 'Server', 'NetName', 3, 'DacComputerName')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 3, 1, 0, 'Database', 'Name', 3, 'DacDatabaseName')
GO
					
INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 4, 3, 0, 'Server', 'InstanceName', 3, 'ServerInstanceName')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 4, 3, 0, 'Server', 'NetName', 3, 'ServerNetName')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 4, 3, 1, 'Server', 'ProcessorUsage', 5, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 4, 3, 2, 'Server', 'ProcessorUsage', 6, 'UtilizationThreshold')
GO
					
INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 2, 1, 0, 'Server', 'InstanceName', 3, 'ServerInstanceName')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 2, 1, 0, 'Server', 'NetName', 3, 'ServerNetName')
GO
					
INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 3, 1, 0, 'Server', 'InstanceName', 3, 'ServerInstanceName')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 3, 1, 0, 'Server', 'NetName', 3, 'ServerNetName')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(3, 1, 3, 0, 'Computer', 'Name', 3, 'ComputerName')
GO
					
INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(3, 1, 3, 1, 'Computer', 'ProcessorUtilization', 5, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(3, 1, 3, 2, 'Computer', 'ProcessorUtilization', 6, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(3, 6, 1, 0, 'Computer', 'Name', 3, 'ComputerName')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(3, 6, 1, 1, 'Volume', 'TotalSpaceUtilization', 5, 'UtilizationThreshold')
GO

INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(3, 6, 1, 2, 'Volume', 'TotalSpaceUtilization', 6, 'UtilizationThreshold')
GO

/**********************************************************************/
/* Create table sysutility_ucp_policy_violations_internal             */
/* This table stores violations for health polices from latest policy evaluation */
/**********************************************************************/
IF(OBJECT_ID(N'dbo.sysutility_ucp_policy_violations_internal', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table dbo.sysutility_ucp_policy_violations_internal', 0, 1) WITH NOWAIT;
    CREATE TABLE sysutility_ucp_policy_violations_internal
    (
        health_policy_id INT NOT NULL,
        policy_id INT NOT NULL, 
        policy_name SYSNAME NULL,
        history_id INT NOT NULL,
        detail_id INT NOT NULL,
        target_query_expression NVARCHAR(MAX) NULL,
        target_query_expression_with_id NVARCHAR(MAX) NULL,
        execution_date DATETIME NULL,
        result INT NULL,

        CONSTRAINT [PK_sysutility_ucp_policy_violations_internal] 
          PRIMARY KEY CLUSTERED (policy_id, history_id, detail_id)
    )
END
GO

/**********************************************************************/
/* Create the health policy violations view                           */
/* This view fetches violations for health polices from latest policy evaluation */
/**********************************************************************/
IF object_id(N'dbo.sysutility_ucp_policy_violations', 'V') IS NOT NULL
   DROP VIEW dbo.sysutility_ucp_policy_violations
GO

CREATE VIEW dbo.sysutility_ucp_policy_violations 
AS
    SELECT pv.health_policy_id
        , pv.policy_id
        , pv.policy_name
        , pv.history_id
        , pv.detail_id
        , pv.target_query_expression
        , pv.target_query_expression_with_id
        , pv.execution_date
        , pv.result
    FROM dbo.sysutility_ucp_policy_violations_internal pv
GO

/**********************************************************************/
/* Create procedure sp_sysutility_ucp_get_policy_violations           */
/* This SP is used to fetch the violations for health polices from    */
/* latest policy evaluation and cache them in the intermediate table  */
/**********************************************************************/

IF OBJECT_ID ('dbo.sp_sysutility_ucp_get_policy_violations') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_get_policy_violations', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_get_policy_violations 
END;
GO

RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_get_policy_violations', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE dbo.sp_sysutility_ucp_get_policy_violations 
WITH EXECUTE AS OWNER
AS
BEGIN
    -- Clear the existing policy violations        
    TRUNCATE TABLE dbo.sysutility_ucp_policy_violations_internal
    
    -- Cache the latest policy violations for non-volatile resources
    -- The health state for non-volatile resource is determined based on 
    -- the latest policy violation against the target (file, volume) type.
    INSERT INTO dbo.sysutility_ucp_policy_violations_internal
    SELECT p.health_policy_id
        , p.policy_id
        , p.policy_name
        , d.history_id
        , d.detail_id
        , d.target_query_expression
        , d.target_query_expression_with_id
        , d.execution_date
        , d.result
    FROM msdb.dbo.sysutility_ucp_policies p
    INNER JOIN msdb.dbo.syspolicy_policy_execution_history_internal h 
        ON h.policy_id = p.policy_id
    INNER JOIN msdb.dbo.syspolicy_policy_execution_history_details_internal d 
        ON d.history_id = h.history_id
    WHERE p.resource_type = 1 -- Filter non-volatile resources (currently storage type only)   
        -- PBM stores the end_date in local time so convert the 'latest_processing_time' datetimeoffset to local datetime before compare
        AND h.end_date >= (SELECT CONVERT(DATETIME, latest_processing_time) FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal]) 
        AND h.is_full_run = 1
        AND h.result = 0
        AND d.result = 0; 
        
    -- Get the policy evaluation count for volatile resources over the trailing window. 
    -- The health state for volatile resource is determined based on the policy 
    -- violation against the target (cpu) type over a trailing window and should
    -- exeed the occurrence frequency percent. E.g. a tartget can be considered
    -- as over utilized if its violating the policy for last 3 out of 4 evaluations
    -- (1 hour trailing window and 70 % occurrence frequency)    
    SELECT p.policy_id
          , MAX(h.end_date) execution_date
          , CASE WHEN 0 = COUNT(*) THEN 1 ELSE COUNT(*) END AS evaluation_count
          , p.utilization_type
          , p.health_policy_id
          , p.policy_name
          , pc.occurence_frequency
    INTO #policy_evaluations 
    FROM msdb.dbo.sysutility_ucp_policies p
    INNER JOIN msdb.dbo.syspolicy_policy_execution_history_internal h 
        ON p.policy_id = h.policy_id
    INNER JOIN msdb.dbo.sysutility_ucp_policy_configuration pc
        ON p.utilization_type = pc.utilization_type
    WHERE h.end_date >= DATEADD(MI, -60*pc.trailing_window, CURRENT_TIMESTAMP) 
        AND h.is_full_run = 1  
        AND p.resource_type = 3 -- Filter volatile resources (currently cpu type only)
    GROUP BY p.policy_id
        , p.utilization_type
        , p.health_policy_id
        , p.policy_name
        , pc.occurence_frequency;


    -- Get the policy violation count for the target types over the trailing window
    -- Note: 
    -- 1. If the trailing window is size increased, this computation will continue to
    -- use the exiting violations in the history against the newly configured window size. 
    -- It will only be effective after the full trailing window size is reached.
    -- 2. If the occurrence frequency is changed, it will be effective in the next run of the
    -- health state computation.
    SELECT p.policy_id
        , d.target_query_expression
        , COUNT(*) AS violation_count
        , MAX(h.history_id) as history_id
        , MAX(d.detail_id) AS detail_id
    INTO #policy_violations 
    FROM msdb.dbo.sysutility_ucp_policies p
    INNER JOIN msdb.dbo.syspolicy_policy_execution_history_internal h 
        ON p.policy_id = h.policy_id
    INNER JOIN msdb.dbo.syspolicy_policy_execution_history_details_internal d
        ON d.history_id = h.history_id 
    INNER JOIN msdb.dbo.sysutility_ucp_policy_configuration pc
        ON p.utilization_type = pc.utilization_type
    WHERE h.end_date >= DATEADD(MI, -60*pc.trailing_window, CURRENT_TIMESTAMP)			
        AND h.is_full_run = 1	
        AND h.result = 0
        AND d.result = 0
        AND p.resource_type = 3 -- Filter volatile resources (currently cpu type only)
    GROUP BY p.policy_id, d.target_query_expression;
    
    INSERT INTO dbo.sysutility_ucp_policy_violations_internal
    SELECT pe.health_policy_id
      , pe.policy_id
      , pe.policy_name
      , pv.history_id
      , pv.detail_id
      , pv.target_query_expression
      , N'' AS target_query_expression_with_id
      , pe.execution_date
      , 0 AS result
    FROM #policy_evaluations pe
    INNER JOIN #policy_violations pv 
        ON pe.policy_id = pv.policy_id
    WHERE pe.occurence_frequency <= ((pv.violation_count * 100) / pe.evaluation_count);
	        
END
GO    

--**********************************************************************
-- Create function fn_encode_sqlname_for_powershell             
 
-- Function description:
-- Encodes the sql names making it suitable for powershell representation
-- This is required as some of the characters conflict with PS commands
-- More details: http://msdn.microsoft.com/en-us/library/cc281841.aspx

 --Parameter description:
 --1. @sql_name - the sql name that needs encoding
--*********************************************************************
IF(OBJECT_ID(N'dbo.fn_encode_sqlname_for_powershell', 'FN') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping function dbo.fn_encode_sqlname_for_powershell', 0, 1) WITH NOWAIT;
    DROP FUNCTION dbo.fn_encode_sqlname_for_powershell
END
GO
RAISERROR ('Creating function dbo.fn_encode_sqlname_for_powershell', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION dbo.fn_encode_sqlname_for_powershell
(
	@sql_name SYSNAME
)
RETURNS SYSNAME
AS
BEGIN
	DECLARE @encoded_name SYSNAME = @sql_name

	SET @encoded_name = REPLACE(@encoded_name, N'%', N'%25')
	SET @encoded_name = REPLACE(@encoded_name, N'\', N'%5C')
	SET @encoded_name = REPLACE(@encoded_name, N'/', N'%2F')
	SET @encoded_name = REPLACE(@encoded_name, N':', N'%3A')
	SET @encoded_name = REPLACE(@encoded_name, N'<', N'%3C')
	SET @encoded_name = REPLACE(@encoded_name, N'>', N'%3E')
	SET @encoded_name = REPLACE(@encoded_name, N'*', N'%2A')
	SET @encoded_name = REPLACE(@encoded_name, N'?', N'%3F')
	SET @encoded_name = REPLACE(@encoded_name, N'[', N'%5B')
	SET @encoded_name = REPLACE(@encoded_name, N']', N'%5D')
	SET @encoded_name = REPLACE(@encoded_name, N'|', N'%7C')

	RETURN @encoded_name
END
GO

--**********************************************************************
-- Create procedure sp_sysutility_ucp_delete_policy_history                 
-- Procedure description:
-- This SP purges the utility resource health policy evaluation results history and details
-- For volatile resources, it deletes the records older than the specified trailing window time frame
-- For non-volatile resources, it deletes the records older than the current processing time
-- Since this SP deletes (many) records, it could possibly affect the performance due to impact on indexes  
-- We should consider droping and recreating the indexes if the purged records are huge in number.
--**********************************************************************
IF OBJECT_ID ('dbo.sp_sysutility_ucp_delete_policy_history') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_delete_policy_history', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_delete_policy_history 
END;
RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_delete_policy_history', 0, 1) WITH NOWAIT;
GO

CREATE PROCEDURE dbo.sp_sysutility_ucp_delete_policy_history 
WITH EXECUTE AS OWNER
AS
BEGIN
    SET NOCOUNT ON; 
    
    DECLARE @over_utilization_trailing_window INT = 1
    DECLARE @under_utilization_trailing_window INT = 1

    DECLARE @rows_affected bigint;
    DECLARE @delete_batch_size int;

    -- As we delete the master record in the history table which cascades
    -- to foreign key records in details table; keep the delete batch size to 100.
    SET @delete_batch_size = 100;
    SET @rows_affected = -1;

    -- Get the configured over utilization trailing window
    SELECT @over_utilization_trailing_window = CAST(ci.current_value AS INT)
    FROM msdb.dbo.sysutility_ucp_configuration_internal ci
    WHERE ci.name = 'OverUtilizationTrailingWindow'

    -- Get the configured under utilization trailing window
    SELECT @under_utilization_trailing_window = CAST(ci.current_value AS INT)
    FROM msdb.dbo.sysutility_ucp_configuration_internal ci
    WHERE ci.name = 'UnderUtilizationTrailingWindow'

    -- Purge volatile resource policy evaluation history against over utilization trailing window
    DECLARE @max_end_date datetime;
    SET @max_end_date = DATEADD(HH, -@over_utilization_trailing_window, CURRENT_TIMESTAMP);
    SET @rows_affected = -1;
    WHILE (@rows_affected != 0)
    BEGIN
        -- We use sp_executesql here because the values of @delete_batch_size and @max_end_date could 
        -- influence plan selection. These are variables that have unknown values when the plan for the 
        -- proc is compiled.  By deferring compilation until the variables have taken on their final values, 
        -- we give the optimizer information that it needs to choose the best possible plan.  We could also 
        -- use an OPTION(RECOMPILE) hint to accomplish the same thing, but the sp_executesql approach avoids 
        -- paying the plan compile cost for each loop iteration. 
        EXEC sp_executesql N'
            DELETE TOP (@delete_batch_size) h
            FROM msdb.dbo.syspolicy_policy_execution_history_internal h
            INNER JOIN msdb.dbo.sysutility_ucp_policies p ON p.policy_id = h.policy_id
            WHERE p.resource_type = 3        -- processor resource type
                AND p.utilization_type = 2   -- over-utilization
                AND h.end_date < @max_end_date', 

            N'@delete_batch_size int, @max_end_date datetime', 
            @delete_batch_size = @delete_batch_size, @max_end_date = @max_end_date;

        SET @rows_affected = @@ROWCOUNT;
    END;
    
    -- Purge volatile resource policy evaluation history against under utilization trailing window
    SET @max_end_date = DATEADD(HH, -@under_utilization_trailing_window, CURRENT_TIMESTAMP);
    SET @rows_affected = -1;
    WHILE (@rows_affected != 0)
    BEGIN    
        EXEC sp_executesql N'
            DELETE TOP (@delete_batch_size) h
            FROM msdb.dbo.syspolicy_policy_execution_history_internal h
            INNER JOIN msdb.dbo.sysutility_ucp_policies p ON p.policy_id = h.policy_id
            WHERE p.resource_type = 3        -- processor resource type
                AND p.utilization_type = 1   -- under-utilization
                AND h.end_date < @max_end_date', 

            N'@delete_batch_size int, @max_end_date datetime', 
            @delete_batch_size = @delete_batch_size, @max_end_date = @max_end_date;

        SET @rows_affected = @@ROWCOUNT;
    END;
    
    -- Purge non-volatile resource policy evaluation history older than the current processing_time recorded 
    -- The latest policy evaluation results are not purged to avoid potential conflicts with the health 
    -- state computation running simultaneoulsy in the caching (master) job during the same time schedule. 
    SET @rows_affected = -1;
    -- PBM stores the end_date in local time so convert the 'latest_processing_time' datetimeoffset to a local datetime
    SELECT @max_end_date = CONVERT(DATETIME, latest_processing_time) FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal];
    WHILE (@rows_affected != 0)
    BEGIN     
        EXEC sp_executesql N'
            DELETE TOP (@delete_batch_size) h
            FROM msdb.dbo.syspolicy_policy_execution_history_internal h
            INNER JOIN msdb.dbo.sysutility_ucp_policies p ON p.policy_id = h.policy_id
            WHERE p.resource_type = 1    -- storage space resource type
                AND h.end_date < @max_end_date', 

            N'@delete_batch_size int, @max_end_date datetime',  
            @delete_batch_size = @delete_batch_size, @max_end_date = @max_end_date; 
            
        SET @rows_affected = @@ROWCOUNT;
    END;            
    
END
GO    
    
--**********************************************************************
-- Create function fn_sysutility_ucp_get_global_health_policy  
 
-- Function description:        
-- Identifies the global policy for a given object type 
-- 1. Check if there is a global policy for that object type in a target type
-- 2. Check if there is a global policy for that object type at utility level  
   
-- Parameter description:
-- 1. @rollup_object_type - type of the object (0: global, 1: dac, 2: server)
-- 2. @target_type - target resource object type (e.g. file, logfile, volume, computer etc)
-- 3. @resource_type - type of resource monitored (1: storage space, 2: storage I/O, 3: processor, 4: memory, 5: network I/O)
-- 4. @utilization_type - type of the utilization (1: under utilization, 2: over utilization)
--**********************************************************************
IF(OBJECT_ID(N'dbo.[fn_sysutility_ucp_get_global_health_policy]', 'FN') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping function dbo.[fn_sysutility_ucp_get_global_health_policy]', 0, 1) WITH NOWAIT;
    DROP FUNCTION dbo.[fn_sysutility_ucp_get_global_health_policy]
END
GO
RAISERROR ('Creating function dbo.[fn_sysutility_ucp_get_global_health_policy]', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION dbo.[fn_sysutility_ucp_get_global_health_policy](
    @rollup_object_type INT
    , @target_type INT
    , @resource_type INT
    , @utilization_type INT )
RETURNS INT 
AS
BEGIN
	DECLARE @health_policy_id INT
	
    -- Check if there is a global policy for that object type in a target type
	SELECT @health_policy_id = hp.health_policy_id
	FROM msdb.dbo.sysutility_ucp_policies hp
	WHERE hp.rollup_object_type = @rollup_object_type
	    AND hp.target_type = @target_type
	    AND hp.resource_type = @resource_type
	    AND hp.utilization_type = @utilization_type
	    AND hp.is_global_policy = 1
    
    -- If not found, check if there is a global policy for that object type at utility level. 
    -- This is the last resort, must find the global policy here.
    IF @health_policy_id = 0 OR @health_policy_id IS NULL
    BEGIN
		SELECT @health_policy_id = hp.health_policy_id
		FROM msdb.dbo.sysutility_ucp_policies hp
		WHERE hp.rollup_object_type = 0
		    AND hp.target_type = @target_type
		    AND hp.resource_type = @resource_type
		    AND hp.utilization_type = @utilization_type
		    AND hp.is_global_policy = 1    
    END	
    
	RETURN @health_policy_id
END
GO

--**********************************************************************


--**********************************************************************
-- Create function fn_sysutility_ucp_get_applicable_policy  
 
-- Function description:        
-- Identifies the applciable policy for a given object represented by URN 
-- 1. Checking if there is an overridden policy for that object
-- 2. Checking if there is a global policy for that object type   
   
-- Parameter description:
-- 1. @rollup_object_urn - urn of the object
-- 2. @rollup_object_type - type of the object (0: global, 1: dac, 2: server)
-- 3. @target_type - target resource object type (e.g. file, logfile, volume, computer etc)
-- 4. @resource_type - type of resource monitored (1: storage space, 2: storage I/O, 3: processor, 4: memory, 5: network I/O)
-- 5. @utilization_type - type of the utilization (1: under utilization, 2: over utilization)
--**********************************************************************
IF(OBJECT_ID(N'dbo.[fn_sysutility_ucp_get_applicable_policy]', 'FN') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping function dbo.[fn_sysutility_ucp_get_applicable_policy]', 0, 1) WITH NOWAIT;
    DROP FUNCTION dbo.[fn_sysutility_ucp_get_applicable_policy]
END
GO
RAISERROR ('Creating function dbo.[fn_sysutility_ucp_get_applicable_policy]', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION dbo.[fn_sysutility_ucp_get_applicable_policy](
    @rollup_object_urn NVARCHAR(4000)
    , @rollup_object_type INT
    , @target_type INT
    , @resource_type INT
    , @utilization_type INT )
RETURNS INT 
AS
BEGIN
   DECLARE @health_policy_id INT
	
    -- Check if there is an overridden policy for the rollup object
    SELECT @health_policy_id = hp.health_policy_id
    FROM msdb.dbo.sysutility_ucp_policies hp
    WHERE hp.rollup_object_urn = @rollup_object_urn
        AND hp.rollup_object_type = @rollup_object_type
        AND hp.target_type = @target_type
        AND hp.resource_type = @resource_type
        AND hp.utilization_type = @utilization_type
    
    -- If no overridden policy exist, get the global policy
    -- Check if the specific rollup_object has the global policy
    IF @health_policy_id = 0 OR @health_policy_id IS NULL
    BEGIN
		SELECT @health_policy_id = msdb.dbo.fn_sysutility_ucp_get_global_health_policy(@rollup_object_type
                                                                                , @target_type
                                                                                , @resource_type
                                                                                , @utilization_type)
    END
    
	RETURN @health_policy_id
     
END
GO

/**********************************************************************/
/* Create function fn_sysutility_ucp_get_aggregated_failure_count              */
/**********************************************************************/
IF(OBJECT_ID(N'[dbo].[fn_sysutility_ucp_get_aggregated_failure_count]', 'FN') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping function [fn_sysutility_ucp_get_aggregated_failure_count]', 0, 1) WITH NOWAIT;
    DROP FUNCTION [dbo].[fn_sysutility_ucp_get_aggregated_failure_count]
END
GO
RAISERROR ('Creating function [fn_sysutility_ucp_get_aggregated_failure_count]', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysutility_ucp_get_aggregated_failure_count](@policy_name SYSNAME, @target_query_expression NVARCHAR(max))
RETURNS INT 
AS
BEGIN
   DECLARE @count INT
   SET @count = 0;

    SELECT @count = COUNT(hs.result) 
    FROM msdb.dbo.sysutility_ucp_policy_violations hs
    INNER JOIN msdb.dbo.syspolicy_policies p ON hs.policy_id = p.policy_id
    WHERE (hs.target_query_expression_with_id LIKE +'%'+@target_query_expression+'%' ESCAPE '\'
    OR hs.target_query_expression LIKE +'%'+@target_query_expression+'%')
    AND hs.result = 0
    AND p.name = @policy_name

   RETURN @count
END
GO

/**********************************************************************/
/* Create function fn_sysutility_ucp_get_policy_violations          */
/**********************************************************************/
IF(OBJECT_ID(N'[dbo].[fn_sysutility_ucp_get_policy_violations]', 'TF') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping function [fn_sysutility_ucp_get_policy_violations]', 0, 1) WITH NOWAIT;
    DROP FUNCTION [dbo].[fn_sysutility_ucp_get_policy_violations]
END
GO
RAISERROR ('Creating function [fn_sysutility_ucp_get_policy_violations]', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysutility_ucp_get_policy_violations](@policy_name SYSNAME, @target_query_expression NVARCHAR(max))
RETURNS @data TABLE 
( health_state_id BIGINT ) 
AS
BEGIN

   INSERT INTO @data
    SELECT hs.detail_id
    FROM msdb.dbo.sysutility_ucp_policy_violations hs
    INNER JOIN msdb.dbo.syspolicy_policies p ON hs.policy_id = p.policy_id
    WHERE (hs.target_query_expression_with_id LIKE +'%'+@target_query_expression+'%' ESCAPE '\'
    OR hs.target_query_expression LIKE +'%'+@target_query_expression+'%')
    AND hs.result = 0
    AND p.name = @policy_name
    
   RETURN 
END
GO

/********************************************************************************/
/********************************************************************************/
/* Create function [fn_sysutility_ucp_get_file_space_utilization_history] which returns */
/* a table containing storage utilization history                               */
--  @object_type : 
--         0 --> Utility-level
--         1 --> Server (computer) level
--         2 --> Volume level
--         3 --> Instance level
--         4 --> Database level
--         5 --> Filegroup level
--         6 --> DataFile level
--         7 --> LogFile level
--  @virtual_server_name: virtual computer name 
--                        (logical name for the failover cluster if this is part of a cluster)
--                        otherwise, the name of the standalone computer
--  @volume_device_id: device_id of the volume
--  @server_instance_name: SQL instance name (server-qualified name)
--  @database_name: Name of the database
--  @filegroup_name: name of the filegroup
--  @dbfile_name: Name of the datafile/logfile
--  @start_time	: starting time for interval
--  @end_time	: end time for the interval
--  @aggregation_interval: This attribute is helpful setting starttime for the timespan.
--         1 --> No aggregation (raw data)
--         2 --> Hourly aggregation
--         3 --> Daily aggregation
--
-- NOTE: total_space_bytes may be NULL for database, filegroup, instance objects.
--    We treat that as equivalent to 0 for the purposes of this function, although
--    it's not entirely clear that that's appropriate.
/********************************************************************************/
IF(OBJECT_ID(N'[dbo].[fn_sysutility_ucp_get_file_space_utilization_history]') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping function [fn_sysutility_ucp_get_file_space_utilization_history]', 0, 1) WITH NOWAIT;
    DROP FUNCTION [dbo].[fn_sysutility_ucp_get_file_space_utilization_history]
END
GO

RAISERROR ('Creating function [fn_sysutility_ucp_get_file_space_utilization_history]', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysutility_ucp_get_file_space_utilization_history]( 
   @object_type TINYINT, 
   @virtual_server_name SYSNAME, 
   @volume_device_id SYSNAME, 
   @server_instance_name SYSNAME, 
   @database_name SYSNAME, 
   @filegroup_name SYSNAME, 
   @database_file_name SYSNAME,
   @start_time DATETIMEOFFSET(7),
   @end_time DATETIMEOFFSET(7),
   @aggregation_interval TINYINT
   )
RETURNS TABLE AS RETURN (
    SELECT	CASE WHEN ISNULL(total_space_bytes, 0) = 0 THEN 0 ELSE (used_space_bytes * 100)/total_space_bytes END AS storage_utilization_percent,
		    CONVERT(BIGINT, used_space_bytes) AS storage_utilization_in_bytes, 
		    CONVERT(BIGINT, ISNULL(total_space_bytes, 0)) AS storage_capacity_in_bytes, 
		    processing_time as sample_time
    FROM dbo.syn_sysutility_ucp_space_utilization 
    WHERE @object_type = object_type AND
          @aggregation_interval = aggregation_type AND
          (processing_time BETWEEN @start_time AND @end_time) AND
          ISNULL(@virtual_server_name, '') = virtual_server_name AND
          ISNULL(@volume_device_id, '') = volume_device_id AND
          ISNULL(@server_instance_name, '') = server_instance_name AND
          ISNULL(@database_name, '') = database_name AND
          ISNULL(@filegroup_name, '') = [filegroup_name] AND
          ISNULL(@database_file_name, '') = [dbfile_name] 
    )       
GO

/********************************************************************************/
/* Create function [fn_sysutility_ucp_get_cpu_utilization_history] which returns */
/* a table containing processor utilization history                               */
--  @object_type : 
--          1 --> Server (computer) level
--          2 --> Instance level
--          3 --> Database level (DAC)
--  @physical_server_name: (physical) computer name
--  @server_instance_name: SQL instance name (server-qualified name)
--  @dac_name: Name of the DAC instance (also the database_name)
--  @start_time	: starting time for interval
--  @end_time	: end time for the interval
--  @aggregation_interval: This attribute is helpful setting starttime for the timespan.
--         1 --> No aggregation (raw data)
--         2 --> Hourly aggregation
--         3 --> Daily aggregation
/********************************************************************************/
IF(OBJECT_ID(N'[dbo].[fn_sysutility_ucp_get_cpu_utilization_history]') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping function [fn_sysutility_ucp_get_cpu_utilization_history]', 0, 1) WITH NOWAIT;
    DROP FUNCTION [dbo].[fn_sysutility_ucp_get_cpu_utilization_history]
END
GO

RAISERROR ('Creating function [fn_sysutility_ucp_get_cpu_utilization_history]', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysutility_ucp_get_cpu_utilization_history]( 
   @object_type TINYINT, 
   @physical_server_name SYSNAME, 
   @server_instance_name SYSNAME, 
   @dac_name SYSNAME,
   @start_time DATETIMEOFFSET(7),
   @end_time DATETIMEOFFSET(7),
   @aggregation_interval TINYINT
   )
RETURNS TABLE 
AS
RETURN (	
    SELECT percent_total_cpu_utilization AS processor_utilization_percent, 
           processing_time AS sample_time
    FROM dbo.syn_sysutility_ucp_cpu_utilization 
    WHERE @object_type = object_type AND
          @aggregation_interval = aggregation_type AND
          (processing_time BETWEEN @start_time AND @end_time) AND
          ISNULL(@physical_server_name, '') = physical_server_name AND
          ISNULL(@server_instance_name, '') = server_instance_name AND
          ISNULL(@dac_name, '') = database_name
    )
GO

--------------------------------------------------------------------------------------------------
-- View that exposes the latest properties for all installed DACs. 
-- This view wraps a synonym that gets redirected to a MDW table after sysutility_mdw is created.
--------------------------------------------------------------------------------------------------
IF OBJECT_ID(N'[dbo].[sysutility_ucp_deployed_dacs]', 'V') IS NOT NULL
BEGIN 
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_deployed_dacs]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_deployed_dacs];
END;
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_deployed_dacs]', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_deployed_dacs
AS
SELECT
   dac_id,    -- todo (VSTS #345036): This column will be removed
   dac_name,
   dac_deploy_date AS dac_deployed_date,
   dac_description AS dac_description,
   dac_percent_total_cpu_utilization AS dac_percent_total_cpu_utilization,
   server_instance_name AS dac_server_instance_name,
   physical_server_name AS dac_physical_server_name,
   batch_time AS dac_collection_time,
   processing_time AS dac_processing_time,
   urn,
   powershell_path
FROM dbo.syn_sysutility_ucp_dacs;
GO

/**********************************************************************/
/* create the computer object view that utilizes the data collected   */
/* in the MDW                                                         */
/*                                                                    */
/* We keep track of two names here - the "physical_server_name" and   */
/* the "virtual_server_name". In most cases, these two have the same  */
/* value. However, in the case of failover-cluster-instances, the     */
/* virtual_server_name refers to the logical name for the cluster,    */
/* while the physical_server_name refers to the name of the computer  */
/* within that cluster.                                               */
/* In general, we use physical_server_name across the board, except   */
/* for "shared" resources like space-utilization, where the virtual   */
/* name is more appropriate                                           */
/**********************************************************************/
IF OBJECT_ID(N'dbo.sysutility_ucp_computers', 'V') IS NOT NULL
BEGIN
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_computers]', 0, 1) WITH NOWAIT;
	DROP VIEW dbo.sysutility_ucp_computers;
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_computers]', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_computers
AS
   SELECT  
       server_table.id AS computer_id    -- todo (VSTS #345036): This column will be removed
       , server_table.virtual_server_name AS virtual_server_name
       , server_table.physical_server_name AS physical_server_name
       , server_table.is_clustered_server AS is_clustered
       , server_table.percent_total_cpu_utilization AS processor_utilization
       , server_table.cpu_name AS cpu_name
       , server_table.cpu_max_clock_speed AS cpu_max_clock_speed
       , server_table.processing_time AS processing_time
       , urn
       , powershell_path       
   FROM    [dbo].[syn_sysutility_ucp_computers] as server_table
GO



-- =============================================
-- Create View : sysutility_ucp_volumes
-- =============================================
IF OBJECT_ID(N'dbo.sysutility_ucp_volumes', 'V') IS NOT NULL
BEGIN
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_volumes]', 0, 1) WITH NOWAIT;
   DROP VIEW dbo.sysutility_ucp_volumes
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_volumes]', 0, 1) WITH NOWAIT;
GO

CREATE VIEW dbo.sysutility_ucp_volumes
AS
SELECT
	[ID] AS volume_id    -- todo (VSTS #345036): This column will be removed
	, physical_server_name AS physical_server_name
	, virtual_server_name AS virtual_server_name
	, volume_name
	, volume_device_id
	, total_space_available AS total_space
	, total_space_utilized AS total_space_used
	, percent_total_space_utilization AS total_space_utilization  
FROM dbo.syn_sysutility_ucp_volumes;
GO


-- =============================================
-- Create View : sysutility_ucp_utility_space_utilization
-- =============================================
IF OBJECT_ID(N'dbo.sysutility_ucp_utility_space_utilization', 'V') IS NOT NULL
BEGIN
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_utility_space_utilization]', 0, 1) WITH NOWAIT;
   DROP VIEW dbo.sysutility_ucp_utility_space_utilization
END;
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_utility_space_utilization]', 0, 1) WITH NOWAIT;
GO
---
--- Gets the total utility space utilization. 
--- The funny left-outer-join is to account for cases where there is no "utility-wide" entry yet 
--- typically, right at bootstrap time
---
CREATE VIEW dbo.sysutility_ucp_utility_space_utilization
AS
   SELECT ISNULL(S2.total_space_bytes, 0) AS total_utility_storage,
          ISNULL(S2.used_space_bytes, 0) AS total_utilized_space 	
   FROM (SELECT 1 AS x) AS S1
         LEFT OUTER JOIN 
        (SELECT total_space_bytes, used_space_bytes 
          FROM dbo.syn_sysutility_ucp_space_utilization
          WHERE object_type = 0 AND -- utility-wide information
                aggregation_type = 0 AND -- detail-information
                processing_time = (SELECT latest_processing_time FROM msdb.dbo.sysutility_ucp_processing_state_internal)
        ) AS S2 ON (1=1)        
GO

-- =============================================
-- Create View : [sysutility_ucp_instances]
-- All of the properties are auto-generated using the below select statement
-- select ', [' +property_name+']' from [msdb].[dbo].[sysutility_mi_smo_property_configurations_internal] where object_type = 1
-- =============================================
IF OBJECT_ID(N'dbo.sysutility_ucp_instances', 'V') IS NOT NULL
BEGIN
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_instances]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_instances]
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_instances]', 0, 1) WITH NOWAIT;
GO
   CREATE VIEW dbo.sysutility_ucp_instances
   AS
   SELECT [urn]
, [powershell_path]   
, [processing_time]
, [batch_time] AS [collection_time]
, [AuditLevel]
, [BackupDirectory]
, [BrowserServiceAccount]
, [BrowserStartMode]
, [BuildClrVersionString]
, [BuildNumber]
, [Collation]
, [CollationID]
, [ComparisonStyle]
, [ComputerNamePhysicalNetBIOS]
, [DefaultFile]
, [DefaultLog]
, [Edition]
, [EngineEdition]
, [ErrorLogPath]
, [FilestreamShareName]
, [InstallDataDirectory]
, [InstallSharedDirectory]
, [InstanceName]
, [IsCaseSensitive]
, [IsClustered]
, [IsFullTextInstalled]
, [IsSingleUser]
, [Language]
, [MailProfile]
, [MasterDBLogPath]
, [MasterDBPath]
, [MaxPrecision]
, [Name]
, [NamedPipesEnabled]
, [NetName]
, [NumberOfLogFiles]
, [OSVersion]
, [PerfMonMode]
, [PhysicalMemory]
, [Platform]
, [Processors]
, [ProcessorUsage]
, [Product]
, [ProductLevel]
, [ResourceVersionString]
, [RootDirectory]
, [ServerType]
, [ServiceAccount]
, [ServiceInstanceId]
, [ServiceName]
, [ServiceStartMode]
, [SqlCharSet]
, [SqlCharSetName]
, [SqlDomainGroup]
, [SqlSortOrder]
, [SqlSortOrderName]
, [Status]
, [TapeLoadWaitTime]
, [TcpEnabled]
, [VersionMajor]
, [VersionMinor]
, [VersionString] 
   FROM dbo.syn_sysutility_ucp_smo_servers;
GO

-- =============================================
-- Create View : [sysutility_ucp_databases]
-- All of the properties are auto-generated using the below select statement
-- select ', [' +property_name+']' from [msdb].[dbo].[sysutility_mi_smo_properties_to_collect_internal] where object_type = 2
-- =============================================
IF OBJECT_ID(N'[dbo].[sysutility_ucp_databases]', 'V') IS NOT NULL
BEGIN
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_databases]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_databases]
END;
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_databases]', 0, 1) WITH NOWAIT;
GO

CREATE VIEW dbo.sysutility_ucp_databases
AS
   SELECT	S.urn
        , S.parent_urn
        , S.Collation
        , S.CompatibilityLevel
        , S.CreateDate
        , S.EncryptionEnabled
        , S.Name
        , S.server_instance_name
        , S.powershell_path
        , S.RecoveryModel
        , [S].[Trustworthy]
        , [S].processing_time
      FROM [dbo].[syn_sysutility_ucp_databases] AS S
GO

-- =============================================
-- Create View : [sysutility_ucp_filegroups]
-- All of the properties are auto-generated using the below select statement
-- select ', [' +property_name+']' from [msdb].[dbo].[sysutility_mi_smo_properties_to_collect_internal] where object_type = 4
-- =============================================
IF OBJECT_ID(N'[dbo].[sysutility_ucp_filegroups]', 'V') IS NOT NULL
BEGIN
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_filegroups]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_filegroups]
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_filegroups]', 0, 1) WITH NOWAIT;
GO

CREATE VIEW dbo.sysutility_ucp_filegroups
AS
   SELECT  [S].[urn]
        , [S].[parent_urn]
        , [S].[Name]
        , [S].[server_instance_name]
        , [S].[database_name]
        , [S].[powershell_path]
        , [S].[processing_time]
        FROM [dbo].[syn_sysutility_ucp_filegroups] S
GO

-- =============================================
-- Create View : [sysutility_ucp_datafiles]
-- All of the properties are auto-generated using the below select statement
-- select ', [' +property_name+']' from [msdb].[dbo].[sysutility_mi_smo_properties_to_collect_internal] where object_type = 5
-- =============================================
IF OBJECT_ID(N'[dbo].[sysutility_ucp_datafiles]', 'V') IS NOT NULL
BEGIN
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_datafiles]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_datafiles]
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_datafiles]', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_datafiles
AS
SELECT  [S].[urn]
        , [S].[parent_urn]
        , [S].[Growth]
        , [S].[GrowthType]
        , [S].[MaxSize]
        , [S].[Name]
        , [S].[Size]
        , [S].[UsedSpace]
        , [S].[FileName]
        , [S].[VolumeFreeSpace]
        , [S].[server_instance_name]
        , [S].[database_name]
        , [S].[filegroup_name]
        , [S].[powershell_path]
        , [S].[volume_name]
        , [S].[volume_device_id]
        , [S].[physical_server_name]
        , [S].[available_space] -- in bytes
        , CASE WHEN [S].[available_space] = 0.0 THEN 0.0 ELSE ([S].[UsedSpace] * 100)/[S].[available_space] END AS percent_utilization
        , [S].[processing_time]
FROM [dbo].[syn_sysutility_ucp_datafiles] S
GO

-- =============================================
-- Create View : [sysutility_ucp_logfiles]
-- All of the properties are auto-generated using the below select statement
-- select ', [' +property_name+']' from [msdb].[dbo].[sysutility_mi_smo_properties_to_collect_internal] where object_type = 3
-- =============================================
IF OBJECT_ID(N'[dbo].[sysutility_ucp_logfiles]', 'V') IS NOT NULL
BEGIN
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_logfiles]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_logfiles]
END
GO
RAISERROR ('Creating view [dbo].[sysutility_ucp_logfiles]', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_logfiles
AS
SELECT  [S].[urn]
        , [S].[parent_urn]
        , [S].[Growth]
        , [S].[GrowthType]
        , [S].[MaxSize]
        , [S].[Name]
        , [S].[Size]
        , [S].[UsedSpace]
        , [S].[FileName]
        , [S].[VolumeFreeSpace]
        , [S].[server_instance_name]
        , [S].[database_name]
        , [S].[powershell_path]
        , [S].[volume_name]
        , [S].[volume_device_id]
        , [S].[physical_server_name]
        , [S].[available_space] -- in bytes
        , CASE WHEN [S].[available_space] = 0.0 THEN 0.0 ELSE ([S].[UsedSpace] * 100)/[S].[available_space] END AS percent_utilization
        , [S].[processing_time]
FROM [dbo].[syn_sysutility_ucp_logfiles] S
GO

-- =============================================
-- Create View : [sysutility_ucp_database_files]
-- Simple (union) view over sysutility_ucp_datafiles and sysutility_ucp_logfiles
-- =============================================
IF OBJECT_ID(N'[dbo].[sysutility_ucp_database_files]', N'V') IS NOT NULL
BEGIN
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_database_files]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_database_files]
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_database_files]', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_database_files
AS
        SELECT [S].[server_instance_name], [S].[database_name], [S].[filegroup_name], [S].[Name] AS [Name],
               [S].[volume_name], [S].[volume_device_id], [S].[FileName], [S].[Growth], [S].[GrowthType],
               [S].[processing_time], [S].[powershell_path],
               1 AS [file_type],
               [S].[MaxSize], [S].[Size], [S].[UsedSpace], [S].[available_space], [S].[percent_utilization]
        FROM [dbo].[sysutility_ucp_datafiles] AS S
        UNION ALL
        SELECT [S].[server_instance_name], [S].[database_name], N'' AS [filegroup_name], [S].[Name] AS [Name],
               [S].[volume_name], [S].[volume_device_id], [S].[FileName], [S].[Growth], [S].[GrowthType],
               [S].[processing_time], [S].[powershell_path],
               2 AS [file_type],
               [S].[MaxSize], [S].[Size], [S].[UsedSpace], [S].[available_space], [S].[percent_utilization]
        FROM [dbo].[sysutility_ucp_logfiles] AS S  
GO

/****************************************************************************/
/*                                                                          */
/* Creating a utilization view on top of several object views in CMR which  */
/* collects the data and log file related data for all databases in the     */
/* utility                                                                   */                                                                   
/*                                                                          */
/****************************************************************************/

IF (OBJECT_ID(N'[dbo].[sysutility_ucp_mi_database_file_space_utilizations]', 'V') IS NOT NULL)
BEGIN 
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_mi_database_file_space_utilizations]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_mi_database_file_space_utilizations]
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_mi_database_file_space_utilizations]', 0, 1) WITH NOWAIT;
GO

CREATE VIEW [dbo].[sysutility_ucp_mi_database_file_space_utilizations] AS
    SELECT	df.server_instance_name, 
            df.database_name, 
            df.filegroup_name,
            df.Name,
            df.volume_name,
            df.volume_device_id,
            df.FileName AS databasefile_name, 
            df.percent_utilization AS current_utilization, 
            df.UsedSpace AS used_space, 
            df.available_space AS available_space,
            10 AS under_utilization,  
            70 AS over_utilization,
            df.file_type,
            df.GrowthType AS growth_type	
    FROM	msdb.dbo.sysutility_ucp_database_files AS df
GO



/****************************************************************************/
/*                                                                          */
/* Creating a utilization view on top of several object views in CMR which  */
/* collects the data and log file related data for all deployed dacs in the */
/* utility                                                                   */                                                                   
/*                                                                          */
/****************************************************************************/

IF (OBJECT_ID(N'[dbo].[sysutility_ucp_dac_database_file_space_utilizations]', 'V') IS NOT NULL)
BEGIN 
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_dac_database_file_space_utilizations]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_dac_database_file_space_utilizations]
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_dac_database_file_space_utilizations]', 0, 1) WITH NOWAIT;
GO

CREATE VIEW [dbo].[sysutility_ucp_dac_database_file_space_utilizations] AS
    SELECT	dd.dac_server_instance_name AS server_instance_name, 
            dd.dac_name AS dac_name,
            df.[filegroup_name],
            df.[Name],
            df.volume_name,
            df.volume_device_id,
            df.FileName AS databasefile_name, 
            df.percent_utilization AS current_utilization, 
            df.UsedSpace AS used_space, 
            df.available_space,
            10 AS under_utilization, 
            70 AS over_utilization,
            df.file_type,
            df.GrowthType AS growth_type
    FROM	msdb.dbo.sysutility_ucp_deployed_dacs AS dd,
            msdb.dbo.sysutility_ucp_database_files AS df
    WHERE dd.dac_server_instance_name = df.server_instance_name
      AND dd.dac_name = df.database_name         
GO


/****************************************************************************/
/*                                                                          */
/* Creating a utilization view on top of several object views in CMR which  */
/* collects the volume related data for all databases in the utility         */                                                                   
/*                                                                          */
/****************************************************************************/

IF (OBJECT_ID(N'[dbo].[sysutility_ucp_mi_volume_space_utilizations]', 'V') IS NOT NULL)
BEGIN 
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_mi_volume_space_utilizations]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_mi_volume_space_utilizations]
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_mi_volume_space_utilizations]', 0, 1) WITH NOWAIT;
GO

CREATE VIEW [dbo].[sysutility_ucp_mi_volume_space_utilizations] AS(
-- TODO VSTS 280036(rnagpal) : Temporarily Keeping under_utilization to 10 and over_utilization to 70 for now
-- since we might reintroduce them in near future which will not require any interface change for the 
-- Utility object model / UI. Presently, we are not exposing under and over utilization thresholds in UI
-- so they are not exposed. If that remains the same till KJ CTP2, i'll remove them.
SELECT	vol.physical_server_name AS physical_server_name, 
		svr.Name as server_instance_name,
		vol.volume_name AS volume_name, 
		vol.volume_device_id AS volume_device_id, 
		vol.total_space_utilization AS current_utilization, 
		vol.total_space_used AS used_space,
		vol.total_space AS available_space,
		10 AS under_utilization, 
		70 AS over_utilization
FROM	msdb.dbo.sysutility_ucp_volumes AS vol,
		msdb.dbo.sysutility_ucp_instances AS svr
WHERE	vol.physical_server_name = svr.ComputerNamePhysicalNetBIOS)
GO


/****************************************************************************/
/*                                                                          */
/* Creating a utilization view on top of several object views in CMR which  */
/* collects the volume related data for all deployed dacs in the utility     */                                                                   
/*                                                                          */
/****************************************************************************/

IF (OBJECT_ID(N'[dbo].[sysutility_ucp_dac_volume_space_utilizations]', 'V') IS NOT NULL)
BEGIN 
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_dac_volume_space_utilizations]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_dac_volume_space_utilizations]
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_dac_volume_space_utilizations]', 0, 1) WITH NOWAIT;
GO

CREATE VIEW [dbo].[sysutility_ucp_dac_volume_space_utilizations] AS(
-- TODO VSTS 280036(rnagpal) : Temporarily Keeping under_utilization to 10 and over_utilization to 70 for now
-- since we might reintroduce them in near future which will not require any interface change for the 
-- Utility object model / UI. Presently, we are not exposing under and over utilization thresholds in UI
-- so they are not exposed. If that remains the same till KJ CTP2, i'll remove them.
SELECT	vol.physical_server_name AS physical_server_name,
		dd.dac_name AS dac_name,
		dd.dac_server_instance_name AS server_instance_name, 
		vol.volume_name AS volume_name, 
		vol.volume_device_id AS volume_device_id, 
		vol.total_space_utilization AS current_utilization, 
		vol.total_space_used AS used_space,
		vol.total_space AS available_space,
		10 AS under_utilization, 
		70 AS over_utilization
		
FROM	msdb.dbo.sysutility_ucp_volumes AS vol,
		msdb.dbo.sysutility_ucp_deployed_dacs AS dd
		
WHERE	vol.physical_server_name = dd.dac_physical_server_name)
GO


/****************************************************************************/
/*                                                                          */
/* Creating a utilization view on top of several object views in CMR which  */
/* collects the server related data for all instances in the utility         */                                                                   
/*                                                                          */
/****************************************************************************/
-- TODO VSTS 280036(rnagpal) : Temporarily Keeping under_utilization to 10 and over_utilization to 70 for now
-- since we might reintroduce them in near future which will not require any interface change for the 
-- Utility object model / UI. Presently, we are not exposing under and over utilization thresholds in UI
-- so they are not exposed. If that remains the same till KJ CTP2, i'll remove them.
IF (OBJECT_ID(N'[dbo].[sysutility_ucp_mi_cpu_utilizations]', 'V') IS NOT NULL)
BEGIN 
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_mi_cpu_utilizations]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_mi_cpu_utilizations];
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_mi_cpu_utilizations]', 0, 1) WITH NOWAIT;
GO

CREATE VIEW [dbo].[sysutility_ucp_mi_cpu_utilizations]
AS
SELECT svr.Name AS server_instance_name, 
   10 AS under_utilization, 
   CAST(svr.ProcessorUsage AS INT) AS current_utilization, 
   70 AS over_utilization
FROM	msdb.dbo.sysutility_ucp_instances AS svr;
GO


/****************************************************************************/
/*                                                                          */
/* Creating a utilization view on top of several object views in CMR which  */
/* collects the computer related data for all computers in the utility       */                                                                   
/*                                                                          */
/****************************************************************************/
-- TODO VSTS 280036(rnagpal) : Temporarily Keeping under_utilization to 10 and over_utilization to 70 for now
-- since we might reintroduce them in near future which will not require any interface change for the 
-- Utility object model / UI. Presently, we are not exposing under and over utilization thresholds in UI
-- so they are not exposed. If that remains the same till KJ CTP2, i'll remove them.
IF (OBJECT_ID(N'[dbo].[sysutility_ucp_computer_cpu_utilizations]', 'V') IS NOT NULL)
BEGIN 
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_computer_cpu_utilizations]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_computer_cpu_utilizations];
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_computer_cpu_utilizations]', 0, 1) WITH NOWAIT;
GO

CREATE VIEW [dbo].[sysutility_ucp_computer_cpu_utilizations]
AS
SELECT comp.physical_server_name AS physical_server_name, 
   10 AS under_utilization, 
   comp.processor_utilization AS current_utilization, 
   70 AS over_utilization
FROM	msdb.dbo.sysutility_ucp_computers AS comp
GO



/****************************************************************************/
/*                                                                          */
/* Creating a utilization view on top of several object views in CMR which  */
/* collects the DAC related data for all DACs in the utility                 */                                                                   
/*                                                                          */
/****************************************************************************/
-- TODO VSTS 280036(rnagpal) : Temporarily Keeping under_utilization to 10 and over_utilization to 70 for now
-- since we might reintroduce them in near future which will not require any interface change for the 
-- Utility object model / UI. Presently, we are not exposing under and over utilization thresholds in UI
-- so they are not exposed. If that remains the same till KJ CTP2, i'll remove them.
IF (OBJECT_ID(N'[dbo].[sysutility_ucp_dac_cpu_utilizations]', 'V') IS NOT NULL)
BEGIN 
   RAISERROR ('Dropping view [dbo].[sysutility_ucp_dac_cpu_utilizations]', 0, 1) WITH NOWAIT;
   DROP VIEW [dbo].[sysutility_ucp_dac_cpu_utilizations];
END
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_dac_cpu_utilizations]', 0, 1) WITH NOWAIT;
GO

CREATE VIEW [dbo].[sysutility_ucp_dac_cpu_utilizations]
AS
SELECT
   dac.dac_name AS dac_name, 
   dac.dac_server_instance_name AS server_instance_name, 
   10 AS under_utilization, 
   dac.dac_percent_total_cpu_utilization AS current_utilization, 
   70 AS over_utilization
 FROM	msdb.dbo.sysutility_ucp_deployed_dacs AS dac
GO



/********************************************************************
Procedure sp_sysutility_ucp_provision_utility_object_internal
   Helper proc that grants permissions (based on object type) to a role. 
********************************************************************/
IF  OBJECT_ID(N'dbo.sp_sysutility_ucp_provision_utility_object_internal') IS NOT NULL
BEGIN
   RAISERROR('Dropping procedure dbo.sp_sysutility_ucp_provision_utility_object_internal', 0, 1)  WITH NOWAIT;
   DROP PROCEDURE dbo.sp_sysutility_ucp_provision_utility_object_internal;
END
GO
RAISERROR('Creating procedure dbo.sp_sysutility_ucp_provision_utility_object_internal', 0, 1)  WITH NOWAIT;
GO

CREATE PROCEDURE dbo.sp_sysutility_ucp_provision_utility_object_internal
   @object_name sysname, @role_name sysname
WITH EXECUTE AS CALLER
AS
BEGIN
   DECLARE @sql_stmt nvarchar(max);
   DECLARE @grant_type NVARCHAR(20);
   DECLARE @object_type char(2);
   DECLARE @database_name sysname;
   DECLARE @quoted_object_name_with_dbo nvarchar(max);
   
   SET @database_name = DB_NAME();
   SET @quoted_object_name_with_dbo = 'dbo.' + QUOTENAME(@object_name);

   SELECT @object_type = [type] FROM sys.objects WHERE object_id = OBJECT_ID(@quoted_object_name_with_dbo);

   -- TSQL or CLR procs and non-inline functions
   --    P  - stored proc (TSQL)
   --    PC - stored proc (SQLCLR)
   --    FN - scalar function (TSQL)
   --    FS - scalar function (SQLCLR)
   IF (@object_type IN ('P', 'PC', 'FN', 'FS'))
   BEGIN
      SET @grant_type = 'EXECUTE';
   END

   -- Views, inline functions, tables
   --    V  - view
   --    IF - inline function (TSQL)
   --    U  - user table
   --    S  - system table
   --    TF - table-valued function (TSQL)
   --    FT - table-valued function (SQLCLR)
   ELSE IF (@object_type IN ('V', 'IF', 'U', 'S', 'FT', 'TF'))
   BEGIN
      SET @grant_type = 'SELECT';
   END;
   ELSE BEGIN
      -- The object '%s' does not exist in database '%s' or is invalid for this operation.
      RAISERROR (15009, 16, 1, @quoted_object_name_with_dbo, @database_name);
      RETURN;
   END;

   SELECT @sql_stmt = N'GRANT '+ @grant_type +' ON ' + @quoted_object_name_with_dbo + ' TO ' + QUOTENAME(@role_name);
   RAISERROR ('Executing: %s', 0, 1, @sql_stmt) WITH NOWAIT
   EXEC sp_executesql @sql_stmt;
END;
GO



/**********************************************************************/
/* Create function fn_sysutility_ucp_get_aggregated_health       
    
   This function determines the aggregated health state based on the  
   input arguments over and under utilization policy violations.     
        
   The aggregated health state computation algorithm is as follows:   
   If both over and under utililization policy violations are 0 then its steady state (green) 
   If there is over utilization policy violation, then its over utilized state (red) 
   If there is under utilization policy violation (with no over utilization) then its under utilized state (orange)*/
/**********************************************************************/
IF(OBJECT_ID(N'dbo.fn_sysutility_ucp_get_aggregated_health') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping function fn_sysutility_ucp_get_aggregated_health', 0, 1) WITH NOWAIT;
    DROP FUNCTION dbo.fn_sysutility_ucp_get_aggregated_health
END
GO
RAISERROR ('Creating function fn_sysutility_ucp_get_aggregated_health', 0, 1) WITH NOWAIT;
GO
CREATE FUNCTION [dbo].[fn_sysutility_ucp_get_aggregated_health]
(
	@is_over_utilized INT,
	@is_under_utilized INT
)
RETURNS TABLE
AS
RETURN ((SELECT 
CASE WHEN 0 = @is_over_utilized AND 0 = @is_under_utilized THEN 1 
ELSE CASE WHEN @is_over_utilized > 0 THEN 3 
ELSE 2 END END AS val))
GO	

--**********************************************************************
-- Create view sysutility_ucp_volume_powershell_path             
 
-- View Description:
-- Returns the powershell path of the volumes 
-- Powershell path - SQLSERVER:\Utility\<server_instance_name>\Computers\<computer_name>\Volumes\<volume_name>

 --Attribute description:
 -- physical_server_name - the name of the computer in the utility
 -- volume_name - volume mounted on the computer 
 -- powershell_path - the path representing the volume hiearchy in powershell
--*********************************************************************
IF (OBJECT_ID(N'[dbo].[sysutility_ucp_volume_powershell_path]', 'V') IS NOT NULL) 
BEGIN
    RAISERROR ('Dropping view [dbo].[sysutility_ucp_volume_powershell_path]', 0, 1) WITH NOWAIT;
	DROP VIEW dbo.sysutility_ucp_volume_powershell_path
END	
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_volume_powershell_path]...', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_volume_powershell_path AS
(    
    SELECT vo.physical_server_name
        , vo.volume_name
        , vo.volume_device_id
        , N'SQLSERVER:\Utility\'+CASE WHEN 0 = CHARINDEX(N'\', CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')), 1) THEN CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')) + N'\DEFAULT' ELSE CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')) END 
         +N'\Computers\'+msdb.dbo.fn_encode_sqlname_for_powershell(vo.physical_server_name) 
         +N'\Volumes\'+msdb.dbo.fn_encode_sqlname_for_powershell(vo.volume_name) AS powershell_path
    FROM msdb.dbo.sysutility_ucp_volumes vo 
)
GO

--**********************************************************************
-- Create view sysutility_ucp_instance_policies             
 
-- View Description:
-- Returns the resource health policy details for the managed instances 
-- Powershell path - SQLSERVER:\SQL\<server_instance_name>        
-- smo_server_urn - Server[@Name='<server_instance_name>']
-- utility_server_urn - Utility[@Name='<utility_name>']/Server[@Name='<server_instance_name>']        

 --Attribute description:
 -- server_instance_name - the name of the server instance
 -- smo_server_urn - the urn of the server in smo hiearchy (Server as the root)
 -- utility_server_urn - the urn of the server in utility hiearchy (Utility as the root)
 -- powershell_path - the path representing the server hiearchy in powershell
 -- policy_id - resource health policy global / local (if exists)
 -- resource_type - resource type on which the policy is configured
 -- target_type - target type against which the policy is evaluated
 -- utilization_type - type of the policy utilization (over/under) configuration
--*********************************************************************
IF (OBJECT_ID(N'[dbo].[sysutility_ucp_instance_policies]', 'V') IS NOT NULL) 
BEGIN
    RAISERROR ('Dropping view [dbo].[sysutility_ucp_instance_policies]', 0, 1) WITH NOWAIT;
	DROP VIEW dbo.sysutility_ucp_instance_policies
END	
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_instance_policies]...', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_instance_policies AS
(    
    SELECT sp.server_instance_name 
        , sp.smo_server_urn
        , sp.utility_server_urn
        , sp.powershell_path
        , ISNULL(lp.policy_id, sp.policy_id) AS policy_id -- if exists get local (overridden) policy, else return global policy 
        , ISNULL(lp.is_global_policy, 1) AS is_global_policy
        , sp.resource_type
        , sp.target_type
        , sp.utilization_type
    FROM (
            -- fetch the global policies 
            SELECT sv.Name AS server_instance_name
                , sv.urn AS smo_server_urn
                , N'Utility[@Name=''' + CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')) + N''']/' + sv.urn AS utility_server_urn
                , sv.powershell_path AS powershell_path
                , gp.policy_id
                , gp.resource_type
                , gp.target_type
                , gp.utilization_type
            FROM msdb.dbo.sysutility_ucp_instances sv
                , msdb.dbo.sysutility_ucp_policies gp
            WHERE gp.rollup_object_type = 2  
                AND gp.is_global_policy = 1    
        ) sp
        LEFT JOIN msdb.dbo.sysutility_ucp_policies lp -- fetch the local policies (if exists)
        ON lp.rollup_object_urn = sp.utility_server_urn 
            AND lp.rollup_object_type = 2
            AND lp.is_global_policy = 0
            AND lp.resource_type = sp.resource_type
            AND lp.target_type = sp.target_type
            AND lp.utilization_type = sp.utilization_type
)
GO	

--**********************************************************************
-- Create view sysutility_ucp_computer_policies             
 
-- View Description:
-- Returns the resource health policy details for the computers
-- Powershell path - SQLSERVER:\Utility\<server_instance_name>\Computers\<computer_name>
-- computer_urn - Utility[@Name='<utility_name>']/Computer[@Name='<computer_name>']

 --Attribute description:
 -- physical_server_name - the name of the computer
 -- computer_urn - the urn of the computer in utility hiearchy 
 -- powershell_path - the path representing the computer hiearchy in powershell
 -- policy_id - resource health policy global / local (if exists)
 -- resource_type - resource type on which the policy is configured
 -- target_type - target type against which the policy is evaluated
 -- utilization_type - type of the policy utilization (over/under) configuration
--*********************************************************************
IF (OBJECT_ID(N'[dbo].[sysutility_ucp_computer_policies]', 'V') IS NOT NULL) 
BEGIN
    RAISERROR ('Dropping view [dbo].[sysutility_ucp_computer_policies]', 0, 1) WITH NOWAIT;
	DROP VIEW dbo.sysutility_ucp_computer_policies
END	
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_computer_policies]...', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_computer_policies AS
(    
    SELECT cp.physical_server_name
        , cp.computer_urn
        , cp.powershell_path
        , ISNULL(lp.policy_id, cp.policy_id) AS policy_id -- if exists get local (overridden) policy, else return global policy 
        , ISNULL(lp.is_global_policy, 1) AS is_global_policy
        , cp.resource_type
        , cp.target_type
        , cp.utilization_type
    FROM 
        (
            -- fetch the global policies 
            -- Should we be using "virtual_server_name" or "physical_server_name" here?
            SELECT co.physical_server_name AS physical_server_name
                , co.urn AS computer_urn
                , co.powershell_path AS powershell_path
                , gp.policy_id
                , gp.resource_type
                , gp.target_type
                , gp.utilization_type     
            FROM msdb.dbo.sysutility_ucp_computers co
                , msdb.dbo.sysutility_ucp_policies gp
            WHERE gp.rollup_object_type = 3  
                AND gp.is_global_policy = 1    
        ) cp
        LEFT JOIN msdb.dbo.sysutility_ucp_policies lp -- fetch the local policies (if exists)
        ON lp.rollup_object_urn = cp.computer_urn
            AND lp.rollup_object_type = 3
            AND lp.is_global_policy = 0
            AND lp.resource_type = cp.resource_type
            AND lp.target_type = cp.target_type
            AND lp.utilization_type = cp.utilization_type
)
GO

--**********************************************************************
-- Create view sysutility_ucp_dac_policies             
 
-- View Description:
-- Returns the resource health policy details for the dac's
-- Powershell path - SQLSERVER:\Utility\<server_instance_name>\DeployedDacs\<dac_name>.<dac_server_instance_name>
-- dac_urn -  Utility[@Name='<utility_name>']/DeployedDac[@Name='<dac_name>' and @ServerInstanceName='<dac_server_instnace>']

 --Attribute description:
 -- dac_name - the name of the data-tier application
 -- dac_server_instance_name - the server\instance on which the dac is hosted
 -- dac_urn - the urn of the dac in utility hiearchy 
 -- powershell_path - the path representing the dac hiearchy in powershell
 -- policy_id - resource health policy global / local (if exists)
 -- resource_type - resource type on which the policy is configured
 -- target_type - target type against which the policy is evaluated
 -- utilization_type - type of the policy utilization (over/under) configuration
--*********************************************************************
IF (OBJECT_ID(N'[dbo].[sysutility_ucp_dac_policies]', 'V') IS NOT NULL) 
BEGIN
    RAISERROR ('Dropping view [dbo].[sysutility_ucp_dac_policies]', 0, 1) WITH NOWAIT;
	DROP VIEW dbo.sysutility_ucp_dac_policies
END	
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_dac_policies]...', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_dac_policies AS
(    
    SELECT dp.dac_name
        , dp.dac_server_instance_name
        , dp.dac_urn
        , dp.powershell_path
        , ISNULL(lp.policy_id, dp.policy_id) AS policy_id -- if exists get local (overridden) policy, else return global policy 
        , ISNULL(lp.is_global_policy, 1) AS is_global_policy
        , dp.resource_type
        , dp.target_type
        , dp.utilization_type
    FROM 
        (
            -- fetch the global policies 
            SELECT dd.dac_name
                , dd.dac_server_instance_name 
                , dd.urn AS dac_urn
                , dd.powershell_path AS powershell_path
                , gp.policy_id
                , gp.resource_type
                , gp.target_type
                , gp.utilization_type
            FROM msdb.dbo.sysutility_ucp_deployed_dacs dd
                , msdb.dbo.sysutility_ucp_policies gp
            WHERE gp.rollup_object_type = 1  
                AND gp.is_global_policy = 1    
        ) dp
        LEFT JOIN msdb.dbo.sysutility_ucp_policies lp -- fetch the local policies (if exists)
        ON lp.rollup_object_urn = dp.dac_urn 
            AND lp.rollup_object_type = 1
            AND lp.is_global_policy = 0
            AND lp.resource_type = dp.resource_type
            AND lp.target_type = dp.target_type
            AND lp.utilization_type = dp.utilization_type
)
GO	

--**********************************************************************
-- Create view sysutility_ucp_dac_policy_type             
 
-- This view selects the existing dac's and indicates whther the corresponding
-- resource health policy is global or overridden

 --Attributes:
 --1. dac_name - the name of the dac (rollup object)
 --2. dac_server_instance_name - the name of the smo server in the form server_name\instance_name
 --3. is_global_policy - Indicates whether the policy type is global or overridden
--*********************************************************************
IF (OBJECT_ID(N'[dbo].[sysutility_ucp_dac_policy_type]', 'V') IS NOT NULL) 
BEGIN
    RAISERROR ('Dropping view [dbo].[sysutility_ucp_dac_policy_type]', 0, 1) WITH NOWAIT;
	DROP VIEW dbo.sysutility_ucp_dac_policy_type
END	
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_dac_policy_type]...', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_dac_policy_type AS
(    

    -- Target types
    -- computer_type = 1
    -- volume_type = 6

    -- Resource types
    -- processor_type = 3
    -- space_type = 1

    SELECT DISTINCT dd.dac_server_instance_name
	    , dd.dac_name
        , CASE WHEN ((0 < ip.is_policy_overridden) OR (0 < cp.is_policy_overridden)) THEN 1 ELSE 0 END AS is_policy_overridden
    FROM msdb.dbo.sysutility_ucp_deployed_dacs dd 
        , (SELECT dac_name, dac_server_instance_name, SUM(CASE WHEN 0 < is_global_policy THEN 0 ELSE 1 END) AS is_policy_overridden
           FROM msdb.dbo.sysutility_ucp_dac_policies 
           GROUP BY dac_name, dac_server_instance_name) ip 
        , (SELECT physical_server_name, SUM(CASE WHEN 0 < is_global_policy THEN 0 ELSE 1 END) AS is_policy_overridden
           FROM msdb.dbo.sysutility_ucp_computer_policies  
           GROUP BY physical_server_name) cp     
    WHERE ip.dac_name = dd.dac_name
        AND ip.dac_server_instance_name = dd.dac_server_instance_name
        AND cp.physical_server_name = dd.dac_physical_server_name	
) 
GO

--**********************************************************************
-- Create view sysutility_ucp_instance_policy_type
 
-- This view selects the existing server's and indicates whther the corresponding
-- resource health policy is global or overridden

 --Attributes:
 --1. server_instance_name - the name of the smo server in the form server_name\instance_name
 --2. is_global_policy - Indicates whether the policy type is global or overridden
--*********************************************************************
IF (OBJECT_ID(N'[dbo].[sysutility_ucp_instance_policy_type]', 'V') IS NOT NULL) 
BEGIN
    RAISERROR ('Dropping view [dbo].[sysutility_ucp_instance_policy_type]', 0, 1) WITH NOWAIT;
	DROP VIEW dbo.sysutility_ucp_instance_policy_type
END	
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_instance_policy_type]...', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_instance_policy_type AS
(  
    -- Target types
    -- computer_type = 1
    -- volume_type = 6

    -- Resource types
    -- processor_type = 3
    -- space_type = 1

    SELECT sv.Name AS server_instance_name
        , CASE WHEN ((0 < ip.is_policy_overridden) OR (0 < cp.is_policy_overridden)) THEN 1 ELSE 0 END AS is_policy_overridden
    FROM msdb.dbo.sysutility_ucp_instances sv
        , (SELECT server_instance_name , SUM(CASE WHEN 0 < is_global_policy THEN 0 ELSE 1 END) AS is_policy_overridden
           FROM msdb.dbo.sysutility_ucp_instance_policies 
           GROUP BY server_instance_name) ip 
        , (SELECT physical_server_name, SUM(CASE WHEN 0 < is_global_policy THEN 0 ELSE 1 END) AS is_policy_overridden
           FROM msdb.dbo.sysutility_ucp_computer_policies  
           GROUP BY physical_server_name) cp 
    WHERE ip.server_instance_name = sv.Name
        AND cp.physical_server_name = sv.ComputerNamePhysicalNetBIOS
) 
GO

/**********************************************************************/
/* Create table sysutility_ucp_mi_file_space_health_internal         */
/* This table stores the file-space health states for data/log file groups of MI */
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_mi_file_space_health_internal', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table dbo.sysutility_ucp_mi_file_space_health_internal', 0, 1) WITH NOWAIT;

    CREATE TABLE dbo.sysutility_ucp_mi_file_space_health_internal
    (
        server_instance_name SYSNAME NOT NULL
        , database_name SYSNAME NOT NULL
        , fg_name SYSNAME NOT NULL
        , over_utilized_count INT NOT NULL DEFAULT(0)
        , under_utilized_count INT NOT NULL DEFAULT(0)
        , file_type INT NOT NULL 
        , set_number INT NOT NULL DEFAULT(0)
        , processing_time DATETIMEOFFSET(7) NOT NULL DEFAULT(SYSDATETIMEOFFSET())

        CONSTRAINT [PK_sysutility_ucp_mi_file_space_health_internal_name] 
          PRIMARY KEY CLUSTERED (set_number, server_instance_name, database_name, fg_name)
    )
END
GO

/**********************************************************************/
/* Create table sysutility_ucp_mi_database_health_internal          */
/* This table stores the file-space health states for databases of MI */
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_mi_database_health_internal', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table dbo.sysutility_ucp_mi_database_health_internal', 0, 1) WITH NOWAIT;

    CREATE TABLE dbo.sysutility_ucp_mi_database_health_internal
    (
        server_instance_name SYSNAME NOT NULL
        , database_name SYSNAME NOT NULL
        , over_utilized_count INT NOT NULL DEFAULT(0)
        , under_utilized_count INT NOT NULL DEFAULT(0)
        , set_number INT NOT NULL DEFAULT(0)
        , processing_time DATETIMEOFFSET(7) NOT NULL DEFAULT(SYSDATETIMEOFFSET())

        CONSTRAINT [PK_sysutility_ucp_mi_database_health_internal_name] 
          PRIMARY KEY CLUSTERED (set_number, server_instance_name, database_name)
    )
END
GO    

/**********************************************************************/
/* Create table sysutility_ucp_dac_file_space_health_internal        */
/* This table stores the file-space health states for data/log file groups of DAC */
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_dac_file_space_health_internal', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table dbo.sysutility_ucp_dac_file_space_health_internal', 0, 1) WITH NOWAIT;

    CREATE TABLE dbo.sysutility_ucp_dac_file_space_health_internal
    (
        dac_name SYSNAME NOT NULL
        , dac_server_instance_name SYSNAME NOT NULL
        , fg_name SYSNAME NOT NULL
        , over_utilized_count INT NOT NULL DEFAULT(0)
        , under_utilized_count INT NOT NULL DEFAULT(0)
        , file_type INT NOT NULL 
        , set_number INT NOT NULL DEFAULT(0)
        , processing_time DATETIMEOFFSET(7) NOT NULL DEFAULT(SYSDATETIMEOFFSET())

        CONSTRAINT [PK_sysutility_ucp_dac_file_space_health_internal_name] 
          PRIMARY KEY CLUSTERED (set_number, dac_server_instance_name, dac_name, fg_name)
    )
END
GO	

/**********************************************************************/
/* Create table sysutility_ucp_mi_volume_space_health_internal       */
/* This table stores the volume-space health states for computer MI's */
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_mi_volume_space_health_internal', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table dbo.sysutility_ucp_mi_volume_space_health_internal', 0, 1) WITH NOWAIT;

    CREATE TABLE dbo.sysutility_ucp_mi_volume_space_health_internal
    (
        physical_server_name SYSNAME NOT NULL
        , server_instance_name SYSNAME NOT NULL
        , volume_device_id SYSNAME NOT NULL
        , health_state INT NOT NULL
        , set_number INT NOT NULL DEFAULT(0)
        , processing_time DATETIMEOFFSET(7) NOT NULL DEFAULT(SYSDATETIMEOFFSET())

        CONSTRAINT [PK_sysutility_ucp_mi_volume_space_health_internal_name] 
          PRIMARY KEY CLUSTERED (set_number, server_instance_name, volume_device_id)
    )
END
GO	

/**********************************************************************/
/* Create table sysutility_ucp_computer_cpu_health_internal           */
/* This table stores the processor health states for computer         */
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_computer_cpu_health_internal', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table dbo.sysutility_ucp_computer_cpu_health_internal', 0, 1) WITH NOWAIT;

    CREATE TABLE dbo.sysutility_ucp_computer_cpu_health_internal
    (
        physical_server_name SYSNAME NOT NULL
        , health_state INT NOT NULL
        , set_number INT NOT NULL DEFAULT(0)
        , processing_time DATETIMEOFFSET(7) NOT NULL DEFAULT(SYSDATETIMEOFFSET())

        CONSTRAINT [PK_sysutility_ucp_computer_cpu_health_internal_name] 
          PRIMARY KEY CLUSTERED (set_number, physical_server_name)
    )
END
GO	

/**********************************************************************/
/* Create view sysutility_ucp_computer_cpu_health                     */
/* This view returns the latest processor health states for computers */
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_computer_cpu_health', 'V') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping view dbo.sysutility_ucp_computer_cpu_health', 0, 1) WITH NOWAIT;
    DROP VIEW dbo.sysutility_ucp_computer_cpu_health
END
GO
	CREATE VIEW dbo.sysutility_ucp_computer_cpu_health 
	AS
	SELECT t.physical_server_name,
			t.health_state,
			t.processing_time
	FROM msdb.dbo.sysutility_ucp_computer_cpu_health_internal AS t
	WHERE t.set_number = (SELECT latest_health_state_id FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal])
GO	

/**********************************************************************/
/* Create view sysutility_ucp_mi_volume_space_health            */
/* This view returns the latest volume space health states for computers */
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_mi_volume_space_health', 'V') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping view dbo.sysutility_ucp_mi_volume_space_health', 0, 1) WITH NOWAIT;
    DROP VIEW dbo.sysutility_ucp_mi_volume_space_health
END
GO
	CREATE VIEW dbo.sysutility_ucp_mi_volume_space_health 
	AS
	SELECT t.physical_server_name,
			t.server_instance_name,
			t.volume_device_id,
			t.health_state,
			t.processing_time
	FROM msdb.dbo.sysutility_ucp_mi_volume_space_health_internal AS t
	WHERE t.set_number = (SELECT latest_health_state_id FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal])
GO	
       
/**********************************************************************/
/* Create view sysutility_ucp_mi_file_space_health              */
/* This view returns the latest file space health states for MI's     */
/**********************************************************************/
   
IF(OBJECT_ID(N'dbo.sysutility_ucp_mi_file_space_health', 'V') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping view dbo.sysutility_ucp_mi_file_space_health', 0, 1) WITH NOWAIT;
    DROP VIEW dbo.sysutility_ucp_mi_file_space_health
END
GO
	CREATE VIEW dbo.sysutility_ucp_mi_file_space_health 
	AS
	SELECT t.server_instance_name,
			t.database_name,
			t.fg_name,
			t.file_type,
			(SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.over_utilized_count, t.under_utilized_count)) health_state,
			t.processing_time
	FROM msdb.dbo.sysutility_ucp_mi_file_space_health_internal AS t
	WHERE t.set_number = (SELECT latest_health_state_id FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal])
GO

/**********************************************************************/
/* Create view sysutility_ucp_mi_database_health               */
/* This view returns the latest database health states for MI's       */
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_mi_database_health', 'V') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping view dbo.sysutility_ucp_mi_database_health', 0, 1) WITH NOWAIT;
    DROP VIEW dbo.sysutility_ucp_mi_database_health
END
GO
	CREATE VIEW dbo.sysutility_ucp_mi_database_health 
	AS
	SELECT t.server_instance_name,
			t.database_name,
			(SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.over_utilized_count, t.under_utilized_count)) health_state,
			t.processing_time
	FROM msdb.dbo.sysutility_ucp_mi_database_health_internal AS t
	WHERE t.set_number = (SELECT latest_health_state_id FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal])
GO

/**********************************************************************/
/* Create view sysutility_ucp_dac_database_file_space_health             */
/* This view returns the latest filespace health states for DAC's     */
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_dac_database_file_space_health', 'V') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping view dbo.sysutility_ucp_dac_database_file_space_health', 0, 1) WITH NOWAIT;
    DROP VIEW dbo.sysutility_ucp_dac_database_file_space_health
END
GO
	CREATE VIEW dbo.sysutility_ucp_dac_database_file_space_health 
	AS
	SELECT  t.dac_name
			, t.dac_server_instance_name
			, t.fg_name
			, t.file_type
			, (SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.over_utilized_count, t.under_utilized_count)) health_state
			, t.processing_time
	FROM msdb.dbo.sysutility_ucp_dac_file_space_health_internal AS t
	WHERE t.set_number = (SELECT latest_health_state_id FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal])
GO    

/**********************************************************************/
/* Create table sysutility_ucp_aggregated_dac_health_internal                  */
/* This table stores the aggregated health statistics for DAC's                */
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_aggregated_dac_health_internal', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table dbo.sysutility_ucp_aggregated_dac_health_internal', 0, 1) WITH NOWAIT;

	CREATE TABLE dbo.sysutility_ucp_aggregated_dac_health_internal
	(
		dac_count INT NOT NULL DEFAULT(0)
		, dac_healthy_count INT NOT NULL DEFAULT(0)
		, dac_unhealthy_count INT NOT NULL DEFAULT(0)
		, dac_over_utilize_count INT NOT NULL DEFAULT(0)
		, dac_under_utilize_count INT NOT NULL DEFAULT(0)
		, dac_on_over_utilized_computer_count INT NOT NULL DEFAULT(0)
		, dac_on_under_utilized_computer_count INT NOT NULL DEFAULT(0)
		, dac_with_files_on_over_utilized_volume_count INT NOT NULL DEFAULT(0)
		, dac_with_files_on_under_utilized_volume_count INT NOT NULL DEFAULT(0)
		, dac_with_over_utilized_file_count INT NOT NULL DEFAULT(0)
		, dac_with_under_utilized_file_count INT NOT NULL DEFAULT(0)
		, dac_with_over_utilized_processor_count INT NOT NULL DEFAULT(0)
		, dac_with_under_utilized_processor_count INT NOT NULL DEFAULT(0)
		, set_number INT NOT NULL DEFAULT(0)
	)
END
GO

/**********************************************************************/
/* Create table sysutility_ucp_aggregated_mi_health_internal                   */
/* This table stores the aggregated health statistics for MI's                 */
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_aggregated_mi_health_internal', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table dbo.sysutility_ucp_aggregated_mi_health_internal', 0, 1) WITH NOWAIT;

	CREATE TABLE sysutility_ucp_aggregated_mi_health_internal
	(
		mi_count INT NOT NULL DEFAULT(0)
		, mi_healthy_count INT NOT NULL DEFAULT(0)
		, mi_unhealthy_count INT NOT NULL DEFAULT(0)
		, mi_over_utilize_count INT NOT NULL DEFAULT(0)
		, mi_under_utilize_count INT NOT NULL DEFAULT(0)
		, mi_on_over_utilized_computer_count INT NOT NULL DEFAULT(0)
		, mi_on_under_utilized_computer_count INT NOT NULL DEFAULT(0)
		, mi_with_files_on_over_utilized_volume_count INT NOT NULL DEFAULT(0)
		, mi_with_files_on_under_utilized_volume_count INT NOT NULL DEFAULT(0)
		, mi_with_over_utilized_file_count INT NOT NULL DEFAULT(0)
		, mi_with_under_utilized_file_count INT NOT NULL DEFAULT(0)
		, mi_with_over_utilized_processor_count INT NOT NULL DEFAULT(0)
		, mi_with_under_utilized_processor_count INT NOT NULL DEFAULT(0)
		, set_number INT NOT NULL DEFAULT(0)
	)
END
GO

/**********************************************************************/
/* Create table sysutility_ucp_dac_health_internal                           */
/* This table stores the resource health states for DAC's             */
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_dac_health_internal', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table dbo.sysutility_ucp_dac_health_internal', 0, 1) WITH NOWAIT;

	CREATE TABLE sysutility_ucp_dac_health_internal
	(
		dac_name SYSNAME NOT NULL
		, dac_server_instance_name SYSNAME NOT NULL
		, is_volume_space_over_utilized INT NOT NULL DEFAULT(0)
		, is_volume_space_under_utilized INT NOT NULL DEFAULT(0)
		, is_computer_processor_over_utilized INT NOT NULL DEFAULT(0)
		, is_computer_processor_under_utilized INT NOT NULL DEFAULT(0)
		, is_file_space_over_utilized INT NOT NULL DEFAULT(0)
		, is_file_space_under_utilized INT NOT NULL DEFAULT(0)
		, is_dac_processor_over_utilized INT NOT NULL DEFAULT(0)
		, is_dac_processor_under_utilized INT NOT NULL DEFAULT(0)
		, is_policy_overridden BIT NOT NULL DEFAULT(0)
		, set_number INT NOT NULL DEFAULT(0)
		, processing_time DATETIMEOFFSET(7) NOT NULL DEFAULT(SYSDATETIMEOFFSET())

		CONSTRAINT [PK_sysutility_ucp_dac_health_internal_name] 
		  PRIMARY KEY CLUSTERED (set_number, dac_server_instance_name, dac_name)
	)
END
GO

/**********************************************************************/
/* Create table sysutility_ucp_mi_health_internal                           */
/* This table stores the resource health states for MI's              */
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_mi_health_internal', 'U') IS NULL)
BEGIN
   RAISERROR ('Creating table dbo.sysutility_ucp_mi_health_internal', 0, 1) WITH NOWAIT;

	CREATE TABLE sysutility_ucp_mi_health_internal
	(
		mi_name SYSNAME NOT NULL
		, is_volume_space_over_utilized INT NOT NULL DEFAULT(0)
		, is_volume_space_under_utilized INT NOT NULL DEFAULT(0) 
		, is_computer_processor_over_utilized INT NOT NULL DEFAULT(0)
		, is_computer_processor_under_utilized INT NOT NULL DEFAULT(0)
		, is_file_space_over_utilized INT NOT NULL DEFAULT(0)
		, is_file_space_under_utilized INT NOT NULL DEFAULT(0)
		, is_mi_processor_over_utilized INT NOT NULL DEFAULT(0)
		, is_mi_processor_under_utilized INT NOT NULL DEFAULT(0)
		, is_policy_overridden BIT NOT NULL DEFAULT(0)
		, set_number INT NOT NULL DEFAULT(0)
		, processing_time DATETIMEOFFSET(7) NOT NULL DEFAULT(SYSDATETIMEOFFSET())

		CONSTRAINT [PK_sysutility_ucp_mi_health_internal_name] 
		  PRIMARY KEY CLUSTERED (set_number, mi_name)
	)
END
GO

/**********************************************************************/
/* Create the sysutility_ucp_aggregated_dac_health               */
/* This view returns the latest health statistics for DAC's           */
/**********************************************************************/
IF object_id(N'dbo.sysutility_ucp_aggregated_dac_health', 'V') IS NOT NULL
BEGIN
   RAISERROR ('Dropping view dbo.sysutility_ucp_mi_health_internal', 0, 1) WITH NOWAIT;
	DROP VIEW dbo.sysutility_ucp_aggregated_dac_health
END;
GO

RAISERROR ('Creating view dbo.sysutility_ucp_aggregated_dac_health', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_aggregated_dac_health 
AS
SELECT t.dac_count
	   , t.dac_healthy_count
	   , t.dac_unhealthy_count
	   , t.dac_over_utilize_count
	   , t.dac_under_utilize_count
	   , t.dac_on_over_utilized_computer_count
	   , t.dac_on_under_utilized_computer_count
	   , t.dac_with_files_on_over_utilized_volume_count
	   , t.dac_with_files_on_under_utilized_volume_count
	   , t.dac_with_over_utilized_file_count
	   , t.dac_with_under_utilized_file_count
	   , t.dac_with_over_utilized_processor_count
	   , t.dac_with_under_utilized_processor_count
FROM msdb.dbo.sysutility_ucp_aggregated_dac_health_internal AS t
WHERE t.set_number = (SELECT latest_health_state_id FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal])        
GO

/**********************************************************************/
/* Create the sysutility_ucp_aggregated_mi_health                */
/* This view returns the latest health statistics for MI's            */
/**********************************************************************/
IF object_id(N'dbo.sysutility_ucp_aggregated_mi_health', 'V') IS NOT NULL
BEGIN
   RAISERROR ('Dropping view dbo.sysutility_ucp_aggregated_mi_health', 0, 1) WITH NOWAIT;
	DROP VIEW dbo.sysutility_ucp_aggregated_mi_health
END;
GO

RAISERROR ('Creating view dbo.sysutility_ucp_aggregated_mi_health', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_aggregated_mi_health 
AS
SELECT t.mi_count
	   , t.mi_healthy_count
	   , t.mi_unhealthy_count
	   , t.mi_over_utilize_count
	   , t.mi_under_utilize_count
	   , t.mi_on_over_utilized_computer_count
	   , t.mi_on_under_utilized_computer_count
	   , t.mi_with_files_on_over_utilized_volume_count
	   , t.mi_with_files_on_under_utilized_volume_count
	   , t.mi_with_over_utilized_file_count
	   , t.mi_with_under_utilized_file_count
	   , t.mi_with_over_utilized_processor_count
	   , t.mi_with_under_utilized_processor_count
FROM msdb.dbo.sysutility_ucp_aggregated_mi_health_internal AS t
WHERE t.set_number = (SELECT latest_health_state_id FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal])
GO

/**********************************************************************/
/* Create the sysutility_ucp_dac_health                        */
/* This view returns the latest resource health states for DAC's      */
/**********************************************************************/
IF object_id(N'dbo.sysutility_ucp_dac_health', 'V') IS NOT NULL
BEGIN
   RAISERROR ('Dropping view dbo.sysutility_ucp_dac_health', 0, 1) WITH NOWAIT;
	DROP VIEW dbo.sysutility_ucp_dac_health
END;
GO

RAISERROR ('Creating view dbo.sysutility_ucp_dac_health', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_dac_health 
AS
SELECT t.dac_name
	   , t.dac_server_instance_name
	   , (SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.is_volume_space_over_utilized, t.is_volume_space_under_utilized)) volume_space_health_state  
       , (SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.is_computer_processor_over_utilized, t.is_computer_processor_under_utilized)) computer_processor_health_state  
       , (SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.is_file_space_over_utilized, t.is_file_space_under_utilized)) file_space_health_state  
       , (SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.is_dac_processor_over_utilized, t.is_dac_processor_under_utilized)) dac_processor_health_state  
	   , t.is_policy_overridden
	   , t.processing_time
FROM msdb.dbo.sysutility_ucp_dac_health_internal AS t
WHERE t.set_number = (SELECT latest_health_state_id FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal])
GO

/**********************************************************************/
/* Create the sysutility_ucp_mi_health                         */
/* This view returns the latest resource health states for MI's       */
/**********************************************************************/
IF object_id(N'dbo.sysutility_ucp_mi_health', 'V') IS NOT NULL
BEGIN
   RAISERROR ('Dropping view dbo.sysutility_ucp_mi_health', 0, 1) WITH NOWAIT;
	DROP VIEW dbo.sysutility_ucp_mi_health
END;
GO

RAISERROR ('Creating view dbo.sysutility_ucp_mi_health', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_mi_health 
AS
SELECT t.mi_name
	   , (SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.is_volume_space_over_utilized, t.is_volume_space_under_utilized)) volume_space_health_state
	   , (SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.is_computer_processor_over_utilized, t.is_computer_processor_under_utilized)) computer_processor_health_state
	   , (SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.is_file_space_over_utilized, t.is_file_space_under_utilized)) file_space_health_state
	   , (SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.is_mi_processor_over_utilized, t.is_mi_processor_under_utilized)) mi_processor_health_state
	   , t.is_policy_overridden
	   , t.processing_time
FROM msdb.dbo.sysutility_ucp_mi_health_internal AS t
WHERE t.set_number = (SELECT latest_health_state_id FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal])
GO

/**********************************************************************/
-- Table sysutility_ucp_filegroups_with_policy_violations_internal                           */
-- This table stores an entry for each filegroup that has at least one file
-- that violates the specified policy. 
-- LogFiles are rolled up into an entry (with filegroup_name = N'')   
/**********************************************************************/

IF(OBJECT_ID(N'dbo.sysutility_ucp_filegroups_with_policy_violations_internal', 'U') IS NULL)
BEGIN
    RAISERROR ('Creating table dbo.sysutility_ucp_filegroups_with_policy_violations_internal', 0, 1) WITH NOWAIT;
    CREATE TABLE sysutility_ucp_filegroups_with_policy_violations_internal
    (
        server_instance_name SYSNAME,
        database_name SYSNAME,
        [filegroup_name] SYSNAME,  -- N'' for log files
        policy_id INT, -- id of the offending policy
        set_number INT NOT NULL,

        CONSTRAINT [PK_sysutility_ucp_filegroups_with_policy_violations_internal] 
          PRIMARY KEY CLUSTERED (set_number, policy_id, server_instance_name, database_name, [filegroup_name])
    )
END
GO

--*********************************************************************
-- Procedure sp_sysutility_ucp_calculate_filegroups_with_policy_violations 
-- Description: This function identifies filegroups where *every* single file 
--   violates a policy (the same policy for all files in the filegroups). 
--   Logfiles are also captured here under a special entry (with filegroup_name = N'')
--**********************************************************************      
IF OBJECT_ID ('dbo.sp_sysutility_ucp_calculate_filegroups_with_policy_violations') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_filegroups_with_policy_violations', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_filegroups_with_policy_violations 
END;
GO

RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_filegroups_with_policy_violations', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_filegroups_with_policy_violations 
   @new_set_number INT
WITH EXECUTE AS OWNER
AS
BEGIN
   INSERT INTO sysutility_ucp_filegroups_with_policy_violations_internal(
        server_instance_name, 
        database_name, 
        [filegroup_name],
        policy_id,
        set_number)
      SELECT fg1.server_instance_name,
             fg1.database_name,
             fg1.[filegroup_name],
             fg1.policy_id,
             @new_set_number
      FROM (SELECT pv.policy_id,
                   f.server_instance_name, f.database_name, f.[filegroup_name], 
                   COUNT(*) as policy_violations
            FROM dbo.sysutility_ucp_database_files AS f,
                 dbo.sysutility_ucp_policy_violations AS pv
            WHERE f.powershell_path = pv.target_query_expression 
            GROUP BY pv.policy_id, f.server_instance_name, f.database_name, f.[filegroup_name]) as fg1,
            (SELECT f.server_instance_name, f.database_name, f.[filegroup_name],
                    COUNT(*) as file_count
             FROM dbo.sysutility_ucp_database_files AS f
             GROUP BY f.server_instance_name, f.database_name, f.[filegroup_name]) AS fg2
      WHERE fg1.server_instance_name = fg2.server_instance_name AND
            fg1.database_name = fg2.database_name AND
            fg1.[filegroup_name] = fg2.[filegroup_name] AND
            fg1.policy_violations = fg2.file_count     
END
GO

--*********************************************************************
-- Create view sysutility_ucp_dac_cpu_utilization
-- Description: Gets the dac cpu utilization based on processor violating the health policy 
--              Marks the dac as unhealthy if processor violate the policy
-- Attributes: 
-- dac_name - The name of data-tier application
-- dac_server_instance_name - The server\instance on which the DAC is hosted 
-- under_utilized_count - Flag indicating whether the processor is under utilized
-- over_utilized_count - Flag indicating whether the processor is over utilized
--**********************************************************************
IF (OBJECT_ID(N'[dbo].[sysutility_ucp_dac_cpu_utilization]', 'V') IS NOT NULL) 
BEGIN
    RAISERROR ('Dropping view [dbo].[sysutility_ucp_dac_cpu_utilization]', 0, 1) WITH NOWAIT;
    DROP VIEW dbo.sysutility_ucp_dac_cpu_utilization
END	
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_dac_cpu_utilization]...', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_dac_cpu_utilization AS
( 
    -- dac_object_type = 1
    -- processor_resource_type = 3
    -- target_type = 5

    SELECT dp.dac_name
        , dp.dac_server_instance_name
        , SUM(CASE WHEN dp.utilization_type = 1 THEN 1 ELSE 0 END) AS under_utilized_count
        , SUM(CASE WHEN dp.utilization_type = 2 THEN 1 ELSE 0 END) AS over_utilized_count
    FROM msdb.dbo.sysutility_ucp_dac_policies AS dp
		INNER JOIN msdb.dbo.sysutility_ucp_policy_violations pv
			ON dp.policy_id = pv.policy_id AND dp.powershell_path = pv.target_query_expression
    WHERE dp.resource_type = 3
        AND dp.target_type = 5
    GROUP BY dp.dac_name, dp.dac_server_instance_name
    
)
GO

--*********************************************************************
-- Create view sysutility_ucp_instance_cpu_utilization
-- Description: Gets the MI cpu utilization based on processor violating the health policy 
--              Marks the instance as unhealthy if processor violate the policy
-- Attributes: 
-- server_instance_name - The name of the server in the format server_name\instance_name
-- under_utilized_count - Flag indicating whether the processor is under utilized
-- over_utilized_count - Flag indicating whether the processor is over utilized
--**********************************************************************
IF (OBJECT_ID(N'[dbo].[sysutility_ucp_instance_cpu_utilization]', 'V') IS NOT NULL) 
BEGIN
    RAISERROR ('Dropping view [dbo].[sysutility_ucp_instance_cpu_utilization]', 0, 1) WITH NOWAIT;
    DROP VIEW dbo.sysutility_ucp_instance_cpu_utilization
END	
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_instance_cpu_utilization]...', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_instance_cpu_utilization AS
(    
    -- server_object_type = 2
    -- processor_resource_type = 3
    -- target_type = 4
	
    SELECT ip.server_instance_name AS server_instance_name
        , SUM(CASE WHEN ip.utilization_type = 1 THEN 1 ELSE 0 END) AS under_utilized_count
        , SUM(CASE WHEN ip.utilization_type = 2 THEN 1 ELSE 0 END) AS over_utilized_count
    FROM msdb.dbo.sysutility_ucp_instance_policies ip
		INNER JOIN msdb.dbo.sysutility_ucp_policy_violations pv
			ON ip.policy_id = pv.policy_id AND ip.powershell_path = pv.target_query_expression
    WHERE ip.resource_type = 3
       AND ip.target_type = 4
    GROUP BY ip.server_instance_name

)
GO
         	
--*********************************************************************
-- Create view sysutility_ucp_computer_cpu_utilization
-- Description: Gets the computer cpu utilization based on processor violating the health policy 
--              Marks the computer as unhealthy if processor violate the policy
-- Attributes: 
-- physical_server_name - The name of the computer on which MI/DAC is hosted
-- under_utilized_count - Flag indicating whether the processor is under utilized
-- over_utilized_count - Flag indicating whether the processor is over utilized
--**********************************************************************
IF (OBJECT_ID(N'[dbo].[sysutility_ucp_computer_cpu_utilization]', 'V') IS NOT NULL) 
BEGIN
    RAISERROR ('Dropping view [dbo].[sysutility_ucp_computer_cpu_utilization]', 0, 1) WITH NOWAIT;
    DROP VIEW dbo.sysutility_ucp_computer_cpu_utilization
END	
GO

RAISERROR ('Creating view [dbo].[sysutility_ucp_computer_cpu_utilization]...', 0, 1) WITH NOWAIT;
GO
CREATE VIEW dbo.sysutility_ucp_computer_cpu_utilization AS
(   
    -- computer_object_type = 3
    -- processor_resource_type = 3
    -- target_type = 1

    SELECT cp.physical_server_name as physical_server_name
        , SUM(CASE WHEN cp.utilization_type = 1 THEN 1 ELSE 0 END) AS under_utilized_count
        , SUM(CASE WHEN cp.utilization_type = 2 THEN 1 ELSE 0 END) AS over_utilized_count
    FROM msdb.dbo.sysutility_ucp_computer_policies cp 
		INNER JOIN msdb.dbo.sysutility_ucp_policy_violations pv
			ON cp.policy_id = pv.policy_id AND cp.powershell_path = pv.target_query_expression
    WHERE cp.resource_type = 3
        AND cp.target_type = 1
    GROUP BY cp.physical_server_name    
    
)
GO

--*********************************************************************
-- Create procedure sp_sysutility_ucp_calculate_dac_file_space_health 
-- Description: Computes the file space (data/log file group)health state for the DAC's
-- The computed result is consumed by the UI for health state drill down and further
-- compute the rollup health state for the DAC's
--**********************************************************************
IF(OBJECT_ID(N'dbo.sp_sysutility_ucp_calculate_dac_file_space_health', 'P') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_dac_file_space_health', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_dac_file_space_health
END
GO
      
RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_dac_file_space_health', 0, 1) WITH NOWAIT;
GO

CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_dac_file_space_health 
  @new_set_number INT
WITH EXECUTE AS OWNER
AS
BEGIN    
    DECLARE @under_utilize_type INT = 1
    DECLARE @over_utilize_type INT = 2;

    -- space_resource_type = 1
    -- datafile_target_type = 2
    -- logfile_target_type = 3
    
    INSERT INTO msdb.dbo.sysutility_ucp_dac_file_space_health_internal(
       dac_name, dac_server_instance_name, 
       fg_name, set_number, processing_time
       , over_utilized_count
       , under_utilized_count
       , file_type)        

    -- Insert the dac filegroup utilization details
    SELECT dd.dac_name
        , dd.dac_server_instance_name
        , fg.Name AS file_group_name
        , @new_set_number
        , dd.dac_processing_time
        , SUM(CASE WHEN df.policy_id IS NOT NULL AND dp.utilization_type = 2 THEN 1 ELSE 0 END) AS over_utilized_count
        , SUM(CASE WHEN df.policy_id IS NOT NULL AND dp.utilization_type = 1 THEN 1 ELSE 0 END) AS under_utilized_count
        , fg.file_type
    FROM msdb.dbo.sysutility_ucp_deployed_dacs AS dd
        INNER JOIN (SELECT 1 AS file_type, server_instance_name, database_name, [Name], processing_time
                    FROM msdb.dbo.sysutility_ucp_filegroups 
                    UNION ALL
                    SELECT 2 AS file_type, server_instance_name, Name as database_name, N'' AS [Name], processing_time
                    FROM msdb.dbo.sysutility_ucp_databases) AS fg
          ON dd.dac_server_instance_name = fg.server_instance_name AND 
             dd.dac_name = fg.database_name
        INNER JOIN msdb.dbo.sysutility_ucp_dac_policies AS dp 
          ON dp.dac_name = dd.dac_name AND 
             dp.dac_server_instance_name = dd.dac_server_instance_name
        LEFT JOIN msdb.dbo.sysutility_ucp_filegroups_with_policy_violations_internal AS df 
          ON df.server_instance_name = dd.dac_server_instance_name AND 
             df.database_name = dd.dac_name AND 
             fg.Name = df.filegroup_name AND 
             dp.policy_id = df.policy_id AND
             df.set_number = @new_set_number
    WHERE dp.resource_type = 1
        AND dp.target_type = fg.file_type + 1 -- target_type = 2 (datafile); 3 (logfile)
    GROUP BY dd.dac_name, dd.dac_server_instance_name, fg.Name , fg.file_type, dd.dac_processing_time            
END
GO

--*********************************************************************
-- Create procedure sp_sysutility_ucp_calculate_mi_file_space_health 
-- Description: Computes the file space (data/log file group)health state for the MI's
-- The computed result is consumed by the UI for health state drill down and further 
-- compute the rollup health state for the MI's
--**********************************************************************
IF(OBJECT_ID(N'dbo.sp_sysutility_ucp_calculate_mi_file_space_health', 'P') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_mi_file_space_health', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_mi_file_space_health
END
GO

RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_mi_file_space_health', 0, 1) WITH NOWAIT;
GO

CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_mi_file_space_health 
    @new_set_number INT
WITH EXECUTE AS OWNER
AS
BEGIN
    DECLARE @under_utilize_type INT = 1
    DECLARE @over_utilize_type INT = 2;


    -- space_resource_type = 1
    -- datafile_target_type = 2
    -- logfile_target_type = 3

    INSERT INTO msdb.dbo.sysutility_ucp_mi_file_space_health_internal(
           server_instance_name
           , database_name
           , fg_name
           , set_number
           , processing_time
           , over_utilized_count
           , under_utilized_count
           , file_type)                         
      -- Insert the server filegroup utilization details
      SELECT fg.server_instance_name
             , fg.database_name
             , fg.Name AS file_group_name
             , @new_set_number
             , fg.processing_time
             , SUM(CASE WHEN df.policy_id IS NOT NULL AND ip.utilization_type = 2 THEN 1 ELSE 0 END) AS over_utilized_count
             , SUM(CASE WHEN df.policy_id IS NOT NULL AND ip.utilization_type = 1 THEN 1 ELSE 0 END) AS under_utilized_count
             , fg.file_type
      FROM (SELECT 1 AS file_type, fg.server_instance_name, fg.database_name, fg.Name, fg.processing_time
            FROM msdb.dbo.sysutility_ucp_filegroups AS fg  
            UNION ALL
            SELECT 2 AS file_type, db.server_instance_name, db.Name AS database_name, N'' AS Name, db.processing_time
            FROM msdb.dbo.sysutility_ucp_databases AS db) AS fg
        INNER JOIN msdb.dbo.sysutility_ucp_instance_policies AS ip ON fg.server_instance_name = ip.server_instance_name
        LEFT JOIN msdb.dbo.sysutility_ucp_filegroups_with_policy_violations_internal AS df 
            ON fg.server_instance_name = df.server_instance_name AND 
               fg.database_name = df.database_name AND
               fg.Name = df.[filegroup_name] AND 
               df.set_number = @new_set_number AND
               ip.policy_id = df.policy_id
    WHERE ip.resource_type = 1
        AND ip.target_type = file_type + 1 -- target_type = 2 (datafile), 3 (logfile)
    GROUP BY fg.server_instance_name, fg.database_name, fg.Name, fg.file_type, fg.processing_time        
   
    -- Compute the database health state for the MI's based on the file-space computation.
    
     -- Insert the server database utilization details
    INSERT INTO msdb.dbo.sysutility_ucp_mi_database_health_internal(server_instance_name, database_name, set_number, processing_time
           , over_utilized_count
           , under_utilized_count)
    SELECT fs.server_instance_name
        , fs.database_name AS database_name
        , @new_set_number
        , svr.processing_time
        , SUM(fs.over_utilized_count) AS over_utilized_count
        , SUM(fs.under_utilized_count) AS under_utilized_count
    FROM  msdb.dbo.sysutility_ucp_mi_file_space_health_internal AS fs
        , msdb.dbo.sysutility_ucp_instances AS svr
    WHERE svr.Name = fs.server_instance_name AND
          fs.set_number = @new_set_number        
    GROUP BY fs.server_instance_name, fs.database_name, svr.processing_time        
    
END
GO

--*********************************************************************
-- Create procedure sp_sysutility_ucp_calculate_computer_health 
-- Description: Computes the volume space health state for the computer.
-- The computed result is consumed by the DAC / MI to determine rollup health state 
--**********************************************************************        
IF(OBJECT_ID(N'dbo.sp_sysutility_ucp_calculate_computer_health', 'P') IS NOT NULL)
BEGIN
    RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_computer_health', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_computer_health
END
GO

RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_computer_health', 0, 1) WITH NOWAIT;
GO

CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_computer_health
   @new_set_number INT 
WITH EXECUTE AS OWNER
AS
BEGIN
    DECLARE @under_utilize_type INT = 1
    DECLARE @over_utilize_type INT = 2

    DECLARE @computer_object_type INT = 3
    DECLARE @target_type INT = 6
    DECLARE @space_resource_type INT = 1;

    -- Compute the volume space health state for the computer.

    -- CTE to identify the computer volumes violating the under / over utilization policy
    WITH volume_utilization (physical_server_name, volume_device_id, utilization_type)
    AS
    (
        SELECT vo.physical_server_name, vo.volume_device_id, cp.utilization_type 
        FROM msdb.dbo.sysutility_ucp_computer_policies cp
            , msdb.dbo.sysutility_ucp_volume_powershell_path vo
            , msdb.dbo.sysutility_ucp_policy_violations pv
        WHERE cp.physical_server_name = vo.physical_server_name
            AND cp.resource_type = @space_resource_type
            AND cp.target_type = @target_type
            AND pv.policy_id = cp.policy_id
            AND pv.target_query_expression = vo.powershell_path
    )
    -- Insert new record
    INSERT INTO msdb.dbo.sysutility_ucp_mi_volume_space_health_internal(physical_server_name, server_instance_name, volume_device_id, set_number, processing_time
           ,health_state)
    SELECT CAST(svr.ComputerNamePhysicalNetBIOS AS SYSNAME), 
           CAST(svr.Name AS SYSNAME), 
           vol.volume_device_id, 
           @new_set_number, 
           svr.processing_time,
		   CASE WHEN (@over_utilize_type = ISNULL(vu.utilization_type, 0))
			 THEN 3 -- over utilized
			 WHEN (@under_utilize_type = ISNULL(vu.utilization_type, 0))
			 THEN 2 -- under utilized
			 ELSE 1 -- healthy
		   END 
    FROM msdb.dbo.sysutility_ucp_instances AS svr
      INNER JOIN msdb.dbo.sysutility_ucp_volumes AS vol ON vol.physical_server_name = svr.ComputerNamePhysicalNetBIOS
      LEFT JOIN volume_utilization vu ON vol.physical_server_name = vu.physical_server_name AND vol.volume_device_id = vu.volume_device_id
   
   -- Computes the processor health state for the computer.

    -- Insert new record
    INSERT INTO msdb.dbo.sysutility_ucp_computer_cpu_health_internal(physical_server_name, set_number, processing_time, health_state)
    SELECT c.physical_server_name
        , @new_set_number
        , c.processing_time,
    CASE WHEN 0 < ISNULL(cu.over_utilized_count, 0) THEN 
        3 -- over utilized
    WHEN 0 < ISNULL(cu.under_utilized_count, 0) THEN 
        2 -- under utilized
    ELSE 1 -- healthy 
    END AS health_state
    FROM msdb.dbo.sysutility_ucp_computers AS c
    LEFT JOIN msdb.dbo.sysutility_ucp_computer_cpu_utilization cu
    ON c.physical_server_name = cu.physical_server_name

END
GO

--*********************************************************************
-- Create procedure sp_sysutility_ucp_calculate_dac_health 
-- Description: This is the top level procedure that computes all
-- the resource health states for the DAC.
--**********************************************************************   
IF OBJECT_ID ('dbo.sp_sysutility_ucp_calculate_dac_health') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_dac_health', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_dac_health 
END;
GO

RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_dac_health', 0, 1) WITH NOWAIT;
GO

CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_dac_health 
   @new_set_number INT
WITH EXECUTE AS OWNER
AS
BEGIN

    -- Compute dac filegroup/log files health state
    EXEC msdb.dbo.sp_sysutility_ucp_calculate_dac_file_space_health @new_set_number;

    -- Compute dac health state
	
    -- Insert new records
    SELECT dd.dac_server_instance_name
        , dd.dac_name
        , SUM(CASE WHEN hs.health_state = 2 THEN 1 ELSE 0 END) AS under_utilized_count
        , SUM(CASE WHEN hs.health_state = 3 THEN 1 ELSE 0 END) AS over_utilized_count
    INTO #dac_volume_file_space_utilization 
    FROM msdb.dbo.sysutility_ucp_deployed_dacs AS dd
    INNER JOIN msdb.dbo.sysutility_ucp_mi_volume_space_health_internal AS hs 
        ON hs.server_instance_name = dd.dac_server_instance_name
    INNER JOIN (
        SELECT server_instance_name, database_name, volume_device_id FROM sysutility_ucp_datafiles 
        UNION ALL
        SELECT server_instance_name, database_name, volume_device_id FROM sysutility_ucp_logfiles
    ) AS df 
        ON df.volume_device_id = hs.volume_device_id
        AND dd.dac_server_instance_name = df.server_instance_name 
        AND dd.dac_name = df.database_name
    WHERE hs.set_number = @new_set_number 
    GROUP BY dd.dac_server_instance_name, dd.dac_name;
			  
    SELECT dd.dac_server_instance_name
        , dd.dac_name
        , SUM(CASE WHEN hs.health_state = 2 THEN 1 ELSE 0 END) AS under_utilized_count
        , SUM(CASE WHEN hs.health_state = 3 THEN 1 ELSE 0 END) AS over_utilized_count
    INTO #dac_computer_cpu_utilization 
    FROM msdb.dbo.sysutility_ucp_computer_cpu_health_internal AS hs
    INNER JOIN msdb.dbo.sysutility_ucp_deployed_dacs AS dd 
        ON hs.physical_server_name = dd.dac_physical_server_name
    WHERE hs.set_number = @new_set_number 
    GROUP BY dd.dac_server_instance_name, dd.dac_name;
    
    SELECT hs.dac_server_instance_name
        , hs.dac_name
        , SUM(CASE WHEN health_state.val = 2 THEN 1 ELSE 0 END) AS under_utilized_count
        , SUM(CASE WHEN health_state.val = 3 THEN 1 ELSE 0 END) AS over_utilized_count
    INTO #dac_file_space_utilization 
    FROM msdb.dbo.sysutility_ucp_dac_file_space_health_internal hs
    CROSS APPLY dbo.fn_sysutility_ucp_get_aggregated_health(hs.over_utilized_count, hs.under_utilized_count) health_state
    WHERE hs.set_number = @new_set_number 
    GROUP BY hs.dac_server_instance_name, hs.dac_name;

    INSERT INTO msdb.dbo.sysutility_ucp_dac_health_internal(dac_name, dac_server_instance_name, set_number
	       , processing_time
	       , is_volume_space_over_utilized
	       , is_volume_space_under_utilized
	       , is_computer_processor_over_utilized
	       , is_computer_processor_under_utilized
	       , is_file_space_over_utilized
	       , is_file_space_under_utilized
	       , is_dac_processor_over_utilized
	       , is_dac_processor_under_utilized
	       , is_policy_overridden)
    SELECT dd.dac_name
        , dd.dac_server_instance_name
        , @new_set_number
	    , dd.dac_processing_time
	    , vu.over_utilized_count AS dac_volume_space_over_utilized_count
	    , vu.under_utilized_count AS dac_volume_space_under_utilized_count
	    , cu.over_utilized_count AS dac_computer_cpu_over_utilized_count
	    , cu.under_utilized_count AS dac_computer_cpu_under_utilized_count 
        , su.over_utilized_count AS dac_file_space_over_utilized_count
        , su.under_utilized_count AS dac_file_space_under_utilized_count
	    , ISNULL(du.over_utilized_count ,0) AS dac_cpu_over_utilized_count
	    , ISNULL(du.under_utilized_count ,0) AS dac_cpu_under_utilized_count
	    , pt.is_policy_overridden
    FROM msdb.dbo.sysutility_ucp_deployed_dacs dd
    LEFT JOIN msdb.dbo.sysutility_ucp_dac_cpu_utilization du 
        ON dd.dac_name = du.dac_name AND dd.dac_server_instance_name = du.dac_server_instance_name
    INNER JOIN #dac_volume_file_space_utilization AS vu 
        ON dd.dac_name = vu.dac_name AND dd.dac_server_instance_name = vu.dac_server_instance_name            
    INNER JOIN #dac_computer_cpu_utilization AS cu 
        ON dd.dac_name = cu.dac_name AND dd.dac_server_instance_name = cu.dac_server_instance_name
    INNER JOIN #dac_file_space_utilization AS su 
        ON dd.dac_name = su.dac_name AND dd.dac_server_instance_name = su.dac_server_instance_name
    INNER JOIN msdb.dbo.sysutility_ucp_dac_policy_type pt
        ON dd.dac_name = pt.dac_name AND dd.dac_server_instance_name = pt.dac_server_instance_name;

END
GO    

--*********************************************************************
-- Create procedure sp_sysutility_ucp_calculate_aggregated_dac_health 
-- Description: This is the top level procedure that computes the health
-- statistics for the DAC. This uses the pre-computed health states for DAC's
-- The resulting health statistics are used in dashboard display
--**********************************************************************       
IF OBJECT_ID ('dbo.sp_sysutility_ucp_calculate_aggregated_dac_health') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_aggregated_dac_health', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_aggregated_dac_health 
END;
GO

RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_aggregated_dac_health', 0, 1) WITH NOWAIT;
GO

CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_aggregated_dac_health 
   @new_set_number INT
WITH EXECUTE AS OWNER
AS
BEGIN
       
    -- DacCount
    DECLARE @dac_count INT = 0
    SELECT @dac_count = COUNT(*) 
    FROM msdb.dbo.sysutility_ucp_dac_health_internal hs
    WHERE hs.set_number = @new_set_number

    -- DacOverUtilizeCount
    DECLARE @dac_over_utilize_count INT = 0
    SELECT @dac_over_utilize_count = COUNT(*)
    FROM msdb.dbo.sysutility_ucp_dac_health_internal hs
    WHERE hs.set_number = @new_set_number AND
          (0 != hs.is_dac_processor_over_utilized OR
           0 != hs.is_computer_processor_over_utilized OR
           0 != hs.is_file_space_over_utilized OR
           0 != hs.is_volume_space_over_utilized)

    -- DacUnderUtilizeCount
    DECLARE @dac_under_utilize_count INT = 0
    SELECT @dac_under_utilize_count = COUNT(*)
    FROM msdb.dbo.sysutility_ucp_dac_health_internal hs
    WHERE hs.set_number = @new_set_number AND
          (0 != hs.is_dac_processor_under_utilized OR
           0 != hs.is_computer_processor_under_utilized OR
           0 != hs.is_file_space_under_utilized OR
           0 != hs.is_volume_space_under_utilized)
           AND 0 = hs.is_dac_processor_over_utilized 
           AND 0 = hs.is_computer_processor_over_utilized 
           AND 0 = hs.is_file_space_over_utilized 
           AND 0 = hs.is_volume_space_over_utilized
    	   
    -- DacUnhealthyCount
    DECLARE @dac_unhealthy_count INT = 0
    SELECT @dac_unhealthy_count = @dac_over_utilize_count + @dac_under_utilize_count;

    -- DacHealthyCount
    DECLARE @dac_healthy_count INT = 0
    SELECT @dac_healthy_count = COUNT(*)
    FROM msdb.dbo.sysutility_ucp_dac_health_internal hs
    WHERE hs.set_number = @new_set_number 
    AND 0 = hs.is_dac_processor_under_utilized 
    AND 0 = hs.is_computer_processor_under_utilized 
    AND 0 = hs.is_file_space_under_utilized 
    AND 0 = hs.is_volume_space_under_utilized
    AND 0 = hs.is_dac_processor_over_utilized 
    AND 0 = hs.is_computer_processor_over_utilized 
    AND 0 = hs.is_file_space_over_utilized 
    AND 0 = hs.is_volume_space_over_utilized        

    -- Insert new record
    INSERT INTO msdb.dbo.sysutility_ucp_aggregated_dac_health_internal(set_number
            , dac_count
            , dac_healthy_count
            , dac_unhealthy_count
            , dac_over_utilize_count
            , dac_under_utilize_count
            , dac_on_over_utilized_computer_count
            , dac_on_under_utilized_computer_count
            , dac_with_files_on_over_utilized_volume_count
            , dac_with_files_on_under_utilized_volume_count
            , dac_with_over_utilized_file_count
            , dac_with_under_utilized_file_count
            , dac_with_over_utilized_processor_count
            , dac_with_under_utilized_processor_count)
    SELECT @new_set_number
            , @dac_count 
            , @dac_healthy_count 
            , @dac_unhealthy_count 
            , @dac_over_utilize_count 
            , @dac_under_utilize_count 
            , ISNULL(SUM(CASE WHEN 0 < hs.is_computer_processor_over_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_computer_processor_under_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_volume_space_over_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_volume_space_under_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_file_space_over_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_file_space_under_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_dac_processor_over_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_dac_processor_under_utilized THEN 1 ELSE 0 END), 0)
    FROM msdb.dbo.sysutility_ucp_dac_health_internal hs   
    WHERE hs.set_number = @new_set_number       

END
GO

--*********************************************************************
-- Create procedure sp_sysutility_ucp_calculate_mi_health 
-- Description: This is the top level procedure that computes all
-- the resource health states for the MI.
--**********************************************************************      
IF OBJECT_ID ('dbo.sp_sysutility_ucp_calculate_mi_health') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_mi_health', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_mi_health 
END;
GO

RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_mi_health', 0, 1) WITH NOWAIT;
GO

CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_mi_health 
   @new_set_number INT
WITH EXECUTE AS OWNER
AS
BEGIN

    -- Compute managed instance database health state
    EXEC msdb.dbo.sp_sysutility_ucp_calculate_mi_file_space_health @new_set_number;

    -- Compute managed instance health state

    -- Insert new record
    SELECT hs.server_instance_name AS server_instance_name, 
       SUM(CASE WHEN health_state.val = 2 THEN 1 ELSE 0 END) AS under_utilized_count,
       SUM(CASE WHEN health_state.val = 3 THEN 1 ELSE 0 END) AS over_utilized_count
       INTO #instance_file_space_utilization 
    FROM msdb.dbo.sysutility_ucp_mi_file_space_health_internal as hs
    CROSS APPLY msdb.dbo.fn_sysutility_ucp_get_aggregated_health(hs.over_utilized_count, hs.under_utilized_count) as health_state
    WHERE hs.set_number = @new_set_number
    GROUP BY hs.server_instance_name;
    
    SELECT sv.Name AS server_instance_name, 
        SUM(CASE WHEN hs.health_state = 2 THEN 1 ELSE 0 END) AS under_utilized_count,
        SUM(CASE WHEN hs.health_state = 3 THEN 1 ELSE 0 END) AS over_utilized_count
    INTO #instance_computer_cpu_utilization 
    FROM msdb.dbo.sysutility_ucp_computer_cpu_health_internal AS hs 
    INNER JOIN msdb.dbo.sysutility_ucp_instances AS sv 
        ON hs.physical_server_name = sv.ComputerNamePhysicalNetBIOS
    WHERE hs.set_number = @new_set_number 
    GROUP BY sv.Name;
            
    SELECT hs.server_instance_name AS server_instance_name, 
        SUM(CASE WHEN hs.health_state = 2 THEN 1 ELSE 0 END) AS under_utilized_count,
        SUM(CASE WHEN hs.health_state = 3 THEN 1 ELSE 0 END) AS over_utilized_count
    INTO #instance_volume_file_space_utilization 
    FROM msdb.dbo.sysutility_ucp_mi_volume_space_health_internal AS hs 
    INNER JOIN (
        SELECT server_instance_name, database_name, volume_device_id FROM dbo.sysutility_ucp_datafiles 
        UNION ALL
        SELECT server_instance_name, database_name, volume_device_id FROM dbo.sysutility_ucp_logfiles
    ) AS df 
        ON hs.volume_device_id = df.volume_device_id AND hs.server_instance_name = df.server_instance_name  
    WHERE hs.set_number = @new_set_number  
    GROUP BY hs.server_instance_name;


    INSERT INTO msdb.dbo.sysutility_ucp_mi_health_internal(mi_name, set_number
        , processing_time     
        , is_volume_space_over_utilized
        , is_volume_space_under_utilized
        , is_computer_processor_over_utilized
        , is_computer_processor_under_utilized
        , is_file_space_over_utilized
        , is_file_space_under_utilized
        , is_mi_processor_over_utilized
        , is_mi_processor_under_utilized
        , is_policy_overridden)
    SELECT CAST(sv.Name AS SYSNAME) mi_name
        , @new_set_number
        , sv.processing_time
        , vu.over_utilized_count AS mi_volume_space_over_utilized_count
        , vu.under_utilized_count AS mi_volume_space_under_utilized_count
        , cu.over_utilized_count AS mi_computer_cpu_over_utilized_count
        , cu.under_utilized_count AS mi_computer_cpu_under_utilized_count 
        , su.over_utilized_count AS mi_file_space_over_utilized_count
        , su.under_utilized_count AS mi_file_space_under_utilized_count
        , ISNULL(iu.over_utilized_count ,0) AS mi_cpu_over_utilized_count
        , ISNULL(iu.under_utilized_count ,0) AS mi_cpu_under_utilized_count
        , pt.is_policy_overridden
    FROM msdb.dbo.sysutility_ucp_managed_instances AS mi
    INNER JOIN msdb.dbo.sysutility_ucp_instances AS sv ON sv.Name = mi.instance_name
    LEFT OUTER JOIN msdb.dbo.sysutility_ucp_instance_cpu_utilization AS iu ON sv.Name = iu.server_instance_name
    INNER JOIN #instance_volume_file_space_utilization AS vu ON sv.Name = vu.server_instance_name
    INNER JOIN #instance_computer_cpu_utilization AS cu ON sv.Name = cu.server_instance_name
    INNER JOIN #instance_file_space_utilization AS su ON sv.Name = su.server_instance_name
    INNER JOIN msdb.dbo.sysutility_ucp_instance_policy_type AS pt ON sv.Name = pt.server_instance_name;

END
GO
    
--*********************************************************************
-- Create procedure sp_sysutility_ucp_calculate_aggregated_mi_health 
-- Description: This is the top level procedure that computes the health
-- statistics for the MI. This uses the pre-computed health states for MI's
-- The resulting health statistics are used in dashboard display
--**********************************************************************      
IF OBJECT_ID ('dbo.sp_sysutility_ucp_calculate_aggregated_mi_health') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_aggregated_mi_health', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_aggregated_mi_health 
END;
GO

RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_aggregated_mi_health', 0, 1) WITH NOWAIT;
GO

CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_aggregated_mi_health 
   @new_set_number INT
WITH EXECUTE AS OWNER
AS
BEGIN
       
    -- ManagedInstanceCount
    DECLARE @mi_count INT = 0
    SELECT @mi_count = COUNT(*) 
    FROM msdb.dbo.sysutility_ucp_mi_health_internal hs
    WHERE hs.set_number = @new_set_number

    -- ManagedInstanceOverUtilizeCount
    DECLARE @mi_over_utilize_count INT = 0
    SELECT @mi_over_utilize_count = COUNT(*)
    FROM msdb.dbo.sysutility_ucp_mi_health_internal hs
    WHERE hs.set_number = @new_set_number AND
          (0 != hs.is_volume_space_over_utilized OR
           0 != hs.is_computer_processor_over_utilized OR
           0 != hs.is_file_space_over_utilized OR
           0 != hs.is_mi_processor_over_utilized)
           
    -- ManagedInstanceUnderUtilizeCount
    DECLARE @mi_under_utilize_count INT = 0
    SELECT @mi_under_utilize_count = COUNT(*)
    FROM msdb.dbo.sysutility_ucp_mi_health_internal hs
    WHERE hs.set_number = @new_set_number AND
          (0 != hs.is_volume_space_under_utilized OR
           0 != hs.is_computer_processor_under_utilized OR
           0 != hs.is_file_space_under_utilized OR
           0 != hs.is_mi_processor_under_utilized)
           AND 0 = hs.is_volume_space_over_utilized
           AND 0 = hs.is_computer_processor_over_utilized
           AND 0 = hs.is_file_space_over_utilized
           AND 0 = hs.is_mi_processor_over_utilized	           
    	   
    -- ManagedInstanceUnhealthyCount
    DECLARE @mi_unhealthy_count INT = 0
    SELECT @mi_unhealthy_count = @mi_over_utilize_count + @mi_under_utilize_count

    -- ManagedInstanceHealthyCount
    DECLARE @mi_healthy_count INT = 0
    SELECT @mi_healthy_count = COUNT(*)
    FROM msdb.dbo.sysutility_ucp_mi_health_internal hs
    WHERE hs.set_number = @new_set_number
    AND 0 = hs.is_volume_space_under_utilized
    AND 0 = hs.is_computer_processor_under_utilized
    AND 0 = hs.is_file_space_under_utilized
    AND 0 = hs.is_mi_processor_under_utilized
    AND 0 = hs.is_volume_space_over_utilized
    AND 0 = hs.is_computer_processor_over_utilized
    AND 0 = hs.is_file_space_over_utilized
    AND 0 = hs.is_mi_processor_over_utilized

    -- Insert new record
    INSERT INTO msdb.dbo.sysutility_ucp_aggregated_mi_health_internal(set_number
           , mi_count
           , mi_healthy_count
           , mi_unhealthy_count
           , mi_over_utilize_count
           , mi_under_utilize_count
           , mi_on_over_utilized_computer_count
           , mi_on_under_utilized_computer_count
           , mi_with_files_on_over_utilized_volume_count
           , mi_with_files_on_under_utilized_volume_count
           , mi_with_over_utilized_file_count
           , mi_with_under_utilized_file_count
           , mi_with_over_utilized_processor_count
           , mi_with_under_utilized_processor_count)
    SELECT @new_set_number
            , @mi_count 
            , @mi_healthy_count 
            , @mi_unhealthy_count 
            , @mi_over_utilize_count 
            , @mi_under_utilize_count 
            , ISNULL(SUM(CASE WHEN 0 < hs.is_computer_processor_over_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_computer_processor_under_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_volume_space_over_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_volume_space_under_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_file_space_over_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_file_space_under_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_mi_processor_over_utilized THEN 1 ELSE 0 END), 0)
            , ISNULL(SUM(CASE WHEN 0 < hs.is_mi_processor_under_utilized THEN 1 ELSE 0 END), 0)
    FROM msdb.dbo.sysutility_ucp_mi_health_internal hs 
    WHERE hs.set_number = @new_set_number
END
GO        

--**********************************************************************
-- Create procedure sp_sysutility_ucp_calculate_health                 
-- Procedure description:
-- This SP computes the health state of the utility resources based on the 
-- policy evaluation results. The scheduled job runs the policies which evaluate
-- the resource for under and over utilization. The health state on the rollup object
-- are then determined by aggregating the health state of the underlying objects. 
-- Following are the resource on which health states determined 
-- 1. DAC / Server processor 
-- 2. File storage space  
-- 3. Volume storage space
-- 4. Computer processor
--**********************************************************************
IF OBJECT_ID ('dbo.sp_sysutility_ucp_calculate_health') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_health', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_health 
END;
GO

RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_health', 0, 1) WITH NOWAIT;
GO

CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_health 
WITH EXECUTE AS OWNER
AS
BEGIN
    SET NOCOUNT ON;
    -- Snapshot isolation prevents the nightly purge jobs that delete much older data from blocking us. 
    SET TRANSACTION ISOLATION LEVEL SNAPSHOT; 

    DECLARE @new_set_number INT
    DECLARE @myTableVar table(next_health_state_id INT);
    DECLARE @task_start_time DATETIME;
    DECLARE @task_elapsed_ms INT;
            
    -- get the "latest" set-number. We want all the health_state tables to 
    -- reflect the same point in time, and we achieve this by using a single 
    -- set_number column in each of the tables. At any point of time, we should 
    -- be using the entries from the table which correspond to the latest_health_state_id 
    -- value in the sysutility_ucp_processing_state_internal table
    UPDATE msdb.dbo.sysutility_ucp_processing_state_internal 
      SET next_health_state_id = next_health_state_id + 1
      OUTPUT INSERTED.next_health_state_id INTO @myTableVar;

    SELECT @new_set_number = next_health_state_id 
      FROM @myTableVar;
      
    -- Fetch the violations for health polices from latest policy evaluation 
    -- and cache them in the intermediate table. All the health state queries
    -- reference this table to optimize performance       
    SET @task_start_time = GETUTCDATE();
    EXEc dbo.sp_sysutility_ucp_get_policy_violations
    SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE());
    RAISERROR ('sp_sysutility_ucp_get_policy_violations completed in %d ms', 0, 1, @task_elapsed_ms);
    SET @task_start_time = GETUTCDATE();
    
    -- Identify filegroups that have a policy violation. (i.e.) all files in the filegroup
    -- should have violated the same policy. Logfiles are considered to belong to a
    -- fake filegroup with name=N''
    -- We will use this information in subsequent calls
    EXEC dbo.sp_sysutility_ucp_calculate_filegroups_with_policy_violations @new_set_number
    SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE());
    RAISERROR ('sp_sysutility_ucp_calculate_filegroups_with_policy_violations completed in %d ms', 0, 1, @task_elapsed_ms);
    SET @task_start_time = GETUTCDATE();
    
    -- Compute computer health state
    EXEC  sp_sysutility_ucp_calculate_computer_health @new_set_number
    SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE());
    RAISERROR ('sp_sysutility_ucp_calculate_computer_health completed in %d ms', 0, 1, @task_elapsed_ms);
    SET @task_start_time = GETUTCDATE();

    -- Compute dac health state
    EXEC  msdb.dbo.sp_sysutility_ucp_calculate_dac_health @new_set_number
    SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE());
    RAISERROR ('sp_sysutility_ucp_calculate_dac_health completed in %d ms', 0, 1, @task_elapsed_ms);
    SET @task_start_time = GETUTCDATE();
    
    -- Compute dac dashboard health stats 
    EXEC msdb.dbo.sp_sysutility_ucp_calculate_aggregated_dac_health @new_set_number
    SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE());
    RAISERROR ('sp_sysutility_ucp_calculate_aggregated_dac_health completed in %d ms', 0, 1, @task_elapsed_ms);
    SET @task_start_time = GETUTCDATE();
    
    -- Compute managed instance health state
    EXEC msdb.dbo.sp_sysutility_ucp_calculate_mi_health @new_set_number
    SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE());
    RAISERROR ('sp_sysutility_ucp_calculate_mi_health completed in %d ms', 0, 1, @task_elapsed_ms);
    SET @task_start_time = GETUTCDATE();
    
    -- Compute managed instance dashboard health stats 
    EXEC msdb.dbo.sp_sysutility_ucp_calculate_aggregated_mi_health @new_set_number
    SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE());
    RAISERROR ('sp_sysutility_ucp_calculate_aggregated_mi_health completed in %d ms', 0, 1, @task_elapsed_ms);
    SET @task_start_time = GETUTCDATE();
    
    -- Update the config table with the new set_number
    UPDATE msdb.dbo.sysutility_ucp_processing_state_internal
      SET latest_health_state_id = @new_set_number
    
    -- Delete the old sets
    SET @task_start_time = GETUTCDATE();
    DELETE FROM msdb.dbo.sysutility_ucp_aggregated_mi_health_internal WHERE set_number < @new_set_number
    DELETE FROM msdb.dbo.sysutility_ucp_mi_health_internal WHERE set_number < @new_set_number
    DELETE FROM msdb.dbo.sysutility_ucp_aggregated_dac_health_internal WHERE set_number < @new_set_number
    DELETE FROM msdb.dbo.sysutility_ucp_dac_health_internal WHERE set_number < @new_set_number
    DELETE FROM msdb.dbo.sysutility_ucp_computer_cpu_health_internal WHERE set_number < @new_set_number
    DELETE FROM msdb.dbo.sysutility_ucp_mi_volume_space_health_internal WHERE set_number < @new_set_number
    DELETE FROM msdb.dbo.sysutility_ucp_mi_database_health_internal WHERE set_number < @new_set_number
    DELETE FROM msdb.dbo.sysutility_ucp_mi_file_space_health_internal WHERE set_number < @new_set_number
    DELETE FROM msdb.dbo.sysutility_ucp_dac_file_space_health_internal WHERE set_number < @new_set_number
    DELETE FROM msdb.dbo.sysutility_ucp_filegroups_with_policy_violations_internal WHERE set_number < @new_set_number
    SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE());
    RAISERROR ('Deleted older sets in %d ms', 0, 1, @task_elapsed_ms);
    
END
GO


IF OBJECT_ID ('dbo.sp_sysutility_ucp_configure_policies') IS NOT NULL
BEGIN
   RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_configure_policies', 0, 1) WITH NOWAIT;
   DROP PROCEDURE dbo.sp_sysutility_ucp_configure_policies
END;
GO

RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_configure_policies', 0, 1) WITH NOWAIT;
GO
CREATE PROCEDURE [dbo].[sp_sysutility_ucp_configure_policies]
WITH EXECUTE AS OWNER
AS
BEGIN
    DECLARE @condition_id INT
    DECLARE @object_set_id INT
    DECLARE @target_set_id INT
    DECLARE @policy_id INT
    DECLARE @computer_urn NVARCHAR(4000)
    DECLARE @dac_urn NVARCHAR(4000)
    DECLARE @server_urn NVARCHAR(4000)
    DECLARE @start DATETIME

    SELECT @start = getdate()

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- Delete existing Policies, Conditions, objectSets and Schedule
    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityComputerProcessorOverUtilizationPolicy')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityComputerProcessorOverUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityComputerProcessorOverUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityComputerProcessorOverUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityComputerProcessorOverUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityComputerProcessorOverUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityComputerProcessorOverUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityComputerProcessorOverUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityComputerProcessorOverUtilizationCondition'
    END

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityComputerProcessorOverUtilizationTargetCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityComputerProcessorOverUtilizationTargetCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityComputerProcessorOverUtilizationTargetCondition'
    END

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityComputerProcessorUnderUtilizationPolicy')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityComputerProcessorUnderUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityComputerProcessorUnderUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityComputerProcessorUnderUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityComputerProcessorUnderUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityComputerProcessorUnderUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityComputerProcessorUnderUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityComputerProcessorUnderUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityComputerProcessorUnderUtilizationCondition'
    END   

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityComputerProcessorUnderUtilizationTargetCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityComputerProcessorUnderUtilizationTargetCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityComputerProcessorUnderUtilizationTargetCondition'
    END   

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityDacDataFileSpaceOverUtilizationPolicy')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacDataFileSpaceOverUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityDacDataFileSpaceOverUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityDacDataFileSpaceOverUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacDataFileSpaceOverUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityDacDataFileSpaceOverUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacDataFileSpaceOverUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacDataFileSpaceOverUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacDataFileSpaceOverUtilizationCondition'
    END    

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacDataFileSpaceUnderUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacDataFileSpaceUnderUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacDataFileSpaceUnderUtilizationCondition'
    END   

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityDacLogFileSpaceOverUtilizationPolicy')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacLogFileSpaceOverUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityDacLogFileSpaceOverUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityDacLogFileSpaceOverUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacLogFileSpaceOverUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityDacLogFileSpaceOverUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacLogFileSpaceOverUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacLogFileSpaceOverUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacLogFileSpaceOverUtilizationCondition'
    END    

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacLogFileSpaceUnderUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacLogFileSpaceUnderUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacLogFileSpaceUnderUtilizationCondition'
    END    

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityServerDataFileSpaceOverUtilizationPolicy')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerDataFileSpaceOverUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityServerDataFileSpaceOverUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityServerDataFileSpaceOverUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerDataFileSpaceOverUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityServerDataFileSpaceOverUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerDataFileSpaceOverUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerDataFileSpaceOverUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerDataFileSpaceOverUtilizationCondition'
    END    

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerDataFileSpaceUnderUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerDataFileSpaceUnderUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerDataFileSpaceUnderUtilizationCondition'
    END   

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityServerLogFileSpaceOverUtilizationPolicy')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerLogFileSpaceOverUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityServerLogFileSpaceOverUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityServerLogFileSpaceOverUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerLogFileSpaceOverUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityServerLogFileSpaceOverUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerLogFileSpaceOverUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerLogFileSpaceOverUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerLogFileSpaceOverUtilizationCondition'
    END    

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy')
    BEGIN
    	EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerLogFileSpaceUnderUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerLogFileSpaceUnderUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerLogFileSpaceUnderUtilizationCondition'
    END    

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityServerProcessorOverUtilizationPolicy')
    BEGIN
    	EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerProcessorOverUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityServerProcessorOverUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityServerProcessorOverUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerProcessorOverUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityServerProcessorOverUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerProcessorOverUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerProcessorOverUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerProcessorOverUtilizationCondition'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerProcessorOverUtilizationTargetCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerProcessorOverUtilizationTargetCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerProcessorOverUtilizationTargetCondition'
    END    

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityServerProcessorUnderUtilizationPolicy')
    BEGIN
    	EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerProcessorUnderUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityServerProcessorUnderUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityServerProcessorUnderUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerProcessorUnderUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityServerProcessorUnderUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerProcessorUnderUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerProcessorUnderUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerProcessorUnderUtilizationCondition'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerProcessorUnderUtilizationTargetCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerProcessorUnderUtilizationTargetCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerProcessorUnderUtilizationTargetCondition'
    END    

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityDacProcessorOverUtilizationPolicy')
    BEGIN
    	EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacProcessorOverUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityDacProcessorOverUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityDacProcessorOverUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacProcessorOverUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityDacProcessorOverUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacProcessorOverUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacProcessorOverUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacProcessorOverUtilizationCondition'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacProcessorOverUtilizationTargetCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacProcessorOverUtilizationTargetCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacProcessorOverUtilizationTargetCondition'
    END    

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityDacProcessorUnderUtilizationPolicy')
    BEGIN
    	EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacProcessorUnderUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityDacProcessorUnderUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityDacProcessorUnderUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacProcessorUnderUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityDacProcessorUnderUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacProcessorUnderUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacProcessorUnderUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacProcessorUnderUtilizationCondition'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacProcessorUnderUtilizationTargetCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacProcessorUnderUtilizationTargetCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacProcessorUnderUtilizationTargetCondition'
    END    

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityVolumeSpaceOverUtilizationPolicy')
    BEGIN
    	EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityVolumeSpaceOverUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityVolumeSpaceOverUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityVolumeSpaceOverUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityVolumeSpaceOverUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityVolumeSpaceOverUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityVolumeSpaceOverUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityVolumeSpaceOverUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityVolumeSpaceOverUtilizationCondition'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityVolumeSpaceOverUtilizationTargetCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityVolumeSpaceOverUtilizationTargetCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityVolumeSpaceOverUtilizationTargetCondition'
    END    

    IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityVolumeSpaceUnderUtilizationPolicy')
    BEGIN
    	EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityVolumeSpaceUnderUtilizationPolicy', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityVolumeSpaceUnderUtilizationPolicy'
    END    

    IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityVolumeSpaceUnderUtilizationPolicy_ObjectSet')
    BEGIN   
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityVolumeSpaceUnderUtilizationPolicy_ObjectSet', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityVolumeSpaceUnderUtilizationPolicy_ObjectSet'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityVolumeSpaceUnderUtilizationCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityVolumeSpaceUnderUtilizationCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityVolumeSpaceUnderUtilizationCondition'
    END    

    IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityVolumeSpaceUnderUtilizationTargetCondition')
    BEGIN
        EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityVolumeSpaceUnderUtilizationTargetCondition', @marker=0
        EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityVolumeSpaceUnderUtilizationTargetCondition'
    END    

    IF EXISTS(SELECT COUNT(*) FROM msdb.dbo.sysutility_ucp_health_policies_internal)
    BEGIN
        DELETE FROM msdb.dbo.sysutility_ucp_health_policies_internal
    END    

    IF EXISTS(SELECT schedule_id FROM msdb.dbo.sysschedules WHERE name=N'UtilityResourceHealthStateSchedule')
    BEGIN
        EXEC msdb.dbo.sp_delete_schedule @schedule_name=N'UtilityResourceHealthStateSchedule', @force_delete=1
    END 

    IF  EXISTS (SELECT job_id FROM msdb.dbo.sysjobs_view WHERE name = N'Utility Resource Health State')
    BEGIN
        EXEC msdb.dbo.sp_delete_job @job_name=N'Utility Resource Health State', @delete_unused_schedule=1
    END

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityComputerProcessorOverUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityComputerProcessorOverUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the CPU overutilization policy is satisfied for a computer that hosts a managed instance of SQL Server. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'Computer', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>LE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>ProcessorUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>70</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id
    
    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityComputerProcessorOverUtilizationCondition', @marker=1


    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityComputerProcessorOverUtilizationTargetCondition', 
    @description=N'The SQL Server Utility condition that is used to determine whether the CPU overutilization policy is violated for a computer that hosts a managed instance of SQL Server. This condition is used by the utility control point to query the computers.', 
    @facet=N'Computer', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>GT</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>ProcessorUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>70</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id
        
    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityComputerProcessorOverUtilizationTargetCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityComputerProcessorUnderUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityComputerProcessorUnderUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the CPU underutilization policy is satisfied for a computer that hosts a managed instance of SQL Server. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'Computer', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>GE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>ProcessorUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>0</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityComputerProcessorUnderUtilizationCondition', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityComputerProcessorUnderUtilizationTargetCondition', 
    @description=N'The SQL Server Utility condition that is used to determine whether the CPU underutilization policy is violated for a computer that hosts a managed instance of SQL Server. This condition is used by the utility control point to query the computers.', 
    @facet=N'Computer', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>LT</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>ProcessorUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>0</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityComputerProcessorUnderUtilizationTargetCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityDacDataFileSpaceOverUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityDacDataFileSpaceOverUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the data file space overutilization policy is satisfied for a deployed data-tier application. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'IDataFilePerformanceFacet', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>LE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>SpaceUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>70</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacDataFileSpaceOverUtilizationCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityDacDataFileSpaceUnderUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityDacDataFileSpaceUnderUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the data file space underutilization policy is satisfied for a deployed data-tier application. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'IDataFilePerformanceFacet', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>GE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>SpaceUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>0</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacDataFileSpaceUnderUtilizationCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityDacLogFileSpaceOverUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityDacLogFileSpaceOverUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the log file space overutilization policy is satisfied for a deployed data-tier application. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'ILogFilePerformanceFacet', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>LE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>SpaceUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>70</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacLogFileSpaceOverUtilizationCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityDacLogFileSpaceUnderUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityDacLogFileSpaceUnderUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the log file space underutilization policy is satisfied for a deployed data-tier application. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'ILogFilePerformanceFacet', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>GE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>SpaceUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>0</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacLogFileSpaceUnderUtilizationCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityServerDataFileSpaceOverUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityServerDataFileSpaceOverUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the data file space overutilization policy is satisfied for a managed instance of SQL Server. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'IDataFilePerformanceFacet', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>LE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>SpaceUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>70</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerDataFileSpaceOverUtilizationCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityServerDataFileSpaceUnderUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityServerDataFileSpaceUnderUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the data file space underutilization policy is satisfied for a managed instance of SQL Server. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'IDataFilePerformanceFacet', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>GE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>SpaceUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>0</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerDataFileSpaceUnderUtilizationCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityServerLogFileSpaceOverUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityServerLogFileSpaceOverUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the log file space overutilization policy is satisfied for a managed instance of SQL Server. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'ILogFilePerformanceFacet', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>LE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>SpaceUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>70</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerLogFileSpaceOverUtilizationCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityServerLogFileSpaceUnderUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityServerLogFileSpaceUnderUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the log file space underutilization policy is satisfied for a managed instance of SQL Server. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'ILogFilePerformanceFacet', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>GE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>SpaceUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>0</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerLogFileSpaceUnderUtilizationCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityServerProcessorOverUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityServerProcessorOverUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the CPU overutilization policy is satisfied for a managed instance of SQL Server. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'Server', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>LE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>ProcessorUsage</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>70</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerProcessorOverUtilizationCondition', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityServerProcessorOverUtilizationTargetCondition', 
    @description=N'The SQL Server Utility condition that is used to determine whether the CPU overutilization policy is violated for a managed instance of SQL Server. This condition is used by the utility control point to query the managed instances of SQL Server.', 
    @facet=N'Server', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>GT</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>ProcessorUsage</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>70</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerProcessorOverUtilizationTargetCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityServerProcessorUnderUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityServerProcessorUnderUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the CPU underutilization policy is satisfied for a managed instance of SQL Server. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'Server', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>GE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>ProcessorUsage</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>0</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerProcessorUnderUtilizationCondition', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityServerProcessorUnderUtilizationTargetCondition', 
    @description=N'The SQL Server Utility condition that is used to determine whether the CPU underutilization policy is violated for a managed instance of SQL Server. This condition is used by the utility control point to query the managed instances of SQL Server.', 
    @facet=N'Server', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>LT</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>ProcessorUsage</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>0</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerProcessorUnderUtilizationTargetCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityDacProcessorOverUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityDacProcessorOverUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the CPU overutilization policy is satisfied for a deployed data-tier application. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'DeployedDac', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>LE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>ProcessorUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>70</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacProcessorOverUtilizationCondition', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityDacProcessorOverUtilizationTargetCondition', 
    @description=N'The SQL Server Utility condition that is used to determine whether the CPU overutilization policy is violated for a deployed data-tier application. This condition is used by the utility control point to query the deployed data-tier applications. ', 
    @facet=N'DeployedDac', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>GT</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>ProcessorUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>70</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacProcessorOverUtilizationTargetCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityDacProcessorUnderUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityDacProcessorUnderUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the CPU underutilization policy is satisfied for a deployed data-tier application. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'DeployedDac', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>GE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>ProcessorUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>0</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacProcessorUnderUtilizationCondition', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityDacProcessorUnderUtilizationTargetCondition', 
    @description=N'The SQL Server Utility condition that is used to determine whether the CPU underutilization policy is violated for a deployed data-tier application. This condition is used by the utility control point to query the deployed data-tier applications. ', 
    @facet=N'DeployedDac', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>LT</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>ProcessorUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>0</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacProcessorUnderUtilizationTargetCondition', @marker=1

    ------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityVolumeSpaceOverUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityVolumeSpaceOverUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the volume space overutilization policy is satisfied for a computer that hosts a managed instance of SQL Server. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'Volume', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>LE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>TotalSpaceUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>70</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityVolumeSpaceOverUtilizationCondition', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityVolumeSpaceOverUtilizationTargetCondition', 
    @description=N'The SQL Server Utility condition that is used to determine whether the volume space overutilization policy is violated for a computer that hosts a managed instance of SQL Server. This condition is used by the utility control point to query the volumes.', 
    @facet=N'Volume', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>GT</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>TotalSpaceUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>70</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityVolumeSpaceOverUtilizationTargetCondition', @marker=1

    ------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityVolumeSpaceUnderUtilizationCondition
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityVolumeSpaceUnderUtilizationCondition', 
    @description=N'The SQL Server Utility condition that expresses when the volume space underutilization policy is satisfied for a computer that hosts a managed instance of SQL Server. The value that is used in the condition expression is set in SQL Server Utility Explorer.', 
    @facet=N'Volume', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>GE</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>TotalSpaceUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>0</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityVolumeSpaceUnderUtilizationCondition', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityVolumeSpaceUnderUtilizationTargetCondition', 
    @description=N'The SQL Server Utility condition that is used to determine whether the volume space underutilization policy is violated for a computer that hosts a managed instance of SQL Server. This condition is used by the utility control point to query the volumes. ', 
    @facet=N'Volume', @expression=N'<Operator>
      <TypeClass>Bool</TypeClass>
      <OpType>LT</OpType>
      <Count>2</Count>
      <Attribute>
        <TypeClass>Numeric</TypeClass>
        <Name>TotalSpaceUtilization</Name>
      </Attribute>
      <Constant>
        <TypeClass>Numeric</TypeClass>
        <ObjType>System.Double</ObjType>
        <Value>0</Value>
      </Constant>
    </Operator>', @is_name_condition=0, @obj_name=N'', @condition_id=@condition_id OUTPUT
    Select @condition_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityVolumeSpaceUnderUtilizationTargetCondition', @marker=1

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityComputerProcessorOverUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityComputerProcessorOverUtilizationPolicy_ObjectSet', @facet=N'Computer', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityComputerProcessorOverUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityComputerProcessorOverUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Computer', @type=N'COMPUTER', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Computer', @level_name=N'Computer', @condition_name=N'UtilityComputerProcessorOverUtilizationTargetCondition', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityComputerProcessorOverUtilizationPolicy', @condition_name=N'UtilityComputerProcessorOverUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for CPU overutilization on a computer that hosts a managed instance of SQL Server.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityComputerProcessorOverUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityComputerProcessorOverUtilizationPolicy', @marker=1

    SELECT @computer_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/Computer'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityComputerProcessorOverUtilizationPolicy',@rollup_object_type=3,@rollup_object_urn=@computer_urn,@target_type=1,@resource_type=3,@utilization_type=2,@utilization_threshold=70

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityComputerProcessorUnderUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityComputerProcessorUnderUtilizationPolicy_ObjectSet', @facet=N'Computer', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityComputerProcessorUnderUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityComputerProcessorUnderUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Computer', @type=N'COMPUTER', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Computer', @level_name=N'Computer', @condition_name=N'UtilityComputerProcessorUnderUtilizationTargetCondition', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityComputerProcessorUnderUtilizationPolicy', @condition_name=N'UtilityComputerProcessorUnderUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for CPU underutilization on a computer that hosts a managed instance of SQL Server.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityComputerProcessorUnderUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityComputerProcessorUnderUtilizationPolicy', @marker=1

    SELECT @computer_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/Computer'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityComputerProcessorUnderUtilizationPolicy',@rollup_object_type=3,@rollup_object_urn=@computer_urn,@target_type=1,@resource_type=3,@utilization_type=1,@utilization_threshold=0

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityDacDataFileSpaceOverUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityDacDataFileSpaceOverUtilizationPolicy_ObjectSet', @facet=N'IDataFilePerformanceFacet', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacDataFileSpaceOverUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityDacDataFileSpaceOverUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Server/Database/FileGroup/File', @type=N'FILE', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database/FileGroup/File', @level_name=N'File', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database/FileGroup', @level_name=N'FileGroup', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database', @level_name=N'Database', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server', @level_name=N'Server', @condition_name=N'', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityDacDataFileSpaceOverUtilizationPolicy', @condition_name=N'UtilityDacDataFileSpaceOverUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for data file space overutilization for a deployed data-tier application.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityDacDataFileSpaceOverUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacDataFileSpaceOverUtilizationPolicy', @marker=1

    SELECT @dac_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/DeployedDac'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityDacDataFileSpaceOverUtilizationPolicy',@rollup_object_type=1,@rollup_object_urn=@dac_urn,@target_type=2,@resource_type=1,@utilization_type=2,@utilization_threshold=70

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityDacDataFileSpaceUnderUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy_ObjectSet', @facet=N'IDataFilePerformanceFacet', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Server/Database/FileGroup/File', @type=N'FILE', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database/FileGroup/File', @level_name=N'File', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database/FileGroup', @level_name=N'FileGroup', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database', @level_name=N'Database', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server', @level_name=N'Server', @condition_name=N'', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy', @condition_name=N'UtilityDacDataFileSpaceUnderUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for data file space underutilization for a deployed data-tier application.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityDacDataFileSpaceUnderUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy', @marker=1

    SELECT @dac_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/DeployedDac'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy',@rollup_object_type=1,@rollup_object_urn=@dac_urn,@target_type=2,@resource_type=1,@utilization_type=1,@utilization_threshold=0

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityDacLogFileSpaceOverUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityDacLogFileSpaceOverUtilizationPolicy_ObjectSet', @facet=N'ILogFilePerformanceFacet', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacLogFileSpaceOverUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityDacLogFileSpaceOverUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Server/Database/LogFile', @type=N'LOGFILE', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database/LogFile', @level_name=N'LogFile', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database', @level_name=N'Database', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server', @level_name=N'Server', @condition_name=N'', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityDacLogFileSpaceOverUtilizationPolicy', @condition_name=N'UtilityDacLogFileSpaceOverUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for log file space overutilization for a deployed data-tier application.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityDacLogFileSpaceOverUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacLogFileSpaceOverUtilizationPolicy', @marker=1

    SELECT @dac_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/DeployedDac'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityDacLogFileSpaceOverUtilizationPolicy',@rollup_object_type=1,@rollup_object_urn=@dac_urn,@target_type=3,@resource_type=1,@utilization_type=2,@utilization_threshold=70

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityDacLogFileSpaceUnderUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy_ObjectSet', @facet=N'ILogFilePerformanceFacet', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Server/Database/LogFile', @type=N'LOGFILE', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database/LogFile', @level_name=N'LogFile', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database', @level_name=N'Database', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server', @level_name=N'Server', @condition_name=N'', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy', @condition_name=N'UtilityDacLogFileSpaceUnderUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for log file space underutilization for a deployed data-tier application.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityDacLogFileSpaceUnderUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy', @marker=1

    SELECT @dac_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/DeployedDac'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy',@rollup_object_type=1,@rollup_object_urn=@dac_urn,@target_type=3,@resource_type=1,@utilization_type=1,@utilization_threshold=0

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityServerDataFileSpaceOverUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityServerDataFileSpaceOverUtilizationPolicy_ObjectSet', @facet=N'IDataFilePerformanceFacet', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerDataFileSpaceOverUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityServerDataFileSpaceOverUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Server/Database/FileGroup/File', @type=N'FILE', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database/FileGroup/File', @level_name=N'File', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database/FileGroup', @level_name=N'FileGroup', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database', @level_name=N'Database', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server', @level_name=N'Server', @condition_name=N'', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityServerDataFileSpaceOverUtilizationPolicy', @condition_name=N'UtilityServerDataFileSpaceOverUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for data file space overutilization on a managed instance of SQL Server.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityServerDataFileSpaceOverUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerDataFileSpaceOverUtilizationPolicy', @marker=1

    SELECT @server_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/Server'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityServerDataFileSpaceOverUtilizationPolicy',@rollup_object_type=2,@rollup_object_urn=@server_urn,@target_type=2,@resource_type=1,@utilization_type=2,@utilization_threshold=70

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityServerDataFileSpaceUnderUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy_ObjectSet', @facet=N'IDataFilePerformanceFacet', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Server/Database/FileGroup/File', @type=N'FILE', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database/FileGroup/File', @level_name=N'File', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database/FileGroup', @level_name=N'FileGroup', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database', @level_name=N'Database', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server', @level_name=N'Server', @condition_name=N'', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy', @condition_name=N'UtilityServerDataFileSpaceUnderUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for data file space underutilization on a managed instance of SQL Server.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityServerDataFileSpaceUnderUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy', @marker=1

    SELECT @server_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/Server'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy',@rollup_object_type=2,@rollup_object_urn=@server_urn,@target_type=2,@resource_type=1,@utilization_type=1,@utilization_threshold=0

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityServerLogFileSpaceOverUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityServerLogFileSpaceOverUtilizationPolicy_ObjectSet', @facet=N'ILogFilePerformanceFacet', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerLogFileSpaceOverUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityServerLogFileSpaceOverUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Server/Database/LogFile', @type=N'LOGFILE', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database/LogFile', @level_name=N'LogFile', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database', @level_name=N'Database', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server', @level_name=N'Server', @condition_name=N'', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityServerLogFileSpaceOverUtilizationPolicy', @condition_name=N'UtilityServerLogFileSpaceOverUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for log file space overutilization on a managed instance of SQL Server.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityServerLogFileSpaceOverUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerLogFileSpaceOverUtilizationPolicy', @marker=1

    SELECT @server_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/Server'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityServerLogFileSpaceOverUtilizationPolicy',@rollup_object_type=2,@rollup_object_urn=@server_urn,@target_type=3,@resource_type=1,@utilization_type=2,@utilization_threshold=70

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityServerLogFileSpaceUnderUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy_ObjectSet', @facet=N'ILogFilePerformanceFacet', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Server/Database/LogFile', @type=N'LOGFILE', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database/LogFile', @level_name=N'LogFile', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server/Database', @level_name=N'Database', @condition_name=N'', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server', @level_name=N'Server', @condition_name=N'', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy', @condition_name=N'UtilityServerLogFileSpaceUnderUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for log file space underutilization on a managed instance of SQL Server.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityServerLogFileSpaceUnderUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy', @marker=1

    SELECT @server_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/Server'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy',@rollup_object_type=2,@rollup_object_urn=@server_urn,@target_type=3,@resource_type=1,@utilization_type=1,@utilization_threshold=0

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityServerProcessorOverUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityServerProcessorOverUtilizationPolicy_ObjectSet', @facet=N'Server', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerProcessorOverUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityServerProcessorOverUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Server', @type=N'SERVER', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server', @level_name=N'Server', @condition_name=N'UtilityServerProcessorOverUtilizationTargetCondition', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityServerProcessorOverUtilizationPolicy', @condition_name=N'UtilityServerProcessorOverUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for CPU overutilization on a managed instance of SQL Server.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityServerProcessorOverUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerProcessorOverUtilizationPolicy', @marker=1

    SELECT @server_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/Server'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityServerProcessorOverUtilizationPolicy',@rollup_object_type=2,@rollup_object_urn=@server_urn,@target_type=4,@resource_type=3,@utilization_type=2,@utilization_threshold=70

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityServerProcessorUnderUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityServerProcessorUnderUtilizationPolicy_ObjectSet', @facet=N'Server', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerProcessorUnderUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityServerProcessorUnderUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Server', @type=N'SERVER', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Server', @level_name=N'Server', @condition_name=N'UtilityServerProcessorUnderUtilizationTargetCondition', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityServerProcessorUnderUtilizationPolicy', @condition_name=N'UtilityServerProcessorUnderUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for CPU underutilization on a managed instance of SQL Server.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityServerProcessorUnderUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerProcessorUnderUtilizationPolicy', @marker=1

    SELECT @server_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/Server'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityServerProcessorUnderUtilizationPolicy',@rollup_object_type=2,@rollup_object_urn=@server_urn,@target_type=4,@resource_type=3,@utilization_type=1,@utilization_threshold=0

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityDacProcessorOverUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityDacProcessorOverUtilizationPolicy_ObjectSet', @facet=N'DeployedDac', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacProcessorOverUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityDacProcessorOverUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/DeployedDac', @type=N'DEPLOYEDDAC', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/DeployedDac', @level_name=N'DeployedDac', @condition_name=N'UtilityDacProcessorOverUtilizationTargetCondition', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityDacProcessorOverUtilizationPolicy', @condition_name=N'UtilityDacProcessorOverUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for CPU overutilization for a deployed data-tier application.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityDacProcessorOverUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacProcessorOverUtilizationPolicy', @marker=1

    SELECT @dac_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/DeployedDac'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityDacProcessorOverUtilizationPolicy',@rollup_object_type=1,@rollup_object_urn=@dac_urn,@target_type=5,@resource_type=3,@utilization_type=2,@utilization_threshold=70

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityDacProcessorUnderUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityDacProcessorUnderUtilizationPolicy_ObjectSet', @facet=N'DeployedDac', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacProcessorUnderUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityDacProcessorUnderUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/DeployedDac', @type=N'DEPLOYEDDAC', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/DeployedDac', @level_name=N'DeployedDac', @condition_name=N'UtilityDacProcessorUnderUtilizationTargetCondition', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityDacProcessorUnderUtilizationPolicy', @condition_name=N'UtilityDacProcessorUnderUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for CPU underutilization for a deployed data-tier application.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityDacProcessorUnderUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacProcessorUnderUtilizationPolicy', @marker=1

    SELECT @dac_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/DeployedDac'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityDacProcessorUnderUtilizationPolicy',@rollup_object_type=1,@rollup_object_urn=@dac_urn,@target_type=5,@resource_type=3,@utilization_type=1,@utilization_threshold=0

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityVolumeSpaceOverUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityVolumeSpaceOverUtilizationPolicy_ObjectSet', @facet=N'Volume', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityVolumeSpaceOverUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityVolumeSpaceOverUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Computer/Volume', @type=N'VOLUME', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Computer/Volume', @level_name=N'Volume', @condition_name=N'UtilityVolumeSpaceOverUtilizationTargetCondition', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Computer', @level_name=N'Computer', @condition_name=N'', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityVolumeSpaceOverUtilizationPolicy', @condition_name=N'UtilityVolumeSpaceOverUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for volume space overutilization on a computer that hosts a managed instance of SQL Server.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityVolumeSpaceOverUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityVolumeSpaceOverUtilizationPolicy', @marker=1

    SELECT @computer_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/Computer'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityVolumeSpaceOverUtilizationPolicy',@rollup_object_type=3,@rollup_object_urn=@computer_urn,@target_type=6,@resource_type=1,@utilization_type=2,@utilization_threshold=70

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- UtilityVolumeSpaceUnderUtilizationPolicy
    -------------------------------------------------------------------------------------------------------------------------------------------------------------

    EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'UtilityVolumeSpaceUnderUtilizationPolicy_ObjectSet', @facet=N'Volume', @object_set_id=@object_set_id OUTPUT
    Select @object_set_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityVolumeSpaceUnderUtilizationPolicy_ObjectSet', @marker=1

    EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'UtilityVolumeSpaceUnderUtilizationPolicy_ObjectSet', @type_skeleton=N'Utility/Computer/Volume', @type=N'VOLUME', @enabled=True, @target_set_id=@target_set_id OUTPUT
    Select @target_set_id

    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Computer/Volume', @level_name=N'Volume', @condition_name=N'UtilityVolumeSpaceUnderUtilizationTargetCondition', @target_set_level_id=0
    EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@target_set_id, @type_skeleton=N'Utility/Computer', @level_name=N'Computer', @condition_name=N'', @target_set_level_id=0

    EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'UtilityVolumeSpaceUnderUtilizationPolicy', @condition_name=N'UtilityVolumeSpaceUnderUtilizationCondition', @policy_category=N'', 
    @description=N'The SQL Server Utility policy that checks for volume space underutilization on a computer that hosts a managed instance of SQL Server.', 
    @help_text=N'', @help_link=N'', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=@policy_id OUTPUT, @root_condition_name=N'', @object_set=N'UtilityVolumeSpaceUnderUtilizationPolicy_ObjectSet'
    Select @policy_id

    EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityVolumeSpaceUnderUtilizationPolicy', @marker=1

    SELECT @computer_urn = 'Utility[@Name='''+CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName'))+''']/Computer'
    EXEC msdb.dbo.sp_sysutility_ucp_add_policy @policy_name=N'UtilityVolumeSpaceUnderUtilizationPolicy',@rollup_object_type=3,@rollup_object_urn=@computer_urn,@target_type=6,@resource_type=1,@utilization_type=1,@utilization_threshold=0

    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    -- Update sysutility_ucp_health_policies_internal entries to system policies 
    -------------------------------------------------------------------------------------------------------------------------------------------------------------
    IF EXISTS(SELECT COUNT(*) FROM msdb.dbo.sysutility_ucp_health_policies_internal)
    BEGIN
        UPDATE msdb.dbo.sysutility_ucp_health_policies_internal SET is_global_policy = 1
    END    
    
    -- Compute default health states during UCP creation (boot strap scenario)
    EXEC msdb.dbo.sp_sysutility_ucp_calculate_health
    
    --**********************************************************************
    -- Mark all newly created sp_sysutility* artifacts as system.
    -- (code extracted and modified from "Mark system objects" in instdb.sql.)
    --**********************************************************************
    BEGIN
        declare @name sysname;
        declare newsysobjs cursor for select name from sys.objects where schema_id = 1 and name LIKE '%sysutility%' and create_date >= @start 
        open newsysobjs
        fetch next from newsysobjs into @name
        while @@fetch_status = 0
        begin
            Exec sp_MS_marksystemobject @name
            fetch next from newsysobjs into @name
        end
        deallocate newsysobjs
    END

END
GO

/*******************************************************************
 Procedure to remove utility control point (UCP) from target SQL server instance 

 Following are the pre-requsite validations done by the procedure
 
 a. The user executing the script has sysadmin role on target instance
 b. The target instance must be a utility control point
 c. There are no active enrolled managed instances in the UCP

 The procedure handles cleanup of following UCP artifacts
 
 1. Drops the MDW database if there are no system collection sets enabled; 
    else truncates the utility live, dimension and measure tables.
 2. Update UCP configuration tables with default values
 3. Drop all the resource health policies
 4. Truncate utility health state tables in MSDB database
 5. Remove the utility aggregation jobs 
 6. Remove the utility related registry keys 
*******************************************************************/

IF OBJECT_ID(N'[dbo].[sp_sysutility_ucp_remove]') IS NOT NULL
BEGIN
   RAISERROR('Dropping [dbo].[sp_sysutility_ucp_remove] procedure', 0, 1)  WITH NOWAIT;
   DROP PROCEDURE [dbo].[sp_sysutility_ucp_remove];
END
GO

RAISERROR('Creating [dbo].[sp_sysutility_ucp_remove] procedure', 0, 1)  WITH NOWAIT;
GO

CREATE PROCEDURE [dbo].[sp_sysutility_ucp_remove]
WITH EXECUTE AS CALLER
AS
BEGIN
    SET NOCOUNT ON;
    
    ---------------------------------------------------------------------
    -- Validation Steps
    ---------------------------------------------------------------------

    -- Validate the user running the script is sysadmin on the UCP instance
    IF (1 != IS_SRVROLEMEMBER(N'sysadmin ', SUSER_NAME()))  
    BEGIN
        RAISERROR(37008, -1, -1) 
        RETURN(1)
    END   

    -- Validate the instance is UCP
    IF (0 = (SELECT msdb.dbo.fn_sysutility_get_is_instance_ucp()))
    BEGIN
        RAISERROR(37009, -1, -1) 
        RETURN(1)
    END        
 
    -- Validate all managed instances are un-enrolled
    IF (0 < (SELECT COUNT(*) FROM [dbo].[sysutility_ucp_managed_instances]))  
    BEGIN
        RAISERROR(37010, -1, -1) 
        RETURN(1)
    END  
 

    ---------------------------------------------------------------------
    -- Remove UCP artifacts
    ---------------------------------------------------------------------

    IF  EXISTS (SELECT name FROM [master].[sys].[databases] WHERE name = N'sysutility_mdw')
    BEGIN

        -- Check whether there are other non-utility (DC system / custom) collection sets targeted to sysutility_mdw database
        IF (0 = (SELECT COUNT(*) FROM [sysutility_mdw].[core].[source_info_internal] WHERE collection_set_uid != N'ABA37A22-8039-48C6-8F8F-39BFE0A195DF'))  
        BEGIN

            -- Drop utility MDW database as there are no non-utility collection sets uploading data to this DB
            ALTER DATABASE [sysutility_mdw] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
            DROP DATABASE [sysutility_mdw];
            
            -- Delete MDW purge jobs 
            IF  EXISTS (SELECT job_id FROM [dbo].[sysjobs_view] WHERE name = N'mdw_purge_data_[sysutility_mdw]')
            BEGIN
                EXEC [dbo].sp_delete_job @job_name=N'mdw_purge_data_[sysutility_mdw]', @delete_unused_schedule=1
            END
            
        END  
        ELSE
        BEGIN
        
            -- There are non-utility collection sets uploading data to mdw
            -- so do not drop the MDW database; instead truncate utility tables to purge data
            DECLARE @schema_name SYSNAME
            DECLARE @table_name SYSNAME
            DECLARE @expression NVARCHAR(MAX)
            
            -- Truncate the dimension, measure and live tables in MDW database
            DECLARE tables_cursor CURSOR FOR    
            SELECT object_schema, object_name 
            FROM [sysutility_mdw].[sysutility_ucp_misc].[utility_objects_internal]
            WHERE sql_object_type = N'USER_TABLE'
              AND utility_object_type IN (N'DIMENSION', N'MEASURE', N'LIVE')
                
            OPEN tables_cursor;
            FETCH NEXT FROM tables_cursor INTO @schema_name, @table_name
            WHILE (@@FETCH_STATUS <> -1)
            BEGIN

                SET @expression = 'TRUNCATE TABLE [sysutility_mdw].' + QUOTENAME(@schema_name) + '.' + QUOTENAME(@table_name);
                EXEC sp_executesql @expression;

                FETCH NEXT FROM tables_cursor INTO @schema_name, @table_name
            END;
            CLOSE tables_cursor;
            DEALLOCATE tables_cursor;

        END
    END     

    --###FP 1

    ---------------------------------------------------------------------
    -- Truncate the utility tables in msdb database
    -- Note: Do not truncate tables in which data is pre-shipped
    ---------------------------------------------------------------------
    TRUNCATE TABLE [dbo].[sysutility_ucp_mi_health_internal];
    TRUNCATE TABLE [dbo].[sysutility_ucp_aggregated_mi_health_internal];
    TRUNCATE TABLE [dbo].[sysutility_ucp_mi_database_health_internal];
    TRUNCATE TABLE [dbo].[sysutility_ucp_mi_volume_space_health_internal];
    TRUNCATE TABLE [dbo].[sysutility_ucp_mi_file_space_health_internal];
    TRUNCATE TABLE [dbo].[sysutility_ucp_dac_health_internal];
    TRUNCATE TABLE [dbo].[sysutility_ucp_aggregated_dac_health_internal];
    TRUNCATE TABLE [dbo].[sysutility_ucp_dac_file_space_health_internal];
    TRUNCATE TABLE [dbo].[sysutility_ucp_computer_cpu_health_internal];
    TRUNCATE TABLE [dbo].[sysutility_ucp_filegroups_with_policy_violations_internal];
    TRUNCATE TABLE [dbo].[sysutility_ucp_policy_violations_internal];
    TRUNCATE TABLE [dbo].[sysutility_ucp_snapshot_partitions_internal];

    --###FP 2

    ---------------------------------------------------------------------
    -- Delete utility aggregation jobs 
    ---------------------------------------------------------------------
    IF  EXISTS (SELECT job_id FROM [dbo].[sysjobs_view] WHERE name = N'sysutility_get_views_data_into_cache_tables')
    BEGIN
        EXEC [dbo].sp_delete_job @job_name=N'sysutility_get_views_data_into_cache_tables', @delete_unused_schedule=1
    END       

    IF  EXISTS (SELECT job_id FROM [dbo].[sysjobs_view] WHERE name = N'sysutility_get_cache_tables_data_into_aggregate_tables_hourly')
    BEGIN
        EXEC [dbo].sp_delete_job @job_name=N'sysutility_get_cache_tables_data_into_aggregate_tables_hourly', @delete_unused_schedule=1
    END        

    IF  EXISTS (SELECT job_id FROM [dbo].[sysjobs_view] WHERE name = N'sysutility_get_cache_tables_data_into_aggregate_tables_daily')
    BEGIN
        EXEC [dbo].sp_delete_job @job_name=N'sysutility_get_cache_tables_data_into_aggregate_tables_daily', @delete_unused_schedule=1
    END        

    --###FP 3

    ---------------------------------------------------------------------
    -- Drop resource health policies, conditions and objectSets 
    ---------------------------------------------------------------------
    DECLARE @policy_name SYSNAME
    DECLARE @health_policy_id INT
    DECLARE @policy_id INT
    DECLARE @object_set_id INT
    DECLARE @condition_id INT
    DECLARE @target_condition_id INT
    
    DECLARE policies_cursor CURSOR FOR
    SELECT policy_name, health_policy_id
    FROM [dbo].[sysutility_ucp_policies]

    OPEN policies_cursor;
    FETCH NEXT FROM policies_cursor INTO @policy_name, @health_policy_id
    WHILE (@@FETCH_STATUS <> -1)
    BEGIN
        SELECT @policy_id = policy_id
            , @object_set_id = object_set_id
            , @condition_id = condition_id
        FROM [dbo].[syspolicy_policies]
        WHERE name = @policy_name
        
        -- Delete the policy 
        EXEC [dbo].sp_syspolicy_mark_system @type=N'POLICY', @object_id=@policy_id, @marker=0
        EXEC [dbo].sp_syspolicy_delete_policy @policy_id=@policy_id

        -- Get the target set condtions before deleting the object set  
        CREATE TABLE #target_conditions(condition_id INT);
        
        INSERT INTO #target_conditions 
        SELECT condition_id
        FROM [dbo].[syspolicy_target_sets] ts
            , [dbo].[syspolicy_target_set_levels] tsl
        WHERE ts.target_set_id = tsl.target_set_id
            AND ts.object_set_id = @object_set_id   
            
        -- Delete the object set
        EXEC [dbo].sp_syspolicy_mark_system @type=N'OBJECTSET', @object_id=@object_set_id, @marker=0
        EXEC [dbo].sp_syspolicy_delete_object_set @object_set_id=@object_set_id
        
        DECLARE target_conditions_cursor CURSOR FOR
        SELECT condition_id
        FROM #target_conditions
        
        OPEN target_conditions_cursor;
        FETCH NEXT FROM target_conditions_cursor INTO @target_condition_id
        WHILE (@@FETCH_STATUS <> -1)
        BEGIN
        
            IF (@target_condition_id IS NOT NULL)
            BEGIN
                --- Delete the target set condition
                EXEC [dbo].sp_syspolicy_mark_system @type=N'CONDITION', @object_id=@target_condition_id, @marker=0
                EXEC [dbo].sp_syspolicy_delete_condition @condition_id=@target_condition_id
            END
            FETCH NEXT FROM target_conditions_cursor INTO @target_condition_id
            
        END;
        CLOSE target_conditions_cursor;
        DEALLOCATE target_conditions_cursor;
        DROP TABLE #target_conditions            
                            
        --- Delete the check condition
        EXEC [dbo].sp_syspolicy_mark_system @type=N'CONDITION', @object_id=@condition_id, @marker=0
        EXEC [dbo].sp_syspolicy_delete_condition @condition_id=@condition_id

        -- Delete the resource health policy
        DELETE [dbo].[sysutility_ucp_health_policies_internal]
        WHERE health_policy_id = @health_policy_id
            
        FETCH NEXT FROM policies_cursor INTO @policy_name, @health_policy_id
        
    END;
    CLOSE policies_cursor;
    DEALLOCATE policies_cursor;

    --###FP 4
        
    ---------------------------------------------------------------------
    -- Remove the utility related registry keys from the system
    ---------------------------------------------------------------------
    -- Remove the UtilityVersion registry key value 
    DECLARE @utility_version nvarchar(1024)
    EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                        N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility',
                                        N'UtilityVersion',
                                        @utility_version OUTPUT

    IF (@utility_version IS NOT NULL) 
    BEGIN
        EXEC master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE',
                                                   N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility',
                                                   N'UtilityVersion'
    END

    -- Remove the UcpName registry key value 
    DECLARE @utility_name nvarchar(1024)
    EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                        N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility',
                                        N'UcpName',
                                        @utility_name OUTPUT

    IF (@utility_name IS NOT NULL) 
    BEGIN
        EXEC master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE',
                                                   N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility',
                                                   N'UcpName'
    END
   
    -- Remove the UcpFriendlyName registry key value 
    DECLARE @utility_friendly_name nvarchar(1024)
    EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                        N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility',
                                        N'UcpFriendlyName',
                                        @utility_friendly_name OUTPUT

    IF (@utility_friendly_name IS NOT NULL) 
    BEGIN
        EXEC master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE',
                                                   N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility',
                                                   N'UcpFriendlyName'
    END

    -- Remove the Utility registry key  
    EXEC master.dbo.xp_instance_regdeletekey N'HKEY_LOCAL_MACHINE',
                                             N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility'    

    --###FP 5
            
    ---------------------------------------------------------------------
    -- Reset the processing state table to default values
    ---------------------------------------------------------------------    
    UPDATE [dbo].[sysutility_ucp_processing_state_internal]
    SET latest_processing_time = SYSDATETIMEOFFSET(), latest_health_state_id = 0, next_health_state_id = 1    

    --###FP 6

    ---------------------------------------------------------------------
    -- Update utility configuration table entries to default values
    -- Note: Keep this cleanup as the last one as the script uses this 
    -- to check if the target instance is a UCP in the validation
    ---------------------------------------------------------------------
    UPDATE [dbo].[sysutility_ucp_configuration_internal] SET current_value = N'' WHERE name like N'Utility%'
    UPDATE [dbo].[sysutility_ucp_configuration_internal] SET current_value = N'' WHERE name = N'MdwDatabaseName'
END
GO

/**********************************************************************/
/* Delete Managed Instance.                                           */
/*                                                                    */
/*                                                                    */
/**********************************************************************/
IF OBJECT_ID ('dbo.sp_sysutility_ucp_remove_mi') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_remove_mi]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE dbo.sp_sysutility_ucp_remove_mi 
END;
GO


RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_remove_mi]', 0, 1) WITH NOWAIT;
GO

CREATE PROCEDURE [dbo].[sp_sysutility_ucp_remove_mi]
@instance_id int
WITH EXECUTE AS OWNER
AS
BEGIN
   DECLARE @retval              INT


    IF (@instance_id IS NULL)
    BEGIN
        RAISERROR(14043, -1, -1, 'instance_id', 'sp_sysutility_ucp_remove_mi')
        RETURN(1)
    END

    DECLARE @instance_name SYSNAME
    SELECT @instance_name = instance_name 
    FROM msdb.dbo.sysutility_ucp_managed_instances_internal 
    WHERE instance_id = @instance_id
    
    -- Clean up managed instance health states and update dashboard stats
    -- This block comes before the delete from sysutility_ucp_managed_instances_internal
    -- so we can retrieve the instance name in case there's an error inside the block and
    -- this sp is rerun
    IF EXISTS (SELECT 1 FROM msdb.dbo.sysutility_ucp_mi_health_internal WHERE mi_name = @instance_name)
    BEGIN
        DECLARE @health_state_id INT
        SELECT @health_state_id = latest_health_state_id FROM msdb.dbo.sysutility_ucp_processing_state_internal
        
        -- Delete the managed instance record
        DELETE FROM msdb.dbo.sysutility_ucp_mi_health_internal WHERE mi_name = @instance_name

        -- Re-compute the dashboard health stats
        DELETE FROM msdb.dbo.sysutility_ucp_aggregated_mi_health_internal WHERE set_number = @health_state_id
        EXEC msdb.dbo.sp_sysutility_ucp_calculate_aggregated_mi_health @health_state_id   
    END

    DELETE [dbo].[sysutility_ucp_managed_instances_internal] 
        WHERE instance_id = @instance_id

    SELECT @retval = @@error
    RETURN(@retval)
END
GO

/**********************************************************************/
/*                                                                    */
/* Remove a managed instance from its UCP                             */
/*                                                                    */
/**********************************************************************/
IF OBJECT_ID ('dbo.sp_sysutility_mi_remove') IS NOT NULL 
BEGIN
    RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_remove]', 0, 1) WITH NOWAIT;
    DROP PROCEDURE [dbo].[sp_sysutility_mi_remove]
END;
GO

CREATE PROCEDURE [dbo].[sp_sysutility_mi_remove]
WITH EXECUTE AS OWNER
AS
BEGIN
    SET NOCOUNT ON;

    EXEC msdb.dbo.sp_sysutility_mi_disable_collection;
    
    --###FP 1

    EXEC msdb.dbo.sp_syscollector_disable_collector;
    
    --###FP 2

    DECLARE @collection_set_id int;
    DECLARE @proxy_id int;
    DECLARE @utility_collection_set_uid uniqueidentifier = N'ABA37A22-8039-48C6-8F8F-39BFE0A195DF';

    -- find our collection set and determine if its proxy is set
    SELECT 
         @collection_set_id = collection_set.collection_set_id
        ,@proxy_id = collection_set.proxy_id
    FROM msdb.dbo.syscollector_collection_sets AS collection_set
    WHERE collection_set.collection_set_uid = @utility_collection_set_uid;

    -- determine if DC is running
    -- if agent is not running, is_running won't be changed
    -- so default it to false
    DECLARE @is_running int = 0
    EXEC msdb.dbo.sp_syscollector_get_collection_set_execution_status @collection_set_id, @is_running OUTPUT;
    
    --###FP 3

    IF (@is_running = 1)
    BEGIN
      EXEC msdb.dbo.sp_syscollector_stop_collection_set @collection_set_id;
    END
    
    --###FP 4

    IF (@proxy_id IS NOT NULL )
    BEGIN
        -- retrieve the current cache directory setting
        -- if the setting can't be found, assume it is not set	
        DECLARE @cache_directory_is_set bit = 0
        SELECT @cache_directory_is_set = CASE WHEN config.parameter_value IS NULL THEN 0 ELSE 1 END
        FROM msdb.dbo.syscollector_config_store AS config
        WHERE config.parameter_name = N'CacheDirectory';
        
        IF(@cache_directory_is_set = 1)
        BEGIN
          EXEC msdb.dbo.sp_syscollector_set_cache_directory @cache_directory = NULL;
        END
        
        --###FP 5
        
        -- clear the proxy
        -- because we only enter this block if proxy is set,
        -- postpone clearing proxy until the end of the block
        -- to ensure that if clearing the cache directory fails
        -- we will re-enter this block the next time this proc is called
        EXEC msdb.dbo.sp_syscollector_update_collection_set @collection_set_id = @collection_set_id, @proxy_name = N'';
        
        --###FP 6
    END
    
    EXEC msdb.dbo.sp_syscollector_enable_collector;
    
    --###FP 7

    EXEC msdb.dbo.sp_sysutility_mi_remove_ucp_registration;
END;
GO

/**********************************************************************/
/* Provision security for utility objects                             */
/**********************************************************************/

EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_file_space_health_internal', N'UtilityCMRReader'
EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_database_health_internal', N'UtilityCMRReader'
EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_file_space_health_internal', N'UtilityCMRReader'
EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_volume_space_health_internal', N'UtilityCMRReader'

EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_file_space_health', N'UtilityCMRReader'
EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_database_health', N'UtilityCMRReader'
EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_database_file_space_health', N'UtilityCMRReader'
EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_volume_space_health', N'UtilityCMRReader'



/**********************************************************************/
/* Add an extended database property to identify database as a utility */
/* store.                                                             */
/*                                                                    */
/**********************************************************************/

DECLARE @prop_name sysname
DECLARE @new_value sql_variant
DECLARE @old_value sql_variant
DECLARE @new_value_str varchar(256);
SET @prop_name = 'Microsoft_Management_Utility_Version'
SET @new_value = '___SQLVERSION___NEW___'     -- This should be replaced at build time with the sql build number.  See DC's code for how to do this when needed.

SELECT @old_value = value
FROM fn_listextendedproperty(@prop_name, NULL, NULL, NULL, NULL, NULL, NULL)

IF (@old_value IS NOT NULL)
BEGIN
    -- At some point we might want to do version checking here, but for now, just drop the property
    RAISERROR ('Dropping extended database property - Microsoft_Management_Utility_Version', 0, 1) WITH NOWAIT;
    EXEC sp_dropextendedproperty @name = @prop_name
END            

SET @new_value_str = CONVERT (varchar(256), @new_value);
RAISERROR ('Creating extended database property - Microsoft_Management_Utility_Version (version ''%s'')', 0, 1, @new_value_str) WITH NOWAIT;
EXEC sp_addextendedproperty
        @name = @prop_name,
        @value = @new_value
GO



-----------------------------------------------------------
-- Security for Utility objects
-----------------------------------------------------------
-- Provision the Utility tables: Grant the tables, Select permissions to public
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_configuration_internal', N'UtilityIMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_session_statistics_internal', N'UtilityIMRReader'
GRANT INSERT, DELETE  ON [dbo].[sysutility_mi_session_statistics_internal] TO [UtilityIMRWriter]
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_dac_execution_statistics_internal', N'UtilityIMRReader'
GRANT INSERT, DELETE  ON [dbo].[sysutility_mi_dac_execution_statistics_internal] TO [UtilityIMRWriter]
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_volumes_stage_internal', N'UtilityIMRReader'
GRANT INSERT, DELETE  ON [dbo].[sysutility_mi_volumes_stage_internal]    TO [UtilityIMRWriter]
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_cpu_stage_internal', N'UtilityIMRReader'
GRANT INSERT, DELETE  ON [dbo].[sysutility_mi_cpu_stage_internal]    TO [UtilityIMRWriter]
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_smo_stage_internal', N'UtilityIMRReader'
GRANT INSERT, DELETE  ON [dbo].[sysutility_mi_smo_stage_internal]    TO [UtilityIMRWriter]
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_smo_properties_to_collect_internal', N'UtilityIMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_smo_objects_to_collect_internal', N'UtilityIMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_managed_instances_internal', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_configuration_internal', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_processing_state_internal', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_health_policies_internal', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_aggregated_dac_health_internal', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_aggregated_mi_health_internal', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_health_internal', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_health_internal', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_policy_check_conditions_internal', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_policy_target_conditions_internal', N'UtilityCMRReader'


EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_deployed_dacs', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_computers', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_volumes', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_utility_space_utilization', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_instances', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_databases', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_filegroups', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_datafiles', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_logfiles', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_database_file_space_utilizations', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_database_file_space_utilizations', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_volume_space_utilizations', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_volume_space_utilizations', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_cpu_utilizations', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_computer_cpu_utilizations', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_cpu_utilizations', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_file_space_utilization_history', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_cpu_utilization_history', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_get_is_instance_ucp', N'UtilityCMRReader'
GO

-- Provision the Utility views: Keep the views UtilityCMRReader
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_managed_instances', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_configuration', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_policies', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_policy_violations', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_aggregated_dac_health', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_aggregated_mi_health', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_health', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_health', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_policy_check_conditions', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_policy_target_conditions', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_policy_type', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_instance_policy_type', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_volume_powershell_path', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_instance_policies', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_policies', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_computer_policies', N'UtilityCMRReader'

EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_encode_sqlname_for_powershell', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_applicable_policy', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_aggregated_failure_count', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_global_health_policy', N'UtilityCMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_aggregated_health', N'UtilityCMRReader'
GO


-- Grant Utility SPs, execute permissions to the role
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sp_sysutility_mi_collect_dac_execution_statistics_internal', N'UtilityIMRWriter'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sp_sysutility_mi_get_dac_execution_statistics_internal', N'UtilityIMRWriter'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_mi_get_batch_manifest', N'UtilityIMRWriter'

EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_mi_get_cpu_architecture_name', N'UtilityIMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_mi_get_cpu_family_name', N'UtilityIMRReader'
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_get_culture_invariant_conversion_style_internal', N'UtilityIMRReader'
GO

-- Provision the table valued functions
EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_policy_violations', N'UtilityCMRReader'
GO


DECLARE @advanced_options_original_value INT;
DECLARE @configuration_original_value    INT;
SELECT 
   @advanced_options_original_value = advanced_options_original_value, 
   @configuration_original_value = configuration_original_value 
FROM #configuration_original_values 
WHERE configuration_option = 'Agent XPs';

-- Restore Agent XPs to previous state
EXECUTE #sp_restore_component_state 'Agent XPs', 
    @advopt_old_value = @advanced_options_original_value, 
    @comp_old_value = @configuration_original_value;
GO


/**************************************************************/
/*                     END UTILITY SCRIPTS                    */
/**************************************************************/




/**************************************************************/
/* Sign agent sps and add them to Off By Default component    */
/*                                                            */
/* Also sign SPs for other components located in MSDB         */
/**************************************************************/

-- List all of the stored procedures we need to sign with the Agent
-- signing certicate. Optionally if your SP belongs in the 'Agent XPs'
-- Off-by-default component, then specify 1 for the 'obdComponent'
-- column; If it belongs in 'DBMail XPs', specify 2.

create table #sp_table (name sysname, sign int, obdComponent int, bNewProc int)
go
insert into #sp_table values(N'sp_sqlagent_is_srvrolemember',               1, 0, 0)
insert into #sp_table values(N'sp_verify_category_identifiers',               1, 0, 0)
insert into #sp_table values(N'sp_verify_proxy_identifiers',               1, 0, 0)
insert into #sp_table values(N'sp_verify_credential_identifiers',          1, 0, 0)
insert into #sp_table values(N'sp_verify_subsystem_identifiers',           1, 0, 0)
insert into #sp_table values(N'sp_verify_login_identifiers',               1, 0, 0)
insert into #sp_table values(N'sp_verify_proxy',                  1, 0, 0)
insert into #sp_table values(N'sp_add_proxy',                     1, 0, 0)
insert into #sp_table values(N'sp_delete_proxy',                  1, 0, 0)
insert into #sp_table values(N'sp_update_proxy',                  1, 0, 0)
insert into #sp_table values(N'sp_sqlagent_is_member',                  1, 0, 0)
insert into #sp_table values(N'sp_verify_proxy_permissions',               1, 0, 0)
insert into #sp_table values(N'sp_help_proxy',                    1, 0, 0)
insert into #sp_table values(N'sp_grant_proxy_to_subsystem',               1, 0, 0)
insert into #sp_table values(N'sp_grant_login_to_proxy',             1, 0, 0)
insert into #sp_table values(N'sp_revoke_login_from_proxy',             1, 0, 0)
insert into #sp_table values(N'sp_revoke_proxy_from_subsystem',               1, 0, 0)
insert into #sp_table values(N'sp_enum_proxy_for_subsystem',               1, 0, 0)
insert into #sp_table values(N'sp_enum_login_for_proxy',             1, 0, 0)
insert into #sp_table values(N'sp_sqlagent_get_startup_info',              1, 1, 0)
insert into #sp_table values(N'sp_sqlagent_has_server_access',             1, 1, 0)
insert into #sp_table values(N'sp_sem_add_message',                  1, 0, 0)
insert into #sp_table values(N'sp_sem_drop_message',                 1, 0, 0)
insert into #sp_table values(N'sp_get_message_description',             1, 0, 0)
insert into #sp_table values(N'sp_sqlagent_get_perf_counters',             1, 0, 0)
insert into #sp_table values(N'sp_sqlagent_notify',                  1, 1, 0)
insert into #sp_table values(N'sp_is_sqlagent_starting',             1, 1, 0)
insert into #sp_table values(N'sp_verify_job_identifiers',              1, 0, 0)
insert into #sp_table values(N'sp_verify_schedule_identifiers',               1, 0, 0)
insert into #sp_table values(N'sp_verify_jobproc_caller',               1, 0, 0)
insert into #sp_table values(N'sp_downloaded_row_limiter',              1, 1, 0)
insert into #sp_table values(N'sp_post_msx_operation',                  1, 1, 0)
insert into #sp_table values(N'sp_verify_performance_condition',           1, 0, 0)
insert into #sp_table values(N'sp_verify_job_date',                  1, 0, 0)
insert into #sp_table values(N'sp_verify_job_time',                  1, 0, 0)
insert into #sp_table values(N'sp_verify_alert',                  1, 1, 0)
insert into #sp_table values(N'sp_update_alert',                  1, 0, 0)
insert into #sp_table values(N'sp_delete_job_references',               1, 0, 0)
insert into #sp_table values(N'sp_delete_all_msx_jobs',                 1, 0, 0)
insert into #sp_table values(N'sp_generate_target_server_job_assignment_sql',       1, 0, 0)
insert into #sp_table values(N'sp_generate_server_description',               1, 1, 0)
insert into #sp_table values(N'sp_msx_set_account',                  1, 1, 0)
insert into #sp_table values(N'sp_msx_get_account',                  1, 1, 0)
insert into #sp_table values(N'sp_delete_operator',                  1, 0, 0)
insert into #sp_table values(N'sp_msx_defect',                    1, 1, 0)
insert into #sp_table values(N'sp_msx_enlist',                    1, 1, 0)
insert into #sp_table values(N'sp_delete_targetserver',                 1, 0, 0)
insert into #sp_table values(N'sp_get_sqlagent_properties',             1, 1, 0)
insert into #sp_table values(N'sp_set_sqlagent_properties',             1, 1, 0)
insert into #sp_table values(N'sp_add_targetservergroup',               1, 0, 0)
insert into #sp_table values(N'sp_update_targetservergroup',               1, 0, 0)
insert into #sp_table values(N'sp_delete_targetservergroup',               1, 0, 0)
insert into #sp_table values(N'sp_help_targetservergroup',              1, 0, 0)
insert into #sp_table values(N'sp_add_targetsvrgrp_member',             1, 0, 0)
insert into #sp_table values(N'sp_delete_targetsvrgrp_member',             1, 0, 0)
insert into #sp_table values(N'sp_verify_category',                  1, 0, 0)
insert into #sp_table values(N'sp_add_category',                  1, 0, 0)
insert into #sp_table values(N'sp_update_category',                  1, 0, 0)
insert into #sp_table values(N'sp_delete_category',                  1, 0, 0)
insert into #sp_table values(N'sp_help_category',                 1, 0, 0)
insert into #sp_table values(N'sp_help_targetserver',                1, 0, 0)
insert into #sp_table values(N'sp_resync_targetserver',                 1, 0, 0)
insert into #sp_table values(N'sp_purge_jobhistory',                 1, 0, 0)
insert into #sp_table values(N'sp_help_jobhistory',                  1, 0, 0)
insert into #sp_table values(N'sp_help_jobhistory_full',                  1, 0, 0)
insert into #sp_table values(N'sp_help_jobhistory_summary',                  1, 0, 0)
insert into #sp_table values(N'sp_help_jobhistory_sem',                  1, 0, 0)
insert into #sp_table values(N'sp_add_jobserver',                 1, 0, 0)
insert into #sp_table values(N'sp_delete_jobserver',                 1, 0, 0)
insert into #sp_table values(N'sp_help_jobserver',                1, 0, 0)
insert into #sp_table values(N'sp_help_downloadlist',                1, 0, 0)
insert into #sp_table values(N'sp_enum_sqlagent_subsystems',               1, 0, 0)
insert into #sp_table values(N'sp_enum_sqlagent_subsystems_internal', 1, 0, 0)
insert into #sp_table values(N'sp_verify_subsystem',                 1, 1, 0)
insert into #sp_table values(N'sp_verify_subsystems',                 1, 0, 0)
insert into #sp_table values(N'sp_verify_schedule',                  1, 0, 0)
insert into #sp_table values(N'sp_add_schedule',                  1, 0, 0)
insert into #sp_table values(N'sp_attach_schedule',                  1, 0, 0)
insert into #sp_table values(N'sp_detach_schedule',                  1, 0, 0)
insert into #sp_table values(N'sp_update_schedule',                  1, 0, 0)
insert into #sp_table values(N'sp_delete_schedule',                  1, 0, 0)
insert into #sp_table values(N'sp_get_jobstep_db_username',             1, 0, 0)
insert into #sp_table values(N'sp_verify_jobstep',                1, 0, 0)
insert into #sp_table values(N'sp_add_jobstep_internal',             1, 0, 0)
insert into #sp_table values(N'sp_add_jobstep',                   1, 0, 0)
insert into #sp_table values(N'sp_update_jobstep',                1, 0, 0)
insert into #sp_table values(N'sp_delete_jobstep',                1, 0, 0)
insert into #sp_table values(N'sp_help_jobstep',                  1, 0, 0)
insert into #sp_table values(N'sp_write_sysjobstep_log',             1, 0, 0)
insert into #sp_table values(N'sp_help_jobsteplog',                  1, 0, 0)
insert into #sp_table values(N'sp_delete_jobsteplog',                1, 0, 0)
insert into #sp_table values(N'sp_get_schedule_description',               1, 1, 0)
insert into #sp_table values(N'sp_add_jobschedule',                  1, 0, 0)
insert into #sp_table values(N'sp_update_replication_job_parameter',          1, 0, 0)
insert into #sp_table values(N'sp_update_jobschedule',                  1, 0, 0)
insert into #sp_table values(N'sp_delete_jobschedule',                  1, 0, 0)
insert into #sp_table values(N'sp_help_schedule',                 1, 0, 0)
insert into #sp_table values(N'sp_help_jobschedule',                 1, 0, 0)
insert into #sp_table values(N'sp_verify_job',                    1, 1, 0)
insert into #sp_table values(N'sp_add_job',                    1, 0, 0)
insert into #sp_table values(N'sp_update_job',                    1, 0, 0)
insert into #sp_table values(N'sp_delete_job',                    1, 0, 0)
insert into #sp_table values(N'sp_get_composite_job_info',              1, 1, 0)
insert into #sp_table values(N'sp_help_job',                   1, 0, 0)
insert into #sp_table values(N'sp_help_jobcount ',                1, 0, 0)
insert into #sp_table values(N'sp_help_jobs_in_schedule',               1, 0, 0)
insert into #sp_table values(N'sp_manage_jobs_by_login',             1, 0, 0)
insert into #sp_table values(N'sp_apply_job_to_targets',             1, 0, 0)
insert into #sp_table values(N'sp_remove_job_from_targets',             1, 0, 0)
insert into #sp_table values(N'sp_get_job_alerts',                1, 0, 0)
insert into #sp_table values(N'sp_convert_jobid_to_char',               1, 0, 0)
insert into #sp_table values(N'sp_start_job',                     1, 0, 0)
insert into #sp_table values(N'sp_stop_job',                   1, 0, 0)
insert into #sp_table values(N'sp_cycle_agent_errorlog',             1, 0, 0)
insert into #sp_table values(N'sp_get_chunked_jobstep_params',             1, 0, 0)
insert into #sp_table values(N'sp_check_for_owned_jobs',             1, 0, 0)
insert into #sp_table values(N'sp_check_for_owned_jobsteps',               1, 0, 0)
insert into #sp_table values(N'sp_sqlagent_refresh_job',             1, 0, 0)
insert into #sp_table values(N'sp_jobhistory_row_limiter',              1, 1, 0)
insert into #sp_table values(N'sp_sqlagent_log_jobhistory',             1, 0, 0)
insert into #sp_table values(N'sp_sqlagent_check_msx_version',             1, 0, 0)
insert into #sp_table values(N'sp_sqlagent_probe_msx',                  1, 0, 0)
insert into #sp_table values(N'sp_set_local_time',                1, 1, 0)
insert into #sp_table values(N'sp_multi_server_job_summary',               1, 0, 0)
insert into #sp_table values(N'sp_target_server_summary',               1, 0, 0)
insert into #sp_table values(N'sp_uniquetaskname',                1, 0, 0)
insert into #sp_table values(N'sp_addtask',                    1, 0, 0)
insert into #sp_table values(N'sp_droptask',                   1, 0, 0)
insert into #sp_table values(N'sp_add_alert_internal',                  1, 0, 0)
insert into #sp_table values(N'sp_add_alert',                     1, 0, 0)
insert into #sp_table values(N'sp_delete_alert',                  1, 0, 0)
insert into #sp_table values(N'sp_help_alert',                    1, 0, 0)
insert into #sp_table values(N'sp_verify_operator',                  1, 0, 0)
insert into #sp_table values(N'sp_add_operator',                  1, 0, 0)
insert into #sp_table values(N'sp_update_operator',                  1, 1, 0)
insert into #sp_table values(N'sp_help_operator',                 1, 0, 0)
insert into #sp_table values(N'sp_help_operator_jobs',                  1, 0, 0)
insert into #sp_table values(N'sp_verify_operator_identifiers',               1, 0, 0)
insert into #sp_table values(N'sp_notify_operator',                  1, 0, 0)
insert into #sp_table values(N'sp_verify_notification',                 1, 0, 0)
insert into #sp_table values(N'sp_add_notification',                 1, 0, 0)
insert into #sp_table values(N'sp_update_notification',                 1, 0, 0)
insert into #sp_table values(N'sp_delete_notification',                 1, 0, 0)
insert into #sp_table values(N'sp_help_notification',                1, 0, 0)
insert into #sp_table values(N'sp_help_jobactivity',                 1, 0, 0)
insert into #sp_table values(N'sp_enlist_tsx',                    1, 1, 0)
insert into #sp_table values(N'trig_targetserver_insert',               1, 0, 0)

-- Database Mail configuration procs
insert into #sp_table values(N'sysmail_verify_accountparams_sp',           1, 0, 0)
insert into #sp_table values(N'sysmail_verify_principal_sp',               1, 0, 0)
insert into #sp_table values(N'sysmail_verify_profile_sp',              1, 0, 0)
insert into #sp_table values(N'sysmail_verify_account_sp',              1, 0, 0)
insert into #sp_table values(N'sysmail_add_profile_sp',                 1, 0, 0)
insert into #sp_table values(N'sysmail_update_profile_sp',              1, 0, 0)
insert into #sp_table values(N'sysmail_delete_profile_sp',              1, 0, 0)
insert into #sp_table values(N'sysmail_help_profile_sp',             1, 0, 0)
insert into #sp_table values(N'sysmail_create_user_credential_sp',            1, 0, 0)
insert into #sp_table values(N'sysmail_alter_user_credential_sp',          1, 0, 0)
insert into #sp_table values(N'sysmail_drop_user_credential_sp',           1, 0, 0)
insert into #sp_table values(N'sysmail_add_account_sp',                 1, 0, 0)
insert into #sp_table values(N'sysmail_update_account_sp',              1, 0, 0)
insert into #sp_table values(N'sysmail_delete_account_sp',              1, 0, 0)
insert into #sp_table values(N'sysmail_help_account_sp',             1, 0, 0)
insert into #sp_table values(N'sysmail_help_admin_account_sp',             1, 0, 0)
insert into #sp_table values(N'sysmail_add_profileaccount_sp',             1, 0, 0)
insert into #sp_table values(N'sysmail_update_profileaccount_sp',          1, 0, 0)
insert into #sp_table values(N'sysmail_delete_profileaccount_sp',          1, 0, 0)
insert into #sp_table values(N'sysmail_help_profileaccount_sp',               1, 0, 0)
insert into #sp_table values(N'sysmail_configure_sp',                1, 0, 0)
insert into #sp_table values(N'sysmail_help_configure_sp',              1, 0, 0)
insert into #sp_table values(N'sysmail_help_configure_value_sp',           1, 0, 0)
insert into #sp_table values(N'sysmail_add_principalprofile_sp',           1, 0, 0)
insert into #sp_table values(N'sysmail_update_principalprofile_sp',           1, 0, 0)
insert into #sp_table values(N'sysmail_delete_principalprofile_sp',           1, 0, 0)
insert into #sp_table values(N'sysmail_help_principalprofile_sp',          1, 0, 0)

-- Database Mail: mail host database specific procs
insert into #sp_table values(N'sysmail_start_sp',             1, 2, 0)
insert into #sp_table values(N'sysmail_stop_sp',              1, 2, 0)
insert into #sp_table values(N'sysmail_logmailevent_sp',      1, 0, 0)
insert into #sp_table values(N'sp_SendMailMessage',           1, 0, 0)
insert into #sp_table values(N'sp_isprohibited',              1, 0, 0)
insert into #sp_table values(N'sp_SendMailQueues',            1, 0, 0)
insert into #sp_table values(N'sp_ProcessResponse',           1, 0, 0)
insert into #sp_table values(N'sp_MailItemResultSets',        1, 0, 0)
insert into #sp_table values(N'sp_process_DialogTimer',       1, 0, 0)
insert into #sp_table values(N'sp_readrequest',               1, 0, 0)
insert into #sp_table values(N'sp_GetAttachmentData',         1, 0, 0)
insert into #sp_table values(N'sp_RunMailQuery',              1, 0, 0)
insert into #sp_table values(N'sysmail_help_queue_sp',        1, 0, 0)
insert into #sp_table values(N'sysmail_help_status_sp',       1, 2, 0)
insert into #sp_table values(N'sysmail_delete_mailitems_sp',  1, 0, 0)
insert into #sp_table values(N'sysmail_delete_log_sp',        1, 0, 0)
insert into #sp_table values(N'sp_validate_user',             1, 2, 0)
insert into #sp_table values(N'sp_send_dbmail',               1, 2, 0)
insert into #sp_table values(N'sp_ExternalMailQueueListener', 1, 0, 0)
insert into #sp_table values(N'sp_sysmail_activate',          1, 0, 0)
insert into #sp_table values(N'sp_get_script',                1, 0, 0)

-- Maintenance Plans
insert into #sp_table values(N'sp_maintplan_delete_log',             1, 0, 0)
insert into #sp_table values(N'sp_maintplan_delete_subplan',               1, 0, 0)
insert into #sp_table values(N'sp_maintplan_open_logentry',             1, 0, 0)
insert into #sp_table values(N'sp_maintplan_close_logentry',               1, 0, 0)
insert into #sp_table values(N'sp_maintplan_update_log',             1, 0, 0)
insert into #sp_table values(N'sp_maintplan_update_subplan',               1, 0, 0)
insert into #sp_table values(N'sp_maintplan_delete_plan',               1, 0, 0)
insert into #sp_table values(N'sp_maintplan_start',                  1, 0, 0)
insert into #sp_table values(N'sp_clear_dbmaintplan_by_db',             1, 0, 0)
insert into #sp_table values(N'sp_add_maintenance_plan',             1, 0, 0)
insert into #sp_table values(N'sp_delete_maintenance_plan',             1, 0, 0)
insert into #sp_table values(N'sp_add_maintenance_plan_db',             1, 0, 0)
insert into #sp_table values(N'sp_delete_maintenance_plan_db',             1, 0, 0)
insert into #sp_table values(N'sp_add_maintenance_plan_job',               1, 1, 0)
insert into #sp_table values(N'sp_delete_maintenance_plan_job',               1, 0, 0)
insert into #sp_table values(N'sp_help_maintenance_plan',               1, 0, 0)
insert into #sp_table values(N'sp_maintplan_update_subplan_tsx',               1, 0, 0)
insert into #sp_table values(N'sp_maintplan_subplans_by_job',               1, 0, 0)

-- Log Shipping
insert into #sp_table values(N'sp_add_log_shipping_monitor_jobs',          1, 0, 0)
insert into #sp_table values(N'sp_add_log_shipping_primary',               1, 0, 0)
insert into #sp_table values(N'sp_add_log_shipping_secondary',             1, 0, 0)
insert into #sp_table values(N'sp_delete_log_shipping_monitor_jobs',          1, 0, 0)
insert into #sp_table values(N'sp_delete_log_shipping_primary',               1, 0, 0)
insert into #sp_table values(N'sp_delete_log_shipping_secondary ',            1, 0, 0)
insert into #sp_table values(N'sp_log_shipping_in_sync',             1, 0, 0)
insert into #sp_table values(N'sp_log_shipping_get_date_from_file ',          1, 0, 0)
insert into #sp_table values(N'sp_get_log_shipping_monitor_info',          1, 0, 0)
insert into #sp_table values(N'sp_update_log_shipping_monitor_info',          1, 0, 0)
insert into #sp_table values(N'sp_delete_log_shipping_monitor_info',          1, 0, 0)
insert into #sp_table values(N'sp_remove_log_shipping_monitor_account',          1, 0, 0)
insert into #sp_table values(N'sp_log_shipping_monitor_backup',               1, 0, 0)
insert into #sp_table values(N'sp_log_shipping_monitor_restore',           1, 0, 0)
insert into #sp_table values(N'sp_change_monitor_role',                 1, 0, 0)
insert into #sp_table values(N'sp_create_log_shipping_monitor_account',          1, 0, 0)

-- DTS
insert into #sp_table values(N'sp_get_dtsversion',                1, 0, 0)
insert into #sp_table values(N'sp_make_dtspackagename',                 1, 0, 0)
insert into #sp_table values(N'sp_add_dtspackage',                1, 0, 0)
insert into #sp_table values(N'sp_drop_dtspackage',                  1, 0, 0)
insert into #sp_table values(N'sp_reassign_dtspackageowner',               1, 0, 0)
insert into #sp_table values(N'sp_get_dtspackage',                1, 0, 0)
insert into #sp_table values(N'sp_reassign_dtspackagecategory',               1, 0, 0)
insert into #sp_table values(N'sp_enum_dtspackages',                 1, 0, 0)
insert into #sp_table values(N'sp_add_dtscategory',                  1, 0, 0)
insert into #sp_table values(N'sp_drop_dtscategory',                 1, 0, 0)
insert into #sp_table values(N'sp_modify_dtscategory',                  1, 0, 0)
insert into #sp_table values(N'sp_enum_dtscategories',                  1, 0, 0)
insert into #sp_table values(N'sp_log_dtspackage_begin',             1, 0, 0)
insert into #sp_table values(N'sp_log_dtspackage_end',                  1, 0, 0)
insert into #sp_table values(N'sp_log_dtsstep_begin',                1, 0, 0)
insert into #sp_table values(N'sp_log_dtsstep_end',                  1, 0, 0)
insert into #sp_table values(N'sp_log_dtstask',                   1, 0, 0)
insert into #sp_table values(N'sp_enum_dtspackagelog',                  1, 0, 0)
insert into #sp_table values(N'sp_enum_dtssteplog',                  1, 0, 0)
insert into #sp_table values(N'sp_enum_dtstasklog',                  1, 0, 0)
insert into #sp_table values(N'sp_dump_dtslog_all',                  1, 0, 0)
insert into #sp_table values(N'sp_dump_dtspackagelog',                  1, 0, 0)
insert into #sp_table values(N'sp_dump_dtssteplog',                  1, 0, 0)
insert into #sp_table values(N'sp_dump_dtstasklog',                  1, 0, 0)
insert into #sp_table values(N'sp_dts_secure',						1, 0, 0)
insert into #sp_table values(N'sp_ssis_addlogentry',                  1, 0, 0)
insert into #sp_table values(N'sp_ssis_listpackages',                 1, 0, 0)
insert into #sp_table values(N'sp_ssis_listfolders',                  1, 0, 0)
insert into #sp_table values(N'sp_ssis_deletepackage',                1, 0, 0)
insert into #sp_table values(N'sp_ssis_deletefolder',                 1, 0, 0)
insert into #sp_table values(N'sp_ssis_getpackage',                1, 0, 0)
insert into #sp_table values(N'sp_ssis_getfolder',                 1, 0, 0)
insert into #sp_table values(N'sp_ssis_putpackage',                1, 0, 0)
insert into #sp_table values(N'sp_ssis_addfolder',                 1, 0, 0)
insert into #sp_table values(N'sp_ssis_renamefolder',                 1, 0, 0)
insert into #sp_table values(N'sp_ssis_setpackageroles',                 1, 0, 0)
insert into #sp_table values(N'sp_ssis_getpackageroles',                 1, 0, 0)
go

/**************************************************************/
/* Mark system objects                                        */
/**************************************************************/
declare  @start datetime
      ,@name  sysname
select @start = start from #InstMsdb
-- As a temporary solution, exclude the syscollector_collection_sets table from being a system table
declare newsysobjs cursor for select name from sys.objects where schema_id = 1 and create_date >= @start
open newsysobjs
fetch next from newsysobjs into @name
while @@fetch_status = 0
begin
   Exec sp_MS_marksystemobject @name
   update #sp_table set bNewProc = 1 where name = @name
   fetch next from newsysobjs into @name
end
deallocate newsysobjs
drop table #InstMsdb
go

PRINT 'Signing sps ...'
-- Create certificate to sign Agent sps
--
declare @certError int

if exists (select * from sys.certificates where name = '##MS_AgentSigningCertificate##')
begin
   PRINT 'Dropping existing Agent certificate ...'
   drop certificate [##MS_AgentSigningCertificate##]
   select @certError = @@error
   IF (@certError <> 0)
   BEGIN
       select 'Cannot drop existing certificate. Objects still signed by ##MS_AgentSigningCertificate##' = object_name(crypts.major_id) 
       from sys.crypt_properties crypts, sys.certificates certs
       where crypts.thumbprint = certs.thumbprint
       and crypts.class = 1
       and certs.name = '##MS_AgentSigningCertificate##'
   
       RAISERROR('Cannot drop existing ##MS_AgentSigningCertificate## in msdb. INSTMSDB.SQL terminating.', 20, 127) WITH LOG
   END
end

-- If the temp table #SqlAgentSignatures exists, then this script is running as part
-- of an upgrade from a previous build of sql server. In this case we want to sign
-- the Agent procedures using the same signatures that exist in a clean install
-- of this same build. Those signatures are in #SqlAgentSignatures. To do this
-- we create the certificate from the .cer file.
--
-- In the non-upgrade case, meaning that this script is being run during the build
-- of the SQL Server product itself, we simply create the certificate from scratch.

CREATE TABLE #InstmsdbAgentCertPath (AgentCert NVARCHAR(1100))
DECLARE @certificate_name NVARCHAR(1100) -- space for path + MS_AgentSigningCertificate.cer
IF (NOT OBJECT_ID(N'tempdb.dbo.#SqlAgentSignatures', 'U') IS NULL)
BEGIN

    -- We need agent XP's to be on in order to lookup the path to our instance install folder.
    DECLARE @advopt_old_value int
    DECLARE @comp_old_value int
    EXEC #sp_enable_component 'Agent XPs', @advopt_old_value out, @comp_old_value out

    DECLARE @InstallRootPath nvarchar(1024)

    -- Note: This is an _instance_ registry lookup, so it will
    -- automatically insert the right instance name after the "Microsoft
    -- SQL Server" part of the path, thus looking in a path like
    -- 'SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL10.INST1\Setup'
    EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\Microsoft SQL Server\Setup', N'SQLPath', @InstallRootPath OUTPUT

    -- Restore Agent XPs to previous state
    EXECUTE #sp_restore_component_state 'Agent XPs', @advopt_old_value, @comp_old_value

    IF @InstallRootPath IS NULL
    BEGIN
       RAISERROR('Cannot find instance-specific registry path for SOFTWARE\Microsoft\Microsoft SQL Server\Setup\SqlPath. INSTMSDB.SQL terminating.', 20, 127) WITH LOG
    END

    -- Now find our certificate in the Install folder, placed there by setup.
    SELECT @InstallRootPath = @InstallRootPath + N'\Install'

    select @certificate_name = @InstallRootPath + N'\MS_AgentSigningCertificate.cer'

    -- Remember this file path so we can import it into master later
    insert #InstmsdbAgentCertPath(AgentCert) VALUES (@certificate_name)
    PRINT 'Updated #InstmsdbAgentCertPath with value ' + @certificate_name

    -- Handle single quotes in the directory name since this string is going to be passed to EXECUTE
    DECLARE @certificate_nameQuoted NVARCHAR(2200)
    SELECT  @certificate_nameQuoted = '''' + REPLACE(@certificate_name, '''', '''''') + ''''

    PRINT 'Creating ##MS_AgentSigningCertificate## from ' + @certificate_name
    EXECUTE(N'create certificate [##MS_AgentSigningCertificate##] from file = ' + @certificate_nameQuoted)
    select @certError = @@error

    IF (@certError <> 0)
    BEGIN
       RAISERROR('Cannot create ##MS_AgentSigningCertificate## from file. INSTMSDB.SQL terminating.', 20, 127) WITH LOG
    END
END
ELSE
BEGIN
   dbcc traceon(4606,-1) -- Ignore domain policy about weak password
   create certificate [##MS_AgentSigningCertificate##] 
      encryption by password = 'Yukon90_'
      with subject = 'MS_AgentSigningCertificate'
   select @certError = @@error
   dbcc traceoff(4606,-1)

   IF (@certError <> 0)
   BEGIN
      RAISERROR('Cannot create ##MS_AgentSigningCertificate## in msdb. INSTMSDB.SQL terminating.', 20, 127) WITH LOG
   END

   --Use master database to get the path to the 'Data' folder
   -- Note: master.dbo.sysaltfiles's filename column is 260 nvarchars
   DECLARE @dataDirName NVARCHAR(520)
   SELECT @dataDirName = SUBSTRING(physical_name, 1, CHARINDEX(N'master.mdf', LOWER(physical_name)) - 1) 
       FROM master.sys.master_files
       WHERE (name = N'master')
   
   if (@dataDirName is null)
      RAISERROR('Cannot deduce path for temporary certificate ##MS_AgentSigningCertificate##. INSTMSDB.SQL terminating.', 20, 127) WITH LOG
   
   SELECT @certificate_name = @dataDirName + 'MS_AgentSigningCertificate.cer'

   -- Remember this file path so we can import it into master later
   insert #InstmsdbAgentCertPath(AgentCert) VALUES (@certificate_name)
   PRINT 'Created #InstmsdbAgentCertPath with value ' + @certificate_name
END

go


BEGIN TRANSACTION
declare @sp sysname
declare @exec_str nvarchar(1024)
declare @sign int
declare @obdComponent int
declare @bNewProc int

declare @bUseExistingSignature int
set @bUseExistingSignature = 0
IF (NOT OBJECT_ID(N'tempdb.dbo.#SqlAgentSignatures', 'U') IS NULL)
BEGIN
    set @bUseExistingSignature = 1
END

declare @err_str nvarchar(1024)
declare ms_crs_sps cursor global for select name, sign, obdComponent, bNewProc from #sp_table 
open ms_crs_sps
fetch next from ms_crs_sps into @sp, @sign, @obdComponent, @bNewProc
while @@fetch_status = 0
begin
   if exists(select * from sys.objects where name = @sp)
   begin
      print 'processing sp: ' + @sp
      if (@sign = 1)
      begin
         if (@bUseExistingSignature = 1)
         begin
            declare @signature varbinary(max)
            select @signature = signature
               from #SqlAgentSignatures
               where object_name = @sp

            if (@signature is null)
            begin
               set @err_str = 'Cannot find existing signature for stored procedure ' + @sp + '. Terminating.'
               RAISERROR(@err_str, 20, 127) WITH LOG
               ROLLBACK TRANSACTION
               return
            end

            set @exec_str = N'add signature to ' + @sp + N' by certificate [##MS_AgentSigningCertificate##] with signature = ' + CONVERT(nvarchar(max), @signature, 1)
         end
         else
         begin
            set @exec_str = N'add signature to ' + @sp + N' by certificate [##MS_AgentSigningCertificate##] with password = ''Yukon90_'''
         end
         Execute(@exec_str)
         if (@@error <> 0)
         begin
            set @err_str = 'Cannot sign stored procedure ' + @sp + '. Terminating.'
            RAISERROR(@err_str, 20, 127) WITH LOG
            ROLLBACK TRANSACTION
            return
         end
      end

      -- If there is a new procedure that goes in a component, put it there
      if (@obdComponent > 0 and @bNewProc > 0)
      begin
         if (@obdComponent = 1) -- SQLAgent
             set @exec_str = N'exec sp_AddFunctionalUnitToComponent N''Agent XPs'', N' + QUOTENAME(@sp, '''')

         else if (@obdComponent = 2) -- DbMail
             set @exec_str = N'exec sp_AddFunctionalUnitToComponent N''Database Mail XPs'', N' + QUOTENAME(@sp, '''')
 
         Execute(@exec_str)
         if (@@error <> 0)
         begin
            RAISERROR('Cannot add stored procedure to component. INSTMSDB.SQL terminating.', 20, 127) WITH LOG
            ROLLBACK TRANSACTION
            return
         end
      end

   end
   fetch next from ms_crs_sps into @sp, @sign, @obdComponent, @bNewProc
end
close ms_crs_sps
deallocate ms_crs_sps
COMMIT TRANSACTION
go
drop table #sp_table
go

PRINT 'Succesfully signed sps'

DECLARE @certificate_name NVARCHAR(1100)
select @certificate_name = AgentCert
   from #InstmsdbAgentCertPath

-- Handle single quotes in the directory name since this string is going to be passed to EXECUTE
-- in both BACKUP CERT and CREATE CERT below.
DECLARE @certificate_nameQuoted NVARCHAR(2200)
SELECT  @certificate_nameQuoted = '''' + REPLACE(@certificate_name, '''', '''''') + ''''

-- If this is not an upgrade, then we made our certificate from scratch.
-- Export it to a new file so that it can be imported into the master database.
IF (OBJECT_ID(N'tempdb.dbo.#SqlAgentSignatures', 'U') IS NULL)
BEGIN
   -- Drop certificate private key. When we export to a file just below,
   -- the file will not include the private key.
   alter certificate [##MS_AgentSigningCertificate##] remove private key

   IF (@@error <> 0)
      RAISERROR('Cannot alter ##MS_AgentSigningCertificate## in msdb. INSTMSDB.SQL terminating.', 20, 127) WITH LOG

   -- Now we export the certificate to a file so that it can be imported into the master database.
   -- Use the data directory to persist the file.
   --
   if (@certificate_name is null)
      RAISERROR('Cannot deduce path for temporary certificate ##MS_AgentSigningCertificate##.', 20, 127) WITH LOG

   declare @certError int

   PRINT 'Exporting ##MS_AgentSigningCertificate## to ' + @certificate_name
   BEGIN TRY
      EXECUTE(N'backup certificate [##MS_AgentSigningCertificate##] to file = ' + @certificate_nameQuoted)
      select @certError = @@error
   END TRY
   BEGIN CATCH
      -- Error 15240 happens when instmsdb.sql is run repeatedly and directly on a sql server, so the
      -- certificate already exists on disk and so the file cannot be written.
      -- It should not happen during the mkmastr build stage since the .cer file was already deleted.
      if (ERROR_NUMBER() != 15240)
      BEGIN
         select @certError = ERROR_NUMBER()
         PRINT 'Error ' + @certError + ' backing up certificate.'
      END
      ELSE
      BEGIN
         PRINT 'Could not export certificate, trying again with random name. If this is a mkmastr build then this is an error.'
         SELECT @certificate_name = SUBSTRING(@certificate_name, 1, CHARINDEX(N'.cer', @certificate_name) - 1) + 
                                    CONVERT(NVARCHAR(520), NEWID()) + N'.cer' 
         SELECT @certificate_nameQuoted = '''' + REPLACE(@certificate_name, '''', '''''') + ''''

         update #InstmsdbAgentCertPath set AgentCert = @certificate_name

         PRINT 'Exporting ##MS_AgentSigningCertificate## to ' + @certificate_name 
         EXECUTE(N'backup certificate [##MS_AgentSigningCertificate##] to file = ' + @certificate_nameQuoted)
         select @certError = @@error
      END
   END CATCH
   
   IF (@certError <> 0)
      RAISERROR('Cannot backup ##MS_AgentSigningCertificate##. INSTMSDB.SQL terminating.', 20, 127) WITH LOG
END

drop table #InstmsdbAgentCertPath

-- Now we want to grant the signed stored procedures access to the
-- master database. To allow the cross-database call from msdb, we
-- will recreate the Agent certificate in the master database and
-- grant execute permission to it.

-- Switch to master so the certificate is recreated there
use master

if exists (select * from sys.database_principals where name = '##MS_AgentSigningCertificate##')
   drop user [##MS_AgentSigningCertificate##]

if exists (select * from sys.server_principals where name = '##MS_AgentSigningCertificate##')
   drop login [##MS_AgentSigningCertificate##]

if exists (select * from sys.certificates where name = '##MS_AgentSigningCertificate##')
   drop certificate [##MS_AgentSigningCertificate##]

-- Recreate the certificate (minus the private key, which we dropped earlier) from the file
-- into the master database.
PRINT 'Creating ##MS_AgentSigningCertificate## in master from ' + @certificate_name
execute(N'create certificate [##MS_AgentSigningCertificate##] from file = ' + @certificate_nameQuoted)
IF (@@error <> 0)
   RAISERROR('Cannot create ##MS_AgentSigningCertificate## certificate in master. INSTMSDB.SQL terminating.', 20, 127) WITH LOG

-- create a login in the master database based on this certicate.
--
create login [##MS_AgentSigningCertificate##] from certificate [##MS_AgentSigningCertificate##]
IF (@@error <> 0)
   RAISERROR('Cannot create ##MS_AgentSigningCertificate## login. INSTMSDB.SQL terminating.', 20, 127) WITH LOG

-- create certificate-based user for execution granting
--
create user [##MS_AgentSigningCertificate##] for certificate [##MS_AgentSigningCertificate##]
IF (@@error <> 0)
   RAISERROR('Cannot create ##MS_AgentSigningCertificate## user. INSTMSDB.SQL terminating.', 20, 127) WITH LOG

-- enable certificate for OBD
--
exec sys.sp_SetOBDCertificate N'##MS_AgentSigningCertificate##',N'ON'

grant execute to [##MS_AgentSigningCertificate##]
PRINT 'Successfully granted execute permission in master to ##MS_AgentSigningCertificate##.'

use msdb
go

--
-- End of signing sps
go

if not exists (select * from [dbo].[sysssispackagefolders] where [folderid] = '00000000-0000-0000-0000-000000000000')
BEGIN
-- Insert our root folder
DECLARE  @advopt_old_value    INT 
DECLARE  @comp_old_value   INT
EXECUTE #sp_enable_component 'Agent XPs', @advopt_old_value out, @comp_old_value out
EXEC sp_ssis_addfolder NULL, '', '00000000-0000-0000-0000-000000000000'
-- Insert the 'Maintenance Plans' node at the root.
-- Note this GUID must never change - 08aa12d5-8f98-4dab-a4fc-980b150a5dc8
EXEC sp_ssis_addfolder '00000000-0000-0000-0000-000000000000', 'Maintenance Plans', '08aa12d5-8f98-4dab-a4fc-980b150a5dc8'
EXECUTE #sp_restore_component_state 'Agent XPs', @advopt_old_value, @comp_old_value
END
GO

/**************************************************************/
/* Enable Logshipping (Yukon)                                 */
/**************************************************************/
declare @retcode int
exec @retcode = sys.sp_logshippinginstallmetadata
if (@retcode = 0)
begin
raiserror('Logshipping enabled successfully', 10, 1)
end
else
begin
raiserror('Logshipping not enabled for this edition', 10, 1)
end
go

/**************************************************************/
/* Drop auxilary procedure to enable OBD component          */
/**************************************************************/
DROP PROCEDURE #sp_enable_component 
go
DROP PROCEDURE #sp_restore_component_state 
go

-- enable policy checking 
IF EXISTS (SELECT * FROM sys.server_triggers WHERE name = N'syspolicy_server_trigger')
    ENABLE TRIGGER [syspolicy_server_trigger] ON ALL SERVER 

-- Restore old state of 'allow updates'
EXECUTE master.dbo.sp_configure N'allow updates', 0
go
RECONFIGURE WITH OVERRIDE
go

PRINT ''
PRINT '----------------------------------'
PRINT 'Execution of INSTMSDB.SQL complete'
PRINT '----------------------------------'
go

CHECKPOINT
go

use msdb
go

DECLARE @CMDExec        sysname

EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
                                        N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent\SubSystems',
                                        N'CmdExec',
                                        @CMDExec OUTPUT,
                                        N'no_output'
IF @CMDExec IS NOT NULL                                        
BEGIN                                        
  PRINT ''
  PRINT 'Remove subsystem definition from registry...'
  --remove subsystem definition from registry and populate subsystem table
  DECLARE @ret INT
  EXEC @ret = master..xp_instance_regdeletekey N'HKEY_LOCAL_MACHINE',
                          N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent\SubSystems'
  IF @ret <> 0
    RAISERROR('Failed to remove subsystems definition from registry', 10, 127) 
END

PRINT ''
PRINT 'Populate syssubsystem table...'

EXEC @ret = sp_enum_sqlagent_subsystems
IF @ret <> 0
  RAISERROR('Failed to add subsystems definition to syssubsystem table. Upgrade script terminating', 20, 127) WITH LOG
go

/**********************************************************************/
/* Loading the syssubsystems related user info                        */
/**********************************************************************/

IF (OBJECT_ID('tempdb..#syssubsystems_temp', 'U') IS NOT NULL)
BEGIN
    PRINT 'Loading saved syssubsystems related user info...'
    UPDATE syssubsystems SET syssubsystems.max_worker_threads = #syssubsystems_temp.max_worker_threads
        FROM #syssubsystems_temp WHERE #syssubsystems_temp.subsystem = syssubsystems.subsystem
END

GO

/**********************************************************************/
/* DONE - Loading the syssubsystems related user info                 */
/**********************************************************************/

--restore Shiloh object permission
DECLARE @state_desc			  nvarchar(60)
DECLARE @permission_name	sysname
DECLARE @object_name		  sysname
DECLARE @grantee_name		  sysname 

DECLARE perms_set_cursor CURSOR LOCAL FOR 
	SELECT state_desc, permission_name, object_name, grantee_name from msdb.dbo.upgrade_perms

OPEN perms_set_cursor
   FETCH NEXT FROM perms_set_cursor INTO @state_desc, @permission_name, @object_name, @grantee_name
   WHILE (@@fetch_status = 0)
   BEGIN
      --PRINT @state_desc + N' ' + @permission_name + N' ON ' + @object_name + N' TO ' + @grantee_name
      BEGIN TRY
        EXEC (@state_desc + N' ' + @permission_name + N' ON ' + @object_name + N' TO ' + @grantee_name)
      END TRY
      BEGIN CATCH
      END CATCH
      FETCH NEXT FROM perms_set_cursor INTO @state_desc, @permission_name, @object_name, @grantee_name
   END
DEALLOCATE perms_set_cursor
--remove public from SQLAgent Shiloh procedures
PRINT ''
PRINT 'Revoke any permission to public role...'
BEGIN TRY
REVOKE ALL ON sp_add_alert																	FROM PUBLIC
REVOKE ALL ON sp_add_category																FROM PUBLIC
REVOKE ALL ON sp_add_job																		FROM PUBLIC
REVOKE ALL ON sp_add_jobschedule														FROM PUBLIC
REVOKE ALL ON sp_add_jobserver															FROM PUBLIC
REVOKE ALL ON sp_add_jobstep																FROM PUBLIC
REVOKE ALL ON sp_add_notification														FROM PUBLIC
REVOKE ALL ON sp_add_operator																FROM PUBLIC
REVOKE ALL ON sp_add_targetservergroup											FROM PUBLIC
REVOKE ALL ON sp_add_targetsvrgrp_member										FROM PUBLIC
REVOKE ALL ON sp_apply_job_to_targets												FROM PUBLIC
REVOKE ALL ON sp_convert_jobid_to_char											FROM PUBLIC
REVOKE ALL ON sp_delete_alert																FROM PUBLIC
REVOKE ALL ON sp_delete_all_msx_jobs												FROM PUBLIC
REVOKE ALL ON sp_delete_category														FROM PUBLIC
REVOKE ALL ON sp_delete_job																	FROM PUBLIC
REVOKE ALL ON sp_delete_job_references											FROM PUBLIC
REVOKE ALL ON sp_delete_jobschedule													FROM PUBLIC
REVOKE ALL ON sp_delete_jobserver														FROM PUBLIC
REVOKE ALL ON sp_delete_jobstep															FROM PUBLIC
REVOKE ALL ON sp_delete_notification												FROM PUBLIC
REVOKE ALL ON sp_delete_operator														FROM PUBLIC
REVOKE ALL ON sp_delete_targetserver												FROM PUBLIC
REVOKE ALL ON sp_delete_targetservergroup										FROM PUBLIC
REVOKE ALL ON sp_delete_targetsvrgrp_member									FROM PUBLIC
REVOKE ALL ON sp_downloaded_row_limiter											FROM PUBLIC
REVOKE ALL ON sp_generate_server_description								FROM PUBLIC
REVOKE ALL ON sp_generate_target_server_job_assignment_sql	FROM PUBLIC
REVOKE ALL ON sp_get_chunked_jobstep_params									FROM PUBLIC
REVOKE ALL ON sp_get_composite_job_info											FROM PUBLIC
REVOKE ALL ON sp_get_job_alerts															FROM PUBLIC
REVOKE ALL ON sp_get_jobstep_db_username										FROM PUBLIC
REVOKE ALL ON sp_get_message_description										FROM PUBLIC
REVOKE ALL ON sp_get_schedule_description										FROM PUBLIC
REVOKE ALL ON sp_get_sqlagent_properties										FROM PUBLIC
REVOKE ALL ON sp_help_alert																	FROM PUBLIC
REVOKE ALL ON sp_help_category															FROM PUBLIC
REVOKE ALL ON sp_help_downloadlist													FROM PUBLIC
REVOKE ALL ON sp_help_job																		FROM PUBLIC
REVOKE ALL ON sp_help_jobhistory														FROM PUBLIC
REVOKE ALL ON sp_help_jobschedule														FROM PUBLIC
REVOKE ALL ON sp_help_jobserver															FROM PUBLIC
REVOKE ALL ON sp_help_jobstep																FROM PUBLIC
REVOKE ALL ON sp_help_notification													FROM PUBLIC
REVOKE ALL ON sp_help_operator															FROM PUBLIC
REVOKE ALL ON sp_help_operator_jobs													FROM PUBLIC
REVOKE ALL ON sp_help_targetserver													FROM PUBLIC
REVOKE ALL ON sp_help_targetservergroup											FROM PUBLIC
REVOKE ALL ON sp_is_sqlagent_starting												FROM PUBLIC
REVOKE ALL ON sp_jobhistory_row_limiter											FROM PUBLIC
REVOKE ALL ON sp_manage_jobs_by_login												FROM PUBLIC
REVOKE ALL ON sp_msx_defect																	FROM PUBLIC
REVOKE ALL ON sp_msx_enlist																	FROM PUBLIC
REVOKE ALL ON sp_multi_server_job_summary										FROM PUBLIC
REVOKE ALL ON sp_post_msx_operation													FROM PUBLIC
REVOKE ALL ON sp_purge_jobhistory														FROM PUBLIC
REVOKE ALL ON sp_remove_job_from_targets										FROM PUBLIC
REVOKE ALL ON sp_resync_targetserver												FROM PUBLIC
REVOKE ALL ON sp_sem_add_message														FROM PUBLIC
REVOKE ALL ON sp_sem_drop_message														FROM PUBLIC
REVOKE ALL ON sp_set_local_time															FROM PUBLIC
REVOKE ALL ON sp_set_sqlagent_properties										FROM PUBLIC
REVOKE ALL ON sp_sqlagent_check_msx_version									FROM PUBLIC
REVOKE ALL ON sp_sqlagent_get_perf_counters									FROM PUBLIC
REVOKE ALL ON sp_sqlagent_get_startup_info									FROM PUBLIC
REVOKE ALL ON sp_sqlagent_has_server_access									FROM PUBLIC
REVOKE ALL ON sp_sqlagent_log_jobhistory										FROM PUBLIC
REVOKE ALL ON sp_sqlagent_notify														FROM PUBLIC
REVOKE ALL ON sp_sqlagent_probe_msx													FROM PUBLIC
REVOKE ALL ON sp_sqlagent_refresh_job												FROM PUBLIC
REVOKE ALL ON sp_start_job																	FROM PUBLIC
REVOKE ALL ON sp_stop_job																		FROM PUBLIC
REVOKE ALL ON sp_target_server_summary											FROM PUBLIC
REVOKE ALL ON sp_uniquetaskname															FROM PUBLIC
REVOKE ALL ON sp_update_alert																FROM PUBLIC
REVOKE ALL ON sp_update_category														FROM PUBLIC
REVOKE ALL ON sp_update_job																	FROM PUBLIC
REVOKE ALL ON sp_update_jobschedule													FROM PUBLIC
REVOKE ALL ON sp_update_jobstep															FROM PUBLIC
REVOKE ALL ON sp_update_notification												FROM PUBLIC
REVOKE ALL ON sp_update_operator														FROM PUBLIC
REVOKE ALL ON sp_update_targetservergroup										FROM PUBLIC
REVOKE ALL ON sp_verify_alert																FROM PUBLIC
REVOKE ALL ON sp_verify_category														FROM PUBLIC
REVOKE ALL ON sp_verify_job																	FROM PUBLIC
REVOKE ALL ON sp_verify_job_date														FROM PUBLIC
REVOKE ALL ON sp_verify_job_identifiers											FROM PUBLIC
REVOKE ALL ON sp_verify_job_time														FROM PUBLIC
REVOKE ALL ON sp_verify_jobproc_caller											FROM PUBLIC
REVOKE ALL ON sp_verify_jobstep															FROM PUBLIC
REVOKE ALL ON sp_verify_notification												FROM PUBLIC
REVOKE ALL ON sp_verify_operator														FROM PUBLIC
REVOKE ALL ON sp_verify_performance_condition								FROM PUBLIC
REVOKE ALL ON sp_verify_subsystem														FROM PUBLIC
END TRY
BEGIN CATCH
END CATCH
go
--remove public permission from Shiloh objects that have been secured in Shiloh and overwritten during instmsdb.sql
--create a temporary table with objects granted to public during execution of instmsdb.sql
CREATE TABLE #instmsdb_public_objects(object_name sysname)

INSERT INTO #instmsdb_public_objects VALUES (N'backupfile')
INSERT INTO #instmsdb_public_objects VALUES (N'backupmediafamily')
INSERT INTO #instmsdb_public_objects VALUES (N'backupmediaset')
INSERT INTO #instmsdb_public_objects VALUES (N'backupset')
INSERT INTO #instmsdb_public_objects VALUES (N'restorehistory')
INSERT INTO #instmsdb_public_objects VALUES (N'restorefile')
INSERT INTO #instmsdb_public_objects VALUES (N'restorefilegroup')
INSERT INTO #instmsdb_public_objects VALUES (N'logmarkhistory')
INSERT INTO #instmsdb_public_objects VALUES (N'suspect_pages')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_get_dtsversion')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_make_dtspackagename')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_add_dtspackage')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_drop_dtspackage')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_reassign_dtspackageowner')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_get_dtspackage')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_enum_dtspackages')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_log_dtspackage_begin')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_log_dtspackage_end')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_log_dtsstep_begin')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_log_dtsstep_end')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_log_dtstask')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_enum_dtspackagelog')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_enum_dtssteplog')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_enum_dtstasklog')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_dump_dtslog_all')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_dump_dtspackagelog')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_dump_dtssteplog')
INSERT INTO #instmsdb_public_objects VALUES (N'sp_dump_dtstasklog')
go

DECLARE @object_name		  sysname
DECLARE @tsql             nvarchar(300)
DECLARE public_remove_cursor CURSOR LOCAL FOR 
	SELECT object_name FROM #instmsdb_public_objects

OPEN public_remove_cursor
   FETCH NEXT FROM public_remove_cursor INTO @object_name
   WHILE (@@fetch_status = 0)
   BEGIN
      --PRINT @state_desc + N' ' + @permission_name + N' ON ' + @object_name + N' TO ' + @grantee_name
      BEGIN TRY
      --if this object exists in 8.0 msdb and is granted to public no op otherwise remove permission to public on it
        IF (EXISTS (SELECT * FROM msdb.dbo.upgrade_perms 
              WHERE UPPER(object_name collate SQL_Latin1_General_CP1_CS_AS ) = UPPER(@object_name collate SQL_Latin1_General_CP1_CS_AS )))
            AND (NOT EXISTS (SELECT * FROM msdb.dbo.upgrade_perms WHERE object_name = @object_name 
                 AND UPPER(grantee_name collate SQL_Latin1_General_CP1_CS_AS ) = N'PUBLIC'))
          BEGIN
            SELECT @tsql = N'REVOKE ALL ON ' + QUOTENAME(@object_name) + N' FROM PUBLIC'
            PRINT @tsql
            EXEC (@tsql)
          END
      END TRY
      BEGIN CATCH
      END CATCH
      FETCH NEXT FROM public_remove_cursor INTO @object_name
   END
DEALLOCATE public_remove_cursor
go
DROP TABLE #instmsdb_public_objects
DROP TABLE msdb.dbo.upgrade_perms
go

--------------------------------------------------------------------------------
--update proxy account
--proxy update batch
--read sysadminonly flag
DECLARE @ret              INT
DECLARE @proxy_id         INT
DECLARE @job_id           UNIQUEIDENTIFIER
DECLARE @step_id          INT
DECLARE @subsystem        sysname
DECLARE @owner_name       NVARCHAR(256)
DECLARE @full_name        sysname
DECLARE @owner_sid        VARBINARY(85)
DECLARE @is_sysadmin      INT

--walk throu all job steps excluding TSQL jobsteps
DECLARE jobsteps_cursor CURSOR LOCAL FOR
SELECT js.job_id, js.step_id, js.subsystem, SUSER_SNAME(j.owner_sid)
FROM sysjobs j JOIN sysjobsteps js ON j.job_id = js.job_id
WHERE UPPER(js.subsystem collate SQL_Latin1_General_CP1_CS_AS) <> N'TSQL'
AND SUSER_SNAME(j.owner_sid) IS NOT NULL
FOR READ ONLY

--wals thru all non sysadmin job owners
DECLARE job_nonsysadmin_owners_cursor CURSOR LOCAL FOR
SELECT DISTINCT j.owner_sid FROM sysjobs j 
FOR READ ONLY

OPEN job_nonsysadmin_owners_cursor
FETCH NEXT FROM job_nonsysadmin_owners_cursor INTO @owner_sid
WHILE (@@fetch_status = 0)
BEGIN
	SELECT @owner_name = SUSER_SNAME(@owner_sid)
  IF @owner_name IS NOT NULL
  BEGIN
    --is job owner member of sysadmin role?
    BEGIN TRY
      EXECUTE AS LOGIN=@owner_name -- impersonate 
      SELECT @is_sysadmin = ISNULL(IS_SRVROLEMEMBER('sysadmin'),0) -- check role membership 
      REVERT -- revert back
    END TRY
    BEGIN CATCH
      SET @is_sysadmin = 0
    END CATCH
          
    IF @is_sysadmin = 0
    BEGIN
	    --add job_owner to the SQLAgentUserRole msdb role in order to permit the job owner to handle his jobs
	    --has this login a user in msdb?
	    IF NOT EXISTS(SELECT * FROM sys.database_principals WHERE (sid = @owner_sid) OR (LOWER(name collate SQL_Latin1_General_CP1_CS_AS) = LOWER(@owner_name collate SQL_Latin1_General_CP1_CS_AS)))
	    BEGIN
		    PRINT ''
		    PRINT 'Granting login access''' + @owner_name + ''' to msdb database...'
		    BEGIN TRY
		      EXEC sp_grantdbaccess @loginame = @owner_name
        END TRY
        BEGIN CATCH
          RAISERROR('A problem was encountered granting access to MSDB database for login ''%s''. Make sure this login is provisioned with SQLServer and rerun sqlagent_msdb_upgrade.sql ', 10, 127) WITH LOG
        END CATCH		      
	    END

	    PRINT ''
	    PRINT 'Adding user ''' + @owner_name + ''' to SQLAgentUserRole msdb role...'
		  BEGIN TRY
	      EXEC sp_addrolemember @rolename = 'SQLAgentUserRole', @membername = @owner_name
      END TRY
      BEGIN CATCH
        RAISERROR('A problem was encountered adding user ''%s'' to SQLAgentUserRole. Make sure this is a valid user in MSDB database and rerun sqlagent_msdb_upgrade.sql ', 10, 127) WITH LOG
      END CATCH		      
	  END
	END
	FETCH NEXT FROM job_nonsysadmin_owners_cursor INTO @owner_sid
END
DEALLOCATE job_nonsysadmin_owners_cursor
  

--credential created from LSA values in the file src\upgrade_scripts\sqlagent100_upgrade.sql
IF EXISTS (SELECT credential_id FROM master.sys.credentials  WHERE  name = N'UpgradedCredential')
BEGIN
  IF (NOT EXISTS(SELECT * FROM sysproxies WHERE name = N'UpgradedProxyAccount'))
  BEGIN  
    PRINT 'Update proxy account'
    SELECT @ret = 0

    --create the proxy first
    PRINT ''
    PRINT 'Adding Proxy...'
    BEGIN TRY --prevent sp_add_proxy raising severity 16 error that will terminate upgrade scritp when proxy account has bad info
      EXEC @ret = sp_add_proxy @proxy_name      = N'UpgradedProxyAccount',
                                @credential_name = N'UpgradedCredential',
                                @proxy_id        = @proxy_id OUTPUT
    END TRY
    BEGIN CATCH
      SET @ret = 1
    END CATCH      
                              
    IF @ret <> 0
    BEGIN
      RAISERROR('A problem was encountered updating proxy account. Verify that user name and secret of credential [UpgradedCredential] are correct and rerun sqlagent_msdb_upgrade.sql ', 10, 127) WITH LOG
    END
    ELSE
    BEGIN
      OPEN jobsteps_cursor
      FETCH NEXT FROM jobsteps_cursor INTO @job_id, @step_id, @subsystem, @owner_name

      WHILE (@@fetch_status = 0)
      BEGIN
        --is job owner member of sysadmin role?
        BEGIN TRY
          EXECUTE AS LOGIN = @owner_name -- impersonate 
          SELECT @is_sysadmin = ISNULL(IS_SRVROLEMEMBER('sysadmin'),0) -- check role membership 
          REVERT -- revert back
        END TRY
        BEGIN CATCH
          SET @is_sysadmin = 0
        END CATCH
        
        IF @is_sysadmin = 0
        BEGIN        
          IF NOT EXISTS(SELECT * FROM sysproxysubsystem ps JOIN syssubsystems s 
                        ON ps.subsystem_id = s.subsystem_id
                        WHERE s.subsystem = @subsystem
                        AND ps.proxy_id = @proxy_id)
          BEGIN
            PRINT 'Grant permission to proxy ''UpgradedProxyAccount'' for subsystem ''' + @subsystem + '''...'
            BEGIN TRY
              EXEC @ret = sp_grant_proxy_to_subsystem @subsystem_name = @subsystem,
											          @proxy_id       = @proxy_id
					  END TRY
					  BEGIN CATCH
					    SET @ret = 1
					  END CATCH
            IF @ret <> 0
            BEGIN
              RAISERROR('FAILED TO GRANT PERMISSION TO PROXY (%d) FOR SUBSYSTEM ''%s''.', 10, 127, @proxy_id, @subsystem ) WITH LOG
            END
	        END

          IF NOT EXISTS(SELECT * FROM sysproxylogin WHERE proxy_id = @proxy_id AND sid = SUSER_SID(@owner_name))
          BEGIN
            PRINT 'Grant login ''' + @owner_name + '''  permission to use proxy ''UpgradedProxyAccount'' for subsystem ''' + @subsystem + '''...'
            BEGIN TRY
              EXEC @ret = sp_grant_login_to_proxy @proxy_id       = @proxy_id,
                                                  @login_name     = @owner_name
            END TRY
					  BEGIN CATCH
					    SET @ret = 1
					  END CATCH
            IF @ret <> 0
            BEGIN
              RAISERROR('FAILED TO GRANT PERMISSION TO LOGIN ''%s'' FOR PROXY (%d).', 10, 127, @owner_name, @proxy_id) WITH LOG
            END
          END

          --PRINT 'Update sysjobsteps...'
          UPDATE sysjobsteps SET proxy_id = @proxy_id WHERE job_id = @job_id and step_id = @step_id
        END --is_sysadmin = 0
        FETCH NEXT FROM jobsteps_cursor INTO @job_id, @step_id, @subsystem, @owner_name
      END --WHILE (@@fetch_status = 0)
      CLOSE jobsteps_cursor
    END
  END -- NOT EXISTS(SELECT * FROM sysproxies WHERE name = N'UpgradedProxyAccount')
END  -- EXISTS (SELECT credential_id FROM master.sys.credentials  WHERE  name = N'AgentCred80') 
DEALLOCATE jobsteps_cursor                          
go

--msx proxy upgrade
DECLARE @credential_id          INT
--credential created from 80 lSA values in the file src\upgrade_scripts\sqlagent100_upgrade.sql
IF EXISTS (SELECT credential_id FROM master.sys.credentials  WHERE  name = N'UpgradedMSXCredential')
BEGIN

    SELECT @credential_id = credential_id FROM master.sys.credentials
    WHERE  name = N'UpgradedMSXCredential'

    --set credential_id to agent registry
      EXECUTE master.dbo.xp_instance_regwrite  'HKEY_LOCAL_MACHINE',
                                      'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent',
                                      'MSXCredentialID',
                                      'REG_DWORD', 
                                      @credential_id
END      
go

-- Complete replication job security meta-data upgrades
IF OBJECT_ID('sys.sp_vupgrade_replsecurity_metadata', 'P') IS NOT NULL
BEGIN
	PRINT 'Performing replication job security meta-data upgrades...'
	EXECUTE master.sys.sp_vupgrade_replsecurity_metadata
END
ELSE
BEGIN
	-- "The replication security meta-data could not be upgraded for all database(s). Please ensure that entire server is upgraded and re-execute sp_vupgrade_replsecurity_metadata."
	RAISERROR(21450, 10, -1, 'security meta-data', 'all',  'entire server', 'master.sys.sp_vupgrade_replsecurity_metadata') WITH LOG	
END
GO


-- Log Shipping Upgrade
declare @retcode int
PRINT ''
PRINT 'Upgrading SQL Server Log Shipping...'
exec @retcode = master.sys.sp_upgrade_log_shipping
if @retcode != 0 or @@error != 0
begin
  RAISERROR('Upgrade of Log Shipping for SQL Server did not complete successfully. After upgrade, re-execute sp_upgrade_log_shipping from master database.', 10, 1) WITH LOG
end
PRINT 'Upgraded SQL Server Log Shipping successfully.'
go

-----------------------------------------------------------------------------
--Drop legacy objects
CREATE TABLE #instmsdb_legacy_objects(object_name sysname, type_name sysname, type_id nvarchar(2))
--tables
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.mswebtasks', N'TABLE', N'T' )
--procedures
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_helphistory', N'PROCEDURE', N'P' )
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_helptask', N'PROCEDURE', N'P' )
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_insmswebtask', N'PROCEDURE', N'P' )
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_purgehistory', N'PROCEDURE', N'P' )
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_sem_get_perf_counter_help', N'PROCEDURE', N'P' )
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_updatetask', N'PROCEDURE', N'P' )
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_updmswebtask', N'PROCEDURE', N'P' )
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_verify_jobschedule', N'PROCEDURE', N'P' )
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_verifytaskid', N'PROCEDURE', N'P' )
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_check_localserver_name', N'PROCEDURE', N'P' )

--views
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sysalternates', N'VIEW', N'V' )
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.systasks', N'VIEW', N'V' )
INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.systasks_view', N'VIEW', N'V' )
go

DECLARE @object_name		  sysname
DECLARE @type_name		    sysname
DECLARE @type_id 		      nvarchar(2)

DECLARE @tsql             nvarchar(300)
DECLARE legacy_remove_cursor CURSOR LOCAL FOR 
	SELECT object_name, type_name, type_id FROM #instmsdb_legacy_objects

OPEN legacy_remove_cursor
   FETCH NEXT FROM legacy_remove_cursor INTO @object_name, @type_name, @type_id
   WHILE (@@fetch_status = 0)
   BEGIN
      BEGIN TRY
        IF (OBJECT_ID(@object_name, @type_id) IS NOT NULL)
          BEGIN
            SELECT @tsql = N'DROP ' + @type_name + N' ' +  @object_name
            PRINT @tsql
            EXEC (@tsql)
          END
      END TRY
      BEGIN CATCH
      END CATCH
      FETCH NEXT FROM legacy_remove_cursor INTO @object_name, @type_name, @type_id
   END
CLOSE legacy_remove_cursor   
DEALLOCATE legacy_remove_cursor
go
DROP TABLE #instmsdb_legacy_objects
go
--other legacy objects CTP15 to RTM only
BEGIN TRY
if exists (select * from sys.database_principals where name = 'MS_AgentSigningCertificateUser')
   drop user MS_AgentSigningCertificateUser

if exists (select * from sys.server_principals where name = 'MS_AgentSigningCertificateLogin')
   drop login MS_AgentSigningCertificateLogin
   
-- remove the MaintenanceUserRole role CTP15 to RTM only
IF (EXISTS (SELECT *
            FROM msdb.dbo.sysusers
            WHERE (name = N'MaintenanceUserRole')
              AND (issqlrole = 1)))
BEGIN
  BEGIN
    EXECUTE msdb.dbo.sp_droprole @rolename = N'MaintenanceUserRole'
  END
END
END TRY
BEGIN CATCH
END CATCH
go
-------------------------------------------------------------

PRINT '------------------------------------'
PRINT 'Moving 2005 SSIS Data to 2008 tables'
PRINT '------------------------------------'

--
-- Add the old roles to the new SSIS role membership
--

PRINT 'Mapping SSIS yukon roles to katmai roles...'
GO

if exists (select * from dbo.sysusers where [name] = N'db_dtsadmin' and [issqlrole] = 1)
BEGIN
EXEC sp_addrolemember N'db_ssisadmin', N'db_dtsadmin'
END
GO

if exists (select * from dbo.sysusers where [name] = N'db_dtsltduser' and [issqlrole] = 1)
BEGIN
EXEC sp_addrolemember N'db_ssisltduser', N'db_dtsltduser'
END
GO

if exists (select * from dbo.sysusers where [name] = N'db_dtsoperator' and [issqlrole] = 1)
BEGIN
EXEC sp_addrolemember N'db_ssisoperator', N'db_dtsoperator'
END
GO

--
-- Move folders from sysdtspackagefolders90 to sysssispackagefolders
--

PRINT 'Moving package folders...'
GO

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtspackagefolders90]') AND type = N'U') 
INSERT INTO [msdb].[dbo].[sysssispackagefolders]
           ([folderid]
           ,[parentfolderid]
           ,[foldername])
    SELECT [folderid]
           ,[parentfolderid]
           ,[foldername]
    FROM [msdb].[dbo].[sysdtspackagefolders90]
    WHERE [folderid] != '00000000-0000-0000-0000-000000000000' -- Root folder
      AND [folderid] != '08aa12d5-8f98-4dab-a4fc-980b150a5dc8' -- Maintenance Plans

GO

--
-- Move packages from sysdtspackages90 to sysssispackages
--

PRINT 'Moving packages...'
GO

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtspackages90]') AND type = N'U') 
INSERT INTO [msdb].[dbo].[sysssispackages]
           ([name]
           ,[id]
           ,[description]
           ,[createdate]
           ,[folderid]
           ,[ownersid]
           ,[packagedata]
           ,[packageformat]
           ,[packagetype]
           ,[vermajor]
           ,[verminor]
           ,[verbuild]
           ,[vercomments]
           ,[verid]
           ,[isencrypted]
           ,[readrolesid]
           ,[writerolesid])
    SELECT [name]
           ,[id]
           ,[description]
           ,[createdate]
           ,[folderid]
           ,[ownersid]
           ,[packagedata]
           ,[packageformat]
           ,[packagetype]
           ,[vermajor]
           ,[verminor]
           ,[verbuild]
           ,[vercomments]
           ,[verid]
           ,[isencrypted]
           ,[readrolesid]
           ,[writerolesid]
    FROM [msdb].[dbo].[sysdtspackages90]

GO

--
-- Move log data from sysdtslog90 to sysssislog
--

PRINT 'Moving logs...'
GO

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtslog90]') AND type = N'U') 
INSERT INTO [msdb].[dbo].[sysssislog]
           ([event]
           ,[computer]
           ,[operator]
           ,[source]
           ,[sourceid]
           ,[executionid]
           ,[starttime]
           ,[endtime]
           ,[datacode]
           ,[databytes]
           ,[message])
    SELECT [event]
           ,[computer]
           ,[operator]
           ,[source]
           ,[sourceid]
           ,[executionid]
           ,[starttime]
           ,[endtime]
           ,[datacode]
           ,[databytes]
           ,[message]
    FROM [msdb].[dbo].[sysdtslog90]

GO

--
-- Drop yukon objects
--

PRINT 'Dropping yukon stored procedures...'
GO

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_setpackageroles]') AND type = N'P') 
DROP PROCEDURE [dbo].[sp_dts_setpackageroles]

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_getpackageroles]') AND type = N'P') 
DROP PROCEDURE [dbo].[sp_dts_getpackageroles]

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_deletepackage]') AND type = N'P') 
DROP PROCEDURE [dbo].[sp_dts_deletepackage] 

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_getpackage]') AND type = N'P') 
DROP PROCEDURE [dbo].[sp_dts_getpackage]

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_putpackage]') AND type = N'P') 
DROP PROCEDURE [dbo].[sp_dts_putpackage]

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_checkexists]') AND type = N'P') 
DROP PROCEDURE [dbo].[sp_dts_checkexists]

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_listpackages]') AND type = N'P') 
DROP PROCEDURE [dbo].[sp_dts_listpackages]

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_addlogentry]') AND type = N'P') 
DROP PROCEDURE [dbo].[sp_dts_addlogentry]

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_deletefolder]') AND type = N'P') 
DROP PROCEDURE [dbo].[sp_dts_deletefolder] 

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_addfolder]') AND type = N'P') 
DROP PROCEDURE [dbo].[sp_dts_addfolder]

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_renamefolder]') AND type = N'P') 
DROP PROCEDURE [dbo].[sp_dts_renamefolder]

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_getfolder]') AND type = N'P') 
DROP PROCEDURE [dbo].[sp_dts_getfolder]

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_listfolders]') AND type = N'P') 
DROP PROCEDURE [dbo].[sp_dts_listfolders]

GO

PRINT 'Dropping yukon tables...'
GO

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtslog90]') AND type = N'U') 
DROP TABLE [dbo].[sysdtslog90]

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtspackages90]') AND type = N'U') 
DROP TABLE [dbo].[sysdtspackages90]

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtspackagefolders90]') AND type = N'U')  
DROP TABLE [dbo].[sysdtspackagefolders90]

GO

--
-- Create a view for the logs table for backwards compatibility
--

PRINT 'Creating sysdtslog90 view...'
GO

IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtslog90]') and type = N'V')
BEGIN
	DROP VIEW [dbo].[sysdtslog90]
END

GO

CREATE VIEW [dbo].[sysdtslog90]
AS
	SELECT [id]
		  ,[event]
		  ,[computer]
		  ,[operator]
		  ,[source]
		  ,[sourceid]
		  ,[executionid]
		  ,[starttime]
		  ,[endtime]
		  ,[datacode]
		  ,[databytes]
		  ,[message]
	  FROM [msdb].[dbo].[sysssislog]

GO

-------------------------------------------------------------


/*********************************************************************/
/* Create auxilary procedure to enable OBD (Off By Default component */
/*********************************************************************/
IF (OBJECT_ID(N'tempdb..#sp_enable_component', 'P') IS NOT NULL)
BEGIN
    DROP PROCEDURE [tempdb]..[#sp_enable_component]
END
GO 

CREATE PROCEDURE #sp_enable_component     
   @comp_name     sysname, 
   @advopt_old_value    INT OUT, 
   @comp_old_value   INT OUT 
AS
   BEGIN
   SELECT @advopt_old_value=cast(value_in_use as int) from sys.configurations where name = 'show advanced options';
   SELECT @comp_old_value=cast(value_in_use as int) from sys.configurations where name = @comp_name; 
   EXEC sp_configure 'show advanced options',1;
   RECONFIGURE WITH OVERRIDE;
   EXEC sp_configure @comp_name, 1; 
   RECONFIGURE WITH OVERRIDE;
   END
go

IF (OBJECT_ID(N'tempdb..#sp_restore_component_state ', 'P') IS NOT NULL)
BEGIN
    DROP PROCEDURE [tempdb]..[#sp_restore_component_state ]
END
GO 

CREATE PROCEDURE #sp_restore_component_state 
   @comp_name     sysname, 
   @advopt_old_value    INT, 
   @comp_old_value   INT 
AS
   BEGIN
   EXEC sp_configure @comp_name, @comp_old_value; 
   RECONFIGURE WITH OVERRIDE;
   EXEC sp_configure 'show advanced options',@advopt_old_value;
   RECONFIGURE WITH OVERRIDE;
   END
GO


/**************************************************************/
/* DMF Post-upgrade steps                                      */
/**************************************************************/
--
-- >>> CTP5 -> CTP6 Upgrade
--
-- Restoring previously saved data and converting TargetSets to ObjectSets
IF (OBJECT_ID('dbo.dmf_upgrade', 'U') IS NOT NULL)
BEGIN
	BEGIN TRANSACTION 

	-- Restore policies
	SET IDENTITY_INSERT dbo.syspolicy_policies_internal ON
	INSERT dbo.syspolicy_policies_internal (policy_id,name,condition_id,root_condition_id,date_created,execution_mode,policy_category_id,schedule_uid,description,help_text,help_link,is_enabled,job_id,created_by,modified_by,date_modified)
		SELECT policy_id,name,condition_id,root_condition_id,date_created,execution_mode,policy_category_id,schedule_uid,description,help_text,help_link,is_enabled,job_id,created_by,modified_by,date_modified
		FROM msdb.dbo.tmp_syspolicy_policies_internal
	SET IDENTITY_INSERT dbo.syspolicy_policies_internal OFF
	
	-- Restore Health state 
	SET IDENTITY_INSERT dbo.syspolicy_system_health_state_internal ON
	INSERT dbo.syspolicy_system_health_state_internal (health_state_id,policy_id,last_run_date,target_query_expression_with_id,target_query_expression,result)
		SELECT * FROM msdb.dbo.tmp_syspolicy_system_health_state_internal
	SET IDENTITY_INSERT dbo.syspolicy_system_health_state_internal OFF
	
	-- Restore Execution history
	SET IDENTITY_INSERT dbo.syspolicy_policy_execution_history_internal ON
	INSERT dbo.syspolicy_policy_execution_history_internal (history_id,policy_id,start_date,end_date,result,is_full_run,exception_message,exception) 
		SELECT * FROM msdb.dbo.tmp_syspolicy_policy_execution_history_internal
	SET IDENTITY_INSERT dbo.syspolicy_policy_execution_history_internal OFF

	SET IDENTITY_INSERT dbo.syspolicy_policy_execution_history_details_internal ON
	INSERT dbo.syspolicy_policy_execution_history_details_internal (detail_id,history_id,target_query_expression,target_query_expression_with_id,execution_date,result,result_detail,exception_message,exception) 
		SELECT * FROM msdb.dbo.tmp_syspolicy_policy_execution_history_details_internal
	SET IDENTITY_INSERT dbo.syspolicy_policy_execution_history_details_internal OFF
	
	-- Convert TargetSets to ObjectSets
	SET IDENTITY_INSERT dbo.syspolicy_target_sets_internal ON

	DECLARE @policy_id int, @policy_name sysname, @facet nvarchar(max), @os_name sysname, @os_id int, @ts_id int

	DECLARE ts_cur CURSOR FOR
		SELECT p.policy_id, p.name, c.facet, ts.target_set_id
		FROM msdb.dbo.tmp_syspolicy_target_sets_internal ts JOIN dbo.syspolicy_policies p ON (ts.policy_id = p.policy_id)
			JOIN dbo.syspolicy_conditions c ON (p.condition_id = c.condition_id)
		ORDER BY p.policy_id, ts.target_set_id
		
	OPEN ts_cur
	FETCH ts_cur INTO @policy_id , @policy_name , @facet, @ts_id
	WHILE @@FETCH_STATUS = 0
	BEGIN
		-- make sure ObjectSet name is unique
		SET @os_name = LEFT(@policy_name,118) + '_ObjectSet'
		IF EXISTS (SELECT * FROM dbo.syspolicy_object_sets WHERE object_set_name = @os_name)
		BEGIN
			SET @os_name = LEFT(@policy_name,82) + CAST(NEWID() AS nchar(36)) + '_ObjectSet'
		END
		
		-- if we go through multi-TS ObjectSet we only need to add it once
		-- cursor sort order guarantees @os_id remains correct 
		IF 0 = (SELECT ISNULL(object_set_id, 0) FROM syspolicy_policies WHERE policy_id = @policy_id)
		BEGIN
			Exec sp_syspolicy_add_object_set @os_name, @facet, @os_id OUTPUT 
			UPDATE syspolicy_policies_internal
				SET object_set_id = @os_id
				WHERE policy_id = @policy_id
		END
		INSERT syspolicy_target_sets_internal (target_set_id,object_set_id,type_skeleton,type,enabled) 
			SELECT target_set_id,@os_id,type_skeleton,type,1 
			FROM msdb.dbo.tmp_syspolicy_target_sets_internal WHERE target_set_id = @ts_id
		FETCH ts_cur INTO @policy_id , @policy_name , @facet, @ts_id
	END

	CLOSE ts_cur
	DEALLOCATE ts_cur

	SET IDENTITY_INSERT dbo.syspolicy_target_sets_internal OFF

	SET IDENTITY_INSERT dbo.syspolicy_target_set_levels_internal ON
	INSERT syspolicy_target_set_levels_internal (target_set_level_id,target_set_id,type_skeleton,condition_id,level_name)
		SELECT target_set_level_id,target_set_id,type_skeleton,condition_id,level_name 
		FROM msdb.dbo.tmp_syspolicy_target_set_levels_internal

	SET IDENTITY_INSERT dbo.syspolicy_target_set_levels_internal OFF

	-- Add missing levels for MultipartName sets
	DECLARE @tsl_id int
	SET @ts_id = 0
	SET @tsl_id = 0

	DECLARE @mpn_facet_id int
	SELECT @mpn_facet_id = management_facet_id FROM dbo.syspolicy_management_facets 
		WHERE name = 'IMultipartNameFacet'

	DECLARE os_cur CURSOR FOR
		SELECT object_set_id
		FROM dbo.syspolicy_object_sets_internal  
		WHERE facet_id = @mpn_facet_id 

	OPEN os_cur
	FETCH os_cur INTO @os_id
	WHILE @@FETCH_STATUS = 0
	BEGIN
		IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'PROCEDURE')
		BEGIN
			Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/StoredProcedure', @type='PROCEDURE', @enabled=0, @target_set_id = @ts_id OUTPUT
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id  
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/StoredProcedure', @condition_id=NULL, @level_name='StoredProcedure', @target_set_level_id=@tsl_id  
		END
		IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'SYNONYM')
		BEGIN
			Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/Synonym', @type='SYNONYM', @enabled=0, @target_set_id = @ts_id OUTPUT
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id  
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/Synonym', @condition_id=NULL, @level_name='Synonym', @target_set_level_id=@tsl_id  
		END
		IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'TABLE')
		BEGIN
			Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/Table', @type='TABLE', @enabled=0, @target_set_id = @ts_id OUTPUT
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id  
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/Table', @condition_id=NULL, @level_name='Table', @target_set_level_id=@tsl_id  
		END
		IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'FUNCTION')
		BEGIN
			Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/UserDefinedFunction', @type='FUNCTION', @enabled=0, @target_set_id = @ts_id OUTPUT
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id  
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/UserDefinedFunction', @condition_id=NULL, @level_name='UserDefinedFunction', @target_set_level_id=@tsl_id  
		END
		IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'TYPE')
		BEGIN
			Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/UserDefinedType', @type='TYPE', @enabled=0, @target_set_id = @ts_id OUTPUT
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id  
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/UserDefinedType', @condition_id=NULL, @level_name='UserDefinedType', @target_set_level_id=@tsl_id  
		END
		IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'VIEW')
		BEGIN
			Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/View', @type='VIEW', @enabled=0, @target_set_id = @ts_id OUTPUT
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id  
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/View', @condition_id=NULL, @level_name='View', @target_set_level_id=@tsl_id  
		END
		IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'XMLSCHEMACOLLECTION')
		BEGIN
			Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/XmlSchemaCollection', @type='XMLSCHEMACOLLECTION', @enabled=0, @target_set_id = @ts_id OUTPUT
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id  
			Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/XmlSchemaCollection', @condition_id=NULL, @level_name='XmlSchemaCollection', @target_set_level_id=@tsl_id  
		END
		
		FETCH os_cur INTO @os_id
	END

	CLOSE os_cur
	DEALLOCATE os_cur

	-- Clean up upgrade marker
	DROP TABLE dmf_upgrade
	
	-- And temp tables
	DROP TABLE msdb.dbo.tmp_syspolicy_target_sets_internal
	DROP TABLE msdb.dbo.tmp_syspolicy_target_set_levels_internal
	DROP TABLE msdb.dbo.tmp_syspolicy_policies_internal 
	DROP TABLE msdb.dbo.tmp_syspolicy_system_health_state_internal 
	DROP TABLE msdb.dbo.tmp_syspolicy_policy_execution_history_internal 
	DROP TABLE msdb.dbo.tmp_syspolicy_policy_execution_history_details_internal 
	
	COMMIT TRANSACTION
	PRINT 'DMF successfully upgraded' 
END

--
-- <<< CTP5 -> CTP6 Upgrade
--

-- Make sure Agent XPs are enabled regardless of the state of the server
-- We will restove the state afterwards

DECLARE @advopt_old_value int
DECLARE @comp_old_value int
EXECUTE #sp_enable_component 'Agent XPs', @advopt_old_value out, @comp_old_value out


-- sproc that creates a job for PBM. This covers the creation of job for all upgrade scenarios.
PRINT 'Executing msdb.dbo.sp_syspolicy_create_purge_job'
EXEC msdb.dbo.sp_syspolicy_create_purge_job

-- Now restore the Agent XPs to their old state
EXECUTE #sp_restore_component_state 'Agent XPs', @advopt_old_value, @comp_old_value

-- Check if temp table that holds data collector's old status before upgrade exists
-- and enable data collector if DC was enabled before running upgrade scripts

PRINT 'Checking if Data collector was enabled before upgrade...'
IF (OBJECT_ID('tempdb..#data_collector_status', 'U') IS NOT NULL)
BEGIN
    DECLARE @collector_enabled int;

    SELECT @collector_enabled = data_collector_old_status
    FROM #data_collector_status

    IF (@collector_enabled <> 0)
    BEGIN
        PRINT 'Data collector was enabled before upgrade, Enabling Data Collector ...'
        EXEC sp_syscollector_enable_collector
    END
END

GO


-- Final upgrade step does not depend on any schema changes, and thus can be run for any upgrade

-- Make sure that the policies are using a valid evaluation mode
-- If a policy is using an evaluation mode that is not supported for a facet, then the evaluation mode is None and it is not enabled

UPDATE p
	SET p.execution_mode = 0, p.is_enabled = 0
	FROM 
		[msdb].[dbo].[syspolicy_policies_internal] p 
		INNER JOIN 
		[msdb].[dbo].[syspolicy_conditions] c 
		ON (p.condition_id = c.condition_id)
		INNER JOIN
		[msdb].[dbo].[syspolicy_management_facets] f
		ON (c.facet = f.name)
		WHERE p.execution_mode <> (p.execution_mode & f.execution_mode)

-- Upgrade from CTP5 & CTP6 to RC0 - correct typo in the management facet name
UPDATE [msdb].[dbo].[syspolicy_management_facets]
SET name = 'MessageType' 
WHERE name = 'Messagetype'



		
GO

/**************************************************************/
/* End of DMF Post-upgrade steps                               */
/**************************************************************/

IF (NOT OBJECT_ID(N'tempdb.dbo.#SqlAgentSignatures', 'U') IS NULL)
BEGIN
    DROP TABLE #SqlAgentSignatures
END

-------------------------------------------------------------------------
--
/**************************************************************/
/* Data Collector Post-upgrade steps                          */
/**************************************************************/

DECLARE @advopt_old_value int
DECLARE @comp_old_value int
EXECUTE #sp_enable_component 'Agent XPs', @advopt_old_value out, @comp_old_value out


-- Load the instmdw.sql script into the blob
EXECUTE [dbo].[sp_syscollector_upload_instmdw]

-- Now restore the Agent XPs to their old state
EXECUTE #sp_restore_component_state 'Agent XPs', @advopt_old_value, @comp_old_value

GO
/**************************************************************/
/* End of Data Collector Post-upgrade steps                   */
/**************************************************************/


DROP PROCEDURE #sp_enable_component
DROP PROCEDURE #sp_restore_component_state
GO


/**************************************************************/
/* Utility Post-upgrade steps                                 */
/**************************************************************/

-----------------------------------------------
-- SQL 2008 R2 CTP2 --> CTP3 or later
-----------------------------------------------

-- Drop the CTP2 MI job "sysutility_performance_information_collection" if it exists.  
-- This job was renamed in CTP3. 
IF EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = 'sysutility_performance_information_collection')
BEGIN
   EXEC msdb.dbo.sp_delete_job @job_name = 'sysutility_performance_information_collection';
END;
GO


/**************************************************************/
/* End of Utility Post-upgrade steps                          */
/**************************************************************/
GO



/**************************************************************/
/* DAC Post-upgrade steps                                 */
/**************************************************************/

-----------------------------------------------
-- SQL 2008 R2 CTP3 --> RC0 
-----------------------------------------------
DECLARE @dac_constraint_name SYSNAME
DECLARE @sql nvarchar(1000)

--Find the Default constraint system name on the DAC tables for created_by column. If it's suser_sname() (CTP3),
--drop this constraint and add the new default pointing to the new function 'fn_sysdac_get_currentusername'

--sysdac_instances_internal
SELECT @dac_constraint_name=dc.name
FROM sys.default_constraints dc
JOIN sys.columns c 
    ON dc.parent_object_id = c.object_id 
    AND dc.parent_column_id = c.column_id
WHERE dc.parent_object_id = object_id('[dbo].[sysdac_instances_internal]', 'U') 
  AND dc.definition ='(suser_sname())' 
  AND c.name='created_by'


IF @dac_constraint_name IS NOT NULL
BEGIN
    SELECT @sql = 'ALTER TABLE [dbo].[sysdac_instances_internal] DROP CONSTRAINT ' + QUOTENAME(@dac_constraint_name)
    EXEC (@sql)

    ALTER TABLE [dbo].[sysdac_instances_internal] 
    ADD DEFAULT dbo.fn_sysdac_get_currentusername() FOR created_by
END

--sysdac_history_internal
SELECT @dac_constraint_name=dc.name
FROM sys.default_constraints dc
JOIN sys.columns c 
    ON dc.parent_object_id = c.object_id 
    AND dc.parent_column_id = c.column_id
WHERE dc.parent_object_id = object_id('[dbo].[sysdac_history_internal]', 'U') 
  AND dc.definition ='(suser_sname())' 
  AND c.name='created_by'


IF @dac_constraint_name IS NOT NULL
BEGIN
    SELECT @sql = 'ALTER TABLE [dbo].[sysdac_history_internal] DROP CONSTRAINT ' + QUOTENAME(@dac_constraint_name)
    EXEC (@sql)

    ALTER TABLE [dbo].[sysdac_history_internal] 
    ADD DEFAULT dbo.fn_sysdac_get_currentusername() FOR created_by
END


/**************************************************************/
/* End of DAC Post-upgrade steps                          */
/**************************************************************/
GO

PRINT ''
PRINT '-----------------------------------------'
PRINT 'Execution of POSTINSTMSDB100.SQL complete'
PRINT '-----------------------------------------'
go

Print 'Upgrading Database Mail related objects...'

/*
	One of the main functions of this script is to handle migration
	of multiple Mail Host database to a single Mail Host Database in MSDB.
	Pre Beta 3 (CPT 15) versions of SQL Server 2005 allowed 
	Databae Mail object to exist in any database. This script will migrate 
        previously created profile, SentItems and log information to MSDB. 
	Finally the script will remove Database Mail objects in these other databases.

	Another goal of this script is to handle all metadata changes between different
	releases of the product starting with B-2 through subsequent CTPs
*/


USE msdb

SET NOCOUNT ON

-- remove obsolete configuration parameter
IF EXISTS(SELECT * FROM msdb.dbo.sysmail_configuration WHERE paramname = N'MaxNumberOfMailsPerDay')
  DELETE FROM msdb.dbo.sysmail_configuration WHERE paramname=N'MaxNumberOfMailsPerDay'

/*
 	Upgrade objects in MSDB if it was previously used as mailhost database
*/
BEGIN TRY
	IF EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='profile_name' and id =
			(SELECT OBJECT_ID(N'dbo.sysmail_mailitems', 'U')))
	BEGIN
		  -- convert data from profile_name column to profile_id column
		  exec sp_executesql N'
		  DECLARE @profile_name sysname
		  DECLARE @profile_id int 

		  DECLARE profile_name_cursor CURSOR LOCAL 
		  FOR
			 SELECT sp.profile_id, sp.name
			 FROM dbo.sysmail_profile sp, dbo.sysmail_mailitems mi
			 WHERE sp.name = mi.profile_name AND mi.profile_name IS NOT NULL

		  OPEN profile_name_cursor
		  FETCH NEXT FROM profile_name_cursor INTO @profile_id, @profile_name
		  WHILE (@@fetch_status = 0)
		  BEGIN
			 UPDATE dbo.sysmail_mailitems SET profile_id = @profile_id WHERE profile_name = @profile_name
			 FETCH NEXT FROM profile_name_cursor INTO @profile_id, @profile_name
		  END
		  Close profile_name_cursor
		  DEALLOCATE profile_name_cursor'

		  -- remove obsolete column
		  ALTER TABLE dbo.sysmail_mailitems DROP COLUMN profile_name
	END
END TRY
BEGIN CATCH
	print 'There was a problem upgrading MSDB mail host database.'
	print 'Unable to map existing profile name values to profile_id column'
	print 'Error State: ' + convert(varchar(10),ERROR_STATE() )
	print 'Error Message: ' + ERROR_MESSAGE()
	print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) 
	print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY())
END CATCH
GO

if EXISTS(Select * from msdb.dbo.sysmail_principalprofile)
BEGIN
	BEGIN TRY
		-- add existing mail users defined in MSDB to new role
		DECLARE @DBMailUser     sysname
		DECLARE principal_profile_cursor CURSOR LOCAL FOR
				SELECT dp.name FROM dbo.sysmail_principalprofile pp INNER JOIN sys.database_principals dp ON pp.principal_sid = dp.sid and dp.principal_id > 4

		OPEN principal_profile_cursor
		FETCH NEXT FROM principal_profile_cursor INTO @DBMailUser
		WHILE (@@fetch_status = 0)
		BEGIN
			EXEC msdb..sp_addrolemember @rolename = 'DatabaseMailUserRole',@membername = @DBMailUser
			FETCH NEXT FROM principal_profile_cursor INTO @DBMailUser
		END
		CLOSE principal_profile_cursor
		DEALLOCATE principal_profile_cursor
	END TRY
	BEGIN CATCH
		print 'There was a problem upgrading MSDB mail host database.'
		print 'Unable to add existing mail user to DatabaseMailUserRole'
		print 'Error State: ' + convert(varchar(10),ERROR_STATE() )
		print 'Error Message: ' + ERROR_MESSAGE()
		print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) 
		print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY())
	END CATCH
END
GO

if EXISTS(Select * from msdb.dbo.sysmail_principalprofile)
BEGIN
	BEGIN TRY
		-- cleaning up database mail related broker conversations
		if( SELECT is_broker_enabled from msdb.sys.databases WHERE name = 'msdb' ) = 0
		BEGIN
			PRINT 'NEED TO RE-ENABLE SSB'
			WHILE(1=1)
			BEGIN
				DECLARE @handle UNIQUEIDENTIFIER
				SET @handle = NULL
				SELECT TOP(1) @handle=conversation_handle FROM msdb.sys.conversation_endpoints
				WHERE (msdb.sys.conversation_endpoints.far_service IN 
							('SQL/Notifications/SysMailNotification/v1.0',
							'ExternalMailService',
							'InternalMailService',
							'SqlQueryNotificationService',
							'iMailRequestorService',
							'iMailResponderService',
							'http://schemas.microsoft.com/SQL/Notifications/EventNotificationService',
							'http://schemas.microsoft.com/SQL/Notifications/PostEventNotification'
							)
						)

				IF @handle IS NULL BREAK
				PRINT 'ENDING CONVERSATION ' + convert(varchar(256),@handle)
				END CONVERSATION @handle WITH CLEANUP
			END
		END

		-- We cannot turn the broker on, because we don't know whether it was disabled by the user
		-- ALTER DATABASE msdb SET ENABLE_BROKER
	END TRY
	BEGIN CATCH
		print 'There was a problem upgrading Mail Host databases.'
		print 'Unable to re-enable server broker. You might need to manually'
		print 'end any pending secure conversations.'
		print 'Error State: ' + convert(varchar(10),ERROR_STATE() )
		print 'Error Message: ' + ERROR_MESSAGE()
		print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) 
		print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY())
	END CATCH
END
GO

if EXISTS(SELECT * FROM msdb.dbo.sysmail_principalprofile) AND
   EXISTS(SELECT * FROM msdb.dbo.syscolumns WHERE name='database_id' and id =(SELECT OBJECT_ID(N'dbo.sysmail_principalprofile', 'U')))
BEGIN
	/*
 		Handle migration of multiple mail host databases into MSDB
	*/
	BEGIN TRY
		DECLARE @DBName		sysname
		DECLARE @DBNameQuote	sysname
		DECLARE @sql 		nvarchar(max)

		DECLARE @new_mailitem_id 	int
		DECLARE @old_mailitem_id 	int
		DECLARE @profile_name_exists 	bit
		DECLARE @Error			bit
		DECLARE @db_id 			int

		DECLARE  DBName_Cursor CURSOR FOR
			select name from sys.databases WHERE name NOT IN ('msdb', 'tempdb', 'master')
				and HAS_DBACCESS([name]) <> 0

		OPEN DBName_Cursor 

		FETCH NEXT FROM DBName_Cursor INTO @DBName

		WHILE @@FETCH_STATUS = 0
		BEGIN
			Print 'Checking database: ' + @DBName
			SET @DBNameQuote = QUOTENAME(@DBName)
			SELECT @db_id = db_id(@DBName)
			
			IF ( OBJECT_ID(@DBNameQuote + '.dbo.sysmail_log') IS NOT NULL) --Determine if Database Mail objects exist
			BEGIN
				Print @DBName + ' is a MailHost database.'
				
				--Going to migrate profiles from database to MSDB
						DECLARE @DBMailUser				sysname
				DECLARE @sid_MSDB				varbinary(85)
				DECLARE @sid_principal				varbinary(85)
				DECLARE @old_profile_id				int
				DECLARE @new_principal_id			int
				DECLARE @old_principal_id			int
				DECLARE @LoginName					sysname
				
				SET @sql = N'DECLARE  DBMail_Cursor CURSOR FOR
					SELECT p.name
						, pp.profile_id
						, msdb_p.sid
						, p.sid
						, pp.principal_id
					FROM msdb..sysmail_principalprofile pp 
						JOIN '+ @DBNameQuote +'.sys.database_principals p 
							ON pp.principal_id = p.principal_id
						LEFT JOIN msdb.sys.database_principals msdb_p 
							ON p.sid = msdb_p.sid
					where pp.database_id = ' + convert(nvarchar(10),@db_id)
				--print @sql
				EXEC(@sql)

				OPEN DBMail_Cursor

				FETCH NEXT FROM DBMail_Cursor INTO @DBMailUser, @old_profile_id, @sid_MSDB, @sid_principal, @old_principal_id

				WHILE @@FETCH_STATUS = 0
				BEGIN	
					
					Print 'Going to process user: ' + @DBMailUser
				
					IF (@sid_MSDB IS NULL) -- does not already exist in MSDB
					BEGIN
						IF (NOT EXISTS(Select * from sysusers where name = @DBMailUser))
						BEGIN
							IF (EXISTS(Select * from master.dbo.syslogins where sid = @sid_principal))
							BEGIN

								/* Determine the Login Name from the SID	*/
								SELECT @LoginName = name 
								FROM	master.dbo.syslogins
								WHERE	sid = @sid_principal
								
								PRINT 'Add USER to MSDB: ' + @DBMailUser

								SET @sql = N'CREATE USER ' + QUOTENAME(@DBMailUser) + ' FOR LOGIN ' + QUOTENAME(@LoginName)
								EXEC (@sql)
			
								IF (@old_principal_id > 4) -- do not add special accounts such as dbo, public, sys
								BEGIN
									Print 'Grant USER permission to send mail.'
									exec msdb..sp_addrolemember @rolename = 'DatabaseMailUserRole',@membername = @DBMailUser
								END
							END
							ELSE
							BEGIN
								PRINT 'Can not add user as the login does not exist.'
							END
							
						END
						ELSE
						BEGIN
							Print 'User has already been added to MSDB: ' + @DBMailUser
						
							IF (@old_principal_id > 4) -- do not add special accounts such as dbo, public, sys
							BEGIN
								PRINT 'Ensure user has the right to send mail.'
							
								exec msdb..sp_addrolemember @rolename = 'DatabaseMailUserRole',@membername = @DBMailUser

							END

						END


					END
					ELSE
					BEGIN
						Print 'Login already mapped to a user in MSDB'
					END

					IF (EXISTS(Select * from master.dbo.syslogins where sid = @sid_principal))
					BEGIN
						--Get principle_id
						SELECT @new_principal_id = principal_id 
						FROM msdb.sys.database_principals
						Where sid = @sid_principal

						IF (@new_principal_id is not Null)
						BEGIN
							print 'New Principal_id: ' + Convert(varchar(10),@new_principal_id) + '  Old profile_id: ' + convert(varchar(10),@old_profile_id) + '  Old principal id: ' + convert(varchar(10),@old_principal_id)

							SET @sql = N'
							IF NOT EXISTS(select * from msdb..sysmail_principalprofile
											Where profile_id = ' + Convert(varchar(10),@old_profile_id) + '
												AND database_id = 4
												AND principal_id = ' + Convert(varchar(10),@new_principal_id) + ')
							BEGIN
								--Update the Profile
								UPDATE msdb..sysmail_principalprofile
								SET database_id = 4
									, principal_id = ' + Convert(varchar(10),@new_principal_id) + '
								Where profile_id = ' + Convert(varchar(10),@old_profile_id) + '
									AND database_id = ' + Convert(varchar(10),@db_id) + '
									AND principal_id = ' + Convert(varchar(10),@old_principal_id) + '

								IF (@@rowcount = 1)
								BEGIN
									Print ''sysmail_principalprofile updated based on moving user to MSDB.''
								END
							END
							ELSE
							BEGIN
								Print ''This user already has already been configured with this profile in MSDB.''
							END'
							EXEC(@sql)
						END
						ELSE
						BEGIN
							Print 'sysmail_principalprofile table can not be updated for sid: ' + convert(varchar(100),@sid_principal)
						END
					END

					FETCH NEXT FROM DBMail_Cursor INTO @DBMailUser, @old_profile_id, @sid_MSDB, @sid_principal, @old_principal_id
					
				END

				CLOSE DBMail_Cursor
				DEALLOCATE DBMail_Cursor
						
				--/*	Move Data from user Mail Host database to MSDB */
				--Print 'Move Data from user Mail Host database to MSDB.'

				SET @sql = N'DECLARE  mailitem_id_Cursor CURSOR FOR
					SELECT mailitem_id FROM ' + @DBNameQuote + '.dbo.sysmail_mailitems'
				
				EXEC(@sql)
				
				OPEN mailitem_id_Cursor
				
				Set @Error = 0	--Assume no errors

				BEGIN TRANSACTION

				/* Disable Trigger so the "last_mod_date" and "last_mod_user colums" are not updated during transfer. */
				EXEC ('DISABLE TRIGGER [dbo].[trig_sysmail_attachments] ON [dbo].[sysmail_attachments]')
				EXEC ('DISABLE TRIGGER [dbo].[trig_sysmail_log] ON [dbo].[sysmail_log]')
				EXEC ('DISABLE TRIGGER [dbo].[trig_sysmail_mailitems]ON [dbo].[sysmail_mailitems]')

				Print 'Going to move ALL sysmail_log items not associated with a mailitem'

				SET @sql = N'INSERT INTO msdb.dbo.sysmail_log
					(event_type, log_date, description, process_id, mailitem_id, account_id, last_mod_date, last_mod_user)
					SELECT event_type, log_date, description, process_id, NULL, account_id, last_mod_date, last_mod_user
					FROM '+	@DBNameQuote +'.dbo.sysmail_log
					WHERE mailitem_id IS NULL '
				
				exec(@sql)

				FETCH NEXT FROM mailitem_id_Cursor INTO @old_mailitem_id
				WHILE @@FETCH_STATUS = 0
				BEGIN
					/****************************************/
					/*	MOVE dbo.sysmail_mailitems DATA	*/
					/****************************************/

					/* Need to check the schema defination of the table	*/
					SET @sql = N'USE ' + @DBNameQuote + '
							DECLARE @sql nvarchar(max) 
							IF (EXISTS(select * from syscolumns
							where id = object_id(''[dbo].[sysmail_mailitems]'')
								AND name = ''profile_name''))
							BEGIN						
							SET @sql = N''INSERT INTO msdb.dbo.sysmail_mailitems
							(profile_id, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_date, send_request_user, sent_account_id, sent_status, sent_date, last_mod_date, last_mod_user)
							SELECT CASE WHEN p.profile_id IS NULL THEN -1 ELSE p.profile_id END, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_date, send_request_user, sent_account_id, sent_status, sent_date, last_mod_date, mi.last_mod_user
							FROM '+	@DBNameQuote +'.dbo.sysmail_mailitems mi LEFT JOIN msdb..sysmail_profile p
								ON mi.profile_name = p.name
							WHERE mailitem_id = ' + CONVERT(VARCHAR(20),@old_mailitem_id) + ''' 
							END
							ELSE
							BEGIN
							SET @sql = N''INSERT INTO msdb.dbo.sysmail_mailitems
							(profile_id, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_date, send_request_user, sent_account_id, sent_status, sent_date, last_mod_date, last_mod_user)
							SELECT profile_id, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_date, send_request_user, sent_account_id, sent_status, sent_date, last_mod_date, last_mod_user
							FROM '+	@DBNameQuote +'.dbo.sysmail_mailitems 
							WHERE mailitem_id = ' + CONVERT(VARCHAR(20),@old_mailitem_id) + ''' 
							END

							EXEC(@sql)'

					
					EXEC(@sql)

					IF (@@error <> 0)	--Check for error
					BEGIN
						Set @Error = 1
					END
				
					SELECT @new_mailitem_id = @@identity

					/****************************************/
					/*	MOVE dbo.sysmail_log DATA	*/
					/****************************************/
					SET @sql = N'INSERT INTO msdb.dbo.sysmail_log
							(event_type, log_date, description, process_id, mailitem_id, account_id, last_mod_date, last_mod_user)
							SELECT event_type, log_date, description, process_id, ' + convert(varchar(5),@new_mailitem_id) + ', account_id, last_mod_date, last_mod_user
							FROM '+	@DBNameQuote +'.dbo.sysmail_log
							WHERE mailitem_id = ' + CONVERT(VARCHAR(20),@old_mailitem_id)
				
					EXEC(@sql)

					IF @@error <> 0 	--Check for error
					BEGIN
						Set @Error = 1
					END

					/****************************************/
					/*	MOVE dbo.sysmail_attachments DATA	*/ 
					/****************************************/
				SET @sql = N'USE ' + @DBNameQuote + ' 
								IF (object_id(''dbo.sysmail_attachments'') IS NOT NULL)
								begin
									INSERT INTO msdb.dbo.sysmail_attachments 
									(mailitem_id, filename, filesize, attachment, last_mod_date, last_mod_user)
									SELECT ' + convert(varchar(5),@new_mailitem_id) + ', filename, filesize, attachment, last_mod_date, last_mod_user
									FROM '+	@DBNameQuote +'.dbo.sysmail_attachments 
									WHERE mailitem_id = ' + CONVERT(VARCHAR(20),@old_mailitem_id)  + '
								end'
					EXEC(@sql)

					IF @@error <> 0 	--Check for error
					BEGIN
						Set @Error = 1
					END


					FETCH NEXT FROM mailitem_id_Cursor INTO @old_mailitem_id
				
				END
			
				IF @Error = 0
				BEGIN
					Print 'Completed data transfer to MSDB.'
					COMMIT TRANSACTION
				END
				ELSE
				BEGIN
					Print 'Not able to complete data transfer to MSDB.'
					ROLLBACK TRANSACTION

				END
			
				/* ENABLE Triggers as they were previously DISABLE	*/
				EXEC ('ENABLE TRIGGER [dbo].[trig_sysmail_attachments] ON [dbo].[sysmail_attachments]')
				EXEC ('ENABLE TRIGGER [dbo].[trig_sysmail_log] ON [dbo].[sysmail_log]')
				EXEC ('ENABLE TRIGGER [dbo].[trig_sysmail_mailitems]ON [dbo].[sysmail_mailitems]')

				CLOSE mailitem_id_Cursor
				DEALLOCATE mailitem_id_Cursor
				
				IF @Error = 0
				BEGIN
					/**********************************************************************/
					/*                                                                    */
					/* Uninstalls the tables, triggers and stored procedures necessary for*/
					/* sqlimail operations                                                */
					/*                                                                    */
					/*
					** Copyright Microsoft, Inc. 2004
					** All Rights Reserved.
					*/
					/**********************************************************************/
					
					
					
					/**************************************************************/
					--		Drop ALL Database Mail objects (i.e Functions/Procedures )
					/**************************************************************/ 
					
					PRINT ''
					PRINT 'Dropping old Database Mail FUNCTIONS and PROCEDURES ...'
					PRINT ''
					
					-----
					PRINT 'Dropping function ConvertToInt'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.ConvertToInt'', ''FN'') IS NULL
								DROP FUNCTION ConvertToInt')
					
					-----
					PRINT 'Dropping procedure sysmail_start_sp'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_start_sp'', ''P'') IS NULL
						DROP PROCEDURE dbo.sysmail_start_sp')
					
					-----
					PRINT 'Dropping procedure sysmail_stop_sp'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_stop_sp'', ''P'') IS NULL
						DROP PROCEDURE dbo.sysmail_stop_sp')
					
					-----
					PRINT 'Dropping procedure sysmail_logmailevent_sp'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_logmailevent_sp'', ''P'') IS NULL
						DROP PROCEDURE dbo.sysmail_logmailevent_sp')
					
					-----
					PRINT 'Dropping procedure sp_has_changedbuser_permission'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_has_changedbuser_permission'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_has_changedbuser_permission')
					
					-----
					PRINT 'Dropping procedure sp_getprofilerequestxml'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_getprofilerequestxml'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_getprofilerequestxml')
					
					-----
					PRINT 'Dropping procedure sp_sendandreturn'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendandreturn'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_sendandreturn')
					
					-----
					PRINT 'Dropping procedure sp_sendandblock'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendandblock'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_sendandblock')
					
					-----
					PRINT 'Dropping procedure sp_isprohibited'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_isprohibited'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_isprohibited')
					
					-----
					PRINT 'Dropping procedure sp_sendimailqueues'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendimailqueues'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_sendimailqueues')
					
					-----
					PRINT 'Dropping procedure sp_gettestprofilexml'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_gettestprofilexml'', ''P'') IS NULL
					DROP PROCEDURE dbo.sp_gettestprofilexml')
					
					-----
					PRINT 'Dropping procedure sp_testprofileimailqueues'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_testprofileimailqueues'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_testprofileimailqueues')
					
					-----
					PRINT 'Dropping procedure sp_endconversation'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_endconversation'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_endconversation')
					
					-----
					PRINT 'Dropping procedure sp_endconversation'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_endconversation'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_endconversation')
					
					-----
					PRINT 'Dropping procedure sp_readrequest'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_readrequest'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_readrequest')
					
					-----
					PRINT 'Dropping procedure sp_sendresponse'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendresponse'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_sendresponse')
					
					-----
					PRINT 'Dropping procedure sp_sendtestprofileresponse'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendtestprofileresponse'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_sendtestprofileresponse')
					
					-----
					PRINT 'Dropping procedure sp_getsendmailxml'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_getsendmailxml'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_getsendmailxml')
					
					-----
					PRINT 'Dropping procedure sendimail_sp'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sendimail_sp'', ''P'') IS NULL
						DROP PROCEDURE dbo.sendimail_sp')
					
					-----
					PRINT 'Dropping procedure sp_testimailprofile'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_testimailprofile'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_testimailprofile')
						
					-----
					PRINT 'Dropping procedure dbo.sp_add_quota_information'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_add_quota_information'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_add_quota_information')
			
					-----
					PRINT 'Dropping procedure dbo.sp_current_principal_mails'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_current_principal_mails'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_current_principal_mails')
			
					-----
					PRINT 'Dropping procedure dbo.sp_delete_quota_information'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_delete_quota_information'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_delete_quota_information')

			
					-----
					PRINT 'Dropping procedure  dbo.sp_ExernalMailQueueListener'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_ExernalMailQueueListener'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_ExernalMailQueueListener')

			
					-----
					PRINT 'Dropping procedure dbo.sp_GetAttachmentData'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_GetAttachmentData'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_GetAttachmentData')

			
					-----
					PRINT 'Dropping procedure dbo.sp_RunMailQuery'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_RunMailQuery'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_RunMailQuery')

			
					-----
					PRINT 'Dropping procedure dbo.sp_SendMailMessage'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_SendMailMessage'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_SendMailMessage')

			
					-----
					PRINT 'Dropping procedure dbo.sp_SendMailQueues'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_SendMailQueues'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_SendMailQueues')
			
					-----
					PRINT 'Dropping procedure dbo.sp_verify_quota_mail_count'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_verify_quota_mail_count'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_verify_quota_mail_count')

					-----
					PRINT 'Dropping procedure dbo.sp_activate_sqlimail'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_activate_sqlimail'', ''P'') IS NULL
						DROP PROCEDURE dbo.sp_activate_sqlimail')



			
					
					/**************************************************************/
					--    Drop all Database Mail TABLES 
					/**************************************************************/ 
					
					PRINT ''
					PRINT 'Dropping TABLES...'
					PRINT ''
					
					-----
					PRINT 'Dropping table sysmail_log'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_log'', ''U'') IS NULL
						DROP TABLE sysmail_log')
					
					----- 
					PRINT 'Dropping table sysmail_send_retries'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_send_retries'', ''U'') IS NULL
						DROP TABLE dbo.sysmail_send_retries')
					
					-----
					PRINT 'Dropping table sqlimail_data_transfer'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sqlimail_data_transfer'', ''U'') IS NULL
						DROP TABLE sqlimail_data_transfer')
					
					-----
					PRINT 'Dropping table sysmail_attachments'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_attachments'', ''U'') IS NULL
						DROP TABLE sysmail_attachments')
			
					-----
					PRINT 'Dropping table sysmail_query_transfer'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_query_transfer'', ''U'') IS NULL
						DROP TABLE sysmail_query_transfer')
			
					-----
					PRINT 'Dropping table sysmail_attachments_transfer'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_attachments_transfer'', ''U'') IS NULL
						DROP TABLE sysmail_attachments_transfer')
			
					-----
					PRINT 'Dropping table sysmail_quota_information'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_quota_information'', ''U'') IS NULL
						DROP TABLE sysmail_quota_information')
			
					-----
					PRINT 'Dropping table sysmail_mailitems'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_mailitems'', ''U'') IS NULL
						DROP TABLE sysmail_mailitems')
			
					
					/**************************************************************/
					--		Drop MESSAGES, CONTRACTS, QUEUES AND SERVICES 
					/**************************************************************/
					
					PRINT ''
					PRINT 'Dropping MESSAGES, CONTRACTS, QUEUES AND SERVICES...'
					PRINT ''
					
					-----
					PRINT 'Dropping service iMailRequestorService'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''iMailRequestorService'')
						DROP SERVICE iMailRequestorService')
					
					-----
					PRINT 'Dropping service iMailResponderService'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''iMailResponderService'')
						DROP SERVICE iMailResponderService')
					
					-----
					PRINT 'Dropping queue iMailRequestor'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.objects WHERE name = ''iMailRequestor'' AND type = ''SQ'')
						DROP QUEUE iMailRequestor')
					
					-----
					PRINT 'Dropping queue iMailResponder'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.objects WHERE name = ''iMailResponder'' AND type = ''SQ'')
						DROP QUEUE iMailResponder')
					
					-----
					PRINT 'Dropping service [SQL/Notifications/IMailNotification/v1.0]'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''SQL/Notifications/IMailNotification/v1.0'')
						DROP SERVICE [SQL/Notifications/IMailNotification/v1.0]')
					   
					-----
					PRINT 'Dropping service [ExternalMailService]'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''ExternalMailService'')
						DROP SERVICE [ExternalMailService]')

					-----
					PRINT 'Dropping service [InternalMailService]'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''InternalMailService'')
						DROP SERVICE [InternalMailService]')

					-----
					PRINT 'Dropping queue iMailNotificationQueue'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.objects WHERE name = ''iMailNotificationQueue'' AND type = ''SQ'')
						DROP QUEUE iMailNotificationQueue')
					   
					-----
					PRINT 'Dropping contract [//www.microsoft.com/imail/contracts/SendMail/v1.0]'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_contracts 
							WHERE name = ''//www.microsoft.com/imail/contracts/SendMail/v1.0'')          
						DROP CONTRACT [//www.microsoft.com/imail/contracts/SendMail/v1.0]')
					
					-----
					PRINT 'Dropping message type [{//www.microsoft.com/imail/messages}SendMail]'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_message_types 
							WHERE name = ''{//www.microsoft.com/imail/messages}SendMail'')        
					DROP MESSAGE TYPE [{//www.microsoft.com/imail/messages}SendMail]')
					  
					-----
					PRINT 'Dropping contract [//www.microsoft.com/imail/contracts/TestProfile/v1.0]'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_contracts 
							WHERE name = ''//www.microsoft.com/imail/contracts/TestProfile/v1.0'')         
						DROP CONTRACT [//www.microsoft.com/imail/contracts/TestProfile/v1.0]')
					
					-----
					PRINT 'Dropping message type [{//www.microsoft.com/imail/messages}TestProfile]'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_message_types 
							WHERE name = ''{//www.microsoft.com/imail/messages}TestProfile'')        
						DROP MESSAGE TYPE [{//www.microsoft.com/imail/messages}TestProfile]')
					
					-----
					PRINT 'Dropping message type [{//www.microsoft.com/imail/messages}TestProfileResponse]'
					-----
					EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_message_types 
							WHERE name = ''{//www.microsoft.com/imail/messages}TestProfileResponse'')
						DROP MESSAGE TYPE [{//www.microsoft.com/imail/messages}TestProfileResponse]')
				
					-----
					PRINT 'Dropping certificates and related users'
					-----
					DECLARE @sqlcmd     nvarchar(max), 
						@CertName       sysname,
						@CertNameQuoted sysname,
						@MailLogin      sysname,
						@MailLoginQuoted sysname    
					
					SELECT @CertName       = N'SQLiMail-Certificate-' + @DBName,
						@CertNameQuoted = QUOTENAME( @CertName,''''),
						@MailLogin      = N'SQLiMail-' + @DBName + '-Certificate-Login',
						@MailLoginQuoted= QUOTENAME( @MailLogin,'''')
					
					-----
					PRINT 'Dropping user in msdb'
					-----
					SET @sqlcmd = 
					N'USE msdb
					IF(EXISTS (SELECT * FROM sys.database_principals WHERE name = N' + @MailLoginQuoted + '))
						DROP USER ' + QUOTENAME( @MailLogin)
					EXEC sp_executesql @sqlcmd
					
					-----
					PRINT 'Dropping user and certificate login in master'
					-----
					SET @sqlcmd = 
					N'USE master 
						IF(EXISTS (SELECT * FROM sys.database_principals WHERE name = N' + @MailLoginQuoted + '))
						DROP USER ' + QUOTENAME( @MailLogin) + '
						IF(EXISTS(select * from sys.server_principals where name = N' + @MailLoginQuoted + '))
						DROP LOGIN ' + QUOTENAME( @MailLogin)
					EXEC sp_executesql @sqlcmd
					
					-----
					PRINT 'Dropping user in this database'
					-----
					SET @sqlcmd = 
					N'USE ' + @DBNameQuote + '
					IF(EXISTS (SELECT * FROM sys.database_principals WHERE name = N' + @MailLoginQuoted + '))
						DROP USER ' + QUOTENAME( @MailLogin)

					EXEC sp_executesql @sqlcmd			
					
					-----
					PRINT 'Dropping the certificate in this db'
					-----
					SET @sqlcmd = 
					N'USE ' + @DBNameQuote + '	
					IF(EXISTS(SELECT * FROM sys.certificates WHERE name = N' + @CertNameQuoted + '))
					BEGIN
						DROP CERTIFICATE ' + QUOTENAME(@CertName) + ' 
					END'

					EXEC sp_executesql @sqlcmd			
					
					-----
					PRINT 'Dropping certificate from master'
					-----
					SET @sqlcmd = 
					N'USE master
					IF(EXISTS(SELECT * FROM sys.certificates WHERE name = N' + @CertNameQuoted + '))
						DROP CERTIFICATE ' + QUOTENAME(@CertName)

					EXEC sp_executesql @sqlcmd
				
				END
			END

			FETCH NEXT FROM DBName_Cursor INTO @DBName

		END	-- Loop through all databases.

		CLOSE DBName_Cursor
		DEALLOCATE DBName_Cursor

    	-- deleting all principal associations that are not in MSDB or public
	    exec sp_executesql N'
	    delete from msdb.dbo.sysmail_principalprofile
	    where database_id <> 4 and database_id <> 0'
	END TRY
	BEGIN CATCH
		print 'There was a problem upgrading Mail Host databases.'
		print 'Error State: ' + convert(varchar(10),ERROR_STATE() )
		print 'Error Message: ' + ERROR_MESSAGE()
		print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) 
		print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY())
	END CATCH
END

BEGIN TRY
	IF EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='principal_id' and id =
			(SELECT OBJECT_ID(N'dbo.sysmail_principalprofile', 'U')))
	BEGIN
		-- convert data from principal_id to principal_sid
		exec sp_executesql N'
		DECLARE @principal_sid varbinary(85)
		DECLARE @principal_id int 

		DECLARE principal_sid_cursor CURSOR LOCAL 
		FOR
		SELECT distinct principal_id
		FROM dbo.sysmail_principalprofile

		OPEN principal_sid_cursor 
		FETCH NEXT FROM principal_sid_cursor INTO @principal_id
		WHILE (@@fetch_status = 0)
		BEGIN
			IF @principal_id = 0
			   SET @principal_sid = 0x00
			ELSE
			   SELECT @principal_sid = dbo.get_principal_sid(@principal_id)

			IF @principal_sid IS NOT NULL -- principal_id is valid
			BEGIN
				UPDATE dbo.sysmail_principalprofile
				SET principal_sid = @principal_sid
				WHERE principal_id = @principal_id
			END
			ELSE
			BEGIN
				DELETE FROM dbo.sysmail_principalprofile
				WHERE principal_id = @principal_id
			END
			FETCH NEXT FROM principal_sid_cursor INTO @principal_id
		END
		CLOSE principal_sid_cursor 
		DEALLOCATE principal_sid_cursor'

		-- safety clean-up
		DELETE FROM dbo.sysmail_principalprofile WHERE principal_sid = 0xFFFF

		-- remove obsolete column
		ALTER TABLE dbo.sysmail_principalprofile DROP CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique]
		ALTER TABLE dbo.sysmail_principalprofile DROP COLUMN principal_id
  		ALTER TABLE dbo.sysmail_principalprofile ADD CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique] PRIMARY KEY CLUSTERED ([profile_id] ASC,[principal_sid] ASC)
	END
END TRY
BEGIN CATCH
	print 'There was a problem upgrading MSDB mail host database.'
	print 'Unable to map existing profile id values to profile_sid column'
	print 'Error State: ' + convert(varchar(10),ERROR_STATE() )
	print 'Error Message: ' + ERROR_MESSAGE()
	print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) 
	print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY())
END CATCH
GO

BEGIN TRY
    -- remove database_id column
    IF EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='database_id' and id =
       (SELECT OBJECT_ID(N'dbo.sysmail_principalprofile', 'U')))
    BEGIN
        ALTER TABLE dbo.sysmail_principalprofile DROP CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique]
        ALTER TABLE dbo.sysmail_principalprofile DROP COLUMN database_id
        ALTER TABLE dbo.sysmail_principalprofile ADD CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique] PRIMARY KEY CLUSTERED ([profile_id] ASC,[principal_sid] ASC)
    END
END TRY
BEGIN CATCH
	print 'There was a problem upgrading MSDB mail host database.'
	print 'Unable to create a primary key constraint on sysmail_principalprofile table'
	print 'Error State: ' + convert(varchar(10),ERROR_STATE() )
	print 'Error Message: ' + ERROR_MESSAGE()
	print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) 
	print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY())
END CATCH

Print 'Completed upgrade of Database Mail related objects...'
GO
