/**********************************************************************/ /* PRE_MSDB.SQL */ /* */ /* Prepares MSDB for upgrade. */ /* */ /* ** Copyright (c) Microsoft Corporation ** All Rights Reserved. */ /**********************************************************************/ PRINT '----------------------------------' PRINT 'Starting execution of PRE_MSDB.SQL' PRINT '----------------------------------' use msdb go -- If the user sets implicit_transactions on, some specprocs and DBCC commands will fail -- Need to save the state and disable implicit_transactions DECLARE @implicit_transactions_flag INT DECLARE @is_implicit_transactions_set BIT SELECT @is_implicit_transactions_set = CONVERT(BIT, value) FROM fn_listextendedproperty(N'IMPLICIT_TRANSACTIONS', default, default, default, default, default, default); IF (@is_implicit_transactions_set IS NOT NULL) BEGIN EXEC sp_dropextendedproperty N'IMPLICIT_TRANSACTIONS' END SET @implicit_transactions_flag = 2 SET @is_implicit_transactions_set = @@options & @implicit_transactions_flag EXEC sp_addextendedproperty @name = N'IMPLICIT_TRANSACTIONS', @value = @is_implicit_transactions_set SET IMPLICIT_TRANSACTIONS OFF --set compatibily level to 100 EXEC sp_dbcmptlevel @dbname = 'msdb', @new_cmptlevel = '100' GO /**********************************************************************/ /* PRE_SQLAGENT100.SQL */ /* */ /* Upgrades 7.x, 8.x and 9.0 to 10.0 and drops all obsolete 8.x */ /* */ /* ** Copyright (c) Microsoft Corporation ** All Rights Reserved. */ /**********************************************************************/ PRINT '-----------------------------------------' PRINT 'Starting execution of PRE_SQLAGENT100.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 110 ALTER DATABASE msdb SET COMPATIBILITY_LEVEL = 110 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 --alter sysjobsteps table PRINT '' PRINT 'Alter table sysjobsteps...' ALTER TABLE sysjobsteps ALTER COLUMN additional_parameters NVARCHAR(max) go --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 --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 PRINT '' PRINT '-----------------------------------------' PRINT 'Execution of PRE_SQLAGENT100.SQL complete' PRINT '-----------------------------------------' 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 -- Capture Collection set status before upgrade PRINT 'pre_dc100::Check if syscollector_collection_sets_internal table exists...' IF (OBJECT_ID(N'[dbo].[syscollector_collection_sets_internal]', 'U') IS NOT NULL) BEGIN -- Capture Collection set status in temp table IF (OBJECT_ID('tempdb..#data_collector_collectionset_status', 'U') IS NOT NULL) BEGIN PRINT 'pre_dc100::Dropping existing temp table tempdb..#data_collector_collectionset_status...' DROP TABLE #data_collector_collectionset_status END PRINT 'pre_dc100::Capturing Collection set status in temp table...' SELECT collection_set_uid, name, is_running INTO #data_collector_collectionset_status FROM [dbo].[syscollector_collection_sets_internal] END GO -- -- 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 */ /**********************************************************************/ GO /**********************************************************************/ /* DAC Pre-upgrade steps */ /**********************************************************************/ PRINT 'DAC pre-upgrade steps...'; GO 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] PRINT 'End of DAC pre-upgrade steps.'; GO /**********************************************************************/ /* End of DAC Pre-upgrade steps */ /**********************************************************************/ ------------------------------------------------------------------------- -- /**************************************************************/ /* Utility pre-upgrade steps */ /**************************************************************/ /**************************************************************/ /* End of Utility pre-upgrade steps */ /**************************************************************/ /**********************************************************************/ /* MSDB.SQL */ /* */ /* Creates the MSDB database and some utility SPs. */ /* */ /* */ /* Copyright (c) Microsoft Corporation */ /* All Rights Reserved. */ /* */ /**********************************************************************/ PRINT '----------------------------------' PRINT 'Starting execution of MSDB.SQL' PRINT '----------------------------------' go --this version of instmsdb should be executed only against 11.0 and later servers IF (@@microsoftversion / 0x01000000) < 11 BEGIN RAISERROR('This version of instmsdb.sql should only be executed against 11.0 and later 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. MSDB.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. MSDB.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 /**************************************************************/ /* */ /* F I N I S H E D T A B L E D R O P S */ /* */ /**************************************************************/ PRINT '----------------------------------' PRINT 'Finished execution of MSDB.SQL' PRINT '----------------------------------' go PRINT '-----------------------------------------' PRINT 'Starting execution of MSDB_VERSIONING.SQL' PRINT '-----------------------------------------' go -- This table is used by SqlScriptUpgrade to detect the Sql11 version (the code is in <>\sql\mpu\sqlagent\scripts\msdb_upgrade_discovery.sql) -- Even though the version numbers are taken from sql_version.h so they match the server version, -- the intent here is different. We only use the major version (for now). IF (OBJECT_ID(N'dbo.msdb_version', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table msdb_version...' CREATE TABLE dbo.msdb_version ( id INT IDENTITY, version_string NVARCHAR(255) NOT NULL, version_major INT NOT NULL, version_minor INT NOT NULL, version_build INT NOT NULL, version_revision INT NOT NULL ) END GO -- Even if the table exists already, update the version. TRUNCATE TABLE dbo.msdb_version GO DECLARE @vsmaj INT DECLARE @vsmin INT DECLARE @vsbld INT DECLARE @vsrev INT DECLARE @vsstr NVARCHAR(255) -- The variables below will be initialized during preprocessing. -- The right side of the assignment will be replaced with values from sql\version\sqlversion.h -- Actually MSDB version number defines MSDB own version that is not related to SQL product version. -- We use the same SQL version from sqlversion.h just for an automatic build process. Thus MSDB -- version happens being always updated in every build even though there might be no changes in MSDB. SET @vsmaj = 11; SET @vsmin = 0; SET @vsbld = 2100; SET @vsrev = 60; SET @vsstr = CAST (@vsmaj AS NVARCHAR(10)) + N'.' + CAST (@vsmin AS NVARCHAR(10)) + N'.' + CAST (@vsbld AS NVARCHAR(10)) + N'.' + CAST (@vsrev AS NVARCHAR(10)); INSERT INTO msdb.dbo.msdb_version (version_string, version_major, version_minor, version_build, version_revision) VALUES (@vsstr, @vsmaj, @vsmin, @vsbld, @vsrev) GO PRINT '-----------------------------------------' PRINT 'Finished execution of MSDB_VERSIONING.SQL' PRINT '-----------------------------------------' GO --------------------------------------------------------------- -- Replication Datatype mapping tables --------------------------------------------------------------- -- REVIEW - Is this needed? What does this do? exec sys.sp_MSrepl_dropdatatypemappings go CHECKPOINT go --------------------------------------------------------------- -- Replication Datatype mapping tables --------------------------------------------------------------- exec sys.sp_MSrepl_createdatatypemappings go CHECKPOINT go /**********************************************************************/ /* SQLAGENT.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 and SQLServerAgent */ /* */ /* All Rights Reserved. */ /* */ /**********************************************************************/ /**************************************************************/ /* 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 /**************************************************************/ /* */ /* 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 NVARCHAR(max) 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 -- Parse object_name 'SQLServer:Buffer Manager', exclude instance specific info; return as 'Buffer Manager' SELECT RTRIM(SUBSTRING(pc.object_name, CHARINDEX(':', pc.object_name)+1, DATALENGTH(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 /**************************************************************/ /* 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 /**************************************************************/ /* */ /* 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, @name_of_id_parameter, @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, @name_of_name_parameter, @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, @allow_only_windows_credential bit = NULL AS BEGIN DECLARE @retval INT DECLARE @credential_id_as_char NVARCHAR(36) DECLARE @credential_identity NVARCHAR(4000) 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, @credential_identity = credential_identity FROM 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 credential 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, @credential_identity = credential_identity FROM 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 IF(@allow_only_windows_credential IS NOT NULL) BEGIN IF(@allow_only_windows_credential = 1) BEGIN -- Allow only windows credentials. ( domain\user format) IF(CHARINDEX(N'\', @credential_identity) = 0) BEGIN RAISERROR(14720, -1, -1, '@credential_name', @credential_name) RETURN(1) -- Failure END 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'SQLBinRoot', @InstRootPath OUTPUT IF @InstRootPath IS NULL BEGIN RAISERROR(14658, -1, -1) WITH LOG RETURN (1) END IF RIGHT(@InstRootPath, 1) <> N'\' SELECT @InstRootPath = @InstRootPath + N'\' EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\Microsoft Sql Server\110', 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\110\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\110\Tools\ClientSetup', N'SQLPath', @ToolsPath OUTPUT else EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\Microsoft Sql Server\110\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 -- Fix for #525111 - when MSDB is restored from any other sqlserver, it is possible that physical path to agent_exe, subsystem_dll may not be valid on current server -- It is better to delete all records in this table and reinsert them again -- perform delete and re-insert operations within a transaction TRUNCATE TABLE syssubsystems -- 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 INSERT syssubsystems VALUES ( 1, N'TSQL',14556, FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), 20 * @processor_count ) --ActiveScripting subsystem INSERT syssubsystems VALUES ( 2, N'ActiveScripting', 14555, @InstRootPath + N'SQLATXSS.DLL',NULL,N'ActiveScriptStart',N'ActiveScriptEvent',N'ActiveScriptStop', 10 * @processor_count ) --CmdExec subsystem INSERT syssubsystems VALUES ( 3, N'CmdExec', 14550, @InstRootPath + N'SQLCMDSS.DLL',NULL,N'CmdExecStart',N'CmdEvent',N'CmdExecStop', 10 * @processor_count ) --Snapshot subsystem 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 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 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 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 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 INSERT syssubsystems VALUES ( 9, N'ANALYSISQUERY', 14513, @InstRootPath + N'SQLOLAPSS.DLL',NULL,N'OlapStart',N'OlapQueryEvent',N'OlapStop',100 * @processor_count ) --ANALYSISCOMMAND subsystem 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 INSERT syssubsystems VALUES ( 11, N'SSIS', 14538, @InstRootPath + N'SQLDTSSS.DLL',@DtsRootPath + N'DTExec.exe',N'DtsStart',N'DtsEvent',N'DtsStop',100 * @processor_count ) END --PowerShell subsystem 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_get_proxy_properties */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_proxy_properties...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_get_proxy_properties') AND (type = 'P'))) DROP PROCEDURE sp_get_proxy_properties GO CREATE PROCEDURE sp_get_proxy_properties @proxy_id [int] = NULL, -- specify either @current_proxy_id or @current_proxy_name ; if both are specified as null, propery for all proxies are returned back @proxy_name [sysname] = NULL AS BEGIN DECLARE @retval INT SET NOCOUNT ON -- Validate only if either proxy name or proxy id was specified IF NOT (@proxy_id IS NULL ) AND (@proxy_name IS NULL) BEGIN EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) BEGIN -- exception message was raised inside sp_verify_proxy_identifiers; we dont need to RAISERROR again here RETURN(1) -- Failure END END -- return domain name, user name, credential id; used by SQL agent to query for proxy SELECT CASE CHARINDEX(N'\', c.credential_identity) WHEN 0 THEN NULL ELSE LEFT(c.credential_identity, CHARINDEX(N'\', c.credential_identity)-1) END AS user_domain, RIGHT(c.credential_identity, LEN(c.credential_identity)- CHARINDEX(N'\', c.credential_identity)) AS user_name, c.credential_id FROM msdb.dbo.sysproxies p JOIN sys.credentials c ON p.credential_id = c.credential_id WHERE (p.proxy_id = @proxy_id OR @proxy_id IS NULL) 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 -- For CmdExec and Powershell subsystems, make sure that proxy is mapped to windows login IF ((UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS) = 'CMDEXEC') OR (UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS) = 'POWERSHELL')) BEGIN DECLARE @credential_name [sysname] DECLARE @credential_id [INT] SELECT @credential_id = credential_id FROM sysproxies WHERE proxy_id = @proxy_id EXECUTE @retval = sp_verify_credential_identifiers '@credential_name', '@credential_id', @credential_name OUTPUT, @credential_id OUTPUT, @allow_only_windows_credential = 1 IF (@retval <> 0) BEGIN RETURN(1) -- Failure END 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 DECLARE @flags INT DECLARE @affected_records INT = 0 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) RETURN(1) -- Failure END ELSE BEGIN DECLARE revoke_cursor CURSOR LOCAL FOR SELECT flags FROM sysproxylogin WHERE proxy_id = @proxy_id OPEN revoke_cursor FETCH NEXT FROM revoke_cursor INTO @flags WHILE (@@fetch_status = 0) BEGIN if @flags = 1 OR @flags = 0 -- @flags with value 1 indicates fixed server role, flags with value 0 indicates login, both sid(s) should be read from sys.server_principals SELECT @sid = SUSER_SID(@name, 0) --force case insensitive comparation for NT users ELSE SELECT @sid = sid FROM msdb.sys.database_principals WHERE name = @name -- @flags with value 2 indicates MSDB role --check parametrs validity IF (ISNULL(@sid, 0) <> 0) BEGIN DELETE FROM sysproxylogin WHERE proxy_id = @proxy_id AND sid = @sid AND flags = @flags SELECT @affected_records = @affected_records + @@ROWCOUNT END FETCH NEXT FROM revoke_cursor INTO @flags END CLOSE revoke_cursor DEALLOCATE revoke_cursor if @affected_records = 0 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_reassign_proxy */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_reassign_proxy...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_reassign_proxy') AND (type = 'P'))) DROP PROCEDURE sp_reassign_proxy GO CREATE PROCEDURE sp_reassign_proxy @current_proxy_id [int] = NULL, -- must specify either @current_proxy_id or @current_proxy_name @current_proxy_name [sysname] = NULL, @target_proxy_id [int] = NULL, -- must specify either @target_proxy_id or @target_proxy_name @target_proxy_name [sysname] = NULL -- N'' is a special case to allow change of an existing proxy as NULL (run job step in sql agent service account context) AS BEGIN DECLARE @retval INT SET NOCOUNT ON -- validate current proxy id EXECUTE @retval = sp_verify_proxy_identifiers '@current_proxy_name', '@current_proxy_id', @current_proxy_name OUTPUT, @current_proxy_id OUTPUT IF (@retval <> 0) BEGIN -- exception message was raised inside sp_verify_proxy_identifiers; we dont need to RAISERROR again here RETURN(1) -- Failure END -- @target_proxy_name = N'' is a special case to allow change of an existing proxy as NULL (run job step in sql agent service account context) IF (@target_proxy_id IS NOT NULL) OR (@target_proxy_name IS NOT NULL AND @target_proxy_name <> N'') BEGIN EXECUTE @retval = sp_verify_proxy_identifiers '@target_proxy_name', '@target_proxy_id', @target_proxy_name OUTPUT, @target_proxy_id OUTPUT IF (@retval <> 0) BEGIN -- exception message was raised inside sp_verify_proxy_identifiers; we dont need to RAISERROR again here RETURN(1) -- Failure END END -- Validate that current proxy id and target proxy id are not the same IF(@current_proxy_id = @target_proxy_id) BEGIN RAISERROR(14399, -1, -1, @current_proxy_id, @target_proxy_id) RETURN(1) -- Failure END DECLARE @job_id UNIQUEIDENTIFIER DECLARE @step_id int DECLARE @proxy_id int DECLARE @subsystem_id int -- cursor to enumerate list of job steps what has proxy_id as current proxy_id DECLARE @jobstep_cursor CURSOR SET @jobstep_cursor = CURSOR FOR SELECT js.job_id, js.step_id, js.proxy_id , subsys.subsystem_id FROM sysjobsteps js JOIN syssubsystems subsys ON js.subsystem = subsys.subsystem WHERE js.proxy_id = @current_proxy_id OPEN @jobstep_cursor FETCH NEXT FROM @jobstep_cursor INTO @job_id, @step_id, @proxy_id, @subsystem_id WHILE @@FETCH_STATUS = 0 BEGIN -- current proxy might have been granted to be used by this specific subsystem -- making sure that the target proxy has been granted access to same subsystem -- Grant target proxy to subsystem if it was not granted before IF NOT EXISTS( SELECT DISTINCT ps.proxy_id, subsyst.subsystem_id FROM syssubsystems subsyst JOIN sysproxysubsystem ps ON (ps.subsystem_id = subsyst.subsystem_id AND ps.proxy_id = @target_proxy_id AND ps.subsystem_id = @subsystem_id) ) BEGIN -- throw error that user needs to grant permission to this target proxy IF @target_proxy_id IS NOT NULL BEGIN RAISERROR(14400, -1, -1, @target_proxy_id, @subsystem_id) RETURN(1) -- Failure END END -- Update proxy_id for job step with target proxy id using sp_update_jobstep EXEC sp_update_jobstep @job_id = @job_id, @step_id = @step_id , @proxy_name = @target_proxy_name FETCH NEXT FROM @jobstep_cursor INTO @job_id, @step_id, @proxy_id, @subsystem_id END CLOSE @jobstep_cursor DEALLOCATE @jobstep_cursor RETURN(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 (SELECT CASE WHEN compatibility_level >= 70 THEN 1 ELSE 0 END FROM sys.databases WHERE (name = 'msdb')) AS msdb_70_compatible, CASE WHEN DATABASEPROPERTYEX('msdb', 'Updateability') = 'READ_ONLY' THEN 1 ELSE 0 END AS msdb_read_only, ( CASE WHEN DATABASEPROPERTYEX('msdb', 'Status') = 'ONLINE' THEN 1 ELSE 0 END & CASE WHEN DATABASEPROPERTYEX('msdb', 'UserAccess') = 'MULTI_USER' THEN 1 ELSE 0 END) AS msdb_available, CASE ISNULL((SELECT 1 WHERE 'a' = 'A'), 0) WHEN 1 THEN 0 ELSE 1 END AS case_sensitive_server, (SELECT value_in_use FROM sys.configurations WHERE (name = 'user connections')) AS max_user_connection, CONVERT(sysname, SERVERPROPERTY('SERVERNAME')) AS sql_server_name, ISNULL(@tbu, 0) AS tbu, PLATFORM() AS platform, ISNULL(CONVERT(sysname, SERVERPROPERTY('INSTANCENAME')), 'MSSQLSERVER') AS instance_name , CONVERT(INT, SERVERPROPERTY('ISCLUSTERED')) AS is_clustered, @agentAllowed AS agent_allowed RETURN(0) -- Success END go /**************************************************************/ /* SP_SQLAGENT_UPDATE_AGENT_XPS */ /* Sql Agent uses the XPs listed in the "Agent XPs" bucket. */ /* The configuration is enable on agent startup, */ /* and disabled on agent shutdown. */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_update_agent_xps...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_sqlagent_update_agent_xps') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_update_agent_xps go CREATE PROCEDURE sp_sqlagent_update_agent_xps @new_value bit = 1 -- the new value for the "Agent XPs" configuration option. AS BEGIN declare @agent_enabled bit declare @show_advanced bit select @show_advanced = cast(value_in_use as bit) from sys.configurations where name = N'show advanced options' select @agent_enabled = cast(value_in_use as bit) from sys.configurations where name = N'Agent XPs' if @new_value <> @agent_enabled begin if 1 <> @show_advanced begin exec sys.sp_configure @configname = N'show advanced options', @configvalue = 1 reconfigure with override end exec sys.sp_configure @configname = N'Agent XPs', @configvalue = @new_value reconfigure with override if 1 <> @show_advanced begin exec sys.sp_configure @configname = N'show advanced options', @configvalue = 0 reconfigure with override end end 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, @job_id uniqueidentifier = NULL, -- if this is not null, @login_name will be ignored! @is_sysadmin_member INT = NULL OUTPUT AS BEGIN DECLARE @has_server_access BIT DECLARE @is_sysadmin BIT DECLARE @actual_login_name sysname -- Set only when login_name is actually found. It will be zero when @actual_login_name is (unknown). DECLARE @login_found BIT 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) SELECT @login_found = 0 -- If @job_id was set, get the current name associated with the job owner sid. if (@job_id IS NOT NULL) BEGIN SELECT @login_name = dbo.SQLAGENT_SUSER_SNAME(owner_sid) FROM msdb.dbo.sysjobs_view WHERE @job_id = job_id -- If the job_id is invalid, return error IF (@login_name IS NULL) BEGIN RETURN 1; END END 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') SET @login_found = 1 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') SET @login_found = 1 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) SET @login_found = 1 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 IF (SELECT COUNT(*) FROM @xp_results) > 0 BEGIN SET @has_server_access = 1 SET @login_found = 1 END 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) SET @login_found = 1 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, login_found = @login_found ELSE -- output variable only SELECT @is_sysadmin_member = @is_sysadmin 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, PATINDEX('%:%', performance_condition) + 1, CHARINDEX('|', performance_condition, PATINDEX('%_|_%', performance_condition) + 2)-(PATINDEX('%:%', performance_condition) + 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)) -- M (DatabaseMail action [ refresh profile associated with sql agent) @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'JSAGDPLTM') = 0) BEGIN RAISERROR(14266, -1, -1, '@op_type', 'J, S, A, G, D, P, L, T, M') 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' , 'cycle log', dbmail profile refresh action parameters IF (CHARINDEX(@op_type, N'GDPLM') <> 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 --Ex: "SqlServer:General Statistics|User Connections||>|5" => "General Statistics|User Connections||>|5" SELECT @temp_str = SUBSTRING(@performance_condition, PATINDEX('%:%', @performance_condition)+1, DATALENGTH(@performance_condition) - (PATINDEX('%:%', @performance_condition)+1) ) 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 object_name 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 counter_name 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 instance_name 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 -- Allow if message id = 50000 (RAISERROR called with no specific message id) IF(@message_id <> 50000) BEGIN IF (@message_id <> 0) AND (NOT EXISTS (SELECT message_id FROM sys.messages WHERE message_id = @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 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 @use_databasemail INT = NULL, -- 1 or 0 @databasemail_profile SYSNAME = NULL 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 DECLARE @notify_sqlagent_dbmail_settings_update BIT SET @notify_sqlagent_dbmail_settings_update = 0 IF(@use_databasemail IS NOT NULL) BEGIN EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'UseDatabaseMail', N'REG_DWORD', @use_databasemail SET @notify_sqlagent_dbmail_settings_update = 1 END IF(@databasemail_profile IS NOT NULL) BEGIN EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'DatabaseMailProfile', N'REG_SZ', @databasemail_profile SET @notify_sqlagent_dbmail_settings_update = 1 END IF(@notify_sqlagent_dbmail_settings_update = 1 ) BEGIN -- Notify SQL Agent that Databasemail settings for SQL Agent was changed. force a reload EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'M' 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 BEGIN -- First save the current transaction isolation level DECLARE @TRANSACTION_ISOLATION_LEVEL INT SELECT @TRANSACTION_ISOLATION_LEVEL = transaction_isolation_level FROM sys.dm_exec_sessions where session_id = @@SPID -- If the isolation level is not known, do nothing! IF @TRANSACTION_ISOLATION_LEVEL >0 AND @TRANSACTION_ISOLATION_LEVEL < 6 BEGIN -- Set transaction isolation level to READ UNCOMMITTED -- This will ensure that we can still read the history even if the rows are locked by the TABLOCKX operation on the history row limiter SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED END 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) -- Revert the isolation level IF @TRANSACTION_ISOLATION_LEVEL = 1 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ELSE IF @TRANSACTION_ISOLATION_LEVEL = 2 SET TRANSACTION ISOLATION LEVEL READ COMMITTED ELSE IF @TRANSACTION_ISOLATION_LEVEL = 3 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ ELSE IF @TRANSACTION_ISOLATION_LEVEL = 4 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ELSE IF @TRANSACTION_ISOLATION_LEVEL = 5 SET TRANSACTION ISOLATION LEVEL SNAPSHOT END 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 */ /**************************************************************/ 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 caller owns the job --or the caller is a sysadmin IF ((@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 -- Permissions check: -- If sysadmin continue (sysadmin can detach schedules they don't own) -- Otherwise if the caller owns the job, we can detach it -- Except If @delete_unused_schedule = 1 then the caller has to own both the job and the schedule IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN IF (@job_owner_sid = SUSER_SID()) BEGIN IF ((@delete_unused_schedule = 1) AND (@sched_owner_sid <> SUSER_SID())) BEGIN -- Cannot delete the schedule RAISERROR(14394, -1, -1) RETURN(1) -- Failure END END ELSE -- the caller is not sysadmin and it does not own the job -> throw BEGIN RAISERROR(14391, -1, -1) RETURN(1) -- Failure END 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 -- @flags=4 is valid only for TSQL subsystem IF (((@flags & 4) <> 0) AND (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('TSQL'))) BEGIN RAISERROR(14545, -1, -1, '@flags', @subsystem) RETURN(1) -- Failure END -- values 8 and 16 for @flags cannot be combined IF (((@flags & 8) <> 0) AND ((@flags & 16) <> 0)) BEGIN RAISERROR(14545, -1, -1, '@flags', @subsystem) 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 -- Note: explicit check for null is required here 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 NVARCHAR(max) = 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 NVARCHAR(max) = 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 NVARCHAR(max) = 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, 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, 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) 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)) OR -- NT AUTHORITY\NETWORK SERVICE sid (@owner_sid IS NULL) BEGIN IF (@owner_sid IS NULL OR (EXISTS (SELECT sid FROM sys.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_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 @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 SELECT additional_parameters FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @step_id) 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 IF (@step_id = 0) --only check once per job execution. BEGIN EXECUTE msdb.dbo.sp_jobhistory_row_limiter @job_id END 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 NVARCHAR(max) = 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 /**************************************************************/ /** **/ /** 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_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 GRANT SELECT ON sysproxies TO TargetServersRole 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, 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 /**************************************************************/ /* 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_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 -- Add permissions for this SP. GRANT EXECUTE ON sp_help_jobhistory_sem TO SQLAgentUserRole /**************************************************************/ /* */ /* 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 /**************************************************************/ /* */ /* 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 ' + @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 -- -- Stored Proc: -- sysmail_verify_addressparams_sp -- It is an internal SP that verifies if the given address is in the correct format. -- -- Some valid email addresses are: -- -- user@host -- "Display Name" -- "Doe, John" -- -- -- DBMail supports only the first format: user@host, which is separated by a delimiter semicolon (;). -- Since the comma and semicolon are most confusing delimiters to users, this function only checks -- comma (,) as the invalid delimiter to fix the bug VSTS 160781. -- -- On the other hand, the function name is reserved to support more formats than the comma delimiter in -- the future extension. -- -- -- Parameters: -- @address -- The string of address that will be verified -- @parameter_name -- The name of parameter that will be shown in error message to tell users -- which parameter is wrong. -- For example, the name is '@recipients' or '@replyto_address'. -- PRINT '' PRINT 'Creating procedure sysmail_verify_addressparams_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_verify_addressparams_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_verify_addressparams_sp go CREATE PROCEDURE dbo.sysmail_verify_addressparams_sp @address VARCHAR(MAX), @parameter_name NVARCHAR(32) AS IF ((@address IS NOT NULL) AND (@address != N'')) BEGIN DECLARE @commaIndex int SET @commaIndex = CHARINDEX(',', @address) IF (@commaIndex > 0) BEGIN -- Comma is the wrong format to separate addresses. Users should use the semicolon ";". RAISERROR(14613, 16, 1, @parameter_name, @address) RETURN(1) END 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) EXEC @rc = msdb.dbo.sysmail_verify_addressparams_sp @address = @replyto_address, @parameter_name='@replyto_address' IF (@rc <> 0) RETURN @rc --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 EXEC @rc = msdb.dbo.sysmail_verify_addressparams_sp @address = @replyto_address, @parameter_name='@replyto_address' IF (@rc <> 0) RETURN @rc 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 -- Turn on QUOTED IDENTIFIER to compile this stored proc that uses XQuery SET QUOTED_IDENTIFIER ON 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 @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 -- $ISSUE: 325439 - DBMail: Use XML type for all xml document handling in DBMail stored procs DECLARE @xmlblob xml SET @xmlblob = CONVERT(xml, @xml_message_body) SELECT @mailitem_id = MailResponses.Properties.value('(MailItemId/@Id)[1]', 'int'), @sent_status = MailResponses.Properties.value('(SentStatus/@Status)[1]', 'int') FROM @xmlblob.nodes(' declare namespace responses="http://schemas.microsoft.com/databasemail/responses"; /responses:SendMail') AS MailResponses(Properties) 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 GO -- Turn off QUOTED IDENTIFIER after compiling stored proc that uses XQuery SET QUOTED_IDENTIFIER OFF 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 -- Turn on QUOTED IDENTIFIER to compile this stored proc that uses XQuery SET QUOTED_IDENTIFIER ON 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, @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 DECLARE @xmlblob xml SET @xmlblob = CONVERT(xml, @xml_message_body) SELECT @mailitem_id = MailRequest.Properties.value('(MailItemId)[1]', 'int') FROM @xmlblob.nodes(' declare namespace requests="http://schemas.microsoft.com/databasemail/requests"; /requests:SendMail') AS MailRequest(Properties) -- 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, @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 -- 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 -- Turn off QUOTED IDENTIFIER after compiling stored proc that uses XQuery SET QUOTED_IDENTIFIER OFF 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 EXEC @rc = msdb.dbo.sysmail_verify_addressparams_sp @address = @recipients, @parameter_name='@recipients' IF (@rc <> 0) RETURN @rc EXEC @rc = msdb.dbo.sysmail_verify_addressparams_sp @address = @copy_recipients, @parameter_name='@copy_recipients' IF (@rc <> 0) RETURN @rc EXEC @rc = msdb.dbo.sysmail_verify_addressparams_sp @address = @blind_copy_recipients, @parameter_name='@blind_copy_recipients' IF (@rc <> 0) RETURN @rc EXEC @rc = msdb.dbo.sysmail_verify_addressparams_sp @address = @reply_to, @parameter_name='@reply_to' IF (@rc <> 0) RETURN @rc --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 = '' + CONVERT(NVARCHAR(20), @mailitem_id) + N'' -- 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, @mailitem_id) 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 -- Turn on QUOTED IDENTIFIER to compile this stored proc that uses XQuery SET QUOTED_IDENTIFIER ON GO -- Processes messages from the external mail queue -- CREATE PROCEDURE [dbo].[sp_ExternalMailQueueListener] AS BEGIN DECLARE @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: -- -- -- -- -- -- -- -- -- -- -- -- DECLARE @xmlblob xml SET @xmlblob = CONVERT(xml, @xml_message_body) SELECT @mailitem_id = MailResponses.Properties.value('(MailItemId/@Id)[1]', 'int'), @sent_status = MailResponses.Properties.value('(SentStatus/@Status)[1]', 'int'), @sent_account_id = MailResponses.Properties.value('(SentAccountId/@Id)[1]', 'int'), @sent_date = MailResponses.Properties.value('(SentDate/@Date)[1]', 'DateTime'), @processId = MailResponses.Properties.value('(CallingProcess/@Id)[1]', 'int'), @LogMessage = MailResponses.Properties.value('(Information/Failure/@Message)[1]', 'NVARCHAR(max)') FROM @xmlblob.nodes(' declare namespace responses="http://schemas.microsoft.com/databasemail/responses"; /responses:SendMail') AS MailResponses(Properties) 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 -- Turn off QUOTED IDENTIFIER after compiling stored proc that uses XQuery SET QUOTED_IDENTIFIER OFF 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 */ /* */ /**************************************************************/ /**************************************************************/ /* 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 /**************************************************************/ /* */ /* 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 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 **/ /** **/ /**************************************************************/ 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 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 /***************************************************************/ /* 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 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 DECLARE @product_version nvarchar(128) -- SERVERPROPERTY('ProductVersion') returns value of type nvarchar(128) DECLARE @provider_name nvarchar(128) 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 @product_version = CONVERT(nvarchar(128), SERVERPROPERTY('ProductVersion')) -- Get Product Major version from string format 'major.minor.build' SET @provider_name = 'SQLNCLI' + SUBSTRING(@product_version, 0, CHARINDEX('.', @product_version)) 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=' + @provider_name SET @connection_string = @connection_string + N';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(14719, -1, -1, N'dc_admin') 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(14719, -1, -1, N'dc_admin') 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' -- initialize to 0 == not running state; when agent XPs are disabled, call to xp_sqlagent_enum_jobs is never made in -- code below. When Agent Xps are disabled agent would not be running, in this case, jobs will also be in 'not running" state SET @is_collection_running = 0 SET @is_upload_running = 0 SET @collection_job_state = 0 SET @upload_job_state = 0 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 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 END SELECT @is_running = is_running FROM dbo.syscollector_collection_sets 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_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; -- 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(14699, -1, -1) 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 = ' ' SET @parameter_formatter = N'

  • \ () \
  • ' 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 = ' ' SET @parameter_formatter = N'
                
                

    ' 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 -- -- This stored procedure is used to cleanup all activities done while configuring Data collector -- Followign cleanup tasks are done -- a) Delete collect, upload jobs -- b) Set Data collector to non-configured state -- c) Delete all collection set logs -- IF (NOT OBJECT_ID(N'[dbo].[sp_syscollector_cleanup_collector]', 'P') IS NULL) BEGIN RAISERROR('Dropping procedure [dbo].[sp_syscollector_cleanup_collector] ...', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].[sp_syscollector_cleanup_collector] END GO RAISERROR('Creating procedure [dbo].[sp_syscollector_cleanup_collector] ...', 0, 1) WITH NOWAIT; GO CREATE PROC [dbo].[sp_syscollector_cleanup_collector] AS BEGIN -- Disable constraints -- this is done to make sure that constraint logic does not interfere with cleanup process ALTER TABLE dbo.syscollector_collection_sets_internal NOCHECK CONSTRAINT FK_syscollector_collection_sets_collection_sysjobs ALTER TABLE dbo.syscollector_collection_sets_internal NOCHECK CONSTRAINT FK_syscollector_collection_sets_upload_sysjobs -- Delete data collector jobs DECLARE @job_id uniqueidentifier DECLARE datacollector_jobs_cursor CURSOR LOCAL FOR SELECT collection_job_id AS job_id FROM syscollector_collection_sets WHERE collection_job_id IS NOT NULL UNION SELECT upload_job_id AS job_id FROM syscollector_collection_sets WHERE upload_job_id IS NOT NULL OPEN datacollector_jobs_cursor FETCH NEXT FROM datacollector_jobs_cursor INTO @job_id WHILE (@@fetch_status = 0) BEGIN IF EXISTS ( SELECT COUNT(job_id) FROM sysjobs WHERE job_id = @job_id ) BEGIN DECLARE @job_name sysname SELECT @job_name = name from sysjobs WHERE job_id = @job_id PRINT 'Removing job '+ @job_name EXEC dbo.sp_delete_job @job_id=@job_id, @delete_unused_schedule=0 END FETCH NEXT FROM datacollector_jobs_cursor INTO @job_id END CLOSE datacollector_jobs_cursor DEALLOCATE datacollector_jobs_cursor -- Enable Constraints back ALTER TABLE dbo.syscollector_collection_sets_internal CHECK CONSTRAINT FK_syscollector_collection_sets_collection_sysjobs ALTER TABLE dbo.syscollector_collection_sets_internal CHECK CONSTRAINT FK_syscollector_collection_sets_upload_sysjobs -- Disable trigger on syscollector_collection_sets_internal -- this is done to make sure that trigger logic does not interfere with cleanup process EXEC('DISABLE TRIGGER syscollector_collection_set_is_running_update_trigger ON syscollector_collection_sets_internal') -- Set collection sets as not running state UPDATE syscollector_collection_sets_internal SET is_running = 0 -- Update collect and upload jobs as null UPDATE syscollector_collection_sets_internal SET collection_job_id = NULL, upload_job_id = NULL -- Enable back trigger on syscollector_collection_sets_internal EXEC('ENABLE TRIGGER syscollector_collection_set_is_running_update_trigger ON syscollector_collection_sets_internal') -- re-set collector config store UPDATE syscollector_config_store_internal SET parameter_value = 0 WHERE parameter_name IN ('CollectorEnabled') UPDATE syscollector_config_store_internal SET parameter_value = NULL WHERE parameter_name IN ( 'MDWDatabase', 'MDWInstance' ) -- Delete collection set logs DELETE FROM syscollector_execution_log_internal 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 = ' ' SET @parameter_formatter = N'

     ID =  -  
  •  ID =  -  
      -  -  -  
    ' 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 --------------------------------------------------------------- -- 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' 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 < 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'' disk_usage '); -- 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' -- 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 log_usage '); -- 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' 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 > 0 OR wait_time_ms > 0 OR signal_wait_time_ms > 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 > 60000 ) AS merged_wait_stats GROUP BY wait_type os_wait_stats 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 > 0 OR wait_time_ms > 0 os_latch_stats 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, -- Three columns were removed from the dm_os_sys_info DMV in SQL11 as part of a change -- to memory manager architecture: bpool_committed, bpool_commit_target, and bpool_visible. -- While it is no longer correct, strictly speaking, to speak of such things as "buffer -- pool target" memory in SQL Server versions after SQL 2008 R2, in the MDW database these -- values were simply used as a proxy for "SQL Server committed memory" and "SQL Server -- target memory"; the fact that it used to be buffer pool but now is the memory mgr that -- tracks these values is immaterial to how they are used in MDW. There are three new -- columns that were added in versions after SQL 2008 R2; we map the new column that provides -- comparable information to the old SQL 2008/2008 R2 column name. This allows the same MDW -- schema to work with data from any supported version. The old DMV values were counts of -- 8KB pages; the replacement columns provide a KB count. (si.committed_target_kb / 8) AS bpool_commit_target, (si.committed_kb / 8) AS bpool_committed, (si.visible_target_kb / 8) 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 sql_process_and_system_memory SET NOCOUNT ON SELECT memory_node_id, virtual_address_space_reserved_kb, virtual_address_space_committed_kb, locked_page_allocations_kb, -- In SQL 2008 and 2008 R2, we collected the [single_pages_kb] and [multi_pages_kb] columns -- from this DMV. In later versions, the memory manager has been changed so that the fundamental -- distinction between single-page and multi-page allocations no longer exists. As a result, -- these two columns were removed, and a "pages_kb" column was added instead. In the MDW -- database, these two columns were just added together to calculate total memory allocated -- by the memory node. We don''t want to remove these columns from the destination table since -- that would break all SQL2008/2008R2 clients until they could be patched. So, to maximize -- backwards compatibility and minimize the size of this change, we simply return the new -- [pages_kb] value in the existing [single_pages_kb] column. This isn''t an entirely -- accurate name for the value, but it doesn''t affect the way that this value is used after -- it has been uploaded to MDW. pages_kb AS single_pages_kb, 0 AS multi_pages_kb, shared_memory_reserved_kb, shared_memory_committed_kb FROM sys.dm_os_memory_nodes os_memory_nodes SET NOCOUNT ON SELECT type, memory_node_id as memory_node_id, -- See comment in the sys.dm_os_memory_nodes query (above) for more info on -- [single_pages_kb] and [multi_pages_kb]. SUM(pages_kb) as single_pages_kb, 0 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 os_memory_clerks 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 < 128 os_schedulers 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'' --> ''\\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'' --> ''C:'') WHEN CHARINDEX (''\'', LTRIM(f.physical_name), 3) > 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] io_virtual_file_stats '); -- 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' '); -- 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 ('AvailabilityDatabase', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('AvailabilityGroup', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('AvailabilityReplica', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('DatabaseReplicaState', 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 ('IAvailabilityGroupState', 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 ('SearchPropertyList', 6); 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 ('ServerRole', 7); 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 ('Sequence', 7); 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_SERVER_ROLE',N'SERVER ROLE',N'SERVER ROLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ServerRole' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_SERVER_ROLE',N'SERVER ROLE',N'SERVER ROLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ServerRole' 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'SERVER ROLE',N'SERVER ROLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ServerRole' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SEQUENCE',N'SEQUENCE',N'SEQUENCE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'Sequence' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_SEQUENCE',N'SEQUENCE',N'SEQUENCE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'Sequence' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'RENAME',N'SEQUENCE',N'SEQUENCE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'Sequence' 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'SEQUENCE',N'SEQUENCE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'Sequence' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SCHEMA',N'SEQUENCE',N'SEQUENCE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'Sequence' 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' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SEARCH_PROPERTY_LIST',N'SEARCHPROPERTYLIST',N'SEARCH PROPERTY LIST' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'SearchPropertyList' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_SEARCH_PROPERTY_LIST',N'SEARCHPROPERTYLIST',N'SEARCH PROPERTY LIST' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'SearchPropertyList' 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'SEARCHPROPERTYLIST',N'SEARCH PROPERTY LIST' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'SearchPropertyList' 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 -- 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 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 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 -- Protect against non-scheduled enabled policies that have scripts in their conditions 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 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 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 -- 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 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 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, 'SAC_ENDPOINT_CHANGE21075master') 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 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 ------------------------------------------------------------------------------- -- End policy objects ------------------------------------------------------------------------------- /**************************************************************/ /* 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_delete_history', 'P') IS NULL) BEGIN RAISERROR('Dropping Stored Procedure sp_sysdac_delete_history', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysdac_delete_history; 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 @type_version - Version of the Dac type @instance_name - Name of the Dac Instance @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_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 --- Ensure the user is either a db_creator or that the package being referred is visible via the package view. --- For non-dbcreators, the package will only be visible if we are the associated dbo or sysadmin and the instance row exists IF ((dbo.fn_sysdac_is_dac_creator() != 1) AND (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_delete_history] This proc deletes History entries in the dbo.sysdac_history_internal table Parameters: @dac_instance_name - Name of the dac instance whose history is intended to be purged. If it's null, all entries that the user can delete 'or' orphan entries shall be purged. @older_than - DateTime to limit the rows affected. Rows whose modified datetime < @older_than will only be considered. If it's null, current datetime(GETDATE()) is used. User needs to either own the DAC or be sysadmin to purge the entries or else, purging will not succeed. */ RAISERROR('Creating procedure [dbo].[sp_sysdac_delete_history]...', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysdac_delete_history] @dac_instance_name sysname, @older_than datetime AS SET NOCOUNT ON; BEGIN DECLARE @retval INT DECLARE @instanceId UniqueIdentifier SELECT @older_than = COALESCE(@older_than, GETDATE()) IF @dac_instance_name IS NULL BEGIN -- Delete everyone who is not orphaned that you have visibility to DELETE FROM dbo.sysdac_history_internal WHERE instance_id IN (SELECT instance_id FROM dbo.sysdac_instances) AND (date_modified < @older_than) -- Also remove orphans (note that we need to look into sysdac_instances_internal table) DELETE FROM dbo.sysdac_history_internal WHERE instance_id NOT IN( SELECT instance_id FROM dbo.sysdac_instances_internal) AND (date_modified < @older_than) END ELSE BEGIN -- Delete all entries that the user can view (i.e own the DAC or be sysadmin) DELETE FROM dbo.sysdac_history_internal WHERE instance_id IN ( SELECT instance_id FROM dbo.sysdac_instances WHERE instance_name = @dac_instance_name) AND (date_modified < @older_than) END 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_update_instance 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_delete_history 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 */ /**************************************************************//**************************************************************/ /* 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), powershell_path NVARCHAR(4000) ) -- 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 , [state] TINYINT ); 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 DECLARE @drop_statement nvarchar(600) SET @drop_statement = N'DROP SYNONYM [dbo].' + QUOTENAME(@synonym_name) RAISERROR ('Executing: %s', 0, 1, @drop_statement) WITH NOWAIT; 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) WITH NOWAIT; 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. Note that this proc requires the 'Agent XPs' sp_configure setting to be enabled. During upgrade, this setting will need to be manually enabled, since upgrade scripts are executed while Agent is stopped. 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. @force_stub_use - If set to 1 (default is 0), the msdb wrapper views will always be redirected to the msdb stub tables, even if the instance is a UCP. This is set to 1 when the proc is run by instmsdb.sql, to solve an upgrade issue: setup upgrades the msdb schema before upgrading MDW schema. The new msdb view definitions could reference new columns that have not yet been added to the MDW tables (because MDW has not yet been upgraded by setup). Setting @force_stub_use to 1 allows instmsdb.sql to update the msdb wrapper views before MDW has been upgraded. The procedure will be executed again after instmdw.sql runs (in post_upgrade_ucp_cmdw.sql), and that execution will redirect the synonyms to MDW, if the instance is a UCP. @refresh_views - If set to 1 (default) the msdb wrapper views will be updated so that view metadata reflects any changes to the schema of referenced tables. @refresh_views is always set to 0 when this proc is executed by instmsdb.sql. This is because the final execution of the proc in instmsdb.sql may have pointed the (upgraded) msdb wrapper views to MDW tables that have not yet been upgraded, and may lack columns that are referenced by the msdb wrapper views. If the instance is a UCP, the views will be refreshed when the proc is run following MDW upgrade. ********************************************************************/ 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, @force_stub_use BIT = 0, @refresh_views 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)) AND (@force_stub_use = 0) 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; IF (@refresh_views = 1) BEGIN -- Refresh the msdb wrapper views to ensure that the view metadata matches the underlying table metadata. -- This is necessary for two reasons: -- a) When this procecure is executed by the Create UCP process, it may change the structure of the tables -- that the msdb wrapper views reference, by redirecting the synonyms from the msdb stub tables to -- different tables in MDW. The refresh ensures that the view metadata matches that of the new -- referenced tables. -- b) The proc is also executed after msdb and MDW schema upgrade. In this case, the MDW upgrade may have -- changed the MDW table schema even if the synonyms weren't redirected. RAISERROR ('Refreshing msdb wrapper views', 0, 1) WITH NOWAIT; EXEC dbo.sp_refreshview N'dbo.sysutility_ucp_computers'; EXEC dbo.sp_refreshview N'dbo.sysutility_ucp_volumes'; EXEC dbo.sp_refreshview N'dbo.sysutility_ucp_instances'; EXEC dbo.sp_refreshview N'dbo.sysutility_ucp_databases'; EXEC dbo.sp_refreshview N'dbo.sysutility_ucp_filegroups'; EXEC dbo.sp_refreshview N'dbo.sysutility_ucp_datafiles'; EXEC dbo.sp_refreshview N'dbo.sysutility_ucp_logfiles'; EXEC dbo.sp_refreshview N'dbo.sysutility_ucp_utility_space_utilization'; END; END; GO -- Create the Utility synonyms. For details, see comments prior to "BEGIN UTILITY MDW STUB OBJECTS", -- above. We need to create the synonyms at this point so that we can create the Utility msdb wrapper -- views that reference the synonyms. This execution uses @force_stub_use=1 and @refresh_views=0; see -- the documentation for these parameters in the proc header. EXEC msdb.dbo.sp_sysutility_ucp_initialize_mdw @mdw_database_name = 'sysutility_mdw', @require_mdw = 0, @force_stub_use = 1, @refresh_views = 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' EXEC [msdb].[dbo].[sp_sysutility_mi_get_dac_execution_statistics_internal]; sysutility_ucp_dac_collected_execution_statistics_internal -- 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 sysutility_ucp_cpu_memory_configurations_internal -- 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] sysutility_ucp_volumes_internal -- 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] sysutility_ucp_smo_properties_internal -- 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 sysutility_ucp_batch_manifests_internal '); 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 : 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. */ /* */ /* Note that this function calls xp_qv, which requires that the */ /* 'AgentXPs' sp_configure value be enabled. During upgrade this */ /* setting will need to be manually enabled, since upgrade scripts */ /* are executed while Agent is stopped. */ /**********************************************************************/ /* 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 dacs.dac_id, -- todo (VSTS #345036): This column will be removed dacs.dac_name, dacs.dac_deploy_date AS dac_deployed_date, dacs.dac_description AS dac_description, dacs.dac_percent_total_cpu_utilization AS dac_percent_total_cpu_utilization, dacs.server_instance_name AS dac_server_instance_name, dacs.physical_server_name AS dac_physical_server_name, dacs.batch_time AS dac_collection_time, dacs.processing_time AS dac_processing_time, dacs.urn, dacs.powershell_path FROM dbo.syn_sysutility_ucp_dacs as dacs --- The join operator removes those DACs in the managed instances which are unenrolled during --- the time between two consecutive data collection. --- See VSTS #473462 for more information INNER JOIN dbo.sysutility_ucp_managed_instances as mis ON dacs.server_instance_name = mis.instance_name; 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 , powershell_path , 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 , S.state 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_instance_policies -- View Description: -- Returns the resource health policy details for the managed instances -- Powershell path - SQLSERVER:\SQL\ -- smo_server_urn - Server[@Name=''] -- utility_server_urn - Utility[@Name='']/Server[@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\\Computers\ -- computer_urn - Utility[@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\\DeployedDacs\. -- dac_urn - Utility[@Name='']/DeployedDac[@Name='' and @ServerInstanceName=''] --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 , CASE WHEN (is_volume_space_over_utilized > 0) THEN CONVERT(BIT, 1) ELSE CONVERT(BIT, 0) END AS contains_over_utilized_volumes , CASE WHEN (is_volume_space_under_utilized > 0) THEN CONVERT(BIT, 1) ELSE CONVERT(BIT, 0) END AS contains_under_utilized_volumes , CASE WHEN (is_file_space_over_utilized > 0) THEN CONVERT(BIT, 1) ELSE CONVERT(BIT, 0) END AS contains_over_utilized_filegroups , CASE WHEN (is_file_space_under_utilized > 0) THEN CONVERT(BIT, 1) ELSE CONVERT(BIT, 0) END AS contains_under_utilized_filegroups , 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 , CASE WHEN (is_volume_space_over_utilized > 0) THEN CONVERT(BIT, 1) ELSE CONVERT(BIT, 0) END AS contains_over_utilized_volumes , CASE WHEN (is_volume_space_under_utilized > 0) THEN CONVERT(BIT, 1) ELSE CONVERT(BIT, 0) END AS contains_under_utilized_volumes , CASE WHEN (is_file_space_over_utilized > 0) THEN CONVERT(BIT, 1) ELSE CONVERT(BIT, 0) END AS contains_over_utilized_databases , CASE WHEN (is_file_space_under_utilized > 0) THEN CONVERT(BIT, 1) ELSE CONVERT(BIT, 0) END AS contains_under_utilized_databases , 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 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_volumes 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. -- Cache view data into temp table SELECT * INTO #computer_policies FROM dbo.sysutility_ucp_computer_policies -- Get the computer cpu utilization based on processor violating the health policy -- Mark the computer as unhealthy if processor violate the policy 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 INTO #computer_cpu_utilization FROM #computer_policies cp INNER JOIN 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 -- processor_resource_type AND cp.target_type = 1 -- computer_target_type GROUP BY cp.physical_server_name -- 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 #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; -- Cache view data into temp table SELECT * INTO #dac_policies FROM dbo.sysutility_ucp_dac_policies -- Get the database cpu utilization based on processor violating the health policy -- Mark the database as unhealthy if processor violate the policy 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 INTO #dac_cpu_utilizations FROM #dac_policies AS dp INNER JOIN 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 -- processor_resource_type AND dp.target_type = 5 -- database_target_type GROUP BY dp.dac_name, dp.dac_server_instance_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 #dac_cpu_utilizations 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; -- Cache view data into temp table SELECT * INTO #instance_policies FROM dbo.sysutility_ucp_instance_policies -- Get the MI cpu utilization based on processor violating the health policy -- Mark the instance as unhealthy if processor violate the policy 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 INTO #instance_cpu_utilization FROM #instance_policies ip INNER JOIN 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 -- processor_resource_type AND ip.target_type = 4 -- instance_target_type GROUP BY ip.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 #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' Bool LE 2 Numeric ProcessorUtilization Numeric System.Double 70 ', @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' Bool GT 2 Numeric ProcessorUtilization Numeric System.Double 70 ', @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' Bool GE 2 Numeric ProcessorUtilization Numeric System.Double 0 ', @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' Bool LT 2 Numeric ProcessorUtilization Numeric System.Double 0 ', @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' Bool LE 2 Numeric SpaceUtilization Numeric System.Double 70 ', @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' Bool GE 2 Numeric SpaceUtilization Numeric System.Double 0 ', @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' Bool LE 2 Numeric SpaceUtilization Numeric System.Double 70 ', @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' Bool GE 2 Numeric SpaceUtilization Numeric System.Double 0 ', @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' Bool LE 2 Numeric SpaceUtilization Numeric System.Double 70 ', @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' Bool GE 2 Numeric SpaceUtilization Numeric System.Double 0 ', @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' Bool LE 2 Numeric SpaceUtilization Numeric System.Double 70 ', @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' Bool GE 2 Numeric SpaceUtilization Numeric System.Double 0 ', @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' Bool LE 2 Numeric ProcessorUsage Numeric System.Double 70 ', @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' Bool GT 2 Numeric ProcessorUsage Numeric System.Double 70 ', @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' Bool GE 2 Numeric ProcessorUsage Numeric System.Double 0 ', @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' Bool LT 2 Numeric ProcessorUsage Numeric System.Double 0 ', @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' Bool LE 2 Numeric ProcessorUtilization Numeric System.Double 70 ', @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' Bool GT 2 Numeric ProcessorUtilization Numeric System.Double 70 ', @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' Bool GE 2 Numeric ProcessorUtilization Numeric System.Double 0 ', @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' Bool LT 2 Numeric ProcessorUtilization Numeric System.Double 0 ', @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' Bool LE 2 Numeric TotalSpaceUtilization Numeric System.Double 70 ', @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' Bool GT 2 Numeric TotalSpaceUtilization Numeric System.Double 70 ', @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' Bool GE 2 Numeric TotalSpaceUtilization Numeric System.Double 0 ', @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' Bool LT 2 Numeric TotalSpaceUtilization Numeric System.Double 0 ', @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 -- Delete the health records of DACs in the removed instance. DELETE FROM msdb.dbo.sysutility_ucp_dac_health_internal WHERE dac_server_instance_name = @instance_name -- Re-compute the DAC health stats in the dashboard DELETE FROM msdb.dbo.sysutility_ucp_aggregated_dac_health_internal WHERE set_number = @health_state_id EXEC msdb.dbo.sp_sysutility_ucp_calculate_aggregated_dac_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'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_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 -- Redirect the Utility msdb wrapper views from the msdb stub tables to the sysutility_mdw tables, if this -- instance is a UCP. We tell the proc not to refresh the msdb views (yet), though. Because setup upgrades -- the sysutility_mdw database after msdb, when this script is executed the MDW tables might not yet have all -- of the columns that are required by the upgraded msdb views. Refeshing them in this partially-upgraded -- state would cause an error. This procedure will be executed again after instmdw.sql has been run and -- both MDW and msdb have been upgraded. At that time, the views will be refreshed. EXEC msdb.dbo.sp_sysutility_ucp_initialize_mdw @mdw_database_name = 'sysutility_mdw', @require_mdw = 0, @refresh_views = 0; 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 */ /**************************************************************/ /**********************************************************************/ /* OBD.SQL */ /* */ /* Signs all the SPs that will be added to the */ /* Off By Default component. */ /* TODO: Split this too based on each component. */ /* */ /* */ /* Copyright (c) Microsoft Corporation */ /* All Rights Reserved. */ /* */ /**********************************************************************/ /**************************************************************/ /* 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) -- SSIS 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 /**************************************************************/ /* Alwayson built-in policies */ /**************************************************************/ SET NOCOUNT ON PRINT '' PRINT '--------------------------------' PRINT 'Starting execution of Alwayson.SQL' PRINT '--------------------------------' go Begin Declare @alwayson_target_set_id int Declare @alwayson_policy_helptext NVARCHAR(1024) Declare @alwayson_policy_description NVARCHAR(max) Declare @condition_name NVARCHAR(1024) Declare @expression NVARCHAR(max) Declare @alwayson_policy_helptext_prefix NVARCHAR(100) Declare @alwayson_policy_description_prefix NVARCHAR(100) select @alwayson_policy_helptext_prefix = 'Alwayson Policy Helptext ID:' select @alwayson_policy_description_prefix = 'Alwayson Policy Description ID:' ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Delete existing Policies,objectSets -- NOTE: -- We shall not delete condition and category because customer may have dependency on condition, especially on category. -- so if we delete the category, the upgrade will be broken. -- instead we shall use add_condition/add_category if the condition/category does not exist. -- if they exist, use update. -- For Denali, the updating category does not really do anything. But for the future if we want to change the category name, that will be useful -- the condition update will be very useful if we decide to change the expression of the condition. -- the policy does not have any dependency, so "delete and add" are fine. -- object set is also fine because it's already referred by our policy, user-defined policy can not refer to it. -- -- one thing here is if we upgrade from Denali CTP3 (SSS) to Denali CTP3 refresh (MS) or later, the old policy/condition/category will be left ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Deleting existing policies, objectsets...' IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnArJoinStateHealthPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnArJoinStateHealthPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnArJoinStateHealthPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnArJoinStateHealthPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnArJoinStateHealthPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnArJoinStateHealthPolicy_ObjectSet' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnAgReplicasConnectionHealthPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgReplicasConnectionHealthPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnAgReplicasConnectionHealthPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnAgReplicasConnectionHealthPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgReplicasConnectionHealthPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnAgReplicasConnectionHealthPolicy_ObjectSet' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnAgReplicasDataSynchronizationHealthPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgReplicasDataSynchronizationHealthPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnAgReplicasDataSynchronizationHealthPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnAgReplicasDataSynchronizationHealthPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgReplicasDataSynchronizationHealthPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnAgReplicasDataSynchronizationHealthPolicy_ObjectSet' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnAgReplicasRoleHealthPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgReplicasRoleHealthPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnAgReplicasRoleHealthPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnAgReplicasRoleHealthPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgReplicasRoleHealthPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnAgReplicasRoleHealthPolicy_ObjectSet' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy_ObjectSet' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnDbrJoinStatePolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnDbrJoinStatePolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnDbrJoinStatePolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnDbrJoinStatePolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnDbrJoinStatePolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnDbrJoinStatePolicy_ObjectSet' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnDbrSuspendStatePolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnDbrSuspendStatePolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnDbrSuspendStatePolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnDbrSuspendStatePolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnDbrSuspendStatePolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnDbrSuspendStatePolicy_ObjectSet' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnAgOnlineStateHealthPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgOnlineStateHealthPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnAgOnlineStateHealthPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnAgOnlineStateHealthPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgOnlineStateHealthPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnAgOnlineStateHealthPolicy_ObjectSet' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnAgAutomaticFailoverHealthPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgAutomaticFailoverHealthPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnAgAutomaticFailoverHealthPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnAgAutomaticFailoverHealthPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgAutomaticFailoverHealthPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnAgAutomaticFailoverHealthPolicy_ObjectSet' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnArConnectionHealthPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnArConnectionHealthPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnArConnectionHealthPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnArConnectionHealthPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnArConnectionHealthPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnArConnectionHealthPolicy_ObjectSet' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnDbrDataSynchronizationState') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnDbrDataSynchronizationState', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnDbrDataSynchronizationState' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnDbrDataSynchronizationState_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnDbrDataSynchronizationState_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnDbrDataSynchronizationState_ObjectSet' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnArDataSynchronizationHealthPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnArDataSynchronizationHealthPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnArDataSynchronizationHealthPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnArDataSynchronizationHealthPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnArDataSynchronizationHealthPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnArDataSynchronizationHealthPolicy_ObjectSet' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnArRoleHealthPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnArRoleHealthPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnArRoleHealthPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnArRoleHealthPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnArRoleHealthPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnArRoleHealthPolicy_ObjectSet' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'AlwaysOnAgWSFClusterHealthPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgWSFClusterHealthPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'AlwaysOnAgWSFClusterHealthPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'AlwaysOnAgWSFClusterHealthPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgWSFClusterHealthPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'AlwaysOnAgWSFClusterHealthPolicy_ObjectSet' END ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Category: Availability group errors (any replica role) ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating category: Availability group errors (any replica role)...' IF EXISTS(SELECT policy_category_id FROM msdb.dbo.syspolicy_policy_categories WHERE name=N'Availability group errors (any replica role)') BEGIN EXEC msdb.dbo.sp_syspolicy_rename_policy_category @name=N'Availability group errors (any replica role)', @new_name=N'Availability group errors (any replica role)' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_policy_category @name=N'Availability group errors (any replica role)', @policy_category_id=0 END ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Category: Availability group errors (primary replica only) ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating category: Availability group errors (primary replica only)...' IF EXISTS(SELECT policy_category_id FROM msdb.dbo.syspolicy_policy_categories WHERE name=N'Availability group errors (primary replica only)') BEGIN EXEC msdb.dbo.sp_syspolicy_rename_policy_category @name=N'Availability group errors (primary replica only)', @new_name=N'Availability group errors (primary replica only)' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_policy_category @name=N'Availability group errors (primary replica only)', @policy_category_id=0 END ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Category: Availability replica errors ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating category: Availability replica errors...' IF EXISTS(SELECT policy_category_id FROM msdb.dbo.syspolicy_policy_categories WHERE name=N'Availability replica errors') BEGIN EXEC msdb.dbo.sp_syspolicy_rename_policy_category @name=N'Availability replica errors', @new_name=N'Availability replica errors' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_policy_category @name=N'Availability replica errors', @policy_category_id=0 END ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Category: Availability database errors ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating category: Availability database errors...' IF EXISTS(SELECT policy_category_id FROM msdb.dbo.syspolicy_policy_categories WHERE name=N'Availability database errors') BEGIN EXEC msdb.dbo.sp_syspolicy_rename_policy_category @name=N'Availability database errors', @new_name=N'Availability database errors' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_policy_category @name=N'Availability database errors', @policy_category_id=0 END ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Category: Availability group warnings (any replica role) ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating category: Availability group warnings (any replica role)...' IF EXISTS(SELECT policy_category_id FROM msdb.dbo.syspolicy_policy_categories WHERE name=N'Availability group warnings (any replica role)') BEGIN EXEC msdb.dbo.sp_syspolicy_rename_policy_category @name=N'Availability group warnings (any replica role)', @new_name=N'Availability group warnings (any replica role)' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_policy_category @name=N'Availability group warnings (any replica role)', @policy_category_id=0 END ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Category: Availability group warnings (primary replica only) ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating category: Availability group warnings (primary replica only)...' IF EXISTS(SELECT policy_category_id FROM msdb.dbo.syspolicy_policy_categories WHERE name=N'Availability group warnings (primary replica only)') BEGIN EXEC msdb.dbo.sp_syspolicy_rename_policy_category @name=N'Availability group warnings (primary replica only)', @new_name=N'Availability group warnings (primary replica only)' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_policy_category @name=N'Availability group warnings (primary replica only)', @policy_category_id=0 END ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Category: Availability replica warnings ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating category: Availability replica warnings...' IF EXISTS(SELECT policy_category_id FROM msdb.dbo.syspolicy_policy_categories WHERE name=N'Availability replica warnings') BEGIN EXEC msdb.dbo.sp_syspolicy_rename_policy_category @name=N'Availability replica warnings', @new_name=N'Availability replica warnings' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_policy_category @name=N'Availability replica warnings', @policy_category_id=0 END ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Category: Availability database warnings ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating category: Availability database warnings...' IF EXISTS(SELECT policy_category_id FROM msdb.dbo.syspolicy_policy_categories WHERE name=N'Availability database warnings') BEGIN EXEC msdb.dbo.sp_syspolicy_rename_policy_category @name=N'Availability database warnings', @new_name=N'Availability database warnings' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_policy_category @name=N'Availability database warnings', @policy_category_id=0 END ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnAgAutomaticFailoverHealthCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnAgAutomaticFailoverHealthCondition...' set @condition_name = N'AlwaysOnAgAutomaticFailoverHealthCondition' set @expression = N' Bool OR 2 Bool 1 Bool AND 2 Bool EQ 2 Bool IsAutoFailover Bool True Bool 0 Bool GT 2 Numeric NumberOfSynchronizedSecondaryReplicas Numeric System.Double 0 Bool EQ 2 Bool IsAutoFailover Bool False Bool 0 ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'IAvailabilityGroupState', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'IAvailabilityGroupState', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnDbrJoinStateCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnDbrJoinStateCondition...' set @condition_name = N'AlwaysOnDbrJoinStateCondition' set @expression = N' Bool EQ 2 Bool IsJoined Bool True Bool 0 ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'DatabaseReplicaState', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'DatabaseReplicaState', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnAgWSFClusterHealthCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnAgWSFClusterHealthCondition...' set @condition_name = N'AlwaysOnAgWSFClusterHealthCondition' set @expression = N' Bool EQ 2 Numeric ClusterQuorumState Numeric Enum Numeric 2 String System.String Microsoft.SqlServer.Management.Smo.ClusterQuorumState String System.String NormalQuorum ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'Server', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'Server', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnArConnectionHealthCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnArConnectionHealthCondition...' set @condition_name = N'AlwaysOnArConnectionHealthCondition' set @expression = N' Bool EQ 2 Numeric ConnectionState Numeric Enum Numeric 2 String System.String Microsoft.SqlServer.Management.Smo.AvailabilityReplicaConnectionState String System.String Connected ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'AvailabilityReplica', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'AvailabilityReplica', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnAgReplicasConnectionHealthCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnAgReplicasConnectionHealthCondition...' set @condition_name = N'AlwaysOnAgReplicasConnectionHealthCondition' set @expression = N' Bool EQ 2 Numeric NumberOfDisconnectedReplicas Numeric System.Double 0 ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'IAvailabilityGroupState', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'IAvailabilityGroupState', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnAgReplicasDataSynchronizationHealthCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnAgReplicasDataSynchronizationHealthCondition...' set @condition_name = N'AlwaysOnAgReplicasDataSynchronizationHealthCondition' set @expression = N' Bool EQ 2 Numeric NumberOfNotSynchronizingReplicas Numeric System.Double 0 ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'IAvailabilityGroupState', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'IAvailabilityGroupState', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnAgReplicasRoleHealthCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnAgReplicasRoleHealthCondition...' set @condition_name = N'AlwaysOnAgReplicasRoleHealthCondition' set @expression = N' Bool EQ 2 Numeric NumberOfReplicasWithUnhealthyRole Numeric System.Double 0 ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'IAvailabilityGroupState', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'IAvailabilityGroupState', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnAgSynchronousReplicasDataSynchronizationHealthCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnAgSynchronousReplicasDataSynchronizationHealthCondition...' set @condition_name = N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthCondition' set @expression = N' Bool EQ 2 Numeric NumberOfNotSynchronizedReplicas Numeric System.Double 0 ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'IAvailabilityGroupState', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'IAvailabilityGroupState', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnDbrSuspendStateCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnDbrSuspendStateCondition...' set @condition_name = N'AlwaysOnDbrSuspendStateCondition' set @expression = N' Bool EQ 2 Bool IsSuspended Bool False Bool 0 ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'DatabaseReplicaState', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'DatabaseReplicaState', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnDbrDataSynchronizationCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnDbrDataSynchronizationCondition...' set @condition_name = N'AlwaysOnDbrDataSynchronizationCondition' set @expression = N' Bool 1 Bool OR 2 Bool 1 Bool AND 2 Bool EQ 2 Numeric ReplicaAvailabilityMode Numeric Enum Numeric 2 String System.String Microsoft.SqlServer.Management.Smo.AvailabilityReplicaAvailabilityMode String System.String AsynchronousCommit Bool NE 2 Numeric SynchronizationState Numeric Enum Numeric 2 String System.String Microsoft.SqlServer.Management.Smo.AvailabilityDatabaseSynchronizationState String System.String NotSynchronizing Bool EQ 2 Numeric SynchronizationState Numeric Enum Numeric 2 String System.String Microsoft.SqlServer.Management.Smo.AvailabilityDatabaseSynchronizationState String System.String Synchronized ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'DatabaseReplicaState', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'DatabaseReplicaState', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: IsHadrEnabled ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: IsHadrEnabled...' set @condition_name = N'IsHadrEnabled' set @expression = N' Bool EQ 2 Bool IsHadrEnabled Bool True Bool 0 ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'Server', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'Server', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnAgOnlineStateHealthCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnAgOnlineStateHealthCondition...' set @condition_name = N'AlwaysOnAgOnlineStateHealthCondition' set @expression = N' Bool EQ 2 Bool IsOnline Bool True Bool 0 ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'IAvailabilityGroupState', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'IAvailabilityGroupState', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnArDataSynchronizationHealthCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnArDataSynchronizationHealthCondition...' set @condition_name = N'AlwaysOnArDataSynchronizationHealthCondition' set @expression = N' Bool 1 Bool OR 2 Bool 1 Bool AND 2 Bool EQ 2 Numeric AvailabilityMode Numeric Enum Numeric 2 String System.String Microsoft.SqlServer.Management.Smo.AvailabilityReplicaAvailabilityMode String System.String AsynchronousCommit Bool 1 Bool OR 2 Bool EQ 2 Numeric RollupSynchronizationState Numeric Enum Numeric 2 String System.String Microsoft.SqlServer.Management.Smo.AvailabilityReplicaRollupSynchronizationState String System.String Synchronizing Bool EQ 2 Numeric RollupSynchronizationState Numeric Enum Numeric 2 String System.String Microsoft.SqlServer.Management.Smo.AvailabilityReplicaRollupSynchronizationState String System.String Synchronized Bool EQ 2 Numeric RollupSynchronizationState Numeric Enum Numeric 2 String System.String Microsoft.SqlServer.Management.Smo.AvailabilityReplicaRollupSynchronizationState String System.String Synchronized ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'AvailabilityReplica', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'AvailabilityReplica', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnArRoleHealthCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnArRoleHealthCondition...' set @condition_name = N'AlwaysOnArRoleHealthCondition' set @expression = N' Bool OR 2 Bool EQ 2 Numeric Role Numeric Enum Numeric 2 String System.String Microsoft.SqlServer.Management.Smo.AvailabilityReplicaRole String System.String Primary Bool EQ 2 Numeric Role Numeric Enum Numeric 2 String System.String Microsoft.SqlServer.Management.Smo.AvailabilityReplicaRole String System.String Secondary ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'AvailabilityReplica', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'AvailabilityReplica', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Condition: AlwaysOnArJoinStateHealthCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Condition: AlwaysOnArJoinStateHealthCondition' set @condition_name = N'AlwaysOnArJoinStateHealthCondition' set @expression = N' Bool NE 2 Numeric JoinState Numeric Enum Numeric 2 String System.String Microsoft.SqlServer.Management.Smo.AvailabilityReplicaJoinState String System.String NotJoined ' IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=@condition_name) BEGIN EXEC msdb.dbo.sp_syspolicy_update_condition @name=@condition_name, @description=N'', @facet=N'AvailabilityReplica', @expression=@expression, @is_name_condition=0, @obj_name=N'' END ELSE BEGIN EXEC msdb.dbo.sp_syspolicy_add_condition @name=@condition_name, @description=N'', @facet=N'AvailabilityReplica', @expression=@expression, @is_name_condition=0, @obj_name=N'',@condition_id=0 END EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=@condition_name, @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Policy : AlwaysOnAgReplicasConnectionHealthPolicy ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnAgReplicasConnectionHealthPolicy...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41413' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41414' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnAgReplicasConnectionHealthPolicy_ObjectSet', @facet=N'IAvailabilityGroupState', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgReplicasConnectionHealthPolicy_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnAgReplicasConnectionHealthPolicy_ObjectSet', @type_skeleton=N'Server/AvailabilityGroup', @type=N'AVAILABILITYGROUP', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup', @level_name=N'AvailabilityGroup', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnAgReplicasConnectionHealthPolicy', @condition_name=N'AlwaysOnAgReplicasConnectionHealthCondition', @policy_category=N'Availability group warnings (primary replica only)', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'swb.agdashboard.agp7allconnected.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnAgReplicasConnectionHealthPolicy_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgReplicasConnectionHealthPolicy', @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Policy : AlwaysOnAgReplicasDataSynchronizationHealthPolicy ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnAgReplicasDataSynchronizationHealthPolicy...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41407' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41408' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnAgReplicasDataSynchronizationHealthPolicy_ObjectSet', @facet=N'IAvailabilityGroupState', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgReplicasDataSynchronizationHealthPolicy_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnAgReplicasDataSynchronizationHealthPolicy_ObjectSet', @type_skeleton=N'Server/AvailabilityGroup', @type=N'AVAILABILITYGROUP', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup', @level_name=N'AvailabilityGroup', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnAgReplicasDataSynchronizationHealthPolicy', @condition_name=N'AlwaysOnAgReplicasDataSynchronizationHealthCondition', @policy_category=N'Availability group warnings (primary replica only)', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'swb.agdashboard.agp4synchronizing.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnAgReplicasDataSynchronizationHealthPolicy_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgReplicasDataSynchronizationHealthPolicy', @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Policy : AlwaysOnAgReplicasRoleHealthPolicy ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnAgReplicasRoleHealthPolicy...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41411' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41412' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnAgReplicasRoleHealthPolicy_ObjectSet', @facet=N'IAvailabilityGroupState', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgReplicasRoleHealthPolicy_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnAgReplicasRoleHealthPolicy_ObjectSet', @type_skeleton=N'Server/AvailabilityGroup', @type=N'AVAILABILITYGROUP', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup', @level_name=N'AvailabilityGroup', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnAgReplicasRoleHealthPolicy', @condition_name=N'AlwaysOnAgReplicasRoleHealthCondition', @policy_category=N'Availability group warnings (primary replica only)', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'swb.agdashboard.agp6allroleshealthy.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnAgReplicasRoleHealthPolicy_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgReplicasRoleHealthPolicy', @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Policy : AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41409' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41410' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy_ObjectSet', @facet=N'IAvailabilityGroupState', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy_ObjectSet', @type_skeleton=N'Server/AvailabilityGroup', @type=N'AVAILABILITYGROUP', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup', @level_name=N'AvailabilityGroup', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy', @condition_name=N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthCondition', @policy_category=N'Availability group warnings (primary replica only)', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'swb.agdashboard.agp5synchronized.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgSynchronousReplicasDataSynchronizationHealthPolicy', @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Policy : AlwaysOnDbrJoinStatePolicy ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnDbrJoinStatePolicy...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41423' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41424' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnDbrJoinStatePolicy_ObjectSet', @facet=N'DatabaseReplicaState', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnDbrJoinStatePolicy_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnDbrJoinStatePolicy_ObjectSet', @type_skeleton=N'Server/AvailabilityGroup/DatabaseReplicaState', @type=N'DATABASEREPLICASTATE', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup/DatabaseReplicaState', @level_name=N'DatabaseReplicaState', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup', @level_name=N'AvailabilityGroup', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnDbrJoinStatePolicy', @condition_name=N'AlwaysOnDbrJoinStateCondition', @policy_category=N'Availability database warnings', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'swb.agdashboard.drp2joined.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnDbrJoinStatePolicy_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnDbrJoinStatePolicy', @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- --Policy : AlwaysOnDbrSuspendStatePolicy ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnDbrSuspendStatePolicy...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41421' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41422' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnDbrSuspendStatePolicy_ObjectSet', @facet=N'DatabaseReplicaState', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnDbrSuspendStatePolicy_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnDbrSuspendStatePolicy_ObjectSet', @type_skeleton=N'Server/AvailabilityGroup/DatabaseReplicaState', @type=N'DATABASEREPLICASTATE', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup/DatabaseReplicaState', @level_name=N'DatabaseReplicaState', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup', @level_name=N'AvailabilityGroup', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnDbrSuspendStatePolicy', @condition_name=N'AlwaysOnDbrSuspendStateCondition', @policy_category=N'Availability database warnings', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'swb.agdashboard.drp1notsuspended.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnDbrSuspendStatePolicy_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnDbrSuspendStatePolicy', @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- --Policy : AlwaysOnAgOnlineStateHealthPolicy ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnAgOnlineStateHealthPolicy...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41403' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41404' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnAgOnlineStateHealthPolicy_ObjectSet', @facet=N'IAvailabilityGroupState', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgOnlineStateHealthPolicy_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnAgOnlineStateHealthPolicy_ObjectSet', @type_skeleton=N'Server/AvailabilityGroup', @type=N'AVAILABILITYGROUP', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup', @level_name=N'AvailabilityGroup', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnAgOnlineStateHealthPolicy', @condition_name=N'AlwaysOnAgOnlineStateHealthCondition', @policy_category=N'Availability group errors (any replica role)', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'swb.agdashboard.agp2online.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnAgOnlineStateHealthPolicy_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgOnlineStateHealthPolicy', @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- --Policy : AlwaysOnAgAutomaticFailoverHealthPolicy ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnAgAutomaticFailoverHealthPolicy...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41405' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41406' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnAgAutomaticFailoverHealthPolicy_ObjectSet', @facet=N'IAvailabilityGroupState', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgAutomaticFailoverHealthPolicy_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnAgAutomaticFailoverHealthPolicy_ObjectSet', @type_skeleton=N'Server/AvailabilityGroup', @type=N'AVAILABILITYGROUP', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup', @level_name=N'AvailabilityGroup', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnAgAutomaticFailoverHealthPolicy', @condition_name=N'AlwaysOnAgAutomaticFailoverHealthCondition', @policy_category=N'Availability group errors (primary replica only)', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'swb.agdashboard.agp3autofailover.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnAgAutomaticFailoverHealthPolicy_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgAutomaticFailoverHealthPolicy', @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- --Policy : AlwaysOnArConnectionHealthPolicy ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnArConnectionHealthPolicy...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41417' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41418' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnArConnectionHealthPolicy_ObjectSet', @facet=N'AvailabilityReplica', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnArConnectionHealthPolicy_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnArConnectionHealthPolicy_ObjectSet', @type_skeleton=N'Server/AvailabilityGroup/AvailabilityReplica', @type=N'AVAILABILITYREPLICA', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup/AvailabilityReplica', @level_name=N'AvailabilityReplica', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup', @level_name=N'AvailabilityGroup', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnArConnectionHealthPolicy', @condition_name=N'AlwaysOnArConnectionHealthCondition', @policy_category=N'Availability replica errors', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'swb.agdashboard.arp2connected.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnArConnectionHealthPolicy_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnArConnectionHealthPolicy', @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- --Policy : AlwaysOnDbrDataSynchronizationState ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnDbrDataSynchronizationState...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41425' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41426' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnDbrDataSynchronizationState_ObjectSet', @facet=N'DatabaseReplicaState', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnDbrDataSynchronizationState_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnDbrDataSynchronizationState_ObjectSet', @type_skeleton=N'Server/AvailabilityGroup/DatabaseReplicaState', @type=N'DATABASEREPLICASTATE', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup/DatabaseReplicaState', @level_name=N'DatabaseReplicaState', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup', @level_name=N'AvailabilityGroup', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnDbrDataSynchronizationState', @condition_name=N'AlwaysOnDbrDataSynchronizationCondition', @policy_category=N'Availability database warnings', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'swb.agdashboard.drp3datasynchealthy.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnDbrDataSynchronizationState_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnDbrDataSynchronizationState', @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- --Policy : AlwaysOnArDataSynchronizationHealthPolicy ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnArDataSynchronizationHealthPolicy...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41419' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41420' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnArDataSynchronizationHealthPolicy_ObjectSet', @facet=N'AvailabilityReplica', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnArDataSynchronizationHealthPolicy_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnArDataSynchronizationHealthPolicy_ObjectSet', @type_skeleton=N'Server/AvailabilityGroup/AvailabilityReplica', @type=N'AVAILABILITYREPLICA', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup/AvailabilityReplica', @level_name=N'AvailabilityReplica', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup', @level_name=N'AvailabilityGroup', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnArDataSynchronizationHealthPolicy', @condition_name=N'AlwaysOnArDataSynchronizationHealthCondition', @policy_category=N'Availability replica warnings', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'swb.agdashboard.arp3datasynchealthy.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnArDataSynchronizationHealthPolicy_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnArDataSynchronizationHealthPolicy', @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- --Policy : AlwaysOnArRoleHealthPolicy ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnArRoleHealthPolicy...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41415' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41416' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnArRoleHealthPolicy_ObjectSet', @facet=N'AvailabilityReplica', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnArRoleHealthPolicy_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnArRoleHealthPolicy_ObjectSet', @type_skeleton=N'Server/AvailabilityGroup/AvailabilityReplica', @type=N'AVAILABILITYREPLICA', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup/AvailabilityReplica', @level_name=N'AvailabilityReplica', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup', @level_name=N'AvailabilityGroup', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnArRoleHealthPolicy', @condition_name=N'AlwaysOnArRoleHealthCondition', @policy_category=N'Availability replica errors', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'swb.agdashboard.arp1rolehealthy.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnArRoleHealthPolicy_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnArRoleHealthPolicy', @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- --Policy : AlwaysOnAgWSFClusterHealthPolicy ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnAgWSFClusterHealthPolicy...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41401' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41402' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnAgWSFClusterHealthPolicy_ObjectSet', @facet=N'Server', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnAgWSFClusterHealthPolicy_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnAgWSFClusterHealthPolicy_ObjectSet', @type_skeleton=N'Server', @type=N'SERVER', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnAgWSFClusterHealthPolicy', @condition_name=N'AlwaysOnAgWSFClusterHealthCondition', @policy_category=N'Availability group errors (any replica role)', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'swb.agdashboard.agp1WSFCquorum.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnAgWSFClusterHealthPolicy_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnAgWSFClusterHealthPolicy', @marker=1 ------------------------------------------------------------------------------------------------------------------------------------------------------------- --Policy : AlwaysOnArJoinStateHealthPolicy ------------------------------------------------------------------------------------------------------------------------------------------------------------- PRINT '' PRINT 'Creating Policy: AlwaysOnArJoinStateHealthPolicy...' select @alwayson_policy_helptext = @alwayson_policy_helptext_prefix + '41427' select @alwayson_policy_description = @alwayson_policy_description_prefix + '41428' EXEC msdb.dbo.sp_syspolicy_add_object_set @object_set_name=N'AlwaysOnArJoinStateHealthPolicy_ObjectSet', @facet=N'AvailabilityReplica', @object_set_id=0 EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'AlwaysOnArJoinStateHealthPolicy_ObjectSet', @marker=1 EXEC msdb.dbo.sp_syspolicy_add_target_set @object_set_name=N'AlwaysOnArJoinStateHealthPolicy_ObjectSet', @type_skeleton=N'Server/AvailabilityGroup/AvailabilityReplica', @type=N'AVAILABILITYREPLICA', @enabled=True, @target_set_id=@alwayson_target_set_id OUTPUT EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup/AvailabilityReplica', @level_name=N'AvailabilityReplica', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_target_set_level @target_set_id=@alwayson_target_set_id, @type_skeleton=N'Server/AvailabilityGroup', @level_name=N'AvailabilityGroup', @condition_name=N'', @target_set_level_id=0 EXEC msdb.dbo.sp_syspolicy_add_policy @name=N'AlwaysOnArJoinStateHealthPolicy', @condition_name=N'AlwaysOnArJoinStateHealthCondition', @policy_category=N'Availability replica warnings', @description=@alwayson_policy_description, @help_text=@alwayson_policy_helptext, @help_link=N'sql11.swb.agdashboard.arp4joinstate.issues.f1', @schedule_uid=N'00000000-0000-0000-0000-000000000000', @execution_mode=0, @is_enabled=False, @policy_id=0, @root_condition_name=N'IsHadrEnabled', @object_set=N'AlwaysOnArJoinStateHealthPolicy_ObjectSet' EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'AlwaysOnArJoinStateHealthPolicy', @marker=1 End /**********************************************************************/ /* MSDB_SECURITY.SQL */ /* */ /* This is run after all the components are installed in MSDB */ /* It will set up the security and certificates. */ /* TODO: Split this too based on each component. */ /* */ /* */ /* Copyright (c) Microsoft Corporation */ /* All Rights Reserved. */ /* */ /**********************************************************************/ PRINT '' PRINT '-------------------------------------------' PRINT 'Starting execution of MSDB_POST_INSTALL.SQL' PRINT '-------------------------------------------' 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 --create a temporary database in order to get the path to the 'Data' folder IF (EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE (name = N'temp_MS_AgentSigningCertificate_database'))) BEGIN DROP DATABASE temp_MS_AgentSigningCertificate_database END CREATE DATABASE temp_MS_AgentSigningCertificate_database -- Note: master.dbo.sysaltfiles's filename column is 260 nvarchars DECLARE @dataDirName NVARCHAR(520) SELECT @dataDirName = SUBSTRING(filename, 1, CHARINDEX(N'temp_MS_AgentSigningCertificate_database.mdf', filename) - 1) FROM master.dbo.sysaltfiles WHERE (name = N'temp_MS_AgentSigningCertificate_database') if (@dataDirName is null) RAISERROR('Cannot deduce path for temporary certificate ##MS_AgentSigningCertificate##. Was temp_MS_AgentSigningCertificate_database not created? 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' -- drop temporary database IF (EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE (name = N'temp_MS_AgentSigningCertificate_database'))) BEGIN DROP DATABASE temp_MS_AgentSigningCertificate_database END 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''' + @sp + N'''' else if (@obdComponent = 2) -- DbMail set @exec_str = N'exec sp_AddFunctionalUnitToComponent N''Database Mail XPs'', N''' + @sp + N'''' 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 MSDB_POST_INSTALL.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 --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 '------------------------------------------' PRINT 'Execution of POST_SQLAGENT100.SQL complete' PRINT '------------------------------------------' 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 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 */ /**************************************************************/ GO -- Procedure to print Data collector status and collection set status CREATE PROCEDURE #printdatacollectorstatus AS BEGIN DECLARE @parameter_name SYSNAME DECLARE @parameter_value sql_variant PRINT 'Data Collector Status' SELECT @parameter_name = parameter_name, @parameter_value = parameter_value FROM msdb.dbo.syscollector_config_store_internal WHERE parameter_name = 'CollectorEnabled' PRINT @parameter_name + ':' + convert(varchar , @parameter_value) PRINT 'Collection set status' DECLARE @collection_set_uid UNIQUEIDENTIFIER DECLARE @name SYSNAME DECLARE @is_running BIT DECLARE @collection_set_cursor CURSOR SET @collection_set_cursor = CURSOR FOR SELECT collection_set_uid, name, is_running FROM msdb.dbo.syscollector_collection_sets_internal OPEN @collection_set_cursor FETCH NEXT FROM @collection_set_cursor INTO @collection_set_uid, @name, @is_running WHILE @@FETCH_STATUS = 0 BEGIN PRINT 'Uid:' + convert(varchar(50),@collection_set_uid) + ', Name:' + @name + ', IsRunning:' + convert(varchar, @is_running) FETCH NEXT FROM @collection_set_cursor INTO @collection_set_uid, @name, @is_running END CLOSE @collection_set_cursor DEALLOCATE @collection_set_cursor END GO -- 'Agent XPs' setting should be turned on if not error is thrown while restoring -- data collector status PRINT 'post_dc100::Enabling Agent XPs before restoring data collector original state' DECLARE @advopt_old_value int DECLARE @comp_old_value int EXECUTE #sp_enable_component 'Agent XPs', @advopt_old_value out, @comp_old_value out EXECUTE #printdatacollectorstatus -- Load the instmdw.sql script into the blob PRINT 'post_dc100::uploading instmdw.sql to msdb ...' EXECUTE [dbo].[sp_syscollector_upload_instmdw] -- Restore collection set running status PRINT 'post_dc100::Checking if collection set status were captured in temp table...' IF (OBJECT_ID('tempdb..#data_collector_collectionset_status', 'U') IS NOT NULL) BEGIN PRINT 'post_dc100::Restoring collection set running status...' UPDATE [dbo].[syscollector_collection_sets_internal] SET [dbo].[syscollector_collection_sets_internal].is_running = #data_collector_collectionset_status.is_running FROM #data_collector_collectionset_status WHERE [dbo].[syscollector_collection_sets_internal].is_running <> #data_collector_collectionset_status.is_running AND [dbo].[syscollector_collection_sets_internal].collection_set_uid = #data_collector_collectionset_status.collection_set_uid END -- 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 'post_dc100::Checking if Data collector was enabled before upgrade...' IF (OBJECT_ID('tempdb..#data_collector_status', 'U') IS NOT NULL) BEGIN DECLARE @old_state_enabled_status INT -- Get the old state of data collector - predicate checks if the old and current state is not the same -- Expected: @old_state_enabled_status = NULL if old and current states are same -- @old_state_enabled_status = 0 if old state was disabled and current state is enabled -- @old_state_enabled_status = 1 if old state was enabled and current state is disabled SELECT @old_state_enabled_status = oldstatus.data_collector_old_status FROM [dbo].[syscollector_config_store_internal] cs, #data_collector_status oldstatus WHERE cs.parameter_name = 'CollectorEnabled' AND oldstatus.data_collector_old_status <> CONVERT(int, cs.parameter_value) -- @old_state_enabled_status is not null only if old and current states are not the same IF(@old_state_enabled_status IS NOT NULL) BEGIN -- If pre-upgrade state was disabled and current state is enabled, Restore to disabled state IF(@old_state_enabled_status = 0) BEGIN PRINT 'post_dc100::Data collector was disabled before upgrade, Restoring Disabled state...' EXEC sp_syscollector_disable_collector END -- If pre-upgrade state was enabled and current state is disables, enabled data collector ELSE IF(@old_state_enabled_status = 1) BEGIN PRINT 'post_dc100::Data collector was enabled before upgrade, Restoring Enabled State ...' EXEC sp_syscollector_enable_collector END ELSE -- If pre-upgrade state was not 1 or 0, print message ( we should not get into this issue ) BEGIN PRINT 'post_dc100::Invalid enable/disable state:' + convert(varchar, @old_state_enabled_status) END END ELSE BEGIN PRINT 'post_dc100::Current data collector state is same as pre-upgrade state' END END EXECUTE #printdatacollectorstatus -- 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 #printdatacollectorstatus 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 */ /**************************************************************/ /**************************************************************/ /* MSDB Post-upgrade steps */ /**************************************************************/ -- Restoring implicit transactions state DECLARE @is_implicit_transactions_set BIT SELECT @is_implicit_transactions_set = CONVERT(BIT, value) FROM fn_listextendedproperty(N'IMPLICIT_TRANSACTIONS', default, default, default, default, default, default); IF (@is_implicit_transactions_set IS NOT NULL) BEGIN EXEC sp_dropextendedproperty N'IMPLICIT_TRANSACTIONS' IF @is_implicit_transactions_set = 1 BEGIN SET IMPLICIT_TRANSACTIONS ON PRINT 'Restored implicit transactions state to ON' END ELSE BEGIN SET IMPLICIT_TRANSACTIONS OFF PRINT 'Restored implicit transactions state to OFF' END END 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 (c) Microsoft Corporation ** 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