MZ@ !L!This program cannot be run in DOS mode. $ Ydk7Udk7Udk7Udk6Urk7UCLUak7UCZUnk7UCMUek7UCYUgk7UCKUek7UCOUek7URichdk7UPEdၶK" &FOFzF@@ d<PF@= 20 -- Get callstack, SPID, and query for OOM errors ( 17803 , 701 , 802 , 8645 , 8651 , 8657 , 8902 ) or (error = 17803 or error = 701 or error = 802 or error = 8645 or error = 8651 or error = 8657 or error = 8902) ), add event sqlos.scheduler_monitor_non_yielding_ring_buffer_recorded, add event sqlserver.xml_deadlock_report, add event sqlos.wait_info ( action (package0.callstack, sqlserver.session_id, sqlserver.sql_text) where (duration > 15000 and ( (wait_type > 31 -- Waits for latches and important wait resources (not locks ) that have exceeded 15 seconds. and ( (wait_type > 47 and wait_type < 54) or wait_type < 38 or (wait_type > 63 and wait_type < 70) or (wait_type > 96 and wait_type < 100) or (wait_type = 107) or (wait_type = 113) or (wait_type > 174 and wait_type < 179) or (wait_type = 186) or (wait_type = 207) or (wait_type = 269) or (wait_type = 283) or (wait_type = 284) ) ) or (duration > 30000 -- Waits for locks that have exceeded 30 secs. and wait_type < 22 ) ) ) ), add event sqlos.wait_info_external ( action (package0.callstack, sqlserver.session_id, sqlserver.sql_text) where (duration > 5000 and ( ( -- Login related preemptive waits that have exceeded 5 seconds. (wait_type > 365 and wait_type < 372) or (wait_type > 372 and wait_type < 377) or (wait_type > 377 and wait_type < 383) or (wait_type > 420 and wait_type < 424) or (wait_type > 426 and wait_type < 432) or (wait_type > 432 and wait_type < 435) ) or (duration > 45000 -- Preemptive OS waits that have exceeded 45 seconds. and ( (wait_type > 382 and wait_type < 386) or (wait_type > 423 and wait_type < 427) or (wait_type > 434 and wait_type < 437) or (wait_type > 442 and wait_type < 451) or (wait_type > 451 and wait_type < 473) or (wait_type > 484 and wait_type < 499) or wait_type = 365 or wait_type = 372 or wait_type = 377 or wait_type = 387 or wait_type = 432 or wait_type = 502 ) ) ) ) ) add target package0.ring_buffer -- Store events in the ring buffer target (set max_memory = 4096) with (startup_state = on) go declare @vdt varchar(99) select @vdt = convert(varchar,getdate(),113) raiserror('Finishing at %s',0,1,@vdt) go checkpoint go PAD/**********************************************************************/ /* ProvisionAgentGroup.sql */ /* */ /* This script is called during setup to provision SQL Server Agent */ /* user group on SQL Server */ /* */ /* Copyright Microsoft, Inc. 1994 - 2008 */ /* All Rights Reserved. */ /* */ /**********************************************************************/ -- push and enable option state for xp_instance_regread in case categories are off DECLARE @advopt_old_value INT; DECLARE @comp_old_value INT; 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 = 'SMO and DMO XPs'; EXEC sp_configure 'show advanced options', 1; RECONFIGURE WITH OVERRIDE; EXEC sp_configure 'SMO and DMO XPs', 1; RECONFIGURE WITH OVERRIDE; DECLARE @AgentLoginSID nvarchar(256) -- SID of service account or service principal representing agent service DECLARE @AGTGroupSID nvarchar (256) -- Name of the agent group SID EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'AGTService', @AgentLoginSID OUTPUT, no_output EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'AGTGroup', @AGTGroupSID OUTPUT -- restore option state EXEC sp_configure 'Agent XPs', @comp_old_value; RECONFIGURE WITH OVERRIDE; EXEC sp_configure 'show advanced options', @advopt_old_value; RECONFIGURE WITH OVERRIDE; BEGIN IF (@AgentLoginSID IS NOT NULL) BEGIN --we get the login name from SID. Ensures that we always get the login name with correct casing --with respect to sql server collation. DECLARE @AgentLoginSIDBinary varbinary(256) DECLARE @AgentLoginName nvarchar(256) SELECT @AgentLoginSIDBinary = sid_binary(@AgentLoginSID); SELECT @AgentLoginName = suser_sname(@AgentLoginSIDBinary); IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = @AgentLoginName) BEGIN -- Create a new login. Standalone install or S/K upgrade scenario DECLARE @cmd1 nvarchar(max) SELECT @cmd1 = 'CREATE LOGIN ' + QUOTENAME(@AgentLoginName) + ' FROM WINDOWS' print 'Running command to create login: ' + @cmd1 EXEC (@cmd1) EXEC sys.sp_addsrvrolemember @AgentLoginName, N'sysadmin' END END IF (@AGTGroupSID IS NOT NULL) BEGIN --this is the case on upgrade. Basically, if a login already exists representing the AGTGroup, --if its current name on windows is different from what we have on SQL, we alter the name. Needed in Y->K upgrade, --in which we rename the agent local group DECLARE @AGTGroupSIDBinary varbinary(256) -- binary representation of AGTGroupSID DECLARE @AGTGroupName nvarchar (256) -- name of the group DECLARE @existing_name as sysname SELECT @AGTGroupSIDBinary = sid_binary(@AGTGroupSID); SELECT @AGTGroupName = suser_sname(@AGTGroupSIDBinary); SELECT @existing_name = name FROM sys.server_principals WHERE sid = suser_sid(@AGTGroupName); --note that if the login does not exist, we don't add it. IF (@existing_name IS NOT NULL) BEGIN IF (QUOTENAME(@existing_name) <> QUOTENAME(@AGTGroupName)) BEGIN -- Rename this login. Y/K upgrade scenario DECLARE @newcmd nvarchar(max) SELECT @newcmd = 'ALTER LOGIN ' + QUOTENAME(@existing_name) + ' WITH NAME = ' + QUOTENAME(@AGTGroupName) print 'Running command to rename login: ' + @newcmd EXEC (@newcmd) END END END END PA/**********************************************************************/ /* MSDB8TO9.SQL */ /* */ /* Upgrades 7.x, 8.x and 9.0 to 10.0 and drops all obsolete 8.x */ /* */ /* ** Copyright Microsoft, Inc. 1994 - 2008 ** All Rights Reserved. */ /**********************************************************************/ PRINT '----------------------------------------' PRINT 'Starting execution of PREINSTMSDB100.SQL' PRINT '----------------------------------------' use msdb go -- Check that we're in msdb IF (DB_NAME() <> N'msdb') RAISERROR('A problem was encountered accessing msdb. upgrade script terminating.', 20, 127) WITH LOG go CHECKPOINT go --set compatibily level to 100 sp_dbcmptlevel @dbname = 'msdb', @new_cmptlevel = '100' go -- Allow updates to system catalogs so that we can fully manipulate our system objects EXECUTE master.dbo.sp_configure N'allow updates', 1 go RECONFIGURE WITH OVERRIDE go /**************************************************************/ /* Record time of start of creates */ /**************************************************************/ SELECT start = getdate() INTO #InstMsdb go --preserve existing object permnission during upgrade --create perms table IF (NOT OBJECT_ID(N'dbo.upgrade_perms', 'U') IS NULL) BEGIN DROP TABLE dbo.upgrade_perms END -- upgrade_perms is filled with current permission of objects in MSDB -- the structure of table is: -- state_desc = GRANT|DENY -- permission_name = SELECT|EXECUTE|UPDATE ... -- object_name = grantor name -- grantee_name = grantee name CREATE TABLE dbo.upgrade_perms(state_desc nvarchar(60), permission_name sysname, object_name sysname, grantee_name sysname) CREATE INDEX indnc ON dbo.upgrade_perms(object_name) DECLARE @state_desc nvarchar(60) DECLARE @permission_name sysname DECLARE @object_name sysname DECLARE @grantee_name sysname DECLARE perms_cursor CURSOR LOCAL FOR SELECT state_desc, permission_name, OBJECT_NAME(major_id), USER_NAME(grantee_principal_id) from msdb.sys.database_permissions WHERE state_desc IS NOT NULL AND permission_name IS NOT NULL AND OBJECT_NAME(major_id) IS NOT NULL AND USER_NAME(grantee_principal_id) IS NOT NULL OPEN perms_cursor FETCH NEXT FROM perms_cursor INTO @state_desc, @permission_name, @object_name, @grantee_name WHILE (@@fetch_status = 0) BEGIN INSERT dbo.upgrade_perms(state_desc, permission_name, object_name, grantee_name) VALUES(@state_desc, @permission_name, @object_name, @grantee_name) FETCH NEXT FROM perms_cursor INTO @state_desc, @permission_name, @object_name, @grantee_name END DEALLOCATE perms_cursor go ------------------------VIEWS UPGRADE--------------------------------------- ------------------------TABLE UPGRADE--------------------------------------- --create an populate sysoriginatingservers use msdb go IF (NOT EXISTS (SELECT * --just a safe belt, this table shouldn't be in 8.x FROM msdb.dbo.sysobjects WHERE (name = N'sysoriginatingservers') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysoriginatingservers...' CREATE TABLE dbo.sysoriginatingservers ( -- There is only a single MSX server record in this table (originating_server_id = 1) -- 0 is generated by sysoriginatingservers_view and indicates the local server originating_server_id INT CONSTRAINT CK_originating_server_id_MustBe_1 CHECK (originating_server_id = 1) DEFAULT (1) UNIQUE CLUSTERED, originating_server sysname NOT NULL UNIQUE NONCLUSTERED, --Mark this record as a MSX server entry master_server bit CONSTRAINT CK_master_server_MustBe_1 CHECK (master_server = 1) DEFAULT (1) ) END go IF (NOT EXISTS (SELECT t.name FROM msdb.sys.all_columns c JOIN msdb.sys.all_objects t ON c.object_id = t.object_id WHERE c.name = 'originating_server_id' and t.name = 'sysjobs' and t.type = 'U')) BEGIN PRINT '' PRINT 'Adding column originating_server_id to table sysjobs...' --add new column 9.0 originating_server_id ALTER TABLE sysjobs WITH NOCHECK ADD originating_server_id INT NULL END go DECLARE @MSXServerName sysname DECLARE @LocalServerName sysname DECLARE @UpdateOrgServerTSQL nvarchar(MAX) SELECT @LocalServerName = UPPER(CONVERT(sysname, SERVERPROPERTY('servername'))) EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @MSXServerName OUTPUT, N'no_output' SELECT @MSXServerName = LTRIM(RTRIM(UPPER(@MSXServerName))) IF (@MSXServerName = '') SELECT @MSXServerName = NULL IF (@MSXServerName IS NOT NULL) BEGIN IF (NOT EXISTS( SELECT * FROM dbo.sysoriginatingservers WHERE originating_server_id = 1 AND originating_server = @MSXServerName AND master_server = 1)) BEGIN PRINT '' PRINT 'Populate table sysoriginatingservers...' INSERT INTO sysoriginatingservers( originating_server_id, originating_server, master_server) VALUES(1, @MSXServerName, 1) END END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobs') AND (type = 'U'))) BEGIN IF (EXISTS (SELECT t.name FROM msdb.sys.all_columns c JOIN msdb.sys.all_objects t ON c.object_id = t.object_id WHERE c.name = 'originating_server' and t.name = 'sysjobs' and t.type = 'U')) BEGIN PRINT '' PRINT 'Populate new column originating_server_id of table sysjobs...' --set this column based on the value of 8.0 only column originating_server --if MSX server is NULL we come up with server name that cannot exit, a generated GUID SELECT @UpdateOrgServerTSQL = ' UPDATE sysjobs SET originating_server_id = CASE UPPER(originating_server) WHEN ''' + @LocalServerName + ''' THEN 0 --local_server_id WHEN ''' + ISNULL(@MSXServerName, CONVERT(sysname, NEWID())) + ''' THEN 1 --msx_server_id ELSE 0 --7.0 (local) or bad data END ' EXECUTE( @UpdateOrgServerTSQL) PRINT '' PRINT 'Drop column originating_server of table sysjobs...' --drop 8.0 column originating_server DROP INDEX sysjobs.nc2 ALTER TABLE sysjobs DROP COLUMN originating_server END END go --normalize 8.0 sysjobschedules into 9.0 sysschedules and sysjobschedules IF NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysschedules') AND (type = 'U')) BEGIN --create first sysschedules table PRINT '' PRINT 'Creating table sysschedules...' CREATE TABLE dbo.sysschedules ( schedule_id INT IDENTITY PRIMARY KEY CLUSTERED, schedule_uid UNIQUEIDENTIFIER NOT NULL, originating_server_id INT NOT NULL, name sysname NOT NULL, owner_sid varbinary(85) NOT NULL, enabled INT NOT NULL, freq_type INT NOT NULL, freq_interval INT NOT NULL, freq_subday_type INT NOT NULL, freq_subday_interval INT NOT NULL, freq_relative_interval INT NOT NULL, freq_recurrence_factor INT NOT NULL, active_start_date INT NOT NULL, active_end_date INT NOT NULL, active_start_time INT NOT NULL, active_end_time INT NOT NULL, date_created DATETIME NOT NULL DEFAULT (GETDATE()), date_modified DATETIME NOT NULL DEFAULT (GETDATE()), version_number INT NOT NULL DEFAULT (1) ) -- CREATE UNIQUE CLUSTERED INDEX clust ON sysschedules(job_id, name) -- CREATE UNIQUE NONCLUSTERED INDEX nc1 ON sysschedules(schedule_id) END go --a system object cannot be renamed, turn off marking system object trace to alloe renaming of temp_sysjobschedules dbcc traceoff(1717, -1) go -- create temp cross 9.0 table temp_sysjobschedules IF EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'temp_sysjobschedules') AND (type = 'U')) BEGIN DROP TABLE dbo.temp_sysjobschedules END go PRINT '' PRINT 'Creating table temp_sysjobschedules' CREATE TABLE dbo.temp_sysjobschedules ( schedule_id INT REFERENCES dbo.sysschedules(schedule_id), job_id UNIQUEIDENTIFIER REFERENCES dbo.sysjobs(job_id), next_run_date INT NOT NULL DEFAULT 0, next_run_time INT NOT NULL DEFAULT 0 ) go DECLARE @dynamicSQL nvarchar(4000) IF (EXISTS (SELECT t.name FROM msdb.sys.all_columns c JOIN msdb.sys.all_objects t ON c.object_id = t.object_id WHERE c.name = 'name' and t.name = 'sysjobschedules' and t.type = 'U')) BEGIN PRINT '' PRINT 'Moving schedule data ...' SET IDENTITY_INSERT dbo.sysschedules ON SELECT @dynamicSQL = ' INSERT INTO dbo.sysschedules ( schedule_id , schedule_uid , originating_server_id , name , owner_sid , enabled , freq_type , freq_interval , freq_subday_type , freq_subday_interval , freq_relative_interval , freq_recurrence_factor , active_start_date , active_end_date , active_start_time , active_end_time , date_created ) SELECT js.schedule_id , NEWID() , 0 , --local server. TO DO make sure local server = 0 js.name , j.owner_sid , js.enabled , js.freq_type , js.freq_interval , js.freq_subday_type , js.freq_subday_interval , js.freq_relative_interval , js.freq_recurrence_factor , js.active_start_date , js.active_end_date , js.active_start_time , js.active_end_time , js.date_created FROM dbo.sysjobs j JOIN dbo.sysjobschedules js ON j.job_id = js.job_id INSERT INTO dbo.temp_sysjobschedules ( schedule_id , job_id , next_run_date , next_run_time ) SELECT js.schedule_id , js.job_id , js.next_run_date , js.next_run_time FROM dbo.sysjobs j JOIN dbo.sysjobschedules js ON j.job_id = js.job_id ' EXECUTE (@dynamicSQL) SET IDENTITY_INSERT dbo.sysschedules OFF IF (EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE (id = OBJECT_ID(N'sysjobschedules')) AND (name = N'date_created') AND (cdefault <> 0))) EXECUTE sp_unbindefault N'sysjobschedules.date_created' DROP TABLE dbo.sysjobschedules EXECUTE sp_rename 'temp_sysjobschedules', 'sysjobschedules' EXECUTE (N'CREATE UNIQUE CLUSTERED INDEX clust ON dbo.sysjobschedules(job_id, schedule_id)') PRINT '' PRINT 'Updating schedules done' END go --just a safe belt IF EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'temp_sysjobschedules') AND (type = 'U')) BEGIN DROP TABLE dbo.temp_sysjobschedules END go --alter only if command column is not already nvarchar(max) IF (NOT EXISTS (SELECT c.* FROM msdb.sys.all_columns c JOIN msdb.sys.all_objects t ON c.object_id = t.object_id WHERE (c.name = 'proxy_id' OR c.name = 'step_uid') AND t.name = 'sysjobsteps' AND t.type = 'U')) BEGIN PRINT '' PRINT 'Adding proxy_id, step_uid columns to sysjobsteps table' ALTER TABLE sysjobsteps ADD proxy_id INT NULL, step_uid UNIQUEIDENTIFIER NULL END go --rename DTS subsystem to SSIS IF (OBJECT_ID('dbo.sysjobsteps', 'U') IS NOT NULL) BEGIN UPDATE dbo.sysjobsteps SET subsystem = N'SSIS' WHERE UPPER(subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DTS' END go --to be safer populate sysjobsteps with guids, otherwise step table logs are not possible EXECUTE (N'UPDATE sysjobsteps SET step_uid = NEWID() WHERE step_uid IS NULL') go --if there is no index for step_uid, create it, so step table logs can reference it IF NOT EXISTS (SELECT name FROM sys.indexes WHERE name = N'nc2' and object_id = object_id(N'[dbo].[sysjobsteps]') ) BEGIN EXECUTE (N'CREATE UNIQUE NONCLUSTERED INDEX nc2 ON sysjobsteps(step_uid)') END go --alter sysdownloadlist table PRINT '' PRINT 'Alter table sysdownloadlist...' ALTER TABLE sysdownloadlist ALTER COLUMN source_server sysname ALTER TABLE sysdownloadlist ALTER COLUMN target_server sysname go --alter sysjobhistory table PRINT '' PRINT 'Alter table sysjobhistory...' ALTER TABLE sysjobhistory ALTER COLUMN server sysname go IF EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE name = N'sysjobhistory' and type = 'U') BEGIN UPDATE msdb.dbo.sysjobhistory SET server = CONVERT(sysname, SERVERPROPERTY('servername')) WHERE UPPER(server) = '(LOCAL)' END go --alter systargetservers table PRINT '' PRINT 'Alter table systargetservers...' ALTER TABLE systargetservers ALTER COLUMN server_name sysname go /**********************************************************************/ /* Saving the syssubsystems related user info */ /**********************************************************************/ -- Capture current state of syssubsystems.max_worker_threads in temp table PRINT 'Check if syssubsystems table exists...' IF (OBJECT_ID(N'[dbo].[syssubsystems]', 'U') IS NOT NULL) BEGIN IF (OBJECT_ID('tempdb..#syssubsystems_temp', 'U') IS NOT NULL) BEGIN PRINT 'Dropping existing temp table tempdb..#syssubsystems_temp...' DROP TABLE #syssubsystems_temp END SELECT subsystem, max_worker_threads INTO #syssubsystems_temp FROM [dbo].[syssubsystems] END GO /**********************************************************************/ /* DONE - Saving the syssubsystems related user info */ /**********************************************************************/ --drop syssubsystems table if it exists( to support update from IDW(n) to RTM) --it will recreated later with the updated schema IF (OBJECT_ID('dbo.syssubsystems', 'U') IS NOT NULL) BEGIN DROP TABLE dbo.syssubsystems END GO --drop column logshipping from sysdbmaintplans table --alter only if command column is not already nvarchar(max) IF (EXISTS (SELECT c.* FROM msdb.sys.all_columns c JOIN msdb.sys.all_objects t ON c.object_id = t.object_id WHERE (c.name = 'logshipping') AND t.name = 'sysdbmaintplans' AND t.type = 'U')) BEGIN ALTER TABLE sysdbmaintplans DROP COLUMN logshipping END go --make sure --it will recreated later with the updated schema IF (OBJECT_ID('dbo.sysjobstepslogs', 'U') IS NOT NULL) BEGIN ALTER TABLE dbo.sysjobstepslogs ALTER COLUMN log_size bigint END -- sysproxylogin upgrade -- sysproxylogin upgrade BEGIN TRY IF EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='principal_id' and id = (SELECT OBJECT_ID(N'dbo.sysproxylogin', 'U'))) BEGIN -- convert data from principal_id to sid exec sp_executesql N' DECLARE @sid varbinary(85) DECLARE @principal_id int DECLARE principal_sid_cursor CURSOR LOCAL FOR SELECT distinct principal_id FROM dbo.sysproxylogin WHERE (sid IS NULL) AND (flags = 2) OPEN principal_sid_cursor FETCH NEXT FROM principal_sid_cursor INTO @principal_id WHILE (@@fetch_status = 0) BEGIN SELECT @sid=sid FROM msdb.sys.database_principals WHERE principal_id=@principal_id IF @sid IS NOT NULL -- principal_id is valid BEGIN UPDATE dbo.sysproxylogin SET sid = @sid WHERE principal_id = @principal_id END ELSE BEGIN DELETE FROM dbo.sysproxylogin WHERE principal_id = @principal_id END FETCH NEXT FROM principal_sid_cursor INTO @principal_id END CLOSE principal_sid_cursor DEALLOCATE principal_sid_cursor ' -- remove obsolete column DROP INDEX sysproxylogin.clust ALTER TABLE dbo.sysproxylogin DROP COLUMN principal_id CREATE UNIQUE CLUSTERED INDEX clust ON sysproxylogin(proxy_id, sid, flags) END END TRY BEGIN CATCH print 'There was a problem upgrading sysproxylogin table.' print 'Unable to map existing principal_id values to sid column' print 'Error State: ' + convert(varchar(10),ERROR_STATE() ) END CATCH GO /**************************************************************/ /* Mark system objects */ /**************************************************************/ declare @start datetime ,@name sysname select @start = start from #InstMsdb declare newsysobjs cursor for select name from sys.objects where schema_id = 1 and create_date >= @start open newsysobjs fetch next from newsysobjs into @name while @@fetch_status = 0 begin Exec sp_MS_marksystemobject @name fetch next from newsysobjs into @name end deallocate newsysobjs drop table #InstMsdb go EXECUTE master.dbo.sp_configure N'allow updates', 0 go RECONFIGURE WITH OVERRIDE go /**************************************************************/ /* DMF Pre-upgrade steps */ /**************************************************************/ PRINT 'DMF pre-upgrade steps...' -- -- >>> CTP5 -> CTP6 Upgrade -- -- The check is based on presense of ObjectSet tables -- We also check if principal DMF objects is there -- if it's not we either upgrade from Yukon -- or there is nothing to upgrade anyway IF (OBJECT_ID('[dbo].[syspolicy_policies_internal]', 'U') IS NOT NULL) AND (OBJECT_ID('[dbo].[syspolicy_object_sets_internal]', 'U') IS NULL) BEGIN BEGIN TRY -- Open transaction - we don't want to delete tables if we don't have a copy of data BEGIN TRANSACTION -- Create upgrade marker CREATE TABLE dbo.dmf_upgrade (id int) -- STORE DATA SELECT * INTO msdb.dbo.tmp_syspolicy_target_sets_internal FROM syspolicy_target_sets_internal SELECT * INTO msdb.dbo.tmp_syspolicy_target_set_levels_internal FROM syspolicy_target_set_levels_internal SELECT * INTO msdb.dbo.tmp_syspolicy_policies_internal FROM syspolicy_policies_internal SELECT * INTO msdb.dbo.tmp_syspolicy_system_health_state_internal FROM syspolicy_system_health_state_internal SELECT * INTO msdb.dbo.tmp_syspolicy_policy_execution_history_internal FROM syspolicy_policy_execution_history_internal SELECT * INTO msdb.dbo.tmp_syspolicy_policy_execution_history_details_internal FROM syspolicy_policy_execution_history_details_internal -- Delete policies to invoke the trigger that removes dependent objects (jobs, if any). DELETE [dbo].[syspolicy_policies_internal] -- T-SQL Policy jobs are now gone, we must nullify job_id to not violate the foreign key constraint UPDATE msdb.dbo.tmp_syspolicy_policies_internal SET job_id = NULL -- DROP TABLES (WITH DEPENDENCIES) IF (OBJECT_ID('[dbo].[syspolicy_target_set_levels_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[syspolicy_target_set_levels_internal] IF (OBJECT_ID('[dbo].[syspolicy_target_sets_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[syspolicy_target_sets_internal] IF (OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[syspolicy_policy_execution_history_details_internal] IF (OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[syspolicy_policy_execution_history_internal] IF (OBJECT_ID('[dbo].[syspolicy_system_health_state_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[syspolicy_system_health_state_internal] IF (OBJECT_ID('[dbo].[syspolicy_policies]', 'V') IS NOT NULL) DROP VIEW [dbo].[syspolicy_policies] IF (OBJECT_ID('[dbo].[syspolicy_policies_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[syspolicy_policies_internal] COMMIT TRANSACTION END TRY BEGIN CATCH IF (XACT_STATE() <> 0) BEGIN ROLLBACK TRANSACTION; END DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); PRINT 'ERROR: DMF CTP5 to CTP6 upgrade tasks failed' RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); END CATCH; END GO -- -- <<< CTP5 - CTP6 Upgrade -- -- -- >>> CTP6 - RTM Upgrade -- IF (OBJECT_ID('[dbo].[syspolicy_policies_internal]', 'U') IS NOT NULL) BEGIN IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policies_internal]') AND name='object_set_id' AND is_nullable=0) BEGIN ALTER TABLE [dbo].[syspolicy_policies_internal] ALTER COLUMN object_set_id int NULL END END GO IF (OBJECT_ID('[dbo].[syspolicy_system_health_state_internal]', 'U') IS NOT NULL) BEGIN IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_system_health_state_internal]') AND name='target_query_expression_with_id' AND is_nullable=1) BEGIN IF EXISTS (SELECT * FROM sys.indexes WHERE name = N'IX_syspolicy_system_health_state_internal_target_query_expression_with_id') DROP INDEX IX_syspolicy_system_health_state_internal_target_query_expression_with_id ON [dbo].[syspolicy_system_health_state_internal] ALTER TABLE [dbo].[syspolicy_system_health_state_internal] ALTER COLUMN target_query_expression_with_id nvarchar(400) NOT NULL CREATE INDEX IX_syspolicy_system_health_state_internal_target_query_expression_with_id ON [dbo].[syspolicy_system_health_state_internal](target_query_expression_with_id) END IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_system_health_state_internal]') AND name='target_query_expression' AND is_nullable=1) BEGIN ALTER TABLE [dbo].[syspolicy_system_health_state_internal] ALTER COLUMN target_query_expression nvarchar(max) NOT NULL END END GO IF (OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]', 'U') IS NOT NULL) BEGIN IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='policy_id' AND is_nullable=1) BEGIN IF EXISTS (SELECT * FROM sys.indexes WHERE name = N'IX_syspolicy_policy_execution_history_internal_end_date_policy_id') DROP INDEX IX_syspolicy_policy_execution_history_internal_end_date_policy_id ON [dbo].[syspolicy_policy_execution_history_internal] IF EXISTS (SELECT * FROM sys.indexes WHERE name = N'IX_syspolicy_policy_execution_history_internal_policy_id') DROP INDEX IX_syspolicy_policy_execution_history_internal_policy_id ON [dbo].[syspolicy_policy_execution_history_internal] DECLARE @fk_name sysname SELECT @fk_name = k.name from sys.foreign_keys k join sys.foreign_key_columns kc on k.object_id = kc.constraint_object_id join sys.columns c on k.parent_object_id = c.object_id AND kc.parent_column_id = c.column_id where k.parent_object_id=OBJECT_ID('syspolicy_policy_execution_history_internal') and c.name = 'policy_id' IF @fk_name IS NOT NULL BEGIN DECLARE @drop_cmd nvarchar(512) SELECT @drop_cmd = 'ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] DROP CONSTRAINT ' + QUOTENAME(@fk_name) EXECUTE (@drop_cmd) END END END GO IF (OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]', 'U') IS NOT NULL) BEGIN IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='policy_id' AND is_nullable=1) BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ALTER COLUMN policy_id int NOT NULL CREATE INDEX IX_syspolicy_policy_execution_history_internal_end_date_policy_id ON [dbo].[syspolicy_policy_execution_history_internal](policy_id, end_date); CREATE INDEX IX_syspolicy_policy_execution_history_internal_policy_id ON [dbo].[syspolicy_policy_execution_history_internal](policy_id); END IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='start_date' AND is_nullable=1) BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ALTER COLUMN [start_date] datetime NOT NULL END IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='result' AND is_nullable=1) BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ALTER COLUMN result bit NOT NULL END IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='is_full_run' AND is_nullable=1) BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ALTER COLUMN is_full_run bit NOT NULL END IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='exception_message' AND is_nullable=0) BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ALTER COLUMN exception_message nvarchar(max) NULL END IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]') AND name='exception' AND is_nullable=0) BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ALTER COLUMN exception nvarchar(max) NULL END END GO IF (OBJECT_ID('[dbo].[syspolicy_policy_execution_history_internal]', 'U') IS NOT NULL) BEGIN IF NOT EXISTS (SELECT * from sys.foreign_keys k join sys.foreign_key_columns kc on k.object_id = kc.constraint_object_id join sys.columns c on k.parent_object_id = c.object_id AND kc.parent_column_id = c.column_id where k.parent_object_id=OBJECT_ID('syspolicy_policy_execution_history_internal') and c.name = 'policy_id') BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_internal] ADD FOREIGN KEY (policy_id) REFERENCES [syspolicy_policies_internal] END END GO IF (OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]', 'U') IS NOT NULL) BEGIN IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='history_id' AND is_nullable=1) BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN history_id bigint NOT NULL END IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='target_query_expression_with_id' AND is_nullable=1) BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN target_query_expression_with_id nvarchar(4000) NOT NULL END IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='target_query_expression' AND is_nullable=1) BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN target_query_expression nvarchar(4000) NOT NULL END IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='execution_date' AND is_nullable=1) BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN execution_date datetime NOT NULL END IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='result' AND is_nullable=1) BEGIN IF EXISTS (SELECT * FROM sys.indexes WHERE name = N'IX_syspolicy_policy_execution_history_details_internal_result') DROP INDEX IX_syspolicy_policy_execution_history_details_internal_result ON [dbo].[syspolicy_policy_execution_history_details_internal] ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN result bit NOT NULL END IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='result_detail' AND is_nullable=0) BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN result_detail nvarchar(max) NULL END IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='exception_message' AND is_nullable=0) BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN exception_message nvarchar(max) NULL END IF EXISTS (SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]') AND name='exception' AND is_nullable=0) BEGIN ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ALTER COLUMN exception nvarchar(max) NULL END END GO -- -- <<< CTP6 - RTM Upgrade -- -- -- >>> Katmai RTM - KJ CTP2 Upgrade -- -- If there is no 'is_system' column in the following tables, add it -- -- Need to take care of Shiloh/Yukon upgrade - no DMF objects exist -- Only check for policies, assuming we either have all DMF tables or none -- If installation is corrupted (there is policies table, but no conditions table) upgrade fails -- IF (OBJECT_ID('[dbo].[syspolicy_policies_internal]', 'U') IS NOT NULL) BEGIN IF NOT EXISTS (SELECT * FROM sys.columns WHERE name = 'is_system' AND object_id = OBJECT_ID('[dbo].[syspolicy_policies_internal]', 'U')) ALTER TABLE syspolicy_policies_internal ADD is_system bit NOT NULL DEFAULT (0) IF NOT EXISTS (SELECT * FROM sys.columns WHERE name = 'is_system' AND object_id = OBJECT_ID('[dbo].[syspolicy_conditions_internal]', 'U')) ALTER TABLE syspolicy_conditions_internal ADD is_system bit NOT NULL DEFAULT (0) IF NOT EXISTS (SELECT * FROM sys.columns WHERE name = 'is_system' AND object_id = OBJECT_ID('[dbo].[syspolicy_object_sets_internal]', 'U')) ALTER TABLE syspolicy_object_sets_internal ADD is_system bit NOT NULL DEFAULT (0) END GO -- Fix indexes on syspolicy_policy_execution_history_details_internal -- IF (OBJECT_ID('[dbo].[syspolicy_policies_internal]', 'U') IS NOT NULL) BEGIN IF EXISTS (SELECT * FROM sys.indexes WHERE name = N'IX_syspolicy_policy_execution_history_details_internal_result') DROP INDEX IX_syspolicy_policy_execution_history_details_internal_result ON [dbo].[syspolicy_policy_execution_history_details_internal] IF NOT EXISTS (SELECT * FROM sys.key_constraints WHERE name = N'PK_syspolicy_policy_execution_history_details_id') BEGIN DECLARE @ix_name sysname SELECT @ix_name = name FROM sys.key_constraints WHERE parent_object_id = OBJECT_ID('[dbo].[syspolicy_policy_execution_history_details_internal]', 'U') AND type = 'PK' AND is_system_named = 1 IF @ix_name IS NOT NULL BEGIN DECLARE @drop_cmd nvarchar(512) SELECT @drop_cmd = 'ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] DROP CONSTRAINT ' + QUOTENAME(@ix_name) EXECUTE (@drop_cmd) END ALTER TABLE [dbo].[syspolicy_policy_execution_history_details_internal] ADD CONSTRAINT [PK_syspolicy_policy_execution_history_details_id] PRIMARY KEY CLUSTERED(history_id, detail_id) END END GO -- -- <<< Katmai RTM - KJ CTP2 Upgrade -- /**************************************************************/ /* End of DMF Pre-upgrade steps */ /**************************************************************/ /**********************************************************************/ /* DC Pre-upgrade steps */ /**********************************************************************/ GO PRINT 'DC pre-upgrade steps...'; -- Capture current state of DataCollector in temp table -- and re-enable collector after script upgrade -- #304027 - Data Collection is disabled when upgrading SQL Server 2008 -- service pack upgrade or hotfix upgrade PRINT 'Check if Data collector config table exists...' IF (OBJECT_ID(N'[dbo].[syscollector_config_store_internal]', 'U') IS NOT NULL) BEGIN IF (OBJECT_ID('tempdb..#data_collector_status', 'U') IS NOT NULL) BEGIN PRINT 'Dropping existing temp table tempdb..#data_collector_status...' DROP TABLE #data_collector_status END DECLARE @collector_enabled int; SELECT @collector_enabled = ISNULL(CONVERT(int, parameter_value),0) FROM [dbo].[syscollector_config_store_internal] WHERE parameter_name = 'CollectorEnabled' PRINT 'Data Collector state before upgrade: ' + CONVERT(varchar, @collector_enabled) SELECT @collector_enabled AS data_collector_old_status INTO #data_collector_status END -- -- CTP6->CTP6 Refresh -- -- Drop the parent_log_id->log_id self reference IF ((OBJECT_ID ('dbo.syscollector_execution_log_internal') IS NOT NULL) AND (OBJECT_ID('dbo.FK_syscollector_execution_log_parent_log_id', 'F') IS NOT NULL)) BEGIN PRINT 'Dropping [FK_syscollector_execution_log_parent_log_id]'; ALTER TABLE [dbo].[syscollector_execution_log_internal] DROP CONSTRAINT [FK_syscollector_execution_log_parent_log_id]; END -- -- >>> CTP5 -> CTP6 Upgrade -- -- Change int log_id columns to bigint IF (OBJECT_ID ('dbo.syscollector_execution_log_internal') IS NOT NULL) AND EXISTS ( SELECT * FROM sys.columns AS c INNER JOIN sys.types AS t ON c.system_type_id = t.system_type_id WHERE [object_id] = OBJECT_ID ('dbo.syscollector_execution_log_internal') AND c.name = 'log_id' AND t.name = 'int' ) BEGIN BEGIN TRY PRINT 'Starting log_id int -> bigint conversion' BEGIN TRANSACTION PreInstMsdb100_DCUpgrade -- Drop PK/FK constaints referencing log_id columns so we can change the data types of these columns PRINT 'Dropping [FK_syscollector_execution_stats_log_id]'; ALTER TABLE [dbo].[syscollector_execution_stats_internal] DROP CONSTRAINT [FK_syscollector_execution_stats_log_id]; PRINT 'Dropping [PK_syscollector_execution_stats]'; ALTER TABLE [dbo].[syscollector_execution_stats_internal] DROP CONSTRAINT [PK_syscollector_execution_stats]; PRINT 'Dropping [PK_syscollector_execution_log]'; ALTER TABLE [dbo].[syscollector_execution_log_internal] DROP CONSTRAINT [PK_syscollector_execution_log]; -- Truncate the DC log table to avoid causing unnecessary delays during CTP upgrade PRINT 'Truncating [syscollector_execution_log_internal]...'; TRUNCATE TABLE [dbo].[syscollector_execution_log_internal]; -- log_id values stored in syscollector_execution_stats will no longer be valid after we have truncated the log table PRINT 'Truncating [syscollector_execution_stats_internal]...'; TRUNCATE TABLE [dbo].[syscollector_execution_stats_internal]; -- Change the data type of all log_id columns PRINT 'Changing log_id column datatypes...'; PRINT ' syscollector_execution_stats_internal.log_id'; ALTER TABLE [dbo].[syscollector_execution_stats_internal] ALTER COLUMN [log_id] bigint NOT NULL; PRINT ' syscollector_execution_log_internal.log_id'; ALTER TABLE [dbo].[syscollector_execution_log_internal] ALTER COLUMN [log_id] bigint NOT NULL; PRINT ' syscollector_execution_log_internal.parent_log_id'; ALTER TABLE [dbo].[syscollector_execution_log_internal] ALTER COLUMN [parent_log_id] bigint NULL; END TRY BEGIN CATCH IF (XACT_STATE() <> 0) BEGIN ROLLBACK TRANSACTION; END DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); PRINT 'ERROR: DC CTP5 to CTP6 upgrade tasks failed' RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); END CATCH; END GO -- Re-create the PK/FK constraints that we dropped in order to modify column datatypes IF (OBJECT_ID ('dbo.syscollector_execution_log_internal') IS NOT NULL) AND NOT EXISTS (SELECT * FROM sys.objects WHERE name = 'PK_syscollector_execution_log' AND [schema_id] = SCHEMA_ID ('dbo')) BEGIN BEGIN TRY PRINT 'Creating PK_syscollector_execution_log...'; ALTER TABLE [dbo].[syscollector_execution_log_internal] ADD CONSTRAINT [PK_syscollector_execution_log] PRIMARY KEY CLUSTERED (log_id ASC); END TRY BEGIN CATCH -- If we're still in the transaction started by the prior batch, roll it back so that it's -- as if we never dropped any constraints PRINT 'ERROR: DC CTP5 to CTP6 upgrade tasks failed to create [PK_syscollector_execution_log] ' IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; END CATCH; END GO IF (OBJECT_ID ('dbo.syscollector_execution_stats_internal') IS NOT NULL) AND NOT EXISTS (SELECT * FROM sys.objects WHERE name = 'PK_syscollector_execution_stats' AND [schema_id] = SCHEMA_ID ('dbo')) BEGIN BEGIN TRY PRINT 'Creating PK_syscollector_execution_stats...'; ALTER TABLE [dbo].[syscollector_execution_stats_internal] ADD CONSTRAINT [PK_syscollector_execution_stats] PRIMARY KEY CLUSTERED (log_id ASC, task_name ASC, log_time DESC); END TRY BEGIN CATCH PRINT 'ERROR: DC CTP5 to CTP6 upgrade tasks failed to create [PK_syscollector_execution_stats] ' IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; END CATCH; END GO IF (OBJECT_ID ('dbo.syscollector_execution_stats_internal') IS NOT NULL) AND NOT EXISTS (SELECT * FROM sys.objects WHERE name = 'FK_syscollector_execution_stats_log_id' AND [schema_id] = SCHEMA_ID ('dbo')) BEGIN BEGIN TRY PRINT 'Creating FK_syscollector_execution_stats_log_id...'; ALTER TABLE [dbo].[syscollector_execution_stats_internal] ADD CONSTRAINT [FK_syscollector_execution_stats_log_id] FOREIGN KEY (log_id) REFERENCES [dbo].[syscollector_execution_log_internal] (log_id) ON DELETE CASCADE; END TRY BEGIN CATCH PRINT 'ERROR: DC CTP5 to CTP6 upgrade tasks failed to create [FK_syscollector_execution_stats_log_id] ' IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; END CATCH; END GO IF (@@TRANCOUNT > 0) COMMIT TRANSACTION; GO IF (OBJECT_ID ('dbo.syscollector_execution_log_internal') IS NOT NULL) BEGIN EXEC sp_refreshview 'dbo.syscollector_execution_log' EXEC sp_refreshview 'dbo.syscollector_execution_stats' END GO -- -- <<< CTP5 - CTP6 Upgrade -- -- -- >>> Delete auto-generated T-SQL packages stored in msdb. -- IF OBJECT_ID ('dbo.syscollector_tsql_query_collector') IS NOT NULL BEGIN RAISERROR ('Deleting cached auto-generated T-SQL Data Collection packages from msdb...', 0, 1) WITH NOWAIT /* ** We have a row in [syscollector_tsql_query_collector] for each T-SQL collection item, and ** each item has a collection and an upload package. There's a DELETE trigger on this table ** that will take care of deleting the packages from sysssispackages for us. The packages ** will be re-generated automatically the next time their parent collection set's collection ** package runs. */ DELETE FROM syscollector_tsql_query_collector END -- -- <<< Delete auto-generated T-SQL packages stored in msdb. -- GO PRINT 'End of DC pre-upgrade steps.'; /**********************************************************************/ /* End of DC Pre-upgrade steps */ /**********************************************************************/ /**********************************************************************/ /* DAC Pre-upgrade steps */ /**********************************************************************/ RAISERROR('Starting DAC pre-upgrade steps ...', 0, 1) WITH NOWAIT; -- -- SQL Server 2008 R2 : CTP2->CTP3 upgrade -- 1. sysdac_history table has two extra columns that we added in CTP3 (required, comments). -- It has been decided not to preserve CTP2 history data i.e. on an upgrade from CTP2->CTP3, deploy/uninstall logs are cleared. -- -- 2. {sysdac_packages_internal, sysdac_packages, sysdac_parts_internal, sysdac_parts} are deleted. They are replaced with new artifacts in CTP3. -- IF (OBJECT_ID('[dbo].[sysdac_history_internal]', 'U') IS NOT NULL) BEGIN IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE (name = 'comments' OR name = 'required') AND object_id = OBJECT_ID('[dbo].[sysdac_history_internal]', 'U') ) BEGIN DROP TABLE [dbo].[sysdac_history_internal] END END IF (OBJECT_ID('[dbo].[sysdac_parts]', 'V') IS NOT NULL) DROP VIEW [dbo].[sysdac_parts] IF (OBJECT_ID('[dbo].[sysdac_packages]', 'V') IS NOT NULL) DROP VIEW [dbo].[sysdac_packages] IF (OBJECT_ID('[dbo].[sysdac_parts_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[sysdac_parts_internal] IF (OBJECT_ID('[dbo].[sysdac_packages_internal]', 'U') IS NOT NULL) DROP TABLE [dbo].[sysdac_packages_internal] /**********************************************************************/ /* End of DAC Pre-upgrade steps */ /**********************************************************************/ GO PRINT '' PRINT '----------------------------------------' PRINT 'Execution of PREINSTMSDB100.SQL complete' PRINT '----------------------------------------' go /**********************************************************************/ /* INSTMSDB.SQL */ /* */ /* Installs the tables, triggers and stored procedures necessary for */ /* supporting local (and multi-server) jobs, alerts, operators, and */ /* backup history. These objects are used by SQL SMO, SQL Management */ /* Studio, SQLServerAgent, DMF, Data Collector and Synthesis */ /* */ /* Also contains SSIS (Data Transformation Services) tables and */ /* stored procedures for local SQL Server storage of SSIS Packages. */ /* */ /* */ /* Copyright Microsoft, Inc. 1994 - 2008 */ /* All Rights Reserved. */ /* */ /**********************************************************************/ PRINT '----------------------------------' PRINT 'Starting execution of INSTMSDB.SQL' PRINT '----------------------------------' go --this version of instmsdb should be executed only against 10.0 servers IF (@@microsoftversion / 0x01000000) <> 10 BEGIN RAISERROR('This version of instmsdb.sql should only be executed against 10.0 servers.', 20, 127) WITH LOG END go -- disable the event collection for policies while running this script IF EXISTS (SELECT * FROM sys.server_triggers WHERE name = N'syspolicy_server_trigger') DISABLE TRIGGER [syspolicy_server_trigger] ON ALL SERVER GO IF EXISTS (SELECT * FROM sys.service_queues where name = N'syspolicy_event_queue') ALTER QUEUE [syspolicy_event_queue] WITH ACTIVATION (STATUS = OFF) GO /*********************************************************************/ /* Create auxilary procedure to enable OBD (Off By Default component */ /*********************************************************************/ CREATE PROCEDURE #sp_enable_component @comp_name sysname, @advopt_old_value INT OUT, @comp_old_value INT OUT AS BEGIN SELECT @advopt_old_value=cast(value_in_use as int) from sys.configurations where name = 'show advanced options'; SELECT @comp_old_value=cast(value_in_use as int) from sys.configurations where name = @comp_name; EXEC sp_configure 'show advanced options',1; RECONFIGURE WITH OVERRIDE; EXEC sp_configure @comp_name, 1; RECONFIGURE WITH OVERRIDE; END go CREATE PROCEDURE #sp_restore_component_state @comp_name sysname, @advopt_old_value INT, @comp_old_value INT AS BEGIN EXEC sp_configure @comp_name, @comp_old_value; RECONFIGURE WITH OVERRIDE; EXEC sp_configure 'show advanced options',@advopt_old_value; RECONFIGURE WITH OVERRIDE; END go -- Explicitly set the options that the server stores with the object in sysobjects.status -- so that it doesn't matter if the script is run using a DBLib or ODBC based client. SET QUOTED_IDENTIFIER OFF -- We don't use quoted identifiers SET ANSI_NULLS ON -- We don't want (NULL = NULL) == TRUE go SET ANSI_PADDING ON -- Set so that trailing zeros aren't trimmed off sysjobs.owner_login_sid go -- Allow updates to system catalogs so that all our SP's inherit full DML capability on -- system objects and so that we can exercise full DDL control on our system objects EXECUTE master.dbo.sp_configure N'allow updates', 1 go RECONFIGURE WITH OVERRIDE go /**************************************************************/ /* */ /* D A T A B A S E C R E A T I O N */ /* */ /**************************************************************/ IF (NOT EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE (name = N'msdb'))) BEGIN PRINT 'Creating the msdb database...' END go USE master go SET NOCOUNT ON -- NOTE: It is important that this script can be re-run WITHOUT causing loss of data, hence -- we only create the database if it missing (if the database already exists we test -- that it has enough free space and if not we expand both the device and the database). DECLARE @model_db_size INT DECLARE @msdb_db_size INT DECLARE @sz_msdb_db_size VARCHAR(10) DECLARE @device_directory NVARCHAR(520) DECLARE @page_size INT DECLARE @size INT DECLARE @free_db_space FLOAT SELECT @page_size = 8 IF (NOT EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE (name = N'msdb'))) BEGIN -- Make sure that we create [the data portion of] MSDB to be at least as large as -- the MODEL database SELECT @model_db_size = (SUM(size) * @page_size) FROM model.dbo.sysfiles IF (@model_db_size > 3072) -- 3 is the minimum required size for MSDB (in megabytes) SELECT @msdb_db_size = @model_db_size ELSE SELECT @msdb_db_size = 3072 SELECT @device_directory = SUBSTRING(filename, 1, CHARINDEX(N'master.mdf', LOWER(filename)) - 1) FROM master.dbo.sysaltfiles WHERE (name = N'master') -- Drop any existing MSDBData / MSDBLog file(s) DECLARE @advopt_old_value INT DECLARE @comp_old_value INT EXECUTE #sp_enable_component 'xp_cmdshell', @advopt_old_value out, @comp_old_value out EXECUTE(N'EXECUTE master.dbo.xp_cmdshell N''DEL ' + @device_directory + N'MSDBData.mdf'', no_output') EXECUTE(N'EXECUTE master.dbo.xp_cmdshell N''DEL ' + @device_directory + N'MSDBLog.ldf'', no_output') EXECUTE #sp_restore_component_state 'xp_cmdshell', @advopt_old_value, @comp_old_value -- Create the database PRINT '' PRINT 'Creating MSDB database...' SELECT @sz_msdb_db_size = RTRIM(LTRIM(CONVERT(VARCHAR, @msdb_db_size))) EXECUTE (N'CREATE DATABASE msdb ON (NAME = N''MSDBData'', FILENAME = N''' + @device_directory + N'MSDBData.mdf'', SIZE = ' + @sz_msdb_db_size + N'KB, MAXSIZE = UNLIMITED, FILEGROWTH = 10%) LOG ON (NAME = N''MSDBLog'', FILENAME = N''' + @device_directory + N'MSDBLog.ldf'', SIZE = 512KB, MAXSIZE = UNLIMITED, FILEGROWTH = 10%)') EXECUTE (N'ALTER DATABASE msdb SET DB_CHAINING ON') PRINT '' END ELSE BEGIN PRINT 'Checking the size of MSDB...' DBCC UPDATEUSAGE(N'msdb') WITH NO_INFOMSGS -- Make sure that MSDBLog has unlimited growth ALTER DATABASE msdb MODIFY FILE (NAME = N'MSDBLog', MAXSIZE = 2TB) -- Determine amount of free space in msdb. We need at least 2MB free. SELECT @free_db_space = ((((SELECT SUM(size) FROM msdb.dbo.sysfiles WHERE status & 0x8040 = 0) - (SELECT SUM(reserved) FROM msdb.dbo.sysindexes WHERE indid IN (0, 1, 255))) * @page_size) / 1024.0) IF (@free_db_space < 2) BEGIN DECLARE @logical_file_name sysname DECLARE @os_file_name NVARCHAR(255) DECLARE @size_as_char VARCHAR(10) SELECT @logical_file_name = name, @os_file_name = filename, @size_as_char = CONVERT(VARCHAR(10), size*8 + 2048) -- size column in sysaltfiles is in number of 8KB pages FROM master.dbo.sysaltfiles WHERE (name = N'MSDBData') set @os_file_name = QUOTENAME(@os_file_name,'''') PRINT 'Attempting to expand the msdb database...' EXECUTE(N'ALTER DATABASE msdb MODIFY FILE (NAME = N''' + @logical_file_name + N''', FILENAME = N' + @os_file_name + N', SIZE =' + @size_as_char + N'KB)') IF (@@error <> 0) RAISERROR('Unable to expand the msdb database. INSTMSDB.SQL terminating.', 20, 127) WITH LOG END PRINT '' END EXECUTE (N'ALTER DATABASE msdb SET TRUSTWORTHY ON') go -- truncate log on checkpoint ALTER DATABASE msdb SET RECOVERY SIMPLE go USE msdb go -- Check that we're in msdb IF (DB_NAME() <> N'msdb') RAISERROR('A problem was encountered accessing msdb. INSTMSDB.SQL terminating.', 20, 127) WITH LOG go -- Add the guest user IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysusers WHERE (name = N'guest') AND (hasdbaccess = 1))) BEGIN PRINT '' EXECUTE sys.sp_adduser N'guest' END go CHECKPOINT go /**************************************************************/ /* Record time of start of creates */ /**************************************************************/ SELECT start = getdate() INTO #InstMsdb go /**************************************************************/ /* */ /* T A B L E D R O P S */ /* */ /**************************************************************/ SET NOCOUNT ON DECLARE @build_number INT SELECT @build_number = @@microsoftversion & 0xffff -- Do any necessary changes based on build number here /**************************************************************/ /* drop certificate signature from Agent signed sps */ /**************************************************************/ BEGIN TRANSACTION declare @sp sysname declare @exec_str nvarchar(1024) declare ms_crs_sps cursor global for select object_name(crypts.major_id) from sys.crypt_properties crypts, sys.certificates certs where crypts.thumbprint = certs.thumbprint and crypts.class = 1 and certs.name = '##MS_AgentSigningCertificate##' open ms_crs_sps fetch next from ms_crs_sps into @sp while @@fetch_status = 0 begin if exists(select * from sys.objects where name = @sp) begin print 'Dropping signature from: ' + @sp set @exec_str = N'drop signature from ' + quotename(@sp) + N' by certificate [##MS_AgentSigningCertificate##]' Execute(@exec_str) if (@@error <> 0) begin declare @err_str nvarchar(1024) set @err_str = 'Cannot drop signature from ' + quotename(@sp) + '. Terminating.' close ms_crs_sps deallocate ms_crs_sps ROLLBACK TRANSACTION RAISERROR(@err_str, 20, 127) WITH LOG return end end fetch next from ms_crs_sps into @sp end close ms_crs_sps deallocate ms_crs_sps COMMIT TRANSACTION go --------------------------------------------------------------- -- Replication Datatype mapping tables --------------------------------------------------------------- -- REVIEW - Is this needed? What does this do? exec sys.sp_MSrepl_dropdatatypemappings go CHECKPOINT go /**************************************************************/ /* */ /* D E F A U L T S */ /* */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'default_sdl_error_message') AND (type = 'D'))) EXECUTE('CREATE DEFAULT default_sdl_error_message AS NULL') go IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'default_current_date') AND (type = 'D'))) EXECUTE('CREATE DEFAULT default_current_date AS GETDATE()') go IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'default_zero') AND (type = 'D'))) EXECUTE('CREATE DEFAULT default_zero AS 0') go IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'default_one') AND (type = 'D'))) EXECUTE('CREATE DEFAULT default_one AS 1') go /**************************************************************/ /* */ /* T A B L E S */ /* */ /**************************************************************/ /**************************************************************/ /* SYSPROXIES */ /**************************************************************/ IF (OBJECT_ID(N'dbo.sysproxies', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysproxies...' CREATE TABLE dbo.sysproxies ( proxy_id INT IDENTITY, --used to identify a proxy name sysname NOT NULL, --friendly name of a proxy credential_id INT NOT NULL, enabled TINYINT NOT NULL, description NVARCHAR(512) NULL, --nvarchar(512) user_sid VARBINARY(85) NOT NULL, --sid of proxy NT user credential_date_created DATETIME NOT NULL --the date the associated credential has been created ) CREATE UNIQUE CLUSTERED INDEX clust ON sysproxies(proxy_id) CREATE UNIQUE NONCLUSTERED INDEX nc1 ON sysproxies(name) END go IF (OBJECT_ID(N'dbo.syssubsystems', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table syssubsystems...' CREATE TABLE dbo.syssubsystems ( subsystem_id INT NOT NULL, -- used to identify the subsystem subsystem NVARCHAR(40) COLLATE database_default NOT NULL, -- Name of subsystem description_id INT NULL, -- Message number of description string. These messages are stored in sysmessages table for localization purposes. Same story as for Shiloh subsystem_dll NVARCHAR(255) COLLATE database_default NULL, -- Store full path of the name of subsystem dll subsystem are always installed in the binn folder of an instance agent_exe NVARCHAR(255) COLLATE database_default NULL, -- Full path to the executable that use the subsystem start_entry_point NVARCHAR(30) COLLATE database_default NULL, -- Function called when initializing subsystem event_entry_point NVARCHAR(30) COLLATE database_default NULL, -- Function called when executing a subsystem step stop_entry_point NVARCHAR(30) COLLATE database_default NULL, -- Function called when unloading a subsystem max_worker_threads INT NULL -- Number of maximum concurrent steps for a subsystem ) CREATE UNIQUE CLUSTERED INDEX clust ON syssubsystems(subsystem_id) CREATE UNIQUE NONCLUSTERED INDEX nc1 ON syssubsystems(subsystem) END go IF (OBJECT_ID(N'dbo.sysproxysubsystem', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysproxysubsystem...' CREATE TABLE dbo.sysproxysubsystem ( subsystem_id INT NOT NULL, -- used to identify the subsystem proxy_id INT NOT NULL, -- used to identify the proxy ) CREATE UNIQUE CLUSTERED INDEX clust ON sysproxysubsystem(subsystem_id, proxy_id) END go IF (OBJECT_ID(N'dbo.sysproxylogin', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysproxylogin...' CREATE TABLE dbo.sysproxylogin ( proxy_id INT NOT NULL, --used to identify the proxy sid VARBINARY(85) NULL, --keep logins, fixed server roles or msdb database roles flags INT DEFAULT 0 NOT NULL -- tells is member_id is login = 0, server fixed role, msdb role. ) CREATE UNIQUE CLUSTERED INDEX clust ON sysproxylogin(proxy_id, sid, flags) END go PRINT '' PRINT 'Creating view sysproxyloginsubsystem_view...' go IF (NOT OBJECT_ID(N'dbo.sysproxyloginsubsystem_view', 'V') IS NULL) DROP VIEW sysproxyloginsubsystem_view go CREATE VIEW sysproxyloginsubsystem_view AS SELECT ps.subsystem_id AS subsystem_id, pl.proxy_id AS proxy_id, pl.sid AS sid, pl.flags AS flags FROM sysproxylogin pl JOIN sysproxysubsystem ps ON pl.proxy_id = ps.proxy_id go IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sqlagent_info') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sqlagent_info...' CREATE TABLE sqlagent_info ( attribute sysname NOT NULL, value NVARCHAR(512) NOT NULL ) END go /**************************************************************/ /* SYSDOWNLOADLIST */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdownloadlist') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysdownloadlist...' CREATE TABLE sysdownloadlist ( instance_id INT IDENTITY NOT NULL, source_server sysname NOT NULL, operation_code TINYINT NOT NULL, object_type TINYINT NOT NULL, object_id UNIQUEIDENTIFIER NOT NULL, target_server sysname NOT NULL, error_message NVARCHAR(1024) NULL, date_posted DATETIME NOT NULL, date_downloaded DATETIME NULL, status TINYINT NOT NULL, deleted_object_name sysname NULL ) EXECUTE sys.sp_bindefault default_sdl_error_message, N'sysdownloadlist.error_message' EXECUTE sys.sp_bindefault default_current_date, N'sysdownloadlist.date_posted' EXECUTE sys.sp_bindefault default_zero, N'sysdownloadlist.status' CREATE UNIQUE CLUSTERED INDEX clust ON sysdownloadlist(instance_id) CREATE NONCLUSTERED INDEX nc1 ON sysdownloadlist(target_server) CREATE NONCLUSTERED INDEX nc2 ON sysdownloadlist(object_id) END go /**************************************************************/ /* SYSJOBHISTORY */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobhistory') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysjobhistory...' CREATE TABLE sysjobhistory ( instance_id INT IDENTITY NOT NULL, job_id UNIQUEIDENTIFIER NOT NULL, step_id INT NOT NULL, step_name sysname NOT NULL, sql_message_id INT NOT NULL, sql_severity INT NOT NULL, message NVARCHAR(4000) NULL, run_status INT NOT NULL, run_date INT NOT NULL, run_time INT NOT NULL, run_duration INT NOT NULL, operator_id_emailed INT NOT NULL, operator_id_netsent INT NOT NULL, operator_id_paged INT NOT NULL, retries_attempted INT NOT NULL, server sysname NOT NULL ) CREATE UNIQUE CLUSTERED INDEX clust ON sysjobhistory(instance_id) CREATE NONCLUSTERED INDEX nc1 ON sysjobhistory(job_id) END ELSE BEGIN ALTER TABLE sysjobhistory ALTER COLUMN message NVARCHAR(4000) NULL END go /**************************************************************/ /* sysoriginatingservers */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysoriginatingservers') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysoriginatingservers...' CREATE TABLE dbo.sysoriginatingservers ( -- There is only a single MSX server record in this table (originating_server_id = 1) -- 0 is generated by sysoriginatingservers_view and indicates the local server originating_server_id INT CONSTRAINT CK_originating_server_id_MustBe_1 CHECK (originating_server_id = 1) DEFAULT (1) UNIQUE CLUSTERED, originating_server sysname NOT NULL UNIQUE NONCLUSTERED, --Mark this record as a MSX server entry master_server bit CONSTRAINT CK_master_server_MustBe_1 CHECK (master_server = 1) DEFAULT (1) ) END go /**************************************************************/ /* trig_sysoriginatingservers_delete */ /**************************************************************/ PRINT '' PRINT 'Creating trigger trig_sysoriginatingservers_delete...' IF NOT OBJECT_ID('dbo.trig_sysoriginatingservers_delete', 'TR') IS NULL DROP TRIGGER dbo.trig_sysoriginatingservers_delete GO CREATE TRIGGER dbo.trig_sysoriginatingservers_delete ON dbo.sysoriginatingservers FOR DELETE AS BEGIN SET NOCOUNT ON -- Only a single MSX server entry can exist in this table. ie. originating_server_id = 1 and master_server = 1. IF((EXISTS (SELECT * FROM deleted AS d JOIN dbo.sysjobs AS j ON d.originating_server_id = j.originating_server_id)) OR (EXISTS (SELECT * FROM deleted AS d JOIN dbo.sysschedules AS s ON d.originating_server_id = s.originating_server_id))) BEGIN RAISERROR(14380, -1, -1) ROLLBACK TRANSACTION RETURN END END go /**************************************************************/ /* sysoriginatingservers_view */ /**************************************************************/ PRINT '' PRINT 'Creating view sysoriginatingservers_view...' GO IF (NOT OBJECT_ID(N'dbo.sysoriginatingservers_view', 'V') IS NULL) DROP VIEW sysoriginatingservers_view GO CREATE VIEW dbo.sysoriginatingservers_view(originating_server_id, originating_server, master_server) AS SELECT 0 AS originating_server_id, UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) AS originating_server, 0 AS master_server UNION SELECT originating_server_id, originating_server, master_server FROM dbo.sysoriginatingservers GO /**************************************************************/ /* SYSJOBS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobs') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysjobs...' CREATE TABLE sysjobs ( job_id UNIQUEIDENTIFIER NOT NULL, originating_server_id INT NOT NULL, -- REFERENCE enforced by trig_sysjobs_insert_update name sysname NOT NULL, enabled TINYINT NOT NULL, description NVARCHAR(512) NULL, start_step_id INT NOT NULL, category_id INT NOT NULL, owner_sid VARBINARY(85) NOT NULL, notify_level_eventlog INT NOT NULL, notify_level_email INT NOT NULL, notify_level_netsend INT NOT NULL, notify_level_page INT NOT NULL, notify_email_operator_id INT NOT NULL, notify_netsend_operator_id INT NOT NULL, notify_page_operator_id INT NOT NULL, delete_level INT NOT NULL, date_created DATETIME NOT NULL, date_modified DATETIME NOT NULL, version_number INT NOT NULL ) CREATE UNIQUE CLUSTERED INDEX clust ON sysjobs(job_id) CREATE NONCLUSTERED INDEX nc1 ON sysjobs(name) -- NOTE: This is deliberately non-unique CREATE NONCLUSTERED INDEX nc3 ON sysjobs(category_id) CREATE NONCLUSTERED INDEX nc4 ON sysjobs(owner_sid) END go /**************************************************************/ /* trig_sysjobs_insert_update */ /**************************************************************/ PRINT '' PRINT 'Creating trigger trig_sysjobs_insert_update...' IF NOT OBJECT_ID('dbo.trig_sysjobs_insert_update', 'TR') IS NULL DROP TRIGGER dbo.trig_sysjobs_insert_update GO CREATE TRIGGER dbo.trig_sysjobs_insert_update ON dbo.sysjobs FOR INSERT, UPDATE AS BEGIN SET NOCOUNT ON -- Disallow the insert or update if the originating_server_id isn't in sysoriginatingservers_view. IF (EXISTS (SELECT * FROM inserted WHERE inserted.originating_server_id NOT IN (SELECT v.originating_server_id FROM sysoriginatingservers_view AS v))) BEGIN RAISERROR(14379, -1, -1, 'dbo.sysjobs') ROLLBACK TRANSACTION RETURN END END go /**************************************************************/ /* SYSJOBSERVERS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobservers') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysjobservers...' CREATE TABLE sysjobservers ( job_id UNIQUEIDENTIFIER NOT NULL, server_id INT NOT NULL, last_run_outcome TINYINT NOT NULL, last_outcome_message NVARCHAR(4000) NULL, last_run_date INT NOT NULL, last_run_time INT NOT NULL, last_run_duration INT NOT NULL ) CREATE CLUSTERED INDEX clust ON sysjobservers(job_id) CREATE NONCLUSTERED INDEX nc1 ON sysjobservers(server_id) END ELSE BEGIN ALTER TABLE sysjobservers ALTER COLUMN last_outcome_message NVARCHAR(4000) NULL END go /**************************************************************/ /* SYSJOBS_VIEW */ /**************************************************************/ PRINT '' PRINT 'Creating view sysjobs_view...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobs_view') AND (type = 'V'))) DROP VIEW sysjobs_view go CREATE VIEW sysjobs_view AS SELECT jobs.job_id, svr.originating_server, jobs.name, jobs.enabled, jobs.description, jobs.start_step_id, jobs.category_id, jobs.owner_sid, jobs.notify_level_eventlog, jobs.notify_level_email, jobs.notify_level_netsend, jobs.notify_level_page, jobs.notify_email_operator_id, jobs.notify_netsend_operator_id, jobs.notify_page_operator_id, jobs.delete_level, jobs.date_created, jobs.date_modified, jobs.version_number, jobs.originating_server_id, svr.master_server FROM msdb.dbo.sysjobs as jobs JOIN msdb.dbo.sysoriginatingservers_view as svr ON jobs.originating_server_id = svr.originating_server_id --LEFT JOIN msdb.dbo.sysjobservers js ON jobs.job_id = js.job_id WHERE (owner_sid = SUSER_SID()) OR (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'SQLAgentReaderRole'), 0) = 1) OR ( (ISNULL(IS_MEMBER(N'TargetServersRole'), 0) = 1) AND (EXISTS(SELECT * FROM msdb.dbo.sysjobservers js WHERE js.server_id <> 0 AND js.job_id = jobs.job_id))) -- filter out local jobs go IF (OBJECT_ID(N'dbo.syssessions', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table syssessions...' CREATE TABLE dbo.syssessions ( session_id INT IDENTITY PRIMARY KEY, agent_start_date DATETIME NOT NULL ) CREATE UNIQUE NONCLUSTERED INDEX nonclust ON syssessions(agent_start_date) END go IF (OBJECT_ID(N'dbo.sysjobactivity', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysjobactivity...' CREATE TABLE dbo.sysjobactivity ( session_id INT NOT NULL REFERENCES syssessions(session_id), job_id UNIQUEIDENTIFIER NOT NULL REFERENCES sysjobs(job_id) ON DELETE CASCADE, run_requested_date DATETIME NULL, run_requested_source sysname NULL, queued_date DATETIME NULL, start_execution_date DATETIME NULL, last_executed_step_id INT NULL, last_executed_step_date DATETIME NULL, stop_execution_date DATETIME NULL, job_history_id INT NULL, --keeps a reference to the record in sysjobhistory for detailed job information next_scheduled_run_date DATETIME NULL ) CREATE UNIQUE CLUSTERED INDEX clust ON sysjobactivity(session_id, job_id) END go /**************************************************************/ /* SYSJOBSTEPS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobsteps') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysjobsteps...' CREATE TABLE sysjobsteps ( job_id UNIQUEIDENTIFIER NOT NULL, step_id INT NOT NULL, step_name sysname NOT NULL, subsystem NVARCHAR(40) NOT NULL, command NVARCHAR(max) NULL, flags INT NOT NULL, additional_parameters NTEXT NULL, cmdexec_success_code INT NOT NULL, on_success_action TINYINT NOT NULL, on_success_step_id INT NOT NULL, on_fail_action TINYINT NOT NULL, on_fail_step_id INT NOT NULL, server sysname NULL, -- Used only by replication and OLAP database_name sysname NULL, database_user_name sysname NULL, retry_attempts INT NOT NULL, retry_interval INT NOT NULL, os_run_priority INT NOT NULL, -- NOTE: Cannot use TINYINT because we need a signed number output_file_name NVARCHAR(200) NULL, last_run_outcome INT NOT NULL, last_run_duration INT NOT NULL, last_run_retries INT NOT NULL, last_run_date INT NOT NULL, last_run_time INT NOT NULL, proxy_id INT NULL, step_uid UNIQUEIDENTIFIER NULL ) CREATE UNIQUE CLUSTERED INDEX clust ON sysjobsteps(job_id, step_id) CREATE UNIQUE NONCLUSTERED INDEX nc1 ON sysjobsteps(job_id, step_name) CREATE UNIQUE NONCLUSTERED INDEX nc2 ON sysjobsteps(step_uid) END go /**************************************************************/ /* SYSJOBSTEPSLOGS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobstepslogs') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysjobstepslogs...' CREATE TABLE sysjobstepslogs ( log_id INT IDENTITY (1,1) PRIMARY KEY NOT NULL, log NVARCHAR(max) NOT NULL, date_created DATETIME NOT NULL DEFAULT getdate(), date_modified DATETIME NOT NULL DEFAULT getdate(), log_size bigint NOT NULL , step_uid UNIQUEIDENTIFIER NOT NULL REFERENCES sysjobsteps(step_uid) ON DELETE CASCADE ) END go /**************************************************************/ /* SYSSCHEDULES */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysschedules') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysschedules...' CREATE TABLE sysschedules ( schedule_id INT IDENTITY PRIMARY KEY CLUSTERED, schedule_uid UNIQUEIDENTIFIER NOT NULL, originating_server_id INT NOT NULL, -- REFERENCE enforced by trig_sysschedules_insert_update name sysname NOT NULL, owner_sid varbinary(85) NOT NULL, enabled INT NOT NULL, freq_type INT NOT NULL, freq_interval INT NOT NULL, freq_subday_type INT NOT NULL, freq_subday_interval INT NOT NULL, freq_relative_interval INT NOT NULL, freq_recurrence_factor INT NOT NULL, active_start_date INT NOT NULL, active_end_date INT NOT NULL, active_start_time INT NOT NULL, active_end_time INT NOT NULL, date_created DATETIME NOT NULL DEFAULT (GETDATE()), date_modified DATETIME NOT NULL DEFAULT (GETDATE()), version_number INT NOT NULL DEFAULT (1) ) -- CREATE UNIQUE CLUSTERED INDEX clust ON sysschedules(job_id, name) END go /**************************************************************/ /* trig_sysschedules_insert_update */ /**************************************************************/ PRINT '' PRINT 'Creating trigger trig_sysschedules_insert_update...' IF NOT OBJECT_ID('dbo.trig_sysschedules_insert_update', 'TR') IS NULL DROP TRIGGER dbo.trig_sysschedules_insert_update GO CREATE TRIGGER dbo.trig_sysschedules_insert_update ON dbo.sysschedules FOR INSERT, UPDATE AS BEGIN SET NOCOUNT ON -- Disallow the insert or update if the originating_server_id isn't in sysoriginatingservers_view. IF (EXISTS (SELECT * FROM inserted WHERE inserted.originating_server_id NOT IN (SELECT v.originating_server_id FROM sysoriginatingservers_view AS v))) BEGIN RAISERROR(14379, -1, -1, 'dbo.sysschedules') ROLLBACK TRANSACTION RETURN END END go /**************************************************************/ /* SYSSCHEDULES_LOCALSERVER_VIEW */ /**************************************************************/ PRINT '' PRINT 'Creating view sysschedules_localserver_view...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysschedules_localserver_view') AND (type = 'V'))) DROP VIEW sysschedules_localserver_view go CREATE VIEW sysschedules_localserver_view AS SELECT sched.schedule_id, sched.schedule_uid, sched.originating_server_id, sched.name, sched.owner_sid, sched.enabled, sched.freq_type, sched.freq_interval, sched.freq_subday_type, sched.freq_subday_interval, sched.freq_relative_interval, sched.freq_recurrence_factor, sched.active_start_date, sched.active_end_date, sched.active_start_time, sched.active_end_time, sched.date_created, sched.date_modified, sched.version_number, svr.originating_server, svr.master_server FROM msdb.dbo.sysschedules as sched JOIN msdb.dbo.sysoriginatingservers_view as svr ON sched.originating_server_id = svr.originating_server_id WHERE (svr.master_server = 0) AND ( (sched.owner_sid = SUSER_SID()) OR (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'SQLAgentReaderRole'), 0) = 1) ) go /**************************************************************/ /* SYSJOBSCHEDULES */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobschedules') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysjobschedules...' CREATE TABLE sysjobschedules ( schedule_id INT REFERENCES sysschedules(schedule_id), job_id UNIQUEIDENTIFIER REFERENCES sysjobs(job_id), next_run_date INT NOT NULL DEFAULT 0, next_run_time INT NOT NULL DEFAULT 0 ) CREATE UNIQUE CLUSTERED INDEX clust ON sysjobschedules(job_id, schedule_id) CREATE NONCLUSTERED INDEX [NC_sysjobschedules_schedule_id] ON sysjobschedules(schedule_id) END go /**************************************************************/ /* SYSCATEGORIES */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'syscategories') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table syscategories...' CREATE TABLE syscategories ( category_id INT IDENTITY NOT NULL, category_class INT NOT NULL, -- 1 = Job, 2 = Alert, 3 = Operator category_type TINYINT NOT NULL, -- 1 = Local, 2 = Multi-Server [Only relevant if class is 1; otherwise, 3 (None)] name sysname NOT NULL ) CREATE UNIQUE CLUSTERED INDEX clust ON syscategories(name, category_class) END go -- Install standard [permanent] categories (reserved ID range is 0 - 99) SET IDENTITY_INSERT msdb.dbo.syscategories ON DELETE FROM msdb.dbo.syscategories WHERE (category_id < 100) -- Core categories INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 0, 1, 1, N'[Uncategorized (Local)]') -- Local default INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 1, 1, 1, N'Jobs from MSX') -- All jobs downloaded from the MSX are placed in this category INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 2, 1, 2, N'[Uncategorized (Multi-Server)]') -- Multi-server default INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 3, 1, 1, N'Database Maintenance') -- Default for all jobs created by the Maintenance Plan Wizard INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 5, 1, 1, N'Full-Text') -- Default for all jobs created by the Index Server INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 6, 1, 1, N'Log Shipping') -- Default for Log Shipping jobs INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 7, 1, 1, N'Database Engine Tuning Advisor') -- Default for DTA jobs INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES ( 8, 1, 1, N'Data Collector') -- Default for all jobs created by the Data Collector INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (98, 2, 3, N'[Uncategorized]') -- Alert default INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (99, 3, 3, N'[Uncategorized]') -- Operator default -- Replication categories INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (10, 1, 1, N'REPL-Distribution') INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (11, 1, 1, N'REPL-Distribution Cleanup') INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (12, 1, 1, N'REPL-History Cleanup') INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (13, 1, 1, N'REPL-LogReader') INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (14, 1, 1, N'REPL-Merge') INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (15, 1, 1, N'REPL-Snapshot') INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (16, 1, 1, N'REPL-Checkup') INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (17, 1, 1, N'REPL-Subscription Cleanup') INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (18, 1, 1, N'REPL-Alert Response') INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (19, 1, 1, N'REPL-QueueReader') INSERT INTO msdb.dbo.syscategories (category_id, category_class, category_type, name) VALUES (20, 2, 3, N'Replication') SET IDENTITY_INSERT msdb.dbo.syscategories OFF go /**************************************************************/ /* SYSTARGETSERVERS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'systargetservers') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table systargetservers...' CREATE TABLE systargetservers ( server_id INT IDENTITY NOT NULL, server_name sysname NOT NULL, location NVARCHAR(200) NULL, time_zone_adjustment INT NOT NULL, -- The offset from GMT in minutes (set by sp_msx_enlist) enlist_date DATETIME NOT NULL, last_poll_date DATETIME NOT NULL, status INT NOT NULL, -- 1 = Normal, 2 = Offline, 4 = Blocked local_time_at_last_poll DATETIME NOT NULL, -- The local time at the target server as-of the last time it polled the MSX enlisted_by_nt_user NVARCHAR(100) NOT NULL, poll_interval INT NOT NULL -- The MSX polling interval (in seconds) ) EXECUTE sys.sp_bindefault default_current_date, N'systargetservers.enlist_date' EXECUTE sys.sp_bindefault default_current_date, N'systargetservers.last_poll_date' EXECUTE sys.sp_bindefault default_one, N'systargetservers.status' CREATE UNIQUE CLUSTERED INDEX clust ON systargetservers(server_id) CREATE UNIQUE NONCLUSTERED INDEX nc1 ON systargetservers(server_name) END go /**************************************************************/ /* SYSTARGETSERVERS_VIEW */ /**************************************************************/ PRINT '' PRINT 'Creating view systargetservers_view...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'systargetservers_view') AND (type = 'V'))) DROP VIEW systargetservers_view go CREATE VIEW systargetservers_view AS SELECT server_id, server_name, enlist_date, last_poll_date FROM msdb.dbo.systargetservers UNION SELECT 0, CONVERT(sysname, SERVERPROPERTY('ServerName')), CONVERT(DATETIME, N'19981113', 112), CONVERT(DATETIME, N'19981113', 112) go /**************************************************************/ /* SYSTARGETSERVERGROUPS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'systargetservergroups') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table systargetservergroups...' CREATE TABLE systargetservergroups ( servergroup_id INT IDENTITY NOT NULL, name sysname NOT NULL ) CREATE UNIQUE CLUSTERED INDEX clust ON systargetservergroups(name) END go /**************************************************************/ /* SYSTARGETSERVERGROUPMEMBERS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'systargetservergroupmembers') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table systargetservergroupmembers...' CREATE TABLE systargetservergroupmembers ( servergroup_id INT NOT NULL, server_id INT NOT NULL ) CREATE UNIQUE CLUSTERED INDEX clust ON systargetservergroupmembers(servergroup_id, server_id) CREATE NONCLUSTERED INDEX nc1 ON systargetservergroupmembers(server_id) END go /**************************************************************/ /* SYSALERTS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysalerts') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysalerts...' CREATE TABLE sysalerts ( id INT IDENTITY NOT NULL, name sysname NOT NULL, -- Was length 60 in 6.x event_source NVARCHAR(100) NOT NULL, event_category_id INT NULL, event_id INT NULL, message_id INT NOT NULL, -- Was NULL in 6.x severity INT NOT NULL, -- Was NULL in 6.x enabled TINYINT NOT NULL, delay_between_responses INT NOT NULL, last_occurrence_date INT NOT NULL, -- Was NULL in 6.x last_occurrence_time INT NOT NULL, -- Was NULL in 6.x last_response_date INT NOT NULL, -- Was NULL in 6.x last_response_time INT NOT NULL, -- Was NULL in 6.x notification_message NVARCHAR(512) NULL, include_event_description TINYINT NOT NULL, database_name NVARCHAR(512) NULL, event_description_keyword NVARCHAR(100) NULL, occurrence_count INT NOT NULL, count_reset_date INT NOT NULL, -- Was NULL in 6.x count_reset_time INT NOT NULL, -- Was NULL in 6.x job_id UNIQUEIDENTIFIER NOT NULL, -- Was NULL in 6.x has_notification INT NOT NULL, -- New for 7.0 flags INT NOT NULL, -- Was NULL in 6.x performance_condition NVARCHAR(512) NULL, category_id INT NOT NULL -- New for 7.0 ) CREATE UNIQUE CLUSTERED INDEX ByName ON sysalerts(name) CREATE UNIQUE INDEX ByID ON sysalerts(id) END go /**************************************************************/ /* SYSOPERATORS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysoperators') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysoperators...' CREATE TABLE sysoperators ( id INT IDENTITY NOT NULL, name sysname NOT NULL, -- Was length 50 in 6.x enabled TINYINT NOT NULL, email_address NVARCHAR(100) NULL, last_email_date INT NOT NULL, -- Was NULL in 6.x last_email_time INT NOT NULL, -- Was NULL in 6.x pager_address NVARCHAR(100) NULL, last_pager_date INT NOT NULL, -- Was NULL in 6.x last_pager_time INT NOT NULL, -- Was NULL in 6.x weekday_pager_start_time INT NOT NULL, weekday_pager_end_time INT NOT NULL, saturday_pager_start_time INT NOT NULL, saturday_pager_end_time INT NOT NULL, sunday_pager_start_time INT NOT NULL, sunday_pager_end_time INT NOT NULL, pager_days TINYINT NOT NULL, netsend_address NVARCHAR(100) NULL, -- New for 7.0 last_netsend_date INT NOT NULL, -- New for 7.0 last_netsend_time INT NOT NULL, -- New for 7.0 category_id INT NOT NULL -- New for 7.0 ) CREATE UNIQUE CLUSTERED INDEX ByName ON sysoperators(name) CREATE UNIQUE INDEX ByID ON sysoperators(id) END go /**************************************************************/ /* SYSNOTIFICATIONS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysnotifications') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysnotifications...' CREATE TABLE sysnotifications ( alert_id INT NOT NULL, operator_id INT NOT NULL, notification_method TINYINT NOT NULL ) CREATE UNIQUE CLUSTERED INDEX ByAlertIDAndOperatorID ON sysnotifications(alert_id, operator_id) END go /**************************************************************/ /* */ /* M A I N T E N A N C E P L A N S */ /* */ /**************************************************************/ /**************************************************************/ /* sysmaintplan_subplans */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmaintplan_subplans') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysmaintplan_subplans...' -- This table stores the DTS package associated with the maintenance plan -- It also stored metadata about the maintenance plan such as its name, description etc CREATE TABLE sysmaintplan_subplans ( subplan_id UNIQUEIDENTIFIER NOT NULL CONSTRAINT [PK_sysmaintplan_subplan] PRIMARY KEY CLUSTERED, subplan_name sysname NOT NULL, subplan_description NVARCHAR(512) NULL, plan_id UNIQUEIDENTIFIER NOT NULL, job_id UNIQUEIDENTIFIER NOT NULL CONSTRAINT FK_subplan_job_id FOREIGN KEY (job_id) REFERENCES sysjobs(job_id), msx_job_id UNIQUEIDENTIFIER DEFAULT NULL NULL CONSTRAINT FK_subplan_msx_job_id FOREIGN KEY (msx_job_id) REFERENCES sysjobs(job_id), schedule_id INT NULL CONSTRAINT FK_subplan_schedule_id FOREIGN KEY (schedule_id) REFERENCES sysschedules(schedule_id), msx_plan bit DEFAULT 0 NOT NULL ) END go /**************************************************************/ /* sysmaintplan_log */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmaintplan_log') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysmaintplan_log...' -- This table stores the maintenance plan log info CREATE TABLE sysmaintplan_log ( task_detail_id UNIQUEIDENTIFIER NOT NULL CONSTRAINT [PK_sysmaintplan_taskdetail_id] PRIMARY KEY CLUSTERED, plan_id UNIQUEIDENTIFIER NULL, subplan_id UNIQUEIDENTIFIER NULL CONSTRAINT [FK_sysmaintplan_log_subplan_id] FOREIGN KEY (subplan_id) REFERENCES sysmaintplan_subplans(subplan_id), start_time DATETIME NULL, end_time DATETIME NULL, succeeded BIT NULL, logged_remotely bit not null default (0), source_server_name nvarchar (128) NULL, plan_name nvarchar (128) NULL, subplan_name nvarchar (128) NULL ) END go /**************************************************************/ /* sysmaintplan_logdetail */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmaintplan_logdetail') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysmaintplan_logdetail...' -- This table stores the maintenance plan log details CREATE TABLE sysmaintplan_logdetail ( task_detail_id UNIQUEIDENTIFIER NOT NULL CONSTRAINT [FK_sysmaintplan_log_detail_task_id] FOREIGN KEY (task_detail_id) REFERENCES sysmaintplan_log(task_detail_id) ON DELETE CASCADE, line1 NVARCHAR(256) NOT NULL, line2 NVARCHAR(256) NULL, line3 NVARCHAR(256) NULL, line4 NVARCHAR(256) NULL, line5 NVARCHAR(256) NULL, server_name sysname NOT NULL, start_time DATETIME NULL, end_time DATETIME NULL, error_number INT NULL, error_message NVARCHAR(max) NULL, command NVARCHAR(max) NULL, succeeded BIT NULL ) END go /**************************************************************/ /* SYSTASKIDS */ /* */ /* This table provides a mapping between new GUID job ID's */ /* and 6.x INT task ID's. */ /* Entries are made in this table for all existing 6.x tasks */ /* and for all new tasks added using the 7.0 version of */ /* sp_addtask. */ /* Callers of the 7.0 version of sp_helptask will ONLY see */ /* tasks [jobs] that have a corresponding entry in this table */ /* [IE. Jobs created with sp_add_job will not be returned]. */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'systaskids') AND (type = 'U'))) BEGIN CREATE TABLE systaskids ( task_id INT IDENTITY NOT NULL, job_id UNIQUEIDENTIFIER NOT NULL ) CREATE CLUSTERED INDEX clust ON systaskids(job_id) END go /**************************************************************/ /* SYSCACHEDCREDENTIALS */ /* */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'syscachedcredentials') AND (type = 'U'))) BEGIN CREATE TABLE syscachedcredentials ( login_name sysname COLLATE database_default NOT NULL PRIMARY KEY, has_server_access BIT NOT NULL DEFAULT 0, is_sysadmin_member BIT NOT NULL DEFAULT 0, cachedate DATETIME NOT NULL DEFAULT getdate() ) END go --------------------------------------------------------------- -- Replication Datatype mapping tables --------------------------------------------------------------- exec sys.sp_MSrepl_createdatatypemappings go CHECKPOINT go /**************************************************************/ /* */ /* C O R E P R O C E D U R E S */ /* */ /**************************************************************/ PRINT '' PRINT 'Creating function SQLAGENT_SUSER_SNAME ...' IF (NOT OBJECT_ID(N'dbo.SQLAGENT_SUSER_SNAME', 'FN') IS NULL) DROP FUNCTION dbo.SQLAGENT_SUSER_SNAME go CREATE FUNCTION dbo.SQLAGENT_SUSER_SNAME(@user_sid VARBINARY(85)) RETURNS sysname AS BEGIN DECLARE @ret sysname IF @user_sid = 0xFFFFFFFF SELECT @ret = N'$(SQLAgentAccount)' ELSE SELECT @ret = SUSER_SNAME(@user_sid) RETURN @ret END go PRINT '' PRINT 'Creating function SQLAGENT_SUSER_SID ...' IF (NOT OBJECT_ID(N'dbo.SQLAGENT_SUSER_SID', 'FN') IS NULL) DROP FUNCTION dbo.SQLAGENT_SUSER_SID go CREATE FUNCTION dbo.SQLAGENT_SUSER_SID(@user_name sysname) RETURNS VARBINARY(85) AS BEGIN DECLARE @ret VARBINARY(85) IF @user_name = N'$(SQLAgentAccount)' SELECT @ret = 0xFFFFFFFF ELSE SELECT @ret = SUSER_SID(@user_name, 0) RETURN @ret END go ----------------------------------------------------------- -- get_principal_id : retrieves principal_id for a given sid -- ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.get_principal_id', 'FN') IS NULL DROP FUNCTION dbo.get_principal_id GO CREATE FUNCTION dbo.get_principal_id(@principal_sid varbinary(85)) RETURNS int AS BEGIN DECLARE @principal_id int SELECT @principal_id=principal_id FROM msdb.sys.database_principals WHERE sid=@principal_sid RETURN @principal_id END GO ----------------------------------------------------------- -- get_principal_sid : retrieves principal sid from principal_id -- ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.get_principal_sid', 'FN') IS NULL DROP FUNCTION dbo.get_principal_sid GO CREATE FUNCTION dbo.get_principal_sid(@principal_id int) RETURNS varbinary(85) AS BEGIN DECLARE @principal_sid varbinary(85) SELECT @principal_sid=sid FROM msdb.sys.database_principals WHERE principal_id=@principal_id RETURN @principal_sid END GO /**************************************************************/ /* SP_SQLAGENT_IS_SRVROLEMEMBER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure SP_SQLAGENT_IS_SRVROLEMEMBER...' IF (NOT OBJECT_ID(N'dbo.sp_sqlagent_is_srvrolemember', 'P') IS NULL) DROP PROCEDURE dbo.sp_sqlagent_is_srvrolemember go CREATE PROCEDURE sp_sqlagent_is_srvrolemember @role_name sysname, @login_name sysname AS BEGIN DECLARE @is_member INT SET NOCOUNT ON IF @role_name IS NULL OR @login_name IS NULL RETURN(0) SELECT @is_member = 0 --IS_SRVROLEMEMBER works only if the login to be tested is provisioned with sqlserver if( @login_name = SUSER_SNAME()) SELECT @is_member = IS_SRVROLEMEMBER(@role_name) else SELECT @is_member = IS_SRVROLEMEMBER(@role_name, @login_name) --try to impersonate. A try catch is used because we can have @name as NT groups also IF @is_member IS NULL BEGIN BEGIN TRY if( is_srvrolemember('sysadmin') = 1) begin EXECUTE AS LOGIN = @login_name -- impersonate SELECT @is_member = IS_SRVROLEMEMBER(@role_name) -- check role membership REVERT -- revert back end END TRY BEGIN CATCH SELECT @is_member = 0 END CATCH END RETURN ISNULL(@is_member,0) END go /**************************************************************/ /* SP_VERIFY_CATEGORY_IDENTIFIERS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_category_identifiers...' IF (NOT OBJECT_ID(N'dbo.sp_verify_category_identifiers', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_category_identifiers go CREATE PROCEDURE sp_verify_category_identifiers @name_of_name_parameter [varchar](60), @name_of_id_parameter [varchar](60), @category_name [sysname] OUTPUT, @category_id [INT] OUTPUT AS BEGIN DECLARE @retval INT DECLARE @category_id_as_char NVARCHAR(36) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter)) SELECT @name_of_id_parameter = LTRIM(RTRIM(@name_of_id_parameter)) SELECT @category_name = LTRIM(RTRIM(@category_name)) IF (@category_name = N'') SELECT @category_name = NULL IF ((@category_name IS NOT NULL) AND (@category_id IS NOT NULL)) BEGIN RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- Check category id IF (@category_id IS NOT NULL) BEGIN SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = @category_id) IF (@category_name IS NULL) BEGIN SELECT @category_id_as_char = CONVERT(nvarchar(36), @category_id) RAISERROR(14262, -1, -1, '@category_id', @category_id_as_char) RETURN(1) -- Failure END END ELSE -- Check category name IF (@category_name IS NOT NULL) BEGIN -- The name is not ambiguous, so get the corresponding category_id (if the job exists) SELECT @category_id = category_id FROM msdb.dbo.syscategories WHERE (name = @category_name) IF (@category_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@category_name', @category_name) RETURN(1) -- Failure END END RETURN(0) -- Success END go PRINT '' PRINT 'Creating function agent_datetime...' IF (NOT OBJECT_ID(N'dbo.agent_datetime', 'FN') IS NULL) DROP FUNCTION dbo.agent_datetime go CREATE FUNCTION agent_datetime(@date int, @time int) RETURNS DATETIME AS BEGIN RETURN ( CONVERT(DATETIME, CONVERT(NVARCHAR(4),@date / 10000) + N'-' + CONVERT(NVARCHAR(2),(@date % 10000)/100) + N'-' + CONVERT(NVARCHAR(2),@date % 100) + N' ' + CONVERT(NVARCHAR(2),@time / 10000) + N':' + CONVERT(NVARCHAR(2),(@time % 10000)/100) + N':' + CONVERT(NVARCHAR(2),@time % 100), 120) ) END go /**************************************************************/ /* SP_VERIFY_PROXY_IDENTIFIERS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_proxy_identifiers...' IF (NOT OBJECT_ID(N'dbo.sp_verify_proxy_identifiers', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_proxy_identifiers go CREATE PROCEDURE sp_verify_proxy_identifiers @name_of_name_parameter [varchar](60), @name_of_id_parameter [varchar](60), @proxy_name [sysname] OUTPUT, @proxy_id [INT] OUTPUT AS BEGIN DECLARE @retval INT DECLARE @proxy_id_as_char NVARCHAR(36) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter)) SELECT @name_of_id_parameter = LTRIM(RTRIM(@name_of_id_parameter)) SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) IF (@proxy_name = N'') SELECT @proxy_name = NULL IF ((@proxy_name IS NULL) AND (@proxy_id IS NULL)) OR ((@proxy_name IS NOT NULL) AND (@proxy_id IS NOT NULL)) BEGIN RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- Check proxy id IF (@proxy_id IS NOT NULL) BEGIN SELECT @proxy_name = name FROM msdb.dbo.sysproxies WHERE (proxy_id = @proxy_id) IF (@proxy_name IS NULL) BEGIN SELECT @proxy_id_as_char = CONVERT(nvarchar(36), @proxy_id) RAISERROR(14262, -1, -1, '@proxy_id', @proxy_id_as_char) RETURN(1) -- Failure END END ELSE -- Check proxy name IF (@proxy_name IS NOT NULL) BEGIN -- The name is not ambiguous, so get the corresponding proxy_id (if the job exists) SELECT @proxy_id = proxy_id FROM msdb.dbo.sysproxies WHERE (name = @proxy_name) IF (@proxy_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@proxy_name', @proxy_name) RETURN(1) -- Failure END END RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_CREDENTIAL_IDENTIFIERS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_credential_identifiers...' IF (NOT OBJECT_ID(N'dbo.sp_verify_credential_identifiers', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_credential_identifiers go CREATE PROCEDURE sp_verify_credential_identifiers @name_of_name_parameter [varchar](60), @name_of_id_parameter [varchar](60), @credential_name [sysname] OUTPUT, @credential_id [INT] OUTPUT AS BEGIN DECLARE @retval INT DECLARE @credential_id_as_char NVARCHAR(36) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter)) SELECT @name_of_id_parameter = LTRIM(RTRIM(@name_of_id_parameter)) SELECT @credential_name = LTRIM(RTRIM(@credential_name)) IF (@credential_name = N'') SELECT @credential_name = NULL IF ((@credential_name IS NULL) AND (@credential_id IS NULL)) OR ((@credential_name IS NOT NULL) AND (@credential_id IS NOT NULL)) BEGIN RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- Check credential_id IF (@credential_id IS NOT NULL) BEGIN SELECT @credential_name = name FROM master.sys.credentials WHERE (credential_id = @credential_id) IF (@credential_name IS NULL) BEGIN SELECT @credential_id_as_char = CONVERT(nvarchar(36), @credential_id) RAISERROR(14262, -1, -1, '@credential_id', @credential_id_as_char) RETURN(1) -- Failure END END ELSE -- Check proxy name IF (@credential_name IS NOT NULL) BEGIN -- The name is not ambiguous, so get the corresponding credential_id (if the job exists) SELECT @credential_id = credential_id FROM master.sys.credentials WHERE (name = @credential_name) IF (@credential_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@credential_name', @credential_name) RETURN(1) -- Failure END END RETURN(0) -- Success END go /**************************************************************/ /* sp_verify_subsystems */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_subsystems...' IF (NOT OBJECT_ID(N'dbo.sp_verify_subsystems', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_subsystems go CREATE PROCEDURE dbo.sp_verify_subsystems @syssubsytems_refresh_needed BIT = 0 AS BEGIN SET NOCOUNT ON DECLARE @retval INT DECLARE @InstRootPath nvarchar(512) DECLARE @VersionRootPath nvarchar(512) DECLARE @ComRootPath nvarchar(512) DECLARE @DtsRootPath nvarchar(512) DECLARE @SQLPSPath nvarchar(512) DECLARE @DTExec nvarchar(512) DECLARE @DTExecExists INT DECLARE @ToolsPath nvarchar(512) IF ( (@syssubsytems_refresh_needed=1) OR (NOT EXISTS(select * from syssubsystems)) ) BEGIN EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @InstRootPath OUTPUT IF @InstRootPath IS NULL BEGIN RAISERROR(14658, -1, -1) WITH LOG RETURN (1) END SELECT @InstRootPath = @InstRootPath + N'\binn\' EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\Microsoft Sql Server\100', N'VerSpecificRootDir', @VersionRootPath OUTPUT IF @VersionRootPath IS NULL BEGIN RAISERROR(14659, -1, -1) WITH LOG RETURN(1) END EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\Microsoft SQL Server\100\SSIS\Setup\DTSPath', N'', @DtsRootPath OUTPUT, N'no_output' IF (@DtsRootPath IS NOT NULL) BEGIN SELECT @DtsRootPath = @DtsRootPath + N'Binn\' SELECT @DTExec = @DtsRootPath + N'DTExec.exe' CREATE TABLE #t (file_exists int, is_directory int, parent_directory_exists int) INSERT #t EXEC xp_fileexist @DTExec SELECT TOP 1 @DTExecExists=file_exists from #t DROP TABLE #t IF ((@DTExecExists IS NULL) OR (@DTExecExists = 0)) SET @DtsRootPath = NULL END SELECT @ComRootPath = @VersionRootPath + N'COM\' create table #Platform(ID int, Name sysname, Internal_Value int NULL, Value nvarchar(512)) insert #Platform exec master.dbo.xp_msver 'Platform' if EXISTS(select * from #Platform where Value like '%64%') EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Wow6432Node\Microsoft\Microsoft Sql Server\100\Tools\ClientSetup', N'SQLPath', @ToolsPath OUTPUT else EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\Microsoft Sql Server\100\Tools\ClientSetup', N'SQLPath', @ToolsPath OUTPUT drop table #Platform SELECT @SQLPSPath = @ToolsPath + N'\Binn\SQLPS.exe' -- Procedure must start its own transaction if we don't have one already. DECLARE @TranCounter INT; SET @TranCounter = @@TRANCOUNT; IF @TranCounter = 0 BEGIN BEGIN TRANSACTION; END -- Obtain processor count to determine maximum number of threads per subsystem DECLARE @xp_results TABLE ( id INT NOT NULL, name NVARCHAR(30) COLLATE database_default NOT NULL, internal_value INT NULL, character_value NVARCHAR(212) COLLATE database_default NULL ) INSERT INTO @xp_results EXECUTE master.dbo.xp_msver DECLARE @processor_count INT SELECT @processor_count = internal_value from @xp_results where id=16 -- ProcessorCount -- Modify database. BEGIN TRY --create subsystems --TSQL subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'TSQL') INSERT syssubsystems VALUES ( 1, N'TSQL',14556, FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), 20 * @processor_count ) --ActiveScripting subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'ActiveScripting') INSERT syssubsystems VALUES ( 2, N'ActiveScripting', 14555, @InstRootPath + N'SQLATXSS.DLL',NULL,N'ActiveScriptStart',N'ActiveScriptEvent',N'ActiveScriptStop', 10 * @processor_count ) --CmdExec subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'CmdExec') INSERT syssubsystems VALUES ( 3, N'CmdExec', 14550, @InstRootPath + N'SQLCMDSS.DLL',NULL,N'CmdExecStart',N'CmdEvent',N'CmdExecStop', 10 * @processor_count ) --Snapshot subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'Snapshot') INSERT syssubsystems VALUES ( 4, N'Snapshot', 14551, @InstRootPath + N'SQLREPSS.DLL', @ComRootPath + N'SNAPSHOT.EXE', N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count ) --LogReader subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'LogReader') INSERT syssubsystems VALUES ( 5, N'LogReader', 14552, @InstRootPath + N'SQLREPSS.DLL', @ComRootPath + N'logread.exe',N'ReplStart',N'ReplEvent',N'ReplStop',25 * @processor_count ) --Distribution subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'Distribution') INSERT syssubsystems VALUES ( 6, N'Distribution', 14553, @InstRootPath + N'SQLREPSS.DLL', @ComRootPath + N'DISTRIB.EXE',N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count ) --Merge subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'Merge') INSERT syssubsystems VALUES ( 7, N'Merge', 14554, @InstRootPath + N'SQLREPSS.DLL',@ComRootPath + N'REPLMERG.EXE',N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count ) --QueueReader subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'QueueReader') INSERT syssubsystems VALUES ( 8, N'QueueReader', 14581, @InstRootPath + N'SQLREPSS.dll',@ComRootPath + N'qrdrsvc.exe',N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count ) --ANALYSISQUERY subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'ANALYSISQUERY') INSERT syssubsystems VALUES ( 9, N'ANALYSISQUERY', 14513, @InstRootPath + N'SQLOLAPSS.DLL',NULL,N'OlapStart',N'OlapQueryEvent',N'OlapStop',100 * @processor_count ) --ANALYSISCOMMAND subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'ANALYSISCOMMAND') INSERT syssubsystems VALUES ( 10, N'ANALYSISCOMMAND', 14514, @InstRootPath + N'SQLOLAPSS.DLL',NULL,N'OlapStart',N'OlapCommandEvent',N'OlapStop',100 * @processor_count ) IF(@DtsRootPath IS NOT NULL) BEGIN --DTS subsystem IF (NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'SSIS') ) INSERT syssubsystems VALUES ( 11, N'SSIS', 14538, @InstRootPath + N'SQLDTSSS.DLL',@DtsRootPath + N'DTExec.exe',N'DtsStart',N'DtsEvent',N'DtsStop',100 * @processor_count ) ELSE UPDATE syssubsystems SET agent_exe = @DtsRootPath + N'DTExec.exe' WHERE subsystem = N'SSIS' END ELSE BEGIN IF EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'SSIS') DELETE FROM syssubsystems WHERE subsystem = N'SSIS' END --PowerShell subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'PowerShell') INSERT syssubsystems VALUES ( 12, N'PowerShell', 14698, @InstRootPath + N'SQLPOWERSHELLSS.DLL', @SQLPSPath, N'PowerShellStart',N'PowerShellEvent',N'PowerShellStop',2 ) END TRY BEGIN CATCH DECLARE @ErrorMessage NVARCHAR(400) DECLARE @ErrorSeverity INT DECLARE @ErrorState INT SELECT @ErrorMessage = ERROR_MESSAGE() SELECT @ErrorSeverity = ERROR_SEVERITY() SELECT @ErrorState = ERROR_STATE() -- Roll back the transaction that we started if we are not nested IF @TranCounter = 0 BEGIN ROLLBACK TRANSACTION; END -- if we are nested inside another transaction just raise the -- error and let the outer transaction do the rollback RAISERROR (@ErrorMessage, -- Message text. @ErrorSeverity, -- Severity. @ErrorState -- State. ) RETURN (1) END CATCH END --(NOT EXISTS(select * from syssubsystems)) -- commit the transaction we started IF @TranCounter = 0 BEGIN COMMIT TRANSACTION; END RETURN(0) -- Success END go /**************************************************************/ /* sp_verify_subsystem_identifiers */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_subsystem_identifiers...' IF (NOT OBJECT_ID(N'dbo.sp_verify_subsystem_identifiers', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_subsystem_identifiers go CREATE PROCEDURE dbo.sp_verify_subsystem_identifiers @name_of_name_parameter [varchar](60), @name_of_id_parameter [varchar](60), @subsystem_name [sysname] OUTPUT, @subsystem_id [INT] OUTPUT AS BEGIN DECLARE @retval INT DECLARE @subsystem_id_as_char NVARCHAR(36) SET NOCOUNT ON -- this call will populate subsystems table if necessary EXEC @retval = msdb.dbo.sp_verify_subsystems IF @retval <> 0 RETURN(@retval) -- Remove any leading/trailing spaces from parameters SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter)) SELECT @name_of_id_parameter = LTRIM(RTRIM(@name_of_id_parameter)) SELECT @subsystem_name = LTRIM(RTRIM(@subsystem_name)) IF (@subsystem_name = N'') SELECT @subsystem_name = NULL IF ((@subsystem_name IS NULL) AND (@subsystem_id IS NULL)) OR ((@subsystem_name IS NOT NULL) AND (@subsystem_id IS NOT NULL)) BEGIN RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- Check subsystem_id IF (@subsystem_id IS NOT NULL) BEGIN SELECT @subsystem_name = subsystem FROM msdb.dbo.syssubsystems WHERE (subsystem_id = @subsystem_id) IF (@subsystem_name IS NULL) BEGIN SELECT @subsystem_id_as_char = CONVERT(nvarchar(36), @subsystem_id) RAISERROR(14262, -1, -1, '@subsystem_id', @subsystem_id_as_char) RETURN(1) -- Failure END END ELSE -- Check subsystem name IF (@subsystem_name IS NOT NULL) BEGIN -- Make sure Dts is translated into new subsystem's name SSIS IF UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS) = N'DTS' BEGIN SET @subsystem_name = N'SSIS' END -- The name is not ambiguous, so get the corresponding subsystem_id (if the subsystem exists) SELECT @subsystem_id = subsystem_id FROM msdb.dbo.syssubsystems WHERE (UPPER(subsystem collate SQL_Latin1_General_CP1_CS_AS) = UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS)) IF (@subsystem_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@subsystem_name', @subsystem_name) RETURN(1) -- Failure END END RETURN(0) -- Success END go /**************************************************************/ /* sp_verify_login_identifiers */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_login_identifiers...' IF (NOT OBJECT_ID(N'dbo.sp_verify_login_identifiers', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_login_identifiers go CREATE PROCEDURE dbo.sp_verify_login_identifiers @login_name [nvarchar](256), @fixed_server_role [nvarchar](256), @msdb_role [nvarchar](256), @name [nvarchar](256) OUTPUT, @sid varbinary(85) OUTPUT, @flags INT OUTPUT AS BEGIN DECLARE @retval INT DECLARE @raise_error bit SET NOCOUNT ON SELECT @flags = -1, @raise_error = 0 SELECT @sid = NULL IF @login_name IS NOT NULL BEGIN --check validity --use the new optional parameter of SUSER_SID to have a case insensitive comparation for NT users SELECT @sid = SUSER_SID(@login_name, 0) IF @sid IS NULL BEGIN RAISERROR(14520, -1, -1, @login_name) RETURN(1) -- Failure END SELECT @name = @login_name, @flags = 0 END IF COALESCE(@login_name, @fixed_server_role, @msdb_role) IS NULL BEGIN RAISERROR(14519, -1, -1) RETURN(1) -- Failure END IF @fixed_server_role IS NOT NULL AND @flags <> -1 SELECT @raise_error = 1 ELSE IF @fixed_server_role IS NOT NULL --check validity BEGIN -- IS_SRVROLEMEMBER return NULL for an invalid server role IF ISNULL(IS_SRVROLEMEMBER(@fixed_server_role), -1) = -1 BEGIN RAISERROR(14521, -1, -1, @fixed_server_role) RETURN(1) -- Failure END SELECT @name = @fixed_server_role, @flags = 1 SELECT @sid = SUSER_SID(@fixed_server_role) END IF @msdb_role IS NOT NULL AND @flags <> -1 SELECT @raise_error = 1 ELSE IF @msdb_role IS NOT NULL BEGIN --check the correctness of msdb role IF ISNULL(IS_MEMBER(@msdb_role), -1) = -1 BEGIN RAISERROR(14522, -1, -1, @msdb_role) RETURN(1) -- Failure END SELECT @sid = sid from sys.database_principals WHERE UPPER(@msdb_role collate SQL_Latin1_General_CP1_CS_AS) = UPPER(name collate SQL_Latin1_General_CP1_CS_AS) AND type = 'R' IF @sid IS NULL BEGIN RAISERROR(14522, -1, -1, @msdb_role) RETURN(1) -- Failure END SELECT @name = @msdb_role, @flags = 2 END IF @raise_error = 1 BEGIN RAISERROR(14519, -1, -1) RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_PROXY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_proxy...' IF (NOT OBJECT_ID(N'dbo.sp_verify_proxy', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_proxy go CREATE PROCEDURE dbo.sp_verify_proxy @proxy_id [INT] = NULL, @proxy_name [sysname], @enabled [tinyint], @description [nvarchar](512) = NULL AS BEGIN DECLARE @return_code INT SET NOCOUNT ON -- Check if the NewName is unique IF (EXISTS ( SELECT * FROM msdb.dbo.sysproxies WHERE (name = @proxy_name) AND proxy_id <> ISNULL(@proxy_id,0) )) BEGIN RAISERROR(14261, 16, 1, '@name', @proxy_name) RETURN(1) -- Failure END -- Enabled must be 0 or 1 IF (@enabled NOT IN (0, 1)) BEGIN RAISERROR(14266, 16, 1, '@enabled', '0, 1') RETURN(1) -- Failure END RETURN(0) END go /**************************************************************/ /* SP_ADD_PROXY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_proxy...' IF (NOT OBJECT_ID(N'dbo.sp_add_proxy', 'P') IS NULL) DROP PROCEDURE dbo.sp_add_proxy go CREATE PROCEDURE dbo.sp_add_proxy @proxy_name [sysname], @enabled [tinyint] = 1, @description [nvarchar](512) = NULL, @credential_name [sysname] = NULL, @credential_id [INT] = NULL, @proxy_id [int] = NULL OUTPUT AS BEGIN DECLARE @retval INT DECLARE @full_name NVARCHAR(257) --two sysnames + \ DECLARE @user_sid VARBINARY(85) DECLARE @cred_date_time DATETIME SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) SELECT @description = LTRIM(RTRIM(@description)) IF @proxy_name = '' SELECT @proxy_name = NULL IF @description = '' SELECT @description = NULL EXECUTE @retval = sp_verify_proxy NULL, @proxy_name, @enabled, @description IF (@retval <> 0) RETURN(1) -- Failure EXECUTE @retval = sp_verify_credential_identifiers '@credential_name', '@credential_id', @credential_name OUTPUT, @credential_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- warn if the user_domain\user_name does not exist SELECT @full_name = credential_identity, @cred_date_time = create_date from master.sys.credentials WHERE credential_id = @credential_id --force case insensitive comparation for NT users SELECT @user_sid = SUSER_SID(@full_name,0) IF @user_sid IS NULL BEGIN RAISERROR(14529, -1, -1, @full_name) RETURN(1) END -- Finally, do the actual INSERT INSERT INTO msdb.dbo.sysproxies ( name, credential_id, enabled, description, user_sid, credential_date_created ) VALUES ( @proxy_name, @credential_id, @enabled, @description, @user_sid, @cred_date_time ) --get newly created proxy_id; SELECT @proxy_id = SCOPE_IDENTITY() END go /**************************************************************/ /* SP_DELETE_PROXY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_proxy...' IF (NOT OBJECT_ID(N'dbo.sp_delete_proxy', 'P') IS NULL) DROP PROCEDURE dbo.sp_delete_proxy go CREATE PROCEDURE dbo.sp_delete_proxy @proxy_id int = NULL, @proxy_name sysname = NULL -- must specify only one of above parameters to identify the proxy AS BEGIN DECLARE @retval INT SET NOCOUNT ON EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure --no jobsteps should use this proxy IF EXISTS (SELECT * FROM sysjobsteps WHERE @proxy_id = proxy_id) BEGIN RAISERROR(14518, -1, -1, @proxy_id) RETURN(1) -- Failure END BEGIN TRANSACTION --delete any association between subsystems and this proxy DELETE sysproxysubsystem WHERE proxy_id = @proxy_id --delete any association between logins and this proxy DELETE sysproxylogin WHERE proxy_id = @proxy_id -- delete the entry in sysproxies table DELETE sysproxies WHERE proxy_id = @proxy_id COMMIT RETURN(0) END go /**************************************************************/ /* SP_UPDATE_PROXY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_proxy...' IF (NOT OBJECT_ID(N'dbo.sp_update_proxy', 'P') IS NULL) DROP PROCEDURE dbo.sp_update_proxy go CREATE PROCEDURE dbo.sp_update_proxy @proxy_id [int] = NULL, @proxy_name [sysname] = NULL, -- must specify only one of above parameter identify the proxy @credential_name [sysname] = NULL, @credential_id [INT] = NULL, @new_name [sysname] = NULL, @enabled [tinyint] = NULL, @description [nvarchar](512) = NULL AS BEGIN DECLARE @x_new_name [sysname] DECLARE @x_credential_id [int] DECLARE @x_enabled [tinyint] DECLARE @x_description [nvarchar](512) DECLARE @x_credential_date_created [datetime] DECLARE @user_sid VARBINARY(85) DECLARE @full_name [sysname] --two sysnames + \ DECLARE @retval INT SET NOCOUNT ON EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure IF @credential_id IS NOT NULL OR @credential_name IS NOT NULL BEGIN EXECUTE @retval = sp_verify_credential_identifiers '@credential_name', '@credential_id', @credential_name OUTPUT, @credential_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SELECT @new_name = LTRIM(RTRIM(@new_name)) SELECT @description = LTRIM(RTRIM(@description)) -- Turn [nullable] empty string parameters into NULLs IF @new_name = '' SELECT @new_name = NULL IF @description = '' SELECT @description = NULL -- Set the x_ (existing) variables SELECT @x_new_name = name, @x_credential_id = credential_id, @x_enabled = enabled, @x_description = description, @x_credential_date_created = credential_date_created FROM sysproxies WHERE proxy_id = @proxy_id --get the new date from credential table IF (@credential_id IS NOT NULL) SELECT @x_credential_date_created = create_date FROM master.sys.credentials WHERE credential_id = @credential_id -- Fill out the values for all non-supplied parameters from the existing values IF (@new_name IS NULL) SELECT @new_name = @x_new_name IF (@credential_id IS NULL) SELECT @credential_id = @x_credential_id IF (@enabled IS NULL) SELECT @enabled = @x_enabled IF (@description IS NULL) SELECT @description = @x_description -- warn if the user_domain\user_name does not exist SELECT @full_name = credential_identity from master.sys.credentials WHERE credential_id = @credential_id --force case insensitive comparation for NT users SELECT @user_sid = SUSER_SID(@full_name, 0) IF @user_sid IS NULL BEGIN RAISERROR(14529, -1, -1, @full_name) RETURN(1) END -- Finally, do the actual UPDATE UPDATE msdb.dbo.sysproxies SET name = @new_name, credential_id = @credential_id, user_sid = @user_sid, enabled = @enabled, description = @description, credential_date_created = @x_credential_date_created --@x_ is OK in this case WHERE proxy_id = @proxy_id END go /**************************************************************/ /* SP_SQLAGENT_IS_MEMBER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_is_member...' IF (NOT OBJECT_ID(N'dbo.sp_sqlagent_is_member', 'P') IS NULL) DROP PROCEDURE dbo.sp_sqlagent_is_member go -- check if a login is member of NT group\database role -- -- if we specify a NT group SID @login_sid should be NOT NULL -- -- if a @role_principal_id is specified, a NULL login is allowed -- in this case we check if the msdb database user associated -- with the current security context is member of the specified -- msdb database role (this allows us to verify if a particular -- msdb database loginless msdb user is member of that msdb role) CREATE PROCEDURE dbo.sp_sqlagent_is_member ( @group_sid VARBINARY(85) = NULL, @role_principal_id INT = NULL, @login_sid VARBINARY(85) ) AS BEGIN DECLARE @ret_success INT DECLARE @login NVARCHAR(256) DECLARE @impersonated INT DECLARE @group_name NVARCHAR(256) SELECT @ret_success = 0 --failure SELECT @impersonated = 0 IF (@group_sid IS NOT NULL AND @login_sid IS NULL) RETURN(0) --a sysadmin can check for every user group membership IF (@login_sid IS NOT NULL) AND (ISNULL(IS_SRVROLEMEMBER('sysadmin'),0) = 1) BEGIN --get login name from principal_id SELECT @login = SUSER_SNAME(@login_sid) IF SUSER_SNAME() <> @login BEGIN --impersonate EXECUTE sp_setuserbylogin @login SELECT @impersonated = 1 END END IF @group_sid IS NOT NULL SELECT @group_name = SUSER_SNAME(@group_sid) ELSE SELECT @group_name = USER_NAME(@role_principal_id) -- return success, if login is member of the group, and failure if group doesnt exist or login is not member of the group SELECT @ret_success = ISNULL(IS_MEMBER(@group_name),0) --revert to self IF @impersonated = 1 EXECUTE sp_setuserbylogin RETURN @ret_success END go /**************************************************************/ /* sp_verify_proxy_permissions */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_proxy_permissions...' IF (NOT OBJECT_ID(N'dbo.sp_verify_proxy_permissions', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_proxy_permissions go CREATE PROCEDURE dbo.sp_verify_proxy_permissions @subsystem_name sysname, @proxy_id INT = NULL, @name NVARCHAR(256) = NULL, @raise_error INT = 1, @allow_disable_proxy INT = 0, @verify_special_account INT = 0, @check_only_read_perm INT = 0 AS BEGIN DECLARE @retval INT DECLARE @granted_sid VARBINARY(85) DECLARE @is_member INT DECLARE @is_sysadmin BIT DECLARE @flags TINYINT DECLARE @enabled TINYINT DECLARE @name_sid VARBINARY(85) DECLARE @role_from_sid sysname DECLARE @name_from_sid sysname DECLARE @is_SQLAgentOperatorRole BIT DECLARE @check_only_subsystem BIT DECLARE proxy_subsystem CURSOR LOCAL FOR SELECT p.sid, p.flags FROM sysproxyloginsubsystem_view p, syssubsystems s WHERE p.proxy_id = @proxy_id AND p.subsystem_id = s.subsystem_id AND UPPER(s.subsystem collate SQL_Latin1_General_CP1_CS_AS) = UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS) SET NOCOUNT ON SELECT @retval = 1 IF @proxy_id IS NULL RETURN(0) -- TSQL subsystem prohibited IF (UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS) = N'TSQL') BEGIN RAISERROR(14517, -1, -1) RETURN(1) -- Failure END --check if the date stored inside proxy still exists and match the cred create_date inside proxy --otherwise the credential has been tempered from outside --if so, disable proxy and continue execution --only a sysadmin caller have cross database permissions but --when executing by sqlagent this check will be always performed IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) BEGIN IF NOT EXISTS(SELECT * FROM sysproxies p JOIN master.sys.credentials c ON p.credential_id = c.credential_id WHERE p.proxy_id = @proxy_id AND p.credential_date_created = c.create_date AND enabled=1) BEGIN UPDATE sysproxies SET enabled=0 WHERE proxy_id = @proxy_id END END --if no login has been passed check permission against the caller IF @name IS NULL SELECT @name = SUSER_SNAME() --check if the proxy is disable and continue or not based on --allow_disable_proxy --allow creation of a job step with a disabled proxy but --sqlagent always call with @allow_disable_proxy = 0 SELECT @enabled = enabled FROM sysproxies WHERE proxy_id = @proxy_id IF (@enabled = 0) AND (@allow_disable_proxy = 0) BEGIN RAISERROR(14537, -1, -1, @proxy_id) RETURN(2) -- Failure END --we need to check permission only against subsystem in following cases --1. @name is sysadmin --2. @name is member of SQLAgentOperatorRole and @check_only_read_perm=1 --3. @verify_special_account =1 --sysadmin and SQLAgentOperatorRole have permission to view all proxies IF (@verify_special_account = 1) SET @check_only_subsystem = 1 ELSE BEGIN EXEC @is_sysadmin = sp_sqlagent_is_srvrolemember N'sysadmin', @name IF (@is_sysadmin = 1) SET @check_only_subsystem = 1 ELSE BEGIN EXEC @is_SQLAgentOperatorRole = sp_sqlagent_is_srvrolemember N'SQLAgentOperatorRole', @name -- check role membership IF ((@is_SQLAgentOperatorRole = 1) AND (@check_only_read_perm = 1)) SET @check_only_subsystem = 1 END END IF (@check_only_subsystem = 1) BEGIN IF NOT EXISTS(SELECT * FROM sysproxysubsystem sp JOIN syssubsystems s ON sp.subsystem_id = s.subsystem_id WHERE proxy_id = @proxy_id AND UPPER(s.subsystem collate SQL_Latin1_General_CP1_CS_AS) = UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS)) BEGIN IF (@raise_error <> 0) BEGIN RAISERROR(14516, -1, -1, @proxy_id, @subsystem_name, @name) END RETURN(1) -- Failure END RETURN(0) END --get SID from name; we verify if a login has permission to use a certain proxy --force case insensitive comparation for NT users SELECT @name_sid = SUSER_SID(@name, 0) --check first if name has been granted explicit permissions IF (@name_sid IS NOT NULL) BEGIN IF EXISTS(SELECT * FROM sysproxyloginsubsystem_view p, syssubsystems s WHERE p.proxy_id = @proxy_id AND p.subsystem_id = s.subsystem_id AND UPPER(s.subsystem collate SQL_Latin1_General_CP1_CS_AS) = UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS) AND p.sid = @name_sid) -- name has been granted explicit permissions BEGIN RETURN(0) END END OPEN proxy_subsystem FETCH NEXT FROM proxy_subsystem INTO @granted_sid, @flags WHILE (@@fetch_status = 0 AND @retval = 1) BEGIN IF @flags = 0 AND @granted_sid IS NOT NULL AND @name_sid IS NOT NULL -- NT GROUP BEGIN EXEC @is_member = sp_sqlagent_is_member @group_sid = @granted_sid, @login_sid = @name_sid IF @is_member = 1 SELECT @retval = 0 END ELSE IF @flags = 2 AND @granted_sid IS NOT NULL -- MSDB role (@name_sid can be null in case of a loginless user member of msdb) BEGIN DECLARE @principal_id INT SET @principal_id = msdb.dbo.get_principal_id(@granted_sid) EXEC @is_member = sp_sqlagent_is_member @role_principal_id = @principal_id, @login_sid = @name_sid IF @is_member = 1 SELECT @retval = 0 END ELSE IF (@flags = 1) AND @granted_sid IS NOT NULL AND @name_sid IS NOT NULL -- FIXED SERVER Roles BEGIN -- we have to use impersonation to check for role membership SELECT @role_from_sid = SUSER_SNAME(@granted_sid) SELECT @name_from_sid = SUSER_SNAME(@name_sid) EXEC @is_member = sp_sqlagent_is_srvrolemember @role_from_sid, @name_from_sid -- check role membership IF @is_member = 1 SELECT @retval = 0 END IF @retval = 1 BEGIN SELECT @granted_sid = NULL FETCH NEXT FROM proxy_subsystem INTO @granted_sid, @flags END END DEALLOCATE proxy_subsystem IF (@retval = 1 AND @raise_error <> 0) BEGIN RAISERROR(14516, -1, -1, @proxy_id, @subsystem_name, @name) RETURN(1) -- Failure END --0 is for success RETURN @retval END go /**************************************************************/ /* SP_HELP_PROXY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_proxy...' IF (NOT OBJECT_ID(N'dbo.sp_help_proxy', 'P') IS NULL) DROP PROCEDURE dbo.sp_help_proxy go CREATE PROCEDURE dbo.sp_help_proxy @proxy_id int = NULL, @proxy_name sysname = NULL, @subsystem_name sysname = NULL, @name nvarchar(256) = NULL AS BEGIN DECLARE @retval INT DECLARE @subsystem_id INT DECLARE @cur_subsystem_name NVARCHAR(40) DECLARE @current_proxy_id INT DECLARE @not_have_permission INT DECLARE cur_proxy CURSOR LOCAL FOR SELECT p.proxy_id, s.subsystem FROM sysproxies p, syssubsystems s WHERE ISNULL(UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS), UPPER(s.subsystem collate SQL_Latin1_General_CP1_CS_AS) ) = UPPER(s.subsystem collate SQL_Latin1_General_CP1_CS_AS) AND s.subsystem_id <> 1 --last is TSQL subsystem -- this call will populate subsystems table if necessary EXEC @retval = msdb.dbo.sp_verify_subsystems IF @retval <> 0 RETURN(1) --failure --create temp table with returned rows DECLARE @temp_proxy TABLE ( proxy_id INT --used to identify a proxy ) SET NOCOUNT ON SELECT @subsystem_id = NULL -- Remove any leading/trailing spaces from parameters SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) IF @proxy_name = '' SELECT @proxy_name = NULL SELECT @subsystem_name = LTRIM(RTRIM(@subsystem_name)) IF @proxy_name = '' SELECT @proxy_name = NULL SELECT @name = LTRIM(RTRIM(@name)) IF @name = '' SELECT @name = NULL IF (@proxy_id IS NOT NULL OR @proxy_name IS NOT NULL) BEGIN EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END IF @subsystem_name IS NOT NULL BEGIN EXECUTE @retval = sp_verify_subsystem_identifiers '@subsystem_name', '@subsystem_id', @subsystem_name OUTPUT, @subsystem_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END IF (@subsystem_name IS NOT NULL AND @name IS NULL) OR (@subsystem_name IS NULL AND @name IS NOT NULL) BEGIN RAISERROR(14532, -1, -1, '@subsystem_name', '@name') RETURN(1) -- Failure END --only member of sysadmin and SQLAgentOperatorRole roles can see proxies granted to somebody else IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0)) BEGIN SELECT @name = SUSER_SNAME() END IF @name IS NOT NULL BEGIN OPEN cur_proxy FETCH NEXT FROM cur_proxy INTO @current_proxy_id, @cur_subsystem_name WHILE (@@fetch_status = 0) BEGIN --verify if supplied user have permission to use the current proxy for specified subsystem --disabled proxy should be shown as well IF NOT EXISTS(SELECT * FROM @temp_proxy WHERE proxy_id = @current_proxy_id) BEGIN EXECUTE @not_have_permission = sp_verify_proxy_permissions @subsystem_name = @cur_subsystem_name, @proxy_id = @current_proxy_id, @name = @name, @raise_error = 0, @allow_disable_proxy = 1, @verify_special_account = 0, @check_only_read_perm = 1 IF (@not_have_permission = 0) -- have permissions INSERT @temp_proxy VALUES(@current_proxy_id) END FETCH NEXT FROM cur_proxy INTO @current_proxy_id, @cur_subsystem_name END CLOSE cur_proxy DEALLOCATE cur_proxy END ELSE INSERT @temp_proxy SELECT proxy_id from sysproxies -- returns different result sets if caller is admin or not IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 1)) BEGIN SELECT p.proxy_id, p.name, c.credential_identity, p.enabled, p.description, p.user_sid, p.credential_id, CASE WHEN c.credential_id IS NULL THEN 0 ELSE 1 END as credential_identity_exists FROM sysproxies p LEFT JOIN master.sys.credentials c ON p.credential_id = c.credential_id JOIN @temp_proxy t ON p.proxy_id = t.proxy_id WHERE ISNULL(@proxy_id, p.proxy_id) = p.proxy_id END ELSE BEGIN SELECT p.proxy_id, p.name, null as credential_identity, p.enabled, p.description, null as user_sid, p.credential_id, null as credential_identity_exists FROM sysproxies p, @temp_proxy t WHERE ISNULL(@proxy_id, p.proxy_id) = p.proxy_id AND p.proxy_id = t.proxy_id END END go /**************************************************************/ /* sp_grant_proxy_to_subsystem */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_grant_proxy_to_subsystem...' IF (NOT OBJECT_ID(N'dbo.sp_grant_proxy_to_subsystem', 'P') IS NULL) DROP PROCEDURE dbo.sp_grant_proxy_to_subsystem go CREATE PROCEDURE dbo.sp_grant_proxy_to_subsystem @proxy_id int = NULL, @proxy_name sysname = NULL, -- must specify only one of above parameter to identify the proxy @subsystem_id int = NULL, @subsystem_name sysname = NULL -- must specify only one of above parameter to identify the subsystem AS BEGIN DECLARE @retval INT DECLARE @proxy_account sysname SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @subsystem_name = LTRIM(RTRIM(@subsystem_name)) SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) -- Turn [nullable] empty string parameters into NULLs IF @subsystem_name = '' SELECT @subsystem_name = NULL IF @proxy_name = '' SELECT @proxy_name = NULL EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure EXECUTE @retval = sp_verify_subsystem_identifiers '@subsystem_name', '@subsystem_id', @subsystem_name OUTPUT, @subsystem_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure --TSQL subsystem is prohibited IF @subsystem_id = 1 BEGIN RAISERROR(14530, -1, -1) RETURN(1) -- Failure END --check if we already added an user for the pair subsystem-proxy IF (EXISTS(SELECT * FROM sysproxysubsystem WHERE subsystem_id = @subsystem_id AND proxy_id = @proxy_id)) BEGIN RAISERROR(14531, -1, -1) RETURN(1) -- Failure END INSERT INTO sysproxysubsystem ( subsystem_id, proxy_id ) VALUES ( @subsystem_id, @proxy_id ) END go /**************************************************************/ /* sp_grant_login_to_proxy */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_grant_login_to_proxy...' IF (NOT OBJECT_ID(N'dbo.sp_grant_login_to_proxy', 'P') IS NULL) DROP PROCEDURE dbo.sp_grant_login_to_proxy go CREATE PROCEDURE dbo.sp_grant_login_to_proxy @login_name NVARCHAR(256) = NULL, @fixed_server_role NVARCHAR(256) = NULL, @msdb_role NVARCHAR(256) = NULL, -- must specify only one of above parameter to identify the type of login @proxy_id int = NULL, @proxy_name sysname = NULL -- must specify only one of above parameter to identify the proxy AS BEGIN DECLARE @retval INT DECLARE @name nvarchar(256) DECLARE @flags INT DECLARE @sid VARBINARY(85) DECLARE @is_sysadmin BIT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) SELECT @fixed_server_role = LTRIM(RTRIM(@fixed_server_role)) SELECT @msdb_role = LTRIM(RTRIM(@msdb_role)) -- Turn [nullable] empty string parameters into NULLs IF @proxy_name = '' SELECT @proxy_name = NULL IF @login_name = '' SELECT @login_name = NULL IF @fixed_server_role = '' SELECT @fixed_server_role = NULL IF @msdb_role = '' SELECT @msdb_role = NULL EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure EXECUTE @retval = sp_verify_login_identifiers @login_name, @fixed_server_role, @msdb_role, @name OUTPUT, @sid OUTPUT, @flags OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- is login member of sysadmin role? SELECT @is_sysadmin = 0 IF (@login_name IS NOT NULL) BEGIN EXEC @is_sysadmin = sp_sqlagent_is_srvrolemember N'sysadmin', @login_name -- check role membership END IF (@is_sysadmin = 1) BEGIN -- @name is sysadmin, it cannot granted to proxy -- issue a message and do nothing RAISERROR(14395, 10, 1, @name) END ELSE BEGIN --check if we already added an user for the pair subsystem-proxy IF (EXISTS(SELECT * FROM sysproxylogin WHERE proxy_id = @proxy_id AND ISNULL(sid, 0) = ISNULL(@sid,0) AND flags = @flags)) BEGIN RAISERROR(14531, -1, -1) RETURN(1) -- Failure END INSERT INTO sysproxylogin ( proxy_id, sid, flags ) VALUES ( @proxy_id, @sid, @flags) END END go /**************************************************************/ /* sp_revoke_login_from_proxy */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_revoke_login_from_proxy...' IF (NOT OBJECT_ID(N'dbo.sp_revoke_login_from_proxy', 'P') IS NULL) DROP PROCEDURE dbo.sp_revoke_login_from_proxy go CREATE PROCEDURE dbo.sp_revoke_login_from_proxy @name NVARCHAR(256), @proxy_id INT = NULL, @proxy_name sysname = NULL -- must specify only one of above parameter to identify the proxy AS BEGIN DECLARE @retval INT DECLARE @sid VARBINARY(85) DECLARE @is_sysadmin BIT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) SELECT @name = LTRIM(RTRIM(@name)) -- Turn [nullable] empty string parameters into NULLs IF @proxy_name = '' SELECT @proxy_name = NULL IF @name = '' SELECT @name = NULL EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- is login member of sysadmin role? SELECT @is_sysadmin = 0 IF (@name IS NOT NULL) BEGIN EXEC @is_sysadmin = sp_sqlagent_is_srvrolemember N'sysadmin', @name -- check role membership END IF (@is_sysadmin = 1) BEGIN -- @name is sysadmin, it cannot be revoked from proxy -- issue a message and do nothing RAISERROR(14395, 10, -1, @name) END ELSE BEGIN --force case insensitive comparation for NT users SELECT @sid = SUSER_SID(@name, 0) IF @sid IS NULL -- then @name is a MSDB role SELECT @sid = sid FROM sys.database_principals WHERE name = @name --check parametrs validity IF (EXISTS(SELECT * FROM sysproxylogin WHERE proxy_id = @proxy_id AND ISNULL(sid, 0) = ISNULL(@sid, 0))) BEGIN DELETE FROM sysproxylogin WHERE proxy_id = @proxy_id AND ISNULL(sid, 0)= ISNULL(@sid, 0) END ELSE BEGIN RAISERROR(14523, -1, -1, @name, @proxy_name) RETURN(1) -- Failure END END RETURN(0) END go /**************************************************************/ /* sp_revoke_proxy_from_subsystem */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_revoke_proxy_from_subsystem...' IF (NOT OBJECT_ID(N'dbo.sp_revoke_proxy_from_subsystem', 'P') IS NULL) DROP PROCEDURE dbo.sp_revoke_proxy_from_subsystem go CREATE PROCEDURE dbo.sp_revoke_proxy_from_subsystem @proxy_id INT = NULL, @proxy_name sysname = NULL, -- must specify only one of above parameter to identify the proxyAS @subsystem_id INT = NULL, @subsystem_name sysname = NULL -- must specify only one of above parameter to identify the subsystem AS BEGIN DECLARE @retval INT DECLARE @proxy_account sysname SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @subsystem_name = LTRIM(RTRIM(@subsystem_name)) SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) -- Turn [nullable] empty string parameters into NULLs IF @subsystem_name = '' SELECT @subsystem_name = NULL IF @proxy_name = '' SELECT @proxy_name = NULL EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure EXECUTE @retval = sp_verify_subsystem_identifiers '@subsystem_name', '@subsystem_id', @subsystem_name OUTPUT, @subsystem_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure --check parametrs validity IF (EXISTS(SELECT * FROM sysproxysubsystem WHERE subsystem_id = @subsystem_id AND proxy_id = @proxy_id )) BEGIN DELETE FROM sysproxysubsystem WHERE subsystem_id = @subsystem_id AND proxy_id = @proxy_id END ELSE BEGIN RAISERROR(14600, -1, -1, @proxy_name, @subsystem_name) RETURN(1) -- Failure END RETURN(0) END go /**************************************************************/ /* SP_ENUM_PROXY_FOR_SUBSYSTEM */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_proxy_for_subsystem...' IF (NOT OBJECT_ID(N'dbo.sp_enum_proxy_for_subsystem', 'P') IS NULL) DROP PROCEDURE dbo.sp_enum_proxy_for_subsystem go CREATE PROCEDURE sp_enum_proxy_for_subsystem @proxy_id int = NULL, @proxy_name sysname = NULL, -- must specify only one of above parameter to identify the proxy or none @subsystem_id int = NULL, @subsystem_name sysname = NULL -- must specify only one of above parameter to identify the subsystem or none AS BEGIN DECLARE @retval INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @subsystem_name = LTRIM(RTRIM(@subsystem_name)) SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) -- Turn [nullable] empty string parameters into NULLs IF @subsystem_name = '' SELECT @subsystem_name = NULL IF @proxy_name = '' SELECT @proxy_name = NULL IF @proxy_name IS NOT NULL OR @proxy_id IS NOT NULL BEGIN EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END IF @subsystem_name IS NOT NULL OR @subsystem_id IS NOT NULL BEGIN EXECUTE @retval = sp_verify_subsystem_identifiers '@subsystem_name', '@subsystem_id', @subsystem_name OUTPUT, @subsystem_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END SELECT ps.subsystem_id AS subsystem_id, s.subsystem AS subsystem_name, ps.proxy_id AS proxy_id, p.name AS proxy_name FROM sysproxysubsystem ps JOIN sysproxies p ON ps.proxy_id = p.proxy_id JOIN syssubsystems s ON ps.subsystem_id = s.subsystem_id WHERE ISNULL(@subsystem_id, ps.subsystem_id) = ps.subsystem_id AND ISNULL(@proxy_id, ps.proxy_id ) = ps.proxy_id END go /**************************************************************/ /* sp_enum_login_for_proxy */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_login_for_proxy...' IF (NOT OBJECT_ID(N'dbo.sp_enum_login_for_proxy', 'P') IS NULL) DROP PROCEDURE dbo.sp_enum_login_for_proxy go CREATE PROCEDURE sp_enum_login_for_proxy @name NVARCHAR(256) = NULL, @proxy_id INT = NULL, @proxy_name sysname = NULL -- must specify only one of above parameter to identify the proxy or none AS BEGIN DECLARE @retval INT DECLARE @sid VARBINARY(85) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) SELECT @name = LTRIM(RTRIM(@name)) -- Turn [nullable] empty string parameters into NULLs IF @proxy_name = '' SELECT @proxy_name = NULL IF @name = '' SELECT @name = NULL IF @proxy_name IS NOT NULL OR @proxy_id IS NOT NULL BEGIN EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END IF (@name IS NOT NULL) AND --force case insensitive comparation for NT users (ISNULL(SUSER_SID(@name, 0), 0) = 0) AND (ISNULL(IS_SRVROLEMEMBER(@name), -1) = -1) AND (ISNULL(IS_MEMBER(@name), -1) = -1) BEGIN RAISERROR(14520, -1, -1, @name) RETURN(1) -- Failure END --force case insensitive comparation for NT users SELECT @sid = SUSER_SID(@name, 0) IF @sid IS NULL -- then @name is a MSDB role SELECT @sid = sid FROM sys.database_principals WHERE name = @name SELECT pl.proxy_id AS proxy_id, p.name AS proxy_name, pl.flags as flags, CASE pl.flags WHEN 0 THEN SUSER_SNAME(pl.sid) -- SQLLOGIN, NT USER/GROUP WHEN 1 THEN SUSER_SNAME(pl.sid) -- SQL fixed server role WHEN 2 THEN USER_NAME(msdb.dbo.get_principal_id(pl.sid)) -- MSDB role ELSE NULL -- should never be the case END AS name, pl.sid AS sid, msdb.dbo.get_principal_id(pl.sid) AS principal_id FROM sysproxylogin pl JOIN sysproxies p ON pl.proxy_id = p.proxy_id WHERE COALESCE(@proxy_id, pl.proxy_id, 0 ) = ISNULL(pl.proxy_id, 0) AND COALESCE(@sid, pl.sid, 0 ) = ISNULL(pl.sid, 0) END go /**************************************************************/ /* SP_SQLAGENT_GET_STARTUP_INFO */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_get_startup_info...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_sqlagent_get_startup_info') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_get_startup_info go CREATE PROCEDURE sp_sqlagent_get_startup_info AS BEGIN DECLARE @tbu INT DECLARE @agentAllowed INT SET NOCOUNT ON IF (ServerProperty('InstanceName') IS NULL) BEGIN EXECUTE @tbu = master.dbo.xp_qv '1338198028' EXECUTE @agentAllowed = master.dbo.xp_qv '2858542058' END ELSE BEGIN DECLARE @instancename NVARCHAR(128) SELECT @instancename = CONVERT(NVARCHAR(128), ServerProperty('InstanceName')) EXECUTE @tbu = master.dbo.xp_qv '1338198028', @instancename EXECUTE @agentAllowed = master.dbo.xp_qv '2858542058', @instancename END IF (@tbu < 0) SELECT @tbu = 0 IF (@agentAllowed < 0) SELECT @agentAllowed = 0 SELECT 'msdb_70_compatible' = (SELECT CASE WHEN cmptlevel >= 70 THEN 1 ELSE 0 END FROM master.dbo.sysdatabases WHERE (name = 'msdb')), 'msdb_read_only' = DATABASEPROPERTY('msdb', 'IsReadOnly'), 'msdb_available' = CASE ISNULL(DATABASEPROPERTY('msdb', 'IsSingleUser'), 0) WHEN 0 THEN 1 ELSE 0 END & CASE ISNULL(DATABASEPROPERTY('msdb', 'IsDboOnly'), 0) WHEN 0 THEN 1 ELSE 0 END & CASE ISNULL(DATABASEPROPERTY('msdb', 'IsNotRecovered'), 0) WHEN 0 THEN 1 ELSE 0 END & CASE ISNULL(DATABASEPROPERTY('msdb', 'IsSuspect'), 0) WHEN 0 THEN 1 ELSE 0 END, 'case_sensitive_server' = CASE ISNULL((SELECT 1 WHERE 'a' = 'A'), 0) WHEN 1 THEN 0 ELSE 1 END, 'max_user_connection' = (SELECT value FROM master.dbo.syscurconfigs WHERE (config = 103)), 'sql_server_name' = CONVERT(sysname, SERVERPROPERTY('SERVERNAME')), 'tbu' = ISNULL(@tbu, 0), 'platform' = PLATFORM(), 'instance_name' = ISNULL(CONVERT(sysname, SERVERPROPERTY('INSTANCENAME')), 'MSSQLSERVER'), 'is_clustered' = CONVERT(INT, SERVERPROPERTY('ISCLUSTERED')), 'agent_allowed' = @agentAllowed RETURN(0) -- Success END go /**************************************************************/ /* SP_SQLAGENT_HAS_SERVER_ACCESS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_has_server_access...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_sqlagent_has_server_access') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_has_server_access go CREATE PROCEDURE sp_sqlagent_has_server_access @login_name sysname = NULL, @is_sysadmin_member INT = NULL OUTPUT AS BEGIN DECLARE @has_server_access BIT DECLARE @is_sysadmin BIT DECLARE @actual_login_name sysname DECLARE @cachedate DATETIME SET NOCOUNT ON SELECT @cachedate = NULL -- remove expired entries from the cache DELETE msdb.dbo.syscachedcredentials WHERE DATEDIFF(MINUTE, cachedate, GETDATE()) >= 29 -- query the cache SELECT @is_sysadmin = is_sysadmin_member, @has_server_access = has_server_access, @cachedate = cachedate FROM msdb.dbo.syscachedcredentials WHERE login_name = @login_name AND DATEDIFF(MINUTE, cachedate, GETDATE()) < 29 IF (@cachedate IS NOT NULL) BEGIN -- no output variable IF (@is_sysadmin_member IS NULL) BEGIN -- Return result row SELECT has_server_access = @has_server_access, is_sysadmin = @is_sysadmin, actual_login_name = @login_name RETURN END ELSE BEGIN SELECT @is_sysadmin_member = @is_sysadmin RETURN END END -- select from cache -- Set defaults SELECT @has_server_access = 0 SELECT @is_sysadmin = 0 SELECT @actual_login_name = FORMATMESSAGE(14205) IF (@login_name IS NULL) BEGIN SELECT has_server_access = 1, is_sysadmin = IS_SRVROLEMEMBER(N'sysadmin'), actual_login_name = SUSER_SNAME() RETURN END IF (@login_name LIKE '%\%') BEGIN -- Handle the LocalSystem account ('NT AUTHORITY\SYSTEM') as a special case IF (UPPER(@login_name collate SQL_Latin1_General_CP1_CS_AS) = N'NT AUTHORITY\SYSTEM') BEGIN IF (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (UPPER(loginname collate SQL_Latin1_General_CP1_CS_AS) = N'NT AUTHORITY\SYSTEM'))) BEGIN SELECT @has_server_access = hasaccess, @is_sysadmin = sysadmin, @actual_login_name = loginname FROM master.dbo.syslogins WHERE (UPPER(loginname collate SQL_Latin1_General_CP1_CS_AS) = N'NT AUTHORITY\SYSTEM') END ELSE IF (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (UPPER(loginname collate SQL_Latin1_General_CP1_CS_AS) = N'BUILTIN\ADMINISTRATORS'))) BEGIN SELECT @has_server_access = hasaccess, @is_sysadmin = sysadmin, @actual_login_name = loginname FROM master.dbo.syslogins WHERE (UPPER(loginname collate SQL_Latin1_General_CP1_CS_AS) = N'BUILTIN\ADMINISTRATORS') END END ELSE BEGIN -- Check if the NT login has been explicitly denied access IF (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (loginname = @login_name) AND (denylogin = 1))) BEGIN SELECT @has_server_access = 0, @is_sysadmin = sysadmin, @actual_login_name = loginname FROM master.dbo.syslogins WHERE (loginname = @login_name) END ELSE BEGIN -- declare table variable for storing results DECLARE @xp_results TABLE ( account_name sysname COLLATE database_default NOT NULL PRIMARY KEY, type NVARCHAR(10) COLLATE database_default NOT NULL, privilege NVARCHAR(10) COLLATE database_default NOT NULL, mapped_login_name sysname COLLATE database_default NOT NULL, permission_path sysname COLLATE database_default NULL ) -- Call xp_logininfo to determine server access INSERT INTO @xp_results EXECUTE master.dbo.xp_logininfo @login_name SELECT @has_server_access = CASE COUNT(*) WHEN 0 THEN 0 ELSE 1 END FROM @xp_results SELECT @actual_login_name = mapped_login_name, @is_sysadmin = CASE UPPER(privilege collate SQL_Latin1_General_CP1_CS_AS) WHEN 'ADMIN' THEN 1 ELSE 0 END FROM @xp_results END END -- Only cache the NT logins to approximate the behavior of Sql Server and Windows (see bug 323287) -- update the cache only if something is found IF (UPPER(@actual_login_name collate SQL_Latin1_General_CP1_CS_AS) <> '(UNKNOWN)') BEGIN -- Procedure starts its own transaction. BEGIN TRANSACTION; -- Modify database. -- use a try catch login to prevent any error when trying -- to insert/update syscachedcredentials table -- no need to fail since the job owner has been validated BEGIN TRY IF EXISTS (SELECT * FROM msdb.dbo.syscachedcredentials WITH (TABLOCKX) WHERE login_name = @login_name) BEGIN UPDATE msdb.dbo.syscachedcredentials SET has_server_access = @has_server_access, is_sysadmin_member = @is_sysadmin, cachedate = GETDATE() WHERE login_name = @login_name END ELSE BEGIN INSERT INTO msdb.dbo.syscachedcredentials(login_name, has_server_access, is_sysadmin_member) VALUES(@login_name, @has_server_access, @is_sysadmin) END END TRY BEGIN CATCH -- If an error occurred we want to ignore it END CATCH -- The procedure must commit the transaction it started. COMMIT TRANSACTION; END END ELSE BEGIN -- Standard login IF (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (loginname = @login_name))) BEGIN SELECT @has_server_access = hasaccess, @is_sysadmin = sysadmin, @actual_login_name = loginname FROM master.dbo.syslogins WHERE (loginname = @login_name) END END IF (@is_sysadmin_member IS NULL) -- Return result row SELECT has_server_access = @has_server_access, is_sysadmin = @is_sysadmin, actual_login_name = @actual_login_name ELSE -- output variable only SELECT @is_sysadmin_member = @is_sysadmin END go /**************************************************************/ /* SP_SEM_ADD_MESSAGE [used by SEM only] */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sem_add_message...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_sem_add_message') AND (type = 'P'))) DROP PROCEDURE sp_sem_add_message go CREATE PROCEDURE sp_sem_add_message @msgnum INT = NULL, @severity SMALLINT = NULL, @msgtext NVARCHAR(255) = NULL, @lang sysname = NULL, -- Message language name @with_log VARCHAR(5) = 'FALSE', @replace VARCHAR(7) = NULL AS BEGIN DECLARE @retval INT DECLARE @language_name sysname SET NOCOUNT ON SET ROWCOUNT 1 SELECT @language_name = name FROM sys.syslanguages WHERE msglangid = (SELECT number FROM master.dbo.spt_values WHERE (type = 'LNG') AND (name = @lang)) SET ROWCOUNT 0 SELECT @language_name = ISNULL(@language_name, 'us_english') EXECUTE @retval = master.dbo.sp_addmessage @msgnum, @severity, @msgtext, @language_name, @with_log, @replace RETURN(@retval) END go /**************************************************************/ /* SP_SEM_DROP_MESSAGE [used by SEM only] */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sem_drop_message...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_sem_drop_message') AND (type = 'P'))) DROP PROCEDURE sp_sem_drop_message go CREATE PROCEDURE sp_sem_drop_message @msgnum int = NULL, @lang sysname = NULL -- Message language name AS BEGIN DECLARE @retval INT DECLARE @language_name sysname SET NOCOUNT ON SET ROWCOUNT 1 SELECT @language_name = name FROM sys.syslanguages WHERE msglangid = (SELECT number FROM master.dbo.spt_values WHERE (type = 'LNG') AND (name = @lang)) SET ROWCOUNT 0 SELECT @language_name = ISNULL(@language_name, 'us_english') EXECUTE @retval = master.dbo.sp_dropmessage @msgnum, @language_name RETURN(@retval) END go /**************************************************************/ /* SP_GET_MESSAGE_DESCRIPTION [used by SEM only] */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_message_description...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_get_message_description') AND (type = 'P'))) DROP PROCEDURE sp_get_message_description go CREATE PROCEDURE sp_get_message_description @error INT AS BEGIN IF EXISTS (SELECT * FROM master.dbo.sysmessages WHERE (error = @error) AND (msglangid = (SELECT msglangid FROM sys.syslanguages WHERE (langid = @@langid)))) SELECT description FROM master.dbo.sysmessages WHERE (error = @error) AND (msglangid = (SELECT msglangid FROM sys.syslanguages WHERE (langid = @@langid))) ELSE SELECT description FROM master.dbo.sysmessages WHERE (error = @error) AND (msglangid = 1033) END go /**************************************************************/ /* SP_SQLAGENT_GET_PERF_COUNTERS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_get_perf_counters...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_sqlagent_get_perf_counters') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_get_perf_counters go CREATE PROCEDURE sp_sqlagent_get_perf_counters @all_counters BIT = 0 AS BEGIN SET NOCOUNT ON -- 32 bit fraction counter types DECLARE @perfTypeRawFraction INT DECLARE @perfTypeRawBase INT -- A counter of type PERF_RAW_FRACTION, which is a 32-bit counter value. SET @perfTypeRawFraction = 537003008 -- In hex, 0x20020400. -- A count of type PERF_RAW_BASE, which is the 32-bit divisor used -- when handling PERF_RAW_FRACTION types. This counter type should -- not be displayed to the user since it is used for mathematical -- operations. SET @perfTypeRawBase = 1073939459 -- In hex, 0x40030403. -- 64 bit fraction counter types DECLARE @perfTypeLargeRawFraction INT DECLARE @perfTypeLargeRawBase INT -- A counter of type PERF_LARGE RAW_FRACTION, which is a 64-bit counter value. SET @perfTypeLargeRawFraction = 537003264 -- In hex, 0x20020500. -- A count of type PERF_LARGE_RAW_BASE, which is the 64-bit divisor used -- when handling PERF_LARGE_RAW_FRACTION types. This counter type should -- not be displayed to the user since it is used for mathematical -- operations. SET @perfTypeLargeRawBase = 1073939712 -- In hex, 0x40030500. IF (@all_counters = 0) BEGIN SELECT 'object_name' = RTRIM(SUBSTRING(spi1.object_name, 1, 50)), 'counter_name' = RTRIM(SUBSTRING(spi1.counter_name, 1, 50)), 'instance_name' = CASE spi1.instance_name WHEN N'' THEN NULL ELSE RTRIM(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 sys.dm_os_performance_counters spi2 WHERE (spi1.counter_name + ' ' = SUBSTRING(spi2.counter_name, 1, PATINDEX('% Base%', spi2.counter_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 sys.dm_os_performance_counters spi2 WHERE (spi1.counter_name + ' ' = SUBSTRING(spi2.counter_name, 1, PATINDEX('% Base%', spi2.counter_name))) AND (spi1.instance_name = spi2.instance_name) AND (spi2.cntr_type = @perfTypeLargeRawBase)) ELSE spi1.cntr_value END, 'type' = spi1.cntr_type FROM sys.dm_os_performance_counters spi1, ( SELECT DISTINCT SUBSTRING(performance_condition, 1, CHARINDEX('|', performance_condition, PATINDEX('%_|_%', performance_condition) + 2) - 1) as performance_condition_s FROM msdb.dbo.sysalerts WHERE (performance_condition IS NOT NULL) AND ISNULL(event_id, 0) <> 8 -- exclude WMI events that reuse performance_condition field AND (enabled = 1) ) tmp -- We want to select only those counters that have an enabled performance sysalert WHERE (spi1.cntr_type <> @perfTypeRawBase) -- ignore 32-bit denominator counter type AND (spi1.cntr_type <> @perfTypeLargeRawBase) -- ignore 64-bit denominator counter type AND (tmp.performance_condition_s = RTRIM(spi1.object_name) + '|' + RTRIM(spi1.counter_name)) END ELSE BEGIN SELECT 'object_name' = RTRIM(SUBSTRING(spi1.object_name, 1, 50)), 'counter_name' = RTRIM(SUBSTRING(spi1.counter_name, 1, 50)), 'instance_name' = CASE spi1.instance_name WHEN N'' THEN NULL ELSE RTRIM(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 sys.dm_os_performance_counters spi2 WHERE (spi1.counter_name + ' ' = SUBSTRING(spi2.counter_name, 1, PATINDEX('% Base%', spi2.counter_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 sys.dm_os_performance_counters spi2 WHERE (spi1.counter_name + ' ' = SUBSTRING(spi2.counter_name, 1, PATINDEX('% Base%', spi2.counter_name))) AND (spi1.instance_name = spi2.instance_name) AND (spi2.cntr_type = @perfTypeLargeRawBase)) ELSE spi1.cntr_value END, 'type' = spi1.cntr_type FROM sys.dm_os_performance_counters spi1 WHERE (spi1.cntr_type <> @perfTypeRawBase) -- ignore 32-bit denominator counter type AND (spi1.cntr_type <> @perfTypeLargeRawBase) -- ignore 64-bit denominator counter type END END GO /**************************************************************/ /* SP_SQLAGENT_NOTIFY */ /* */ /* NOTE: We define this procedure here instead of in the */ /* 'Support procedures' section because of the many */ /* other procedures that reference it. */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_notify...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_sqlagent_notify') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_notify go CREATE PROCEDURE sp_sqlagent_notify @op_type NCHAR(1), -- One of: J (Job action [refresh or start/stop]), -- S (Schedule action [refresh only]) -- A (Alert action [refresh only]), -- G (Re-cache all registry settings), -- D (Dump job [or job schedule] cache to errorlog) -- P (Force an immediate poll of the MSX) -- L (Cycle log file) -- T (Test WMI parameters (namespace and query)) @job_id UNIQUEIDENTIFIER = NULL, -- JobID (for OpTypes 'J', 'S' and 'D') @schedule_id INT = NULL, -- ScheduleID (for OpType 'S') @alert_id INT = NULL, -- AlertID (for OpType 'A') @action_type NCHAR(1) = NULL, -- For 'J' one of: R (Run - no service check), -- S (Start - with service check), -- I (Insert), -- U (Update), -- D (Delete), -- C (Stop [Cancel]) -- For 'S' or 'A' one of: I (Insert), -- U (Update), -- D (Delete) @error_flag INT = 1, -- Set to 0 to suppress the error from xp_sqlagent_notify if SQLServer agent is not running @wmi_namespace nvarchar(128) = NULL, @wmi_query nvarchar(512) = NULL AS BEGIN DECLARE @retval INT DECLARE @id_as_char VARCHAR(10) DECLARE @job_id_as_char VARCHAR(36) DECLARE @nt_user_name NVARCHAR(100) SET NOCOUNT ON SELECT @retval = 0 -- Success -- Make sure that we're dealing only with uppercase characters SELECT @op_type = UPPER(@op_type collate SQL_Latin1_General_CP1_CS_AS) SELECT @action_type = UPPER(@action_type collate SQL_Latin1_General_CP1_CS_AS) -- Verify operation code IF (CHARINDEX(@op_type, N'JSAGDPLT') = 0) BEGIN RAISERROR(14266, -1, -1, '@op_type', 'J, S, A, G, D, P, L, T') RETURN(1) -- Failure END -- Check the job id for those who use it IF (CHARINDEX(@op_type, N'JSD') <> 0) BEGIN IF (NOT ((@op_type = N'D' OR @op_type = N'S') AND (@job_id IS NULL))) -- For 'D' and 'S' job_id is optional BEGIN IF ((@job_id IS NULL) OR ((@action_type <> N'D') AND NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id)))) BEGIN SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) RAISERROR(14262, -1, -1, '@job_id', @job_id_as_char) RETURN(1) -- Failure END END END -- Verify 'job' action parameters IF (@op_type = N'J') BEGIN SELECT @alert_id = 0 IF (@schedule_id IS NULL) SELECT @schedule_id = 0 -- The schedule_id (if specified) is the start step IF ((CHARINDEX(@action_type, N'RS') <> 0) AND (@schedule_id <> 0)) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @schedule_id))) BEGIN SELECT @id_as_char = ISNULL(CONVERT(VARCHAR, @schedule_id), '(null)') RAISERROR(14262, -1, -1, '@schedule_id', @id_as_char) RETURN(1) -- Failure END END ELSE SELECT @schedule_id = 0 IF (CHARINDEX(@action_type, N'RSIUDC') = 0) BEGIN RAISERROR(14266, -1, -1, '@action_type', 'R, S, I, U, D, C') RETURN(1) -- Failure END END -- Verify 'schedule' action parameters IF (@op_type = N'S') BEGIN SELECT @alert_id = 0 IF (CHARINDEX(@action_type, N'IUD') = 0) BEGIN RAISERROR(14266, -1, -1, '@action_type', 'I, U, D') RETURN(1) -- Failure END IF ((@schedule_id IS NULL) OR ((@action_type <> N'D') AND NOT EXISTS (SELECT * FROM msdb.dbo.sysschedules WHERE (schedule_id = @schedule_id)))) BEGIN SELECT @id_as_char = ISNULL(CONVERT(VARCHAR, @schedule_id), '(null)') RAISERROR(14262, -1, -1, '@schedule_id', @id_as_char) RETURN(1) -- Failure END END -- Verify 'alert' action parameters IF (@op_type = N'A') BEGIN SELECT @job_id = 0x00 SELECT @schedule_id = 0 IF (CHARINDEX(@action_type, N'IUD') = 0) BEGIN RAISERROR(14266, -1, -1, '@action_type', 'I, U, D') RETURN(1) -- Failure END IF ((@alert_id IS NULL) OR ((@action_type <> N'D') AND NOT EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (id = @alert_id)))) BEGIN SELECT @id_as_char = ISNULL(CONVERT(VARCHAR, @alert_id), '(null)') RAISERROR(14262, -1, -1, '@alert_id', @id_as_char) RETURN(1) -- Failure END END -- Verify 'registry', 'job dump' and 'force MSX poll' and 'cycle log' action parameters IF (CHARINDEX(@op_type, N'GDPL') <> 0) BEGIN IF (@op_type <> N'D') SELECT @job_id = 0x00 SELECT @alert_id = 0 SELECT @schedule_id = 0 SELECT @action_type = NULL END -- Parameters are valid, so now check execution permissions... -- For anything except a job (or schedule) action the caller must be SysAdmin, DBO, or DB_Owner IF (@op_type NOT IN (N'J', N'S')) BEGIN IF NOT ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1) OR (UPPER(USER_NAME() collate SQL_Latin1_General_CP1_CS_AS) = N'DBO')) BEGIN RAISERROR(14260, -1, -1) RETURN(1) -- Failure END END -- For a Job Action the caller must be SysAdmin, DBO, DB_Owner, or the job owner IF (@op_type = N'J') BEGIN IF NOT ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1) OR (UPPER(USER_NAME() collate SQL_Latin1_General_CP1_CS_AS) = N'DBO') OR (EXISTS (SELECT * FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id)))) BEGIN RAISERROR(14252, -1, -1) RETURN(1) -- Failure END END --verify WMI parameters IF (@op_type = N'T') BEGIN SELECT @wmi_namespace = LTRIM(RTRIM(@wmi_namespace)) SELECT @wmi_query = LTRIM(RTRIM(@wmi_query)) IF (@wmi_namespace IS NULL) or (@wmi_query IS NULL) BEGIN RAISERROR(14508, 16, 1) RETURN(1) -- Failure END END -- Ok, let's do it... SELECT @nt_user_name = ISNULL(NT_CLIENT(), ISNULL(SUSER_SNAME(), FORMATMESSAGE(14205))) EXECUTE @retval = master.dbo.xp_sqlagent_notify @op_type, @job_id, @schedule_id, @alert_id, @action_type, @nt_user_name, @error_flag, @@trancount, @wmi_namespace, @wmi_query RETURN(@retval) END go /**************************************************************/ /* SP_IS_SQLAGENT_STARTING */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_is_sqlagent_starting...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_is_sqlagent_starting') AND (type = 'P'))) DROP PROCEDURE sp_is_sqlagent_starting go CREATE PROCEDURE sp_is_sqlagent_starting AS BEGIN DECLARE @retval INT SELECT @retval = 0 EXECUTE master.dbo.xp_sqlagent_is_starting @retval OUTPUT IF (@retval = 1) RAISERROR(14258, -1, -1) RETURN(@retval) END go /**************************************************************/ /* SP_VERIFY_JOB_IDENTIFIERS */ /* */ /* NOTE: We define this procedure here instead of in the */ /* 'Support procedures' section because of the many */ /* other procedures that reference it. */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_job_identifiers...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_job_identifiers') AND (type = 'P'))) DROP PROCEDURE sp_verify_job_identifiers go CREATE PROCEDURE sp_verify_job_identifiers @name_of_name_parameter VARCHAR(60), -- Eg. '@job_name' @name_of_id_parameter VARCHAR(60), -- Eg. '@job_id' @job_name sysname OUTPUT, -- Eg. 'My Job' @job_id UNIQUEIDENTIFIER OUTPUT, @sqlagent_starting_test VARCHAR(7) = 'TEST', -- By default we DO want to test if SQLServerAgent is running (caller should specify 'NO_TEST' if not desired) @owner_sid VARBINARY(85) = NULL OUTPUT AS BEGIN DECLARE @retval INT DECLARE @job_id_as_char VARCHAR(36) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter)) SELECT @name_of_id_parameter = LTRIM(RTRIM(@name_of_id_parameter)) SELECT @job_name = LTRIM(RTRIM(@job_name)) IF (@job_name = N'') SELECT @job_name = NULL IF ((@job_name IS NULL) AND (@job_id IS NULL)) OR ((@job_name IS NOT NULL) AND (@job_id IS NOT NULL)) BEGIN RAISERROR(14294, -1, -1, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- Check job id IF (@job_id IS NOT NULL) BEGIN SELECT @job_name = name, @owner_sid = owner_sid FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id) -- the view would take care of all the permissions issues. IF (@job_name IS NULL) BEGIN SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) RAISERROR(14262, -1, -1, '@job_id', @job_id_as_char) RETURN(1) -- Failure END END ELSE -- Check job name IF (@job_name IS NOT NULL) BEGIN -- Check if the job name is ambiguous IF ((SELECT COUNT(*) FROM msdb.dbo.sysjobs_view WHERE (name = @job_name)) > 1) BEGIN RAISERROR(14293, -1, -1, @job_name, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- The name is not ambiguous, so get the corresponding job_id (if the job exists) SELECT @job_id = job_id, @owner_sid = owner_sid FROM msdb.dbo.sysjobs_view WHERE (name = @job_name) -- the view would take care of all the permissions issues. IF (@job_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@job_name', @job_name) RETURN(1) -- Failure END END IF (@sqlagent_starting_test = 'TEST') BEGIN -- Finally, check if SQLServerAgent is in the process of starting and if so prevent the -- calling SP from running EXECUTE @retval = msdb.dbo.sp_is_sqlagent_starting IF (@retval <> 0) RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_SCHEDULE_IDENTIFIERS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_schedule_identifiers...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_schedule_identifiers') AND (type = 'P'))) DROP PROCEDURE sp_verify_schedule_identifiers go CREATE PROCEDURE sp_verify_schedule_identifiers @name_of_name_parameter VARCHAR(60), -- Eg. '@schedule_name' @name_of_id_parameter VARCHAR(60), -- Eg. '@schedule_id' @schedule_name sysname OUTPUT, @schedule_id INT OUTPUT, @owner_sid VARBINARY(85) OUTPUT, @orig_server_id INT OUTPUT, @job_id_filter UNIQUEIDENTIFIER = NULL AS BEGIN DECLARE @retval INT DECLARE @schedule_id_as_char VARCHAR(36) DECLARE @sch_name_count INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter)) SELECT @name_of_id_parameter = LTRIM(RTRIM(@name_of_id_parameter)) SELECT @schedule_name = LTRIM(RTRIM(@schedule_name)) SELECT @sch_name_count = 0 IF (@schedule_name = N'') SELECT @schedule_name = NULL IF ((@schedule_name IS NULL) AND (@schedule_id IS NULL)) OR ((@schedule_name IS NOT NULL) AND (@schedule_id IS NOT NULL)) BEGIN RAISERROR(14373, -1, -1, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- Check schedule id IF (@schedule_id IS NOT NULL) BEGIN -- if Agent is calling look in all schedules not just the local server schedules if(PROGRAM_NAME() LIKE N'SQLAgent%') BEGIN -- Look at all schedules SELECT @schedule_name = name, @owner_sid = owner_sid, @orig_server_id = originating_server_id FROM msdb.dbo.sysschedules WHERE (schedule_id = @schedule_id) END ELSE BEGIN --Look at local schedules only SELECT @schedule_name = name, @owner_sid = owner_sid, @orig_server_id = originating_server_id FROM msdb.dbo.sysschedules_localserver_view WHERE (schedule_id = @schedule_id) END IF (@schedule_name IS NULL) BEGIN --If the schedule is from an MSX and a sysadmin is calling report a specific 'MSX' message IF(PROGRAM_NAME() NOT LIKE N'SQLAgent%' AND ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 AND EXISTS(SELECT * FROM msdb.dbo.sysschedules as sched JOIN msdb.dbo.sysoriginatingservers_view as svr ON sched.originating_server_id = svr.originating_server_id WHERE (schedule_id = @schedule_id) AND (svr.master_server = 1))) BEGIN RAISERROR(14274, -1, -1) END ELSE BEGIN SELECT @schedule_id_as_char = CONVERT(VARCHAR(36), @schedule_id) RAISERROR(14262, -1, -1, '@schedule_id', @schedule_id_as_char) END RETURN(1) -- Failure END END ELSE -- Check job name IF (@schedule_name IS NOT NULL) BEGIN -- if a job_id is supplied use it as a filter. This helps with V8 legacy support IF(@job_id_filter IS NOT NULL) BEGIN -- Check if the job name is ambiguous and also get the schedule_id optimistically. -- If the name is not ambiguous this gets the corresponding schedule_id (if the schedule exists) SELECT @sch_name_count = COUNT(*), @schedule_id = MIN(s.schedule_id), @owner_sid = MIN(owner_sid), @orig_server_id = MIN(originating_server_id) FROM msdb.dbo.sysschedules_localserver_view as s JOIN msdb.dbo.sysjobschedules as js ON s.schedule_id = js.schedule_id WHERE (name = @schedule_name) AND (js.job_id = @job_id_filter) END ELSE BEGIN -- Check if the job name is ambiguous from the count(*) result -- If the name is not ambiguous it is safe use the fields returned by the MIN() function SELECT @sch_name_count = COUNT(*), @schedule_id = MIN(schedule_id), @owner_sid = MIN(owner_sid), @orig_server_id = MIN(originating_server_id) FROM msdb.dbo.sysschedules_localserver_view WHERE (name = @schedule_name) END IF(@sch_name_count > 1) BEGIN -- ambiguous, user needs to use a schedule_id instead of a schedule_name RAISERROR(14371, -1, -1, @schedule_name, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END --schedule_id isn't visible to this user or doesn't exist IF (@schedule_id IS NULL) BEGIN --If the schedule is from an MSX and a sysadmin is calling report a specific 'MSX' message IF(PROGRAM_NAME() NOT LIKE N'SQLAgent%' AND ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 AND EXISTS(SELECT * FROM msdb.dbo.sysschedules as sched JOIN msdb.dbo.sysoriginatingservers_view as svr ON sched.originating_server_id = svr.originating_server_id JOIN msdb.dbo.sysjobschedules as js ON sched.schedule_id = js.schedule_id WHERE (svr.master_server = 1) AND (name = @schedule_name) AND ((@job_id_filter IS NULL) OR (js.job_id = @job_id_filter)))) BEGIN RAISERROR(14274, -1, -1) END ELSE BEGIN --If not a MSX schedule raise local error RAISERROR(14262, -1, -1, '@schedule_name', @schedule_name) END RETURN(1) -- Failure END END RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_JOBPROC_CALLER */ /* */ /* NOTE: We define this procedure here instead of in the */ /* 'Support procedures' section because of the many */ /* other procedures that reference it. */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_jobproc_caller...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_jobproc_caller') AND (type = 'P'))) DROP PROCEDURE sp_verify_jobproc_caller go CREATE PROCEDURE sp_verify_jobproc_caller @job_id UNIQUEIDENTIFIER, @program_name sysname AS BEGIN SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @program_name = LTRIM(RTRIM(@program_name)) IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id) AND (master_server = 1) )) -- master_server = 1 filters on MSX jobs in this TSX server AND (PROGRAM_NAME() NOT LIKE @program_name) BEGIN RAISERROR(14274, -1, -1) RETURN(1) -- Failure END RETURN(0) END go /**************************************************************/ /* SP_DOWNLOADED_ROW_LIMITER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_downloaded_row_limiter...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_downloaded_row_limiter') AND (type = 'P'))) DROP PROCEDURE dbo.sp_downloaded_row_limiter go CREATE PROCEDURE sp_downloaded_row_limiter @server_name sysname -- Target server name AS BEGIN -- This trigger controls how many downloaded (status = 1) sysdownloadlist rows exist -- for any given server. It does NOT control the absolute number of rows in the table. DECLARE @current_rows_per_server INT DECLARE @max_rows_per_server INT -- This value comes from the resgistry (DownloadedMaxRows) DECLARE @rows_to_delete INT DECLARE @quoted_server_name NVARCHAR(514) -- enough room to accomodate the quoted name SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @server_name = LTRIM(RTRIM(@server_name)) -- Check the server name (if it's bad we fail silently) IF (@server_name IS NULL) OR (NOT EXISTS (SELECT * FROM msdb.dbo.sysdownloadlist WHERE (target_server = @server_name))) RETURN(1) -- Failure SELECT @max_rows_per_server = 0 -- Get the max-rows-per-server from the registry EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'DownloadedMaxRows', @max_rows_per_server OUTPUT, N'no_output' -- Check if we are limiting sysdownloadlist rows IF (ISNULL(@max_rows_per_server, -1) = -1) RETURN -- Check that max_rows_per_server is >= 0 IF (@max_rows_per_server < -1) BEGIN -- It isn't, so default to 100 rows SELECT @max_rows_per_server = 100 EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'DownloadedMaxRows', N'REG_DWORD', @max_rows_per_server END -- Get the number of downloaded rows in sysdownloadlist for the target server in question -- NOTE: Determining this [quickly] requires a [non-clustered] index on target_server SELECT @current_rows_per_server = COUNT(*) FROM msdb.dbo.sysdownloadlist WHERE (target_server = @server_name) AND (status = 1) -- Delete the oldest downloaded row(s) for the target server in question if the new row has -- pushed us over the per-server row limit SELECT @rows_to_delete = @current_rows_per_server - @max_rows_per_server IF (@rows_to_delete > 0) BEGIN WITH RowsToDelete AS ( SELECT TOP (@rows_to_delete) * FROM msdb.dbo.sysdownloadlist WHERE (target_server = @server_name) AND (status = 1) ORDER BY instance_id ) DELETE FROM RowsToDelete; END END go /**************************************************************/ /* SP_POST_MSX_OPERATION */ /* */ /* NOTE: We define this procedure here instead of in the */ /* 'Support procedures' section because of the many */ /* other procedures that reference it. */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_post_msx_operation...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_post_msx_operation') AND (type = 'P'))) DROP PROCEDURE sp_post_msx_operation go CREATE PROCEDURE sp_post_msx_operation @operation VARCHAR(64), @object_type VARCHAR(64) = 'JOB',-- Can be JOB, SERVER or SCHEDULE @job_id UNIQUEIDENTIFIER = NULL, -- NOTE: 0x00 means 'ALL' jobs @specific_target_server sysname = NULL, @value INT = NULL, -- For polling interval value @schedule_uid UNIQUEIDENTIFIER = NULL -- schedule_uid if the @object_type = 'SCHEDULE' AS BEGIN DECLARE @operation_code INT DECLARE @specific_target_server_id INT DECLARE @instructions_posted INT DECLARE @job_id_as_char VARCHAR(36) DECLARE @schedule_uid_as_char VARCHAR(36) DECLARE @msx_time_zone_adjustment INT DECLARE @local_machine_name sysname DECLARE @retval INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @operation = LTRIM(RTRIM(@operation)) SELECT @object_type = LTRIM(RTRIM(@object_type)) SELECT @specific_target_server = LTRIM(RTRIM(@specific_target_server)) -- Turn [nullable] empty string parameters into NULLs IF (@specific_target_server = N'') SELECT @specific_target_server = NULL -- Only a sysadmin can do this, but fail silently for a non-sysadmin IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) RETURN(0) -- Success (or more accurately a no-op) -- Check operation SELECT @operation = UPPER(@operation collate SQL_Latin1_General_CP1_CS_AS) SELECT @operation_code = CASE @operation WHEN 'INSERT' THEN 1 WHEN 'UPDATE' THEN 2 WHEN 'DELETE' THEN 3 WHEN 'START' THEN 4 WHEN 'STOP' THEN 5 WHEN 'RE-ENLIST' THEN 6 WHEN 'DEFECT' THEN 7 WHEN 'SYNC-TIME' THEN 8 WHEN 'SET-POLL' THEN 9 ELSE 0 END IF (@operation_code = 0) BEGIN RAISERROR(14266, -1, -1, '@operation_code', 'INSERT, UPDATE, DELETE, START, STOP, RE-ENLIST, DEFECT, SYNC-TIME, SET-POLL') RETURN(1) -- Failure END -- Check object type (in 9.0 only 'JOB', 'SERVER' or 'SCHEDULE'are valid) IF ((@object_type <> 'JOB') AND (@object_type <> 'SERVER') AND (@object_type <> 'SCHEDULE')) BEGIN RAISERROR(14266, -1, -1, '@object_type', 'JOB, SERVER, SCHEDULE') RETURN(1) -- Failure END -- Check that for a object type of JOB a job_id has been supplied IF ((@object_type = 'JOB') AND (@job_id IS NULL)) BEGIN RAISERROR(14233, -1, -1) RETURN(1) -- Failure END -- Check that for a object type of JOB a job_id has been supplied IF ((@object_type = 'SCHEDULE') AND (@schedule_uid IS NULL)) BEGIN RAISERROR(14365, -1, -1) RETURN(1) -- Failure END -- Check polling interval value IF (@operation_code = 9) AND ((ISNULL(@value, 0) < 10) OR (ISNULL(@value, 0) > 28800)) BEGIN RAISERROR(14266, -1, -1, '@value', '10..28800') RETURN(1) -- Failure END -- Check specific target server IF (@specific_target_server IS NOT NULL) BEGIN SELECT @specific_target_server = UPPER(@specific_target_server) -- Check if the local server is being targeted IF (@specific_target_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) BEGIN RETURN(0) END ELSE BEGIN SELECT @specific_target_server_id = server_id FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @specific_target_server) IF (@specific_target_server_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@specific_target_server', @specific_target_server) RETURN(1) -- Failure END END END -- Check that this server is an MSX server IF ((SELECT COUNT(*) FROM msdb.dbo.systargetservers) = 0) BEGIN RETURN(0) END -- Get local machine name EXECUTE @retval = master.dbo.xp_getnetname @local_machine_name OUTPUT IF (@retval <> 0) OR (@local_machine_name IS NULL) BEGIN RAISERROR(14225, -1, -1) RETURN(1) END -- Job-specific processing... IF (@object_type = 'JOB') BEGIN -- Validate the job (if supplied) IF (@job_id <> CONVERT(UNIQUEIDENTIFIER, 0x00)) BEGIN SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) -- Check if the job exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id))) BEGIN RAISERROR(14262, -1, -1, '@job_id', @job_id_as_char) RETURN(1) -- Failure END -- If this is a local job then there's nothing for us to do IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) -- 0 means local server OR (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id))) BEGIN RETURN(0) END END -- Generate the sysdownloadlist row(s)... IF (@operation_code = 1) OR -- Insert (@operation_code = 2) OR -- Update (@operation_code = 3) OR -- Delete (@operation_code = 4) OR -- Start (@operation_code = 5) -- Stop BEGIN IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) -- IE. 'ALL' BEGIN -- All jobs -- Handle DELETE as a special case (rather than posting 1 instruction per job we just -- post a single instruction that means 'delete all jobs from the MSX') IF (@operation_code = 3) BEGIN INSERT INTO msdb.dbo.sysdownloadlist (source_server, operation_code, object_type, object_id, target_server) SELECT @local_machine_name, @operation_code, 1, -- 1 means 'JOB' CONVERT(UNIQUEIDENTIFIER, 0x00), sts.server_name FROM systargetservers sts WHERE ((@specific_target_server_id IS NULL) OR (sts.server_id = @specific_target_server_id)) AND ((SELECT COUNT(*) FROM msdb.dbo.sysjobservers WHERE (server_id = sts.server_id)) > 0) SELECT @instructions_posted = @@rowcount END ELSE BEGIN INSERT INTO msdb.dbo.sysdownloadlist (source_server, operation_code, object_type, object_id, target_server) SELECT @local_machine_name, @operation_code, 1, -- 1 means 'JOB' sjv.job_id, sts.server_name FROM sysjobs_view sjv, sysjobservers sjs, systargetservers sts WHERE (sjv.job_id = sjs.job_id) AND (sjs.server_id = sts.server_id) AND (sjs.server_id <> 0) -- We want to exclude local jobs AND ((@specific_target_server_id IS NULL) OR (sjs.server_id = @specific_target_server_id)) SELECT @instructions_posted = @@rowcount END END ELSE BEGIN -- Specific job (ie. @job_id is not 0x00) INSERT INTO msdb.dbo.sysdownloadlist (source_server, operation_code, object_type, object_id, target_server, deleted_object_name) SELECT @local_machine_name, @operation_code, 1, -- 1 means 'JOB' sjv.job_id, sts.server_name, CASE @operation_code WHEN 3 -- Delete THEN sjv.name ELSE NULL END FROM sysjobs_view sjv, sysjobservers sjs, systargetservers sts WHERE (sjv.job_id = @job_id) AND (sjv.job_id = sjs.job_id) AND (sjs.server_id = sts.server_id) AND (sjs.server_id <> 0) -- We want to exclude local jobs AND ((@specific_target_server_id IS NULL) OR (sjs.server_id = @specific_target_server_id)) SELECT @instructions_posted = @@rowcount END END ELSE BEGIN RAISERROR(14266, -1, -1, '@operation_code', 'INSERT, UPDATE, DELETE, START, STOP') RETURN(1) -- Failure END END -- SCHEDULE specific processing for INSERT, UPDATE or DELETE schedule operations -- All msx jobs that use the specified @schedule_uid will be notified with an Insert operation. -- This will cause agent to reload all schedules for each job. -- This is compatible with the legacy shiloh servers that don't know about reusable schedules IF (@object_type = 'SCHEDULE') BEGIN -- Validate the schedule -- Check if the schedule exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysschedules_localserver_view WHERE (schedule_uid = @schedule_uid))) BEGIN SELECT @schedule_uid_as_char = CONVERT(VARCHAR(36), @schedule_uid) RAISERROR(14262, -1, -1, '@schedule_uid', @schedule_uid_as_char) RETURN(1) -- Failure END -- If this schedule is only used locally (no target servers) then there's nothing to do IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysschedules s, msdb.dbo.sysjobschedules js, msdb.dbo.sysjobs_view sjv, msdb.dbo.sysjobservers sjs, msdb.dbo.systargetservers sts WHERE (s.schedule_uid = @schedule_uid) AND (s.schedule_id = js.schedule_id) AND (sjv.job_id = js.job_id) AND (sjv.job_id = sjs.job_id) AND (sjs.server_id = sts.server_id) AND (sjs.server_id <> 0))) BEGIN RETURN(0) END -- Generate the sysdownloadlist row(s)... IF (@operation_code = 1) OR -- Insert (@operation_code = 2) OR -- Update (@operation_code = 3) -- Delete BEGIN -- Insert specific schedule into sysdownloadlist -- We need to create a sysdownloadlist JOB INSERT record for each job that runs the schedule INSERT INTO msdb.dbo.sysdownloadlist (source_server, operation_code, object_type, object_id, target_server) SELECT @local_machine_name, 1, -- 1 means 'Insert' 1, -- 1 means 'JOB' sjv.job_id, sts.server_name FROM msdb.dbo.sysschedules s, msdb.dbo.sysjobschedules js, msdb.dbo.sysjobs_view sjv, msdb.dbo.sysjobservers sjs, systargetservers sts WHERE (s.schedule_id = js.schedule_id) AND (js.job_id = sjv.job_id) AND (sjv.job_id = sjs.job_id) AND (sjs.server_id = sts.server_id) AND (s.schedule_uid = @schedule_uid) AND (sjs.server_id <> 0) -- We want to exclude local jobs AND ((@specific_target_server_id IS NULL) OR (sjs.server_id = @specific_target_server_id)) SELECT @instructions_posted = @@rowcount END ELSE BEGIN RAISERROR(14266, -1, -1, '@operation_code', 'UPDATE, DELETE') RETURN(1) -- Failure END END -- Server-specific processing... IF (@object_type = 'SERVER') BEGIN -- Generate the sysdownloadlist row(s)... IF (@operation_code = 6) OR -- ReEnlist (@operation_code = 7) OR -- Defect (@operation_code = 8) OR -- Synchronize time (with MSX) (@operation_code = 9) -- Set MSX polling interval (in seconds) BEGIN IF (@operation_code = 8) BEGIN EXECUTE master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SYSTEM\CurrentControlSet\Control\TimeZoneInformation', N'Bias', @msx_time_zone_adjustment OUTPUT, N'no_output' SELECT @msx_time_zone_adjustment = -ISNULL(@msx_time_zone_adjustment, 0) END INSERT INTO msdb.dbo.sysdownloadlist (source_server, operation_code, object_type, object_id, target_server) SELECT @local_machine_name, @operation_code, 2, -- 2 means 'SERVER' CASE @operation_code WHEN 8 THEN CONVERT(UNIQUEIDENTIFIER, CONVERT(BINARY(16), -(@msx_time_zone_adjustment - sts.time_zone_adjustment))) WHEN 9 THEN CONVERT(UNIQUEIDENTIFIER, CONVERT(BINARY(16), @value)) ELSE CONVERT(UNIQUEIDENTIFIER, 0x00) END, sts.server_name FROM systargetservers sts WHERE ((@specific_target_server_id IS NULL) OR (sts.server_id = @specific_target_server_id)) SELECT @instructions_posted = @@rowcount END ELSE BEGIN RAISERROR(14266, -1, -1, '@operation_code', 'RE-ENLIST, DEFECT, SYNC-TIME, SET-POLL') RETURN(1) -- Failure END END -- Report number of rows inserted IF (@object_type = 'JOB') AND (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) AND (@instructions_posted = 0) AND (@specific_target_server_id IS NOT NULL) RAISERROR(14231, 0, 1, '@specific_target_server', @specific_target_server) ELSE RAISERROR(14230, 0, 1, @instructions_posted, @operation) -- Delete any [downloaded] instructions that are over the registry-defined limit IF (@specific_target_server IS NOT NULL) EXECUTE msdb.dbo.sp_downloaded_row_limiter @specific_target_server RETURN(0) -- 0 means success END go /**************************************************************/ /* SP_VERIFY_PERFORMANCE_CONDITION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_performance_condition...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_performance_condition') AND (type = 'P'))) DROP PROCEDURE sp_verify_performance_condition go CREATE PROCEDURE sp_verify_performance_condition @performance_condition NVARCHAR(512) AS BEGIN DECLARE @delimiter_count INT DECLARE @temp_str NVARCHAR(512) DECLARE @object_name sysname DECLARE @counter_name sysname DECLARE @instance_name sysname DECLARE @pos INT SET NOCOUNT ON -- The performance condition must have the format 'object|counter|instance|comparator|value' -- NOTE: 'instance' may be empty. IF (PATINDEX(N'%_|%_|%|[><=]|[0-9]%', @performance_condition) = 0) BEGIN RAISERROR(14507, 16, 1) RETURN(1) -- Failure END -- Parse the performance_condition SELECT @delimiter_count = 0 SELECT @temp_str = @performance_condition SELECT @pos = CHARINDEX(N'|', @temp_str) WHILE (@pos <> 0) BEGIN SELECT @delimiter_count = @delimiter_count + 1 IF (@delimiter_count = 1) SELECT @object_name = SUBSTRING(@temp_str, 1, @pos - 1) IF (@delimiter_count = 2) SELECT @counter_name = SUBSTRING(@temp_str, 1, @pos - 1) IF (@delimiter_count = 3) SELECT @instance_name = SUBSTRING(@temp_str, 1, @pos - 1) SELECT @temp_str = SUBSTRING(@temp_str, @pos + 1, (DATALENGTH(@temp_str) / 2) - @pos) SELECT @pos = CHARINDEX(N'|', @temp_str) END IF (@delimiter_count <> 4) BEGIN RAISERROR(14507, 16, 1) RETURN(1) -- Failure END -- Check the object_name IF (NOT EXISTS (SELECT * FROM master.dbo.sysperfinfo WHERE (object_name = @object_name))) BEGIN RAISERROR(14262, 16, 1, 'object_name', @object_name) RETURN(1) -- Failure END -- Check the counter_name IF (NOT EXISTS (SELECT * FROM master.dbo.sysperfinfo WHERE (object_name = @object_name) AND (counter_name = @counter_name))) BEGIN RAISERROR(14262, 16, 1, 'counter_name', @counter_name) RETURN(1) -- Failure END -- Check the instance_name IF (@instance_name IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM master.dbo.sysperfinfo WHERE (object_name = @object_name) AND (counter_name = @counter_name) AND (instance_name = @instance_name))) BEGIN RAISERROR(14262, 16, 1, 'instance_name', @instance_name) RETURN(1) -- Failure END END RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_JOB_DATE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_job_date...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_job_date') AND (type = 'P'))) DROP PROCEDURE sp_verify_job_date go CREATE PROCEDURE sp_verify_job_date @date INT, @date_name VARCHAR(60) = 'date', @error_severity INT = -1 AS BEGIN SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @date_name = LTRIM(RTRIM(@date_name)) IF ((ISDATE(CONVERT(VARCHAR, @date)) = 0) OR (@date < 19900101) OR (@date > 99991231)) BEGIN RAISERROR(14266, @error_severity, -1, @date_name, '19900101..99991231') RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_JOB_TIME */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_job_time...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_job_time') AND (type = 'P'))) DROP PROCEDURE sp_verify_job_time go CREATE PROCEDURE sp_verify_job_time @time INT, @time_name VARCHAR(60) = 'time', @error_severity INT = -1 AS BEGIN DECLARE @hour INT DECLARE @minute INT DECLARE @second INT DECLARE @part_name NVARCHAR(50) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @time_name = LTRIM(RTRIM(@time_name)) IF ((@time < 0) OR (@time > 235959)) BEGIN RAISERROR(14266, @error_severity, -1, @time_name, '000000..235959') RETURN(1) -- Failure END SELECT @hour = (@time / 10000) SELECT @minute = (@time % 10000) / 100 SELECT @second = (@time % 100) -- Check hour range IF (@hour > 23) BEGIN SELECT @part_name = FORMATMESSAGE(14218) RAISERROR(14287, @error_severity, -1, @time_name, @part_name) RETURN(1) -- Failure END -- Check minute range IF (@minute > 59) BEGIN SELECT @part_name = FORMATMESSAGE(14219) RAISERROR(14287, @error_severity, -1, @time_name, @part_name) RETURN(1) -- Failure END -- Check second range IF (@second > 59) BEGIN SELECT @part_name = FORMATMESSAGE(14220) RAISERROR(14287, @error_severity, -1, @time_name, @part_name) RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_ALERT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_alert...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_alert') AND (type = 'P'))) DROP PROCEDURE sp_verify_alert go CREATE PROCEDURE sp_verify_alert @name sysname, @message_id INT, @severity INT, @enabled TINYINT, @delay_between_responses INT, @notification_message NVARCHAR(512), @include_event_description_in TINYINT, @database_name sysname, @event_description_keyword NVARCHAR(100), @job_id UNIQUEIDENTIFIER OUTPUT, @job_name sysname OUTPUT, @occurrence_count INT, @raise_snmp_trap TINYINT, @performance_condition NVARCHAR(512), @category_name sysname, @category_id INT OUTPUT, @count_reset_date INT, @count_reset_time INT, @wmi_namespace NVARCHAR(512), -- New for 9.0 @wmi_query NVARCHAR(512), -- New for 9.0 @event_id INT OUTPUT -- New for 9.0 AS BEGIN DECLARE @retval INT DECLARE @non_alertable_errors VARCHAR(512) DECLARE @message_id_as_string VARCHAR(10) DECLARE @res_valid_range NVARCHAR(100) DECLARE @alert_no_wmi_check INT DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @notification_message = LTRIM(RTRIM(@notification_message)) SELECT @database_name = LTRIM(RTRIM(@database_name)) SELECT @event_description_keyword = LTRIM(RTRIM(@event_description_keyword)) SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @performance_condition = LTRIM(RTRIM(@performance_condition)) SELECT @category_name = LTRIM(RTRIM(@category_name)) SELECT @alert_no_wmi_check = 0 -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check if the NewName is unique IF (EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (name = @name))) BEGIN RAISERROR(14261, 16, 1, '@name', @name) RETURN(1) -- Failure END -- Check if the user has supplied MessageID OR Severity OR Performance-Condition OR WMI namespace/query IF ((@performance_condition IS NULL) AND (@message_id = 0) AND (@severity = 0) AND ((@wmi_namespace IS NULL) OR (@wmi_query IS NULL))) OR ((@performance_condition IS NOT NULL) AND ((@message_id <> 0) OR (@severity <> 0) OR (@wmi_namespace IS NOT NULL) OR (@wmi_query IS NOT NULL))) OR ((@message_id <> 0) AND ((@performance_condition IS NOT NULL) OR (@severity <> 0) OR (@wmi_namespace IS NOT NULL) OR (@wmi_query IS NOT NULL))) OR ((@severity <> 0) AND ((@performance_condition IS NOT NULL) OR (@message_id <> 0) OR (@wmi_namespace IS NOT NULL) OR (@wmi_query IS NOT NULL))) BEGIN RAISERROR(14500, 16, 1) RETURN(1) -- Failure END -- Check the Severity IF ((@severity < 0) OR (@severity > 25)) BEGIN RAISERROR(14266, 16, 1, '@severity', '0..25') RETURN(1) -- Failure END -- Check the MessageID IF (@message_id <> 0) AND (NOT EXISTS (SELECT error FROM master.dbo.sysmessages WHERE (error = @message_id))) BEGIN SELECT @message_id_as_string = CONVERT(VARCHAR, @message_id) RAISERROR(14262, 16, 1, '@message_id', @message_id_as_string) RETURN(1) -- Failure END -- Check if it is legal to set an alert on this MessageID DECLARE @TempRetVal TABLE (RetVal INT) EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'NonAlertableErrors', @non_alertable_errors OUTPUT, N'no_output' IF (ISNULL(@non_alertable_errors, N'NULL') <> N'NULL') BEGIN DECLARE @message_id_as_char VARCHAR(10) SELECT @message_id_as_char = CONVERT(VARCHAR(10), @message_id) INSERT INTO @TempRetVal EXECUTE ('IF (' + @message_id_as_char + ' IN (' + @non_alertable_errors + ')) SELECT 1') END IF (EXISTS (SELECT * FROM @TempRetVal)) BEGIN RAISERROR(14506, 16, 1, @message_id) RETURN(1) -- Failure END -- Enabled must be 0 or 1 IF (@enabled NOT IN (0, 1)) BEGIN RAISERROR(14266, 16, 1, '@enabled', '0, 1') RETURN(1) -- Failure END -- DelayBetweenResponses must be > 0 IF (@delay_between_responses < 0) BEGIN SELECT @res_valid_range = FORMATMESSAGE(14206) RAISERROR(14266, 16, 1, '@delay_between_responses', @res_valid_range) RETURN(1) -- Failure END -- NOTE: We don't check the notification message -- Check IncludeEventDescriptionIn IF ((@include_event_description_in < 0) OR (@include_event_description_in > 7)) BEGIN SELECT @res_valid_range = FORMATMESSAGE(14208) RAISERROR(14266, 16, 1, '@include_event_description_in', @res_valid_range) RETURN(1) -- Failure END -- Check the database name IF (@database_name IS NOT NULL) AND (DB_ID(@database_name) IS NULL) BEGIN RAISERROR(15010, 16, 1, @database_name) RETURN(1) -- Failure END -- NOTE: We don't check the event description keyword -- Check JobName/ID IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL)) BEGIN -- We use '' as a special value which means 'no job' (we cannot use NULL since this forces -- sp_update_alert to use the existing value) IF (@job_name = N'') SELECT @job_id = 0x00 ELSE BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReaderRole and SQLAgentOperatorRole can see all jobs but -- cannot modify them IF (@job_owner_sid <> SUSER_SID() -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) -- is not sysadmin BEGIN RAISERROR(14525, -1, -1); RETURN(1) -- Failure END -- Check that the job is a local job IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN RAISERROR(14527, -1, -1, @job_name) RETURN(1) -- Failure END END END -- OccurrenceCount must be > 0 IF (@occurrence_count < 0) BEGIN RAISERROR(14266, 16, 1, '@occurrence_count', '0..n') RETURN(1) -- Failure END -- RaiseSNMPTrap must be 0 or 1 IF (@raise_snmp_trap NOT IN (0, 1)) BEGIN RAISERROR(14266, 16, 1, '@raise_snmp_trap', '0, 1') RETURN(1) -- Failure END -- Check the performance condition (including invalid parameter combinations) IF (@performance_condition IS NOT NULL) BEGIN IF (@database_name IS NOT NULL) BEGIN RAISERROR(14505, 16, 1, '@database_name') RETURN(1) -- Failure END IF (@event_description_keyword IS NOT NULL) BEGIN RAISERROR(14505, 16, 1, '@event_description_keyword') RETURN(1) -- Failure END IF (@wmi_namespace IS NOT NULL) BEGIN RAISERROR(14505, 16, 1, '@wmi_namespace') RETURN(1) -- Failure END IF (@wmi_query IS NOT NULL) BEGIN RAISERROR(14505, 16, 1, '@wmi_query') RETURN(1) -- Failure END -- Verify the performance condition EXECUTE @retval = msdb.dbo.sp_verify_performance_condition @performance_condition IF (@retval <> 0) RETURN(1) -- Failure END -- Check category name IF (@category_name = N'[DEFAULT]') SELECT @category_id = 98 ELSE BEGIN SELECT @category_id = category_id FROM msdb.dbo.syscategories WHERE (category_class = 2) -- Alerts AND (category_type = 3) -- None AND (name = @category_name) END IF (@category_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@category_name', @category_name) RETURN(1) -- Failure END -- Check count reset date IF (@count_reset_date <> 0) BEGIN EXECUTE @retval = msdb.dbo.sp_verify_job_date @count_reset_date, '@count_reset_date' IF (@retval <> 0) RETURN(1) -- Failure END -- Check count reset time IF (@count_reset_time <> 0) BEGIN EXECUTE @retval = msdb.dbo.sp_verify_job_time @count_reset_time, '@count_reset_time' IF (@retval <> 0) RETURN(1) -- Failure END -- Check WMI parameters. Both must exist IF (@wmi_namespace IS NOT NULL) BEGIN IF (@wmi_query IS NULL) BEGIN RAISERROR(14509, 16, 1, '@wmi_query') RETURN(1) -- Failure END IF (@database_name IS NOT NULL) BEGIN RAISERROR(14510, 16, 1, '@database_name') RETURN(1) -- Failure END IF (@event_description_keyword IS NOT NULL) BEGIN RAISERROR(14510, 16, 1, '@event_description_keyword') RETURN(1) -- Failure END --do not check WMI properties if a registry setting is present EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertNoWmiCheck', @alert_no_wmi_check OUTPUT, 'no_output' if (@alert_no_wmi_check <> 1) BEGIN EXECUTE @retval = msdb.dbo.sp_sqlagent_notify @op_type = N'T', @wmi_namespace = @wmi_namespace, @wmi_query = @wmi_query, @error_flag = 0 IF (@retval <> 0) BEGIN RAISERROR(14511, 16, 1) RETURN(1) -- Failure END END -- Set event_id to indicate WMI alert SELECT @event_id = 8 END ELSE IF (@wmi_query IS NOT NULL) BEGIN RAISERROR(14512, 16, 1, '@wmi_namespace') RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_UPDATE_ALERT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_alert...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_alert') AND (type = 'P'))) DROP PROCEDURE sp_update_alert go CREATE PROCEDURE sp_update_alert @name sysname, @new_name sysname = NULL, @enabled TINYINT = NULL, @message_id INT = NULL, @severity INT = NULL, @delay_between_responses INT = NULL, @notification_message NVARCHAR(512) = NULL, @include_event_description_in TINYINT = NULL, -- 0 = None, 1 = Email, 2 = Pager. 4 = NetSend, 7 = All @database_name sysname = NULL, @event_description_keyword NVARCHAR(100) = NULL, @job_id UNIQUEIDENTIFIER = NULL, -- If provided must NOT also provide job_name @job_name sysname = NULL, -- If provided must NOT also provide job_id @occurrence_count INT = NULL, -- Can only be set to 0 @count_reset_date INT = NULL, @count_reset_time INT = NULL, @last_occurrence_date INT = NULL, -- Can only be set to 0 @last_occurrence_time INT = NULL, -- Can only be set to 0 @last_response_date INT = NULL, -- Can only be set to 0 @last_response_time INT = NULL, -- Can only be set to 0 @raise_snmp_trap TINYINT = NULL, @performance_condition NVARCHAR(512) = NULL, -- New for 7.0 @category_name sysname = NULL, -- New for 7.0 @wmi_namespace sysname = NULL, -- New for 9.0 @wmi_query NVARCHAR(512) = NULL -- New for 9.0 AS BEGIN DECLARE @x_enabled TINYINT DECLARE @x_message_id INT DECLARE @x_severity INT DECLARE @x_delay_between_responses INT DECLARE @x_notification_message NVARCHAR(512) DECLARE @x_include_event_description TINYINT DECLARE @x_database_name sysname DECLARE @x_event_description_keyword NVARCHAR(100) DECLARE @x_occurrence_count INT DECLARE @x_count_reset_date INT DECLARE @x_count_reset_time INT DECLARE @x_last_occurrence_date INT DECLARE @x_last_occurrence_time INT DECLARE @x_last_response_date INT DECLARE @x_last_response_time INT DECLARE @x_flags INT DECLARE @x_performance_condition NVARCHAR(512) DECLARE @x_job_id UNIQUEIDENTIFIER DECLARE @x_category_id INT DECLARE @x_event_id INT DECLARE @x_wmi_namespace sysname DECLARE @x_wmi_query NVARCHAR(512) DECLARE @include_event_desc_code TINYINT DECLARE @return_code INT DECLARE @duplicate_name sysname DECLARE @category_id INT DECLARE @alert_id INT DECLARE @cached_attribute_modified INT DECLARE @event_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @new_name = LTRIM(RTRIM(@new_name)) SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @notification_message = LTRIM(RTRIM(@notification_message)) SELECT @database_name = LTRIM(RTRIM(@database_name)) SELECT @event_description_keyword = LTRIM(RTRIM(@event_description_keyword)) SELECT @performance_condition = LTRIM(RTRIM(@performance_condition)) SELECT @category_name = LTRIM(RTRIM(@category_name)) -- Are we modifying an attribute which SQLServerAgent caches? IF ((@new_name IS NOT NULL) OR (@enabled IS NOT NULL) OR (@message_id IS NOT NULL) OR (@severity IS NOT NULL) OR (@delay_between_responses IS NOT NULL) OR (@notification_message IS NOT NULL) OR (@include_event_description_in IS NOT NULL) OR (@database_name IS NOT NULL) OR (@event_description_keyword IS NOT NULL) OR (@job_id IS NOT NULL) OR (@job_name IS NOT NULL) OR (@last_response_date IS NOT NULL) OR (@last_response_time IS NOT NULL) OR (@raise_snmp_trap IS NOT NULL) OR (@performance_condition IS NOT NULL) OR (@wmi_namespace IS NOT NULL) OR (@wmi_query IS NOT NULL)) SELECT @cached_attribute_modified = 1 ELSE SELECT @cached_attribute_modified = 0 -- Map a job_id of 0 to the real value we use to mean 'no job' IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) AND (@job_name IS NULL) SELECT @job_name = N'' -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) END -- Check if SQLServerAgent is in the process of starting EXECUTE @return_code = msdb.dbo.sp_is_sqlagent_starting IF (@return_code <> 0) RETURN(1) -- Failure -- Check if this Alert exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (name = @name))) BEGIN RAISERROR(14262, 16, 1, '@name', @name) RETURN(1) END -- Certain values (if supplied) may only be updated to 0 IF (@occurrence_count <> 0) BEGIN RAISERROR(14266, -1, -1, '@occurrence_count', '0') RETURN(1) -- Failure END IF (@last_occurrence_date <> 0) BEGIN RAISERROR(14266, -1, -1, '@last_occurrence_date', '0') RETURN(1) -- Failure END IF (@last_occurrence_time <> 0) BEGIN RAISERROR(14266, -1, -1, '@last_occurrence_time', '0') RETURN(1) -- Failure END IF (@last_response_date <> 0) BEGIN RAISERROR(14266, -1, -1, '@last_response_date', '0') RETURN(1) -- Failure END IF (@last_response_time <> 0) BEGIN RAISERROR(14266, -1, -1, '@last_response_time', '0') RETURN(1) -- Failure END -- Get existing (@x_) values SELECT @alert_id = id, @x_enabled = enabled, @x_message_id = message_id, @x_severity = severity, @x_delay_between_responses = delay_between_responses, @x_notification_message = notification_message, @x_include_event_description = include_event_description, @x_database_name = database_name, @x_event_description_keyword = event_description_keyword, @x_occurrence_count = occurrence_count, @x_count_reset_date = count_reset_date, @x_count_reset_time = count_reset_time, @x_job_id = job_id, @x_last_occurrence_date = last_occurrence_date, @x_last_occurrence_time = last_occurrence_time, @x_last_response_date = last_response_date, @x_last_response_time = last_response_time, @x_flags = flags, @x_performance_condition = performance_condition, @x_category_id = category_id, @x_event_id = event_id FROM msdb.dbo.sysalerts WHERE (name = @name) SELECT @x_job_id = sjv.job_id FROM msdb.dbo.sysalerts sa, msdb.dbo.sysjobs_view sjv WHERE (sa.job_id = sjv.job_id) AND (sa.name = @name) -- Fill out the values for all non-supplied parameters from the existsing values IF (@x_event_id = 8) BEGIN -- WMI alert type IF (@wmi_namespace IS NULL) SELECT @wmi_namespace = @x_database_name IF (@wmi_query IS NULL) SELECT @wmi_query = @x_performance_condition END ELSE BEGIN -- Non-WMI alert type IF (@database_name IS NULL) SELECT @database_name = @x_database_name IF (@performance_condition IS NULL) SELECT @performance_condition = @x_performance_condition END IF (@enabled IS NULL) SELECT @enabled = @x_enabled IF (@message_id IS NULL) SELECT @message_id = @x_message_id IF (@severity IS NULL) SELECT @severity = @x_severity IF (@delay_between_responses IS NULL) SELECT @delay_between_responses = @x_delay_between_responses IF (@notification_message IS NULL) SELECT @notification_message = @x_notification_message IF (@include_event_description_in IS NULL) SELECT @include_event_description_in = @x_include_event_description IF (@event_description_keyword IS NULL) SELECT @event_description_keyword = @x_event_description_keyword IF (@job_id IS NULL) AND (@job_name IS NULL) SELECT @job_id = @x_job_id IF (@occurrence_count IS NULL) SELECT @occurrence_count = @x_occurrence_count IF (@count_reset_date IS NULL) SELECT @count_reset_date = @x_count_reset_date IF (@count_reset_time IS NULL) SELECT @count_reset_time = @x_count_reset_time IF (@last_occurrence_date IS NULL) SELECT @last_occurrence_date = @x_last_occurrence_date IF (@last_occurrence_time IS NULL) SELECT @last_occurrence_time = @x_last_occurrence_time IF (@last_response_date IS NULL) SELECT @last_response_date = @x_last_response_date IF (@last_response_time IS NULL) SELECT @last_response_time = @x_last_response_time IF (@raise_snmp_trap IS NULL) SELECT @raise_snmp_trap = @x_flags & 0x1 IF (@category_name IS NULL) SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = @x_category_id) IF (@category_name IS NULL) BEGIN SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = 98) END -- Turn [nullable] empty string parameters into NULLs IF (@new_name = N'') SELECT @new_name = NULL IF (@notification_message = N'') SELECT @notification_message = NULL IF (@database_name = N'') SELECT @database_name = NULL IF (@event_description_keyword = N'') SELECT @event_description_keyword = NULL IF (@performance_condition = N'') SELECT @performance_condition = NULL IF (@wmi_namespace = N'') SELECT @wmi_namespace = NULL IF (@wmi_query = N'') SELECT @wmi_query = NULL -- Verify the Alert IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) SELECT @job_id = NULL EXECUTE @return_code = sp_verify_alert @new_name, @message_id, @severity, @enabled, @delay_between_responses, @notification_message, @include_event_description_in, @database_name, @event_description_keyword, @job_id OUTPUT, @job_name OUTPUT, @occurrence_count, @raise_snmp_trap, @performance_condition, @category_name, @category_id OUTPUT, @count_reset_date, @count_reset_time, @wmi_namespace, @wmi_query, @event_id OUTPUT IF (@return_code <> 0) RETURN(1) -- Failure -- If the user didn't supply a NewName, use the old one. -- NOTE: This must be done AFTER sp_verify_alert. IF (@new_name IS NULL) SELECT @new_name = @name -- Turn the 1st 'flags' bit on or off accordingly IF (@raise_snmp_trap = 0) SELECT @x_flags = @x_flags & 0xFFFE ELSE SELECT @x_flags = @x_flags | 0x0001 -- For WMI alerts replace -- database_name with wmi_namespace and -- performance_conditon with wmi_query -- so we can store them in those columns in sysalerts table IF (@event_id = 8) BEGIN SELECT @database_name = @wmi_namespace SELECT @performance_condition = @wmi_query END -- Check if this Alert already exists SELECT @duplicate_name = FORMATMESSAGE(14205) SELECT @duplicate_name = name FROM msdb.dbo.sysalerts WHERE ((event_id = 8) AND (ISNULL(performance_condition, N'') = ISNULL(@performance_condition, N'')) AND (ISNULL(database_name, N'') = ISNULL(@database_name, N''))) OR ((ISNULL(event_id,1) <> 8) AND (ISNULL(performance_condition, N'apples') = ISNULL(@performance_condition, N'oranges'))) OR ((performance_condition IS NULL) AND (message_id = @message_id) AND (severity = @severity) AND (ISNULL(database_name, N'') = ISNULL(@database_name, N'')) AND (ISNULL(event_description_keyword, N'') = ISNULL(@event_description_keyword, N''))) IF (@duplicate_name <> FORMATMESSAGE(14205) AND @duplicate_name <> @name) BEGIN RAISERROR(14501, 16, 1, @duplicate_name) RETURN(1) -- Failure END -- Finally, do the actual UPDATE UPDATE msdb.dbo.sysalerts SET name = @new_name, message_id = @message_id, severity = @severity, enabled = @enabled, delay_between_responses = @delay_between_responses, notification_message = @notification_message, include_event_description = @include_event_description_in, database_name = @database_name, event_description_keyword = @event_description_keyword, job_id = ISNULL(@job_id, CONVERT(UNIQUEIDENTIFIER, 0x00)), occurrence_count = @occurrence_count, count_reset_date = @count_reset_date, count_reset_time = @count_reset_time, last_occurrence_date = @last_occurrence_date, last_occurrence_time = @last_occurrence_time, last_response_date = @last_response_date, last_response_time = @last_response_time, flags = @x_flags, performance_condition = @performance_condition, category_id = @category_id, event_id = @event_id WHERE (name = @name) -- Notify SQLServerAgent of the change IF (@cached_attribute_modified = 1) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'U' RETURN(0) -- Success END go /**************************************************************/ /* SP_DELETE_JOB_REFERENCES */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_job_references...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_job_references') AND (type = 'P'))) DROP PROCEDURE sp_delete_job_references go CREATE PROCEDURE sp_delete_job_references @notify_sqlagent BIT = 1 AS BEGIN DECLARE @deleted_job_id UNIQUEIDENTIFIER DECLARE @task_id_as_char VARCHAR(10) DECLARE @job_is_cached INT DECLARE @alert_name sysname DECLARE @maintplan_plan_id UNIQUEIDENTIFIER DECLARE @maintplan_subplan_id UNIQUEIDENTIFIER -- Keep SQLServerAgent's cache in-sync and cleanup any 'webtask' cross-references to the deleted job(s) -- NOTE: The caller must have created a table called #temp_jobs_to_delete of the format -- (job_id UNIQUEIDENTIFIER NOT NULL, job_is_cached INT NOT NULL). DECLARE sqlagent_notify CURSOR LOCAL FOR SELECT job_id, job_is_cached FROM #temp_jobs_to_delete OPEN sqlagent_notify FETCH NEXT FROM sqlagent_notify INTO @deleted_job_id, @job_is_cached WHILE (@@fetch_status = 0) BEGIN -- NOTE: We only notify SQLServerAgent if we know the job has been cached IF(@job_is_cached = 1 AND @notify_sqlagent = 1) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @deleted_job_id, @action_type = N'D' IF (EXISTS (SELECT * FROM master.dbo.sysobjects WHERE (name = N'sp_cleanupwebtask') AND (type = 'P'))) BEGIN SELECT @task_id_as_char = CONVERT(VARCHAR(10), task_id) FROM msdb.dbo.systaskids WHERE (job_id = @deleted_job_id) IF (@task_id_as_char IS NOT NULL) EXECUTE ('master.dbo.sp_cleanupwebtask @taskid = ' + @task_id_as_char) END -- Maintenance plan cleanup for SQL 2005. -- If this job came from another server and it runs a subplan of a -- maintenance plan, then delete the subplan record. If that was -- the last subplan still referencing that plan, delete the plan. -- This removes a distributed maintenance plan from a target server -- once all of jobs from the master server that used that maintenance -- plan are deleted. SELECT @maintplan_plan_id = plans.plan_id, @maintplan_subplan_id = plans.subplan_id FROM sysmaintplan_subplans plans, sysjobs_view sjv WHERE plans.job_id = @deleted_job_id AND plans.job_id = sjv.job_id AND sjv.master_server = 1 -- This means the job came from the master IF (@maintplan_subplan_id is not NULL) BEGIN EXECUTE sp_maintplan_delete_subplan @subplan_id = @maintplan_subplan_id, @delete_jobs = 0 IF (NOT EXISTS (SELECT * FROM sysmaintplan_subplans where plan_id = @maintplan_plan_id)) BEGIN DECLARE @plan_name sysname SELECT @plan_name = name FROM sysmaintplan_plans WHERE id = @maintplan_plan_id EXECUTE sp_ssis_deletepackage @name = @plan_name, @folderid = '08aa12d5-8f98-4dab-a4fc-980b150a5dc8' -- this is the guid for 'Maintenance Plans' END END FETCH NEXT FROM sqlagent_notify INTO @deleted_job_id, @job_is_cached END DEALLOCATE sqlagent_notify -- Remove systaskid references (must do this AFTER sp_cleanupwebtask stuff) DELETE FROM msdb.dbo.systaskids WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete) -- Remove sysdbmaintplan_jobs references (legacy maintenance plans prior to SQL 2005) DELETE FROM msdb.dbo.sysdbmaintplan_jobs WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete) -- Finally, clean up any dangling references in sysalerts to the deleted job(s) DECLARE sysalerts_cleanup CURSOR LOCAL FOR SELECT name FROM msdb.dbo.sysalerts WHERE (job_id IN (SELECT job_id FROM #temp_jobs_to_delete)) OPEN sysalerts_cleanup FETCH NEXT FROM sysalerts_cleanup INTO @alert_name WHILE (@@fetch_status = 0) BEGIN EXECUTE msdb.dbo.sp_update_alert @name = @alert_name, @job_id = 0x00 FETCH NEXT FROM sysalerts_cleanup INTO @alert_name END DEALLOCATE sysalerts_cleanup END go /**************************************************************/ /* SP_DELETE_ALL_MSX_JOBS */ /* */ /* NOTE: This is a separate procedure because SQLServerAgent */ /* needs to call it, as does sp_msx_defect and */ /* sp_delete_job. */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_all_msx_jobs...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_all_msx_jobs') AND (type = 'P'))) DROP PROCEDURE sp_delete_all_msx_jobs go CREATE PROCEDURE sp_delete_all_msx_jobs @msx_server sysname, @jobs_deleted INT = NULL OUTPUT AS BEGIN SET NOCOUNT ON -- Change server name to always reflect real servername or servername\instancename IF (UPPER(@msx_server collate SQL_Latin1_General_CP1_CS_AS) = '(LOCAL)') SELECT @msx_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) -- Delete all the jobs that originated from the MSX -- Note: This temp table is referenced by msdb.dbo.sp_delete_job_references CREATE TABLE #temp_jobs_to_delete (job_id UNIQUEIDENTIFIER NOT NULL, job_is_cached INT NOT NULL, owner_sid VARBINARY(85) NOT NULL) -- Table of msx schedules to delete DECLARE @temp_schedules_to_delete TABLE (schedule_id INT NOT NULL) -- Non-sysadmins can only delete jobs they own. sysjobs_view returns all jobs -- for members of SQLAgentReaderRole and SQLAgentOperatorRole, but they should -- not be able to delete those jobs IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)) BEGIN -- NOTE: The left outer-join here is to handle the [unlikely] case of missing sysjobservers rows INSERT INTO #temp_jobs_to_delete SELECT sjv.job_id, CASE sjs.server_id WHEN 0 THEN 1 ELSE 0 END, sjv.owner_sid FROM msdb.dbo.sysjobs_view sjv LEFT OUTER JOIN msdb.dbo.sysjobservers sjs ON (sjv.job_id = sjs.job_id) WHERE (ISNULL(sjs.server_id, 0) = 0) AND (sjv.originating_server = @msx_server) END ELSE BEGIN -- NOTE: The left outer-join here is to handle the [unlikely] case of missing sysjobservers rows INSERT INTO #temp_jobs_to_delete SELECT sjv.job_id, CASE sjs.server_id WHEN 0 THEN 1 ELSE 0 END, sjv.owner_sid FROM msdb.dbo.sysjobs_view sjv LEFT OUTER JOIN msdb.dbo.sysjobservers sjs ON (sjv.job_id = sjs.job_id) WHERE (ISNULL(sjs.server_id, 0) = 0) AND (sjv.originating_server = @msx_server) AND (sjv.owner_sid = SUSER_SID()) END -- Must do this before deleting the job itself since sp_sqlagent_notify does a lookup on sysjobs_view EXECUTE msdb.dbo.sp_delete_job_references BEGIN TRANSACTION --Get the list of schedules to delete, these cant be deleted until the references are deleted in sysjobschedules INSERT INTO @temp_schedules_to_delete SELECT DISTINCT schedule_id FROM msdb.dbo.sysschedules WHERE (schedule_id IN (SELECT schedule_id FROM msdb.dbo.sysjobschedules as js JOIN #temp_jobs_to_delete as tjd ON (js.job_id = tjd.job_id))) DELETE FROM msdb.dbo.sysjobschedules WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete) --Now OK to delete the schedule DELETE FROM msdb.dbo.sysschedules WHERE schedule_id IN (SELECT schedule_id FROM @temp_schedules_to_delete) DELETE FROM msdb.dbo.sysjobservers WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete) DELETE FROM msdb.dbo.sysjobsteps WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete) DELETE FROM msdb.dbo.sysjobs WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete) DELETE FROM msdb.dbo.sysjobhistory WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete) --Finally cleanup any orphaned sysschedules that were downloaded from the MSX DELETE msdb.dbo.sysschedules FROM msdb.dbo.sysschedules s JOIN msdb.dbo.sysoriginatingservers_view os ON (s.originating_server_id = os.originating_server_id) WHERE (os.originating_server = @msx_server) COMMIT TRANSACTION SELECT @jobs_deleted = COUNT(*) FROM #temp_jobs_to_delete DROP TABLE #temp_jobs_to_delete END go /**************************************************************/ /* SP_GENERATE_TARGET_SERVER_JOB_ASSIGNMENT_SQL */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_generate_target_server_job_assignment_sql...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_generate_target_server_job_assignment_sql') AND (type = 'P'))) DROP PROCEDURE sp_generate_target_server_job_assignment_sql go CREATE PROCEDURE sp_generate_target_server_job_assignment_sql @server_name sysname = NULL, @new_server_name sysname = NULL -- Use this if the target server computer has been renamed AS BEGIN SET NOCOUNT ON -- Change server name to always reflect real servername or servername\instancename IF (@server_name IS NULL) OR (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) = '(LOCAL)') SELECT @server_name = CONVERT(sysname, SERVERPROPERTY('ServerName')) IF (@server_name IS NOT NULL) SELECT @server_name = UPPER(@server_name) -- Verify the server name IF (@server_name <> UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) AND (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name))) BEGIN RAISERROR(14262, 16, 1, '@server_name', @server_name) RETURN(1) -- Failure END IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers sjs, msdb.dbo.systargetservers sts WHERE (sjs.server_id = sts.server_id) AND (UPPER(sts.server_name) = @server_name))) BEGIN -- Generate the SQL SELECT 'Execute this SQL to re-assign jobs to the target server' = 'EXECUTE msdb.dbo.sp_add_jobserver @job_id = ''' + CONVERT(VARCHAR(36), sjs.job_id) + ''', @server_name = ''' + ISNULL(@new_server_name, sts.server_name) + '''' FROM msdb.dbo.sysjobservers sjs, msdb.dbo.systargetservers sts WHERE (sjs.server_id = sts.server_id) AND (UPPER(sts.server_name) = @server_name) END ELSE RAISERROR(14548, 10, 1, @server_name) RETURN(0) -- Success END go /**************************************************************/ /* SP_GENERATE_SERVER_DESCRIPTION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_generate_server_description...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_generate_server_description') AND (type = 'P'))) DROP PROCEDURE sp_generate_server_description go CREATE PROCEDURE sp_generate_server_description @description NVARCHAR(100) = NULL OUTPUT, @result_set BIT = 0 AS BEGIN SET NOCOUNT ON DECLARE @xp_results TABLE ( id INT NOT NULL, name NVARCHAR(30) COLLATE database_default NOT NULL, internal_value INT NULL, character_value NVARCHAR(212) COLLATE database_default NULL ) INSERT INTO @xp_results EXECUTE master.dbo.xp_msver UPDATE @xp_results SET character_value = FORMATMESSAGE(14205) WHERE (character_value IS NULL) SELECT @description = (SELECT character_value FROM @xp_results WHERE (id = 1)) + N' ' + (SELECT character_value FROM @xp_results WHERE (id = 2)) + N' / Windows ' + (SELECT character_value FROM @xp_results WHERE (id = 15)) + N' / ' + (SELECT character_value FROM @xp_results WHERE (id = 16)) + N' ' + (SELECT CASE character_value WHEN N'PROCESSOR_INTEL_386' THEN N'386' WHEN N'PROCESSOR_INTEL_486' THEN N'486' WHEN N'PROCESSOR_INTEL_PENTIUM' THEN N'Pentium' WHEN N'PROCESSOR_MIPS_R4000' THEN N'MIPS' WHEN N'PROCESSOR_ALPHA_21064' THEN N'Alpha' ELSE character_value END FROM @xp_results WHERE (id = 18)) + N' CPU(s) / ' + (SELECT CONVERT(NVARCHAR, internal_value) FROM @xp_results WHERE (id = 19)) + N' MB RAM.' IF (@result_set = 1) SELECT @description END go /**************************************************************/ /* SP_MSX_SET_ACCOUNT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_msx_set_account...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_msx_set_account') AND (type = 'P'))) DROP PROCEDURE sp_msx_set_account go CREATE PROCEDURE sp_msx_set_account @credential_name sysname = NULL, @credential_id INT = NULL AS BEGIN DECLARE @retval INT IF @credential_id IS NOT NULL OR @credential_name IS NOT NULL BEGIN EXECUTE @retval = sp_verify_credential_identifiers '@credential_name', '@credential_id', @credential_name OUTPUT, @credential_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure --set credential_id to agent registry EXECUTE master.dbo.xp_instance_regwrite 'HKEY_LOCAL_MACHINE', 'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', 'MSXCredentialID', 'REG_DWORD', @credential_id --set connections to standard EXECUTE master.dbo.xp_instance_regwrite 'HKEY_LOCAL_MACHINE', 'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', 'RegularMSXConnections', 'REG_DWORD', 1 END ELSE BEGIN --just set connection to integrated EXECUTE master.dbo.xp_instance_regwrite 'HKEY_LOCAL_MACHINE', 'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', 'RegularMSXConnections', 'REG_DWORD', 0 END END go /**************************************************************/ /* SP_MSX_GET_ACCOUNT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_msx_get_account...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_msx_get_account') AND (type = 'P'))) DROP PROCEDURE sp_msx_get_account go CREATE PROCEDURE sp_msx_get_account AS BEGIN DECLARE @msx_connection INT DECLARE @credential_id INT SELECT @msx_connection = 0 --integrated connections SELECT @credential_id = NULL EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'RegularMSXConnections', @msx_connection OUTPUT, N'no_output' IF @msx_connection = 1 BEGIN EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXCredentialID', @credential_id OUTPUT, N'no_output' SELECT msx_connection = @msx_connection , msx_credential_id = @credential_id, msx_credential_name = sc.name , msx_login_name = sc.credential_identity FROM master.sys.credentials sc WHERE credential_id = @credential_id END END go /**************************************************************/ /* SP_DELETE_OPERATOR */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_operator...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_operator') AND (type = 'P'))) DROP PROCEDURE sp_delete_operator go CREATE PROCEDURE sp_delete_operator @name sysname, @reassign_to_operator sysname = NULL AS BEGIN DECLARE @id INT DECLARE @alert_fail_safe_operator sysname DECLARE @job_id UNIQUEIDENTIFIER DECLARE @job_id_as_char VARCHAR(36) DECLARE @notify_email_operator_id INT DECLARE @notify_netsend_operator_id INT DECLARE @notify_page_operator_id INT DECLARE @reassign_to_id INT DECLARE @cmd NVARCHAR(1000) DECLARE @current_msx_server sysname DECLARE @reassign_to_escaped NVARCHAR(256) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @reassign_to_operator = LTRIM(RTRIM(@reassign_to_operator)) -- Turn [nullable] empty string parameters into NULLs IF (@reassign_to_operator = N'') SELECT @reassign_to_operator = NULL -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check if this Operator exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (name = @name))) BEGIN RAISERROR(14262, 16, 1, '@name', @name) RETURN(1) -- Failure END -- Check if this operator the FailSafe Operator EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertFailSafeOperator', @alert_fail_safe_operator OUTPUT, N'no_output' -- If it is, we disallow the delete operation IF (LTRIM(RTRIM(@alert_fail_safe_operator)) = @name) BEGIN RAISERROR(14504, 16, 1, @name, @name) RETURN(1) -- Failure END -- Check if this operator is 'MSXOperator' IF (@name = N'MSXOperator') BEGIN DECLARE @server_type VARCHAR(3) -- Disallow the delete operation if we're an MSX or a TSX EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @current_msx_server OUTPUT, N'no_output' IF (@current_msx_server IS NOT NULL) SELECT @server_type = 'TSX' IF ((SELECT COUNT(*) FROM msdb.dbo.systargetservers) > 0) SELECT @server_type = 'MSX' IF (@server_type IS NOT NULL) BEGIN RAISERROR(14223, 16, 1, 'MSXOperator', @server_type) RETURN(1) -- Failure END END -- Convert the Name to it's ID SELECT @id = id FROM msdb.dbo.sysoperators WHERE (name = @name) IF (@reassign_to_operator IS NOT NULL) BEGIN -- On a TSX or standalone server, disallow re-assigning to the MSXOperator IF (@reassign_to_operator = N'MSXOperator') AND (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservers)) BEGIN RAISERROR(14251, -1, -1, @reassign_to_operator) RETURN(1) -- Failure END SELECT @reassign_to_id = id FROM msdb.dbo.sysoperators WHERE (name = @reassign_to_operator) IF (@reassign_to_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@reassign_to_operator', @reassign_to_operator) RETURN(1) -- Failure END END -- Double up any single quotes in @reassign_to_operator IF (@reassign_to_operator IS NOT NULL) SET @reassign_to_escaped = REPLACE(@reassign_to_operator, N'''', N'''''') BEGIN TRANSACTION -- Reassign (or delete) any sysnotifications rows that reference this operator IF (@reassign_to_operator IS NOT NULL) BEGIN UPDATE msdb.dbo.sysnotifications SET operator_id = @reassign_to_id WHERE (operator_id = @id) AND (NOT EXISTS (SELECT * FROM msdb.dbo.sysnotifications sn2 WHERE (sn2.alert_id = msdb.dbo.sysnotifications.alert_id) AND (sn2.operator_id = @reassign_to_id))) END DELETE FROM msdb.dbo.sysnotifications WHERE (operator_id = @id) -- Update any jobs that reference this operator DECLARE jobs_referencing_this_operator CURSOR LOCAL FOR SELECT job_id, notify_email_operator_id, notify_netsend_operator_id, notify_page_operator_id FROM msdb.dbo.sysjobs WHERE (notify_email_operator_id = @id) OR (notify_netsend_operator_id = @id) OR (notify_page_operator_id = @id) OPEN jobs_referencing_this_operator FETCH NEXT FROM jobs_referencing_this_operator INTO @job_id, @notify_email_operator_id, @notify_netsend_operator_id, @notify_page_operator_id WHILE (@@fetch_status = 0) BEGIN SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) SELECT @cmd = N'msdb.dbo.sp_update_job @job_id = ''' + @job_id_as_char + N''', ' IF (@notify_email_operator_id = @id) IF (@reassign_to_operator IS NOT NULL) SELECT @cmd = @cmd + N'@notify_email_operator_name = N''' + @reassign_to_escaped + N''', ' ELSE SELECT @cmd = @cmd + N'@notify_email_operator_name = N'''', @notify_level_email = 0, ' IF (@notify_netsend_operator_id = @id) IF (@reassign_to_operator IS NOT NULL) SELECT @cmd = @cmd + N'@notify_netsend_operator_name = N''' + @reassign_to_escaped + N''', ' ELSE SELECT @cmd = @cmd + N'@notify_netsend_operator_name = N'''', @notify_level_netsend = 0, ' IF (@notify_page_operator_id = @id) IF (@reassign_to_operator IS NOT NULL) SELECT @cmd = @cmd + N'@notify_page_operator_name = N''' + @reassign_to_escaped + N''', ' ELSE SELECT @cmd = @cmd + N'@notify_page_operator_name = N'''', @notify_level_page = 0, ' SELECT @cmd = SUBSTRING(@cmd, 1, (DATALENGTH(@cmd) / 2) - 2) EXECUTE (N'EXECUTE ' + @cmd) FETCH NEXT FROM jobs_referencing_this_operator INTO @job_id, @notify_email_operator_id, @notify_netsend_operator_id, @notify_page_operator_id END DEALLOCATE jobs_referencing_this_operator -- Finally, do the actual DELETE DELETE FROM msdb.dbo.sysoperators WHERE (id = @id) COMMIT TRANSACTION RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_MSX_DEFECT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_msx_defect...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_msx_defect') AND (type = 'P'))) DROP PROCEDURE sp_msx_defect go CREATE PROCEDURE sp_msx_defect @forced_defection BIT = 0 AS BEGIN DECLARE @current_msx_server sysname DECLARE @retval INT DECLARE @jobs_deleted INT DECLARE @polling_interval INT DECLARE @nt_user NVARCHAR(100) SET NOCOUNT ON -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END SELECT @retval = 0 SELECT @jobs_deleted = 0 -- Get the current MSX server name from the registry EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @current_msx_server OUTPUT, N'no_output' SELECT @current_msx_server = UPPER(LTRIM(RTRIM(@current_msx_server))) IF ((@current_msx_server IS NULL) OR (@current_msx_server = N'')) BEGIN RAISERROR(14298, -1, -1) RETURN(1) -- Failure END SELECT @nt_user = ISNULL(NT_CLIENT(), ISNULL(SUSER_SNAME(), FORMATMESSAGE(14205))) EXECUTE @retval = master.dbo.xp_msx_enlist 1, @current_msx_server, @nt_user IF (@retval <> 0) AND (@forced_defection = 0) RETURN(1) -- Failure -- Clear the MSXServerName registry entry EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', N'REG_SZ', N'' -- Delete the MSXPollingInterval registry entry EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXPollInterval', @polling_interval OUTPUT, N'no_output' IF (@polling_interval IS NOT NULL) EXECUTE master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXPollInterval' -- Remove the entry from sqlagent_info DELETE FROM msdb.dbo.sqlagent_info WHERE (attribute = N'DateEnlisted') -- Delete all the jobs that originated from the MSX -- NOTE: We can't use sp_delete_job here since sp_delete_job checks if the caller is -- SQLServerAgent (only SQLServerAgent can delete non-local jobs). EXECUTE msdb.dbo.sp_delete_all_msx_jobs @current_msx_server, @jobs_deleted OUTPUT RAISERROR(14227, 0, 1, @current_msx_server, @jobs_deleted) -- Now delete the old msx server record DELETE msdb.dbo.sysoriginatingservers WHERE (originating_server = @current_msx_server) AND (master_server = 1) -- If a forced defection was performed, attempt to notify the MSXOperator IF (@forced_defection = 1) BEGIN DECLARE @network_address NVARCHAR(100) DECLARE @command NVARCHAR(512) DECLARE @local_machine_name sysname DECLARE @res_warning NVARCHAR(300) SELECT @network_address = netsend_address FROM msdb.dbo.sysoperators WHERE (name = N'MSXOperator') IF (@network_address IS NOT NULL) BEGIN EXECUTE @retval = master.dbo.xp_getnetname @local_machine_name OUTPUT IF (@retval <> 0) RETURN(1) -- Failure SELECT @res_warning = FORMATMESSAGE(14217) SELECT @command = N'NET SEND ' + @network_address + N' ' + @res_warning SELECT @command = STUFF(@command, PATINDEX(N'%[%%]s%', @command), 2, NT_CLIENT()) SELECT @command = STUFF(@command, PATINDEX(N'%[%%]s%', @command), 2, @local_machine_name) EXECUTE master.dbo.xp_cmdshell @command, no_output END END -- Delete the 'MSXOperator' (must do this last) IF (EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (name = N'MSXOperator'))) EXECUTE msdb.dbo.sp_delete_operator @name = N'MSXOperator' RETURN(0) -- 0 means success END go /**************************************************************/ /* SP_MSX_ENLIST */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_msx_enlist...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_msx_enlist') AND (type = 'P'))) DROP PROCEDURE sp_msx_enlist go CREATE PROCEDURE sp_msx_enlist @msx_server_name sysname, @location NVARCHAR(100) = NULL -- The procedure will supply a default AS BEGIN DECLARE @current_msx_server sysname DECLARE @local_machine_name sysname DECLARE @msx_originating_server sysname DECLARE @retval INT DECLARE @time_zone_adjustment INT DECLARE @local_time NVARCHAR(100) DECLARE @nt_user NVARCHAR(100) DECLARE @poll_interval INT SET NOCOUNT ON -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Only an NT server can be enlisted IF ((PLATFORM() & 0x1) <> 0x1) -- NT BEGIN RAISERROR(14540, -1, 1) RETURN(1) -- Failure END -- Only SBS, Standard, or Enterprise editions of SQL Server can be enlisted IF ((PLATFORM() & 0x100) = 0x100) -- Desktop package BEGIN RAISERROR(14539, -1, -1) RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SELECT @msx_server_name = UPPER(LTRIM(RTRIM(@msx_server_name))) SELECT @location = LTRIM(RTRIM(@location)) SELECT @local_machine_name = UPPER(CONVERT(NVARCHAR(30), SERVERPROPERTY('ServerName'))) -- Turn [nullable] empty string parameters into NULLs IF (@location = N'') SELECT @location = NULL SELECT @retval = 0 -- Get the values that we'll need for the [re]enlistment operation (except the local time -- which we get right before we call xp_msx_enlist to that it's as accurate as possible) SELECT @nt_user = ISNULL(NT_CLIENT(), ISNULL(SUSER_SNAME(), FORMATMESSAGE(14205))) EXECUTE master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SYSTEM\CurrentControlSet\Control\TimeZoneInformation', N'Bias', @time_zone_adjustment OUTPUT, N'no_output' IF ((PLATFORM() & 0x1) = 0x1) -- NT SELECT @time_zone_adjustment = -ISNULL(@time_zone_adjustment, 0) ELSE SELECT @time_zone_adjustment = -CONVERT(INT, CONVERT(BINARY(2), ISNULL(@time_zone_adjustment, 0))) EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXPollInterval', @poll_interval OUTPUT, N'no_output' SELECT @poll_interval = ISNULL(@poll_interval, 60) -- This should be the same as DEF_REG_MSX_POLL_INTERVAL EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @current_msx_server OUTPUT, N'no_output' SELECT @current_msx_server = LTRIM(RTRIM(@current_msx_server)) -- Check if this machine is an MSX (and therefore cannot be enlisted into another MSX) IF (EXISTS (SELECT * FROM msdb.dbo.systargetservers)) BEGIN --Get local server/instance name RAISERROR(14299, -1, -1, @local_machine_name) RETURN(1) -- Failure END -- Check if the MSX supplied is the same as the local machine (this is not allowed) IF (UPPER(@local_machine_name) = @msx_server_name) BEGIN RAISERROR(14297, -1, -1) RETURN(1) -- Failure END -- Check if MSDB has be re-installed since we enlisted IF (@current_msx_server IS NOT NULL) AND (NOT EXISTS (SELECT * FROM msdb.dbo.sqlagent_info WHERE (attribute = 'DateEnlisted'))) BEGIN -- User is tring to [re]enlist after a re-install, so we have to forcefully defect before -- we can fully enlist again EXECUTE msdb.dbo.sp_msx_defect @forced_defection = 1 SELECT @current_msx_server = NULL END -- Check if we are already enlisted, in which case we re-enlist IF ((@current_msx_server IS NOT NULL) AND (@current_msx_server <> N'')) BEGIN IF (UPPER(@current_msx_server) = @msx_server_name) BEGIN -- Update the [existing] enlistment SELECT @local_time = CONVERT(NVARCHAR, GETDATE(), 112) + N' ' + CONVERT(NVARCHAR, GETDATE(), 108) EXECUTE @retval = master.dbo.xp_msx_enlist 2, @msx_server_name, @nt_user, @location, @time_zone_adjustment, @local_time, @poll_interval RETURN(@retval) -- 0 means success END ELSE BEGIN RAISERROR(14296, -1, -1, @current_msx_server) RETURN(1) -- Failure END END -- If we get this far then we're dealing with a new enlistment... -- If no location is supplied, generate one (such as we can) IF (@location IS NULL) EXECUTE msdb.dbo.sp_generate_server_description @location OUTPUT SELECT @local_time = CONVERT(NVARCHAR, GETDATE(), 112) + ' ' + CONVERT(NVARCHAR, GETDATE(), 108) EXECUTE @retval = master.dbo.xp_msx_enlist 0, @msx_server_name, @nt_user, @location, @time_zone_adjustment, @local_time, @poll_interval IF (@retval = 0) BEGIN EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', N'REG_SZ', @msx_server_name IF (@current_msx_server IS NOT NULL) RAISERROR(14228, 0, 1, @current_msx_server, @msx_server_name) ELSE RAISERROR(14229, 0, 1, @msx_server_name) -- Update the sysoriginatingservers table with the msx server name. May need to clean up if it already has an msx entry SELECT @msx_originating_server = NULL -- Get the msx server name SELECT @msx_originating_server = originating_server FROM msdb.dbo.sysoriginatingservers WHERE (master_server = 1) IF(@msx_originating_server IS NULL) BEGIN -- Good. No msx server found so just add the new one INSERT INTO msdb.dbo.sysoriginatingservers(originating_server, master_server) VALUES (@msx_server_name, 1) END ELSE BEGIN -- Found a previous entry. If it isn't the same server we need to clean up any existing msx jobs IF(@msx_originating_server != @msx_server_name) BEGIN INSERT INTO msdb.dbo.sysoriginatingservers(originating_server, master_server) VALUES (@msx_server_name, 1) -- Optimistically try and remove any msx jobs left over from the previous msx enlistment. EXECUTE msdb.dbo.sp_delete_all_msx_jobs @msx_originating_server -- And finally delete the old msx server record DELETE msdb.dbo.sysoriginatingservers WHERE (originating_server = @msx_originating_server) AND (master_server = 1) END END -- Add entry to sqlagent_info INSERT INTO msdb.dbo.sqlagent_info (attribute, value) VALUES ('DateEnlisted', CONVERT(VARCHAR(10), GETDATE(), 112)) END RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_DELETE_TARGETSERVER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_targetserver...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_targetserver') AND (type = 'P'))) DROP PROCEDURE sp_delete_targetserver go CREATE PROCEDURE sp_delete_targetserver @server_name sysname, @clear_downloadlist BIT = 1, @post_defection BIT = 1 AS BEGIN DECLARE @server_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name))) -- Check server name SELECT @server_id = server_id FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name) IF (@server_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END BEGIN TRANSACTION IF (@clear_downloadlist = 1) BEGIN DELETE FROM msdb.dbo.sysdownloadlist WHERE (target_server = @server_name) END IF (@post_defection = 1) BEGIN -- Post a defect instruction to the server -- NOTE: We must do this BEFORE deleting the systargetservers row EXECUTE msdb.dbo.sp_post_msx_operation 'DEFECT', 'SERVER', 0x00, @server_name END DELETE FROM msdb.dbo.systargetservers WHERE (server_id = @server_id) DELETE FROM msdb.dbo.systargetservergroupmembers WHERE (server_id = @server_id) DELETE FROM msdb.dbo.sysjobservers WHERE (server_id = @server_id) COMMIT TRANSACTION RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_ENLIST_TSX */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enlist_tsx' go IF EXISTS (SELECT name FROM sysobjects WHERE name = 'sp_enlist_tsx' AND type = 'P') DROP PROCEDURE sp_enlist_tsx GO create proc sp_enlist_tsx @Action int, -- 0 - enlist; 1 - defect; 2 - update @ServerName sysname, -- tsx server name @Location nvarchar(200), -- tsx server location @TimeZoneAdjustment int, -- tsx server time zone adjustment @LocalTime datetime, -- tsx server local time @NTUserName nvarchar(100), -- name of the user performing the enlistment @PollInterval int, -- polling interval @TSX_Version int = 0 -- VersionMajor: ((@TSX_Version / 0x1000000) & 0xff) -- VersionMinor: ((@TSX_Version / 0x10000) & 0xff) -- Build no: (@TSX_Version & 0xFFFF) as begin SET NOCOUNT ON /* check permissions */ IF (ISNULL(IS_MEMBER(N'TargetServersRole'), 0) = 0) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) begin raiserror(15003,-1,-1, N'TargetServersRole') return 1 end --9.0 and above servers set this version param if(@TSX_Version is null) set @TSX_Version = 0 --Only check version during enlistment if(@Action = 0 AND ((@TSX_Version / 0x1000000) & 0xff) < 9) begin DECLARE @majorVer int, @minorVer int, @buildNo int SELECT @majorVer = ((@@microsoftversion / 0x1000000) & 0xff), @minorVer = ((@@microsoftversion / 0x10000) & 0xff), @buildNo = (@@microsoftversion & 0xfff) raiserror(14306, -1, -1, @majorVer, @minorVer, @buildNo ) return 12 end /* check input parameters */ if @ServerName is null begin raiserror(14043, -1, -1, '@ServerName') return 2 end select @ServerName = LTRIM(@ServerName) select @ServerName = RTRIM(@ServerName) if @ServerName = '' begin raiserror(21263, -1, -1, '@ServerName') return 3 end select @ServerName = UPPER(@ServerName) if @Action <> 1 And @Action <> 2 begin /* default action is to enlist */ select @Action = 0 end if @Action = 0 /* enlisting */ begin /* check input parameters */ if @NTUserName is null begin raiserror(14043, -1, -1, '@NTUserName') return 4 end select @NTUserName = LTRIM(@NTUserName) select @NTUserName = RTRIM(@NTUserName) if @NTUserName = '' begin raiserror(21263, -1, -1, '@NTUserName') return 5 end /* check if local server is already configured as TSX machine */ declare @msx_server_name sysname select @msx_server_name = N'' execute master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @msx_server_name OUTPUT select @msx_server_name = LTRIM(@msx_server_name) select @msx_server_name = RTRIM(@msx_server_name) if @msx_server_name <> N'' begin raiserror(14360, -1, -1, @@SERVERNAME) return 6 end /* * check that local server is not running a desktop SKU, * i.e. Win9x, Office, or MSDE */ if( PLATFORM() & 0x100 = 0x100 ) begin raiserror(14362, -1, -1) return 8 end /* check if we have any MSXOperators defined */ if not exists (SELECT * FROM msdb.dbo.sysoperators WHERE name = N'MSXOperator') begin raiserror(14363, -1, -1) return 9 end /* all checks have passed, insert new row into systargetservers table */ INSERT INTO msdb.dbo.systargetservers ( server_name, location, time_zone_adjustment, enlist_date, last_poll_date, status, local_time_at_last_poll, enlisted_by_nt_user, poll_interval ) VALUES ( @ServerName, @Location, @TimeZoneAdjustment, GETDATE(), GETDATE(), 1, @LocalTime, @NTUserName, @PollInterval ) /* delete hanging rows from sysdownloadlist */ DELETE FROM msdb.dbo.sysdownloadlist WHERE target_server = @ServerName end if @Action = 2 /* updating existing enlistment */ begin /* check if we have any MSXOperators defined */ if not exists (SELECT * FROM msdb.dbo.sysoperators WHERE name = N'MSXOperator') begin raiserror(14363, -1, -1) return 10 end /* check if TSX machine is already enlisted */ If not exists (SELECT * FROM msdb.dbo.systargetservers WHERE UPPER(server_name) = @ServerName) begin raiserror(14364, -1, -1) return 11 end if @Location is null /* don't update the location if it is not supplied */ begin UPDATE msdb.dbo.systargetservers SET time_zone_adjustment = @TimeZoneAdjustment, poll_interval = @PollInterval WHERE (UPPER(server_name) = @ServerName) end else begin UPDATE msdb.dbo.systargetservers SET location = @Location, time_zone_adjustment = @TimeZoneAdjustment, poll_interval = @PollInterval WHERE (UPPER(server_name) = @ServerName) end end if @Action = 1 /* defecting */ begin if (exists (SELECT * FROM msdb.dbo.systargetservers WHERE UPPER(server_name) = @ServerName)) begin execute msdb.dbo.sp_delete_targetserver @server_name = @ServerName, @post_defection = 0 end else begin DELETE FROM msdb.dbo.sysdownloadlist WHERE (target_server = @ServerName) end end if @Action = 0 Or @Action = 2 /* enlisting or updating existing enlistment */ begin /* select resultset to return to the caller */ SELECT id, name, enabled, email_address, pager_address, netsend_address, weekday_pager_start_time, weekday_pager_end_time, saturday_pager_start_time, saturday_pager_end_time, sunday_pager_start_time, sunday_pager_end_time, pager_days FROM msdb.dbo.sysoperators WHERE (name = N'MSXOperator') end end go /**************************************************************/ /* SP_GET_SQLAGENT_PROPERTIES */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_sqlagent_properties...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_get_sqlagent_properties') AND (type = 'P'))) DROP PROCEDURE sp_get_sqlagent_properties go CREATE PROCEDURE sp_get_sqlagent_properties AS BEGIN DECLARE @auto_start INT DECLARE @startup_account NVARCHAR(100) DECLARE @msx_server_name sysname -- Non-SQLDMO exposed properties DECLARE @sqlserver_restart INT DECLARE @jobhistory_max_rows INT DECLARE @jobhistory_max_rows_per_job INT DECLARE @errorlog_file NVARCHAR(255) DECLARE @errorlogging_level INT DECLARE @error_recipient NVARCHAR(30) DECLARE @monitor_autostart INT DECLARE @local_host_server sysname DECLARE @job_shutdown_timeout INT DECLARE @cmdexec_account VARBINARY(64) DECLARE @regular_connections INT DECLARE @host_login_name sysname DECLARE @host_login_password VARBINARY(512) DECLARE @login_timeout INT DECLARE @idle_cpu_percent INT DECLARE @idle_cpu_duration INT DECLARE @oem_errorlog INT DECLARE @email_profile NVARCHAR(64) DECLARE @email_save_in_sent_folder INT DECLARE @cpu_poller_enabled INT DECLARE @alert_replace_runtime_tokens INT SET NOCOUNT ON -- NOTE: We return all SQLServerAgent properties at one go for performance reasons -- Read the values from the registry IF ((PLATFORM() & 0x1) = 0x1) -- NT BEGIN DECLARE @key NVARCHAR(200) SELECT @key = N'SYSTEM\CurrentControlSet\Services\' IF (SERVERPROPERTY('INSTANCENAME') IS NOT NULL) SELECT @key = @key + N'SQLAgent$' + CONVERT (sysname, SERVERPROPERTY('INSTANCENAME')) ELSE SELECT @key = @key + N'SQLServerAgent' EXECUTE master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', @key, N'Start', @auto_start OUTPUT, N'no_output' EXECUTE master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', @key, N'ObjectName', @startup_account OUTPUT, N'no_output' END ELSE BEGIN SELECT @auto_start = 3 -- Manual start SELECT @startup_account = NULL END EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @msx_server_name OUTPUT, N'no_output' -- Non-SQLDMO exposed properties EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'RestartSQLServer', @sqlserver_restart OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRows', @jobhistory_max_rows OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRowsPerJob', @jobhistory_max_rows_per_job OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ErrorLogFile', @errorlog_file OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ErrorLoggingLevel', @errorlogging_level OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ErrorMonitor', @error_recipient OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MonitorAutoStart', @monitor_autostart OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ServerHost', @local_host_server OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobShutdownTimeout', @job_shutdown_timeout OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'CmdExecAccount', @cmdexec_account OUTPUT, N'no_output' SET @regular_connections = 0 SET @host_login_name = NULL SET @host_login_password = NULL EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'LoginTimeout', @login_timeout OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'IdleCPUPercent', @idle_cpu_percent OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'IdleCPUDuration', @idle_cpu_duration OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'OemErrorLog', @oem_errorlog OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'EmailProfile', @email_profile OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'EmailSaveSent', @email_save_in_sent_folder OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertReplaceRuntimeTokens', @alert_replace_runtime_tokens OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'CoreEngineMask', @cpu_poller_enabled OUTPUT, N'no_output' IF (@cpu_poller_enabled IS NOT NULL) SELECT @cpu_poller_enabled = CASE WHEN (@cpu_poller_enabled & 32) = 32 THEN 0 ELSE 1 END -- Return the values to the client SELECT auto_start = CASE @auto_start WHEN 2 THEN 1 -- 2 means auto-start WHEN 3 THEN 0 -- 3 means don't auto-start ELSE 0 -- Safety net END, msx_server_name = @msx_server_name, sqlagent_type = (SELECT CASE WHEN (COUNT(*) = 0) AND (ISNULL(DATALENGTH(@msx_server_name), 0) = 0) THEN 1 -- Standalone WHEN (COUNT(*) = 0) AND (ISNULL(DATALENGTH(@msx_server_name), 0) > 0) THEN 2 -- TSX WHEN (COUNT(*) > 0) AND (ISNULL(DATALENGTH(@msx_server_name), 0) = 0) THEN 3 -- MSX WHEN (COUNT(*) > 0) AND (ISNULL(DATALENGTH(@msx_server_name), 0) > 0) THEN 0 -- Multi-Level MSX (currently invalid) ELSE 0 -- Invalid END FROM msdb.dbo.systargetservers), startup_account = @startup_account, -- Non-SQLDMO exposed properties sqlserver_restart = ISNULL(@sqlserver_restart, 1), jobhistory_max_rows = @jobhistory_max_rows, jobhistory_max_rows_per_job = @jobhistory_max_rows_per_job, errorlog_file = @errorlog_file, errorlogging_level = ISNULL(@errorlogging_level, 7), error_recipient = @error_recipient, monitor_autostart = ISNULL(@monitor_autostart, 0), local_host_server = @local_host_server, job_shutdown_timeout = ISNULL(@job_shutdown_timeout, 15), cmdexec_account = @cmdexec_account, regular_connections = ISNULL(@regular_connections, 0), host_login_name = @host_login_name, host_login_password = @host_login_password, login_timeout = ISNULL(@login_timeout, 30), idle_cpu_percent = ISNULL(@idle_cpu_percent, 10), idle_cpu_duration = ISNULL(@idle_cpu_duration, 600), oem_errorlog = ISNULL(@oem_errorlog, 0), sysadmin_only = NULL, email_profile = @email_profile, email_save_in_sent_folder = ISNULL(@email_save_in_sent_folder, 0), cpu_poller_enabled = ISNULL(@cpu_poller_enabled, 0), alert_replace_runtime_tokens = ISNULL(@alert_replace_runtime_tokens, 0) END go /**************************************************************/ /* SP_SET_SQLAGENT_PROPERTIES */ /**************************************************************/ IF EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE name = N'sp_set_sqlagent_properties' AND type = 'P') BEGIN DROP PROCEDURE dbo.sp_set_sqlagent_properties END go PRINT '' PRINT 'Create procedure sp_set_sqlagent_properties...' go CREATE PROCEDURE dbo.sp_set_sqlagent_properties @auto_start INT = NULL, -- 1 or 0 -- Non-SQLDMO exposed properties @sqlserver_restart INT = NULL, -- 1 or 0 @jobhistory_max_rows INT = NULL, -- No maximum = -1, otherwise must be > 1 @jobhistory_max_rows_per_job INT = NULL, -- 1 to @jobhistory_max_rows @errorlog_file NVARCHAR(255) = NULL, -- Full drive\path\name of errorlog file @errorlogging_level INT = NULL, -- 1 = error, 2 = warning, 4 = information @error_recipient NVARCHAR(30) = NULL, -- Network address of error popup recipient @monitor_autostart INT = NULL, -- 1 or 0 @local_host_server sysname = NULL, -- Alias of local host server @job_shutdown_timeout INT = NULL, -- 5 to 600 seconds @cmdexec_account VARBINARY(64) = NULL, -- CmdExec account information @regular_connections INT = NULL, -- obsolete @host_login_name sysname = NULL, -- obsolete @host_login_password VARBINARY(512) = NULL, -- obsolete @login_timeout INT = NULL, -- 5 to 45 (seconds) @idle_cpu_percent INT = NULL, -- 1 to 100 @idle_cpu_duration INT = NULL, -- 20 to 86400 seconds @oem_errorlog INT = NULL, -- 1 or 0 @sysadmin_only INT = NULL, -- not applicable to Yukon server, for backwards compatibility only @email_profile NVARCHAR(64) = NULL, -- Email profile name @email_save_in_sent_folder INT = NULL, -- 1 or 0 @cpu_poller_enabled INT = NULL, -- 1 or 0 @alert_replace_runtime_tokens INT = NULL -- 1 or 0 AS BEGIN -- NOTE: We set all SQLServerAgent properties at one go for performance reasons. -- NOTE: You cannot set the value of the properties msx_server_name, is_msx or -- startup_account - they are all read only. DECLARE @res_valid_range NVARCHAR(100) DECLARE @existing_core_engine_mask INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @errorlog_file = LTRIM(RTRIM(@errorlog_file)) SELECT @error_recipient = LTRIM(RTRIM(@error_recipient)) SELECT @local_host_server = LTRIM(RTRIM(@local_host_server)) SELECT @host_login_name = LTRIM(RTRIM(@host_login_name)) SELECT @email_profile = LTRIM(RTRIM(@email_profile)) -- Make sure values (if supplied) are good IF (@auto_start IS NOT NULL) BEGIN -- NOTE: When setting the the services start value, 2 == auto-start, 3 == Don't auto-start SELECT @auto_start = CASE @auto_start WHEN 0 THEN 3 WHEN 1 THEN 2 ELSE 3 -- Assume non auto-start if passed a junk value END END -- Non-SQLDMO exposed properties IF ((@sqlserver_restart IS NOT NULL) AND (@sqlserver_restart <> 0)) SELECT @sqlserver_restart = 1 IF (@jobhistory_max_rows IS NOT NULL) BEGIN SELECT @res_valid_range = FORMATMESSAGE(14207) IF ((@jobhistory_max_rows < -1) OR (@jobhistory_max_rows = 0)) BEGIN RAISERROR(14266, -1, -1, '@jobhistory_max_rows', @res_valid_range) RETURN(1) -- Failure END END ELSE BEGIN EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRows', @jobhistory_max_rows OUTPUT, N'no_output' SELECT @jobhistory_max_rows = ISNULL(@jobhistory_max_rows, -1) END IF (@jobhistory_max_rows_per_job IS NOT NULL) BEGIN IF (@jobhistory_max_rows = -1) SELECT @jobhistory_max_rows_per_job = 0 ELSE BEGIN IF ((@jobhistory_max_rows_per_job < 1) OR (@jobhistory_max_rows_per_job > @jobhistory_max_rows)) BEGIN SELECT @res_valid_range = N'1..' + CONVERT(NVARCHAR, @jobhistory_max_rows) RAISERROR(14266, -1, -1, '@jobhistory_max_rows', @res_valid_range) RETURN(1) -- Failure END END END IF (@errorlogging_level IS NOT NULL) AND ((@errorlogging_level < 1) OR (@errorlogging_level > 7)) BEGIN RAISERROR(14266, -1, -1, '@errorlogging_level', '1..7') RETURN(1) -- Failure END IF (@monitor_autostart IS NOT NULL) AND ((@monitor_autostart < 0) OR (@monitor_autostart > 1)) BEGIN RAISERROR(14266, -1, -1, '@monitor_autostart', '0, 1') RETURN(1) -- Failure END IF (@job_shutdown_timeout IS NOT NULL) AND ((@job_shutdown_timeout < 5) OR (@job_shutdown_timeout > 600)) BEGIN RAISERROR(14266, -1, -1, '@job_shutdown_timeout', '5..600') RETURN(1) -- Failure END IF (@login_timeout IS NOT NULL) AND ((@login_timeout < 5) OR (@login_timeout > 45)) BEGIN RAISERROR(14266, -1, -1, '@login_timeout', '5..45') RETURN(1) -- Failure END IF ((@idle_cpu_percent IS NOT NULL) AND ((@idle_cpu_percent < 1) OR (@idle_cpu_percent > 100))) BEGIN RAISERROR(14266, -1, -1, '@idle_cpu_percent', '10..100') RETURN(1) -- Failure END IF ((@idle_cpu_duration IS NOT NULL) AND ((@idle_cpu_duration < 20) OR (@idle_cpu_duration > 86400))) BEGIN RAISERROR(14266, -1, -1, '@idle_cpu_duration', '20..86400') RETURN(1) -- Failure END IF (@oem_errorlog IS NOT NULL) AND ((@oem_errorlog < 0) OR (@oem_errorlog > 1)) BEGIN RAISERROR(14266, -1, -1, '@oem_errorlog', '0, 1') RETURN(1) -- Failure END IF (@sysadmin_only IS NOT NULL) BEGIN RAISERROR(14378, -1, -1) RETURN(1) -- Failure END IF (@email_save_in_sent_folder IS NOT NULL) AND ((@email_save_in_sent_folder < 0) OR (@email_save_in_sent_folder > 1)) BEGIN RAISERROR(14266, -1, -1, 'email_save_in_sent_folder', '0, 1') RETURN(1) -- Failure END IF (@cpu_poller_enabled IS NOT NULL) AND ((@cpu_poller_enabled < 0) OR (@cpu_poller_enabled > 1)) BEGIN RAISERROR(14266, -1, -1, 'cpu_poller_enabled', '0, 1') RETURN(1) -- Failure END IF (@alert_replace_runtime_tokens IS NOT NULL) AND ((@alert_replace_runtime_tokens < 0) OR (@alert_replace_runtime_tokens > 1)) BEGIN RAISERROR(14266, -1, -1, 'alert_replace_runtime_tokens', '0, 1') RETURN(1) -- Failure END -- Write out the values IF (@auto_start IS NOT NULL) BEGIN IF ((PLATFORM() & 0x1) = 0x1) -- NT BEGIN DECLARE @key NVARCHAR(200) SELECT @key = N'SYSTEM\CurrentControlSet\Services\' IF (SERVERPROPERTY('INSTANCENAME') IS NOT NULL) SELECT @key = @key + N'SQLAgent$' + CONVERT (sysname, SERVERPROPERTY('INSTANCENAME')) ELSE SELECT @key = @key + N'SQLServerAgent' EXECUTE master.dbo.xp_regwrite N'HKEY_LOCAL_MACHINE', @key, N'Start', N'REG_DWORD', @auto_start END ELSE RAISERROR(14546, 16, 1, '@auto_start') END -- Non-SQLDMO exposed properties IF (@sqlserver_restart IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'RestartSQLServer', N'REG_DWORD', @sqlserver_restart IF (@jobhistory_max_rows IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRows', N'REG_DWORD', @jobhistory_max_rows IF (@jobhistory_max_rows_per_job IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRowsPerJob', N'REG_DWORD', @jobhistory_max_rows_per_job IF (@errorlog_file IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ErrorLogFile', N'REG_SZ', @errorlog_file IF (@errorlogging_level IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ErrorLoggingLevel', N'REG_DWORD', @errorlogging_level IF (@error_recipient IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ErrorMonitor', N'REG_SZ', @error_recipient IF (@monitor_autostart IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MonitorAutoStart', N'REG_DWORD', @monitor_autostart IF (@local_host_server IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ServerHost', N'REG_SZ', @local_host_server IF (@job_shutdown_timeout IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobShutdownTimeout', N'REG_DWORD', @job_shutdown_timeout IF (@cmdexec_account IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'CmdExecAccount', N'REG_BINARY', @cmdexec_account IF (@login_timeout IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'LoginTimeout', N'REG_DWORD', @login_timeout IF (@idle_cpu_percent IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'IdleCPUPercent', N'REG_DWORD', @idle_cpu_percent IF (@idle_cpu_duration IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'IdleCPUDuration', N'REG_DWORD', @idle_cpu_duration IF (@oem_errorlog IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'OemErrorLog', N'REG_DWORD', @oem_errorlog IF (@email_profile IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'EmailProfile', N'REG_SZ', @email_profile IF (@email_save_in_sent_folder IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'EmailSaveSent', N'REG_DWORD', @email_save_in_sent_folder IF (@alert_replace_runtime_tokens IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertReplaceRuntimeTokens', N'REG_DWORD', @alert_replace_runtime_tokens IF (@cpu_poller_enabled IS NOT NULL) BEGIN EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'CoreEngineMask', @existing_core_engine_mask OUTPUT, N'no_output' IF ((@existing_core_engine_mask IS NOT NULL) OR (@cpu_poller_enabled = 1)) BEGIN IF (@cpu_poller_enabled = 1) SELECT @cpu_poller_enabled = (ISNULL(@existing_core_engine_mask, 0) & ~32) ELSE SELECT @cpu_poller_enabled = (ISNULL(@existing_core_engine_mask, 0) | 32) IF ((@existing_core_engine_mask IS NOT NULL) AND (@cpu_poller_enabled = 32)) EXECUTE master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'CoreEngineMask' ELSE EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'CoreEngineMask', N'REG_DWORD', @cpu_poller_enabled END END RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_TARGETSERVERGROUP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_targetservergroup...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_targetservergroup') AND (type = 'P'))) DROP PROCEDURE sp_add_targetservergroup go CREATE PROCEDURE sp_add_targetservergroup @name sysname AS BEGIN SET NOCOUNT ON -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) -- Check if the group already exists IF (EXISTS (SELECT * FROM msdb.dbo.systargetservergroups WHERE name = @name)) BEGIN RAISERROR(14261, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Disallow names with commas in them (since sp_apply_job_to_targets parses a comma-separated list of group names) IF (@name LIKE N'%,%') BEGIN RAISERROR(14289, -1, -1, '@name', ',') RETURN(1) -- Failure END INSERT INTO msdb.dbo.systargetservergroups (name) VALUES (@name) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_UPDATE_TARGETSERVERGROUP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_targetservergroup...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_targetservergroup') AND (type = 'P'))) DROP PROCEDURE sp_update_targetservergroup go CREATE PROCEDURE sp_update_targetservergroup @name sysname, @new_name sysname AS BEGIN SET NOCOUNT ON -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @new_name = LTRIM(RTRIM(@new_name)) -- Check if the group exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservergroups WHERE (name = @name))) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Check if a group with the new name already exists IF (EXISTS (SELECT * FROM msdb.dbo.systargetservergroups WHERE (name = @new_name))) BEGIN RAISERROR(14261, -1, -1, '@new_name', @new_name) RETURN(1) -- Failure END -- Disallow names with commas in them (since sp_apply_job_to_targets parses a comma-separated list of group names) IF (@new_name LIKE N'%,%') BEGIN RAISERROR(14289, -1, -1, '@new_name', ',') RETURN(1) -- Failure END -- Update the group's name UPDATE msdb.dbo.systargetservergroups SET name = @new_name WHERE (name = @name) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_DELETE_TARGETSERVERGROUP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_targetservergroup...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_targetservergroup') AND (type = 'P'))) DROP PROCEDURE sp_delete_targetservergroup go CREATE PROCEDURE sp_delete_targetservergroup @name sysname AS BEGIN DECLARE @servergroup_id INT SET NOCOUNT ON -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) -- Check if the group exists SELECT @servergroup_id = servergroup_id FROM msdb.dbo.systargetservergroups WHERE (name = @name) IF (@servergroup_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Remove the group members DELETE FROM msdb.dbo.systargetservergroupmembers WHERE (servergroup_id = @servergroup_id) -- Remove the group DELETE FROM msdb.dbo.systargetservergroups WHERE (name = @name) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_HELP_TARGETSERVERGROUP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_targetservergroup...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_targetservergroup') AND (type = 'P'))) DROP PROCEDURE sp_help_targetservergroup go CREATE PROCEDURE sp_help_targetservergroup @name sysname = NULL AS BEGIN DECLARE @servergroup_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) IF (@name IS NULL) BEGIN -- Show all groups SELECT servergroup_id, name FROM msdb.dbo.systargetservergroups RETURN(@@error) -- 0 means success END ELSE BEGIN -- Check if the group exists SELECT @servergroup_id = servergroup_id FROM msdb.dbo.systargetservergroups WHERE (name = @name) IF (@servergroup_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Return the members of the group SELECT sts.server_id, sts.server_name FROM msdb.dbo.systargetservers sts, msdb.dbo.systargetservergroupmembers stsgm WHERE (stsgm.servergroup_id = @servergroup_id) AND (stsgm.server_id = sts.server_id) RETURN(@@error) -- 0 means success END END go /**************************************************************/ /* SP_ADD_TARGETSVRGRP_MEMBER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_targetsvgrp_member...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_targetsvrgrp_member') AND (type = 'P'))) DROP PROCEDURE sp_add_targetsvrgrp_member go CREATE PROCEDURE sp_add_targetsvrgrp_member @group_name sysname, @server_name sysname AS BEGIN DECLARE @servergroup_id INT DECLARE @server_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @group_name = LTRIM(RTRIM(@group_name)) SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name))) -- Check if the group exists SELECT @servergroup_id = servergroup_id FROM msdb.dbo.systargetservergroups WHERE (name = @group_name) IF (@servergroup_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@group_name', @group_name) RETURN(1) -- Failure END -- Check if the server exists SELECT @server_id = server_id FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name) IF (@server_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END -- Check if the server is already in this group IF (EXISTS (SELECT * FROM msdb.dbo.systargetservergroupmembers WHERE (servergroup_id = @servergroup_id) AND (server_id = @server_id))) BEGIN RAISERROR(14263, -1, -1, @server_name, @group_name) RETURN(1) -- Failure END -- Add the row to systargetservergroupmembers INSERT INTO msdb.dbo.systargetservergroupmembers VALUES (@servergroup_id, @server_id) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_DELETE_TARGETSVRGRP_MEMBER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_targetsvrgrp_member...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_targetsvrgrp_member') AND (type = 'P'))) DROP PROCEDURE sp_delete_targetsvrgrp_member go CREATE PROCEDURE sp_delete_targetsvrgrp_member @group_name sysname, @server_name sysname AS BEGIN DECLARE @servergroup_id INT DECLARE @server_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @group_name = LTRIM(RTRIM(@group_name)) SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name))) -- Check if the group exists SELECT @servergroup_id = servergroup_id FROM msdb.dbo.systargetservergroups WHERE (name = @group_name) IF (@servergroup_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@group_name', @group_name) RETURN(1) -- Failure END -- Check if the server exists SELECT @server_id = server_id FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name) IF (@server_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END -- Check if the server is in the group IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservergroupmembers WHERE (servergroup_id = @servergroup_id) AND (server_id = @server_id))) BEGIN RAISERROR(14264, -1, -1, @server_name, @group_name) RETURN(1) -- Failure END -- Delete the row from systargetservergroupmembers DELETE FROM msdb.dbo.systargetservergroupmembers WHERE (servergroup_id = @servergroup_id) AND (server_id = @server_id) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_VERIFY_CATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_category...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_category') AND (type = 'P'))) DROP PROCEDURE sp_verify_category go CREATE PROCEDURE sp_verify_category @class VARCHAR(8), @type VARCHAR(12) = NULL, -- Supply NULL only if you don't want it checked @name sysname = NULL, -- Supply NULL only if you don't want it checked @category_class INT OUTPUT, @category_type INT OUTPUT -- Supply NULL only if you don't want the return value AS BEGIN SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @class = LTRIM(RTRIM(@class)) SELECT @type = LTRIM(RTRIM(@type)) SELECT @name = LTRIM(RTRIM(@name)) -- Turn [nullable] empty string parameters into NULLs IF (@type = '') SELECT @type = NULL IF (@name = N'') SELECT @name = NULL -- Check class SELECT @class = UPPER(@class collate SQL_Latin1_General_CP1_CS_AS) SELECT @category_class = CASE @class WHEN 'JOB' THEN 1 WHEN 'ALERT' THEN 2 WHEN 'OPERATOR' THEN 3 ELSE 0 END IF (@category_class = 0) BEGIN RAISERROR(14266, -1, -1, '@class', 'JOB, ALERT, OPERATOR') RETURN(1) -- Failure END -- Check name IF ((@name IS NOT NULL) AND (@name = N'[DEFAULT]')) BEGIN RAISERROR(14200, -1, -1, '@name') RETURN(1) -- Failure END -- Check type [optionally] IF (@type IS NOT NULL) BEGIN IF (@class = 'JOB') BEGIN SELECT @type = UPPER(@type collate SQL_Latin1_General_CP1_CS_AS) SELECT @category_type = CASE @type WHEN 'LOCAL' THEN 1 WHEN 'MULTI-SERVER' THEN 2 ELSE 0 END IF (@category_type = 0) BEGIN RAISERROR(14266, -1, -1, '@type', 'LOCAL, MULTI-SERVER') RETURN(1) -- Failure END END ELSE BEGIN IF (@type <> 'NONE') BEGIN RAISERROR(14266, -1, -1, '@type', 'NONE') RETURN(1) -- Failure END ELSE SELECT @category_type = 3 END END RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_CATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_category...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_category') AND (type = 'P'))) DROP PROCEDURE sp_add_category go CREATE PROCEDURE sp_add_category @class VARCHAR(8) = 'JOB', -- JOB or ALERT or OPERATOR @type VARCHAR(12) = 'LOCAL', -- LOCAL or MULTI-SERVER (for JOB) or NONE otherwise @name sysname AS BEGIN DECLARE @retval INT DECLARE @category_type INT DECLARE @category_class INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @class = LTRIM(RTRIM(@class)) SELECT @type = LTRIM(RTRIM(@type)) SELECT @name = LTRIM(RTRIM(@name)) EXECUTE @retval = sp_verify_category @class, @type, @name, @category_class OUTPUT, @category_type OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check name IF (EXISTS (SELECT * FROM msdb.dbo.syscategories WHERE (category_class = @category_class) AND (name = @name))) BEGIN RAISERROR(14261, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Add the row INSERT INTO msdb.dbo.syscategories (category_class, category_type, name) VALUES (@category_class, @category_type, @name) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_UPDATE_CATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_category...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_category') AND (type = 'P'))) DROP PROCEDURE sp_update_category go CREATE PROCEDURE sp_update_category @class VARCHAR(8), -- JOB or ALERT or OPERATOR @name sysname, @new_name sysname AS BEGIN DECLARE @retval INT DECLARE @category_id INT DECLARE @category_class INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @class = LTRIM(RTRIM(@class)) SELECT @name = LTRIM(RTRIM(@name)) SELECT @new_name = LTRIM(RTRIM(@new_name)) --turn empy parametrs tu null parameters IF @name = '' SELECT @name = NULL EXECUTE @retval = sp_verify_category @class, NULL, @new_name, @category_class OUTPUT, NULL IF (@retval <> 0) RETURN(1) -- Failure --ID @name not null check id such a category exists --check name - it should exist if not null IF @name IS NOT NULL AND NOT EXISTS(SELECT * FROM msdb.dbo.syscategories WHERE name = @name AND category_class = @category_class) BEGIN RAISERROR(14526, -1, -1, @name, @category_class) RETURN(1) -- Failure END -- Check name SELECT @category_id = category_id FROM msdb.dbo.syscategories WHERE (category_class = @category_class) AND (name = @new_name) IF (@category_id IS NOT NULL) BEGIN RAISERROR(14261, -1, -1, '@new_name', @new_name) RETURN(1) -- Failure END -- Make sure that we're not updating one of the permanent categories (id's 0 - 99) IF (@category_id < 100) BEGIN RAISERROR(14276, -1, -1, @name, @class) RETURN(1) -- Failure END -- Update the category name UPDATE msdb.dbo.syscategories SET name = @new_name WHERE (category_class = @category_class) AND (name = @name) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_DELETE_CATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_category...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_category') AND (type = 'P'))) DROP PROCEDURE sp_delete_category go CREATE PROCEDURE sp_delete_category @class VARCHAR(8), -- JOB or ALERT or OPERATOR @name sysname AS BEGIN DECLARE @retval INT DECLARE @category_id INT DECLARE @category_class INT DECLARE @category_type INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @class = LTRIM(RTRIM(@class)) SELECT @name = LTRIM(RTRIM(@name)) EXECUTE @retval = sp_verify_category @class, NULL, NULL, @category_class OUTPUT, NULL IF (@retval <> 0) RETURN(1) -- Failure -- Check name SELECT @category_id = category_id, @category_type = category_type FROM msdb.dbo.syscategories WHERE (category_class = @category_class) AND (name = @name) IF (@category_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Make sure that we're not deleting one of the permanent categories (id's 0 - 99) IF (@category_id < 100) BEGIN RAISERROR(14276, -1, -1, @name, @class) RETURN(1) -- Failure END BEGIN TRANSACTION -- Clean-up any Jobs that reference the deleted category UPDATE msdb.dbo.sysjobs SET category_id = CASE @category_type WHEN 1 THEN 0 -- [Uncategorized (Local)] WHEN 2 THEN 2 -- [Uncategorized (Multi-Server)] END WHERE (category_id = @category_id) -- Clean-up any Alerts that reference the deleted category UPDATE msdb.dbo.sysalerts SET category_id = 98 WHERE (category_id = @category_id) -- Clean-up any Operators that reference the deleted category UPDATE msdb.dbo.sysoperators SET category_id = 99 WHERE (category_id = @category_id) -- Finally, delete the category itself DELETE FROM msdb.dbo.syscategories WHERE (category_id = @category_id) COMMIT TRANSACTION RETURN(0) -- Success END go /**************************************************************/ /* SP_HELP_CATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_category...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_category') AND (type = 'P'))) DROP PROCEDURE sp_help_category go CREATE PROCEDURE sp_help_category @class VARCHAR(8) = 'JOB', -- JOB, ALERT or OPERATOR @type VARCHAR(12) = NULL, -- LOCAL, MULTI-SERVER, or NONE @name sysname = NULL, @suffix BIT = 0 -- 0 = no suffix, 1 = add suffix AS BEGIN DECLARE @retval INT DECLARE @type_in VARCHAR(12) DECLARE @category_type INT DECLARE @category_class INT DECLARE @where_clause NVARCHAR(500) DECLARE @cmd NVARCHAR(max) SET NOCOUNT ON -- Both name and type can be NULL (this is valid, indeed it is how SQLDMO populates -- the JobCategory collection) -- Remove any leading/trailing spaces from parameters SELECT @class = LTRIM(RTRIM(@class)) SELECT @type = LTRIM(RTRIM(@type)) SELECT @name = LTRIM(RTRIM(@name)) -- Turn [nullable] empty string parameters into NULLs IF (@type = '') SELECT @type = NULL IF (@name = N'') SELECT @name = NULL -- Check the type and class IF (@class = 'JOB') AND (@type IS NULL) SELECT @type_in = 'LOCAL' -- This prevents sp_verify_category from failing ELSE IF (@class <> 'JOB') AND (@type IS NULL) SELECT @type_in = 'NONE' ELSE SELECT @type_in = @type EXECUTE @retval = sp_verify_category @class, @type_in, NULL, @category_class OUTPUT, @category_type OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Make sure that 'suffix' is either 0 or 1 IF (@suffix <> 0) SELECT @suffix = 1 --check name - it should exist if not null IF @name IS NOT NULL AND NOT EXISTS(SELECT * FROM msdb.dbo.syscategories WHERE name = @name AND category_class = @category_class) BEGIN DECLARE @category_class_string NVARCHAR(25) SET @category_class_string = CAST(@category_class AS nvarchar(25)) RAISERROR(14526, -1, -1, @name, @category_class_string) RETURN(1) -- Failure END -- Build the WHERE qualifier SELECT @where_clause = N'WHERE (category_class = ' + CONVERT(NVARCHAR, @category_class) + N') ' IF (@name IS NOT NULL) SELECT @where_clause = @where_clause + N'AND (name = N' + QUOTENAME(@name, '''') + N') ' IF (@type IS NOT NULL) SELECT @where_clause = @where_clause + N'AND (category_type = ' + CONVERT(NVARCHAR, @category_type) + N') ' -- Construct the query SELECT @cmd = N'SELECT category_id, ' IF (@suffix = 1) BEGIN SELECT @cmd = @cmd + N'''category_type'' = ' SELECT @cmd = @cmd + N'CASE category_type ' SELECT @cmd = @cmd + N'WHEN 0 THEN ''NONE'' ' SELECT @cmd = @cmd + N'WHEN 1 THEN ''LOCAL'' ' SELECT @cmd = @cmd + N'WHEN 2 THEN ''MULTI-SERVER'' ' SELECT @cmd = @cmd + N'WHEN 3 THEN ''NONE'' ' SELECT @cmd = @cmd + N'ELSE FORMATMESSAGE(14205) ' SELECT @cmd = @cmd + N'END, ' END ELSE BEGIN SELECT @cmd = @cmd + N'category_type, ' END SELECT @cmd = @cmd + N'name ' SELECT @cmd = @cmd + N'FROM msdb.dbo.syscategories ' -- Execute the query EXECUTE (@cmd + @where_clause + N'ORDER BY category_type, name') RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_HELP_TARGETSERVER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_targetserver...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_targetserver') AND (type = 'P'))) DROP PROCEDURE sp_help_targetserver go CREATE PROCEDURE sp_help_targetserver @server_name sysname = NULL AS BEGIN SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name))) IF (@server_name IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name))) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END END DECLARE @unread_instructions TABLE ( target_server sysname COLLATE database_default, unread_instructions INT ) INSERT INTO @unread_instructions SELECT target_server, COUNT(*) FROM msdb.dbo.sysdownloadlist WHERE (status = 0) GROUP BY target_server SELECT sts.server_id, sts.server_name, sts.location, sts.time_zone_adjustment, sts.enlist_date, sts.last_poll_date, 'status' = sts.status | CASE WHEN DATEDIFF(ss, sts.last_poll_date, GETDATE()) > (3 * sts.poll_interval) THEN 0x2 ELSE 0 END | CASE WHEN ((SELECT COUNT(*) FROM msdb.dbo.sysdownloadlist sdl WHERE (sdl.target_server = sts.server_name) AND (sdl.error_message IS NOT NULL)) > 0) THEN 0x4 ELSE 0 END, 'unread_instructions' = ISNULL(ui.unread_instructions, 0), 'local_time' = DATEADD(SS, DATEDIFF(SS, sts.last_poll_date, GETDATE()), sts.local_time_at_last_poll), sts.enlisted_by_nt_user, sts.poll_interval FROM msdb.dbo.systargetservers sts LEFT OUTER JOIN @unread_instructions ui ON (sts.server_name = ui.target_server) WHERE ((@server_name IS NULL) OR (UPPER(sts.server_name) = @server_name)) ORDER BY server_name RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_RESYNC_TARGETSERVER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_resync_targetserver...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_resync_targetserver') AND (type = 'P'))) DROP PROCEDURE sp_resync_targetserver go CREATE PROCEDURE sp_resync_targetserver @server_name sysname AS BEGIN SET NOCOUNT ON -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SELECT @server_name = LTRIM(RTRIM(@server_name)) IF (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) <> N'ALL') BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = UPPER(@server_name)))) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END -- We want the target server to: -- a) delete all their current MSX jobs, and -- b) download all their jobs again. -- So we delete all the current instructions and post a new set DELETE FROM msdb.dbo.sysdownloadlist WHERE (target_server = @server_name) EXECUTE msdb.dbo.sp_post_msx_operation 'DELETE', 'JOB', 0x00, @server_name EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', 0x00, @server_name END ELSE BEGIN -- We want ALL target servers to: -- a) delete all their current MSX jobs, and -- b) download all their jobs again. -- So we delete all the current instructions and post a new set TRUNCATE TABLE msdb.dbo.sysdownloadlist EXECUTE msdb.dbo.sp_post_msx_operation 'DELETE', 'JOB', 0x00, NULL EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', 0x00, NULL END RETURN(@@error) -- 0 means success END go CHECKPOINT go /**************************************************************/ /* SP_PURGE_JOBHISTORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_purge_jobhistory...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_purge_jobhistory') AND (type = 'P'))) DROP PROCEDURE sp_purge_jobhistory go CREATE PROCEDURE sp_purge_jobhistory @job_name sysname = NULL, @job_id UNIQUEIDENTIFIER = NULL, @oldest_date DATETIME = NULL AS BEGIN DECLARE @rows_affected INT DECLARE @total_rows INT DECLARE @datepart INT DECLARE @timepart INT DECLARE @retval INT DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON IF(@oldest_date IS NOT NULL) BEGIN SET @datepart = CONVERT(INT, CONVERT(VARCHAR, @oldest_date, 112)) SET @timepart = (DATEPART(hh, @oldest_date) * 10000) + (DATEPART(mi, @oldest_date) * 100) + (DATEPART(ss, @oldest_date)) END ELSE BEGIN SET @datepart = 99999999 SET @timepart = 0 END IF ((@job_name IS NOT NULL) OR (@job_id IS NOT NULL)) BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReader role that can see all jobs but -- cannot purge history of jobs they do not own IF (@job_owner_sid <> SUSER_SID() -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) -- is not sysadmin AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) <> 1)) -- is not SQLAgentOperatorRole BEGIN RAISERROR(14392, -1, -1); RETURN(1) -- Failure END -- Delete the histories for this job DELETE FROM msdb.dbo.sysjobhistory WHERE (job_id = @job_id) AND ((run_date < @datepart) OR (run_date <= @datepart AND run_time < @timepart)) SELECT @rows_affected = @@rowcount END ELSE BEGIN -- Only a sysadmin or SQLAgentOperatorRole can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) -- is not sysadmin AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) <> 1)) -- is not SQLAgentOperatorRole BEGIN RAISERROR(14392, -1, -1) RETURN(1) -- Failure END IF(@oldest_date IS NOT NULL) BEGIN DELETE FROM msdb.dbo.sysjobhistory WHERE ((run_date < @datepart) OR (run_date <= @datepart AND run_time < @timepart)) END ELSE BEGIN DELETE FROM msdb.dbo.sysjobhistory END SELECT @rows_affected = @@rowcount END RAISERROR(14226, 0, 1, @rows_affected) RETURN(0) -- Success END go /**************************************************************/ /* SP_HELP_JOBHISTORY_FULL */ /**************************************************************/ use [msdb] IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_jobhistory_full') AND (type = 'P'))) DROP PROCEDURE sp_help_jobhistory_full go CREATE PROCEDURE sp_help_jobhistory_full @job_id UNIQUEIDENTIFIER, @job_name sysname, @step_id INT, @sql_message_id INT, @sql_severity INT, @start_run_date INT, @end_run_date INT, @start_run_time INT, @end_run_time INT, @minimum_run_duration INT, @run_status INT, @minimum_retries INT, @oldest_first INT, @server sysname, @mode VARCHAR(7), @order_by INT, @distributed_job_history BIT AS IF(@distributed_job_history = 1) SELECT null as instance_id, sj.job_id, job_name = sj.name, null as step_id, null as step_name, null as sql_message_id, null as sql_severity, sjh.last_outcome_message as message, sjh.last_run_outcome as run_status, sjh.last_run_date as run_date, sjh.last_run_time as run_time, sjh.last_run_duration as run_duration, null as operator_emailed, null as operator_netsentname, null as operator_paged, null as retries_attempted, sts.server_name as server FROM msdb.dbo.sysjobservers sjh JOIN msdb.dbo.systargetservers sts ON (sts.server_id = sjh.server_id) JOIN msdb.dbo.sysjobs_view sj ON(sj.job_id = sjh.job_id) WHERE (@job_id = sjh.job_id) AND ((@start_run_date IS NULL) OR (sjh.last_run_date >= @start_run_date)) AND ((@end_run_date IS NULL) OR (sjh.last_run_date <= @end_run_date)) AND ((@start_run_time IS NULL) OR (sjh.last_run_time >= @start_run_time)) AND ((@minimum_run_duration IS NULL) OR (sjh.last_run_duration >= @minimum_run_duration)) AND ((@run_status IS NULL) OR (@run_status = sjh.last_run_outcome)) AND ((@server IS NULL) OR (sts.server_name = @server)) ELSE SELECT sjh.instance_id, -- This is included just for ordering purposes sj.job_id, job_name = sj.name, sjh.step_id, sjh.step_name, sjh.sql_message_id, sjh.sql_severity, sjh.message, sjh.run_status, sjh.run_date, sjh.run_time, sjh.run_duration, operator_emailed = so1.name, operator_netsent = so2.name, operator_paged = so3.name, sjh.retries_attempted, sjh.server FROM msdb.dbo.sysjobhistory sjh LEFT OUTER JOIN msdb.dbo.sysoperators so1 ON (sjh.operator_id_emailed = so1.id) LEFT OUTER JOIN msdb.dbo.sysoperators so2 ON (sjh.operator_id_netsent = so2.id) LEFT OUTER JOIN msdb.dbo.sysoperators so3 ON (sjh.operator_id_paged = so3.id), msdb.dbo.sysjobs_view sj WHERE (sj.job_id = sjh.job_id) AND ((@job_id IS NULL) OR (@job_id = sjh.job_id)) AND ((@step_id IS NULL) OR (@step_id = sjh.step_id)) AND ((@sql_message_id IS NULL) OR (@sql_message_id = sjh.sql_message_id)) AND ((@sql_severity IS NULL) OR (@sql_severity = sjh.sql_severity)) AND ((@start_run_date IS NULL) OR (sjh.run_date >= @start_run_date)) AND ((@end_run_date IS NULL) OR (sjh.run_date <= @end_run_date)) AND ((@start_run_time IS NULL) OR (sjh.run_time >= @start_run_time)) AND ((@end_run_time IS NULL) OR (sjh.run_time <= @end_run_time)) AND ((@minimum_run_duration IS NULL) OR (sjh.run_duration >= @minimum_run_duration)) AND ((@run_status IS NULL) OR (@run_status = sjh.run_status)) AND ((@minimum_retries IS NULL) OR (sjh.retries_attempted >= @minimum_retries)) AND ((@server IS NULL) OR (sjh.server = @server)) ORDER BY (sjh.instance_id * @order_by) GO /**************************************************************/ /* SP_HELP_JOBHISTORY_SUMMARY */ /**************************************************************/ use [msdb] IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_jobhistory_summary') AND (type = 'P'))) DROP PROCEDURE sp_help_jobhistory_summary go CREATE PROCEDURE sp_help_jobhistory_summary @job_id UNIQUEIDENTIFIER, @job_name sysname, @step_id INT, @sql_message_id INT, @sql_severity INT, @start_run_date INT, @end_run_date INT, @start_run_time INT, @end_run_time INT, @minimum_run_duration INT, @run_status INT, @minimum_retries INT, @oldest_first INT, @server sysname, @mode VARCHAR(7), @order_by INT, @distributed_job_history BIT AS -- Summary format: same WHERE clause as for full, just a different SELECT list IF(@distributed_job_history = 1) SELECT sj.job_id, job_name = sj.name, sjh.last_run_outcome as run_status, sjh.last_run_date as run_date, sjh.last_run_time as run_time, sjh.last_run_duration as run_duration, null as operator_emailed, null as operator_netsentname, null as operator_paged, null as retries_attempted, sts.server_name as server FROM msdb.dbo.sysjobservers sjh JOIN msdb.dbo.systargetservers sts ON (sts.server_id = sjh.server_id) JOIN msdb.dbo.sysjobs_view sj ON(sj.job_id = sjh.job_id) WHERE (@job_id = sjh.job_id) AND ((@start_run_date IS NULL) OR (sjh.last_run_date >= @start_run_date)) AND ((@end_run_date IS NULL) OR (sjh.last_run_date <= @end_run_date)) AND ((@start_run_time IS NULL) OR (sjh.last_run_time >= @start_run_time)) AND ((@minimum_run_duration IS NULL) OR (sjh.last_run_duration >= @minimum_run_duration)) AND ((@run_status IS NULL) OR (@run_status = sjh.last_run_outcome)) AND ((@server IS NULL) OR (sts.server_name = @server)) ELSE SELECT sj.job_id, job_name = sj.name, sjh.run_status, sjh.run_date, sjh.run_time, sjh.run_duration, operator_emailed = substring(so1.name, 1, 20), operator_netsent = substring(so2.name, 1, 20), operator_paged = substring(so3.name, 1, 20), sjh.retries_attempted, sjh.server FROM msdb.dbo.sysjobhistory sjh LEFT OUTER JOIN msdb.dbo.sysoperators so1 ON (sjh.operator_id_emailed = so1.id) LEFT OUTER JOIN msdb.dbo.sysoperators so2 ON (sjh.operator_id_netsent = so2.id) LEFT OUTER JOIN msdb.dbo.sysoperators so3 ON (sjh.operator_id_paged = so3.id), msdb.dbo.sysjobs_view sj WHERE (sj.job_id = sjh.job_id) AND ((@job_id IS NULL) OR (@job_id = sjh.job_id)) AND ((@step_id IS NULL) OR (@step_id = sjh.step_id)) AND ((@sql_message_id IS NULL) OR (@sql_message_id = sjh.sql_message_id)) AND ((@sql_severity IS NULL) OR (@sql_severity = sjh.sql_severity)) AND ((@start_run_date IS NULL) OR (sjh.run_date >= @start_run_date)) AND ((@end_run_date IS NULL) OR (sjh.run_date <= @end_run_date)) AND ((@start_run_time IS NULL) OR (sjh.run_time >= @start_run_time)) AND ((@end_run_time IS NULL) OR (sjh.run_time <= @end_run_time)) AND ((@minimum_run_duration IS NULL) OR (sjh.run_duration >= @minimum_run_duration)) AND ((@run_status IS NULL) OR (@run_status = sjh.run_status)) AND ((@minimum_retries IS NULL) OR (sjh.retries_attempted >= @minimum_retries)) AND ((@server IS NULL) OR (sjh.server = @server)) ORDER BY (sjh.instance_id * @order_by) GO /**************************************************************/ /* SP_HELP_JOBHISTORY_SEM */ /**************************************************************/ use [msdb] IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_jobhistory_sem') AND (type = 'P'))) DROP PROCEDURE sp_help_jobhistory_sem go CREATE PROCEDURE sp_help_jobhistory_sem @job_id UNIQUEIDENTIFIER, @job_name sysname, @step_id INT, @sql_message_id INT, @sql_severity INT, @start_run_date INT, @end_run_date INT, @start_run_time INT, @end_run_time INT, @minimum_run_duration INT, @run_status INT, @minimum_retries INT, @oldest_first INT, @server sysname, @mode VARCHAR(7), @order_by INT, @distributed_job_history BIT AS -- SQL Enterprise Manager format IF(@distributed_job_history = 1) SELECT sj.job_id, null as step_name, sjh.last_outcome_message as message, sjh.last_run_outcome as run_status, sjh.last_run_date as run_date, sjh.last_run_time as run_time, sjh.last_run_duration as run_duration, null as operator_emailed, null as operator_netsentname, null as operator_paged FROM msdb.dbo.sysjobservers sjh JOIN msdb.dbo.systargetservers sts ON (sts.server_id = sjh.server_id) JOIN msdb.dbo.sysjobs_view sj ON(sj.job_id = sjh.job_id) WHERE (@job_id = sjh.job_id) ELSE SELECT sjh.step_id, sjh.step_name, sjh.message, sjh.run_status, sjh.run_date, sjh.run_time, sjh.run_duration, operator_emailed = so1.name, operator_netsent = so2.name, operator_paged = so3.name FROM msdb.dbo.sysjobhistory sjh LEFT OUTER JOIN msdb.dbo.sysoperators so1 ON (sjh.operator_id_emailed = so1.id) LEFT OUTER JOIN msdb.dbo.sysoperators so2 ON (sjh.operator_id_netsent = so2.id) LEFT OUTER JOIN msdb.dbo.sysoperators so3 ON (sjh.operator_id_paged = so3.id), msdb.dbo.sysjobs_view sj WHERE (sj.job_id = sjh.job_id) AND (@job_id = sjh.job_id) ORDER BY (sjh.instance_id * @order_by) GO /**************************************************************/ /* SP_HELP_JOBHISTORY */ /**************************************************************/ use [msdb] PRINT '' PRINT 'Creating procedure sp_help_jobhistory...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_jobhistory') AND (type = 'P'))) DROP PROCEDURE sp_help_jobhistory go CREATE PROCEDURE [dbo].[sp_help_jobhistory] @job_id UNIQUEIDENTIFIER = NULL, @job_name sysname = NULL, @step_id INT = NULL, @sql_message_id INT = NULL, @sql_severity INT = NULL, @start_run_date INT = NULL, -- YYYYMMDD @end_run_date INT = NULL, -- YYYYMMDD @start_run_time INT = NULL, -- HHMMSS @end_run_time INT = NULL, -- HHMMSS @minimum_run_duration INT = NULL, -- HHMMSS @run_status INT = NULL, -- SQLAGENT_EXEC_X code @minimum_retries INT = NULL, @oldest_first INT = 0, -- Or 1 @server sysname = NULL, @mode VARCHAR(7) = 'SUMMARY' -- Or 'FULL' or 'SEM' AS BEGIN DECLARE @retval INT DECLARE @order_by INT -- Must be INT since it can be -1 SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @server = LTRIM(RTRIM(@server)) SELECT @mode = LTRIM(RTRIM(@mode)) -- Turn [nullable] empty string parameters into NULLs IF (@server = N'') SELECT @server = NULL -- Check job id/name (if supplied) IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL)) BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END -- Check @start_run_date IF (@start_run_date IS NOT NULL) BEGIN EXECUTE @retval = sp_verify_job_date @start_run_date, '@start_run_date' IF (@retval <> 0) RETURN(1) -- Failure END -- Check @end_run_date IF (@end_run_date IS NOT NULL) BEGIN EXECUTE @retval = sp_verify_job_date @end_run_date, '@end_run_date' IF (@retval <> 0) RETURN(1) -- Failure END -- Check @start_run_time EXECUTE @retval = sp_verify_job_time @start_run_time, '@start_run_time' IF (@retval <> 0) RETURN(1) -- Failure -- Check @end_run_time EXECUTE @retval = sp_verify_job_time @end_run_time, '@end_run_time' IF (@retval <> 0) RETURN(1) -- Failure -- Check @run_status IF ((@run_status < 0) OR (@run_status > 5)) BEGIN RAISERROR(14198, -1, -1, '@run_status', '0..5') RETURN(1) -- Failure END -- Check mode SELECT @mode = UPPER(@mode collate SQL_Latin1_General_CP1_CS_AS) IF (@mode NOT IN ('SUMMARY', 'FULL', 'SEM')) BEGIN RAISERROR(14266, -1, -1, '@mode', 'SUMMARY, FULL, SEM') RETURN(1) -- Failure END SELECT @order_by = -1 IF (@oldest_first = 1) SELECT @order_by = 1 DECLARE @distributed_job_history BIT SET @distributed_job_history = 0 IF (@job_id IS NOT NULL) AND ( EXISTS (SELECT * FROM msdb.dbo.sysjobs sj, msdb.dbo.sysjobservers sjs WHERE (sj.job_id = sjs.job_id) AND (sj.job_id = @job_id) AND (sjs.server_id <> 0))) SET @distributed_job_history = 1 -- Return history information filtered by the supplied parameters. -- Having actual queries in subprocedures allows better query plans because query optimizer sniffs correct parameters IF (@mode = 'FULL') BEGIN -- NOTE: SQLDMO relies on the 'FULL' format; ** DO NOT CHANGE IT ** EXECUTE sp_help_jobhistory_full @job_id, @job_name, @step_id, @sql_message_id, @sql_severity, @start_run_date, @end_run_date, @start_run_time, @end_run_time, @minimum_run_duration, @run_status, @minimum_retries, @oldest_first, @server, @mode, @order_by, @distributed_job_history END ELSE IF (@mode = 'SUMMARY') BEGIN -- Summary format: same WHERE clause as for full, just a different SELECT list EXECUTE sp_help_jobhistory_summary @job_id, @job_name, @step_id, @sql_message_id, @sql_severity, @start_run_date, @end_run_date, @start_run_time, @end_run_time, @minimum_run_duration, @run_status, @minimum_retries, @oldest_first, @server, @mode, @order_by, @distributed_job_history END ELSE IF (@mode = 'SEM') BEGIN -- SQL Enterprise Manager format EXECUTE sp_help_jobhistory_sem @job_id, @job_name, @step_id, @sql_message_id, @sql_severity, @start_run_date, @end_run_date, @start_run_time, @end_run_time, @minimum_run_duration, @run_status, @minimum_retries, @oldest_first, @server, @mode, @order_by, @distributed_job_history END RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_JOBSERVER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_jobserver...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_jobserver') AND (type = 'P'))) DROP PROCEDURE sp_add_jobserver go CREATE PROCEDURE sp_add_jobserver @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @server_name sysname = NULL, -- if NULL will default to serverproperty('ServerName') @automatic_post BIT = 1 -- Flag for SEM use only AS BEGIN DECLARE @retval INT DECLARE @server_id INT DECLARE @job_type VARCHAR(12) DECLARE @current_job_category_type VARCHAR(12) DECLARE @msx_operator_id INT DECLARE @local_server_name sysname DECLARE @is_sysadmin INT DECLARE @job_owner sysname DECLARE @owner_sid VARBINARY(85) DECLARE @owner_name sysname SET NOCOUNT ON IF (@server_name IS NULL) OR (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) = N'(LOCAL)') SELECT @server_name = CONVERT(sysname, SERVERPROPERTY('ServerName')) -- Remove any leading/trailing spaces from parameters SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name))) EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- First, check if the server is the local server SELECT @local_server_name = CONVERT(NVARCHAR,SERVERPROPERTY ('SERVERNAME')) IF (@server_name = UPPER(@local_server_name)) SELECT @server_name = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) -- For a multi-server job... IF (@server_name <> UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) BEGIN -- 1) Only sysadmin can add a multi-server job IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) BEGIN RAISERROR(14398, -1, -1); RETURN(1) -- Failure END -- 2) Job must be owned by sysadmin SELECT @owner_sid = owner_sid, @owner_name = dbo.SQLAGENT_SUSER_SNAME(owner_sid) FROM msdb.dbo.sysjobs WHERE (job_id = @job_id) IF @owner_sid = 0xFFFFFFFF BEGIN SELECT @is_sysadmin = 1 END ELSE BEGIN SELECT @is_sysadmin = 0 EXECUTE msdb.dbo.sp_sqlagent_has_server_access @login_name = @owner_name, @is_sysadmin_member = @is_sysadmin OUTPUT END IF (@is_sysadmin = 0) BEGIN RAISERROR(14544, -1, -1, @owner_name, N'sysadmin') RETURN(1) -- Failure END -- 3) Check if any of the TSQL steps have a non-null database_user_name IF (EXISTS (SELECT * FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (subsystem = N'TSQL') AND (database_user_name IS NOT NULL))) BEGIN RAISERROR(14542, -1, -1, N'database_user_name') RETURN(1) -- Failure END SELECT @server_id = server_id FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name) IF (@server_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END END ELSE SELECT @server_id = 0 -- Check that this job has not already been targeted at this server IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = @server_id))) BEGIN RAISERROR(14269, -1, -1, @job_name, @server_name) RETURN(1) -- Failure END -- Prevent the job from being targeted at both the local AND remote servers SELECT @job_type = 'UNKNOWN' IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) SELECT @job_type = 'LOCAL' ELSE IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) SELECT @job_type = 'MULTI-SERVER' IF ((@server_id = 0) AND (@job_type = 'MULTI-SERVER')) BEGIN RAISERROR(14290, -1, -1) RETURN(1) -- Failure END IF ((@server_id <> 0) AND (@job_type = 'LOCAL')) BEGIN RAISERROR(14291, -1, -1) RETURN(1) -- Failure END -- For a multi-server job, check that any notifications are to the MSXOperator IF (@job_type = 'MULTI-SERVER') BEGIN SELECT @msx_operator_id = id FROM msdb.dbo.sysoperators WHERE (name = N'MSXOperator') IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE (job_id = @job_id) AND (((notify_email_operator_id <> 0) AND (notify_email_operator_id <> @msx_operator_id)) OR ((notify_page_operator_id <> 0) AND (notify_page_operator_id <> @msx_operator_id)) OR ((notify_netsend_operator_id <> 0) AND (notify_netsend_operator_id <> @msx_operator_id))))) BEGIN RAISERROR(14221, -1, -1, 'MSXOperator') RETURN(1) -- Failure END END -- Insert the sysjobservers row INSERT INTO msdb.dbo.sysjobservers (job_id, server_id, last_run_outcome, last_outcome_message, last_run_date, last_run_time, last_run_duration) VALUES (@job_id, @server_id, 5, -- ie. SQLAGENT_EXEC_UNKNOWN (can't use 0 since this is SQLAGENT_EXEC_FAIL) NULL, 0, 0, 0) -- Re-categorize the job (if necessary) SELECT @current_job_category_type = CASE category_type WHEN 1 THEN 'LOCAL' WHEN 2 THEN 'MULTI-SERVER' END FROM msdb.dbo.sysjobs_view sjv, msdb.dbo.syscategories sc WHERE (sjv.category_id = sc.category_id) AND (sjv.job_id = @job_id) IF (@server_id = 0) AND (@current_job_category_type = 'MULTI-SERVER') BEGIN UPDATE msdb.dbo.sysjobs SET category_id = 0 -- [Uncategorized (Local)] WHERE (job_id = @job_id) END IF (@server_id <> 0) AND (@current_job_category_type = 'LOCAL') BEGIN UPDATE msdb.dbo.sysjobs SET category_id = 2 -- [Uncategorized (Multi-Server)] WHERE (job_id = @job_id) END -- Instruct the new server to pick up the job IF (@automatic_post = 1) EXECUTE @retval = sp_post_msx_operation 'INSERT', 'JOB', @job_id, @server_name -- If the job is local, make sure that SQLServerAgent caches it IF (@server_id = 0) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @action_type = N'I' END RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_DELETE_JOBSERVER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_jobserver...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_jobserver') AND (type = 'P'))) DROP PROCEDURE sp_delete_jobserver go CREATE PROCEDURE sp_delete_jobserver @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @server_name sysname AS BEGIN DECLARE @retval INT DECLARE @server_id INT DECLARE @local_machine_name sysname SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @server_name = LTRIM(RTRIM(@server_name)) IF (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) = '(LOCAL)') SELECT @server_name = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- First, check if the server is the local server EXECUTE @retval = master.dbo.xp_getnetname @local_machine_name OUTPUT IF (@retval <> 0) RETURN(1) -- Failure IF (@local_machine_name IS NOT NULL) AND (UPPER(@server_name) = UPPER(@local_machine_name)) SELECT @server_name = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) -- Check server name IF (UPPER(@server_name) <> UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) BEGIN SELECT @server_id = server_id FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name) IF (@server_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END END ELSE SELECT @server_id = 0 -- Check that the job is indeed targeted at the server IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = @server_id))) BEGIN RAISERROR(14270, -1, -1, @job_name, @server_name) RETURN(1) -- Failure END -- Instruct the deleted server to purge the job -- NOTE: We must do this BEFORE we delete the sysjobservers row EXECUTE @retval = sp_post_msx_operation 'DELETE', 'JOB', @job_id, @server_name -- Delete the sysjobservers row DELETE FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = @server_id) -- We used to change the category_id to 0 when removing the last job server -- from a job. We no longer do this. -- IF (NOT EXISTS (SELECT * -- FROM msdb.dbo.sysjobservers -- WHERE (job_id = @job_id))) -- BEGIN -- UPDATE msdb.dbo.sysjobs -- SET category_id = 0 -- [Uncategorized (Local)] -- WHERE (job_id = @job_id) -- END -- If the job is local, make sure that SQLServerAgent removes it from cache IF (@server_id = 0) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @action_type = N'D' END RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_HELP_JOBSERVER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_jobserver...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_jobserver') AND (type = 'P'))) DROP PROCEDURE sp_help_jobserver go CREATE PROCEDURE sp_help_jobserver @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @show_last_run_details TINYINT = 0 -- Controls if last-run execution information is part of the result set (1 = yes, 0 = no) AS BEGIN DECLARE @retval INT SET NOCOUNT ON EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- The show-last-run-details flag must be either 1 or 0 IF (@show_last_run_details <> 0) SELECT @show_last_run_details = 1 IF (@show_last_run_details = 1) BEGIN -- List the servers that @job_name has been targeted at (INCLUDING last-run details) SELECT stsv.server_id, stsv.server_name, stsv.enlist_date, stsv.last_poll_date, sjs.last_run_date, sjs.last_run_time, sjs.last_run_duration, sjs.last_run_outcome, -- Same as JOB_OUTCOME_CODE (SQLAGENT_EXEC_x) sjs.last_outcome_message FROM msdb.dbo.sysjobservers sjs LEFT OUTER JOIN msdb.dbo.systargetservers_view stsv ON (sjs.server_id = stsv.server_id) WHERE (sjs.job_id = @job_id) END ELSE BEGIN -- List the servers that @job_name has been targeted at (EXCLUDING last-run details) SELECT stsv.server_id, stsv.server_name, stsv.enlist_date, stsv.last_poll_date FROM msdb.dbo.sysjobservers sjs LEFT OUTER JOIN msdb.dbo.systargetservers_view stsv ON (sjs.server_id = stsv.server_id) WHERE (sjs.job_id = @job_id) END RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_HELP_DOWNLOADLIST */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_downloadlist...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_downloadlist') AND (type = 'P'))) DROP PROCEDURE sp_help_downloadlist go CREATE PROCEDURE sp_help_downloadlist @job_id UNIQUEIDENTIFIER = NULL, -- If provided must NOT also provide job_name @job_name sysname = NULL, -- If provided must NOT also provide job_id @operation VARCHAR(64) = NULL, @object_type VARCHAR(64) = NULL, -- Only 'JOB' or 'SERVER' are valid in 7.0 @object_name sysname = NULL, @target_server sysname = NULL, @has_error TINYINT = NULL, -- NULL or 1 @status TINYINT = NULL, @date_posted DATETIME = NULL -- Include all entries made on OR AFTER this date AS BEGIN DECLARE @retval INT DECLARE @operation_code INT DECLARE @object_type_id TINYINT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @operation = LTRIM(RTRIM(@operation)) SELECT @object_type = LTRIM(RTRIM(@object_type)) SELECT @object_name = LTRIM(RTRIM(@object_name)) SELECT @target_server = UPPER(LTRIM(RTRIM(@target_server))) -- Turn [nullable] empty string parameters into NULLs IF (@operation = '') SELECT @operation = NULL IF (@object_type = '') SELECT @object_type = NULL IF (@object_name = N'') SELECT @object_name = NULL IF (@target_server = N'') SELECT @target_server = NULL IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL)) BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END -- Check operation IF (@operation IS NOT NULL) BEGIN SELECT @operation = UPPER(@operation collate SQL_Latin1_General_CP1_CS_AS) SELECT @operation_code = CASE @operation WHEN 'INSERT' THEN 1 WHEN 'UPDATE' THEN 2 WHEN 'DELETE' THEN 3 WHEN 'START' THEN 4 WHEN 'STOP' THEN 5 WHEN 'RE-ENLIST' THEN 6 WHEN 'DEFECT' THEN 7 WHEN 'SYNC-TIME' THEN 8 WHEN 'SET-POLL' THEN 9 ELSE 0 END IF (@operation_code = 0) BEGIN RAISERROR(14266, -1, -1, '@operation_code', 'INSERT, UPDATE, DELETE, START, STOP, RE-ENLIST, DEFECT, SYNC-TIME, SET-POLL') RETURN(1) -- Failure END END -- Check object type (in 7.0 only 'JOB' and 'SERVER' are valid) IF (@object_type IS NOT NULL) BEGIN SELECT @object_type = UPPER(@object_type collate SQL_Latin1_General_CP1_CS_AS) IF ((@object_type <> 'JOB') AND (@object_type <> 'SERVER')) BEGIN RAISERROR(14266, -1, -1, '@object_type', 'JOB, SERVER') RETURN(1) -- Failure END ELSE SELECT @object_type_id = CASE @object_type WHEN 'JOB' THEN 1 WHEN 'SERVER' THEN 2 ELSE 0 END END -- If object-type is supplied then object-name must also be supplied IF ((@object_type IS NOT NULL) AND (@object_name IS NULL)) OR ((@object_type IS NULL) AND (@object_name IS NOT NULL)) BEGIN RAISERROR(14272, -1, -1) RETURN(1) -- Failure END -- Check target server IF (@target_server IS NOT NULL) AND NOT EXISTS (SELECT * FROM msdb.dbo.systargetservers WHERE UPPER(server_name) = @target_server) BEGIN RAISERROR(14262, -1, -1, '@target_server', @target_server) RETURN(1) -- Failure END -- Check has-error IF (@has_error IS NOT NULL) AND (@has_error <> 1) BEGIN RAISERROR(14266, -1, -1, '@has_error', '1, NULL') RETURN(1) -- Failure END -- Check status IF (@status IS NOT NULL) AND (@status <> 0) AND (@status <> 1) BEGIN RAISERROR(14266, -1, -1, '@status', '0, 1') RETURN(1) -- Failure END -- Return the result set SELECT sdl.instance_id, sdl.source_server, 'operation_code' = CASE sdl.operation_code WHEN 1 THEN '1 (INSERT)' WHEN 2 THEN '2 (UPDATE)' WHEN 3 THEN '3 (DELETE)' WHEN 4 THEN '4 (START)' WHEN 5 THEN '5 (STOP)' WHEN 6 THEN '6 (RE-ENLIST)' WHEN 7 THEN '7 (DEFECT)' WHEN 8 THEN '8 (SYNC-TIME)' WHEN 9 THEN '9 (SET-POLL)' ELSE CONVERT(VARCHAR, sdl.operation_code) + ' ' + FORMATMESSAGE(14205) END, 'object_name' = ISNULL(sjv.name, CASE WHEN (sdl.operation_code >= 1) AND (sdl.operation_code <= 5) AND (sdl.object_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) THEN FORMATMESSAGE(14212) -- '(all jobs)' WHEN (sdl.operation_code = 3) AND (sdl.object_id <> CONVERT(UNIQUEIDENTIFIER, 0x00)) THEN sdl.deleted_object_name -- Special case handling for a deleted job WHEN (sdl.operation_code >= 1) AND (sdl.operation_code <= 5) AND (sdl.object_id <> CONVERT(UNIQUEIDENTIFIER, 0x00)) THEN FORMATMESSAGE(14580) -- 'job' (safety belt: should never appear) WHEN (sdl.operation_code >= 6) AND (sdl.operation_code <= 9) THEN sdl.target_server ELSE FORMATMESSAGE(14205) END), 'object_id' = ISNULL(sjv.job_id, CASE sdl.object_id WHEN CONVERT(UNIQUEIDENTIFIER, 0x00) THEN CONVERT(UNIQUEIDENTIFIER, 0x00) ELSE sdl.object_id END), sdl.target_server, sdl.error_message, sdl.date_posted, sdl.date_downloaded, sdl.status FROM msdb.dbo.sysdownloadlist sdl LEFT OUTER JOIN msdb.dbo.sysjobs_view sjv ON (sdl.object_id = sjv.job_id) WHERE ((@operation_code IS NULL) OR (operation_code = @operation_code)) AND ((@object_type_id IS NULL) OR (object_type = @object_type_id)) AND ((@job_id IS NULL) OR (object_id = @job_id)) AND ((@target_server IS NULL) OR (target_server = @target_server)) AND ((@has_error IS NULL) OR (DATALENGTH(error_message) >= 1 * @has_error)) AND ((@status IS NULL) OR (status = @status)) AND ((@date_posted IS NULL) OR (date_posted >= @date_posted)) ORDER BY sdl.instance_id RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_ENUM_SQLAGENT_SUBSYSTEMS_INTERNAL */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_sqlagent_subsystems_internal...' IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_enum_sqlagent_subsystems_internal') AND (type = 'P'))) DROP PROCEDURE sp_enum_sqlagent_subsystems_internal go CREATE PROCEDURE sp_enum_sqlagent_subsystems_internal @syssubsytems_refresh_needed BIT = 0 AS BEGIN DECLARE @retval INT SET NOCOUNT ON -- this call will populate subsystems table if necessary EXEC @retval = msdb.dbo.sp_verify_subsystems @syssubsytems_refresh_needed IF @retval <> 0 RETURN(@retval) -- Check if replication is installed DECLARE @replication_installed INT EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Replication', N'IsInstalled', @replication_installed OUTPUT, N'no_output' SELECT @replication_installed = ISNULL(@replication_installed, 0) IF @replication_installed = 0 SELECT subsystem, description = FORMATMESSAGE(description_id), subsystem_dll, agent_exe, start_entry_point, event_entry_point, stop_entry_point, max_worker_threads, subsystem_id FROM syssubsystems WHERE (subsystem NOT IN (N'Distribution', N'LogReader', N'Merge', N'Snapshot', N'QueueReader')) ORDER by subsystem ELSE SELECT subsystem, description = FORMATMESSAGE(description_id), subsystem_dll, agent_exe, start_entry_point, event_entry_point, stop_entry_point, max_worker_threads, subsystem_id FROM syssubsystems ORDER by subsystem_id RETURN(0) END go /**************************************************************/ /* SP_ENUM_SQLAGENT_SUBSYSTEMS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_sqlagent_subsystems...' IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_enum_sqlagent_subsystems') AND (type = 'P'))) DROP PROCEDURE sp_enum_sqlagent_subsystems go CREATE PROCEDURE sp_enum_sqlagent_subsystems AS BEGIN DECLARE @retval INT EXEC @retval = msdb.dbo.sp_enum_sqlagent_subsystems_internal RETURN(@retval) END go /**************************************************************/ /* SP_VERIFY_SUBSYSTEM */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_subsystem...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_subsystem') AND (type = 'P'))) DROP PROCEDURE sp_verify_subsystem go CREATE PROCEDURE sp_verify_subsystem @subsystem NVARCHAR(40) AS BEGIN DECLARE @retval INT SET NOCOUNT ON -- this call will populate subsystems table if necessary EXEC @retval = msdb.dbo.sp_verify_subsystems IF @retval <> 0 RETURN(@retval) -- Remove any leading/trailing spaces from parameters SELECT @subsystem = LTRIM(RTRIM(@subsystem)) -- Make sure Dts is translated into new subsystem's name SSIS IF (@subsystem IS NOT NULL AND UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DTS') BEGIN SET @subsystem = N'SSIS' END IF EXISTS (SELECT * FROM syssubsystems WHERE UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = UPPER(subsystem collate SQL_Latin1_General_CP1_CS_AS)) RETURN(0) -- Success ELSE BEGIN RAISERROR(14234, -1, -1, '@subsystem', 'sp_enum_sqlagent_subsystems') RETURN(1) -- Failure END END go /**************************************************************/ /* SP_VERIFY_SCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_schedule...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_schedule') AND (type = 'P'))) DROP PROCEDURE sp_verify_schedule go CREATE PROCEDURE sp_verify_schedule @schedule_id INT, @name sysname, @enabled TINYINT, @freq_type INT, @freq_interval INT OUTPUT, -- Output because we may set it to 0 if Frequency Type is one-time or auto-start @freq_subday_type INT OUTPUT, -- As above @freq_subday_interval INT OUTPUT, -- As above @freq_relative_interval INT OUTPUT, -- As above @freq_recurrence_factor INT OUTPUT, -- As above @active_start_date INT OUTPUT, @active_start_time INT OUTPUT, @active_end_date INT OUTPUT, @active_end_time INT OUTPUT, @owner_sid VARBINARY(85) --Must be a valid sid. Will fail if this is NULL AS BEGIN DECLARE @return_code INT DECLARE @res_valid_range NVARCHAR(100) DECLARE @reason NVARCHAR(200) DECLARE @isAdmin INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) -- Make sure that NULL input/output parameters - if NULL - are initialized to 0 SELECT @freq_interval = ISNULL(@freq_interval, 0) SELECT @freq_subday_type = ISNULL(@freq_subday_type, 0) SELECT @freq_subday_interval = ISNULL(@freq_subday_interval, 0) SELECT @freq_relative_interval = ISNULL(@freq_relative_interval, 0) SELECT @freq_recurrence_factor = ISNULL(@freq_recurrence_factor, 0) SELECT @active_start_date = ISNULL(@active_start_date, 0) SELECT @active_start_time = ISNULL(@active_start_time, 0) SELECT @active_end_date = ISNULL(@active_end_date, 0) SELECT @active_end_time = ISNULL(@active_end_time, 0) -- Check owner IF(ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) SELECT @isAdmin = 1 ELSE SELECT @isAdmin = 0 -- If a non-sa is [illegally] trying to create a schedule for another user then raise an error IF ((@isAdmin <> 1) AND (ISNULL(IS_MEMBER('SQLAgentOperatorRole'),0) <> 1 AND @schedule_id IS NULL) AND (@owner_sid <> SUSER_SID())) BEGIN RAISERROR(14366, -1, -1) RETURN(1) -- Failure END -- Now just check that the login id is valid (ie. it exists and isn't an NT group) IF (@owner_sid <> 0x010100000000000512000000) AND -- NT AUTHORITY\SYSTEM sid (@owner_sid <> 0x010100000000000514000000) -- NT AUTHORITY\NETWORK SERVICE sid BEGIN IF (@owner_sid IS NULL) OR (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (sid = @owner_sid) AND (isntgroup <> 0))) BEGIN -- NOTE: In the following message we quote @owner_login_name instead of @owner_sid -- since this is the parameter the user passed to the calling SP (ie. either -- sp_add_schedule, sp_add_job and sp_update_job) SELECT @res_valid_range = FORMATMESSAGE(14203) RAISERROR(14234, -1, -1, '@owner_login_name', @res_valid_range) RETURN(1) -- Failure END END -- Verify name (we disallow schedules called 'ALL' since this has special meaning in sp_delete_jobschedules) IF (UPPER(@name collate SQL_Latin1_General_CP1_CS_AS) = N'ALL') BEGIN RAISERROR(14200, -1, -1, '@name') RETURN(1) -- Failure END -- Verify enabled state IF (@enabled <> 0) AND (@enabled <> 1) BEGIN RAISERROR(14266, -1, -1, '@enabled', '0, 1') RETURN(1) -- Failure END -- Verify frequency type IF (@freq_type = 0x2) -- OnDemand is no longer supported BEGIN RAISERROR(14295, -1, -1) RETURN(1) -- Failure END IF (@freq_type NOT IN (0x1, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80)) BEGIN RAISERROR(14266, -1, -1, '@freq_type', '1, 4, 8, 16, 32, 64, 128') RETURN(1) -- Failure END -- Verify frequency sub-day type IF (@freq_subday_type <> 0) AND (@freq_subday_type NOT IN (0x1, 0x2, 0x4, 0x8)) BEGIN RAISERROR(14266, -1, -1, '@freq_subday_type', '0x1, 0x2, 0x4, 0x8') RETURN(1) -- Failure END -- Default active start/end date/times (if not supplied, or supplied as NULLs or 0) IF (@active_start_date = 0) SELECT @active_start_date = DATEPART(yy, GETDATE()) * 10000 + DATEPART(mm, GETDATE()) * 100 + DATEPART(dd, GETDATE()) -- This is an ISO format: "yyyymmdd" IF (@active_end_date = 0) SELECT @active_end_date = 99991231 -- December 31st 9999 IF (@active_start_time = 0) SELECT @active_start_time = 000000 -- 12:00:00 am IF (@active_end_time = 0) SELECT @active_end_time = 235959 -- 11:59:59 pm -- Verify active start/end dates IF (@active_end_date = 0) SELECT @active_end_date = 99991231 EXECUTE @return_code = sp_verify_job_date @active_end_date, '@active_end_date' IF (@return_code <> 0) RETURN(1) -- Failure EXECUTE @return_code = sp_verify_job_date @active_start_date, '@active_start_date' IF (@return_code <> 0) RETURN(1) -- Failure IF (@active_end_date < @active_start_date) BEGIN RAISERROR(14288, -1, -1, '@active_end_date', '@active_start_date') RETURN(1) -- Failure END EXECUTE @return_code = sp_verify_job_time @active_end_time, '@active_end_time' IF (@return_code <> 0) RETURN(1) -- Failure EXECUTE @return_code = sp_verify_job_time @active_start_time, '@active_start_time' IF (@return_code <> 0) RETURN(1) -- Failure -- NOTE: It's valid for active_end_time to be less than active_start_time since in this -- case we assume that the user wants the active time zone to span midnight. -- But it's not valid for active_start_date and active_end_date to be the same for recurring sec/hour/minute schedules IF (@active_start_time = @active_end_time and (@freq_subday_type in (0x2, 0x4, 0x8))) BEGIN SELECT @res_valid_range = FORMATMESSAGE(14202) RAISERROR(14266, -1, -1, '@active_end_time', @res_valid_range) RETURN(1) -- Failure END -- NOTE: The rest of this procedure is a SQL implementation of VerifySchedule in job.c IF ((@freq_type = 0x1) OR -- FREQTYPE_ONETIME (@freq_type = 0x40) OR -- FREQTYPE_AUTOSTART (@freq_type = 0x80)) -- FREQTYPE_ONIDLE BEGIN -- Set standard defaults for non-required parameters SELECT @freq_interval = 0 SELECT @freq_subday_type = 0 SELECT @freq_subday_interval = 0 SELECT @freq_relative_interval = 0 SELECT @freq_recurrence_factor = 0 -- Check that a one-time schedule isn't already in the past -- Bug 442883: let the creation of the one-time schedule succeed but leave a disabled schedule /* IF (@freq_type = 0x1) -- FREQTYPE_ONETIME BEGIN DECLARE @current_date INT DECLARE @current_time INT -- This is an ISO format: "yyyymmdd" SELECT @current_date = CONVERT(INT, CONVERT(VARCHAR, GETDATE(), 112)) SELECT @current_time = (DATEPART(hh, GETDATE()) * 10000) + (DATEPART(mi, GETDATE()) * 100) + DATEPART(ss, GETDATE()) IF (@active_start_date < @current_date) OR ((@active_start_date = @current_date) AND (@active_start_time <= @current_time)) BEGIN SELECT @res_valid_range = '> ' + CONVERT(VARCHAR, @current_date) + ' / ' + CONVERT(VARCHAR, @current_time) SELECT @reason = '@active_start_date = ' + CONVERT(VARCHAR, @active_start_date) + ' / @active_start_time = ' + CONVERT(VARCHAR, @active_start_time) RAISERROR(14266, -1, -1, @reason, @res_valid_range) RETURN(1) -- Failure END END */ GOTO ExitProc END -- Safety net: If the sub-day-type is 0 (and we know that the schedule is not a one-time or -- auto-start) then set it to 1 (FREQSUBTYPE_ONCE). If the user wanted something -- other than ONCE then they should have explicitly set @freq_subday_type. IF (@freq_subday_type = 0) SELECT @freq_subday_type = 0x1 -- FREQSUBTYPE_ONCE IF ((@freq_subday_type <> 0x1) AND -- FREQSUBTYPE_ONCE (see qsched.h) (@freq_subday_type <> 0x2) AND -- FREQSUBTYPE_SECOND (see qsched.h) (@freq_subday_type <> 0x4) AND -- FREQSUBTYPE_MINUTE (see qsched.h) (@freq_subday_type <> 0x8)) -- FREQSUBTYPE_HOUR (see qsched.h) BEGIN SELECT @reason = FORMATMESSAGE(14266, '@freq_subday_type', '0x1, 0x2, 0x4, 0x8') RAISERROR(14278, -1, -1, @reason) RETURN(1) -- Failure END IF ((@freq_subday_type <> 0x1) AND (@freq_subday_interval < 1)) -- FREQSUBTYPE_ONCE and less than 1 interval OR ((@freq_subday_type = 0x2) AND (@freq_subday_interval < 10)) -- FREQSUBTYPE_SECOND and less than 10 seconds (see MIN_SCHEDULE_GRANULARITY in SqlAgent source code) BEGIN SELECT @reason = FORMATMESSAGE(14200, '@freq_subday_interval') RAISERROR(14278, -1, -1, @reason) RETURN(1) -- Failure END IF (@freq_type = 0x4) -- FREQTYPE_DAILY BEGIN SELECT @freq_recurrence_factor = 0 IF (@freq_interval < 1) BEGIN SELECT @reason = FORMATMESSAGE(14572) RAISERROR(14278, -1, -1, @reason) RETURN(1) -- Failure END END IF (@freq_type = 0x8) -- FREQTYPE_WEEKLY BEGIN IF (@freq_interval < 1) OR (@freq_interval > 127) -- (2^7)-1 [freq_interval is a bitmap (Sun=1..Sat=64)] BEGIN SELECT @reason = FORMATMESSAGE(14573) RAISERROR(14278, -1, -1, @reason) RETURN(1) -- Failure END END IF (@freq_type = 0x10) -- FREQTYPE_MONTHLY BEGIN IF (@freq_interval < 1) OR (@freq_interval > 31) BEGIN SELECT @reason = FORMATMESSAGE(14574) RAISERROR(14278, -1, -1, @reason) RETURN(1) -- Failure END END IF (@freq_type = 0x20) -- FREQTYPE_MONTHLYRELATIVE BEGIN IF (@freq_relative_interval <> 0x01) AND -- RELINT_1ST (@freq_relative_interval <> 0x02) AND -- RELINT_2ND (@freq_relative_interval <> 0x04) AND -- RELINT_3RD (@freq_relative_interval <> 0x08) AND -- RELINT_4TH (@freq_relative_interval <> 0x10) -- RELINT_LAST BEGIN SELECT @reason = FORMATMESSAGE(14575) RAISERROR(14278, -1, -1, @reason) RETURN(1) -- Failure END END IF (@freq_type = 0x20) -- FREQTYPE_MONTHLYRELATIVE BEGIN IF (@freq_interval <> 01) AND -- RELATIVE_SUN (@freq_interval <> 02) AND -- RELATIVE_MON (@freq_interval <> 03) AND -- RELATIVE_TUE (@freq_interval <> 04) AND -- RELATIVE_WED (@freq_interval <> 05) AND -- RELATIVE_THU (@freq_interval <> 06) AND -- RELATIVE_FRI (@freq_interval <> 07) AND -- RELATIVE_SAT (@freq_interval <> 08) AND -- RELATIVE_DAY (@freq_interval <> 09) AND -- RELATIVE_WEEKDAY (@freq_interval <> 10) -- RELATIVE_WEEKENDDAY BEGIN SELECT @reason = FORMATMESSAGE(14576) RAISERROR(14278, -1, -1, @reason) RETURN(1) -- Failure END END IF ((@freq_type = 0x08) OR -- FREQTYPE_WEEKLY (@freq_type = 0x10) OR -- FREQTYPE_MONTHLY (@freq_type = 0x20)) AND -- FREQTYPE_MONTHLYRELATIVE (@freq_recurrence_factor < 1) BEGIN SELECT @reason = FORMATMESSAGE(14577) RAISERROR(14278, -1, -1, @reason) RETURN(1) -- Failure END ExitProc: -- If we made it this far the schedule is good RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_SCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_schedule...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_schedule') AND (type = 'P'))) DROP PROCEDURE sp_add_schedule go CREATE PROCEDURE sp_add_schedule ( @schedule_name sysname, @enabled TINYINT = 1, -- Name does not have to be unique @freq_type INT = 0, @freq_interval INT = 0, @freq_subday_type INT = 0, @freq_subday_interval INT = 0, @freq_relative_interval INT = 0, @freq_recurrence_factor INT = 0, @active_start_date INT = NULL, -- sp_verify_schedule assigns a default @active_end_date INT = 99991231, -- December 31st 9999 @active_start_time INT = 000000, -- 12:00:00 am @active_end_time INT = 235959, -- 11:59:59 pm @owner_login_name sysname = NULL, @schedule_uid UNIQUEIDENTIFIER= NULL OUTPUT, -- Used by a TSX machine when inserting a schedule @schedule_id INT = NULL OUTPUT, @originating_server sysname = NULL ) AS BEGIN DECLARE @retval INT DECLARE @owner_sid VARBINARY(85) DECLARE @orig_server_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @schedule_name = LTRIM(RTRIM(@schedule_name)), @owner_login_name = LTRIM(RTRIM(@owner_login_name)), @originating_server = UPPER(LTRIM(RTRIM(@originating_server))), @schedule_id = 0 -- If the owner isn't supplied make if the current user IF(@owner_login_name IS NULL OR @owner_login_name = '') BEGIN --Get the current users sid SELECT @owner_sid = SUSER_SID() END ELSE BEGIN -- Get the sid for @owner_login_name SID --force case insensitive comparation for NT users SELECT @owner_sid = dbo.SQLAGENT_SUSER_SID(@owner_login_name) -- Cannot proceed if @owner_login_name doesn't exist IF(@owner_sid IS NULL) BEGIN RAISERROR(14262, -1, -1, '@owner_login_name', @owner_login_name) RETURN(1) -- Failure END END -- Check schedule (frequency and owner) parameters EXECUTE @retval = sp_verify_schedule NULL, -- schedule_id does not exist for the new schedule @name = @schedule_name, @enabled = @enabled, @freq_type = @freq_type, @freq_interval = @freq_interval OUTPUT, @freq_subday_type = @freq_subday_type OUTPUT, @freq_subday_interval = @freq_subday_interval OUTPUT, @freq_relative_interval = @freq_relative_interval OUTPUT, @freq_recurrence_factor = @freq_recurrence_factor OUTPUT, @active_start_date = @active_start_date OUTPUT, @active_start_time = @active_start_time OUTPUT, @active_end_date = @active_end_date OUTPUT, @active_end_time = @active_end_time OUTPUT, @owner_sid = @owner_sid IF (@retval <> 0) RETURN(1) -- Failure -- ignore @originating_server unless SQLAgent is calling if((@originating_server IS NULL) OR (@originating_server = N'') OR (PROGRAM_NAME() NOT LIKE N'SQLAgent%')) BEGIN --Get the local originating_server_id SELECT @orig_server_id = originating_server_id FROM msdb.dbo.sysoriginatingservers_view WHERE master_server = 0 END ELSE BEGIN --Get the MSX originating_server_id. If @originating_server isn't the msx server error out SELECT @orig_server_id = originating_server_id FROM msdb.dbo.sysoriginatingservers_view WHERE (originating_server = @originating_server) IF (@orig_server_id IS NULL) BEGIN RAISERROR(14370, -1, -1) RETURN(1) -- Failure END END IF (@schedule_uid IS NULL) BEGIN -- Assign the GUID SELECT @schedule_uid = NEWID() END ELSE IF (@schedule_uid <> CONVERT(UNIQUEIDENTIFIER, 0x00)) BEGIN --Try and find the schedule if a @schedule_uid is provided. --A TSX server uses the @schedule_uid to identify a schedule downloaded from the MSX SELECT @schedule_id = schedule_id FROM msdb.dbo.sysschedules WHERE schedule_uid = @schedule_uid IF((@schedule_id IS NOT NULL) AND (@schedule_id <> 0)) BEGIN --If found update the fields UPDATE msdb.dbo.sysschedules SET name = ISNULL(@schedule_name, name), enabled = ISNULL(@enabled, enabled), freq_type = ISNULL(@freq_type, freq_type), freq_interval = ISNULL(@freq_interval, freq_interval), freq_subday_type = ISNULL(@freq_subday_type, freq_subday_type), freq_subday_interval = ISNULL(@freq_subday_interval, freq_subday_interval), freq_relative_interval = ISNULL(@freq_relative_interval, freq_relative_interval), freq_recurrence_factor = ISNULL(@freq_recurrence_factor, freq_recurrence_factor), active_start_date = ISNULL(@active_start_date, active_start_date), active_end_date = ISNULL(@active_end_date, active_end_date), active_start_time = ISNULL(@active_start_time, active_start_time), active_end_time = ISNULL(@active_end_time, active_end_time) WHERE schedule_uid = @schedule_uid RETURN(@@ERROR) END END --MSX not found so add a record to sysschedules INSERT INTO msdb.dbo.sysschedules (schedule_uid, originating_server_id, name, owner_sid, enabled, freq_type, freq_interval, freq_subday_type, freq_subday_interval, freq_relative_interval, freq_recurrence_factor, active_start_date, active_end_date, active_start_time, active_end_time) select @schedule_uid, @orig_server_id, @schedule_name, @owner_sid, @enabled, @freq_type, @freq_interval, @freq_subday_type, @freq_subday_interval, @freq_relative_interval, @freq_recurrence_factor, @active_start_date, @active_end_date, @active_start_time, @active_end_time SELECT @retval = @@ERROR, @schedule_id = @@IDENTITY RETURN(@retval) -- 0 means success END GO /**************************************************************/ /* SP_ATTACH_SCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_attach_schedule ...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_attach_schedule') AND (type = 'P'))) DROP PROCEDURE sp_attach_schedule go CREATE PROCEDURE sp_attach_schedule ( @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @schedule_id INT = NULL, -- Must provide either this or schedule_name @schedule_name sysname = NULL, -- Must provide either this or schedule_id @automatic_post BIT = 1 -- If 1 will post notifications to all tsx servers to that run this job ) AS BEGIN DECLARE @retval INT DECLARE @sched_owner_sid VARBINARY(85) DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Check that we can uniquely identify the job EXECUTE @retval = msdb.dbo.sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check authority (only SQLServerAgent can add a schedule to a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- Check that we can uniquely identify the schedule EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = @sched_owner_sid OUTPUT, @orig_server_id = NULL IF (@retval <> 0) RETURN(1) -- Failure --Schedules can only be attached to a job if the job and schedule have the --same owner or the caller is a sysadmin IF ((@sched_owner_sid <> @job_owner_sid) AND ((@sched_owner_sid <> SUSER_SID()) OR (@job_owner_sid <> SUSER_SID())) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(14377, -1, -1) RETURN(1) -- Failure END -- If the record doesn't already exist create it IF( NOT EXISTS(SELECT * FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) AND (job_id = @job_id)) ) BEGIN INSERT INTO msdb.dbo.sysjobschedules (schedule_id, job_id) SELECT @schedule_id, @job_id SELECT @retval = @@ERROR -- Notify SQLServerAgent of the change, but only if we know the job has been cached IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'S', @job_id = @job_id, @schedule_id = @schedule_id, @action_type = N'I' END -- For a multi-server job, remind the user that they need to call sp_post_msx_operation IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines IF (@automatic_post = 1) EXECUTE sp_post_msx_operation @operation = 'INSERT', @object_type = 'JOB', @job_id = @job_id ELSE RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation') -- update this job's subplan to point to this schedule UPDATE msdb.dbo.sysmaintplan_subplans SET schedule_id = @schedule_id WHERE (job_id = @job_id) AND (schedule_id IS NULL) END RETURN(@retval) -- 0 means success END GO /**************************************************************/ /* SP_DETACH_SCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_detach_schedule ...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_detach_schedule') AND (type = 'P'))) DROP PROCEDURE sp_detach_schedule go CREATE PROCEDURE sp_detach_schedule ( @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @schedule_id INT = NULL, -- Must provide either this or schedule_name @schedule_name sysname = NULL, -- Must provide either this or schedule_id @delete_unused_schedule BIT = 0, -- Can optionally delete schedule if it isn't referenced. -- The default is to keep schedules @automatic_post BIT = 1 -- If 1 will post notifications to all tsx servers to that run this job ) AS BEGIN DECLARE @retval INT DECLARE @sched_owner_sid VARBINARY(85) DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Check that we can uniquely identify the job EXECUTE @retval = msdb.dbo.sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check authority (only SQLServerAgent can add a schedule to a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- Check that we can uniquely identify the schedule EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = @sched_owner_sid OUTPUT, @orig_server_id = NULL, @job_id_filter = @job_id IF (@retval <> 0) RETURN(1) -- Failure -- If the record doesn't exist raise an error IF( NOT EXISTS(SELECT * FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) AND (job_id = @job_id)) ) BEGIN RAISERROR(14374, 0, 1, @schedule_name, @job_name) RETURN(1) -- Failure END ELSE BEGIN -- Only sysadmin can detach schedules from jobs they do not own IF (((@sched_owner_sid <> SUSER_SID()) OR (@job_owner_sid <> SUSER_SID())) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(14391, -1, -1) RETURN(1) -- Failure END DELETE FROM msdb.dbo.sysjobschedules WHERE (job_id = @job_id) AND (schedule_id = @schedule_id) SELECT @retval = @@ERROR --delete the schedule if requested and it isn't referenced IF(@retval = 0 AND @delete_unused_schedule = 1) BEGIN IF(NOT EXISTS(SELECT * FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id))) BEGIN DELETE FROM msdb.dbo.sysschedules WHERE (schedule_id = @schedule_id) END END -- Update the job's version/last-modified information UPDATE msdb.dbo.sysjobs SET version_number = version_number + 1, date_modified = GETDATE() WHERE (job_id = @job_id) -- Notify SQLServerAgent of the change, but only if we know the job has been cached IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'S', @job_id = @job_id, @schedule_id = @schedule_id, @action_type = N'D' END -- For a multi-server job, remind the user that they need to call sp_post_msx_operation IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines IF (@automatic_post = 1) EXECUTE sp_post_msx_operation @operation = 'INSERT', @object_type = 'JOB', @job_id = @job_id ELSE RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation') -- set this job's subplan to the first schedule in sysjobschedules or NULL if there is none UPDATE msdb.dbo.sysmaintplan_subplans SET schedule_id = ( SELECT TOP(1) schedule_id FROM msdb.dbo.sysjobschedules WHERE (job_id = @job_id) ) WHERE (job_id = @job_id) AND (schedule_id = @schedule_id) END RETURN(@retval) -- 0 means success END GO /**************************************************************/ /* SP_UPDATE_REPLICATION_JOB_PARAMETER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_replication_job_parameter...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_update_replication_job_parameter') AND (type = 'P'))) DROP PROCEDURE sp_update_replication_job_parameter go CREATE PROCEDURE sp_update_replication_job_parameter @job_id UNIQUEIDENTIFIER, @old_freq_type INT, @new_freq_type INT AS BEGIN DECLARE @category_id INT DECLARE @pattern NVARCHAR(50) DECLARE @patternidx INT DECLARE @cmdline NVARCHAR(3200) DECLARE @step_id INT SET NOCOUNT ON SELECT @pattern = N'%[-/][Cc][Oo][Nn][Tt][Ii][Nn][Uu][Oo][Uu][Ss]%' -- Make sure that we are dealing with relevant replication jobs SELECT @category_id = category_id FROM msdb.dbo.sysjobs WHERE (@job_id = job_id) -- @category_id = 10 (REPL-Distribution), 13 (REPL-LogReader), 14 (REPL-Merge), -- 19 (REPL-QueueReader) IF @category_id IN (10, 13, 14, 19) BEGIN -- Adding the -Continuous parameter (non auto-start to auto-start) IF ((@old_freq_type <> 0x40) AND (@new_freq_type = 0x40)) BEGIN -- Use a cursor to handle multiple replication agent job steps DECLARE step_cursor CURSOR LOCAL FOR SELECT command, step_id FROM msdb.dbo.sysjobsteps WHERE (@job_id = job_id) AND (UPPER(subsystem collate SQL_Latin1_General_CP1_CS_AS) IN (N'MERGE', N'LOGREADER', N'DISTRIBUTION', N'QUEUEREADER')) OPEN step_cursor FETCH step_cursor INTO @cmdline, @step_id WHILE (@@FETCH_STATUS <> -1) BEGIN SELECT @patternidx = PATINDEX(@pattern, @cmdline) -- Make sure that the -Continuous parameter has not been specified already IF (@patternidx = 0) BEGIN SELECT @cmdline = @cmdline + N' -Continuous' UPDATE msdb.dbo.sysjobsteps SET command = @cmdline WHERE (@job_id = job_id) AND (@step_id = step_id) END -- IF (@patternidx = 0) FETCH NEXT FROM step_cursor into @cmdline, @step_id END -- WHILE (@@FETCH_STATUS <> -1) CLOSE step_cursor DEALLOCATE step_cursor END -- IF ((@old_freq_type... -- Removing the -Continuous parameter (auto-start to non auto-start) ELSE IF ((@old_freq_type = 0x40) AND (@new_freq_type <> 0x40)) BEGIN DECLARE step_cursor CURSOR LOCAL FOR SELECT command, step_id FROM msdb.dbo.sysjobsteps WHERE (@job_id = job_id) AND (UPPER(subsystem collate SQL_Latin1_General_CP1_CS_AS) IN (N'MERGE', N'LOGREADER', N'DISTRIBUTION', N'QUEUEREADER')) OPEN step_cursor FETCH step_cursor INTO @cmdline, @step_id WHILE (@@FETCH_STATUS <> -1) BEGIN SELECT @patternidx = PATINDEX(@pattern, @cmdline) IF (@patternidx <> 0) BEGIN -- Handle multiple instances of -Continuous in the commandline WHILE (@patternidx <> 0) BEGIN SELECT @cmdline = STUFF(@cmdline, @patternidx, 11, N'') IF (@patternidx > 1) BEGIN -- Remove the preceding space if -Continuous does not start at the beginning of the commandline SELECT @cmdline = stuff(@cmdline, @patternidx - 1, 1, N'') END SELECT @patternidx = PATINDEX(@pattern, @cmdline) END -- WHILE (@patternidx <> 0) UPDATE msdb.dbo.sysjobsteps SET command = @cmdline WHERE (@job_id = job_id) AND (@step_id = step_id) END -- IF (@patternidx <> -1) FETCH NEXT FROM step_cursor INTO @cmdline, @step_id END -- WHILE (@@FETCH_STATUS <> -1) CLOSE step_cursor DEALLOCATE step_cursor END -- ELSE IF ((@old_freq_type = 0x40)... END -- IF @category_id IN (10, 13, 14) RETURN 0 END go /**************************************************************/ /* SP_UPDATE_SCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_schedule ...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_schedule') AND (type = 'P'))) DROP PROCEDURE sp_update_schedule go CREATE PROCEDURE sp_update_schedule ( @schedule_id INT = NULL, -- Must provide either this or schedule_name @name sysname = NULL, -- Must provide either this or schedule_id @new_name sysname = NULL, @enabled TINYINT = NULL, @freq_type INT = NULL, @freq_interval INT = NULL, @freq_subday_type INT = NULL, @freq_subday_interval INT = NULL, @freq_relative_interval INT = NULL, @freq_recurrence_factor INT = NULL, @active_start_date INT = NULL, @active_end_date INT = NULL, @active_start_time INT = NULL, @active_end_time INT = NULL, @owner_login_name sysname = NULL, @automatic_post BIT = 1 -- If 1 will post notifications to all tsx servers to -- update all jobs that use this schedule ) AS BEGIN DECLARE @retval INT DECLARE @owner_sid VARBINARY(85) DECLARE @cur_owner_sid VARBINARY(85) DECLARE @x_name sysname DECLARE @enable_only_used INT DECLARE @x_enabled TINYINT DECLARE @x_freq_type INT DECLARE @x_freq_interval INT DECLARE @x_freq_subday_type INT DECLARE @x_freq_subday_interval INT DECLARE @x_freq_relative_interval INT DECLARE @x_freq_recurrence_factor INT DECLARE @x_active_start_date INT DECLARE @x_active_end_date INT DECLARE @x_active_start_time INT DECLARE @x_active_end_time INT DECLARE @schedule_uid UNIQUEIDENTIFIER SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @new_name = LTRIM(RTRIM(@new_name)) SELECT @owner_login_name = LTRIM(RTRIM(@owner_login_name)) -- Turn [nullable] empty string parameters into NULLs IF (@new_name = N'') SELECT @new_name = NULL -- If the owner is supplied get the sid and check it IF(@owner_login_name IS NOT NULL AND @owner_login_name <> '') BEGIN -- Get the sid for @owner_login_name SID --force case insensitive comparation for NT users SELECT @owner_sid = dbo.SQLAGENT_SUSER_SID(@owner_login_name) -- Cannot proceed if @owner_login_name doesn't exist IF(@owner_sid IS NULL) BEGIN RAISERROR(14262, -1, -1, '@owner_login_name', @owner_login_name) RETURN(1) -- Failure END END -- Check that we can uniquely identify the schedule. This only returns a schedule that is visible to this user EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@name', @name_of_id_parameter = '@schedule_id', @schedule_name = @name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = @cur_owner_sid OUTPUT, @orig_server_id = NULL IF (@retval <> 0) RETURN(1) -- Failure -- Is @enable the only parameter used beside jobname and jobid? IF ((@enabled IS NOT NULL) AND (@new_name IS NULL) AND (@freq_type IS NULL) AND (@freq_interval IS NULL) AND (@freq_subday_type IS NULL) AND (@freq_subday_interval IS NULL) AND (@freq_relative_interval IS NULL) AND (@freq_recurrence_factor IS NULL) AND (@active_start_date IS NULL) AND (@active_end_date IS NULL) AND (@active_start_time IS NULL) AND (@active_end_time IS NULL) AND (@owner_login_name IS NULL)) SELECT @enable_only_used = 1 ELSE SELECT @enable_only_used = 0 -- Non-sysadmins can only update jobs schedules they own. -- Members of SQLAgentReaderRole and SQLAgentOperatorRole can view job schedules, -- but they should not be able to delete them IF ((@cur_owner_sid <> SUSER_SID()) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'),0) <> 1) AND (@enable_only_used <> 1 OR ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) <> 1)) BEGIN RAISERROR(14394, -1, -1) RETURN(1) -- Failure END -- If the param @owner_login_name is null or doesn't get resolved by SUSER_SID() set it to the current owner of the schedule if(@owner_sid IS NULL) SELECT @owner_sid = @cur_owner_sid -- Set the x_ (existing) variables SELECT @x_name = name, @x_enabled = enabled, @x_freq_type = freq_type, @x_freq_interval = freq_interval, @x_freq_subday_type = freq_subday_type, @x_freq_subday_interval = freq_subday_interval, @x_freq_relative_interval = freq_relative_interval, @x_freq_recurrence_factor = freq_recurrence_factor, @x_active_start_date = active_start_date, @x_active_end_date = active_end_date, @x_active_start_time = active_start_time, @x_active_end_time = active_end_time FROM msdb.dbo.sysschedules WHERE (schedule_id = @schedule_id ) -- Fill out the values for all non-supplied parameters from the existing values IF (@new_name IS NULL) SELECT @new_name = @x_name IF (@enabled IS NULL) SELECT @enabled = @x_enabled IF (@freq_type IS NULL) SELECT @freq_type = @x_freq_type IF (@freq_interval IS NULL) SELECT @freq_interval = @x_freq_interval IF (@freq_subday_type IS NULL) SELECT @freq_subday_type = @x_freq_subday_type IF (@freq_subday_interval IS NULL) SELECT @freq_subday_interval = @x_freq_subday_interval IF (@freq_relative_interval IS NULL) SELECT @freq_relative_interval = @x_freq_relative_interval IF (@freq_recurrence_factor IS NULL) SELECT @freq_recurrence_factor = @x_freq_recurrence_factor IF (@active_start_date IS NULL) SELECT @active_start_date = @x_active_start_date IF (@active_end_date IS NULL) SELECT @active_end_date = @x_active_end_date IF (@active_start_time IS NULL) SELECT @active_start_time = @x_active_start_time IF (@active_end_time IS NULL) SELECT @active_end_time = @x_active_end_time -- Check schedule (frequency and owner) parameters EXECUTE @retval = sp_verify_schedule @schedule_id = @schedule_id, @name = @new_name, @enabled = @enabled, @freq_type = @freq_type, @freq_interval = @freq_interval OUTPUT, @freq_subday_type = @freq_subday_type OUTPUT, @freq_subday_interval = @freq_subday_interval OUTPUT, @freq_relative_interval = @freq_relative_interval OUTPUT, @freq_recurrence_factor = @freq_recurrence_factor OUTPUT, @active_start_date = @active_start_date OUTPUT, @active_start_time = @active_start_time OUTPUT, @active_end_date = @active_end_date OUTPUT, @active_end_time = @active_end_time OUTPUT, @owner_sid = @owner_sid IF (@retval <> 0) RETURN(1) -- Failure -- Update the sysschedules table UPDATE msdb.dbo.sysschedules SET name = @new_name, owner_sid = @owner_sid, enabled = @enabled, freq_type = @freq_type, freq_interval = @freq_interval, freq_subday_type = @freq_subday_type, freq_subday_interval = @freq_subday_interval, freq_relative_interval = @freq_relative_interval, freq_recurrence_factor = @freq_recurrence_factor, active_start_date = @active_start_date, active_end_date = @active_end_date, active_start_time = @active_start_time, active_end_time = @active_end_time, date_modified = GETDATE(), version_number = version_number + 1 WHERE (schedule_id = @schedule_id) SELECT @retval = @@error -- update any job that has repl steps DECLARE @job_id UNIQUEIDENTIFIER DECLARE jobsschedule_cursor CURSOR LOCAL FOR SELECT job_id FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) IF @x_freq_type <> @freq_type BEGIN OPEN jobsschedule_cursor FETCH NEXT FROM jobsschedule_cursor INTO @job_id WHILE (@@FETCH_STATUS = 0) BEGIN EXEC sp_update_replication_job_parameter @job_id = @job_id, @old_freq_type = @x_freq_type, @new_freq_type = @freq_type FETCH NEXT FROM jobsschedule_cursor INTO @job_id END CLOSE jobsschedule_cursor END DEALLOCATE jobsschedule_cursor -- Notify SQLServerAgent of the change if this is attached to a local job IF (EXISTS (SELECT * FROM msdb.dbo.sysjobschedules AS jsched JOIN msdb.dbo.sysjobservers AS jsvr ON jsched.job_id = jsvr.job_id WHERE (jsched.schedule_id = @schedule_id) AND (jsvr.server_id = 0)) ) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'S', @schedule_id = @schedule_id, @action_type = N'U' END -- Instruct the tsx servers to pick up the altered schedule IF (@automatic_post = 1) BEGIN SELECT @schedule_uid = schedule_uid FROM sysschedules WHERE schedule_id = @schedule_id IF(NOT @schedule_uid IS NULL) BEGIN -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines EXECUTE @retval = sp_post_msx_operation @operation = 'INSERT', @object_type = 'SCHEDULE', @schedule_uid = @schedule_uid END END RETURN(@retval) -- 0 means success END GO /**************************************************************/ /* SP_DELETE_SCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_schedule ...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_schedule') AND (type = 'P'))) DROP PROCEDURE sp_delete_schedule go CREATE PROCEDURE sp_delete_schedule ( @schedule_id INT = NULL, -- Must provide either this or schedule_name @schedule_name sysname = NULL, -- Must provide either this or schedule_id @force_delete bit = 0, @automatic_post BIT = 1 -- If 1 will post notifications to all tsx servers to that run this schedule ) AS BEGIN DECLARE @retval INT DECLARE @owner_sid VARBINARY(85) DECLARE @job_count INT DECLARE @targ_server_id INT SET NOCOUNT ON --Get the owners sid SELECT @job_count = 0 -- Check that we can uniquely identify the schedule. This only returns a schedule that is visible to this user EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = @owner_sid OUTPUT, @orig_server_id = NULL IF (@retval <> 0) RETURN(1) -- Failure -- Non-sysadmins can only update jobs schedules they own. -- Members of SQLAgentReaderRole and SQLAgentOperatorRole can view job schedules, -- but they should not be able to delete them IF ((@owner_sid <> SUSER_SID()) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'),0) <> 1)) BEGIN RAISERROR(14394, -1, -1) RETURN(1) -- Failure END --check if there are jobs using this schedule SELECT @job_count = count(*) FROM sysjobschedules WHERE (schedule_id = @schedule_id) -- If we aren't force deleting the schedule make sure no jobs are using it IF ((@force_delete = 0) AND (@job_count > 0)) BEGIN RAISERROR(14372, -1, -1) RETURN (1) -- Failure END -- Get the one of the terget server_id's. -- Getting MIN(jsvr.server_id) works here because we are only interested in this ID -- to determine if the schedule ID is for local jobs or MSX jobs. -- Note, an MSX job can't be run on the local server SELECT @targ_server_id = MIN(jsvr.server_id) FROM msdb.dbo.sysjobschedules AS jsched JOIN msdb.dbo.sysjobservers AS jsvr ON jsched.job_id = jsvr.job_id WHERE (jsched.schedule_id = @schedule_id) --OK to delete the job - schedule link DELETE sysjobschedules WHERE schedule_id = @schedule_id --OK to delete the schedule DELETE sysschedules WHERE schedule_id = @schedule_id -- @targ_server_id would be null if no jobs use this schedule IF (@targ_server_id IS NOT NULL) BEGIN -- Notify SQLServerAgent of the change but only if it the schedule was used by a local job IF (@targ_server_id = 0) BEGIN -- Only send a notification if the schedule is force deleted. If it isn't force deleted -- a notification would have already been sent while detaching the schedule (sp_detach_schedule) IF (@force_delete = 1) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'S', @schedule_id = @schedule_id, @action_type = N'D' END END ELSE BEGIN -- Instruct the tsx servers to pick up the altered schedule IF (@automatic_post = 1) BEGIN DECLARE @schedule_uid UNIQUEIDENTIFIER SELECT @schedule_uid = schedule_uid FROM sysschedules WHERE schedule_id = @schedule_id IF(NOT @schedule_uid IS NULL) BEGIN -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines EXECUTE sp_post_msx_operation @operation = 'INSERT', @object_type = 'SCHEDULE', @schedule_uid = @schedule_uid END END ELSE RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation') END END RETURN(@retval) -- 0 means success END GO /**************************************************************/ /* SP_GET_JOBSTEP_DB_USERNAME */ /* */ /* NOTE: For NT login names this procedure can take several */ /* seconds to return as it hits the PDC/BDC. */ /* SQLServerAgent calls this at runtime. */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_jobstep_db_username...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_get_jobstep_db_username') AND (type = 'P'))) DROP PROCEDURE sp_get_jobstep_db_username go CREATE PROCEDURE sp_get_jobstep_db_username @database_name sysname, @login_name sysname = NULL, @username_in_targetdb sysname OUTPUT AS BEGIN DECLARE @suser_sid_clause NVARCHAR(512) -- Check the database name IF (DB_ID(@database_name) IS NULL) BEGIN RAISERROR(14262, 16, 1, 'database', @database_name) RETURN(1) -- Failure END -- Initialize return value SELECT @username_in_targetdb = NULL -- Make sure login name is never NULL IF (@login_name IS NULL) SELECT @login_name = SUSER_SNAME() IF (@login_name IS NULL) RETURN(1) -- Failure -- Handle an NT login name IF (@login_name LIKE N'%\%') BEGIN -- Special case... IF (UPPER(@login_name collate SQL_Latin1_General_CP1_CS_AS) = N'NT AUTHORITY\SYSTEM') SELECT @username_in_targetdb = N'dbo' ELSE SELECT @username_in_targetdb = @login_name RETURN(0) -- Success END -- Handle a SQL login name SELECT @suser_sid_clause = N'SUSER_SID(N' + QUOTENAME(@login_name, '''') + N')' IF (SUSER_SID(@login_name) IS NULL) RETURN(1) -- Failure DECLARE @quoted_database_name NVARCHAR(258) SELECT @quoted_database_name = QUOTENAME(@database_name, N'[') DECLARE @temp_username TABLE (user_name sysname COLLATE database_default NOT NULL, is_aliased BIT) -- 1) Look for the user name of the current login in the target database INSERT INTO @temp_username EXECUTE (N'SET NOCOUNT ON SELECT name, isaliased FROM '+ @quoted_database_name + N'.[dbo].[sysusers] WHERE (sid = ' + @suser_sid_clause + N') AND (hasdbaccess = 1)') -- 2) Look for the alias user name of the current login in the target database IF (EXISTS (SELECT * FROM @temp_username WHERE (is_aliased = 1))) BEGIN DELETE FROM @temp_username INSERT INTO @temp_username EXECUTE (N'SET NOCOUNT ON SELECT name, 0 FROM '+ @quoted_database_name + N'.[dbo].[sysusers] WHERE uid = (SELECT altuid FROM ' + @quoted_database_name + N'.[dbo].[sysusers] WHERE (sid = ' + @suser_sid_clause + N')) AND (hasdbaccess = 1)') END -- 3) Look for the guest user name in the target database IF (NOT EXISTS (SELECT * FROM @temp_username)) INSERT INTO @temp_username EXECUTE (N'SET NOCOUNT ON SELECT name, 0 FROM '+ @quoted_database_name + N'.[dbo].[sysusers] WHERE (name = N''guest'') AND (hasdbaccess = 1)') SELECT @username_in_targetdb = user_name FROM @temp_username RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_JOBSTEP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_jobstep...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_jobstep') AND (type = 'P'))) DROP PROCEDURE sp_verify_jobstep go CREATE PROCEDURE sp_verify_jobstep @job_id UNIQUEIDENTIFIER, @step_id INT, @step_name sysname, @subsystem NVARCHAR(40), @command NVARCHAR(max), @server sysname, @on_success_action TINYINT, @on_success_step_id INT, @on_fail_action TINYINT, @on_fail_step_id INT, @os_run_priority INT, @database_name sysname OUTPUT, @database_user_name sysname OUTPUT, @flags INT, @output_file_name NVARCHAR(200), @proxy_id INT AS BEGIN DECLARE @max_step_id INT DECLARE @retval INT DECLARE @valid_values VARCHAR(50) DECLARE @database_name_temp NVARCHAR(258) DECLARE @database_user_name_temp NVARCHAR(256) DECLARE @temp_command NVARCHAR(max) DECLARE @iPos INT DECLARE @create_count INT DECLARE @destroy_count INT DECLARE @is_olap_subsystem BIT DECLARE @owner_sid VARBINARY(85) DECLARE @owner_name sysname -- Remove any leading/trailing spaces from parameters SELECT @subsystem = LTRIM(RTRIM(@subsystem)) SELECT @server = LTRIM(RTRIM(@server)) SELECT @output_file_name = LTRIM(RTRIM(@output_file_name)) -- Get current maximum step id SELECT @max_step_id = ISNULL(MAX(step_id), 0) FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) -- Check step id IF (@step_id < 1) OR (@step_id > @max_step_id + 1) BEGIN SELECT @valid_values = '1..' + CONVERT(VARCHAR, @max_step_id + 1) RAISERROR(14266, -1, -1, '@step_id', @valid_values) RETURN(1) -- Failure END -- Check subsystem EXECUTE @retval = sp_verify_subsystem @subsystem IF (@retval <> 0) RETURN(1) -- Failure --check if proxy is allowed for this subsystem for current user IF (@proxy_id IS NOT NULL) BEGIN --get the job owner SELECT @owner_sid = owner_sid FROM sysjobs WHERE job_id = @job_id IF @owner_sid = 0xFFFFFFFF BEGIN --ask to verify for the special account EXECUTE @retval = sp_verify_proxy_permissions @subsystem_name = @subsystem, @proxy_id = @proxy_id, @name = NULL, @raise_error = 1, @allow_disable_proxy = 1, @verify_special_account = 1 IF (@retval <> 0) RETURN(1) -- Failure END ELSE BEGIN SELECT @owner_name = SUSER_SNAME(@owner_sid) EXECUTE @retval = sp_verify_proxy_permissions @subsystem_name = @subsystem, @proxy_id = @proxy_id, @name = @owner_name, @raise_error = 1, @allow_disable_proxy = 1 IF (@retval <> 0) RETURN(1) -- Failure END END --Only sysadmin can specify @output_file_name IF (@output_file_name IS NOT NULL) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(14582, -1, -1) RETURN(1) -- Failure END --Determmine if this is a olap subsystem jobstep IF ( UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) in (N'ANALYSISQUERY', N'ANALYSISCOMMAND') ) SELECT @is_olap_subsystem = 1 ELSE SELECT @is_olap_subsystem = 0 -- Check command length -- not necessary now, command can be any length /* IF ((DATALENGTH(@command) / 2) > 3200) BEGIN RAISERROR(14250, 16, 1, '@command', 3200) RETURN(1) -- Failure END */ -- For a VBScript command, check that object creations are paired with object destructions IF ((UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'ACTIVESCRIPTING') AND (@database_name = N'VBScript')) BEGIN SET @temp_command = @command SELECT @create_count = 0 SELECT @iPos = PATINDEX('%[Cc]reate[Oo]bject[ (]%', @temp_command) WHILE(@iPos > 0) BEGIN SELECT @temp_command = SUBSTRING(@temp_command, @iPos + 1, DATALENGTH(@temp_command) / 2) SELECT @iPos = PATINDEX('%[Cc]reate[Oo]bject[ (]%', @temp_command) SELECT @create_count = @create_count + 1 END -- restore @temp_command for next loop SET @temp_command = @command SELECT @destroy_count = 0 SELECT @iPos = PATINDEX('%[Ss]et %=%[Nn]othing%', @temp_command) WHILE(@iPos > 0) BEGIN SELECT @temp_command = SUBSTRING(@temp_command, @iPos + 1, DATALENGTH(@temp_command) / 2) SELECT @iPos = PATINDEX('%[Ss]et %=%[Nn]othing%', @temp_command) SELECT @destroy_count = @destroy_count + 1 END IF(@create_count > @destroy_count) BEGIN RAISERROR(14277, -1, -1) RETURN(1) -- Failure END END -- Check step name IF (EXISTS (SELECT * FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_name = @step_name))) BEGIN RAISERROR(14261, -1, -1, '@step_name', @step_name) RETURN(1) -- Failure END -- Check on-success action/step IF (@on_success_action <> 1) AND -- Quit Qith Success (@on_success_action <> 2) AND -- Quit Qith Failure (@on_success_action <> 3) AND -- Goto Next Step (@on_success_action <> 4) -- Goto Step BEGIN RAISERROR(14266, -1, -1, '@on_success_action', '1, 2, 3, 4') RETURN(1) -- Failure END IF (@on_success_action = 4) AND ((@on_success_step_id < 1) OR (@on_success_step_id = @step_id)) BEGIN -- NOTE: We allow forward references to non-existant steps to prevent the user from -- having to make a second update pass to fix up the flow RAISERROR(14235, -1, -1, '@on_success_step', @step_id) RETURN(1) -- Failure END -- Check on-fail action/step IF (@on_fail_action <> 1) AND -- Quit With Success (@on_fail_action <> 2) AND -- Quit With Failure (@on_fail_action <> 3) AND -- Goto Next Step (@on_fail_action <> 4) -- Goto Step BEGIN RAISERROR(14266, -1, -1, '@on_failure_action', '1, 2, 3, 4') RETURN(1) -- Failure END IF (@on_fail_action = 4) AND ((@on_fail_step_id < 1) OR (@on_fail_step_id = @step_id)) BEGIN -- NOTE: We allow forward references to non-existant steps to prevent the user from -- having to make a second update pass to fix up the flow RAISERROR(14235, -1, -1, '@on_failure_step', @step_id) RETURN(1) -- Failure END -- Warn the user about forward references IF ((@on_success_action = 4) AND (@on_success_step_id > @max_step_id)) RAISERROR(14236, 0, 1, '@on_success_step_id') IF ((@on_fail_action = 4) AND (@on_fail_step_id > @max_step_id)) RAISERROR(14236, 0, 1, '@on_fail_step_id') --Special case the olap subsystem. It can have any server name. --Default it to the local server if @server is null IF(@is_olap_subsystem = 1) BEGIN IF(@server IS NULL) BEGIN --TODO: needs error better message ? >> 'Specify the OLAP server name in the %s parameter' --Must specify the olap server name RAISERROR(14262, -1, -1, '@server', @server) RETURN(1) -- Failure END END ELSE BEGIN -- Check server (this is the replication server, NOT the job-target server) IF (@server IS NOT NULL) AND (NOT EXISTS (SELECT * FROM master.dbo.sysservers WHERE (UPPER(srvname) = UPPER(@server)))) BEGIN RAISERROR(14234, -1, -1, '@server', 'sp_helpserver') RETURN(1) -- Failure END END -- Check run priority: must be a valid value to pass to SetThreadPriority: -- [-15 = IDLE, -1 = BELOW_NORMAL, 0 = NORMAL, 1 = ABOVE_NORMAL, 15 = TIME_CRITICAL] IF (@os_run_priority NOT IN (-15, -1, 0, 1, 15)) BEGIN RAISERROR(14266, -1, -1, '@os_run_priority', '-15, -1, 0, 1, 15') RETURN(1) -- Failure END -- Check flags IF ((@flags < 0) OR (@flags > 114)) BEGIN RAISERROR(14266, -1, -1, '@flags', '0..114') RETURN(1) -- Failure END IF (((@flags & 64) <> 0) AND (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('CMDEXEC'))) BEGIN RAISERROR(14545, -1, -1, '@flags', '@subsystem') RETURN(1) -- Failure END -- Check output file IF (@output_file_name IS NOT NULL) AND (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('TSQL', 'CMDEXEC', 'ANALYSISQUERY', 'ANALYSISCOMMAND', 'SSIS', 'POWERSHELL' )) BEGIN RAISERROR(14545, -1, -1, '@output_file_name', @subsystem) RETURN(1) -- Failure END -- Check writing to table flags IF (@flags IS NOT NULL) AND (((@flags & 8) <> 0) OR ((@flags & 16) <> 0)) AND (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('TSQL', 'CMDEXEC', 'ANALYSISQUERY', 'ANALYSISCOMMAND', 'SSIS', 'POWERSHELL' )) BEGIN RAISERROR(14545, -1, -1, '@flags', @subsystem) RETURN(1) -- Failure END -- For CmdExec steps database-name and database-user-name should both be null IF (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'CMDEXEC') SELECT @database_name = NULL, @database_user_name = NULL -- For non-TSQL steps, database-user-name should be null IF (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) <> 'TSQL') SELECT @database_user_name = NULL -- For a TSQL step, get (and check) the username of the caller in the target database. IF (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = 'TSQL') BEGIN SET NOCOUNT ON -- But first check if remote server name has been supplied IF (@server IS NOT NULL) SELECT @server = NULL -- Default database to 'master' if not supplied IF (LTRIM(RTRIM(@database_name)) IS NULL) SELECT @database_name = N'master' -- Check the database (although this is no guarantee that @database_user_name can access it) IF (DB_ID(@database_name) IS NULL) BEGIN RAISERROR(14262, -1, -1, '@database_name', @database_name) RETURN(1) -- Failure END SELECT @database_user_name = LTRIM(RTRIM(@database_user_name)) -- Only if a SysAdmin is creating the job can the database user name be non-NULL [since only -- SysAdmin's can call SETUSER]. -- NOTE: In this case we don't try to validate the user name (it's too costly to do so) -- so if it's bad we'll get a runtime error when the job executes. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) BEGIN -- If this is a multi-server job then @database_user_name must be null IF (@database_user_name IS NOT NULL) BEGIN IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs sj, msdb.dbo.sysjobservers sjs WHERE (sj.job_id = sjs.job_id) AND (sj.job_id = @job_id) AND (sjs.server_id <> 0))) BEGIN RAISERROR(14542, -1, -1, N'database_user_name') RETURN(1) -- Failure END END -- For a SQL-user, check if it exists IF (@database_user_name NOT LIKE N'%\%') BEGIN SELECT @database_user_name_temp = replace(@database_user_name, N'''', N'''''') SELECT @database_name_temp = QUOTENAME(@database_name) EXECUTE(N'DECLARE @ret INT SELECT @ret = COUNT(*) FROM ' + @database_name_temp + N'.dbo.sysusers WHERE (name = N''' + @database_user_name_temp + N''') HAVING (COUNT(*) > 0)') IF (@@ROWCOUNT = 0) BEGIN RAISERROR(14262, -1, -1, '@database_user_name', @database_user_name) RETURN(1) -- Failure END END END ELSE SELECT @database_user_name = NULL END -- End of TSQL property verification RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_JOBSTEP_INTERNAL */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_jobstep_internal...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_jobstep_internal') AND (type = 'P'))) DROP PROCEDURE dbo.sp_add_jobstep_internal go CREATE PROCEDURE dbo.sp_add_jobstep_internal @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @step_id INT = NULL, -- The proc assigns a default @step_name sysname, @subsystem NVARCHAR(40) = N'TSQL', @command NVARCHAR(max) = NULL, @additional_parameters NTEXT = NULL, @cmdexec_success_code INT = 0, @on_success_action TINYINT = 1, -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step @on_success_step_id INT = 0, @on_fail_action TINYINT = 2, -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step @on_fail_step_id INT = 0, @server sysname = NULL, @database_name sysname = NULL, @database_user_name sysname = NULL, @retry_attempts INT = 0, -- No retries @retry_interval INT = 0, -- 0 minute interval @os_run_priority INT = 0, -- -15 = Idle, -1 = Below Normal, 0 = Normal, 1 = Above Normal, 15 = Time Critical) @output_file_name NVARCHAR(200) = NULL, @flags INT = 0, -- 0 = Normal, -- 1 = Encrypted command (read only), -- 2 = Append output files (if any), -- 4 = Write TSQL step output to step history -- 8 = Write log to table (overwrite existing history) -- 16 = Write log to table (append to existing history) -- 32 = Write all output to job history -- 64 = Create a Windows event to use as a signal for the Cmd jobstep to abort @proxy_id int = NULL, @proxy_name sysname = NULL, -- mutual exclusive; must specify only one of above 2 parameters to -- identify the proxy. @step_uid UNIQUEIDENTIFIER = NULL OUTPUT AS BEGIN DECLARE @retval INT DECLARE @max_step_id INT DECLARE @job_owner_sid VARBINARY(85) DECLARE @subsystem_id INT DECLARE @auto_proxy_name sysname SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @step_name = LTRIM(RTRIM(@step_name)) SELECT @subsystem = LTRIM(RTRIM(@subsystem)) SELECT @server = LTRIM(RTRIM(@server)) SELECT @database_name = LTRIM(RTRIM(@database_name)) SELECT @database_user_name = LTRIM(RTRIM(@database_user_name)) SELECT @output_file_name = LTRIM(RTRIM(@output_file_name)) SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) -- Turn [nullable] empty string parameters into NULLs IF (@server = N'') SELECT @server = NULL IF (@database_name = N'') SELECT @database_name = NULL IF (@database_user_name = N'') SELECT @database_user_name = NULL IF (@output_file_name = N'') SELECT @output_file_name = NULL IF (@proxy_name = N'') SELECT @proxy_name = NULL -- Check authority (only SQLServerAgent can add a step to a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%' IF (@retval <> 0) RETURN(@retval) EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) AND (SUSER_SID() <> @job_owner_sid)) BEGIN RAISERROR(14525, -1, -1) RETURN(1) -- Failure END -- check proxy identifiers only if a proxy has been provided IF (@proxy_id IS NOT NULL) or (@proxy_name IS NOT NULL) BEGIN EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END -- Default step id (if not supplied) IF (@step_id IS NULL) BEGIN SELECT @step_id = ISNULL(MAX(step_id), 0) + 1 FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) END -- Check parameters EXECUTE @retval = sp_verify_jobstep @job_id, @step_id, @step_name, @subsystem, @command, @server, @on_success_action, @on_success_step_id, @on_fail_action, @on_fail_step_id, @os_run_priority, @database_name OUTPUT, @database_user_name OUTPUT, @flags, @output_file_name, @proxy_id IF (@retval <> 0) RETURN(1) -- Failure -- Get current maximum step id SELECT @max_step_id = ISNULL(MAX(step_id), 0) FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) DECLARE @TranCounter INT; SET @TranCounter = @@TRANCOUNT; IF @TranCounter = 0 BEGIN -- start our own transaction if there is no outer transaction BEGIN TRANSACTION; END -- Modify database. BEGIN TRY -- Update the job's version/last-modified information UPDATE msdb.dbo.sysjobs SET version_number = version_number + 1, date_modified = GETDATE() WHERE (job_id = @job_id) -- Adjust step id's (unless the new step is being inserted at the 'end') -- NOTE: We MUST do this before inserting the step. IF (@step_id <= @max_step_id) BEGIN UPDATE msdb.dbo.sysjobsteps SET step_id = step_id + 1 WHERE (step_id >= @step_id) AND (job_id = @job_id) -- Clean up OnSuccess/OnFail references UPDATE msdb.dbo.sysjobsteps SET on_success_step_id = on_success_step_id + 1 WHERE (on_success_step_id >= @step_id) AND (job_id = @job_id) UPDATE msdb.dbo.sysjobsteps SET on_fail_step_id = on_fail_step_id + 1 WHERE (on_fail_step_id >= @step_id) AND (job_id = @job_id) UPDATE msdb.dbo.sysjobsteps SET on_success_step_id = 0, on_success_action = 1 -- Quit With Success WHERE (on_success_step_id = @step_id) AND (job_id = @job_id) UPDATE msdb.dbo.sysjobsteps SET on_fail_step_id = 0, on_fail_action = 2 -- Quit With Failure WHERE (on_fail_step_id = @step_id) AND (job_id = @job_id) END SELECT @step_uid = NEWID() -- Insert the step INSERT INTO msdb.dbo.sysjobsteps (job_id, step_id, step_name, subsystem, command, flags, additional_parameters, cmdexec_success_code, on_success_action, on_success_step_id, on_fail_action, on_fail_step_id, server, database_name, database_user_name, retry_attempts, retry_interval, os_run_priority, output_file_name, last_run_outcome, last_run_duration, last_run_retries, last_run_date, last_run_time, proxy_id, step_uid) VALUES (@job_id, @step_id, @step_name, @subsystem, @command, @flags, @additional_parameters, @cmdexec_success_code, @on_success_action, @on_success_step_id, @on_fail_action, @on_fail_step_id, @server, @database_name, @database_user_name, @retry_attempts, @retry_interval, @os_run_priority, @output_file_name, 0, 0, 0, 0, 0, @proxy_id, @step_uid) IF @TranCounter = 0 BEGIN -- start our own transaction if there is no outer transaction COMMIT TRANSACTION; END END TRY BEGIN CATCH -- Prepare tp echo error information to the caller. DECLARE @ErrorMessage NVARCHAR(400) DECLARE @ErrorSeverity INT DECLARE @ErrorState INT SELECT @ErrorMessage = ERROR_MESSAGE() SELECT @ErrorSeverity = ERROR_SEVERITY() SELECT @ErrorState = ERROR_STATE() IF @TranCounter = 0 BEGIN -- Transaction started in procedure. -- Roll back complete transaction. ROLLBACK TRANSACTION; END RAISERROR (@ErrorMessage, -- Message text. @ErrorSeverity, -- Severity. @ErrorState -- State. ) RETURN (1) END CATCH -- Make sure that SQLServerAgent refreshes the job if the 'Has Steps' property has changed IF ((SELECT COUNT(*) FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id)) = 1) BEGIN -- NOTE: We only notify SQLServerAgent if we know the job has been cached IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @action_type = N'U' END -- For a multi-server job, push changes to the target servers IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) BEGIN EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id END RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_JOBSTEP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_jobstep...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_jobstep') AND (type = 'P'))) DROP PROCEDURE dbo.sp_add_jobstep go CREATE PROCEDURE dbo.sp_add_jobstep @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @step_id INT = NULL, -- The proc assigns a default @step_name sysname, @subsystem NVARCHAR(40) = N'TSQL', @command NVARCHAR(max) = NULL, @additional_parameters NTEXT = NULL, @cmdexec_success_code INT = 0, @on_success_action TINYINT = 1, -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step @on_success_step_id INT = 0, @on_fail_action TINYINT = 2, -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step @on_fail_step_id INT = 0, @server sysname = NULL, @database_name sysname = NULL, @database_user_name sysname = NULL, @retry_attempts INT = 0, -- No retries @retry_interval INT = 0, -- 0 minute interval @os_run_priority INT = 0, -- -15 = Idle, -1 = Below Normal, 0 = Normal, 1 = Above Normal, 15 = Time Critical) @output_file_name NVARCHAR(200) = NULL, @flags INT = 0, -- 0 = Normal, -- 1 = Encrypted command (read only), -- 2 = Append output files (if any), -- 4 = Write TSQL step output to step history, -- 8 = Write log to table (overwrite existing history), -- 16 = Write log to table (append to existing history) -- 32 = Write all output to job history -- 64 = Create a Windows event to use as a signal for the Cmd jobstep to abort @proxy_id INT = NULL, @proxy_name sysname = NULL, -- mutual exclusive; must specify only one of above 2 parameters to -- identify the proxy. @step_uid UNIQUEIDENTIFIER = NULL OUTPUT AS BEGIN DECLARE @retval INT SET NOCOUNT ON -- Only sysadmin's or db_owner's of msdb can add replication job steps directly IF (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) IN (N'DISTRIBUTION', N'SNAPSHOT', N'LOGREADER', N'MERGE', N'QUEUEREADER')) BEGIN IF NOT ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1) OR (UPPER(USER_NAME() collate SQL_Latin1_General_CP1_CS_AS) = N'DBO')) BEGIN RAISERROR(14260, -1, -1) RETURN(1) -- Failure END END --Only sysadmin can specify @database_user_name IF (@database_user_name IS NOT NULL) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(14583, -1, -1) RETURN(1) -- Failure END -- Make sure Dts is translated into new subsystem's name SSIS IF UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DTS' BEGIN SET @subsystem = N'SSIS' END EXECUTE @retval = dbo.sp_add_jobstep_internal @job_id = @job_id, @job_name = @job_name, @step_id = @step_id, @step_name = @step_name, @subsystem = @subsystem, @command = @command, @additional_parameters = @additional_parameters, @cmdexec_success_code = @cmdexec_success_code, @on_success_action = @on_success_action, @on_success_step_id = @on_success_step_id, @on_fail_action = @on_fail_action, @on_fail_step_id = @on_fail_step_id, @server = @server, @database_name = @database_name, @database_user_name = @database_user_name, @retry_attempts = @retry_attempts, @retry_interval = @retry_interval, @os_run_priority = @os_run_priority, @output_file_name = @output_file_name, @flags = @flags, @proxy_id = @proxy_id, @proxy_name = @proxy_name, @step_uid = @step_uid OUTPUT RETURN(@retval) END GO /**************************************************************/ /* SP_UPDATE_JOBSTEP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_jobstep...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_jobstep') AND (type = 'P'))) DROP PROCEDURE sp_update_jobstep go CREATE PROCEDURE sp_update_jobstep @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Not updatable (provided for identification purposes only) @step_id INT, -- Not updatable (provided for identification purposes only) @step_name sysname = NULL, @subsystem NVARCHAR(40) = NULL, @command NVARCHAR(max) = NULL, @additional_parameters NTEXT = NULL, @cmdexec_success_code INT = NULL, @on_success_action TINYINT = NULL, @on_success_step_id INT = NULL, @on_fail_action TINYINT = NULL, @on_fail_step_id INT = NULL, @server sysname = NULL, @database_name sysname = NULL, @database_user_name sysname = NULL, @retry_attempts INT = NULL, @retry_interval INT = NULL, @os_run_priority INT = NULL, @output_file_name NVARCHAR(200) = NULL, @flags INT = NULL, @proxy_id int = NULL, @proxy_name sysname = NULL -- mutual exclusive; must specify only one of above 2 parameters to -- identify the proxy. AS BEGIN DECLARE @retval INT DECLARE @os_run_priority_code INT DECLARE @step_id_as_char VARCHAR(10) DECLARE @new_step_name sysname DECLARE @x_step_name sysname DECLARE @x_subsystem NVARCHAR(40) DECLARE @x_command NVARCHAR(max) DECLARE @x_flags INT DECLARE @x_cmdexec_success_code INT DECLARE @x_on_success_action TINYINT DECLARE @x_on_success_step_id INT DECLARE @x_on_fail_action TINYINT DECLARE @x_on_fail_step_id INT DECLARE @x_server sysname DECLARE @x_database_name sysname DECLARE @x_database_user_name sysname DECLARE @x_retry_attempts INT DECLARE @x_retry_interval INT DECLARE @x_os_run_priority INT DECLARE @x_output_file_name NVARCHAR(200) DECLARE @x_proxy_id INT DECLARE @x_last_run_outcome TINYINT -- Not updatable (but may be in future) DECLARE @x_last_run_duration INT -- Not updatable (but may be in future) DECLARE @x_last_run_retries INT -- Not updatable (but may be in future) DECLARE @x_last_run_date INT -- Not updatable (but may be in future) DECLARE @x_last_run_time INT -- Not updatable (but may be in future) DECLARE @new_proxy_id INT DECLARE @subsystem_id INT DECLARE @auto_proxy_name sysname DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON SELECT @new_proxy_id = NULL -- Remove any leading/trailing spaces from parameters SELECT @step_name = LTRIM(RTRIM(@step_name)) SELECT @subsystem = LTRIM(RTRIM(@subsystem)) SELECT @command = LTRIM(RTRIM(@command)) SELECT @server = LTRIM(RTRIM(@server)) SELECT @database_name = LTRIM(RTRIM(@database_name)) SELECT @database_user_name = LTRIM(RTRIM(@database_user_name)) SELECT @output_file_name = LTRIM(RTRIM(@output_file_name)) SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) -- Make sure Dts is translated into new subsystem's name SSIS IF (@subsystem IS NOT NULL AND UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DTS') BEGIN SET @subsystem = N'SSIS' END -- Only sysadmin's or db_owner's of msdb can directly change -- an existing job step to use one of the replication -- subsystems IF (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) IN (N'DISTRIBUTION', N'SNAPSHOT', N'LOGREADER', N'MERGE', N'QUEUEREADER')) BEGIN IF NOT ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1) OR (UPPER(USER_NAME() collate SQL_Latin1_General_CP1_CS_AS) = N'DBO')) BEGIN RAISERROR(14260, -1, -1) RETURN(1) -- Failure END END EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReader and SQLAgentOperator roles that can see all jobs -- cannot modify jobs they do not own IF (@job_owner_sid <> SUSER_SID() -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) -- is not sysadmin BEGIN RAISERROR(14525, -1, -1); RETURN(1) -- Failure END -- Check that the step exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @step_id))) BEGIN SELECT @step_id_as_char = CONVERT(VARCHAR(10), @step_id) RAISERROR(14262, -1, -1, '@step_id', @step_id_as_char) RETURN(1) -- Failure END -- check proxy identifiers only if a proxy has been provided -- @proxy_name = N'' is a special case to allow change of an existing proxy with NULL IF (@proxy_id IS NOT NULL) OR (@proxy_name IS NOT NULL AND @proxy_name <> N'') BEGIN EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure SELECT @new_proxy_id = @proxy_id END -- Check authority (only SQLServerAgent can modify a step of a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- Set the x_ (existing) variables SELECT @x_step_name = step_name, @x_subsystem = subsystem, @x_command = command, @x_flags = flags, @x_cmdexec_success_code = cmdexec_success_code, @x_on_success_action = on_success_action, @x_on_success_step_id = on_success_step_id, @x_on_fail_action = on_fail_action, @x_on_fail_step_id = on_fail_step_id, @x_server = server, @x_database_name = database_name, @x_database_user_name = database_user_name, @x_retry_attempts = retry_attempts, @x_retry_interval = retry_interval, @x_os_run_priority = os_run_priority, @x_output_file_name = output_file_name, @x_proxy_id = proxy_id, @x_last_run_outcome = last_run_outcome, @x_last_run_duration = last_run_duration, @x_last_run_retries = last_run_retries, @x_last_run_date = last_run_date, @x_last_run_time = last_run_time FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @step_id) IF ((@step_name IS NOT NULL) AND (@step_name <> @x_step_name)) SELECT @new_step_name = @step_name -- Fill out the values for all non-supplied parameters from the existing values IF (@step_name IS NULL) SELECT @step_name = @x_step_name IF (@subsystem IS NULL) SELECT @subsystem = @x_subsystem IF (@command IS NULL) SELECT @command = @x_command IF (@flags IS NULL) SELECT @flags = @x_flags IF (@cmdexec_success_code IS NULL) SELECT @cmdexec_success_code = @x_cmdexec_success_code IF (@on_success_action IS NULL) SELECT @on_success_action = @x_on_success_action IF (@on_success_step_id IS NULL) SELECT @on_success_step_id = @x_on_success_step_id IF (@on_fail_action IS NULL) SELECT @on_fail_action = @x_on_fail_action IF (@on_fail_step_id IS NULL) SELECT @on_fail_step_id = @x_on_fail_step_id IF (@server IS NULL) SELECT @server = @x_server IF (@database_name IS NULL) SELECT @database_name = @x_database_name IF (@database_user_name IS NULL) SELECT @database_user_name = @x_database_user_name IF (@retry_attempts IS NULL) SELECT @retry_attempts = @x_retry_attempts IF (@retry_interval IS NULL) SELECT @retry_interval = @x_retry_interval IF (@os_run_priority IS NULL) SELECT @os_run_priority = @x_os_run_priority IF (@output_file_name IS NULL) SELECT @output_file_name = @x_output_file_name IF (@proxy_id IS NULL) SELECT @new_proxy_id = @x_proxy_id --if an empty proxy_name is supplied the proxy is removed IF @proxy_name = N'' SELECT @new_proxy_id = NULL -- Turn [nullable] empty string parameters into NULLs IF (@command = N'') SELECT @command = NULL IF (@server = N'') SELECT @server = NULL IF (@database_name = N'') SELECT @database_name = NULL IF (@database_user_name = N'') SELECT @database_user_name = NULL IF (@output_file_name = N'') SELECT @output_file_name = NULL -- Check new values EXECUTE @retval = sp_verify_jobstep @job_id, @step_id, @new_step_name, @subsystem, @command, @server, @on_success_action, @on_success_step_id, @on_fail_action, @on_fail_step_id, @os_run_priority, @database_name OUTPUT, @database_user_name OUTPUT, @flags, @output_file_name, @new_proxy_id IF (@retval <> 0) RETURN(1) -- Failure BEGIN TRANSACTION -- Update the job's version/last-modified information UPDATE msdb.dbo.sysjobs SET version_number = version_number + 1, date_modified = GETDATE() WHERE (job_id = @job_id) -- Update the step UPDATE msdb.dbo.sysjobsteps SET step_name = @step_name, subsystem = @subsystem, command = @command, flags = @flags, cmdexec_success_code = @cmdexec_success_code, on_success_action = @on_success_action, on_success_step_id = @on_success_step_id, on_fail_action = @on_fail_action, on_fail_step_id = @on_fail_step_id, server = @server, database_name = @database_name, database_user_name = @database_user_name, retry_attempts = @retry_attempts, retry_interval = @retry_interval, os_run_priority = @os_run_priority, output_file_name = @output_file_name, last_run_outcome = @x_last_run_outcome, last_run_duration = @x_last_run_duration, last_run_retries = @x_last_run_retries, last_run_date = @x_last_run_date, last_run_time = @x_last_run_time, proxy_id = @new_proxy_id WHERE (job_id = @job_id) AND (step_id = @step_id) -- Since we can't declare TEXT parameters (and therefore use the @x_ technique) we handle -- @additional_parameters as a special case... IF (@additional_parameters IS NOT NULL) BEGIN UPDATE msdb.dbo.sysjobsteps SET additional_parameters = @additional_parameters WHERE (job_id = @job_id) AND (step_id = @step_id) END COMMIT TRANSACTION -- For a multi-server job, push changes to the target servers IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) BEGIN EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id END RETURN(0) -- Success END go /**************************************************************/ /* SP_DELETE_JOBSTEP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_jobstep...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_jobstep') AND (type = 'P'))) DROP PROCEDURE sp_delete_jobstep go CREATE PROCEDURE sp_delete_jobstep @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @step_id INT AS BEGIN DECLARE @retval INT DECLARE @max_step_id INT DECLARE @valid_range VARCHAR(50) DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check authority (only SQLServerAgent can delete a step of a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = 'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- SQLAgentReader and SQLAgentOperator roles that can see all jobs -- cannot modify jobs they do not own IF (@job_owner_sid <> SUSER_SID() -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) -- is not sysadmin BEGIN RAISERROR(14525, -1, -1); RETURN(1) -- Failure END -- Get current maximum step id SELECT @max_step_id = ISNULL(MAX(step_id), 0) FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) -- Check step id IF (@step_id < 0) OR (@step_id > @max_step_id) BEGIN SELECT @valid_range = FORMATMESSAGE(14201) + CONVERT(VARCHAR, @max_step_id) RAISERROR(14266, -1, -1, '@step_id', @valid_range) RETURN(1) -- Failure END BEGIN TRANSACTION -- Delete either the specified step or ALL the steps (if step id is 0) IF (@step_id = 0) BEGIN DELETE FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) END ELSE BEGIN DELETE FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @step_id) END IF (@step_id <> 0) BEGIN -- Adjust step id's UPDATE msdb.dbo.sysjobsteps SET step_id = step_id - 1 WHERE (step_id > @step_id) AND (job_id = @job_id) -- Clean up OnSuccess/OnFail references UPDATE msdb.dbo.sysjobsteps SET on_success_step_id = on_success_step_id - 1 WHERE (on_success_step_id > @step_id) AND (job_id = @job_id) UPDATE msdb.dbo.sysjobsteps SET on_fail_step_id = on_fail_step_id - 1 WHERE (on_fail_step_id > @step_id) AND (job_id = @job_id) UPDATE msdb.dbo.sysjobsteps SET on_success_step_id = 0, on_success_action = 1 -- Quit With Success WHERE (on_success_step_id = @step_id) AND (job_id = @job_id) UPDATE msdb.dbo.sysjobsteps SET on_fail_step_id = 0, on_fail_action = 2 -- Quit With Failure WHERE (on_fail_step_id = @step_id) AND (job_id = @job_id) END -- Update the job's version/last-modified information UPDATE msdb.dbo.sysjobs SET version_number = version_number + 1, date_modified = GETDATE() WHERE (job_id = @job_id) COMMIT TRANSACTION -- Make sure that SQLServerAgent refreshes the job if the 'Has Steps' property has changed IF ((SELECT COUNT(*) FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id)) = 0) BEGIN -- NOTE: We only notify SQLServerAgent if we know the job has been cached IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @action_type = N'U' END -- For a multi-server job, push changes to the target servers IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) BEGIN EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id END RETURN(0) -- Success END go /**************************************************************/ /* SP_HELP_JOBSTEP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_jobstep...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_jobstep') AND (type = 'P'))) DROP PROCEDURE sp_help_jobstep go CREATE PROCEDURE sp_help_jobstep @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @step_id INT = NULL, @step_name sysname = NULL, @suffix BIT = 0 -- A flag to control how the result set is formatted AS BEGIN DECLARE @retval INT DECLARE @max_step_id INT DECLARE @valid_range VARCHAR(50) SET NOCOUNT ON EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, 'NO_TEST' IF (@retval <> 0) RETURN(1) -- Failure -- The suffix flag must be either 0 (ie. no suffix) or 1 (ie. add suffix). 0 is the default. IF (@suffix <> 0) SELECT @suffix = 1 -- Check step id (if supplied) IF (@step_id IS NOT NULL) BEGIN -- Get current maximum step id SELECT @max_step_id = ISNULL(MAX(step_id), 0) FROM msdb.dbo.sysjobsteps WHERE job_id = @job_id IF @max_step_id = 0 BEGIN RAISERROR(14528, -1, -1, @job_name) RETURN(1) -- Failure END ELSE IF (@step_id < 1) OR (@step_id > @max_step_id) BEGIN SELECT @valid_range = '1..' + CONVERT(VARCHAR, @max_step_id) RAISERROR(14266, -1, -1, '@step_id', @valid_range) RETURN(1) -- Failure END END -- Check step name (if supplied) -- NOTE: A supplied step id overrides a supplied step name IF ((@step_id IS NULL) AND (@step_name IS NOT NULL)) BEGIN SELECT @step_id = step_id FROM msdb.dbo.sysjobsteps WHERE (step_name = @step_name) AND (job_id = @job_id) IF (@step_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@step_name', @step_name) RETURN(1) -- Failure END END -- Return the job steps for this job (or just return the specific step) IF (@suffix = 0) BEGIN SELECT step_id, step_name, subsystem, command, flags, cmdexec_success_code, on_success_action, on_success_step_id, on_fail_action, on_fail_step_id, server, database_name, database_user_name, retry_attempts, retry_interval, os_run_priority, output_file_name, last_run_outcome, last_run_duration, last_run_retries, last_run_date, last_run_time, proxy_id FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND ((@step_id IS NULL) OR (step_id = @step_id)) ORDER BY job_id, step_id END ELSE BEGIN SELECT step_id, step_name, subsystem, command, 'flags' = CONVERT(NVARCHAR, flags) + N' (' + ISNULL(CASE WHEN (flags = 0) THEN FORMATMESSAGE(14561) END, '') + ISNULL(CASE WHEN (flags & 1) = 1 THEN FORMATMESSAGE(14558) + ISNULL(CASE WHEN (flags > 1) THEN N', ' END, '') END, '') + ISNULL(CASE WHEN (flags & 2) = 2 THEN FORMATMESSAGE(14559) + ISNULL(CASE WHEN (flags > 3) THEN N', ' END, '') END, '') + ISNULL(CASE WHEN (flags & 4) = 4 THEN FORMATMESSAGE(14560) END, '') + N')', cmdexec_success_code, 'on_success_action' = CASE on_success_action WHEN 1 THEN CONVERT(NVARCHAR, on_success_action) + N' ' + FORMATMESSAGE(14562) WHEN 2 THEN CONVERT(NVARCHAR, on_success_action) + N' ' + FORMATMESSAGE(14563) WHEN 3 THEN CONVERT(NVARCHAR, on_success_action) + N' ' + FORMATMESSAGE(14564) WHEN 4 THEN CONVERT(NVARCHAR, on_success_action) + N' ' + FORMATMESSAGE(14565) ELSE CONVERT(NVARCHAR, on_success_action) + N' ' + FORMATMESSAGE(14205) END, on_success_step_id, 'on_fail_action' = CASE on_fail_action WHEN 1 THEN CONVERT(NVARCHAR, on_fail_action) + N' ' + FORMATMESSAGE(14562) WHEN 2 THEN CONVERT(NVARCHAR, on_fail_action) + N' ' + FORMATMESSAGE(14563) WHEN 3 THEN CONVERT(NVARCHAR, on_fail_action) + N' ' + FORMATMESSAGE(14564) WHEN 4 THEN CONVERT(NVARCHAR, on_fail_action) + N' ' + FORMATMESSAGE(14565) ELSE CONVERT(NVARCHAR, on_fail_action) + N' ' + FORMATMESSAGE(14205) END, on_fail_step_id, server, database_name, database_user_name, retry_attempts, retry_interval, 'os_run_priority' = CASE os_run_priority WHEN -15 THEN CONVERT(NVARCHAR, os_run_priority) + N' ' + FORMATMESSAGE(14566) WHEN -1 THEN CONVERT(NVARCHAR, os_run_priority) + N' ' + FORMATMESSAGE(14567) WHEN 0 THEN CONVERT(NVARCHAR, os_run_priority) + N' ' + FORMATMESSAGE(14561) WHEN 1 THEN CONVERT(NVARCHAR, os_run_priority) + N' ' + FORMATMESSAGE(14568) WHEN 15 THEN CONVERT(NVARCHAR, os_run_priority) + N' ' + FORMATMESSAGE(14569) ELSE CONVERT(NVARCHAR, os_run_priority) + N' ' + FORMATMESSAGE(14205) END, output_file_name, last_run_outcome, last_run_duration, last_run_retries, last_run_date, last_run_time, proxy_id FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND ((@step_id IS NULL) OR (step_id = @step_id)) ORDER BY job_id, step_id END RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_WRITE_SYSJOBSTEP_LOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_write_sysjobstep_log...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_write_sysjobstep_log') AND (type = 'P'))) DROP PROCEDURE sp_write_sysjobstep_log go CREATE PROCEDURE sp_write_sysjobstep_log @job_id UNIQUEIDENTIFIER, @step_id INT, @log_text NVARCHAR(MAX), @append_to_last INT = 0 AS BEGIN DECLARE @step_uid UNIQUEIDENTIFIER DECLARE @log_already_exists int SET @log_already_exists = 0 SET @step_uid = ( SELECT step_uid FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @step_id) ) IF(EXISTS(SELECT * FROM msdb.dbo.sysjobstepslogs WHERE step_uid = @step_uid )) BEGIN SET @log_already_exists = 1 END --Need create log if "overwrite is selected or log does not exists. IF (@append_to_last = 0) OR (@log_already_exists = 0) BEGIN -- flag is overwrite --if overwrite and log exists, delete it IF (@append_to_last = 0 AND @log_already_exists = 1) BEGIN -- remove previous logs entries EXEC sp_delete_jobsteplog @job_id, NULL, @step_id, NULL END INSERT INTO msdb.dbo.sysjobstepslogs ( log, log_size, step_uid ) VALUES ( @log_text, DATALENGTH(@log_text), @step_uid ) END ELSE BEGIN DECLARE @log_id INT --Selecting TOP is just a safety net - there is only one log entry row per step. SET @log_id = ( SELECT TOP 1 log_id FROM msdb.dbo.sysjobstepslogs WHERE (step_uid = @step_uid) ORDER BY log_id DESC ) -- Append @log_text to the existing log record. Note that if this -- action would make the value of the log column longer than -- nvarchar(max), then the engine will raise error 599. UPDATE msdb.dbo.sysjobstepslogs SET log .WRITE(@log_text,NULL,0), log_size = DATALENGTH(log) + DATALENGTH(@log_text) , date_modified = getdate() WHERE log_id = @log_id END RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_HELP_JOBSTEPLOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_jobsteplog...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_jobsteplog') AND (type = 'P'))) DROP PROCEDURE sp_help_jobsteplog go CREATE PROCEDURE sp_help_jobsteplog @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @step_id INT = NULL, @step_name sysname = NULL AS BEGIN DECLARE @retval INT DECLARE @max_step_id INT DECLARE @valid_range VARCHAR(50) EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, 'NO_TEST' IF (@retval <> 0) RETURN(1) -- Failure -- Check step id (if supplied) IF (@step_id IS NOT NULL) BEGIN -- Get current maximum step id SELECT @max_step_id = ISNULL(MAX(step_id), 0) FROM msdb.dbo.sysjobsteps WHERE job_id = @job_id IF @max_step_id = 0 BEGIN RAISERROR(14528, -1, -1, @job_name) RETURN(1) -- Failure END ELSE IF (@step_id < 1) OR (@step_id > @max_step_id) BEGIN SELECT @valid_range = '1..' + CONVERT(VARCHAR, @max_step_id) RAISERROR(14266, -1, -1, '@step_id', @valid_range) RETURN(1) -- Failure END END -- Check step name (if supplied) -- NOTE: A supplied step id overrides a supplied step name IF ((@step_id IS NULL) AND (@step_name IS NOT NULL)) BEGIN SELECT @step_id = step_id FROM msdb.dbo.sysjobsteps WHERE (step_name = @step_name) AND (job_id = @job_id) IF (@step_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@step_name', @step_name) RETURN(1) -- Failure END END SELECT sjv.job_id, @job_name as job_name, steps.step_id, steps.step_name, steps.step_uid, logs.date_created, logs.date_modified, logs.log_size, logs.log FROM msdb.dbo.sysjobs_view sjv, msdb.dbo.sysjobsteps as steps, msdb.dbo.sysjobstepslogs as logs WHERE (sjv.job_id = @job_id) AND (steps.job_id = @job_id) AND ((@step_id IS NULL) OR (step_id = @step_id)) AND (steps.step_uid = logs.step_uid) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_DELETE_JOBSTEPLOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_jobsteplog...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_jobsteplog') AND (type = 'P'))) DROP PROCEDURE sp_delete_jobsteplog go CREATE PROCEDURE sp_delete_jobsteplog @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @step_id INT = NULL, @step_name sysname = NULL, @older_than datetime = NULL, @larger_than int = NULL -- (in megabytes) AS BEGIN DECLARE @retval INT DECLARE @max_step_id INT DECLARE @valid_range VARCHAR(50) EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, 'NO_TEST' IF (@retval <> 0) RETURN(1) -- Failure -- Check step id (if supplied) IF (@step_id IS NOT NULL) BEGIN -- Get current maximum step id SELECT @max_step_id = ISNULL(MAX(step_id), 0) FROM msdb.dbo.sysjobsteps WHERE job_id = @job_id IF @max_step_id = 0 BEGIN RAISERROR(14528, -1, -1, @job_name) RETURN(1) -- Failure END ELSE IF (@step_id < 1) OR (@step_id > @max_step_id) BEGIN SELECT @valid_range = '1..' + CONVERT(VARCHAR, @max_step_id) RAISERROR(14266, -1, -1, '@step_id', @valid_range) RETURN(1) -- Failure END END -- Check step name (if supplied) -- NOTE: A supplied step id overrides a supplied step name IF ((@step_id IS NULL) AND (@step_name IS NOT NULL)) BEGIN SELECT @step_id = step_id FROM msdb.dbo.sysjobsteps WHERE (step_name = @step_name) AND (job_id = @job_id) IF (@step_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@step_name', @step_name) RETURN(1) -- Failure END END -- Delete either the specified step or ALL the steps (if step id is NULL) DELETE FROM msdb.dbo.sysjobstepslogs WHERE (step_uid IN (SELECT DISTINCT step_uid FROM msdb.dbo.sysjobsteps js, msdb.dbo.sysjobs_view jv WHERE ( @job_id = jv.job_id ) AND (js.job_id = jv.job_id ) AND ((@step_id IS NULL) OR (@step_id = step_id)))) AND ((@older_than IS NULL) OR (date_modified < @older_than)) AND ((@larger_than IS NULL) OR (log_size > @larger_than)) RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_GET_SCHEDULE_DESCRIPTION */ /* */ /* NOTE: This SP only returns an English description of the */ /* schedule due to the piecemeal nature of the */ /* description's construction. */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_schedule_description...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_get_schedule_description') AND (type = 'P'))) DROP PROCEDURE sp_get_schedule_description go CREATE PROCEDURE sp_get_schedule_description @freq_type INT = NULL, @freq_interval INT = NULL, @freq_subday_type INT = NULL, @freq_subday_interval INT = NULL, @freq_relative_interval INT = NULL, @freq_recurrence_factor INT = NULL, @active_start_date INT = NULL, @active_end_date INT = NULL, @active_start_time INT = NULL, @active_end_time INT = NULL, @schedule_description NVARCHAR(255) OUTPUT AS BEGIN DECLARE @loop INT DECLARE @idle_cpu_percent INT DECLARE @idle_cpu_duration INT SET NOCOUNT ON IF (@freq_type = 0x1) -- OneTime BEGIN SELECT @schedule_description = N'Once on ' + CONVERT(NVARCHAR, @active_start_date) + N' at ' + CONVERT(NVARCHAR, @active_start_time) RETURN END IF (@freq_type = 0x4) -- Daily BEGIN SELECT @schedule_description = N'Every day ' END IF (@freq_type = 0x8) -- Weekly BEGIN SELECT @schedule_description = N'Every ' + CONVERT(NVARCHAR, @freq_recurrence_factor) + N' week(s) on ' SELECT @loop = 1 WHILE (@loop <= 7) BEGIN IF (@freq_interval & POWER(2, @loop - 1) = POWER(2, @loop - 1)) SELECT @schedule_description = @schedule_description + DATENAME(dw, N'1996120' + CONVERT(NVARCHAR, @loop)) + N', ' SELECT @loop = @loop + 1 END IF (RIGHT(@schedule_description, 2) = N', ') SELECT @schedule_description = SUBSTRING(@schedule_description, 1, (DATALENGTH(@schedule_description) / 2) - 2) + N' ' END IF (@freq_type = 0x10) -- Monthly BEGIN SELECT @schedule_description = N'Every ' + CONVERT(NVARCHAR, @freq_recurrence_factor) + N' months(s) on day ' + CONVERT(NVARCHAR, @freq_interval) + N' of that month ' END IF (@freq_type = 0x20) -- Monthly Relative BEGIN SELECT @schedule_description = N'Every ' + CONVERT(NVARCHAR, @freq_recurrence_factor) + N' months(s) on the ' SELECT @schedule_description = @schedule_description + CASE @freq_relative_interval WHEN 0x01 THEN N'first ' WHEN 0x02 THEN N'second ' WHEN 0x04 THEN N'third ' WHEN 0x08 THEN N'fourth ' WHEN 0x10 THEN N'last ' END + CASE WHEN (@freq_interval > 00) AND (@freq_interval < 08) THEN DATENAME(dw, N'1996120' + CONVERT(NVARCHAR, @freq_interval)) WHEN (@freq_interval = 08) THEN N'day' WHEN (@freq_interval = 09) THEN N'week day' WHEN (@freq_interval = 10) THEN N'weekend day' END + N' of that month ' END IF (@freq_type = 0x40) -- AutoStart BEGIN SELECT @schedule_description = FORMATMESSAGE(14579) RETURN END IF (@freq_type = 0x80) -- OnIdle BEGIN EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'IdleCPUPercent', @idle_cpu_percent OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'IdleCPUDuration', @idle_cpu_duration OUTPUT, N'no_output' SELECT @schedule_description = FORMATMESSAGE(14578, ISNULL(@idle_cpu_percent, 10), ISNULL(@idle_cpu_duration, 600)) RETURN END -- Subday stuff SELECT @schedule_description = @schedule_description + CASE @freq_subday_type WHEN 0x1 THEN N'at ' + CONVERT(NVARCHAR, @active_start_time) WHEN 0x2 THEN N'every ' + CONVERT(NVARCHAR, @freq_subday_interval) + N' second(s)' WHEN 0x4 THEN N'every ' + CONVERT(NVARCHAR, @freq_subday_interval) + N' minute(s)' WHEN 0x8 THEN N'every ' + CONVERT(NVARCHAR, @freq_subday_interval) + N' hour(s)' END IF (@freq_subday_type IN (0x2, 0x4, 0x8)) SELECT @schedule_description = @schedule_description + N' between ' + CONVERT(NVARCHAR, @active_start_time) + N' and ' + CONVERT(NVARCHAR, @active_end_time) END go CHECKPOINT go /**************************************************************/ /* SP_ADD_JOBSCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_jobschedule...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_jobschedule') AND (type = 'P'))) DROP PROCEDURE sp_add_jobschedule go CREATE PROCEDURE sp_add_jobschedule -- This SP is deprecated. Use sp_add_schedule and sp_attach_schedule. @job_id UNIQUEIDENTIFIER = NULL, @job_name sysname = NULL, @name sysname, @enabled TINYINT = 1, @freq_type INT = 1, @freq_interval INT = 0, @freq_subday_type INT = 0, @freq_subday_interval INT = 0, @freq_relative_interval INT = 0, @freq_recurrence_factor INT = 0, @active_start_date INT = NULL, -- sp_verify_schedule assigns a default @active_end_date INT = 99991231, -- December 31st 9999 @active_start_time INT = 000000, -- 12:00:00 am @active_end_time INT = 235959, -- 11:59:59 pm @schedule_id INT = NULL OUTPUT, @automatic_post BIT = 1, -- If 1 will post notifications to all tsx servers to that run this job @schedule_uid UNIQUEIDENTIFIER = NULL OUTPUT AS BEGIN DECLARE @retval INT DECLARE @owner_login_name sysname SET NOCOUNT ON -- Check authority (only SQLServerAgent can add a schedule to a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- Check that we can uniquely identify the job EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Get the owner of the job. Prior to resusable schedules the job owner also owned the schedule SELECT @owner_login_name = dbo.SQLAGENT_SUSER_SNAME(owner_sid) FROM sysjobs WHERE (job_id = @job_id) IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) AND (SUSER_SNAME() <> @owner_login_name)) BEGIN RAISERROR(14525, -1, -1) RETURN(1) -- Failure END -- Check authority (only SQLServerAgent can add a schedule to a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- Add the schedule first EXECUTE @retval = msdb.dbo.sp_add_schedule @schedule_name = @name, @enabled = @enabled, @freq_type = @freq_type, @freq_interval = @freq_interval, @freq_subday_type = @freq_subday_type, @freq_subday_interval = @freq_subday_interval, @freq_relative_interval = @freq_relative_interval, @freq_recurrence_factor = @freq_recurrence_factor, @active_start_date = @active_start_date, @active_end_date = @active_end_date, @active_start_time = @active_start_time, @active_end_time = @active_end_time, @owner_login_name = @owner_login_name, @schedule_uid = @schedule_uid OUTPUT, @schedule_id = @schedule_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure EXECUTE @retval = msdb.dbo.sp_attach_schedule @job_id = @job_id, @job_name = NULL, @schedule_id = @schedule_id, @schedule_name = NULL, @automatic_post = @automatic_post IF (@retval <> 0) RETURN(1) -- Failure RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_UPDATE_JOBSCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_jobschedule...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_jobschedule') AND (type = 'P'))) DROP PROCEDURE sp_update_jobschedule go CREATE PROCEDURE sp_update_jobschedule -- This SP is deprecated by sp_update_schedule. @job_id UNIQUEIDENTIFIER = NULL, @job_name sysname = NULL, @name sysname, @new_name sysname = NULL, @enabled TINYINT = NULL, @freq_type INT = NULL, @freq_interval INT = NULL, @freq_subday_type INT = NULL, @freq_subday_interval INT = NULL, @freq_relative_interval INT = NULL, @freq_recurrence_factor INT = NULL, @active_start_date INT = NULL, @active_end_date INT = NULL, @active_start_time INT = NULL, @active_end_time INT = NULL, @automatic_post BIT = 1 -- If 1 will post notifications to all tsx servers to that run this job AS BEGIN DECLARE @retval INT DECLARE @sched_count INT DECLARE @schedule_id INT DECLARE @job_owner_sid VARBINARY(85) DECLARE @enable_only_used INT DECLARE @x_name sysname DECLARE @x_enabled TINYINT DECLARE @x_freq_type INT DECLARE @x_freq_interval INT DECLARE @x_freq_subday_type INT DECLARE @x_freq_subday_interval INT DECLARE @x_freq_relative_interval INT DECLARE @x_freq_recurrence_factor INT DECLARE @x_active_start_date INT DECLARE @x_active_end_date INT DECLARE @x_active_start_time INT DECLARE @x_active_end_time INT DECLARE @owner_sid VARBINARY(85) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @new_name = LTRIM(RTRIM(@new_name)) -- Turn [nullable] empty string parameters into NULLs IF (@new_name = N'') SELECT @new_name = NULL -- Check authority (only SQLServerAgent can modify a schedule of a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = 'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- Check that we can uniquely identify the job EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Is @enable the only parameter used beside jobname and jobid? IF ((@enabled IS NOT NULL) AND (@name IS NULL) AND (@new_name IS NULL) AND (@freq_type IS NULL) AND (@freq_interval IS NULL) AND (@freq_subday_type IS NULL) AND (@freq_subday_interval IS NULL) AND (@freq_relative_interval IS NULL) AND (@freq_recurrence_factor IS NULL) AND (@active_start_date IS NULL) AND (@active_end_date IS NULL) AND (@active_start_time IS NULL) AND (@active_end_time IS NULL)) SELECT @enable_only_used = 1 ELSE SELECT @enable_only_used = 0 IF ((SUSER_SID() <> @job_owner_sid) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) AND (@enable_only_used <> 1 OR ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) <> 1)) BEGIN RAISERROR(14525, -1, -1) RETURN(1) -- Failure END -- Make sure the schedule_id can be uniquely identified and that it exists -- Note: It's safe use the values returned by the MIN() function because the SP errors out if more than 1 record exists SELECT @sched_count = COUNT(*), @schedule_id = MIN(sched.schedule_id), @owner_sid = MIN(sched.owner_sid) FROM msdb.dbo.sysjobschedules as jsched JOIN msdb.dbo.sysschedules_localserver_view as sched ON jsched.schedule_id = sched.schedule_id WHERE (jsched.job_id = @job_id) AND (sched.name = @name) -- Need to use sp_update_schedule to update this ambiguous schedule name IF(@sched_count > 1) BEGIN RAISERROR(14375, -1, -1, @name, @job_name) RETURN(1) -- Failure END IF (@schedule_id IS NULL) BEGIN --raise an explicit message if the schedule does exist but isn't attached to this job IF EXISTS(SELECT * FROM sysschedules_localserver_view WHERE (name = @name)) BEGIN RAISERROR(14374, -1, -1, @name, @job_name) END ELSE BEGIN --If the schedule is from an MSX and a sysadmin is calling report a specific 'MSX' message IF(PROGRAM_NAME() NOT LIKE N'SQLAgent%' AND ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 AND EXISTS(SELECT * FROM msdb.dbo.sysschedules as sched JOIN msdb.dbo.sysoriginatingservers_view as svr ON sched.originating_server_id = svr.originating_server_id JOIN msdb.dbo.sysjobschedules as js ON sched.schedule_id = js.schedule_id WHERE (svr.master_server = 1) AND (sched.name = @name) AND (js.job_id = @job_id))) BEGIN RAISERROR(14274, -1, -1) END ELSE BEGIN --Generic message that the schedule doesn't exist RAISERROR(14262, -1, -1, 'Schedule Name', @name) END END RETURN(1) -- Failure END -- Set the x_ (existing) variables SELECT @x_name = name, @x_enabled = enabled, @x_freq_type = freq_type, @x_freq_interval = freq_interval, @x_freq_subday_type = freq_subday_type, @x_freq_subday_interval = freq_subday_interval, @x_freq_relative_interval = freq_relative_interval, @x_freq_recurrence_factor = freq_recurrence_factor, @x_active_start_date = active_start_date, @x_active_end_date = active_end_date, @x_active_start_time = active_start_time, @x_active_end_time = active_end_time FROM msdb.dbo.sysschedules_localserver_view WHERE (schedule_id = @schedule_id ) -- Fill out the values for all non-supplied parameters from the existing values IF (@new_name IS NULL) SELECT @new_name = @x_name IF (@enabled IS NULL) SELECT @enabled = @x_enabled IF (@freq_type IS NULL) SELECT @freq_type = @x_freq_type IF (@freq_interval IS NULL) SELECT @freq_interval = @x_freq_interval IF (@freq_subday_type IS NULL) SELECT @freq_subday_type = @x_freq_subday_type IF (@freq_subday_interval IS NULL) SELECT @freq_subday_interval = @x_freq_subday_interval IF (@freq_relative_interval IS NULL) SELECT @freq_relative_interval = @x_freq_relative_interval IF (@freq_recurrence_factor IS NULL) SELECT @freq_recurrence_factor = @x_freq_recurrence_factor IF (@active_start_date IS NULL) SELECT @active_start_date = @x_active_start_date IF (@active_end_date IS NULL) SELECT @active_end_date = @x_active_end_date IF (@active_start_time IS NULL) SELECT @active_start_time = @x_active_start_time IF (@active_end_time IS NULL) SELECT @active_end_time = @x_active_end_time -- Check schedule (frequency and owner) parameters EXECUTE @retval = sp_verify_schedule @schedule_id = @schedule_id, @name = @new_name, @enabled = @enabled, @freq_type = @freq_type, @freq_interval = @freq_interval OUTPUT, @freq_subday_type = @freq_subday_type OUTPUT, @freq_subday_interval = @freq_subday_interval OUTPUT, @freq_relative_interval = @freq_relative_interval OUTPUT, @freq_recurrence_factor = @freq_recurrence_factor OUTPUT, @active_start_date = @active_start_date OUTPUT, @active_start_time = @active_start_time OUTPUT, @active_end_date = @active_end_date OUTPUT, @active_end_time = @active_end_time OUTPUT, @owner_sid = @owner_sid IF (@retval <> 0) RETURN(1) -- Failure -- Update the JobSchedule UPDATE msdb.dbo.sysschedules SET name = @new_name, enabled = @enabled, freq_type = @freq_type, freq_interval = @freq_interval, freq_subday_type = @freq_subday_type, freq_subday_interval = @freq_subday_interval, freq_relative_interval = @freq_relative_interval, freq_recurrence_factor = @freq_recurrence_factor, active_start_date = @active_start_date, active_end_date = @active_end_date, active_start_time = @active_start_time, active_end_time = @active_end_time, date_modified = GETDATE(), version_number = version_number + 1 WHERE (schedule_id = @schedule_id) SELECT @retval = @@error -- Update the job's version/last-modified information UPDATE msdb.dbo.sysjobs SET version_number = version_number + 1, date_modified = GETDATE() WHERE (job_id = @job_id) -- Notify SQLServerAgent of the change, but only if we know the job has been cached IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'S', @job_id = @job_id, @schedule_id = @schedule_id, @action_type = N'U' END -- For a multi-server job, remind the user that they need to call sp_post_msx_operation IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) -- Instruct the tsx servers to pick up the altered schedule IF (@automatic_post = 1) BEGIN DECLARE @schedule_uid UNIQUEIDENTIFIER SELECT @schedule_uid = schedule_uid FROM sysschedules WHERE schedule_id = @schedule_id IF(NOT @schedule_uid IS NULL) BEGIN -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines EXECUTE sp_post_msx_operation @operation = 'INSERT', @object_type = 'SCHEDULE', @schedule_uid = @schedule_uid END END ELSE RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation') -- Automatic addition and removal of -Continous parameter for replication agent EXECUTE sp_update_replication_job_parameter @job_id = @job_id, @old_freq_type = @x_freq_type, @new_freq_type = @freq_type RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_DELETE_JOBSCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_jobschedule...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_delete_jobschedule') AND (type = 'P'))) DROP PROCEDURE sp_delete_jobschedule go CREATE PROCEDURE sp_delete_jobschedule -- This SP is deprecated. Use sp_detach_schedule and sp_delete_schedule. @job_id UNIQUEIDENTIFIER = NULL, @job_name sysname = NULL, @name sysname, @keep_schedule int = 0, @automatic_post BIT = 1 -- If 1 will post notifications to all tsx servers to that run this schedule AS BEGIN DECLARE @retval INT DECLARE @sched_count INT DECLARE @schedule_id INT DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) -- Check authority (only SQLServerAgent can delete a schedule of a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- Check that we can uniquely identify the job EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) AND (SUSER_SID() <> @job_owner_sid)) BEGIN RAISERROR(14525, -1, -1) RETURN(1) -- Failure END IF (UPPER(@name collate SQL_Latin1_General_CP1_CS_AS) = N'ALL') BEGIN SELECT @schedule_id = -1 -- We use this in the call to sp_sqlagent_notify --Delete the schedule(s) if it isn't being used by other jobs DECLARE @temp_schedules_to_delete TABLE (schedule_id INT NOT NULL) --If user requests that the schedules be removed (the legacy behavoir) --make sure it isnt being used by other jobs IF (@keep_schedule = 0) BEGIN --Get the list of schedules to delete INSERT INTO @temp_schedules_to_delete SELECT DISTINCT schedule_id FROM msdb.dbo.sysschedules WHERE (schedule_id IN (SELECT schedule_id FROM msdb.dbo.sysjobschedules WHERE (job_id = @job_id))) --make sure no other jobs use these schedules IF( EXISTS(SELECT * FROM msdb.dbo.sysjobschedules WHERE (job_id <> @job_id) AND (schedule_id in ( SELECT schedule_id FROM @temp_schedules_to_delete )))) BEGIN RAISERROR(14367, -1, -1) RETURN(1) -- Failure END END --OK to delete the jobschedule DELETE FROM msdb.dbo.sysjobschedules WHERE (job_id = @job_id) --OK to delete the schedule - temp_schedules_to_delete is empty if @keep_schedule <> 0 DELETE FROM msdb.dbo.sysschedules WHERE schedule_id IN (SELECT schedule_id from @temp_schedules_to_delete) END ELSE BEGIN -- Make sure the schedule_id can be uniquely identified and that it exists -- Note: It's safe use the values returned by the MIN() function because the SP errors out if more than 1 record exists SELECT @sched_count = COUNT(*), @schedule_id = MIN(sched.schedule_id) FROM msdb.dbo.sysjobschedules as jsched JOIN msdb.dbo.sysschedules_localserver_view as sched ON jsched.schedule_id = sched.schedule_id WHERE (jsched.job_id = @job_id) AND (sched.name = @name) -- Need to use sp_detach_schedule to remove this ambiguous schedule name IF(@sched_count > 1) BEGIN RAISERROR(14376, -1, -1, @name, @job_name) RETURN(1) -- Failure END IF (@schedule_id IS NULL) BEGIN --raise an explicit message if the schedule does exist but isn't attached to this job IF EXISTS(SELECT * FROM sysschedules_localserver_view WHERE (name = @name)) BEGIN RAISERROR(14374, -1, -1, @name, @job_name) END ELSE BEGIN --If the schedule is from an MSX and a sysadmin is calling report a specific 'MSX' message IF(PROGRAM_NAME() NOT LIKE N'SQLAgent%' AND ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 AND EXISTS(SELECT * FROM msdb.dbo.sysschedules as sched JOIN msdb.dbo.sysoriginatingservers_view as svr ON sched.originating_server_id = svr.originating_server_id JOIN msdb.dbo.sysjobschedules as js ON sched.schedule_id = js.schedule_id WHERE (svr.master_server = 1) AND (sched.name = @name) AND (js.job_id = @job_id))) BEGIN RAISERROR(14274, -1, -1) END ELSE BEGIN --Generic message that the schedule doesn't exist RAISERROR(14262, -1, -1, '@name', @name) END END RETURN(1) -- Failure END --If user requests that the schedule be removed (the legacy behavoir) --make sure it isnt being used by another job IF (@keep_schedule = 0) BEGIN IF( EXISTS(SELECT * FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) AND (job_id <> @job_id) )) BEGIN RAISERROR(14368, -1, -1, @name) RETURN(1) -- Failure END END --Delete the job schedule link first DELETE FROM msdb.dbo.sysjobschedules WHERE (job_id = @job_id) AND (schedule_id = @schedule_id) --Delete schedule if required IF (@keep_schedule = 0) BEGIN --Now delete the schedule if required DELETE FROM msdb.dbo.sysschedules WHERE (schedule_id = @schedule_id) END END -- Update the job's version/last-modified information UPDATE msdb.dbo.sysjobs SET version_number = version_number + 1, date_modified = GETDATE() WHERE (job_id = @job_id) -- Notify SQLServerAgent of the change, but only if we know the job has been cached IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'S', @job_id = @job_id, @schedule_id = @schedule_id, @action_type = N'D' END -- For a multi-server job, remind the user that they need to call sp_post_msx_operation IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) -- Instruct the tsx servers to pick up the altered schedule IF (@automatic_post = 1) BEGIN DECLARE @schedule_uid UNIQUEIDENTIFIER SELECT @schedule_uid = schedule_uid FROM sysschedules WHERE schedule_id = @schedule_id IF(NOT @schedule_uid IS NULL) BEGIN -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines EXECUTE sp_post_msx_operation @operation = 'INSERT', @object_type = 'SCHEDULE', @schedule_uid = @schedule_uid END END ELSE RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation') RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_HELP_SCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_schedule...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_schedule') AND (type = 'P'))) DROP PROCEDURE sp_help_schedule go CREATE PROCEDURE sp_help_schedule @schedule_id INT = NULL, -- If both @schedule_id and @schedule_name are NULL retreive all schedules @schedule_name sysname = NULL, @attached_schedules_only BIT = 0, -- If 1 only retreive all schedules that are attached to jobs @include_description BIT = 0 -- 1 if a schedule description is required (NOTE: It's expensive to generate the description) AS BEGIN DECLARE @retval INT DECLARE @schedule_description NVARCHAR(255) DECLARE @name sysname DECLARE @freq_type INT DECLARE @freq_interval INT DECLARE @freq_subday_type INT DECLARE @freq_subday_interval INT DECLARE @freq_relative_interval INT DECLARE @freq_recurrence_factor INT DECLARE @active_start_date INT DECLARE @active_end_date INT DECLARE @active_start_time INT DECLARE @active_end_time INT DECLARE @schedule_id_as_char VARCHAR(10) SET NOCOUNT ON -- If both @schedule_id and @schedule_name are NULL retreive all schedules (depending on @attached_schedules_only) -- otherwise verify the schedule exists IF (@schedule_id IS NOT NULL) OR (@schedule_name IS NOT NULL) BEGIN -- Check that we can uniquely identify the schedule EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = NULL, @orig_server_id = NULL IF (@retval <> 0) RETURN(1) -- Failure END -- Get the schedule(s) that are attached to a job (or all schs if @attached_schedules_only = 0) into a temporary table SELECT schedule_id, schedule_uid, 'schedule_name' = name, enabled, freq_type, freq_interval, freq_subday_type, freq_subday_interval, freq_relative_interval, freq_recurrence_factor, active_start_date, active_end_date, active_start_time, active_end_time, date_created, 'schedule_description' = FORMATMESSAGE(14549) INTO #temp_jobschedule FROM msdb.dbo.sysschedules_localserver_view as sch WHERE ( (@attached_schedules_only = 0) OR (EXISTS(SELECT * FROM sysjobschedules as jobsch WHERE sch.schedule_id = jobsch.schedule_id)) ) AND((@schedule_id IS NULL) OR (schedule_id = @schedule_id)) IF (@include_description = 1) BEGIN -- For each schedule, generate the textual schedule description and update the temporary -- table with it IF (EXISTS (SELECT * FROM #temp_jobschedule)) BEGIN WHILE (EXISTS (SELECT * FROM #temp_jobschedule WHERE schedule_description = FORMATMESSAGE(14549))) BEGIN SET ROWCOUNT 1 SELECT @name = schedule_name, @freq_type = freq_type, @freq_interval = freq_interval, @freq_subday_type = freq_subday_type, @freq_subday_interval = freq_subday_interval, @freq_relative_interval = freq_relative_interval, @freq_recurrence_factor = freq_recurrence_factor, @active_start_date = active_start_date, @active_end_date = active_end_date, @active_start_time = active_start_time, @active_end_time = active_end_time FROM #temp_jobschedule WHERE (schedule_description = FORMATMESSAGE(14549)) SET ROWCOUNT 0 EXECUTE sp_get_schedule_description @freq_type, @freq_interval, @freq_subday_type, @freq_subday_interval, @freq_relative_interval, @freq_recurrence_factor, @active_start_date, @active_end_date, @active_start_time, @active_end_time, @schedule_description OUTPUT UPDATE #temp_jobschedule SET schedule_description = ISNULL(LTRIM(RTRIM(@schedule_description)), FORMATMESSAGE(14205)) WHERE (schedule_name = @name) END -- While END END -- Return the result set, adding job count SELECT *, (SELECT COUNT(*) FROM sysjobschedules WHERE sysjobschedules.schedule_id = #temp_jobschedule.schedule_id) as 'job_count' FROM #temp_jobschedule ORDER BY schedule_id RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_HELP_JOBSCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_jobschedule...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_jobschedule') AND (type = 'P'))) DROP PROCEDURE sp_help_jobschedule go CREATE PROCEDURE sp_help_jobschedule @job_id UNIQUEIDENTIFIER = NULL, @job_name sysname = NULL, @schedule_name sysname = NULL, @schedule_id INT = NULL, @include_description BIT = 0 -- 1 if a schedule description is required (NOTE: It's expensive to generate the description) AS BEGIN DECLARE @retval INT DECLARE @schedule_description NVARCHAR(255) DECLARE @name sysname DECLARE @freq_type INT DECLARE @freq_interval INT DECLARE @freq_subday_type INT DECLARE @freq_subday_interval INT DECLARE @freq_relative_interval INT DECLARE @freq_recurrence_factor INT DECLARE @active_start_date INT DECLARE @active_end_date INT DECLARE @active_start_time INT DECLARE @active_end_time INT DECLARE @schedule_id_as_char VARCHAR(10) DECLARE @job_count INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @schedule_name = LTRIM(RTRIM(@schedule_name)) SELECT @job_count = 0 -- Turn [nullable] empty string parameters into NULLs IF (@schedule_name = N'') SELECT @schedule_name = NULL -- The user must provide either: -- 1) job_id (or job_name) and (optionally) a schedule name -- or... -- 2) just schedule_id IF (@schedule_id IS NULL) AND (@job_id IS NULL) AND (@job_name IS NULL) BEGIN RAISERROR(14273, -1, -1) RETURN(1) -- Failure END IF (@schedule_id IS NOT NULL) AND ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL) OR (@schedule_name IS NOT NULL)) BEGIN RAISERROR(14273, -1, -1) RETURN(1) -- Failure END -- Check that the schedule (by ID) exists and it is only used by one job. -- Allowing this for backward compatibility with versions prior to V9 IF (@schedule_id IS NOT NULL) AND (@job_id IS NULL) AND (@job_name IS NULL) BEGIN SELECT @job_count = COUNT(*) FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) if(@job_count > 1) BEGIN SELECT @schedule_id_as_char = CONVERT(VARCHAR, @schedule_id) RAISERROR(14369, -1, -1, @schedule_id_as_char) RETURN(1) -- Failure END SELECT @job_id = job_id FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) IF (@job_id IS NULL) BEGIN SELECT @schedule_id_as_char = CONVERT(VARCHAR, @schedule_id) RAISERROR(14262, -1, -1, '@schedule_id', @schedule_id_as_char) RETURN(1) -- Failure END END -- Check that we can uniquely identify the job IF (@job_id IS NOT NULL) OR (@job_name IS NOT NULL) BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, 'NO_TEST' IF (@retval <> 0) RETURN(1) -- Failure END IF (@schedule_id IS NOT NULL OR @schedule_name IS NOT NULL) BEGIN -- Check that we can uniquely identify the schedule EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = NULL, @orig_server_id = NULL, @job_id_filter = @job_id IF (@retval <> 0) RETURN(1) -- Failure END -- Check that the schedule (by name) exists IF (@schedule_name IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobschedules AS js JOIN msdb.dbo.sysschedules AS s ON js.schedule_id = s.schedule_id WHERE (js.job_id = @job_id) AND (s.name = @schedule_name))) BEGIN RAISERROR(14262, -1, -1, '@schedule_name', @schedule_name) RETURN(1) -- Failure END END -- Get the schedule(s) into a temporary table SELECT s.schedule_id, 'schedule_name' = name, enabled, freq_type, freq_interval, freq_subday_type, freq_subday_interval, freq_relative_interval, freq_recurrence_factor, active_start_date, active_end_date, active_start_time, active_end_time, date_created, 'schedule_description' = FORMATMESSAGE(14549), js.next_run_date, js.next_run_time, s.schedule_uid INTO #temp_jobschedule FROM msdb.dbo.sysjobschedules AS js JOIN msdb.dbo.sysschedules AS s ON js.schedule_id = s.schedule_id WHERE ((@job_id IS NULL) OR (js.job_id = @job_id)) AND ((@schedule_name IS NULL) OR (s.name = @schedule_name)) AND ((@schedule_id IS NULL) OR (s.schedule_id = @schedule_id)) IF (@include_description = 1) BEGIN -- For each schedule, generate the textual schedule description and update the temporary -- table with it IF (EXISTS (SELECT * FROM #temp_jobschedule)) BEGIN WHILE (EXISTS (SELECT * FROM #temp_jobschedule WHERE schedule_description = FORMATMESSAGE(14549))) BEGIN SET ROWCOUNT 1 SELECT @name = schedule_name, @freq_type = freq_type, @freq_interval = freq_interval, @freq_subday_type = freq_subday_type, @freq_subday_interval = freq_subday_interval, @freq_relative_interval = freq_relative_interval, @freq_recurrence_factor = freq_recurrence_factor, @active_start_date = active_start_date, @active_end_date = active_end_date, @active_start_time = active_start_time, @active_end_time = active_end_time FROM #temp_jobschedule WHERE (schedule_description = FORMATMESSAGE(14549)) SET ROWCOUNT 0 EXECUTE sp_get_schedule_description @freq_type, @freq_interval, @freq_subday_type, @freq_subday_interval, @freq_relative_interval, @freq_recurrence_factor, @active_start_date, @active_end_date, @active_start_time, @active_end_time, @schedule_description OUTPUT UPDATE #temp_jobschedule SET schedule_description = ISNULL(LTRIM(RTRIM(@schedule_description)), FORMATMESSAGE(14205)) WHERE (schedule_name = @name) END -- While END END -- Return the result set, adding job count to it SELECT *, (SELECT COUNT(*) FROM sysjobschedules WHERE sysjobschedules.schedule_id = #temp_jobschedule.schedule_id) as 'job_count' FROM #temp_jobschedule ORDER BY schedule_id RETURN(@@error) -- 0 means success END go CHECKPOINT go /**************************************************************/ /* SP_VERIFY_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_job') AND (type = 'P'))) DROP PROCEDURE sp_verify_job go CREATE PROCEDURE sp_verify_job @job_id UNIQUEIDENTIFIER, @name sysname, @enabled TINYINT, @start_step_id INT, @category_name sysname, @owner_sid VARBINARY(85) OUTPUT, -- Output since we may modify it @notify_level_eventlog INT, @notify_level_email INT OUTPUT, -- Output since we may reset it to 0 @notify_level_netsend INT OUTPUT, -- Output since we may reset it to 0 @notify_level_page INT OUTPUT, -- Output since we may reset it to 0 @notify_email_operator_name sysname, @notify_netsend_operator_name sysname, @notify_page_operator_name sysname, @delete_level INT, @category_id INT OUTPUT, -- The ID corresponding to the name @notify_email_operator_id INT OUTPUT, -- The ID corresponding to the name @notify_netsend_operator_id INT OUTPUT, -- The ID corresponding to the name @notify_page_operator_id INT OUTPUT, -- The ID corresponding to the name @originating_server sysname OUTPUT -- Output since we may modify it AS BEGIN DECLARE @job_type INT DECLARE @retval INT DECLARE @current_date INT DECLARE @res_valid_range NVARCHAR(200) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @category_name = LTRIM(RTRIM(@category_name)) SELECT @originating_server = UPPER(LTRIM(RTRIM(@originating_server))) SELECT @originating_server = ISNULL(@originating_server, UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) -- Check originating server (only the SQLServerAgent can add jobs that originate from a remote server) IF (@originating_server <> UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) AND (PROGRAM_NAME() NOT LIKE N'SQLAgent%') BEGIN RAISERROR(14275, -1, -1) RETURN(1) -- Failure END -- NOTE: We allow jobs with the same name (since job_id is always unique) but only if -- they originate from different servers. Thus jobs can flow from an MSX to a TSX -- without having to worry about naming conflicts. IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs as job JOIN msdb.dbo.sysoriginatingservers_view as svr ON (svr.originating_server_id = job.originating_server_id) WHERE (name = @name) AND (svr.originating_server = @originating_server) AND (job_id <> ISNULL(@job_id, 0x911)))) -- When adding a new job @job_id is NULL BEGIN RAISERROR(14261, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Check enabled state IF (@enabled <> 0) AND (@enabled <> 1) BEGIN RAISERROR(14266, -1, -1, '@enabled', '0, 1') RETURN(1) -- Failure END -- Check start step IF (@job_id IS NULL) BEGIN -- New job -- NOTE: For [new] MSX jobs we allow the start step to be other than 1 since -- the start step was validated when the job was created at the MSX IF (@start_step_id <> 1) AND (@originating_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) BEGIN RAISERROR(14266, -1, -1, '@start_step_id', '1') RETURN(1) -- Failure END END ELSE BEGIN -- Existing job DECLARE @max_step_id INT DECLARE @valid_range VARCHAR(50) -- Get current maximum step id SELECT @max_step_id = ISNULL(MAX(step_id), 0) FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) IF (@start_step_id < 1) OR (@start_step_id > @max_step_id + 1) BEGIN SELECT @valid_range = '1..' + CONVERT(VARCHAR, @max_step_id + 1) RAISERROR(14266, -1, -1, '@start_step_id', @valid_range) RETURN(1) -- Failure END END -- Check category SELECT @job_type = NULL IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) SELECT @job_type = 1 -- LOCAL IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) SELECT @job_type = 2 -- MULTI-SERVER -- A local job cannot be added to a multi-server job_category IF (@job_type = 1) AND (EXISTS (SELECT * FROM msdb.dbo.syscategories WHERE (category_class = 1) -- Job AND (category_type = 2) -- Multi-Server AND (name = @category_name))) BEGIN RAISERROR(14285, -1, -1) RETURN(1) -- Failure END -- A multi-server job cannot be added to a local job_category IF (@job_type = 2) AND (EXISTS (SELECT * FROM msdb.dbo.syscategories WHERE (category_class = 1) -- Job AND (category_type = 1) -- Local AND (name = @category_name))) BEGIN RAISERROR(14286, -1, -1) RETURN(1) -- Failure END -- Get the category_id, handling any special-cases as appropriate SELECT @category_id = NULL IF (@category_name = N'[DEFAULT]') -- User wants to revert to the default job category BEGIN SELECT @category_id = CASE ISNULL(@job_type, 1) WHEN 1 THEN 0 -- [Uncategorized (Local)] WHEN 2 THEN 2 -- [Uncategorized (Multi-Server)] END END ELSE IF (@category_name IS NULL) -- The sp_add_job default BEGIN SELECT @category_id = 0 END ELSE BEGIN SELECT @category_id = category_id FROM msdb.dbo.syscategories WHERE (category_class = 1) -- Job AND (name = @category_name) END IF (@category_id IS NULL) BEGIN RAISERROR(14234, -1, -1, '@category_name', 'sp_help_category') RETURN(1) -- Failure END -- Only SQLServerAgent may add jobs to the 'Jobs From MSX' category IF (@category_id = 1) AND (PROGRAM_NAME() NOT LIKE N'SQLAgent%') BEGIN RAISERROR(14267, -1, -1, @category_name) RETURN(1) -- Failure END -- Check owner -- Default the owner to be the calling user if: -- caller is not a sysadmin -- caller is not SQLAgentOperator and job_id is NULL, meaning new job IF (((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) AND ((ISNULL(IS_MEMBER('SQLAgentOperatorRole'), 0) = 0) AND @job_id IS NULL)) AND (@owner_sid <> SUSER_SID())) BEGIN SELECT @owner_sid = SUSER_SID() END -- Now just check that the login id is valid (ie. it exists and isn't an NT group) IF (@owner_sid <> 0x010100000000000512000000) AND -- NT AUTHORITY\SYSTEM sid (@owner_sid <> 0x010100000000000514000000) -- NT AUTHORITY\NETWORK SERVICE sid BEGIN IF (@owner_sid IS NULL) OR (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (sid = @owner_sid) AND (isntgroup <> 0))) BEGIN -- NOTE: In the following message we quote @owner_login_name instead of @owner_sid -- since this is the parameter the user passed to the calling SP (ie. either -- sp_add_job or sp_update_job) SELECT @res_valid_range = FORMATMESSAGE(14203) RAISERROR(14234, -1, -1, '@owner_login_name', @res_valid_range) RETURN(1) -- Failure END END -- Check notification levels (must be 0, 1, 2 or 3) IF (@notify_level_eventlog & 0x3 <> @notify_level_eventlog) BEGIN RAISERROR(14266, -1, -1, '@notify_level_eventlog', '0, 1, 2, 3') RETURN(1) -- Failure END IF (@notify_level_email & 0x3 <> @notify_level_email) BEGIN RAISERROR(14266, -1, -1, '@notify_level_email', '0, 1, 2, 3') RETURN(1) -- Failure END IF (@notify_level_netsend & 0x3 <> @notify_level_netsend) BEGIN RAISERROR(14266, -1, -1, '@notify_level_netsend', '0, 1, 2, 3') RETURN(1) -- Failure END IF (@notify_level_page & 0x3 <> @notify_level_page) BEGIN RAISERROR(14266, -1, -1, '@notify_level_page', '0, 1, 2, 3') RETURN(1) -- Failure END -- If we're at a TSX, only SQLServerAgent may add jobs that notify 'MSXOperator' IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservers)) AND ((@notify_email_operator_name = N'MSXOperator') OR (@notify_page_operator_name = N'MSXOperator') OR (@notify_netsend_operator_name = N'MSXOperator')) AND (PROGRAM_NAME() NOT LIKE N'SQLAgent%') BEGIN RAISERROR(14251, -1, -1, 'MSXOperator') RETURN(1) -- Failure END -- Check operator to notify (via email) IF (@notify_email_operator_name IS NOT NULL) BEGIN SELECT @notify_email_operator_id = id FROM msdb.dbo.sysoperators WHERE (name = @notify_email_operator_name) IF (@notify_email_operator_id IS NULL) BEGIN RAISERROR(14234, -1, -1, '@notify_email_operator_name', 'sp_help_operator') RETURN(1) -- Failure END -- If a valid operator is specified the level must be non-zero IF (@notify_level_email = 0) BEGIN RAISERROR(14266, -1, -1, '@notify_level_email', '1, 2, 3') RETURN(1) -- Failure END END ELSE BEGIN SELECT @notify_email_operator_id = 0 SELECT @notify_level_email = 0 END -- Check operator to notify (via netsend) IF (@notify_netsend_operator_name IS NOT NULL) BEGIN SELECT @notify_netsend_operator_id = id FROM msdb.dbo.sysoperators WHERE (name = @notify_netsend_operator_name) IF (@notify_netsend_operator_id IS NULL) BEGIN RAISERROR(14234, -1, -1, '@notify_netsend_operator_name', 'sp_help_operator') RETURN(1) -- Failure END -- If a valid operator is specified the level must be non-zero IF (@notify_level_netsend = 0) BEGIN RAISERROR(14266, -1, -1, '@notify_level_netsend', '1, 2, 3') RETURN(1) -- Failure END END ELSE BEGIN SELECT @notify_netsend_operator_id = 0 SELECT @notify_level_netsend = 0 END -- Check operator to notify (via page) IF (@notify_page_operator_name IS NOT NULL) BEGIN SELECT @notify_page_operator_id = id FROM msdb.dbo.sysoperators WHERE (name = @notify_page_operator_name) IF (@notify_page_operator_id IS NULL) BEGIN RAISERROR(14234, -1, -1, '@notify_page_operator_name', 'sp_help_operator') RETURN(1) -- Failure END -- If a valid operator is specified the level must be non-zero IF (@notify_level_page = 0) BEGIN RAISERROR(14266, -1, -1, '@notify_level_page', '1, 2, 3') RETURN(1) -- Failure END END ELSE BEGIN SELECT @notify_page_operator_id = 0 SELECT @notify_level_page = 0 END -- Check delete level (must be 0, 1, 2 or 3) IF (@delete_level & 0x3 <> @delete_level) BEGIN RAISERROR(14266, -1, -1, '@delete_level', '0, 1, 2, 3') RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_job') AND (type = 'P'))) DROP PROCEDURE sp_add_job go CREATE PROCEDURE sp_add_job @job_name sysname, @enabled TINYINT = 1, -- 0 = Disabled, 1 = Enabled @description NVARCHAR(512) = NULL, @start_step_id INT = 1, @category_name sysname = NULL, @category_id INT = NULL, -- A language-independent way to specify which category to use @owner_login_name sysname = NULL, -- The procedure assigns a default @notify_level_eventlog INT = 2, -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always @notify_level_email INT = 0, -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always @notify_level_netsend INT = 0, -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always @notify_level_page INT = 0, -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always @notify_email_operator_name sysname = NULL, @notify_netsend_operator_name sysname = NULL, @notify_page_operator_name sysname = NULL, @delete_level INT = 0, -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always @job_id UNIQUEIDENTIFIER = NULL OUTPUT, @originating_server sysname = NULL -- For SQLAgent use only AS BEGIN DECLARE @retval INT DECLARE @notify_email_operator_id INT DECLARE @notify_netsend_operator_id INT DECLARE @notify_page_operator_id INT DECLARE @owner_sid VARBINARY(85) DECLARE @originating_server_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters (except @owner_login_name) SELECT @originating_server = UPPER(LTRIM(RTRIM(@originating_server))) SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @description = LTRIM(RTRIM(@description)) SELECT @category_name = LTRIM(RTRIM(@category_name)) SELECT @notify_email_operator_name = LTRIM(RTRIM(@notify_email_operator_name)) SELECT @notify_netsend_operator_name = LTRIM(RTRIM(@notify_netsend_operator_name)) SELECT @notify_page_operator_name = LTRIM(RTRIM(@notify_page_operator_name)) SELECT @originating_server_id = NULL -- Turn [nullable] empty string parameters into NULLs IF (@originating_server = N'') SELECT @originating_server = NULL IF (@description = N'') SELECT @description = NULL IF (@category_name = N'') SELECT @category_name = NULL IF (@notify_email_operator_name = N'') SELECT @notify_email_operator_name = NULL IF (@notify_netsend_operator_name = N'') SELECT @notify_netsend_operator_name = NULL IF (@notify_page_operator_name = N'') SELECT @notify_page_operator_name = NULL IF (@originating_server IS NULL) OR (@originating_server = '(LOCAL)') SELECT @originating_server= UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) --only members of sysadmins role can set the owner IF (@owner_login_name IS NOT NULL AND ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) AND (@owner_login_name <> SUSER_SNAME()) BEGIN RAISERROR(14515, -1, -1) RETURN(1) -- Failure END -- Default the owner (if not supplied or if a non-sa is [illegally] trying to create a job for another user) -- allow special account only when caller is sysadmin IF (@owner_login_name = N'$(SQLAgentAccount)') AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) BEGIN SELECT @owner_sid = 0xFFFFFFFF END ELSE IF (@owner_login_name IS NULL) OR ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) AND (@owner_login_name <> SUSER_SNAME())) BEGIN SELECT @owner_sid = SUSER_SID() END ELSE BEGIN --force case insensitive comparation for NT users SELECT @owner_sid = SUSER_SID(@owner_login_name, 0) -- If @owner_login_name is invalid then SUSER_SID() will return NULL END -- Default the description (if not supplied) IF (@description IS NULL) SELECT @description = FORMATMESSAGE(14571) -- If a category ID is provided this overrides any supplied category name EXECUTE @retval = sp_verify_category_identifiers '@category_name', '@category_id', @category_name OUTPUT, @category_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check parameters EXECUTE @retval = sp_verify_job NULL, -- The job id is null since this is a new job @job_name, @enabled, @start_step_id, @category_name, @owner_sid OUTPUT, @notify_level_eventlog, @notify_level_email OUTPUT, @notify_level_netsend OUTPUT, @notify_level_page OUTPUT, @notify_email_operator_name, @notify_netsend_operator_name, @notify_page_operator_name, @delete_level, @category_id OUTPUT, @notify_email_operator_id OUTPUT, @notify_netsend_operator_id OUTPUT, @notify_page_operator_id OUTPUT, @originating_server OUTPUT IF (@retval <> 0) RETURN(1) -- Failure SELECT @originating_server_id = originating_server_id FROM msdb.dbo.sysoriginatingservers_view WHERE (originating_server = @originating_server) IF (@originating_server_id IS NULL) BEGIN RAISERROR(14370, -1, -1) RETURN(1) -- Failure END IF (@job_id IS NULL) BEGIN -- Assign the GUID SELECT @job_id = NEWID() END ELSE BEGIN -- A job ID has been provided, so check that the caller is SQLServerAgent (inserting an MSX job) IF (PROGRAM_NAME() NOT LIKE N'SQLAgent%') BEGIN RAISERROR(14274, -1, -1) RETURN(1) -- Failure END END INSERT INTO msdb.dbo.sysjobs (job_id, originating_server_id, name, enabled, description, start_step_id, category_id, owner_sid, notify_level_eventlog, notify_level_email, notify_level_netsend, notify_level_page, notify_email_operator_id, notify_netsend_operator_id, notify_page_operator_id, delete_level, date_created, date_modified, version_number) VALUES (@job_id, @originating_server_id, @job_name, @enabled, @description, @start_step_id, @category_id, @owner_sid, @notify_level_eventlog, @notify_level_email, @notify_level_netsend, @notify_level_page, @notify_email_operator_id, @notify_netsend_operator_id, @notify_page_operator_id, @delete_level, GETDATE(), GETDATE(), 1) -- Version number 1 SELECT @retval = @@error -- NOTE: We don't notify SQLServerAgent to update it's cache (we'll do this in sp_add_jobserver) RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_UPDATE_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_job') AND (type = 'P'))) DROP PROCEDURE sp_update_job go CREATE PROCEDURE sp_update_job @job_id UNIQUEIDENTIFIER = NULL, -- Must provide this or current_name @job_name sysname = NULL, -- Must provide this or job_id @new_name sysname = NULL, @enabled TINYINT = NULL, @description NVARCHAR(512) = NULL, @start_step_id INT = NULL, @category_name sysname = NULL, @owner_login_name sysname = NULL, @notify_level_eventlog INT = NULL, @notify_level_email INT = NULL, @notify_level_netsend INT = NULL, @notify_level_page INT = NULL, @notify_email_operator_name sysname = NULL, @notify_netsend_operator_name sysname = NULL, @notify_page_operator_name sysname = NULL, @delete_level INT = NULL, @automatic_post BIT = 1 -- Flag for SEM use only AS BEGIN DECLARE @retval INT DECLARE @category_id INT DECLARE @notify_email_operator_id INT DECLARE @notify_netsend_operator_id INT DECLARE @notify_page_operator_id INT DECLARE @owner_sid VARBINARY(85) DECLARE @alert_id INT DECLARE @cached_attribute_modified INT DECLARE @is_sysadmin INT DECLARE @current_owner sysname DECLARE @enable_only_used INT DECLARE @x_new_name sysname DECLARE @x_enabled TINYINT DECLARE @x_description NVARCHAR(512) DECLARE @x_start_step_id INT DECLARE @x_category_name sysname DECLARE @x_category_id INT DECLARE @x_owner_sid VARBINARY(85) DECLARE @x_notify_level_eventlog INT DECLARE @x_notify_level_email INT DECLARE @x_notify_level_netsend INT DECLARE @x_notify_level_page INT DECLARE @x_notify_email_operator_name sysname DECLARE @x_notify_netsnd_operator_name sysname DECLARE @x_notify_page_operator_name sysname DECLARE @x_delete_level INT DECLARE @x_originating_server_id INT -- Not updatable DECLARE @x_master_server BIT -- Remove any leading/trailing spaces from parameters (except @owner_login_name) SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @new_name = LTRIM(RTRIM(@new_name)) SELECT @description = LTRIM(RTRIM(@description)) SELECT @category_name = LTRIM(RTRIM(@category_name)) SELECT @notify_email_operator_name = LTRIM(RTRIM(@notify_email_operator_name)) SELECT @notify_netsend_operator_name = LTRIM(RTRIM(@notify_netsend_operator_name)) SELECT @notify_page_operator_name = LTRIM(RTRIM(@notify_page_operator_name)) SET NOCOUNT ON EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Are we modifying an attribute which SQLServerAgent caches? IF ((@new_name IS NOT NULL) OR (@enabled IS NOT NULL) OR (@start_step_id IS NOT NULL) OR (@owner_login_name IS NOT NULL) OR (@notify_level_eventlog IS NOT NULL) OR (@notify_level_email IS NOT NULL) OR (@notify_level_netsend IS NOT NULL) OR (@notify_level_page IS NOT NULL) OR (@notify_email_operator_name IS NOT NULL) OR (@notify_netsend_operator_name IS NOT NULL) OR (@notify_page_operator_name IS NOT NULL) OR (@delete_level IS NOT NULL)) SELECT @cached_attribute_modified = 1 ELSE SELECT @cached_attribute_modified = 0 -- Is @enable the only parameter used beside jobname and jobid? IF ((@enabled IS NOT NULL) AND (@new_name IS NULL) AND (@description IS NULL) AND (@start_step_id IS NULL) AND (@category_name IS NULL) AND (@owner_login_name IS NULL) AND (@notify_level_eventlog IS NULL) AND (@notify_level_email IS NULL) AND (@notify_level_netsend IS NULL) AND (@notify_level_page IS NULL) AND (@notify_email_operator_name IS NULL) AND (@notify_netsend_operator_name IS NULL) AND (@notify_page_operator_name IS NULL) AND (@delete_level IS NULL)) SELECT @enable_only_used = 1 ELSE SELECT @enable_only_used = 0 -- Set the x_ (existing) variables SELECT @x_new_name = sjv.name, @x_enabled = sjv.enabled, @x_description = sjv.description, @x_start_step_id = sjv.start_step_id, @x_category_name = sc.name, -- From syscategories @x_category_id = sc.category_id, -- From syscategories @x_owner_sid = sjv.owner_sid, @x_notify_level_eventlog = sjv.notify_level_eventlog, @x_notify_level_email = sjv.notify_level_email, @x_notify_level_netsend = sjv.notify_level_netsend, @x_notify_level_page = sjv.notify_level_page, @x_notify_email_operator_name = so1.name, -- From sysoperators @x_notify_netsnd_operator_name = so2.name, -- From sysoperators @x_notify_page_operator_name = so3.name, -- From sysoperators @x_delete_level = sjv.delete_level, @x_originating_server_id = sjv.originating_server_id, @x_master_server = master_server FROM msdb.dbo.sysjobs_view sjv LEFT OUTER JOIN msdb.dbo.sysoperators so1 ON (sjv.notify_email_operator_id = so1.id) LEFT OUTER JOIN msdb.dbo.sysoperators so2 ON (sjv.notify_netsend_operator_id = so2.id) LEFT OUTER JOIN msdb.dbo.sysoperators so3 ON (sjv.notify_page_operator_id = so3.id), msdb.dbo.syscategories sc WHERE (sjv.job_id = @job_id) AND (sjv.category_id = sc.category_id) -- Check authority (only SQLServerAgent can modify a non-local job) IF ((@x_master_server = 1) AND (PROGRAM_NAME() NOT LIKE N'SQLAgent%') ) BEGIN RAISERROR(14274, -1, -1) RETURN(1) -- Failure END -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReader and SQLAgentOperator roles that can see all jobs -- cannot modify jobs they do not own IF ( (@x_owner_sid <> SUSER_SID()) -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) -- is not sysadmin AND (@enable_only_used <> 1 OR ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) <> 1)) BEGIN RAISERROR(14525, -1, -1); RETURN(1) -- Failure END -- Check job category, only sysadmin can modify mutli-server jobs IF (EXISTS (SELECT * FROM msdb.dbo.syscategories WHERE (category_class = 1) -- Job AND (category_type = 2) -- Multi-Server AND (category_id = @x_category_id) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))) -- is not sysadmin BEGIN RAISERROR(14396, -1, -1); RETURN(1) -- Failure END IF (@new_name = N'') SELECT @new_name = NULL -- Fill out the values for all non-supplied parameters from the existing values IF (@new_name IS NULL) SELECT @new_name = @x_new_name IF (@enabled IS NULL) SELECT @enabled = @x_enabled IF (@description IS NULL) SELECT @description = @x_description IF (@start_step_id IS NULL) SELECT @start_step_id = @x_start_step_id IF (@category_name IS NULL) SELECT @category_name = @x_category_name IF (@owner_sid IS NULL) SELECT @owner_sid = @x_owner_sid IF (@notify_level_eventlog IS NULL) SELECT @notify_level_eventlog = @x_notify_level_eventlog IF (@notify_level_email IS NULL) SELECT @notify_level_email = @x_notify_level_email IF (@notify_level_netsend IS NULL) SELECT @notify_level_netsend = @x_notify_level_netsend IF (@notify_level_page IS NULL) SELECT @notify_level_page = @x_notify_level_page IF (@notify_email_operator_name IS NULL) SELECT @notify_email_operator_name = @x_notify_email_operator_name IF (@notify_netsend_operator_name IS NULL) SELECT @notify_netsend_operator_name = @x_notify_netsnd_operator_name IF (@notify_page_operator_name IS NULL) SELECT @notify_page_operator_name = @x_notify_page_operator_name IF (@delete_level IS NULL) SELECT @delete_level = @x_delete_level -- If the SA is attempting to assign ownership of the job to someone else, then convert -- the login name to an ID IF (@owner_login_name = N'$(SQLAgentAccount)') AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) BEGIN SELECT @owner_sid = 0xFFFFFFFF END ELSE IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) AND (@owner_login_name IS NOT NULL)) BEGIN --force case insensitive comparation for NT users SELECT @owner_sid = SUSER_SID(@owner_login_name, 0) -- If @owner_login_name is invalid then SUSER_SID() will return NULL END -- Only the SA can re-assign jobs IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) AND (@owner_login_name IS NOT NULL)) RAISERROR(14242, -1, -1) -- Ownership of a multi-server job cannot be assigned to a non-sysadmin IF (@owner_login_name IS NOT NULL) AND (EXISTS (SELECT * FROM msdb.dbo.sysjobs sj, msdb.dbo.sysjobservers sjs WHERE (sj.job_id = sjs.job_id) AND (sj.job_id = @job_id) AND (sjs.server_id <> 0))) BEGIN IF (@owner_login_name = N'$(SQLAgentAccount)') -- allow distributed jobs to be assigned to special account BEGIN SELECT @is_sysadmin = 1 END ELSE BEGIN SELECT @is_sysadmin = 0 EXECUTE msdb.dbo.sp_sqlagent_has_server_access @login_name = @owner_login_name, @is_sysadmin_member = @is_sysadmin OUTPUT END IF (@is_sysadmin = 0) BEGIN SELECT @current_owner = dbo.SQLAGENT_SUSER_SNAME(@x_owner_sid) RAISERROR(14543, -1, -1, @current_owner, N'sysadmin') RETURN(1) -- Failure END END -- Turn [nullable] empty string parameters into NULLs IF (@description = N'') SELECT @description = NULL IF (@category_name = N'') SELECT @category_name = NULL IF (@notify_email_operator_name = N'') SELECT @notify_email_operator_name = NULL IF (@notify_netsend_operator_name = N'') SELECT @notify_netsend_operator_name = NULL IF (@notify_page_operator_name = N'') SELECT @notify_page_operator_name = NULL -- Check new values EXECUTE @retval = sp_verify_job @job_id, @new_name, @enabled, @start_step_id, @category_name, @owner_sid OUTPUT, @notify_level_eventlog, @notify_level_email OUTPUT, @notify_level_netsend OUTPUT, @notify_level_page OUTPUT, @notify_email_operator_name, @notify_netsend_operator_name, @notify_page_operator_name, @delete_level, @category_id OUTPUT, @notify_email_operator_id OUTPUT, @notify_netsend_operator_id OUTPUT, @notify_page_operator_id OUTPUT, NULL IF (@retval <> 0) RETURN(1) -- Failure BEGIN TRANSACTION -- If the job is being re-assigned, modify sysjobsteps.database_user_name as necessary IF (@owner_login_name IS NOT NULL) BEGIN IF (EXISTS (SELECT * FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (subsystem = N'TSQL'))) BEGIN IF (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (sid = @owner_sid) AND (sysadmin <> 1))) BEGIN -- The job is being re-assigned to an non-SA UPDATE msdb.dbo.sysjobsteps SET database_user_name = NULL WHERE (job_id = @job_id) AND (subsystem = N'TSQL') END END END UPDATE msdb.dbo.sysjobs SET name = @new_name, enabled = @enabled, description = @description, start_step_id = @start_step_id, category_id = @category_id, -- Returned from sp_verify_job owner_sid = @owner_sid, notify_level_eventlog = @notify_level_eventlog, notify_level_email = @notify_level_email, notify_level_netsend = @notify_level_netsend, notify_level_page = @notify_level_page, notify_email_operator_id = @notify_email_operator_id, -- Returned from sp_verify_job notify_netsend_operator_id = @notify_netsend_operator_id, -- Returned from sp_verify_job notify_page_operator_id = @notify_page_operator_id, -- Returned from sp_verify_job delete_level = @delete_level, version_number = version_number + 1, -- Update the job's version date_modified = GETDATE() -- Update the job's last-modified information WHERE (job_id = @job_id) SELECT @retval = @@error COMMIT TRANSACTION -- Always re-post the job if it's an auto-delete job (or if we're updating an auto-delete job -- to be non-auto-delete) IF (((SELECT delete_level FROM msdb.dbo.sysjobs WHERE (job_id = @job_id)) <> 0) OR ((@x_delete_level = 1) AND (@delete_level = 0))) EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id ELSE BEGIN -- Post the update to target servers IF (@automatic_post = 1) EXECUTE msdb.dbo.sp_post_msx_operation 'UPDATE', 'JOB', @job_id END -- Keep SQLServerAgent's cache in-sync -- NOTE: We only notify SQLServerAgent if we know the job has been cached and if -- attributes other than description or category have been changed (since -- SQLServerAgent doesn't cache these two) IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0) AND (@cached_attribute_modified = 1))) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @action_type = N'U' -- If the name was changed, make SQLServerAgent re-cache any alerts that reference the job -- since the alert cache contains the job name IF ((@job_name <> @new_name) AND (EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (job_id = @job_id)))) BEGIN DECLARE sysalerts_cache_update CURSOR LOCAL FOR SELECT id FROM msdb.dbo.sysalerts WHERE (job_id = @job_id) OPEN sysalerts_cache_update FETCH NEXT FROM sysalerts_cache_update INTO @alert_id WHILE (@@fetch_status = 0) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'U' FETCH NEXT FROM sysalerts_cache_update INTO @alert_id END DEALLOCATE sysalerts_cache_update END RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_DELETE_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_job') AND (type = 'P'))) DROP PROCEDURE sp_delete_job go CREATE PROCEDURE sp_delete_job @job_id UNIQUEIDENTIFIER = NULL, -- If provided should NOT also provide job_name @job_name sysname = NULL, -- If provided should NOT also provide job_id @originating_server sysname = NULL, -- Reserved (used by SQLAgent) @delete_history BIT = 1, -- Reserved (used by SQLAgent) @delete_unused_schedule BIT = 1 -- For backward compatibility schedules are deleted by default if they are not -- being used by another job. With the introduction of reusable schedules in V9 -- callers should set this to 0 so the schedule will be preserved for reuse. AS BEGIN DECLARE @current_msx_server sysname DECLARE @bMSX_job BIT DECLARE @retval INT DECLARE @local_machine_name sysname DECLARE @category_id INT DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @originating_server = UPPER(LTRIM(RTRIM(@originating_server))) -- Turn [nullable] empty string parameters into NULLs IF (@originating_server = N'') SELECT @originating_server = NULL -- Change server name to always reflect real servername or servername\instancename IF (@originating_server IS NOT NULL AND @originating_server = '(LOCAL)') SELECT @originating_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL)) BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END -- We need either a job name or a server name, not both IF ((@job_name IS NULL) AND (@originating_server IS NULL)) OR ((@job_name IS NOT NULL) AND (@originating_server IS NOT NULL)) BEGIN RAISERROR(14279, -1, -1) RETURN(1) -- Failure END -- Get category to see if it is a misc. replication agent. @category_id will be -- NULL if there is no @job_id. select @category_id = category_id from msdb.dbo.sysjobs where job_id = @job_id -- If job name was given, determine if the job is from an MSX IF (@job_id IS NOT NULL) BEGIN SELECT @bMSX_job = CASE UPPER(originating_server) WHEN UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) THEN 0 ELSE 1 END FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id) END -- If server name was given, warn user if different from current MSX IF (@originating_server IS NOT NULL) BEGIN EXECUTE @retval = master.dbo.xp_getnetname @local_machine_name OUTPUT IF (@retval <> 0) RETURN(1) -- Failure IF ((@originating_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) OR (@originating_server = UPPER(@local_machine_name))) SELECT @originating_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @current_msx_server OUTPUT, N'no_output' SELECT @current_msx_server = UPPER(@current_msx_server) -- If server name was given but it's not the current MSX, print a warning SELECT @current_msx_server = LTRIM(RTRIM(@current_msx_server)) IF ((@current_msx_server IS NOT NULL) AND (@current_msx_server <> N'') AND (@originating_server <> @current_msx_server)) RAISERROR(14224, 0, 1, @current_msx_server) END -- Check authority (only SQLServerAgent can delete a non-local job) IF (((@originating_server IS NOT NULL) AND (@originating_server <> UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))))) OR (@bMSX_job = 1)) AND (PROGRAM_NAME() NOT LIKE N'SQLAgent%') BEGIN RAISERROR(14274, -1, -1) RETURN(1) -- Failure END -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReader and SQLAgentOperator roles that can see all jobs -- cannot delete jobs they do not own IF (@job_id IS NOT NULL) BEGIN IF (@job_owner_sid <> SUSER_SID() -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) -- is not sysadmin BEGIN RAISERROR(14525, -1, -1); RETURN(1) -- Failure END END -- Do the delete (for a specific job) IF (@job_id IS NOT NULL) BEGIN -- Note: This temp table is referenced by msdb.dbo.sp_delete_job_references, -- so it cannot be declared as a local table. CREATE TABLE #temp_jobs_to_delete (job_id UNIQUEIDENTIFIER NOT NULL PRIMARY KEY CLUSTERED, job_is_cached INT NOT NULL) DECLARE @temp_schedules_to_delete TABLE (schedule_id INT NOT NULL) INSERT INTO #temp_jobs_to_delete SELECT job_id, (SELECT COUNT(*) FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0)) FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id) -- Check if we have any work to do IF (NOT EXISTS (SELECT * FROM #temp_jobs_to_delete)) BEGIN DROP TABLE #temp_jobs_to_delete RETURN(0) -- Success END -- Post the delete to any target servers (need to do this BEFORE -- deleting the job itself, but AFTER clearing all all pending -- download instructions). Note that if the job is NOT a -- multi-server job then sp_post_msx_operation will catch this and -- will do nothing. Since it will do nothing that is why we need -- to NOT delete any pending delete requests, because that delete -- request might have been for the last target server and thus -- this job isn't a multi-server job anymore so posting the global -- delete would do nothing. DELETE FROM msdb.dbo.sysdownloadlist WHERE (object_id = @job_id) and (operation_code != 3) -- Delete EXECUTE msdb.dbo.sp_post_msx_operation 'DELETE', 'JOB', @job_id -- Must do this before deleting the job itself since sp_sqlagent_notify does a lookup on sysjobs_view -- Note: Don't notify agent in this call. It is done after the transaction is committed -- just in case this job is in the process of deleting itself EXECUTE msdb.dbo.sp_delete_job_references @notify_sqlagent = 0 -- Delete all traces of the job BEGIN TRANSACTION DECLARE @err int --Get the schedules to delete before deleting records from sysjobschedules IF(@delete_unused_schedule = 1) BEGIN --Get the list of schedules to delete INSERT INTO @temp_schedules_to_delete SELECT DISTINCT schedule_id FROM msdb.dbo.sysschedules WHERE (schedule_id IN (SELECT schedule_id FROM msdb.dbo.sysjobschedules WHERE (job_id = @job_id))) END DELETE FROM msdb.dbo.sysjobschedules WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete) DELETE FROM msdb.dbo.sysjobservers WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete) DELETE FROM msdb.dbo.sysjobsteps WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete) DELETE FROM msdb.dbo.sysjobs WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete) SELECT @err = @@ERROR IF @err <> 0 BEGIN ROLLBACK TRANSACTION RETURN @err END --Delete the schedule(s) if requested to and it isn't being used by other jobs IF(@delete_unused_schedule = 1) BEGIN --Now OK to delete the schedule DELETE FROM msdb.dbo.sysschedules WHERE schedule_id IN (SELECT schedule_id FROM @temp_schedules_to_delete as sdel WHERE NOT EXISTS(SELECT * FROM msdb.dbo.sysjobschedules AS js WHERE (js.schedule_id = sdel.schedule_id))) END -- Delete the job history if requested IF (@delete_history = 1) BEGIN DELETE FROM msdb.dbo.sysjobhistory WHERE job_id IN (SELECT job_id FROM #temp_jobs_to_delete) END -- All done COMMIT TRANSACTION -- Now notify agent to delete the job. IF(EXISTS(SELECT * FROM #temp_jobs_to_delete WHERE job_is_cached > 0)) BEGIN DECLARE @nt_user_name NVARCHAR(100) SELECT @nt_user_name = ISNULL(NT_CLIENT(), ISNULL(SUSER_SNAME(), FORMATMESSAGE(14205))) --Call the xp directly. sp_sqlagent_notify checks sysjobs_view and the record has already been deleted EXEC master.dbo.xp_sqlagent_notify N'J', @job_id, 0, 0, N'D', @nt_user_name, 1, @@trancount, NULL, NULL END END ELSE -- Do the delete (for all jobs originating from the specific server) IF (@originating_server IS NOT NULL) BEGIN EXECUTE msdb.dbo.sp_delete_all_msx_jobs @msx_server = @originating_server -- NOTE: In this case there is no need to propagate the delete via sp_post_msx_operation -- since this type of delete is only ever performed on a TSX. END IF (OBJECT_ID(N'tempdb.dbo.#temp_jobs_to_delete', 'U') IS NOT NULL) DROP TABLE #temp_jobs_to_delete RETURN(0) -- 0 means success END go /**************************************************************/ /* SP_GET_COMPOSITE_JOB_INFO */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_composite_job_info...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_get_composite_job_info') AND (type = 'P'))) DROP PROCEDURE sp_get_composite_job_info go CREATE PROCEDURE sp_get_composite_job_info @job_id UNIQUEIDENTIFIER = NULL, @job_type VARCHAR(12) = NULL, -- LOCAL or MULTI-SERVER @owner_login_name sysname = NULL, @subsystem NVARCHAR(40) = NULL, @category_id INT = NULL, @enabled TINYINT = NULL, @execution_status INT = NULL, -- 0 = Not idle or suspended, 1 = Executing, 2 = Waiting For Thread, 3 = Between Retries, 4 = Idle, 5 = Suspended, [6 = WaitingForStepToFinish], 7 = PerformingCompletionActions @date_comparator CHAR(1) = NULL, -- >, < or = @date_created DATETIME = NULL, @date_last_modified DATETIME = NULL, @description NVARCHAR(512) = NULL, -- We do a LIKE on this so it can include wildcards @schedule_id INT = NULL -- if supplied only return the jobs that use this schedule AS BEGIN DECLARE @can_see_all_running_jobs INT DECLARE @job_owner sysname SET NOCOUNT ON -- By 'composite' we mean a combination of sysjobs and xp_sqlagent_enum_jobs data. -- This proc should only ever be called by sp_help_job, so we don't verify the -- parameters (sp_help_job has already done this). -- Step 1: Create intermediate work tables DECLARE @job_execution_state TABLE (job_id UNIQUEIDENTIFIER NOT NULL, date_started INT NOT NULL, time_started INT NOT NULL, execution_job_status INT NOT NULL, execution_step_id INT NULL, execution_step_name sysname COLLATE database_default NULL, execution_retry_attempt INT NOT NULL, next_run_date INT NOT NULL, next_run_time INT NOT NULL, next_run_schedule_id INT NOT NULL) DECLARE @filtered_jobs TABLE (job_id UNIQUEIDENTIFIER NOT NULL, date_created DATETIME NOT NULL, date_last_modified DATETIME NOT NULL, current_execution_status INT NULL, current_execution_step sysname COLLATE database_default NULL, current_retry_attempt INT NULL, last_run_date INT NOT NULL, last_run_time INT NOT NULL, last_run_outcome INT NOT NULL, next_run_date INT NULL, next_run_time INT NULL, next_run_schedule_id INT NULL, type INT NOT NULL) DECLARE @xp_results TABLE (job_id UNIQUEIDENTIFIER NOT NULL, last_run_date INT NOT NULL, last_run_time INT NOT NULL, next_run_date INT NOT NULL, next_run_time INT NOT NULL, next_run_schedule_id INT NOT NULL, requested_to_run INT NOT NULL, -- BOOL request_source INT NOT NULL, request_source_id sysname COLLATE database_default NULL, running INT NOT NULL, -- BOOL current_step INT NOT NULL, current_retry_attempt INT NOT NULL, job_state INT NOT NULL) -- Step 2: Capture job execution information (for local jobs only since that's all SQLServerAgent caches) SELECT @can_see_all_running_jobs = ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) IF (@can_see_all_running_jobs = 0) BEGIN SELECT @can_see_all_running_jobs = ISNULL(IS_MEMBER(N'SQLAgentReaderRole'), 0) END SELECT @job_owner = SUSER_SNAME() IF ((@@microsoftversion / 0x01000000) >= 8) -- SQL Server 8.0 or greater INSERT INTO @xp_results EXECUTE master.dbo.xp_sqlagent_enum_jobs @can_see_all_running_jobs, @job_owner, @job_id ELSE INSERT INTO @xp_results EXECUTE master.dbo.xp_sqlagent_enum_jobs @can_see_all_running_jobs, @job_owner INSERT INTO @job_execution_state SELECT xpr.job_id, xpr.last_run_date, xpr.last_run_time, xpr.job_state, sjs.step_id, sjs.step_name, xpr.current_retry_attempt, xpr.next_run_date, xpr.next_run_time, xpr.next_run_schedule_id FROM @xp_results xpr LEFT OUTER JOIN msdb.dbo.sysjobsteps sjs ON ((xpr.job_id = sjs.job_id) AND (xpr.current_step = sjs.step_id)), msdb.dbo.sysjobs_view sjv WHERE (sjv.job_id = xpr.job_id) -- Step 3: Filter on everything but dates and job_type IF ((@subsystem IS NULL) AND (@owner_login_name IS NULL) AND (@enabled IS NULL) AND (@category_id IS NULL) AND (@execution_status IS NULL) AND (@description IS NULL) AND (@job_id IS NULL)) BEGIN -- Optimize for the frequently used case... INSERT INTO @filtered_jobs SELECT sjv.job_id, sjv.date_created, sjv.date_modified, ISNULL(jes.execution_job_status, 4), -- Will be NULL if the job is non-local or is not in @job_execution_state (NOTE: 4 = STATE_IDLE) CASE ISNULL(jes.execution_step_id, 0) WHEN 0 THEN NULL -- Will be NULL if the job is non-local or is not in @job_execution_state ELSE CONVERT(NVARCHAR, jes.execution_step_id) + N' (' + jes.execution_step_name + N')' END, jes.execution_retry_attempt, -- Will be NULL if the job is non-local or is not in @job_execution_state 0, -- last_run_date placeholder (we'll fix it up in step 3.3) 0, -- last_run_time placeholder (we'll fix it up in step 3.3) 5, -- last_run_outcome placeholder (we'll fix it up in step 3.3 - NOTE: We use 5 just in case there are no jobservers for the job) jes.next_run_date, -- Will be NULL if the job is non-local or is not in @job_execution_state jes.next_run_time, -- Will be NULL if the job is non-local or is not in @job_execution_state jes.next_run_schedule_id, -- Will be NULL if the job is non-local or is not in @job_execution_state 0 -- type placeholder (we'll fix it up in step 3.4) FROM msdb.dbo.sysjobs_view sjv LEFT OUTER JOIN @job_execution_state jes ON (sjv.job_id = jes.job_id) WHERE ((@schedule_id IS NULL) OR (EXISTS(SELECT * FROM sysjobschedules as js WHERE (sjv.job_id = js.job_id) AND (js.schedule_id = @schedule_id)))) END ELSE BEGIN INSERT INTO @filtered_jobs SELECT DISTINCT sjv.job_id, sjv.date_created, sjv.date_modified, ISNULL(jes.execution_job_status, 4), -- Will be NULL if the job is non-local or is not in @job_execution_state (NOTE: 4 = STATE_IDLE) CASE ISNULL(jes.execution_step_id, 0) WHEN 0 THEN NULL -- Will be NULL if the job is non-local or is not in @job_execution_state ELSE CONVERT(NVARCHAR, jes.execution_step_id) + N' (' + jes.execution_step_name + N')' END, jes.execution_retry_attempt, -- Will be NULL if the job is non-local or is not in @job_execution_state 0, -- last_run_date placeholder (we'll fix it up in step 3.3) 0, -- last_run_time placeholder (we'll fix it up in step 3.3) 5, -- last_run_outcome placeholder (we'll fix it up in step 3.3 - NOTE: We use 5 just in case there are no jobservers for the job) jes.next_run_date, -- Will be NULL if the job is non-local or is not in @job_execution_state jes.next_run_time, -- Will be NULL if the job is non-local or is not in @job_execution_state jes.next_run_schedule_id, -- Will be NULL if the job is non-local or is not in @job_execution_state 0 -- type placeholder (we'll fix it up in step 3.4) FROM msdb.dbo.sysjobs_view sjv LEFT OUTER JOIN @job_execution_state jes ON (sjv.job_id = jes.job_id) LEFT OUTER JOIN msdb.dbo.sysjobsteps sjs ON (sjv.job_id = sjs.job_id) WHERE ((@subsystem IS NULL) OR (sjs.subsystem = @subsystem)) AND ((@owner_login_name IS NULL) OR (sjv.owner_sid = dbo.SQLAGENT_SUSER_SID(@owner_login_name)))--force case insensitive comparation for NT users AND ((@enabled IS NULL) OR (sjv.enabled = @enabled)) AND ((@category_id IS NULL) OR (sjv.category_id = @category_id)) AND ((@execution_status IS NULL) OR ((@execution_status > 0) AND (jes.execution_job_status = @execution_status)) OR ((@execution_status = 0) AND (jes.execution_job_status <> 4) AND (jes.execution_job_status <> 5))) AND ((@description IS NULL) OR (sjv.description LIKE @description)) AND ((@job_id IS NULL) OR (sjv.job_id = @job_id)) AND ((@schedule_id IS NULL) OR (EXISTS(SELECT * FROM sysjobschedules as js WHERE (sjv.job_id = js.job_id) AND (js.schedule_id = @schedule_id)))) END -- Step 3.1: Change the execution status of non-local jobs from 'Idle' to 'Unknown' UPDATE @filtered_jobs SET current_execution_status = NULL WHERE (current_execution_status = 4) AND (job_id IN (SELECT job_id FROM msdb.dbo.sysjobservers WHERE (server_id <> 0))) -- Step 3.2: Check that if the user asked to see idle jobs that we still have some. -- If we don't have any then the query should return no rows. IF (@execution_status = 4) AND (NOT EXISTS (SELECT * FROM @filtered_jobs WHERE (current_execution_status = 4))) BEGIN DELETE FROM @filtered_jobs END -- Step 3.3: Populate the last run date/time/outcome [this is a little tricky since for -- multi-server jobs there are multiple last run details in sysjobservers, so -- we simply choose the most recent]. IF (EXISTS (SELECT * FROM msdb.dbo.systargetservers)) BEGIN UPDATE @filtered_jobs SET last_run_date = sjs.last_run_date, last_run_time = sjs.last_run_time, last_run_outcome = sjs.last_run_outcome FROM @filtered_jobs fj, msdb.dbo.sysjobservers sjs WHERE (CONVERT(FLOAT, sjs.last_run_date) * 1000000) + sjs.last_run_time = (SELECT MAX((CONVERT(FLOAT, last_run_date) * 1000000) + last_run_time) FROM msdb.dbo.sysjobservers WHERE (job_id = sjs.job_id)) AND (fj.job_id = sjs.job_id) END ELSE BEGIN UPDATE @filtered_jobs SET last_run_date = sjs.last_run_date, last_run_time = sjs.last_run_time, last_run_outcome = sjs.last_run_outcome FROM @filtered_jobs fj, msdb.dbo.sysjobservers sjs WHERE (fj.job_id = sjs.job_id) END -- Step 3.4 : Set the type of the job to local (1) or multi-server (2) -- NOTE: If the job has no jobservers then it wil have a type of 0 meaning -- unknown. This is marginally inconsistent with the behaviour of -- defaulting the category of a new job to [Uncategorized (Local)], but -- prevents incompletely defined jobs from erroneously showing up as valid -- local jobs. UPDATE @filtered_jobs SET type = 1 -- LOCAL FROM @filtered_jobs fj, msdb.dbo.sysjobservers sjs WHERE (fj.job_id = sjs.job_id) AND (server_id = 0) UPDATE @filtered_jobs SET type = 2 -- MULTI-SERVER FROM @filtered_jobs fj, msdb.dbo.sysjobservers sjs WHERE (fj.job_id = sjs.job_id) AND (server_id <> 0) -- Step 4: Filter on job_type IF (@job_type IS NOT NULL) BEGIN IF (UPPER(@job_type collate SQL_Latin1_General_CP1_CS_AS) = 'LOCAL') DELETE FROM @filtered_jobs WHERE (type <> 1) -- IE. Delete all the non-local jobs IF (UPPER(@job_type collate SQL_Latin1_General_CP1_CS_AS) = 'MULTI-SERVER') DELETE FROM @filtered_jobs WHERE (type <> 2) -- IE. Delete all the non-multi-server jobs END -- Step 5: Filter on dates IF (@date_comparator IS NOT NULL) BEGIN IF (@date_created IS NOT NULL) BEGIN IF (@date_comparator = '=') DELETE FROM @filtered_jobs WHERE (date_created <> @date_created) IF (@date_comparator = '>') DELETE FROM @filtered_jobs WHERE (date_created <= @date_created) IF (@date_comparator = '<') DELETE FROM @filtered_jobs WHERE (date_created >= @date_created) END IF (@date_last_modified IS NOT NULL) BEGIN IF (@date_comparator = '=') DELETE FROM @filtered_jobs WHERE (date_last_modified <> @date_last_modified) IF (@date_comparator = '>') DELETE FROM @filtered_jobs WHERE (date_last_modified <= @date_last_modified) IF (@date_comparator = '<') DELETE FROM @filtered_jobs WHERE (date_last_modified >= @date_last_modified) END END -- Return the result set (NOTE: No filtering occurs here) SELECT sjv.job_id, originating_server, sjv.name, sjv.enabled, sjv.description, sjv.start_step_id, category = ISNULL(sc.name, FORMATMESSAGE(14205)), owner = dbo.SQLAGENT_SUSER_SNAME(sjv.owner_sid), sjv.notify_level_eventlog, sjv.notify_level_email, sjv.notify_level_netsend, sjv.notify_level_page, notify_email_operator = ISNULL(so1.name, FORMATMESSAGE(14205)), notify_netsend_operator = ISNULL(so2.name, FORMATMESSAGE(14205)), notify_page_operator = ISNULL(so3.name, FORMATMESSAGE(14205)), sjv.delete_level, sjv.date_created, sjv.date_modified, sjv.version_number, fj.last_run_date, fj.last_run_time, fj.last_run_outcome, next_run_date = ISNULL(fj.next_run_date, 0), -- This column will be NULL if the job is non-local next_run_time = ISNULL(fj.next_run_time, 0), -- This column will be NULL if the job is non-local next_run_schedule_id = ISNULL(fj.next_run_schedule_id, 0), -- This column will be NULL if the job is non-local current_execution_status = ISNULL(fj.current_execution_status, 0), -- This column will be NULL if the job is non-local current_execution_step = ISNULL(fj.current_execution_step, N'0 ' + FORMATMESSAGE(14205)), -- This column will be NULL if the job is non-local current_retry_attempt = ISNULL(fj.current_retry_attempt, 0), -- This column will be NULL if the job is non-local has_step = (SELECT COUNT(*) FROM msdb.dbo.sysjobsteps sjst WHERE (sjst.job_id = sjv.job_id)), has_schedule = (SELECT COUNT(*) FROM msdb.dbo.sysjobschedules sjsch WHERE (sjsch.job_id = sjv.job_id)), has_target = (SELECT COUNT(*) FROM msdb.dbo.sysjobservers sjs WHERE (sjs.job_id = sjv.job_id)), type = fj.type FROM @filtered_jobs fj LEFT OUTER JOIN msdb.dbo.sysjobs_view sjv ON (fj.job_id = sjv.job_id) LEFT OUTER JOIN msdb.dbo.sysoperators so1 ON (sjv.notify_email_operator_id = so1.id) LEFT OUTER JOIN msdb.dbo.sysoperators so2 ON (sjv.notify_netsend_operator_id = so2.id) LEFT OUTER JOIN msdb.dbo.sysoperators so3 ON (sjv.notify_page_operator_id = so3.id) LEFT OUTER JOIN msdb.dbo.syscategories sc ON (sjv.category_id = sc.category_id) ORDER BY sjv.job_id END go /**************************************************************/ /* SP_HELP_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_job') AND (type = 'P'))) DROP PROCEDURE sp_help_job go CREATE PROCEDURE sp_help_job -- Individual job parameters @job_id UNIQUEIDENTIFIER = NULL, -- If provided should NOT also provide job_name @job_name sysname = NULL, -- If provided should NOT also provide job_id @job_aspect VARCHAR(9) = NULL, -- JOB, STEPS, SCHEDULES, TARGETS or ALL -- Job set parameters @job_type VARCHAR(12) = NULL, -- LOCAL or MULTI-SERVER @owner_login_name sysname = NULL, @subsystem NVARCHAR(40) = NULL, @category_name sysname = NULL, @enabled TINYINT = NULL, @execution_status INT = NULL, -- 1 = Executing, 2 = Waiting For Thread, 3 = Between Retries, 4 = Idle, 5 = Suspended, 6 = [obsolete], 7 = PerformingCompletionActions @date_comparator CHAR(1) = NULL, -- >, < or = @date_created DATETIME = NULL, @date_last_modified DATETIME = NULL, @description NVARCHAR(512) = NULL -- We do a LIKE on this so it can include wildcards AS BEGIN DECLARE @retval INT DECLARE @category_id INT DECLARE @job_id_as_char VARCHAR(36) DECLARE @res_valid_range NVARCHAR(200) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters (except @owner_login_name) SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @job_aspect = LTRIM(RTRIM(@job_aspect)) SELECT @job_type = LTRIM(RTRIM(@job_type)) SELECT @subsystem = LTRIM(RTRIM(@subsystem)) SELECT @category_name = LTRIM(RTRIM(@category_name)) SELECT @description = LTRIM(RTRIM(@description)) -- Turn [nullable] empty string parameters into NULLs IF (@job_name = N'') SELECT @job_name = NULL IF (@job_aspect = '') SELECT @job_aspect = NULL IF (@job_type = '') SELECT @job_type = NULL IF (@owner_login_name = N'') SELECT @owner_login_name = NULL IF (@subsystem = N'') SELECT @subsystem = NULL IF (@category_name = N'') SELECT @category_name = NULL IF (@description = N'') SELECT @description = NULL IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL)) BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) -- If the user provided a job name or id but no aspect, default to ALL IF ((@job_name IS NOT NULL) OR (@job_id IS NOT NULL)) AND (@job_aspect IS NULL) SELECT @job_aspect = 'ALL' -- The caller must supply EITHER job name (or job id) and aspect OR one-or-more of the set -- parameters OR no parameters at all IF (((@job_name IS NOT NULL) OR (@job_id IS NOT NULL)) AND ((@job_aspect IS NULL) OR (@job_type IS NOT NULL) OR (@owner_login_name IS NOT NULL) OR (@subsystem IS NOT NULL) OR (@category_name IS NOT NULL) OR (@enabled IS NOT NULL) OR (@date_comparator IS NOT NULL) OR (@date_created IS NOT NULL) OR (@date_last_modified IS NOT NULL))) OR ((@job_name IS NULL) AND (@job_id IS NULL) AND (@job_aspect IS NOT NULL)) BEGIN RAISERROR(14280, -1, -1) RETURN(1) -- Failure END IF (@job_id IS NOT NULL) BEGIN -- Individual job... -- Check job aspect SELECT @job_aspect = UPPER(@job_aspect collate SQL_Latin1_General_CP1_CS_AS) IF (@job_aspect NOT IN ('JOB', 'STEPS', 'SCHEDULES', 'TARGETS', 'ALL')) BEGIN RAISERROR(14266, -1, -1, '@job_aspect', 'JOB, STEPS, SCHEDULES, TARGETS, ALL') RETURN(1) -- Failure END -- Generate results set... IF (@job_aspect IN ('JOB', 'ALL')) BEGIN IF (@job_aspect = 'ALL') BEGIN RAISERROR(14213, 0, 1) PRINT REPLICATE('=', DATALENGTH(FORMATMESSAGE(14213)) / 2) END EXECUTE sp_get_composite_job_info @job_id, @job_type, @owner_login_name, @subsystem, @category_id, @enabled, @execution_status, @date_comparator, @date_created, @date_last_modified, @description END IF (@job_aspect IN ('STEPS', 'ALL')) BEGIN IF (@job_aspect = 'ALL') BEGIN PRINT '' RAISERROR(14214, 0, 1) PRINT REPLICATE('=', DATALENGTH(FORMATMESSAGE(14214)) / 2) END EXECUTE ('EXECUTE sp_help_jobstep @job_id = ''' + @job_id_as_char + ''', @suffix = 1') END IF (@job_aspect IN ('SCHEDULES', 'ALL')) BEGIN IF (@job_aspect = 'ALL') BEGIN PRINT '' RAISERROR(14215, 0, 1) PRINT REPLICATE('=', DATALENGTH(FORMATMESSAGE(14215)) / 2) END EXECUTE ('EXECUTE sp_help_jobschedule @job_id = ''' + @job_id_as_char + '''') END IF (@job_aspect IN ('TARGETS', 'ALL')) BEGIN IF (@job_aspect = 'ALL') BEGIN PRINT '' RAISERROR(14216, 0, 1) PRINT REPLICATE('=', DATALENGTH(FORMATMESSAGE(14216)) / 2) END EXECUTE ('EXECUTE sp_help_jobserver @job_id = ''' + @job_id_as_char + ''', @show_last_run_details = 1') END END ELSE BEGIN -- Set of jobs... -- Check job type IF (@job_type IS NOT NULL) BEGIN SELECT @job_type = UPPER(@job_type collate SQL_Latin1_General_CP1_CS_AS) IF (@job_type NOT IN ('LOCAL', 'MULTI-SERVER')) BEGIN RAISERROR(14266, -1, -1, '@job_type', 'LOCAL, MULTI-SERVER') RETURN(1) -- Failure END END -- Check owner IF (@owner_login_name IS NOT NULL) BEGIN IF (SUSER_SID(@owner_login_name, 0) IS NULL)--force case insensitive comparation for NT users BEGIN RAISERROR(14262, -1, -1, '@owner_login_name', @owner_login_name) RETURN(1) -- Failure END END -- Check subsystem IF (@subsystem IS NOT NULL) BEGIN EXECUTE @retval = sp_verify_subsystem @subsystem IF (@retval <> 0) RETURN(1) -- Failure END -- Check job category IF (@category_name IS NOT NULL) BEGIN SELECT @category_id = category_id FROM msdb.dbo.syscategories WHERE (category_class = 1) -- Job AND (name = @category_name) IF (@category_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@category_name', @category_name) RETURN(1) -- Failure END END -- Check enabled state IF (@enabled IS NOT NULL) AND (@enabled NOT IN (0, 1)) BEGIN RAISERROR(14266, -1, -1, '@enabled', '0, 1') RETURN(1) -- Failure END -- Check current execution status IF (@execution_status IS NOT NULL) BEGIN IF (@execution_status NOT IN (0, 1, 2, 3, 4, 5, 7)) BEGIN SELECT @res_valid_range = FORMATMESSAGE(14204) RAISERROR(14266, -1, -1, '@execution_status', @res_valid_range) RETURN(1) -- Failure END END -- If a date comparator is supplied, we must have either a date-created or date-last-modified IF ((@date_comparator IS NOT NULL) AND (@date_created IS NOT NULL) AND (@date_last_modified IS NOT NULL)) OR ((@date_comparator IS NULL) AND ((@date_created IS NOT NULL) OR (@date_last_modified IS NOT NULL))) BEGIN RAISERROR(14282, -1, -1) RETURN(1) -- Failure END -- Check dates / comparator IF (@date_comparator IS NOT NULL) AND (@date_comparator NOT IN ('=', '<', '>')) BEGIN RAISERROR(14266, -1, -1, '@date_comparator', '=, >, <') RETURN(1) -- Failure END IF (@date_created IS NOT NULL) AND ((@date_created < '19900101') OR (@date_created > '99991231 23:59')) BEGIN RAISERROR(14266, -1, -1, '@date_created', '1990-01-01 12:00am .. 9999-12-31 11:59pm') RETURN(1) -- Failure END IF (@date_last_modified IS NOT NULL) AND ((@date_last_modified < '19900101') OR (@date_last_modified > '99991231 23:59')) BEGIN RAISERROR(14266, -1, -1, '@date_last_modified', '1990-01-01 12:00am .. 9999-12-31 11:59pm') RETURN(1) -- Failure END -- Generate results set... EXECUTE sp_get_composite_job_info @job_id, @job_type, @owner_login_name, @subsystem, @category_id, @enabled, @execution_status, @date_comparator, @date_created, @date_last_modified, @description END RETURN(0) -- Success END go CHECKPOINT go /**************************************************************/ /* sp_help_jobcount */ /* At least one parameter must be specified */ /* returns a one row/one column recordset with a integer */ /* representing the number of jobs assigned to the */ /* specified schedule */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_jobcount...' GO IF (NOT OBJECT_ID(N'dbo.sp_help_jobcount', 'P') IS NULL) DROP PROCEDURE dbo.sp_help_jobcount GO CREATE PROCEDURE sp_help_jobcount @schedule_name sysname = NULL, -- Specify if @schedule_id is null @schedule_id INT = NULL -- Specify if @schedule_name is null AS BEGIN SET NOCOUNT ON DECLARE @retval INT -- Check that we can uniquely identify the schedule. This only returns a schedule that is visible to this user EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = NULL, @orig_server_id = NULL IF (@retval <> 0) RETURN(1) -- Failure SELECT COUNT(*) AS JobCount FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) RETURN (0) -- 0 means success END go /**************************************************************/ /* sp_help_jobs_in_schedule */ /* At least one parameter must be specified to identify */ /* the schedule. Returns the same information as */ /* sp_help_job. Only jobs in the specified schedule are */ /* in the recordset */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_jobs_in_schedule...' GO IF (NOT OBJECT_ID(N'dbo.sp_help_jobs_in_schedule', 'P') IS NULL) DROP PROCEDURE dbo.sp_help_jobs_in_schedule GO CREATE PROCEDURE sp_help_jobs_in_schedule @schedule_name sysname = NULL, -- Specify if @schedule_id is null @schedule_id INT = NULL -- Specify if @schedule_name is null AS BEGIN SET NOCOUNT ON DECLARE @retval INT -- Check that we can uniquely identify the schedule. This only returns a schedule that is visible to this user EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = NULL, @orig_server_id = NULL IF (@retval <> 0) RETURN(1) -- Failure EXECUTE @retval = msdb.dbo.sp_get_composite_job_info @schedule_id = @schedule_id IF (@retval <> 0) RETURN(1) -- Failure RETURN (0) -- 0 means success END go /**************************************************************/ /* SP_MANAGE_JOBS_BY_LOGIN */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_manage_jobs_by_login...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_manage_jobs_by_login') AND (type = 'P'))) DROP PROCEDURE sp_manage_jobs_by_login go CREATE PROCEDURE sp_manage_jobs_by_login @action VARCHAR(10), -- DELETE or REASSIGN @current_owner_login_name sysname, @new_owner_login_name sysname = NULL AS BEGIN DECLARE @current_sid VARBINARY(85) DECLARE @new_sid VARBINARY(85) DECLARE @job_id UNIQUEIDENTIFIER DECLARE @rows_affected INT DECLARE @is_sysadmin INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @action = LTRIM(RTRIM(@action)) SELECT @current_owner_login_name = LTRIM(RTRIM(@current_owner_login_name)) SELECT @new_owner_login_name = LTRIM(RTRIM(@new_owner_login_name)) -- Turn [nullable] empty string parameters into NULLs IF (@new_owner_login_name = N'') SELECT @new_owner_login_name = NULL -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check action IF (@action NOT IN ('DELETE', 'REASSIGN')) BEGIN RAISERROR(14266, -1, -1, '@action', 'DELETE, REASSIGN') RETURN(1) -- Failure END -- Check parameter combinations IF ((@action = 'DELETE') AND (@new_owner_login_name IS NOT NULL)) RAISERROR(14281, 0, 1) IF ((@action = 'REASSIGN') AND (@new_owner_login_name IS NULL)) BEGIN RAISERROR(14237, -1, -1) RETURN(1) -- Failure END -- Check current login SELECT @current_sid = dbo.SQLAGENT_SUSER_SID(@current_owner_login_name) IF (@current_sid IS NULL) BEGIN RAISERROR(14262, -1, -1, '@current_owner_login_name', @current_owner_login_name) RETURN(1) -- Failure END -- Check new login (if supplied) IF (@new_owner_login_name IS NOT NULL) BEGIN SELECT @new_sid = dbo.SQLAGENT_SUSER_SID(@new_owner_login_name) IF (@new_sid IS NULL) BEGIN RAISERROR(14262, -1, -1, '@new_owner_login_name', @new_owner_login_name) RETURN(1) -- Failure END END IF (@action = 'DELETE') BEGIN DECLARE jobs_to_delete CURSOR LOCAL FOR SELECT job_id FROM msdb.dbo.sysjobs WHERE (owner_sid = @current_sid) OPEN jobs_to_delete FETCH NEXT FROM jobs_to_delete INTO @job_id SELECT @rows_affected = 0 WHILE (@@fetch_status = 0) BEGIN EXECUTE sp_delete_job @job_id = @job_id SELECT @rows_affected = @rows_affected + 1 FETCH NEXT FROM jobs_to_delete INTO @job_id END DEALLOCATE jobs_to_delete RAISERROR(14238, 0, 1, @rows_affected) END ELSE IF (@action = 'REASSIGN') BEGIN -- Check if the current owner owns any multi-server jobs. -- If they do, then the new owner must be member of the sysadmin role. IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs sj, msdb.dbo.sysjobservers sjs WHERE (sj.job_id = sjs.job_id) AND (sj.owner_sid = @current_sid) AND (sjs.server_id <> 0)) AND @new_sid <> 0xFFFFFFFF) -- speical account allowed for MSX jobs BEGIN SELECT @is_sysadmin = 0 EXECUTE msdb.dbo.sp_sqlagent_has_server_access @login_name = @new_owner_login_name, @is_sysadmin_member = @is_sysadmin OUTPUT IF (@is_sysadmin = 0) BEGIN RAISERROR(14543, -1, -1, @current_owner_login_name, N'sysadmin') RETURN(1) -- Failure END END UPDATE msdb.dbo.sysjobs SET owner_sid = @new_sid WHERE (owner_sid = @current_sid) RAISERROR(14239, 0, 1, @@rowcount, @new_owner_login_name) END RETURN(0) -- Success END go /**************************************************************/ /* SP_APPLY_JOB_TO_TARGETS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_apply_job_to_targets...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_apply_job_to_targets') AND (type = 'P'))) DROP PROCEDURE sp_apply_job_to_targets go CREATE PROCEDURE sp_apply_job_to_targets @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @target_server_groups NVARCHAR(2048) = NULL, -- A comma-separated list of target server groups @target_servers NVARCHAR(2048) = NULL, -- An comma-separated list of target servers @operation VARCHAR(7) = 'APPLY' -- Or 'REMOVE' AS BEGIN DECLARE @retval INT DECLARE @rows_affected INT DECLARE @server_name sysname DECLARE @groups NVARCHAR(2048) DECLARE @group sysname DECLARE @servers NVARCHAR(2048) DECLARE @server sysname DECLARE @pos_of_comma INT SET NOCOUNT ON -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SELECT @target_server_groups = LTRIM(RTRIM(@target_server_groups)) SELECT @target_servers = UPPER(LTRIM(RTRIM(@target_servers))) SELECT @operation = LTRIM(RTRIM(@operation)) -- Turn [nullable] empty string parameters into NULLs IF (@target_server_groups = NULL) SELECT @target_server_groups = NULL IF (@target_servers = NULL) SELECT @target_servers = NULL IF (@operation = NULL) SELECT @operation = NULL EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check operation type IF ((@operation <> 'APPLY') AND (@operation <> 'REMOVE')) BEGIN RAISERROR(14266, -1, -1, '@operation', 'APPLY, REMOVE') RETURN(1) -- Failure END -- Check that we have a target server group list and/or a target server list IF ((@target_server_groups IS NULL) AND (@target_servers IS NULL)) BEGIN RAISERROR(14283, -1, -1) RETURN(1) -- Failure END DECLARE @temp_groups TABLE (group_name sysname COLLATE database_default NOT NULL) DECLARE @temp_server_name TABLE (server_name sysname COLLATE database_default NOT NULL) -- Parse the Target Server comma-separated list (if supplied) IF (@target_servers IS NOT NULL) BEGIN SELECT @servers = @target_servers SELECT @pos_of_comma = CHARINDEX(N',', @servers) WHILE (@pos_of_comma <> 0) BEGIN SELECT @server = SUBSTRING(@servers, 1, @pos_of_comma - 1) INSERT INTO @temp_server_name (server_name) VALUES (LTRIM(RTRIM(@server))) SELECT @servers = RIGHT(@servers, (DATALENGTH(@servers) / 2) - @pos_of_comma) SELECT @pos_of_comma = CHARINDEX(N',', @servers) END INSERT INTO @temp_server_name (server_name) VALUES (LTRIM(RTRIM(@servers))) END -- Parse the Target Server Groups comma-separated list IF (@target_server_groups IS NOT NULL) BEGIN SELECT @groups = @target_server_groups SELECT @pos_of_comma = CHARINDEX(N',', @groups) WHILE (@pos_of_comma <> 0) BEGIN SELECT @group = SUBSTRING(@groups, 1, @pos_of_comma - 1) INSERT INTO @temp_groups (group_name) VALUES (LTRIM(RTRIM(@group))) SELECT @groups = RIGHT(@groups, (DATALENGTH(@groups) / 2) - @pos_of_comma) SELECT @pos_of_comma = CHARINDEX(N',', @groups) END INSERT INTO @temp_groups (group_name) VALUES (LTRIM(RTRIM(@groups))) END -- Check server groups SET ROWCOUNT 1 -- We do this so that we catch the FIRST invalid group SELECT @group = NULL SELECT @group = group_name FROM @temp_groups WHERE group_name NOT IN (SELECT name FROM msdb.dbo.systargetservergroups) IF (@group IS NOT NULL) BEGIN RAISERROR(14262, -1, -1, '@target_server_groups', @group) RETURN(1) -- Failure END SET ROWCOUNT 0 -- Find the distinct list of servers being targeted INSERT INTO @temp_server_name (server_name) SELECT DISTINCT sts.server_name FROM msdb.dbo.systargetservergroups stsg, msdb.dbo.systargetservergroupmembers stsgm, msdb.dbo.systargetservers sts WHERE (stsg.name IN (SELECT group_name FROM @temp_groups)) AND (stsg.servergroup_id = stsgm.servergroup_id) AND (stsgm.server_id = sts.server_id) AND (UPPER(sts.server_name) NOT IN (SELECT server_name FROM @temp_server_name)) IF (@operation = 'APPLY') BEGIN -- Remove those servers to which the job has already been applied DELETE FROM @temp_server_name WHERE server_name IN (SELECT sts.server_name FROM msdb.dbo.sysjobservers sjs, msdb.dbo.systargetservers sts WHERE (sjs.job_id = @job_id) AND (sjs.server_id = sts.server_id)) END IF (@operation = 'REMOVE') BEGIN -- Remove those servers to which the job is not currently applied DELETE FROM @temp_server_name WHERE server_name NOT IN (SELECT sts.server_name FROM msdb.dbo.sysjobservers sjs, msdb.dbo.systargetservers sts WHERE (sjs.job_id = @job_id) AND (sjs.server_id = sts.server_id)) END SELECT @rows_affected = COUNT(*) FROM @temp_server_name SET ROWCOUNT 1 WHILE (EXISTS (SELECT * FROM @temp_server_name)) BEGIN SELECT @server_name = server_name FROM @temp_server_name IF (@operation = 'APPLY') EXECUTE sp_add_jobserver @job_id = @job_id, @server_name = @server_name ELSE IF (@operation = 'REMOVE') EXECUTE sp_delete_jobserver @job_id = @job_id, @server_name = @server_name DELETE FROM @temp_server_name WHERE (server_name = @server_name) END SET ROWCOUNT 0 IF (@operation = 'APPLY') RAISERROR(14240, 0, 1, @rows_affected) IF (@operation = 'REMOVE') RAISERROR(14241, 0, 1, @rows_affected) RETURN(0) -- Success END go /**************************************************************/ /* SP_REMOVE_JOB_FROM_TARGETS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_remove_job_from_targets...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_remove_job_from_targets') AND (type = 'P'))) DROP PROCEDURE sp_remove_job_from_targets go CREATE PROCEDURE sp_remove_job_from_targets @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @target_server_groups NVARCHAR(1024) = NULL, -- A comma-separated list of target server groups @target_servers NVARCHAR(1024) = NULL -- A comma-separated list of target servers AS BEGIN DECLARE @retval INT SET NOCOUNT ON EXECUTE @retval = sp_apply_job_to_targets @job_id, @job_name, @target_server_groups, @target_servers, 'REMOVE' RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_GET_JOB_ALERTS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_job_alerts...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_get_job_alerts') AND (type = 'P'))) DROP PROCEDURE sp_get_job_alerts go CREATE PROCEDURE sp_get_job_alerts @job_id UNIQUEIDENTIFIER = NULL, @job_name sysname = NULL AS BEGIN DECLARE @retval INT EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure SELECT id, name, enabled, type = CASE ISNULL(performance_condition, '!') WHEN '!' THEN 1 -- SQL Server event alert ELSE CASE event_id WHEN 8 THEN 3 -- WMI event alert ELSE 2 -- SQL Server performance condition alert END END FROM msdb.dbo.sysalerts WHERE (job_id = @job_id) RETURN(0) -- Success END go /**************************************************************/ /* */ /* S U P P O R T P R O C E D U R E S */ /* */ /**************************************************************/ /**************************************************************/ /* SP_CONVERT_JOBID_TO_CHAR [used by SEM only] */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_convert_jobid_to_char...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_convert_jobid_to_char') AND (type = 'P'))) DROP PROCEDURE sp_convert_jobid_to_char go CREATE PROCEDURE sp_convert_jobid_to_char @job_id UNIQUEIDENTIFIER, @job_id_as_char NVARCHAR(34) OUTPUT -- 34 because of the leading '0x' AS BEGIN DECLARE @job_id_as_binary BINARY(16) DECLARE @temp NCHAR(8) DECLARE @counter INT DECLARE @byte_value INT DECLARE @high_word INT DECLARE @low_word INT DECLARE @high_high_nybble INT DECLARE @high_low_nybble INT DECLARE @low_high_nybble INT DECLARE @low_low_nybble INT SET NOCOUNT ON SELECT @job_id_as_binary = CONVERT(BINARY(16), @job_id) SELECT @temp = CONVERT(NCHAR(8), @job_id_as_binary) SELECT @job_id_as_char = N'' SELECT @counter = 1 WHILE (@counter <= (DATALENGTH(@temp) / 2)) BEGIN SELECT @byte_value = CONVERT(INT, CONVERT(BINARY(2), SUBSTRING(@temp, @counter, 1))) SELECT @high_word = (@byte_value & 0xff00) / 0x100 SELECT @low_word = (@byte_value & 0x00ff) SELECT @high_high_nybble = (@high_word & 0xff) / 16 SELECT @high_low_nybble = (@high_word & 0xff) % 16 SELECT @low_high_nybble = (@low_word & 0xff) / 16 SELECT @low_low_nybble = (@low_word & 0xff) % 16 IF (@high_high_nybble < 10) SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('0') + @high_high_nybble) ELSE SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('A') + (@high_high_nybble - 10)) IF (@high_low_nybble < 10) SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('0') + @high_low_nybble) ELSE SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('A') + (@high_low_nybble - 10)) IF (@low_high_nybble < 10) SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('0') + @low_high_nybble) ELSE SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('A') + (@low_high_nybble - 10)) IF (@low_low_nybble < 10) SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('0') + @low_low_nybble) ELSE SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('A') + (@low_low_nybble - 10)) SELECT @counter = @counter + 1 END SELECT @job_id_as_char = N'0x' + LOWER(@job_id_as_char) END go /**************************************************************/ /* SP_START_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_start_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_start_job') AND (type = 'P'))) DROP PROCEDURE sp_start_job go CREATE PROCEDURE sp_start_job @job_name sysname = NULL, @job_id UNIQUEIDENTIFIER = NULL, @error_flag INT = 1, -- Set to 0 to suppress the error from sp_sqlagent_notify if SQLServerAgent is not running @server_name sysname = NULL, -- The specific target server to start the [multi-server] job on @step_name sysname = NULL, -- The name of the job step to start execution with [for use with a local job only] @output_flag INT = 1 -- Set to 0 to suppress the success message AS BEGIN DECLARE @job_id_as_char VARCHAR(36) DECLARE @retval INT DECLARE @step_id INT DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name))) SELECT @step_name = LTRIM(RTRIM(@step_name)) -- Turn [nullable] empty string parameters into NULLs IF (@job_name = N'') SELECT @job_name = NULL IF (@server_name = N'') SELECT @server_name = NULL IF (@step_name = N'') SELECT @step_name = NULL EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReader role can see all jobs but -- cannot start/stop jobs they do not own IF (@job_owner_sid <> SUSER_SID() -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) -- is not sysadmin AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0)) -- is not SQLAgentOperatorRole BEGIN RAISERROR(14393, -1, -1); RETURN(1) -- Failure END IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id))) BEGIN SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) RAISERROR(14256, -1, -1, @job_name, @job_id_as_char) RETURN(1) -- Failure END IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN -- The job is local, so start (run) the job locally -- Check the step name (if supplied) IF (@step_name IS NOT NULL) BEGIN SELECT @step_id = step_id FROM msdb.dbo.sysjobsteps WHERE (step_name = @step_name) AND (job_id = @job_id) IF (@step_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@step_name', @step_name) RETURN(1) -- Failure END END EXECUTE @retval = msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @schedule_id = @step_id, -- This is the start step @action_type = N'S', @error_flag = @error_flag IF ((@retval = 0) AND (@output_flag = 1)) RAISERROR(14243, 0, 1, @job_name) END ELSE BEGIN -- The job is a multi-server job -- Only sysadmin can start multi-server job IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(14397, -1, -1); RETURN(1) -- Failure END -- Check target server name (if any) IF (@server_name IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name))) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END END -- Re-post the job if it's an auto-delete job IF ((SELECT delete_level FROM msdb.dbo.sysjobs WHERE (job_id = @job_id)) <> 0) EXECUTE @retval = msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id, @server_name -- Post start instruction(s) EXECUTE @retval = msdb.dbo.sp_post_msx_operation 'START', 'JOB', @job_id, @server_name END RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_STOP_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_stop_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_stop_job') AND (type = 'P'))) DROP PROCEDURE sp_stop_job go CREATE PROCEDURE sp_stop_job @job_name sysname = NULL, @job_id UNIQUEIDENTIFIER = NULL, @originating_server sysname = NULL, -- So that we can stop ALL jobs that came from the given server @server_name sysname = NULL -- The specific target server to stop the [multi-server] job on AS BEGIN DECLARE @job_id_as_char VARCHAR(36) DECLARE @retval INT DECLARE @num_parameters INT DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @originating_server = UPPER(LTRIM(RTRIM(@originating_server))) SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name))) -- Turn [nullable] empty string parameters into NULLs IF (@job_name = N'') SELECT @job_name = NULL IF (@originating_server = N'') SELECT @originating_server = NULL IF (@server_name = N'') SELECT @server_name = NULL -- We must have EITHER a job id OR a job name OR an originating server SELECT @num_parameters = 0 IF (@job_id IS NOT NULL) SELECT @num_parameters = @num_parameters + 1 IF (@job_name IS NOT NULL) SELECT @num_parameters = @num_parameters + 1 IF (@originating_server IS NOT NULL) SELECT @num_parameters = @num_parameters + 1 IF (@num_parameters <> 1) BEGIN RAISERROR(14232, -1, -1) RETURN(1) -- Failure END IF (@originating_server IS NOT NULL) BEGIN -- Stop (cancel) ALL local jobs that originated from the specified server IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs_view WHERE (originating_server = @originating_server))) BEGIN RAISERROR(14268, -1, -1, @originating_server) RETURN(1) -- Failure END -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReader role that can see all jobs but -- cannot start/stop jobs they do not own IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) -- is not sysadmin AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0)) -- is not SQLAgentOperatorRole BEGIN RAISERROR(14393, -1, -1); RETURN(1) -- Failure END DECLARE @total_counter INT DECLARE @success_counter INT DECLARE stop_jobs CURSOR LOCAL FOR SELECT job_id FROM msdb.dbo.sysjobs_view WHERE (originating_server = @originating_server) SELECT @total_counter = 0, @success_counter = 0 OPEN stop_jobs FETCH NEXT FROM stop_jobs INTO @job_id WHILE (@@fetch_status = 0) BEGIN SELECT @total_counter + @total_counter + 1 EXECUTE @retval = msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @action_type = N'C' IF (@retval = 0) SELECT @success_counter = @success_counter + 1 FETCH NEXT FROM stop_jobs INTO @job_id END RAISERROR(14253, 0, 1, @success_counter, @total_counter) DEALLOCATE stop_jobs RETURN(0) -- 0 means success END ELSE BEGIN -- Stop ONLY the specified job EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id))) BEGIN SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) RAISERROR(14257, -1, -1, @job_name, @job_id_as_char) RETURN(1) -- Failure END -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReader role that can see all jobs but -- cannot start/stop jobs they do not own IF (@job_owner_sid <> SUSER_SID() -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) -- is not sysadmin AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0)) -- is not SQLAgentOperatorRole BEGIN RAISERROR(14393, -1, -1); RETURN(1) -- Failure END IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN -- The job is local, so stop (cancel) the job locally EXECUTE @retval = msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @action_type = N'C' IF (@retval = 0) RAISERROR(14254, 0, 1, @job_name) END ELSE BEGIN -- The job is a multi-server job -- Only sysadmin can stop multi-server job IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(14397, -1, -1); RETURN(1) -- Failure END -- Check target server name (if any) IF (@server_name IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name))) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END END -- Post the stop instruction(s) EXECUTE @retval = sp_post_msx_operation 'STOP', 'JOB', @job_id, @server_name END RETURN(@retval) -- 0 means success END END go /**************************************************************/ /* SP_CYCLE_AGENT_ERRORLOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_cycle_agent_errorlog...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_cycle_agent_errorlog') AND (type = 'P'))) DROP PROCEDURE sp_cycle_agent_errorlog go CREATE PROCEDURE sp_cycle_agent_errorlog AS BEGIN exec sp_sqlagent_notify N'L' END go /**************************************************************/ /* SP_GET_CHUNKED_JOBSTEP_PARAMS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_chunked_jobstep_params...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_get_chunked_jobstep_params') AND (type = 'P'))) DROP PROCEDURE sp_get_chunked_jobstep_params go CREATE PROCEDURE sp_get_chunked_jobstep_params @job_name sysname, @step_id INT = 1 AS BEGIN DECLARE @job_id UNIQUEIDENTIFIER DECLARE @step_id_as_char VARCHAR(10) DECLARE @text_pointer VARBINARY(16) DECLARE @remaining_length INT DECLARE @offset INT DECLARE @chunk INT DECLARE @retval INT SET NOCOUNT ON -- Check that the job exists EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check that the step exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @step_id))) BEGIN SELECT @step_id_as_char = CONVERT(VARCHAR(10), @step_id) RAISERROR(14262, -1, -1, '@step_id', @step_id_as_char) RETURN(1) -- Failure END -- Return the sysjobsteps.additional_parameters TEXT column as multiple readtexts of -- length 2048 SELECT @text_pointer = TEXTPTR(additional_parameters), @remaining_length = (DATALENGTH(additional_parameters) / 2) FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @step_id) SELECT @offset = 0, @chunk = 100 -- Get all the chunks of @chunk size WHILE (@remaining_length > @chunk) BEGIN READTEXT msdb.dbo.sysjobsteps.additional_parameters @text_pointer @offset @chunk SELECT @offset = @offset + @chunk SELECT @remaining_length = @remaining_length - @chunk END -- Get the last chunk IF (@remaining_length > 0) READTEXT msdb.dbo.sysjobsteps.additional_parameters @text_pointer @offset @remaining_length RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_CHECK_FOR_OWNED_JOBS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_check_for_owned_jobs...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_check_for_owned_jobs') AND (type = 'P'))) DROP PROCEDURE sp_check_for_owned_jobs go CREATE PROCEDURE sp_check_for_owned_jobs @login_name sysname, @table_name sysname AS BEGIN SET NOCOUNT ON -- This procedure is called by sp_droplogin to check if the login being dropped -- still owns jobs. The return value (the number of jobs owned) is passed back -- via the supplied table name [this cumbersome approach is necessary because -- sp_check_for_owned_jobs is invoked via an EXEC() and because we always want -- sp_droplogin to work, even if msdb and/or sysjobs does not exist]. IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobs') AND (type = 'U'))) BEGIN DECLARE @sql NVARCHAR(1024) SET @sql = N'INSERT INTO ' + QUOTENAME(@table_name, N'[') + N' SELECT COUNT(*) FROM msdb.dbo.sysjobs WHERE (owner_sid = SUSER_SID(N' + QUOTENAME(@login_name, '''') + ', 0))' --force case insensitive comparation for NT users EXEC sp_executesql @statement = @sql END END go /**************************************************************/ /* SP_CHECK_FOR_OWNED_JOBSTEPS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_check_for_owned_jobsteps...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_check_for_owned_jobsteps') AND (type = 'P'))) DROP PROCEDURE sp_check_for_owned_jobsteps go CREATE PROCEDURE sp_check_for_owned_jobsteps @login_name sysname = NULL, -- Supply this OR the database_X parameters, but not both @database_name sysname = NULL, @database_user_name sysname = NULL AS BEGIN DECLARE @db_name NVARCHAR(128) DECLARE @delimited_db_name NVARCHAR(258) DECLARE @escaped_db_name NVARCHAR(256) -- double sysname DECLARE @escaped_login_name NVARCHAR(256) -- double sysname SET NOCOUNT ON CREATE TABLE #work_table ( database_name sysname COLLATE database_default, database_user_name sysname COLLATE database_default ) IF ((@login_name IS NOT NULL) AND (@database_name IS NULL) AND (@database_user_name IS NULL)) BEGIN IF (SUSER_SID(@login_name, 0) IS NULL)--force case insensitive comparation for NT users BEGIN DROP TABLE #work_table RAISERROR(14262, -1, -1, '@login_name', @login_name) RETURN(1) -- Failure END DECLARE all_databases CURSOR LOCAL FOR SELECT name FROM master.dbo.sysdatabases OPEN all_databases FETCH NEXT FROM all_databases INTO @db_name -- Double up any single quotes in @login_name SELECT @escaped_login_name = REPLACE(@login_name, N'''', N'''''') WHILE (@@fetch_status = 0) BEGIN SELECT @delimited_db_name = QUOTENAME(@db_name, N'[') SELECT @escaped_db_name = REPLACE(@db_name, '''', '''''') EXECUTE(N'INSERT INTO #work_table SELECT N''' + @escaped_db_name + N''', name FROM ' + @delimited_db_name + N'.dbo.sysusers WHERE (sid = SUSER_SID(N''' + @escaped_login_name + N''', 0))')--force case insensitive comparation for NT users FETCH NEXT FROM all_databases INTO @db_name END DEALLOCATE all_databases -- If the login is an NT login, check for steps run as the login directly (as is the case with transient NT logins) IF (@login_name LIKE '%\%') BEGIN INSERT INTO #work_table SELECT database_name, database_user_name FROM msdb.dbo.sysjobsteps WHERE (database_user_name = @login_name) END END IF ((@login_name IS NULL) AND (@database_name IS NOT NULL) AND (@database_user_name IS NOT NULL)) BEGIN INSERT INTO #work_table SELECT @database_name, @database_user_name END IF (EXISTS (SELECT * FROM #work_table wt, msdb.dbo.sysjobsteps sjs WHERE (wt.database_name = sjs.database_name) AND (wt.database_user_name = sjs.database_user_name))) BEGIN SELECT sjv.job_id, sjv.name, sjs.step_id, sjs.step_name FROM #work_table wt, msdb.dbo.sysjobsteps sjs, msdb.dbo.sysjobs_view sjv WHERE (wt.database_name = sjs.database_name) AND (wt.database_user_name = sjs.database_user_name) AND (sjv.job_id = sjs.job_id) ORDER BY sjs.job_id END DROP TABLE #work_table RETURN(0) -- 0 means success END go /**************************************************************/ /* SP_SQLAGENT_REFRESH_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_refresh_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_sqlagent_refresh_job') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_refresh_job go CREATE PROCEDURE sp_sqlagent_refresh_job @job_id UNIQUEIDENTIFIER = NULL, @server_name sysname = NULL -- This parameter allows a TSX to use this SP when updating a job AS BEGIN DECLARE @server_id INT SET NOCOUNT ON IF (@server_name IS NULL) OR (UPPER(@server_name collate SQL_Latin1_General_CP1_CS_AS) = '(LOCAL)') SELECT @server_name = CONVERT(sysname, SERVERPROPERTY('ServerName')) SELECT @server_name = UPPER(@server_name) SELECT @server_id = server_id FROM msdb.dbo.systargetservers_view WHERE (UPPER(server_name) = ISNULL(@server_name, UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))))) SELECT @server_id = ISNULL(@server_id, 0) SELECT sjv.job_id, sjv.name, sjv.enabled, sjv.start_step_id, owner = dbo.SQLAGENT_SUSER_SNAME(sjv.owner_sid), sjv.notify_level_eventlog, sjv.notify_level_email, sjv.notify_level_netsend, sjv.notify_level_page, sjv.notify_email_operator_id, sjv.notify_netsend_operator_id, sjv.notify_page_operator_id, sjv.delete_level, has_step = (SELECT COUNT(*) FROM msdb.dbo.sysjobsteps sjst WHERE (sjst.job_id = sjv.job_id)), sjv.version_number, last_run_date = ISNULL(sjs.last_run_date, 0), last_run_time = ISNULL(sjs.last_run_time, 0), sjv.originating_server, sjv.description, agent_account = CASE sjv.owner_sid WHEN 0xFFFFFFFF THEN 1 ELSE 0 END FROM msdb.dbo.sysjobservers sjs, msdb.dbo.sysjobs_view sjv WHERE ((@job_id IS NULL) OR (@job_id = sjv.job_id)) AND (sjv.job_id = sjs.job_id) AND (sjs.server_id = @server_id) ORDER BY sjv.job_id OPTION (FORCE ORDER) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_JOBHISTORY_ROW_LIMITER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_jobhistory_row_limiter...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_jobhistory_row_limiter') AND (type = 'P'))) DROP PROCEDURE dbo.sp_jobhistory_row_limiter go CREATE PROCEDURE sp_jobhistory_row_limiter @job_id UNIQUEIDENTIFIER AS BEGIN DECLARE @max_total_rows INT -- This value comes from the registry (MaxJobHistoryTableRows) DECLARE @max_rows_per_job INT -- This value comes from the registry (MaxJobHistoryRows) DECLARE @rows_to_delete INT DECLARE @current_rows INT DECLARE @current_rows_per_job INT SET NOCOUNT ON -- Get max-job-history-rows from the registry EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRows', @max_total_rows OUTPUT, N'no_output' -- Check if we are limiting sysjobhistory rows IF (ISNULL(@max_total_rows, -1) = -1) RETURN(0) -- Check that max_total_rows is more than 1 IF (ISNULL(@max_total_rows, 0) < 2) BEGIN -- It isn't, so set the default to 1000 rows SELECT @max_total_rows = 1000 EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRows', N'REG_DWORD', @max_total_rows END -- Get the per-job maximum number of rows to keep SELECT @max_rows_per_job = 0 EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRowsPerJob', @max_rows_per_job OUTPUT, N'no_output' -- Check that max_rows_per_job is <= max_total_rows IF ((@max_rows_per_job > @max_total_rows) OR (@max_rows_per_job < 1)) BEGIN -- It isn't, so default the rows_per_job to max_total_rows SELECT @max_rows_per_job = @max_total_rows EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRowsPerJob', N'REG_DWORD', @max_rows_per_job END BEGIN TRANSACTION SELECT @current_rows_per_job = COUNT(*) FROM msdb.dbo.sysjobhistory with (TABLOCKX) WHERE (job_id = @job_id) -- Delete the oldest history row(s) for the job being inserted if the new row has -- pushed us over the per-job row limit (MaxJobHistoryRows) SELECT @rows_to_delete = @current_rows_per_job - @max_rows_per_job IF (@rows_to_delete > 0) BEGIN WITH RowsToDelete AS ( SELECT TOP (@rows_to_delete) * FROM msdb.dbo.sysjobhistory WHERE (job_id = @job_id) ORDER BY instance_id ) DELETE FROM RowsToDelete; END -- Delete the oldest history row(s) if inserting the new row has pushed us over the -- global MaxJobHistoryTableRows limit. SELECT @current_rows = COUNT(*) FROM msdb.dbo.sysjobhistory SELECT @rows_to_delete = @current_rows - @max_total_rows IF (@rows_to_delete > 0) BEGIN WITH RowsToDelete AS ( SELECT TOP (@rows_to_delete) * FROM msdb.dbo.sysjobhistory ORDER BY instance_id ) DELETE FROM RowsToDelete; END IF (@@trancount > 0) COMMIT TRANSACTION RETURN(0) -- Success END go /**************************************************************/ /* SP_SQLAGENT_LOG_JOBHISTORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_log_jobhistory...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_sqlagent_log_jobhistory') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_log_jobhistory go CREATE PROCEDURE sp_sqlagent_log_jobhistory @job_id UNIQUEIDENTIFIER, @step_id INT, @sql_message_id INT = 0, @sql_severity INT = 0, @message NVARCHAR(4000) = NULL, @run_status INT, -- SQLAGENT_EXEC_X code @run_date INT, @run_time INT, @run_duration INT, @operator_id_emailed INT = 0, @operator_id_netsent INT = 0, @operator_id_paged INT = 0, @retries_attempted INT, @server sysname = NULL, @session_id INT = 0 AS BEGIN DECLARE @retval INT DECLARE @operator_id_as_char VARCHAR(10) DECLARE @step_name sysname DECLARE @error_severity INT SET NOCOUNT ON IF (@server IS NULL) OR (UPPER(@server collate SQL_Latin1_General_CP1_CS_AS) = '(LOCAL)') SELECT @server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) -- Check authority (only SQLServerAgent can add a history entry for a job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- NOTE: We raise all errors as informational (sev 0) to prevent SQLServerAgent from caching -- the operation (if it fails) since if the operation will never run successfully we -- don't want it to hang around in the operation cache. SELECT @error_severity = 0 -- Check job_id IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id))) BEGIN DECLARE @job_id_as_char VARCHAR(36) SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) RAISERROR(14262, @error_severity, -1, 'Job', @job_id_as_char) RETURN(1) -- Failure END -- Check step id IF (@step_id <> 0) -- 0 means 'for the whole job' BEGIN SELECT @step_name = step_name FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @step_id) IF (@step_name IS NULL) BEGIN DECLARE @step_id_as_char VARCHAR(10) SELECT @step_id_as_char = CONVERT(VARCHAR, @step_id) RAISERROR(14262, @error_severity, -1, '@step_id', @step_id_as_char) RETURN(1) -- Failure END END ELSE SELECT @step_name = FORMATMESSAGE(14570) -- Check run_status IF (@run_status NOT IN (0, 1, 2, 3, 4, 5)) -- SQLAGENT_EXEC_X code BEGIN RAISERROR(14266, @error_severity, -1, '@run_status', '0, 1, 2, 3, 4, 5') RETURN(1) -- Failure END -- Check run_date EXECUTE @retval = sp_verify_job_date @run_date, '@run_date', 10 IF (@retval <> 0) RETURN(1) -- Failure -- Check run_time EXECUTE @retval = sp_verify_job_time @run_time, '@run_time', 10 IF (@retval <> 0) RETURN(1) -- Failure -- Check operator_id_emailed IF (@operator_id_emailed <> 0) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (id = @operator_id_emailed))) BEGIN SELECT @operator_id_as_char = CONVERT(VARCHAR, @operator_id_emailed) RAISERROR(14262, @error_severity, -1, '@operator_id_emailed', @operator_id_as_char) RETURN(1) -- Failure END END -- Check operator_id_netsent IF (@operator_id_netsent <> 0) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (id = @operator_id_netsent))) BEGIN SELECT @operator_id_as_char = CONVERT(VARCHAR, @operator_id_netsent) RAISERROR(14262, @error_severity, -1, '@operator_id_netsent', @operator_id_as_char) RETURN(1) -- Failure END END -- Check operator_id_paged IF (@operator_id_paged <> 0) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (id = @operator_id_paged))) BEGIN SELECT @operator_id_as_char = CONVERT(VARCHAR, @operator_id_paged) RAISERROR(14262, @error_severity, -1, '@operator_id_paged', @operator_id_as_char) RETURN(1) -- Failure END END -- Insert the history row INSERT INTO msdb.dbo.sysjobhistory (job_id, step_id, step_name, sql_message_id, sql_severity, message, run_status, run_date, run_time, run_duration, operator_id_emailed, operator_id_netsent, operator_id_paged, retries_attempted, server) VALUES (@job_id, @step_id, @step_name, @sql_message_id, @sql_severity, @message, @run_status, @run_date, @run_time, @run_duration, @operator_id_emailed, @operator_id_netsent, @operator_id_paged, @retries_attempted, @server) -- Update sysjobactivity table IF (@step_id = 0) --only update for job, not for each step BEGIN UPDATE msdb.dbo.sysjobactivity SET stop_execution_date = DATEADD(ms, -DATEPART(ms, GetDate()), GetDate()), job_history_id = SCOPE_IDENTITY() WHERE session_id = @session_id AND job_id = @job_id END -- Special handling of replication jobs DECLARE @job_name sysname DECLARE @category_id int SELECT @job_name = name, @category_id = category_id from msdb.dbo.sysjobs WHERE job_id = @job_id -- If replicatio agents (snapshot, logreader, distribution, merge, and queuereader -- and the step has been canceled and if we are at the distributor. IF @category_id in (10,13,14,15,19) and @run_status = 3 and object_id('MSdistributiondbs') is not null BEGIN -- Get the database DECLARE @database sysname SELECT @database = database_name from sysjobsteps where job_id = @job_id and lower(subsystem) in (N'distribution', N'logreader','snapshot',N'merge', N'queuereader') -- If the database is a distribution database IF EXISTS (select * from MSdistributiondbs where name = @database) BEGIN DECLARE @proc nvarchar(500) SELECT @proc = quotename(@database) + N'.dbo.sp_MSlog_agent_cancel' EXEC @proc @job_id = @job_id, @category_id = @category_id, @message = @message END END -- Delete any history rows that are over the registry-defined limits EXECUTE msdb.dbo.sp_jobhistory_row_limiter @job_id RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_SQLAGENT_CHECK_MSX_VERSION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_check_msx_version...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_sqlagent_check_msx_version') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_check_msx_version go CREATE PROCEDURE sp_sqlagent_check_msx_version @required_microsoft_version INT = NULL AS BEGIN SET NOCOUNT ON DECLARE @msx_version NVARCHAR(16) DECLARE @required_msx_version NVARCHAR(16) IF (@required_microsoft_version IS NULL) SELECT @required_microsoft_version = 0x07000252 -- 7.0.594 IF (@@microsoftversion < @required_microsoft_version) BEGIN SELECT @msx_version = CONVERT( NVARCHAR(2), CONVERT( INT, CONVERT( BINARY(1), @@microsoftversion / 0x1000000 ) ) ) + N'.' + CONVERT( NVARCHAR(2), CONVERT( INT, CONVERT( BINARY(1), CONVERT( BINARY(2), ((@@microsoftversion / 0x10000) % 0x100) ) ) ) ) + N'.' + CONVERT( NVARCHAR(4), @@microsoftversion % 0x10000 ) SELECT @required_msx_version = CONVERT( NVARCHAR(2), CONVERT( INT, CONVERT( BINARY(1), @required_microsoft_version / 0x1000000 ) ) ) + N'.' + CONVERT( NVARCHAR(2), CONVERT( INT, CONVERT( BINARY(1), CONVERT( BINARY(2), ((@required_microsoft_version / 0x10000) % 0x100) ) ) ) ) + N'.' + CONVERT( NVARCHAR(4), @required_microsoft_version % 0x10000 ) RAISERROR(14541, -1, -1, @msx_version, @required_msx_version) RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_SQLAGENT_PROBE_MSX */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_probe_msx...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_sqlagent_probe_msx') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_probe_msx go CREATE PROCEDURE sp_sqlagent_probe_msx @server_name sysname, -- The name of the target server probing the MSX @local_time NVARCHAR(100), -- The local time at the target server in the format YYYY/MM/DD HH:MM:SS @poll_interval INT, -- The frequency (in seconds) with which the target polls the MSX @time_zone_adjustment INT = NULL -- The offset from GMT in minutes (may be NULL if unknown) AS BEGIN DECLARE @bad_enlistment BIT DECLARE @blocking_instructions INT DECLARE @pending_instructions INT SET NOCOUNT ON SELECT @server_name = UPPER(@server_name) SELECT @bad_enlistment = 0, @blocking_instructions = 0, @pending_instructions = 0 UPDATE msdb.dbo.systargetservers SET last_poll_date = GETDATE(), local_time_at_last_poll = CONVERT(DATETIME, @local_time, 111), poll_interval = @poll_interval, time_zone_adjustment = ISNULL(@time_zone_adjustment, time_zone_adjustment) WHERE (UPPER(server_name) = @server_name) -- If the systargetservers entry is missing (and no DEFECT instruction has been posted) -- then the enlistment is bad IF (NOT EXISTS (SELECT 1 FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name))) AND (NOT EXISTS (SELECT 1 FROM msdb.dbo.sysdownloadlist WHERE (target_server = @server_name) AND (operation_code = 7) AND (object_type = 2))) SELECT @bad_enlistment = 1 SELECT @blocking_instructions = COUNT(*) FROM msdb.dbo.sysdownloadlist WHERE (target_server = @server_name) AND (error_message IS NOT NULL) SELECT @pending_instructions = COUNT(*) FROM msdb.dbo.sysdownloadlist WHERE (target_server = @server_name) AND (error_message IS NULL) AND (status = 0) SELECT @bad_enlistment, @blocking_instructions, @pending_instructions END go /**************************************************************/ /* SP_SET_LOCAL_TIME */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_set_local_time...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_set_local_time') AND (type = 'P'))) DROP PROCEDURE sp_set_local_time go CREATE PROCEDURE sp_set_local_time @server_name sysname = NULL, @adjustment_in_minutes INT = 0 -- Only needed for Win9x AS BEGIN DECLARE @ret INT DECLARE @local_time INT DECLARE @local_date INT DECLARE @current_datetime DATETIME DECLARE @local_time_sz VARCHAR(30) DECLARE @cmd NVARCHAR(200) DECLARE @date_format NVARCHAR(64) DECLARE @year_sz NVARCHAR(16) DECLARE @month_sz NVARCHAR(16) DECLARE @day_sz NVARCHAR(16) -- Synchronize the clock with the remote server (if supplied) -- NOTE: NT takes timezones into account, whereas Win9x does not IF (@server_name IS NOT NULL) BEGIN SELECT @cmd = N'net time \\' + @server_name + N' /set /y' EXECUTE @ret = master.dbo.xp_cmdshell @cmd, no_output IF (@ret <> 0) RETURN(1) -- Failure END -- Since NET TIME on Win9x does not take time zones into account we need to manually adjust -- for this using @adjustment_in_minutes which will be the difference between the MSX GMT -- offset and the target server GMT offset IF ((PLATFORM() & 0x2) = 0x2) -- Win9x BEGIN -- Get the date format from the registry (so that we can construct our DATE command-line command) EXECUTE master.dbo.xp_regread N'HKEY_CURRENT_USER', N'Control Panel\International', N'sShortDate', @date_format OUTPUT, N'no_output' SELECT @date_format = LOWER(@date_format) IF (@adjustment_in_minutes <> 0) BEGIN -- Wait for SQLServer to re-cache the OS time WAITFOR DELAY '00:01:00' SELECT @current_datetime = DATEADD(mi, @adjustment_in_minutes, GETDATE()) SELECT @local_time_sz = SUBSTRING(CONVERT(VARCHAR, @current_datetime, 8), 1, 5) SELECT @local_time = CONVERT(INT, LTRIM(SUBSTRING(@local_time_sz, 1, PATINDEX('%:%', @local_time_sz) - 1) + SUBSTRING(@local_time_sz, PATINDEX('%:%', @local_time_sz) + 1, 2))) SELECT @local_date = CONVERT(INT, CONVERT(VARCHAR, @current_datetime, 112)) -- Set the date SELECT @year_sz = CONVERT(NVARCHAR, @local_date / 10000) SELECT @month_sz = CONVERT(NVARCHAR, (@local_date % 10000) / 100) SELECT @day_sz = CONVERT(NVARCHAR, @local_date % 100) IF (@date_format LIKE N'y%m%d') SELECT @cmd = N'DATE ' + @year_sz + N'-' + @month_sz + N'-' + @day_sz IF (@date_format LIKE N'y%d%m') SELECT @cmd = N'DATE ' + @year_sz + N'-' + @day_sz + N'-' + @month_sz IF (@date_format LIKE N'm%d%y') SELECT @cmd = N'DATE ' + @month_sz + N'-' + @day_sz + N'-' + @year_sz IF (@date_format LIKE N'd%m%y') SELECT @cmd = N'DATE ' + @day_sz + N'-' + @month_sz + N'-' + @year_sz EXECUTE @ret = master.dbo.xp_cmdshell @cmd, no_output IF (@ret <> 0) RETURN 1 -- Failure -- Set the time (NOTE: We can't set the millisecond part of the time, so we may be up to .999 sec off) SELECT @cmd = N'TIME ' + CONVERT(NVARCHAR, @local_time / 100) + N':' + CONVERT(NVARCHAR, @local_time % 100) + ':' + CONVERT(NVARCHAR(2), DATEPART(SS, GETDATE())) EXECUTE @ret = master.dbo.xp_cmdshell @cmd, no_output IF (@ret <> 0) RETURN 1 -- Failure END END RETURN(0) -- Success END go /**************************************************************/ /* SP_MULTI_SERVER_JOB_SUMMARY [used by SEM only] */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_multi_server_job_summary...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_multi_server_job_summary') AND (type = 'P'))) DROP PROCEDURE sp_multi_server_job_summary go CREATE PROCEDURE sp_multi_server_job_summary @job_id UNIQUEIDENTIFIER = NULL, @job_name sysname = NULL AS BEGIN DECLARE @retval INT SET NOCOUNT ON IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL)) BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END -- NOTE: We join with syscategories - not sysjobservers - since we want to include jobs -- which are of type multi-server but which don't currently have any servers SELECT 'job_id' = sj.job_id, 'job_name' = sj.name, 'enabled' = sj.enabled, 'category_name' = sc.name, 'target_servers' = (SELECT COUNT(*) FROM msdb.dbo.sysjobservers sjs WHERE (sjs.job_id = sj.job_id)), 'pending_download_instructions' = (SELECT COUNT(*) FROM msdb.dbo.sysdownloadlist sdl WHERE (sdl.object_id = sj.job_id) AND (status = 0)), 'download_errors' = (SELECT COUNT(*) FROM msdb.dbo.sysdownloadlist sdl WHERE (sdl.object_id = sj.job_id) AND (sdl.error_message IS NOT NULL)), 'execution_failures' = (SELECT COUNT(*) FROM msdb.dbo.sysjobservers sjs WHERE (sjs.job_id = sj.job_id) AND (sjs.last_run_date <> 0) AND (sjs.last_run_outcome <> 1)) -- 1 is success FROM msdb.dbo.sysjobs sj, msdb.dbo.syscategories sc WHERE (sj.category_id = sc.category_id) AND (sc.category_class = 1) -- JOB AND (sc.category_type = 2) -- Multi-Server AND ((@job_id IS NULL) OR (sj.job_id = @job_id)) AND ((@job_name IS NULL) OR (sj.name = @job_name)) RETURN(0) -- Success END go /**************************************************************/ /* SP_TARGET_SERVER_SUMMARY [used by SEM only] */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_target_server_summary...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_target_server_summary') AND (type = 'P'))) DROP PROCEDURE sp_target_server_summary go CREATE PROCEDURE sp_target_server_summary @target_server sysname = NULL AS BEGIN SET NOCOUNT ON SELECT server_id, server_name, 'local_time' = DATEADD(SS, DATEDIFF(SS, last_poll_date, GETDATE()), local_time_at_last_poll), last_poll_date, 'unread_instructions' = (SELECT COUNT(*) FROM msdb.dbo.sysdownloadlist sdl WHERE (UPPER(sdl.target_server) = UPPER(sts.server_name)) AND (sdl.status = 0)), 'blocked' = (SELECT COUNT(*) FROM msdb.dbo.sysdownloadlist sdl WHERE (UPPER(sdl.target_server) = UPPER(sts.server_name)) AND (sdl.error_message IS NOT NULL)), poll_interval FROM msdb.dbo.systargetservers sts WHERE ((@target_server IS NULL) OR (UPPER(@target_server) = UPPER(sts.server_name))) END go CHECKPOINT go /**************************************************************/ /* */ /* 6 . X P R O C E D U R E S */ /* */ /* These procedures are provided for backwards compatability */ /* with 6.x scripts and 6.x replication. The re-implemented */ /* procedures are as follows: */ /* */ /* - sp_uniquetaskname (SQLDMO) */ /* - systasks_view (INSTDIST.SQL) */ /* - sp_addtask (INSTREPL.SQL, INSTDIST.SQL, SQLDMO) */ /* - sp_droptask (INSTREPL.SQL, INSTDIST.SQL, SQLDMO) */ /* - systasks (For completeness only) */ /**************************************************************/ /**************************************************************/ /* SP_UNIQUETASKNAME */ /**************************************************************/ PRINT '' PRINT 'Creating [legacy] procedure sp_uniquetaskname...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_uniquetaskname') AND (type = 'P'))) DROP PROCEDURE sp_uniquetaskname go CREATE PROCEDURE sp_uniquetaskname @seed NVARCHAR(92) AS BEGIN DECLARE @newest_suffix INT SET NOCOUNT ON -- We're going to add a suffix of 8 characters so make sure the seed is at most 84 characters SELECT @seed = LTRIM(RTRIM(@seed)) IF (DATALENGTH(@seed) > 0) SELECT @seed = SUBSTRING(@seed, 1, 84) -- Find the newest (highest) suffix so far SELECT @newest_suffix = MAX(CONVERT(INT, RIGHT(name, 8))) FROM msdb.dbo.sysjobs -- DON'T use sysjobs_view here! WHERE (name LIKE N'%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]') -- Generate the task name by appending the 'newest suffix' value (plus one) to the seed IF (@newest_suffix IS NOT NULL) BEGIN SELECT @newest_suffix = @newest_suffix + 1 SELECT 'TaskName' = CONVERT(NVARCHAR(92), @seed + REPLICATE(N'0', 8 - (DATALENGTH(CONVERT(NVARCHAR, @newest_suffix)) / 2)) + CONVERT(NVARCHAR, @newest_suffix)) END ELSE SELECT 'TaskName' = CONVERT(NVARCHAR(92), @seed + N'00000001') END go /**************************************************************/ /* SP_ADDTASK */ /**************************************************************/ PRINT '' PRINT 'Creating [legacy] procedure sp_addtask...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_addtask') AND (type = 'P'))) DROP PROCEDURE sp_addtask go CREATE PROCEDURE sp_addtask @name sysname, -- Was VARCHAR(100) in 6.x @subsystem NVARCHAR(40) = N'TSQL', -- Was VARCHAR(30) in 6.x @server sysname = NULL, @username sysname = NULL, -- Was VARCHAR(30) in 6.x @databasename sysname = NULL, -- Was VARCHAR(30) in 6.x @enabled TINYINT = 0, @freqtype INT = 2, -- 2 means OnDemand @freqinterval INT = 1, @freqsubtype INT = 1, @freqsubinterval INT = 1, @freqrelativeinterval INT = 1, @freqrecurrencefactor INT = 1, @activestartdate INT = 0, @activeenddate INT = 0, @activestarttimeofday INT = 0, @activeendtimeofday INT = 0, @nextrundate INT = 0, @nextruntime INT = 0, @runpriority INT = 0, @emailoperatorname sysname = NULL, -- Was VARCHAR(50) in 6.x @retryattempts INT = 0, @retrydelay INT = 10, @command NVARCHAR(max) = NULL, @loghistcompletionlevel INT = 2, @emailcompletionlevel INT = 0, @description NVARCHAR(512) = NULL, -- Was VARCHAR(255) in 6.x @tagadditionalinfo VARCHAR(96) = NULL, -- Obsolete in 7.0 @tagobjectid INT = NULL, -- Obsolete in 7.0 @tagobjecttype INT = NULL, -- Obsolete in 7.0 @newid INT = NULL OUTPUT, @parameters NTEXT = NULL, -- Was TEXT in 6.x @cmdexecsuccesscode INT = 0, @category_name sysname = NULL, -- New for 7.0 @category_id INT = NULL -- New for 7.0 AS BEGIN DECLARE @retval INT DECLARE @job_id UNIQUEIDENTIFIER DECLARE @id INT DECLARE @distdb sysname DECLARE @proc nvarchar(255) SET NOCOUNT ON SELECT @retval = 1 -- 0 means success, 1 means failure -- Set 7.0 category names for 6.5 replication tasks IF (LOWER(@subsystem) = N'sync') SELECT @category_id = 15 ELSE IF (LOWER(@subsystem) = N'logreader') SELECT @category_id = 13 ELSE IF (LOWER(@subsystem) = N'distribution') SELECT @category_id = 10 -- Convert old replication synchronization subsystem name to the 7.0 name IF (LOWER(@subsystem) = N'sync') SELECT @subsystem = N'Snapshot' -- If a category ID is provided this overrides any supplied category name IF (@category_id IS NOT NULL) BEGIN SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = @category_id) SELECT @category_name = ISNULL(@category_name, FORMATMESSAGE(14205)) END -- In 6.x active start date was not restricted, but it is in 7.0; so to avoid a "noisey" -- failure in sp_add_jobschedule we modify the value accordingly IF ((@activestartdate <> 0) AND (@activestartdate < 19900101)) SELECT @activestartdate = 19900101 BEGIN TRANSACTION -- Add the job EXECUTE @retval = sp_add_job @job_name = @name, @enabled = @enabled, @start_step_id = 1, @description = @description, @category_name = @category_name, @notify_level_eventlog = @loghistcompletionlevel, @notify_level_email = @emailcompletionlevel, @notify_email_operator_name = @emailoperatorname, @job_id = @job_id OUTPUT IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END -- Add an entry to systaskids for the new job (created by a 6.x client) INSERT INTO msdb.dbo.systaskids (job_id) VALUES (@job_id) -- Get the assigned task id SELECT @id = task_id, @newid = task_id FROM msdb.dbo.systaskids WHERE (job_id = @job_id) -- Add the job step EXECUTE @retval = sp_add_jobstep @job_id = @job_id, @step_id = 1, @step_name = N'Step 1', @subsystem = @subsystem, @command = @command, @additional_parameters = @parameters, @cmdexec_success_code = @cmdexecsuccesscode, @server = @server, @database_name = @databasename, @database_user_name = @username, @retry_attempts = @retryattempts, @retry_interval = @retrydelay, @os_run_priority = @runpriority IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END -- Add the job schedule IF (@activestartdate = 0) SELECT @activestartdate = NULL IF (@activeenddate = 0) SELECT @activeenddate = NULL IF (@activestarttimeofday = 0) SELECT @activestarttimeofday = NULL IF (@activeendtimeofday = 0) SELECT @activeendtimeofday = NULL IF (@freqtype <> 0x2) -- OnDemand tasks simply have no schedule in 7.0 BEGIN EXECUTE @retval = sp_add_jobschedule @job_id = @job_id, @name = N'6.x schedule', @enabled = 1, @freq_type = @freqtype, @freq_interval = @freqinterval, @freq_subday_type = @freqsubtype, @freq_subday_interval = @freqsubinterval, @freq_relative_interval = @freqrelativeinterval, @freq_recurrence_factor = @freqrecurrencefactor, @active_start_date = @activestartdate, @active_end_date = @activeenddate, @active_start_time = @activestarttimeofday, @active_end_time = @activeendtimeofday IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END END -- And finally, add the job server EXECUTE @retval = sp_add_jobserver @job_id = @job_id, @server_name = NULL IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END -- Add the replication agent for monitoring IF (@category_id = 13) -- Logreader BEGIN SELECT @distdb = distribution_db from MSdistpublishers where name = @server SELECT @proc = @distdb + '.dbo.sp_MSadd_logreader_agent' EXECUTE @retval = @proc @name = @name, @publisher = @server, @publisher_db = @databasename, @publication = '', @local_job = 1, @job_existing = 1, @job_id = @job_id IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END END ELSE IF (@category_id = 15) -- Snapshot BEGIN DECLARE @publication sysname EXECUTE @retval = master.dbo.sp_MSget_publication_from_taskname @taskname = @name, @publisher = @server, @publisherdb = @databasename, @publication = @publication OUTPUT IF (@publication IS NOT NULL) BEGIN SELECT @distdb = distribution_db from MSdistpublishers where name = @server SELECT @proc = @distdb + '.dbo.sp_MSadd_snapshot_agent' EXECUTE @retval = @proc @name = @name, @publisher = @server, @publisher_db = @databasename, @publication = @publication, @local_job = 1, @job_existing = 1, @snapshot_jobid = @job_id IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END SELECT @proc = @distdb + '.dbo.sp_MSadd_publication' EXECUTE @retval = @proc @publisher = @server, @publisher_db = @databasename, @publication = @publication, @publication_type = 0 -- Transactional IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END END END COMMIT TRANSACTION -- If this is an autostart LogReader or Distribution job, add the [new] '-Continuous' paramter to the command IF (@freqtype = 0x40) AND ((UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'LOGREADER') OR (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DISTRIBUTION')) BEGIN UPDATE msdb.dbo.sysjobsteps SET command = command + N' -Continuous' WHERE (job_id = @job_id) AND (step_id = 1) END -- If this is an autostart job, start it now (for backwards compatibility with 6.x SQLExecutive behaviour) IF (@freqtype = 0x40) EXECUTE msdb.dbo.sp_start_job @job_id = @job_id, @error_flag = 0, @output_flag = 0 Quit: RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_DROPTASK */ /**************************************************************/ PRINT '' PRINT 'Creating [legacy] procedure sp_droptask...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_droptask') AND (type = 'P'))) DROP PROCEDURE sp_droptask go CREATE PROCEDURE sp_droptask @name sysname = NULL, -- Was VARCHAR(100) in 6.x @loginname sysname = NULL, -- Was VARCHAR(30) in 6.x @id INT = NULL AS BEGIN DECLARE @retval INT DECLARE @job_id UNIQUEIDENTIFIER DECLARE @category_id int SET NOCOUNT ON IF ((@name IS NULL) AND (@id IS NULL) AND (@loginname IS NULL)) OR ((@name IS NOT NULL) AND ((@id IS NOT NULL) OR (@loginname IS NOT NULL))) OR ((@id IS NOT NULL) AND ((@name IS NOT NULL) OR (@loginname IS NOT NULL))) OR ((@loginname IS NOT NULL) AND ((@name IS NOT NULL) OR (@id IS NOT NULL))) BEGIN RAISERROR(14245, -1, -1) RETURN(1) -- Failure END -- If the name is supplied, get the job_id directly from sysjobs IF (@name IS NOT NULL) BEGIN -- Check if the name is ambiguous IF ((SELECT COUNT(*) FROM msdb.dbo.sysjobs_view WHERE (name = @name)) > 1) BEGIN RAISERROR(14292, -1, -1, @name, '@id', '@name') RETURN(1) -- Failure END SELECT @job_id = job_id, @category_id = category_id FROM msdb.dbo.sysjobs_view WHERE (name = @name) SELECT @id = task_id FROM msdb.dbo.systaskids WHERE (job_id = @job_id) IF (@job_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END END -- If the id is supplied lookup the corresponding job_id from systaskids IF (@id IS NOT NULL) BEGIN SELECT @job_id = job_id FROM msdb.dbo.systaskids WHERE (task_id = @id) -- Check that the job still exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id))) BEGIN SELECT @name = CONVERT(NVARCHAR, @id) RAISERROR(14262, -1, -1, '@id', @name) RETURN(1) -- Failure END -- Get the name of this job SELECT @name = name, @category_id = category_id FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id) END -- Delete the specific job IF (@name IS NOT NULL) BEGIN BEGIN TRANSACTION DELETE FROM msdb.dbo.systaskids WHERE (job_id = @job_id) EXECUTE @retval = sp_delete_job @job_id = @job_id IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END -- If a Logreader or Snapshot task, delete corresponding replication agent information IF @category_id = 13 or @category_id = 15 BEGIN EXECUTE @retval = sp_MSdrop_6x_replication_agent @job_id, @category_id IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END END COMMIT TRANSACTION END -- Delete all jobs belonging to the specified login IF (@loginname IS NOT NULL) BEGIN BEGIN TRANSACTION DELETE FROM msdb.dbo.systaskids WHERE job_id IN (SELECT job_id FROM msdb.dbo.sysjobs_view WHERE (owner_sid = SUSER_SID(@loginname))) EXECUTE @retval = sp_manage_jobs_by_login @action = 'DELETE', @current_owner_login_name = @loginname IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END COMMIT TRANSACTION END Quit: RETURN(@retval) -- 0 means success END go /**************************************************************/ /* */ /* E R R O R M E S S A G E S */ /* */ /* These are now created by MESSAGES.SQL. */ /* */ /* NOTE: 14255 and 14265 are called by dynamic SQL generated */ /* by SQLServerAgent. */ /**************************************************************/ /**************************************************************/ /* */ /* T R I G G E R S */ /* */ /**************************************************************/ /**************************************************************/ /* TRIG_TARGETSERVER_INSERT */ /**************************************************************/ PRINT '' PRINT 'Creating trigger trig_targetserver_insert...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_targetserver_insert') AND (type = 'TR'))) DROP TRIGGER dbo.trig_targetserver_insert go CREATE TRIGGER trig_targetserver_insert ON msdb.dbo.systargetservers FOR INSERT, DELETE AS BEGIN SET NOCOUNT ON -- Disallow the insert if the server is called 'ALL' -- NOTE: We have to do this check here in the trigger since there is no sp_add_targetserver -- (target servers insert a row for themselves when they 'enlist' in an MSX) IF (EXISTS (SELECT * FROM inserted WHERE (server_name = N'ALL'))) BEGIN DELETE FROM msdb.dbo.systargetservers WHERE (server_name = N'ALL') RAISERROR(14271, -1, -1, 'ALL') RETURN END -- Set (or delete) the registy flag (so that SETUP can detect if we're an MSX) IF ((SELECT COUNT(*) FROM msdb.dbo.systargetservers) = 0) BEGIN DECLARE @val INT EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServer', @val OUTPUT, N'no_output' IF (@val IS NOT NULL) EXECUTE master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServer' END ELSE EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServer', N'REG_DWORD', 1 END go CHECKPOINT go /**************************************************************/ /** **/ /** A L E R T S A N D O P E R A T O R S **/ /** **/ /**************************************************************/ /**************************************************************/ /* */ /* C O R E P R O C E D U R E S */ /* */ /**************************************************************/ /**************************************************************/ /* SP_ADD_ALERT_INTERNAL */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_alert_internal...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_alert_internal') AND (type = 'P'))) DROP PROCEDURE sp_add_alert_internal go CREATE PROCEDURE sp_add_alert_internal @name sysname, @message_id INT = 0, @severity INT = 0, @enabled TINYINT = 1, @delay_between_responses INT = 0, @notification_message NVARCHAR(512) = NULL, @include_event_description_in TINYINT = 5, -- 0 = None, 1 = Email, 2 = Pager, 4 = NetSend, 7 = All @database_name sysname = NULL, @event_description_keyword NVARCHAR(100) = NULL, @job_id UNIQUEIDENTIFIER = NULL, -- If provided must NOT also provide job_name @job_name sysname = NULL, -- If provided must NOT also provide job_id @raise_snmp_trap TINYINT = 0, @performance_condition NVARCHAR(512) = NULL, -- New for 7.0 @category_name sysname = NULL, -- New for 7.0 @wmi_namespace NVARCHAR(512) = NULL, -- New for 9.0 @wmi_query NVARCHAR(512) = NULL, -- New for 9.0 @verify_alert TINYINT = 1 -- 0 = do not verify alert, 1(or anything else) = verify alert before adding AS BEGIN DECLARE @event_source NVARCHAR(100) DECLARE @event_category_id INT DECLARE @event_id INT DECLARE @last_occurrence_date INT DECLARE @last_occurrence_time INT DECLARE @last_notification_date INT DECLARE @last_notification_time INT DECLARE @occurrence_count INT DECLARE @count_reset_date INT DECLARE @count_reset_time INT DECLARE @has_notification INT DECLARE @return_code INT DECLARE @duplicate_name sysname DECLARE @category_id INT DECLARE @alert_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @notification_message = LTRIM(RTRIM(@notification_message)) SELECT @database_name = LTRIM(RTRIM(@database_name)) SELECT @event_description_keyword = LTRIM(RTRIM(@event_description_keyword)) SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @performance_condition = LTRIM(RTRIM(@performance_condition)) SELECT @category_name = LTRIM(RTRIM(@category_name)) -- Turn [nullable] empty string parameters into NULLs IF (@notification_message = N'') SELECT @notification_message = NULL IF (@database_name = N'') SELECT @database_name = NULL IF (@event_description_keyword = N'') SELECT @event_description_keyword = NULL IF (@job_name = N'') SELECT @job_name = NULL IF (@performance_condition = N'') SELECT @performance_condition = NULL IF (@category_name = N'') SELECT @category_name = NULL SELECT @message_id = ISNULL(@message_id, 0) SELECT @severity = ISNULL(@severity, 0) -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check if SQLServerAgent is in the process of starting EXECUTE @return_code = msdb.dbo.sp_is_sqlagent_starting IF (@return_code <> 0) RETURN(1) -- Failure -- Hard-code the new Alert defaults -- event source needs to be instance aware DECLARE @instance_name sysname SELECT @instance_name = CONVERT (sysname, SERVERPROPERTY ('InstanceName')) IF (@instance_name IS NULL OR @instance_name = N'MSSQLSERVER') SELECT @event_source = N'MSSQLSERVER' ELSE SELECT @event_source = N'MSSQL$' + @instance_name SELECT @event_category_id = NULL SELECT @event_id = NULL SELECT @last_occurrence_date = 0 SELECT @last_occurrence_time = 0 SELECT @last_notification_date = 0 SELECT @last_notification_time = 0 SELECT @occurrence_count = 0 SELECT @count_reset_date = 0 SELECT @count_reset_time = 0 SELECT @has_notification = 0 IF (@category_name IS NULL) BEGIN --Default category_id for alerts SELECT @category_id = 98 SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = 98) END -- Map a job_id of 0 to the real value we use to mean 'no job' IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) AND (@job_name IS NULL) SELECT @job_name = N'' -- Verify the Alert if @verify_alert <> 0 IF (@verify_alert <> 0) BEGIN IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) SELECT @job_id = NULL EXECUTE @return_code = sp_verify_alert @name, @message_id, @severity, @enabled, @delay_between_responses, @notification_message, @include_event_description_in, @database_name, @event_description_keyword, @job_id OUTPUT, @job_name OUTPUT, @occurrence_count, @raise_snmp_trap, @performance_condition, @category_name, @category_id OUTPUT, @count_reset_date, @count_reset_time, @wmi_namespace, @wmi_query, @event_id OUTPUT IF (@return_code <> 0) BEGIN RETURN(1) -- Failure END END -- For WMI alerts replace -- database_name with wmi_namespace and -- performance_conditon with wmi_query -- so we can store them in those columns in sysalerts table IF (@event_id = 8) BEGIN SELECT @database_name = @wmi_namespace SELECT @performance_condition = @wmi_query END -- Check if this Alert already exists SELECT @duplicate_name = FORMATMESSAGE(14205) SELECT @duplicate_name = name FROM msdb.dbo.sysalerts WHERE ((event_id = 8) AND (ISNULL(performance_condition, N'') = ISNULL(@performance_condition, N'')) AND (ISNULL(database_name, N'') = ISNULL(@database_name, N''))) OR ((ISNULL(event_id,1) <> 8) AND (ISNULL(performance_condition, N'apples') = ISNULL(@performance_condition, N'oranges'))) OR ((performance_condition IS NULL) AND (message_id = @message_id) AND (severity = @severity) AND (ISNULL(database_name, N'') = ISNULL(@database_name, N'')) AND (ISNULL(event_description_keyword, N'') = ISNULL(@event_description_keyword, N''))) IF (@duplicate_name <> FORMATMESSAGE(14205)) BEGIN RAISERROR(14501, 16, 1, @duplicate_name) RETURN(1) -- Failure END -- Finally, do the actual INSERT INSERT INTO msdb.dbo.sysalerts (name, event_source, event_category_id, event_id, message_id, severity, enabled, delay_between_responses, last_occurrence_date, last_occurrence_time, last_response_date, last_response_time, notification_message, include_event_description, database_name, event_description_keyword, occurrence_count, count_reset_date, count_reset_time, job_id, has_notification, flags, performance_condition, category_id) VALUES (@name, @event_source, @event_category_id, @event_id, @message_id, @severity, @enabled, @delay_between_responses, @last_occurrence_date, @last_occurrence_time, @last_notification_date, @last_notification_time, @notification_message, @include_event_description_in, @database_name, @event_description_keyword, @occurrence_count, @count_reset_date, @count_reset_time, ISNULL(@job_id, CONVERT(UNIQUEIDENTIFIER, 0x00)), @has_notification, @raise_snmp_trap, @performance_condition, @category_id) -- Notify SQLServerAgent of the change SELECT @alert_id = id FROM msdb.dbo.sysalerts WHERE (name = @name) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'I' RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_ALERT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_alert...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_alert') AND (type = 'P'))) DROP PROCEDURE sp_add_alert go CREATE PROCEDURE sp_add_alert @name sysname, @message_id INT = 0, @severity INT = 0, @enabled TINYINT = 1, @delay_between_responses INT = 0, @notification_message NVARCHAR(512) = NULL, @include_event_description_in TINYINT = 5, -- 0 = None, 1 = Email, 2 = Pager, 4 = NetSend, 7 = All @database_name sysname = NULL, @event_description_keyword NVARCHAR(100) = NULL, @job_id UNIQUEIDENTIFIER = NULL, -- If provided must NOT also provide job_name @job_name sysname = NULL, -- If provided must NOT also provide job_id @raise_snmp_trap TINYINT = 0, @performance_condition NVARCHAR(512) = NULL, -- New for 7.0 @category_name sysname = NULL, -- New for 7.0 @wmi_namespace sysname = NULL, -- New for 9.0 @wmi_query NVARCHAR(512) = NULL -- New for 9.0 AS BEGIN DECLARE @verify_alert INT --Always verify alerts before adding SELECT @verify_alert = 1 EXECUTE msdb.dbo.sp_add_alert_internal @name, @message_id, @severity, @enabled, @delay_between_responses, @notification_message, @include_event_description_in, @database_name, @event_description_keyword, @job_id, @job_name, @raise_snmp_trap, @performance_condition, @category_name, @wmi_namespace, @wmi_query, @verify_alert END GO /**************************************************************/ /* SP_DELETE_ALERT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_alert...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_alert') AND (type = 'P'))) DROP PROCEDURE sp_delete_alert go CREATE PROCEDURE sp_delete_alert @name sysname AS BEGIN DECLARE @alert_id INT DECLARE @return_code INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check if SQLServerAgent is in the process of starting EXECUTE @return_code = msdb.dbo.sp_is_sqlagent_starting IF (@return_code <> 0) RETURN(1) -- Failure -- Check if this Alert exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (name = @name))) BEGIN RAISERROR(14262, 16, 1, '@name', @name) RETURN(1) -- Failure END -- Convert the Name to it's ID SELECT @alert_id = id FROM msdb.dbo.sysalerts WHERE (name = @name) BEGIN TRANSACTION -- Delete sysnotifications entries DELETE FROM msdb.dbo.sysnotifications WHERE (alert_id = @alert_id) -- Finally, do the actual DELETE DELETE FROM msdb.dbo.sysalerts WHERE (id = @alert_id) COMMIT TRANSACTION -- Notify SQLServerAgent of the change EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'D' RETURN(0) -- Success END go /**************************************************************/ /* SP_HELP_ALERT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_alert...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_alert') AND (type = 'P'))) DROP PROCEDURE sp_help_alert go CREATE PROCEDURE sp_help_alert @alert_name sysname = NULL, @order_by sysname = N'name', @alert_id INT = NULL, @category_name sysname = NULL, @legacy_format BIT = 0 AS BEGIN DECLARE @alert_id_as_char NVARCHAR(10) DECLARE @escaped_alert_name NVARCHAR(256) -- double sysname DECLARE @escaped_category_name NVARCHAR(256) -- double sysname SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @alert_name = LTRIM(RTRIM(@alert_name)) SELECT @order_by = LTRIM(RTRIM(@order_by)) SELECT @category_name = LTRIM(RTRIM(@category_name)) -- Turn [nullable] empty string parameters into NULLs IF (@category_name = N'') SELECT @category_name = NULL IF (@alert_name = N'') SELECT @alert_name = NULL -- Check alert name IF (@alert_name IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (name = @alert_name))) BEGIN RAISERROR(14262, -1, -1, '@alert_name', @alert_name) RETURN(1) -- Failure END END -- Check alert id IF (@alert_id IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (id = @alert_id))) BEGIN SELECT @alert_id_as_char = CONVERT(VARCHAR, @alert_id) RAISERROR(14262, -1, -1, '@alert_id', @alert_id_as_char) RETURN(1) -- Failure END END IF (@alert_id IS NOT NULL) SELECT @alert_id_as_char = CONVERT(VARCHAR, @alert_id) ELSE SELECT @alert_id_as_char = N'NULL' -- Double up any single quotes in @alert_name IF (@alert_name IS NOT NULL) SELECT @escaped_alert_name = REPLACE(@alert_name, N'''', N'''''') -- Double up any single quotes in @category_name IF (@category_name IS NOT NULL) SELECT @escaped_category_name = REPLACE(@category_name, N'''', N'''''') IF (@legacy_format <> 0) BEGIN -- @order_by parameter validation. IF ( (@order_by IS NOT NULL) AND (EXISTS(SELECT so.object_id FROM msdb.sys.objects so JOIN msdb.sys.columns sc ON (so.object_id = sc.object_id) WHERE so.type='U' AND so.name='sysalerts' AND LOWER(sc.name collate SQL_Latin1_General_CP1_CS_AS)=LOWER(@order_by collate SQL_Latin1_General_CP1_CS_AS) ) ) ) BEGIN SELECT @order_by = N'sa.' + @order_by END ELSE BEGIN IF (LOWER(@order_by collate SQL_Latin1_General_CP1_CS_AS) NOT IN ( N'job_name', N'category_name', N'type' ) ) AND --special "order by" clause used only by sqlagent. if you change it you need to change agent too (@order_by <> N'event_id DESC, severity ASC, message_id ASC, database_name DESC') AND (@order_by <> N'severity ASC, message_id ASC, database_name DESC') BEGIN RAISERROR(18750, -1, -1, 'sp_help_alert', '@order_by') RETURN(1) -- Failure END END -- Old query version (for SQL Server 2000 and older servers) -- database_name and performance_conditions are reported -- directly from sysalerts columns EXECUTE (N'SELECT sa.id, sa.name, sa.event_source, sa.event_category_id, sa.event_id, sa.message_id, sa.severity, sa.enabled, sa.delay_between_responses, sa.last_occurrence_date, sa.last_occurrence_time, sa.last_response_date, sa.last_response_time, sa.notification_message, sa.include_event_description, sa.database_name, sa.event_description_keyword, sa.occurrence_count, sa.count_reset_date, sa.count_reset_time, sjv.job_id, job_name = sjv.name, sa.has_notification, sa.flags, sa.performance_condition, category_name = sc.name, type = CASE ISNULL(sa.performance_condition, ''!'') WHEN ''!'' THEN 1 -- SQL Server event alert ELSE CASE sa.event_id WHEN 8 THEN 4 -- WMI event alert ELSE 2 -- SQL Server performance condition alert END END FROM msdb.dbo.sysalerts sa LEFT OUTER JOIN msdb.dbo.sysjobs_view sjv ON (sa.job_id = sjv.job_id) LEFT OUTER JOIN msdb.dbo.syscategories sc ON (sa.category_id = sc.category_id) WHERE ((N''' + @escaped_alert_name + N''' = N'''') OR (sa.name = N''' + @escaped_alert_name + N''')) AND ((' + @alert_id_as_char + N' IS NULL) OR (sa.id = ' + @alert_id_as_char + N')) AND ((N''' + @escaped_category_name + N''' = N'''') OR (sc.name = N''' + @escaped_category_name + N''')) ORDER BY ' + @order_by) END ELSE BEGIN -- @order_by parameter validation. IF ( (@order_by IS NOT NULL) AND (EXISTS(SELECT so.object_id FROM msdb.sys.objects so JOIN msdb.sys.columns sc ON (so.object_id = sc.object_id) WHERE so.type='U' AND so.name='sysalerts' AND LOWER(sc.name collate SQL_Latin1_General_CP1_CS_AS)=LOWER(@order_by collate SQL_Latin1_General_CP1_CS_AS) ) ) ) BEGIN SELECT @order_by = N'sa.' + @order_by END ELSE BEGIN IF (LOWER(@order_by collate SQL_Latin1_General_CP1_CS_AS) NOT IN (N'database_name', N'job_name', N'performance_condition', N'category_name', N'wmi_namespace', N'wmi_query', N'type' ) ) AND --special "order by" clause used only by sqlagent. if you change it you need to change agent too (@order_by <> N'event_id DESC, severity ASC, message_id ASC, database_name DESC') AND (@order_by <> N'severity ASC, message_id ASC, database_name DESC') BEGIN RAISERROR(18750, -1, -1, 'sp_help_alert', '@order_by') RETURN(1) -- Failure END END -- New query version. If alert is a WMI alert -- then database_name is reported as wmi_namespace and -- performance_condition is reported as wmi_query. -- For other alerts those two new columns are NULL EXECUTE (N'SELECT sa.id, sa.name, sa.event_source, sa.event_category_id, sa.event_id, sa.message_id, sa.severity, sa.enabled, sa.delay_between_responses, sa.last_occurrence_date, sa.last_occurrence_time, sa.last_response_date, sa.last_response_time, sa.notification_message, sa.include_event_description, database_name = CASE ISNULL(sa.event_id, 1) WHEN 8 THEN NULL ELSE sa.database_name END, sa.event_description_keyword, sa.occurrence_count, sa.count_reset_date, sa.count_reset_time, sjv.job_id, job_name = sjv.name, sa.has_notification, sa.flags, performance_condition = CASE ISNULL(sa.event_id, 1) WHEN 8 THEN NULL ELSE sa.performance_condition END, category_name = sc.name, wmi_namespace = CASE ISNULL(sa.event_id, 1) WHEN 8 THEN sa.database_name ELSE NULL END, wmi_query = CASE ISNULL(sa.event_id, 1) WHEN 8 THEN sa.performance_condition ELSE NULL END, type = CASE ISNULL(sa.performance_condition, ''!'') WHEN ''!'' THEN 1 -- SQL Server event alert ELSE CASE sa.event_id WHEN 8 THEN 4 -- WMI event alert ELSE 2 -- SQL Server performance condition alert END END FROM msdb.dbo.sysalerts sa LEFT OUTER JOIN msdb.dbo.sysjobs_view sjv ON (sa.job_id = sjv.job_id) LEFT OUTER JOIN msdb.dbo.syscategories sc ON (sa.category_id = sc.category_id) WHERE ((N''' + @escaped_alert_name + N''' = N'''') OR (sa.name = N''' + @escaped_alert_name + N''')) AND ((' + @alert_id_as_char + N' IS NULL) OR (sa.id = ' + @alert_id_as_char + N')) AND ((N''' + @escaped_category_name + N''' = N'''') OR (sc.name = N''' + @escaped_category_name + N''')) ORDER BY ' + @order_by) END RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_VERIFY_OPERATOR */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_operator...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_operator') AND (type = 'P'))) DROP PROCEDURE sp_verify_operator go CREATE PROCEDURE sp_verify_operator @name sysname, @enabled TINYINT, @pager_days TINYINT, @weekday_pager_start_time INT, @weekday_pager_end_time INT, @saturday_pager_start_time INT, @saturday_pager_end_time INT, @sunday_pager_start_time INT, @sunday_pager_end_time INT, @category_name sysname, @category_id INT OUTPUT AS BEGIN DECLARE @return_code TINYINT DECLARE @res_valid_range NVARCHAR(100) SET NOCOUNT ON SELECT @res_valid_range = FORMATMESSAGE(14209) -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @category_name = LTRIM(RTRIM(@category_name)) -- The name must be unique IF (EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (name = @name))) BEGIN RAISERROR(14261, 16, 1, '@name', @name) RETURN(1) -- Failure END -- Enabled must be 0 or 1 IF (@enabled NOT IN (0, 1)) BEGIN RAISERROR(14266, 16, 1, '@enabled', '0, 1') RETURN(1) -- Failure END -- Check PagerDays IF (@pager_days < 0) OR (@pager_days > 127) BEGIN RAISERROR(14266, 16, 1, '@pager_days', @res_valid_range) RETURN(1) -- Failure END -- Check Start/End Times EXECUTE @return_code = sp_verify_job_time @weekday_pager_start_time, '@weekday_pager_start_time' IF (@return_code <> 0) RETURN(1) EXECUTE @return_code = sp_verify_job_time @weekday_pager_end_time, '@weekday_pager_end_time' IF (@return_code <> 0) RETURN(1) EXECUTE @return_code = sp_verify_job_time @saturday_pager_start_time, '@saturday_pager_start_time' IF (@return_code <> 0) RETURN(1) EXECUTE @return_code = sp_verify_job_time @saturday_pager_end_time, '@saturday_pager_end_time' IF (@return_code <> 0) RETURN(1) EXECUTE @return_code = sp_verify_job_time @sunday_pager_start_time, '@sunday_pager_start_time' IF (@return_code <> 0) RETURN(1) EXECUTE @return_code = sp_verify_job_time @sunday_pager_end_time, '@sunday_pager_end_time' IF (@return_code <> 0) RETURN(1) -- Check category name IF (@category_name = N'[DEFAULT]') SELECT @category_id = 99 ELSE BEGIN SELECT @category_id = category_id FROM msdb.dbo.syscategories WHERE (category_class = 3) -- Operators AND (category_type = 3) -- None AND (name = @category_name) END IF (@category_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@category_name', @category_name) RETURN(1) -- Failure END RETURN(0) END go /**************************************************************/ /* SP_ADD_OPERATOR */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_operator...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_operator') AND (type = 'P'))) DROP PROCEDURE sp_add_operator go CREATE PROCEDURE sp_add_operator @name sysname, @enabled TINYINT = 1, @email_address NVARCHAR(100) = NULL, @pager_address NVARCHAR(100) = NULL, @weekday_pager_start_time INT = 090000, -- HHMMSS using 24 hour clock @weekday_pager_end_time INT = 180000, -- As above @saturday_pager_start_time INT = 090000, -- As above @saturday_pager_end_time INT = 180000, -- As above @sunday_pager_start_time INT = 090000, -- As above @sunday_pager_end_time INT = 180000, -- As above @pager_days TINYINT = 0, -- 1 = Sunday .. 64 = Saturday @netsend_address NVARCHAR(100) = NULL, -- New for 7.0 @category_name sysname = NULL -- New for 7.0 AS BEGIN DECLARE @return_code TINYINT DECLARE @category_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @email_address = LTRIM(RTRIM(@email_address)) SELECT @pager_address = LTRIM(RTRIM(@pager_address)) SELECT @netsend_address = LTRIM(RTRIM(@netsend_address)) SELECT @category_name = LTRIM(RTRIM(@category_name)) -- Turn [nullable] empty string parameters into NULLs IF (@email_address = N'') SELECT @email_address = NULL IF (@pager_address = N'') SELECT @pager_address = NULL IF (@netsend_address = N'') SELECT @netsend_address = NULL IF (@category_name = N'') SELECT @category_name = NULL -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END IF (@category_name IS NULL) BEGIN SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = 99) END -- Verify the operator EXECUTE @return_code = sp_verify_operator @name, @enabled, @pager_days, @weekday_pager_start_time, @weekday_pager_end_time, @saturday_pager_start_time, @saturday_pager_end_time, @sunday_pager_start_time, @sunday_pager_end_time, @category_name, @category_id OUTPUT IF (@return_code <> 0) RETURN(1) -- Failure -- Finally, do the INSERT INSERT INTO msdb.dbo.sysoperators (name, enabled, email_address, last_email_date, last_email_time, pager_address, last_pager_date, last_pager_time, weekday_pager_start_time, weekday_pager_end_time, saturday_pager_start_time, saturday_pager_end_time, sunday_pager_start_time, sunday_pager_end_time, pager_days, netsend_address, last_netsend_date, last_netsend_time, category_id) VALUES (@name, @enabled, @email_address, 0, 0, @pager_address, 0, 0, @weekday_pager_start_time, @weekday_pager_end_time, @saturday_pager_start_time, @saturday_pager_end_time, @sunday_pager_start_time, @sunday_pager_end_time, @pager_days, @netsend_address, 0, 0, @category_id) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_UPDATE_OPERATOR */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_operator...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_operator') AND (type = 'P'))) DROP PROCEDURE sp_update_operator go CREATE PROCEDURE sp_update_operator @name sysname, @new_name sysname = NULL, @enabled TINYINT = NULL, @email_address NVARCHAR(100) = NULL, @pager_address NVARCHAR(100) = NULL, @weekday_pager_start_time INT = NULL, -- HHMMSS using 24 hour clock @weekday_pager_end_time INT = NULL, -- As above @saturday_pager_start_time INT = NULL, -- As above @saturday_pager_end_time INT = NULL, -- As above @sunday_pager_start_time INT = NULL, -- As above @sunday_pager_end_time INT = NULL, -- As above @pager_days TINYINT = NULL, @netsend_address NVARCHAR(100) = NULL, -- New for 7.0 @category_name sysname = NULL -- New for 7.0 AS BEGIN DECLARE @x_enabled TINYINT DECLARE @x_email_address NVARCHAR(100) DECLARE @x_pager_address NVARCHAR(100) DECLARE @x_weekday_pager_start_time INT DECLARE @x_weekday_pager_end_time INT DECLARE @x_saturday_pager_start_time INT DECLARE @x_saturday_pager_end_time INT DECLARE @x_sunday_pager_start_time INT DECLARE @x_sunday_pager_end_time INT DECLARE @x_pager_days TINYINT DECLARE @x_netsend_address NVARCHAR(100) DECLARE @x_category_id INT DECLARE @return_code INT DECLARE @notification_method INT DECLARE @alert_fail_safe_operator sysname DECLARE @current_msx_server sysname DECLARE @category_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @new_name = LTRIM(RTRIM(@new_name)) SELECT @email_address = LTRIM(RTRIM(@email_address)) SELECT @pager_address = LTRIM(RTRIM(@pager_address)) SELECT @netsend_address = LTRIM(RTRIM(@netsend_address)) SELECT @category_name = LTRIM(RTRIM(@category_name)) -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check if this Operator exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (name = @name))) BEGIN RAISERROR(14262, 16, 1, '@name', @name) RETURN(1) -- Failure END -- Check if this operator is 'MSXOperator' IF (@name = N'MSXOperator') BEGIN -- Disallow the update operation if we're at a TSX for all callers other than xp_msx_enlist EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @current_msx_server OUTPUT, N'no_output' IF ((@current_msx_server IS NOT NULL) AND (PROGRAM_NAME() <> N'xp_msx_enlist')) BEGIN RAISERROR(14223, 16, 1, 'MSXOperator', 'TSX') RETURN(1) -- Failure END END -- Get existing (@x_) operator property values SELECT @x_enabled = enabled, @x_email_address = email_address, @x_pager_address = pager_address, @x_weekday_pager_start_time = weekday_pager_start_time, @x_weekday_pager_end_time = weekday_pager_end_time, @x_saturday_pager_start_time = saturday_pager_start_time, @x_saturday_pager_end_time = saturday_pager_end_time, @x_sunday_pager_start_time = sunday_pager_start_time, @x_sunday_pager_end_time = sunday_pager_end_time, @x_pager_days = pager_days, @x_netsend_address = netsend_address, @x_category_id = category_id FROM msdb.dbo.sysoperators WHERE (name = @name) -- Fill out the values for all non-supplied parameters from the existsing values IF (@enabled IS NULL) SELECT @enabled = @x_enabled IF (@email_address IS NULL) SELECT @email_address = @x_email_address IF (@pager_address IS NULL) SELECT @pager_address = @x_pager_address IF (@weekday_pager_start_time IS NULL) SELECT @weekday_pager_start_time = @x_weekday_pager_start_time IF (@weekday_pager_end_time IS NULL) SELECT @weekday_pager_end_time = @x_weekday_pager_end_time IF (@saturday_pager_start_time IS NULL) SELECT @saturday_pager_start_time = @x_saturday_pager_start_time IF (@saturday_pager_end_time IS NULL) SELECT @saturday_pager_end_time = @x_saturday_pager_end_time IF (@sunday_pager_start_time IS NULL) SELECT @sunday_pager_start_time = @x_sunday_pager_start_time IF (@sunday_pager_end_time IS NULL) SELECT @sunday_pager_end_time = @x_sunday_pager_end_time IF (@pager_days IS NULL) SELECT @pager_days = @x_pager_days IF (@netsend_address IS NULL) SELECT @netsend_address = @x_netsend_address IF (@category_name IS NULL) SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = @x_category_id) IF (@category_name IS NULL) BEGIN SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = 99) END -- Turn [nullable] empty string parameters into NULLs IF (@email_address = N'') SELECT @email_address = NULL IF (@pager_address = N'') SELECT @pager_address = NULL IF (@netsend_address = N'') SELECT @netsend_address = NULL IF (@category_name = N'') SELECT @category_name = NULL -- Verify the operator EXECUTE @return_code = sp_verify_operator @new_name, @enabled, @pager_days, @weekday_pager_start_time, @weekday_pager_end_time, @saturday_pager_start_time, @saturday_pager_end_time, @sunday_pager_start_time, @sunday_pager_end_time, @category_name, @category_id OUTPUT IF (@return_code <> 0) RETURN(1) -- Failure -- If no new name is supplied, use the old one -- NOTE: We must do this AFTER calling sp_verify_operator. IF (@new_name IS NULL) SELECT @new_name = @name ELSE BEGIN -- You can't rename the MSXOperator IF (@name = N'MSXOperator') BEGIN RAISERROR(14222, 16, 1, 'MSXOperator') RETURN(1) -- Failure END END -- Do the UPDATE UPDATE msdb.dbo.sysoperators SET name = @new_name, enabled = @enabled, email_address = @email_address, pager_address = @pager_address, weekday_pager_start_time = @weekday_pager_start_time, weekday_pager_end_time = @weekday_pager_end_time, saturday_pager_start_time = @saturday_pager_start_time, saturday_pager_end_time = @saturday_pager_end_time, sunday_pager_start_time = @sunday_pager_start_time, sunday_pager_end_time = @sunday_pager_end_time, pager_days = @pager_days, netsend_address = @netsend_address, category_id = @category_id WHERE (name = @name) -- Check if the operator is 'MSXOperator', in which case we need to re-enlist all the targets -- so that they will download the new MSXOperator details IF ((@name = N'MSXOperator') AND ((SELECT COUNT(*) FROM msdb.dbo.systargetservers) > 0)) EXECUTE msdb.dbo.sp_post_msx_operation 'RE-ENLIST', 'SERVER', 0x00 -- Check if this operator is the FailSafe Operator EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertFailSafeOperator', @alert_fail_safe_operator OUTPUT, N'no_output' -- If it is, we update the 4 'AlertFailSafe...' registry entries and AlertNotificationMethod IF (LTRIM(RTRIM(@alert_fail_safe_operator)) = @name) BEGIN -- Update AlertFailSafeX values EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertFailSafeOperator', N'REG_SZ', @new_name EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertFailSafeEmailAddress', N'REG_SZ', @email_address EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertFailSafePagerAddress', N'REG_SZ', @pager_address EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertFailSafeNetSendAddress', N'REG_SZ', @netsend_address -- Update AlertNotificationMethod values EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertNotificationMethod', @notification_method OUTPUT, N'no_output' IF (LTRIM(RTRIM(@email_address)) IS NULL) SELECT @notification_method = @notification_method & ~1 IF (LTRIM(RTRIM(@pager_address)) IS NULL) SELECT @notification_method = @notification_method & ~2 IF (LTRIM(RTRIM(@netsend_address)) IS NULL) SELECT @notification_method = @notification_method & ~4 EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertNotificationMethod', N'REG_DWORD', @notification_method -- And finally, let SQLServerAgent know of the changes EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'G' END RETURN(0) -- Success END go /**************************************************************/ /* SP_HELP_OPERATOR */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_operator...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_operator') AND (type = 'P'))) DROP PROCEDURE sp_help_operator go CREATE PROCEDURE sp_help_operator @operator_name sysname = NULL, @operator_id INT = NULL AS BEGIN DECLARE @operator_id_as_char VARCHAR(10) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @operator_name = LTRIM(RTRIM(@operator_name)) IF (@operator_name = '') SELECT @operator_name = NULL -- Check operator name IF (@operator_name IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (name = @operator_name))) BEGIN RAISERROR(14262, -1, -1, '@operator_name', @operator_name) RETURN(1) -- Failure END END -- Check operator id IF (@operator_id IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (id = @operator_id))) BEGIN SELECT @operator_id_as_char = CONVERT(VARCHAR, @operator_id) RAISERROR(14262, -1, -1, '@operator_id', @operator_id_as_char) RETURN(1) -- Failure END END SELECT so.id, so.name, so.enabled, so.email_address, so.last_email_date, so.last_email_time, so.pager_address, so.last_pager_date, so.last_pager_time, so.weekday_pager_start_time, so.weekday_pager_end_time, so.saturday_pager_start_time, so.saturday_pager_end_time, so.sunday_pager_start_time, so.sunday_pager_end_time, so.pager_days, so.netsend_address, so.last_netsend_date, so.last_netsend_time, category_name = sc.name FROM msdb.dbo.sysoperators so LEFT OUTER JOIN msdb.dbo.syscategories sc ON (so.category_id = sc.category_id) WHERE ((@operator_name IS NULL) OR (so.name = @operator_name)) AND ((@operator_id IS NULL) OR (so.id = @operator_id)) ORDER BY so.name RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_HELP_OPERATOR_JOBS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_operator_jobs...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_help_operator_jobs') AND (type = 'P'))) DROP PROCEDURE sp_help_operator_jobs go CREATE PROCEDURE sp_help_operator_jobs @operator_name sysname = NULL AS BEGIN DECLARE @operator_id INT SET NOCOUNT ON -- Check operator name SELECT @operator_id = id FROM msdb.dbo.sysoperators WHERE (name = @operator_name) IF (@operator_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@operator_name', @operator_name) RETURN(1) -- Failure END -- Get the job info SELECT job_id, name, notify_level_email, notify_level_netsend, notify_level_page FROM msdb.dbo.sysjobs_view WHERE ((notify_email_operator_id = @operator_id) AND (notify_level_email <> 0)) OR ((notify_netsend_operator_id = @operator_id) AND (notify_level_netsend <> 0)) OR ((notify_page_operator_id = @operator_id) AND (notify_level_page <> 0)) RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_OPERATOR_IDENTIFIERS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_operator_identifiers...' IF (NOT OBJECT_ID(N'dbo.sp_verify_operator_identifiers', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_operator_identifiers go CREATE PROCEDURE sp_verify_operator_identifiers @name_of_name_parameter [varchar](60), @name_of_id_parameter [varchar](60), @operator_name [sysname] OUTPUT, @operator_id [INT] OUTPUT AS BEGIN DECLARE @retval INT DECLARE @operator_id_as_char NVARCHAR(36) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter)) SELECT @name_of_id_parameter = LTRIM(RTRIM(@name_of_id_parameter)) SELECT @operator_name = LTRIM(RTRIM(@operator_name)) IF (@operator_name = N'') SELECT @operator_name = NULL IF ((@operator_name IS NULL) AND (@operator_id IS NULL)) OR ((@operator_name IS NOT NULL) AND (@operator_id IS NOT NULL)) BEGIN RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- Check job id IF (@operator_id IS NOT NULL) BEGIN SELECT @operator_name = name FROM msdb.dbo.sysoperators WHERE (id = @operator_id) IF (@operator_name IS NULL) BEGIN SELECT @operator_id_as_char = CONVERT(nvarchar(36), @operator_id) RAISERROR(14262, -1, -1, '@operator_id', @operator_id_as_char) RETURN(1) -- Failure END END ELSE -- Check proxy name IF (@operator_name IS NOT NULL) BEGIN -- The name is not ambiguous, so get the corresponding operator_id (if the job exists) SELECT @operator_id = id FROM msdb.dbo.sysoperators WHERE (name = @operator_name) IF (@operator_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@operator_name', @operator_name) RETURN(1) -- Failure END END RETURN(0) -- Success END go /**************************************************************/ /* SP_NOTIFY_OPERATOR */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_notify_operator...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_notify_operator') AND (type = 'P'))) DROP PROCEDURE sp_notify_operator go CREATE PROCEDURE sp_notify_operator @profile_name sysname = NULL, --name of Database Mail profile to be used for sending email, cannot be null @id INT = NULL, @name sysname = NULL, --mutual exclusive, one and only one should be non null. Specify the operator whom mail adress will be used to send this email @subject NVARCHAR(256) = NULL, @body NVARCHAR(MAX) = NULL, -- This is the body of the email message @file_attachments NVARCHAR(512) = NULL, @mail_database sysname = N'msdb' -- Have infrastructure in place to support this but disabled by default -- For first implementation we will have this parameters but using it will generate an error - not implemented yet. AS BEGIN DECLARE @retval INT DECLARE @email_address NVARCHAR(100) DECLARE @enabled TINYINT DECLARE @qualified_sp_sendmail sysname DECLARE @db_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @profile_name = LTRIM(RTRIM(@profile_name)) SELECT @name = LTRIM(RTRIM(@name)) SELECT @file_attachments = LTRIM(RTRIM(@file_attachments)) SELECT @mail_database = LTRIM(RTRIM(@mail_database)) IF @profile_name = '' SELECT @profile_name = NULL IF @name = '' SELECT @name = NULL IF @file_attachments = '' SELECT @file_attachments = NULL IF @mail_database = '' SELECT @mail_database = NULL EXECUTE @retval = sp_verify_operator_identifiers '@name', '@id', @name OUTPUT, @id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure --is operator enabled? SELECT @enabled = enabled, @email_address = email_address FROM sysoperators WHERE id = @id IF @enabled = 0 BEGIN RAISERROR(14601, 16, 1, @name) RETURN 1 END IF @email_address IS NULL BEGIN RAISERROR(14602, 16, 1, @name) RETURN 1 END SELECT @qualified_sp_sendmail = @mail_database + '.dbo.sp_send_dbmail' EXEC @retval = @qualified_sp_sendmail @profile_name = @profile_name, @recipients = @email_address, @subject = @subject, @body = @body, @file_attachments = @file_attachments RETURN @retval END go /**************************************************************/ /* SP_VERIFY_NOTIFICATION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_notification...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_notification') AND (type = 'P'))) DROP PROCEDURE sp_verify_notification go CREATE PROCEDURE sp_verify_notification @alert_name sysname, @operator_name sysname, @notification_method TINYINT, @alert_id INT OUTPUT, @operator_id INT OUTPUT AS BEGIN DECLARE @res_valid_range NVARCHAR(100) SET NOCOUNT ON SELECT @res_valid_range = FORMATMESSAGE(14208) -- Remove any leading/trailing spaces from parameters SELECT @alert_name = LTRIM(RTRIM(@alert_name)) SELECT @operator_name = LTRIM(RTRIM(@operator_name)) -- Check if the AlertName is valid SELECT @alert_id = id FROM msdb.dbo.sysalerts WHERE (name = @alert_name) IF (@alert_id IS NULL) BEGIN RAISERROR(14262, 16, 1, '@alert_name', @alert_name) RETURN(1) -- Failure END -- Check if the OperatorName is valid SELECT @operator_id = id FROM msdb.dbo.sysoperators WHERE (name = @operator_name) IF (@operator_id IS NULL) BEGIN RAISERROR(14262, 16, 1, '@operator_name', @operator_name) RETURN(1) -- Failure END -- If we're at a TSX, we disallow using operator 'MSXOperator' IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservers)) AND (@operator_name = N'MSXOperator') BEGIN RAISERROR(14251, -1, -1, @operator_name) RETURN(1) -- Failure END -- Check if the NotificationMethod is valid IF ((@notification_method < 1) OR (@notification_method > 7)) BEGIN RAISERROR(14266, 16, 1, '@notification_method', @res_valid_range) RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_NOTIFICATION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_notification...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_notification') AND (type = 'P'))) DROP PROCEDURE sp_add_notification go CREATE PROCEDURE sp_add_notification @alert_name sysname, @operator_name sysname, @notification_method TINYINT -- 1 = Email, 2 = Pager, 4 = NetSend, 7 = All AS BEGIN DECLARE @alert_id INT DECLARE @operator_id INT DECLARE @notification NVARCHAR(512) DECLARE @retval INT DECLARE @old_has_notification INT DECLARE @new_has_notification INT DECLARE @res_notification NVARCHAR(100) SET NOCOUNT ON SELECT @res_notification = FORMATMESSAGE(14210) -- Remove any leading/trailing spaces from parameters SELECT @alert_name = LTRIM(RTRIM(@alert_name)) SELECT @operator_name = LTRIM(RTRIM(@operator_name)) -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check if the Notification is valid EXECUTE @retval = msdb.dbo.sp_verify_notification @alert_name, @operator_name, @notification_method, @alert_id OUTPUT, @operator_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check if this notification already exists -- NOTE: The unique index would catch this, but testing for the problem here lets us -- control the message. IF (EXISTS (SELECT * FROM msdb.dbo.sysnotifications WHERE (alert_id = @alert_id) AND (operator_id = @operator_id))) BEGIN SELECT @notification = @alert_name + N' / ' + @operator_name RAISERROR(14261, 16, 1, @res_notification, @notification) RETURN(1) -- Failure END SELECT @old_has_notification = has_notification FROM msdb.dbo.sysalerts WHERE (id = @alert_id) -- Do the INSERT INSERT INTO msdb.dbo.sysnotifications (alert_id, operator_id, notification_method) VALUES (@alert_id, @operator_id, @notification_method) SELECT @retval = @@error SELECT @new_has_notification = has_notification FROM msdb.dbo.sysalerts WHERE (id = @alert_id) -- Notify SQLServerAgent of the change - if any - to has_notifications IF (@old_has_notification <> @new_has_notification) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'U' RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_UPDATE_NOTIFICATION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_notification...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_notification') AND (type = 'P'))) DROP PROCEDURE sp_update_notification go CREATE PROCEDURE sp_update_notification @alert_name sysname, @operator_name sysname, @notification_method TINYINT -- 1 = Email, 2 = Pager, 4 = NetSend, 7 = All AS BEGIN DECLARE @alert_id INT DECLARE @operator_id INT DECLARE @notification NVARCHAR(512) DECLARE @retval INT DECLARE @old_has_notification INT DECLARE @new_has_notification INT DECLARE @res_notification NVARCHAR(100) SET NOCOUNT ON SELECT @res_notification = FORMATMESSAGE(14210) -- Remove any leading/trailing spaces from parameters SELECT @alert_name = LTRIM(RTRIM(@alert_name)) SELECT @operator_name = LTRIM(RTRIM(@operator_name)) -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check if the Notification is valid EXECUTE sp_verify_notification @alert_name, @operator_name, @notification_method, @alert_id OUTPUT, @operator_id OUTPUT -- Check if this notification exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysnotifications WHERE (alert_id = @alert_id) AND (operator_id = @operator_id))) BEGIN SELECT @notification = @alert_name + N' / ' + @operator_name RAISERROR(14262, 16, 1, @res_notification, @notification) RETURN(1) -- Failure END SELECT @old_has_notification = has_notification FROM msdb.dbo.sysalerts WHERE (id = @alert_id) -- Do the UPDATE UPDATE msdb.dbo.sysnotifications SET notification_method = @notification_method WHERE (alert_id = @alert_id) AND (operator_id = @operator_id) SELECT @retval = @@error SELECT @new_has_notification = has_notification FROM msdb.dbo.sysalerts WHERE (id = @alert_id) -- Notify SQLServerAgent of the change - if any - to has_notifications IF (@old_has_notification <> @new_has_notification) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'U' RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_DELETE_NOTIFICATION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_notification...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_notification') AND (type = 'P'))) DROP PROCEDURE sp_delete_notification go CREATE PROCEDURE sp_delete_notification @alert_name sysname, @operator_name sysname AS BEGIN DECLARE @alert_id INT DECLARE @operator_id INT DECLARE @ignored TINYINT DECLARE @notification NVARCHAR(512) DECLARE @retval INT DECLARE @old_has_notification INT DECLARE @new_has_notification INT DECLARE @res_notification NVARCHAR(100) SET NOCOUNT ON SELECT @res_notification = FORMATMESSAGE(14210) -- Remove any leading/trailing spaces from parameters SELECT @alert_name = LTRIM(RTRIM(@alert_name)) SELECT @operator_name = LTRIM(RTRIM(@operator_name)) -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Get the alert and operator ID's EXECUTE sp_verify_notification @alert_name, @operator_name, 7, -- A dummy (but valid) value @alert_id OUTPUT, @operator_id OUTPUT -- Check if this notification exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysnotifications WHERE (alert_id = @alert_id) AND (operator_id = @operator_id))) BEGIN SELECT @notification = @alert_name + N' / ' + @operator_name RAISERROR(14262, 16, 1, @res_notification, @notification) RETURN(1) -- Failure END SELECT @old_has_notification = has_notification FROM msdb.dbo.sysalerts WHERE (id = @alert_id) -- Do the Delete DELETE FROM msdb.dbo.sysnotifications WHERE (alert_id = @alert_id) AND (operator_id = @operator_id) SELECT @retval = @@error SELECT @new_has_notification = has_notification FROM msdb.dbo.sysalerts WHERE (id = @alert_id) -- Notify SQLServerAgent of the change - if any - to has_notifications IF (@old_has_notification <> @new_has_notification) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'U' RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_HELP_NOTIFICATION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_notification...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_notification') AND (type = 'P'))) DROP PROCEDURE sp_help_notification go CREATE PROCEDURE sp_help_notification @object_type CHAR(9), -- Either 'ALERTS' (enumerates Alerts for given Operator) -- or 'OPERATORS' (enumerates Operators for given Alert) @name sysname, -- Either an Operator Name (if @object_type is 'ALERTS') -- or an Alert Name (if @object_type is 'OPERATORS') @enum_type CHAR(10), -- Either 'ALL' (enumerate all objects [eg. all alerts irrespective of whether 'Fred' receives a notification for them]) -- or 'ACTUAL' (enumerate only the associated objects [eg. only the alerts which 'Fred' receives a notification for]) -- or 'TARGET' (enumerate only the objects matching @target_name [eg. a single row showing how 'Fred' is notfied for alert 'Test']) @notification_method TINYINT, -- Either 1 (Email) - Modifies the result set to only show use_email column -- or 2 (Pager) - Modifies the result set to only show use_pager column -- or 4 (NetSend) - Modifies the result set to only show use_netsend column -- or 7 (All) - Modifies the result set to show all the use_xxx columns @target_name sysname = NULL -- Either an Alert Name (if @object_type is 'ALERTS') -- or an Operator Name (if @object_type is 'OPERATORS') -- NOTE: This parameter is only required if @enum_type is 'TARGET') AS BEGIN DECLARE @id INT -- We use this to store the decode of @name DECLARE @target_id INT -- We use this to store the decode of @target_name DECLARE @select_clause NVARCHAR(1024) DECLARE @from_clause NVARCHAR(512) DECLARE @where_clause NVARCHAR(512) DECLARE @res_valid_range NVARCHAR(100) SET NOCOUNT ON SELECT @res_valid_range = FORMATMESSAGE(14208) -- Remove any leading/trailing spaces from parameters SELECT @object_type = UPPER(LTRIM(RTRIM(@object_type)) collate SQL_Latin1_General_CP1_CS_AS) SELECT @name = LTRIM(RTRIM(@name)) SELECT @enum_type = UPPER(LTRIM(RTRIM(@enum_type)) collate SQL_Latin1_General_CP1_CS_AS) SELECT @target_name = LTRIM(RTRIM(@target_name)) -- Turn [nullable] empty string parameters into NULLs IF (@target_name = N'') SELECT @target_name = NULL -- Check ObjectType IF (@object_type NOT IN ('ALERTS', 'OPERATORS')) BEGIN RAISERROR(14266, 16, 1, '@object_type', 'ALERTS, OPERATORS') RETURN(1) -- Failure END -- Check AlertName IF (@object_type = 'OPERATORS') AND (NOT EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (name = @name))) BEGIN RAISERROR(14262, 16, 1, '@name', @name) RETURN(1) -- Failure END -- Check OperatorName IF (@object_type = 'ALERTS') AND (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (name = @name))) BEGIN RAISERROR(14262, 16, 1, '@name', @name) RETURN(1) -- Failure END -- Check EnumType IF (@enum_type NOT IN ('ALL', 'ACTUAL', 'TARGET')) BEGIN RAISERROR(14266, 16, 1, '@enum_type', 'ALL, ACTUAL, TARGET') RETURN(1) -- Failure END -- Check Notification Method IF ((@notification_method < 1) OR (@notification_method > 7)) BEGIN RAISERROR(14266, 16, 1, '@notification_method', @res_valid_range) RETURN(1) -- Failure END -- If EnumType is 'TARGET', check if we have a @TargetName parameter IF (@enum_type = 'TARGET') AND (@target_name IS NULL) BEGIN RAISERROR(14502, 16, 1) RETURN(1) -- Failure END -- If EnumType isn't 'TARGET', we shouldn't have an @target_name parameter IF (@enum_type <> 'TARGET') AND (@target_name IS NOT NULL) BEGIN RAISERROR(14503, 16, 1) RETURN(1) -- Failure END -- Translate the Name into an ID IF (@object_type = 'ALERTS') BEGIN SELECT @id = id FROM msdb.dbo.sysoperators WHERE (name = @name) END IF (@object_type = 'OPERATORS') BEGIN SELECT @id = id FROM msdb.dbo.sysalerts WHERE (name = @name) END -- Translate the TargetName into a TargetID IF (@target_name IS NOT NULL) BEGIN IF (@object_type = 'OPERATORS') BEGIN SELECT @target_id = id FROM msdb.dbo.sysoperators WHERE (name = @target_name ) END IF (@object_type = 'ALERTS') BEGIN SELECT @target_id = id FROM msdb.dbo.sysalerts WHERE (name = @target_name) END IF (@target_id IS NULL) -- IE. the Target Name is invalid BEGIN RAISERROR(14262, 16, 1, @object_type, @target_name) RETURN(1) -- Failure END END -- Ok, the parameters look good so generate the SQL then EXECUTE() it... -- Generate the 'stub' SELECT clause and the FROM clause IF (@object_type = 'OPERATORS') -- So we want a list of Operators for the supplied AlertID BEGIN SELECT @select_clause = N'SELECT operator_id = o.id, operator_name = o.name, ' IF (@enum_type = 'ALL') SELECT @from_clause = N'FROM msdb.dbo.sysoperators o LEFT OUTER JOIN msdb.dbo.sysnotifications sn ON (o.id = sn.operator_id) ' ELSE SELECT @from_clause = N'FROM msdb.dbo.sysoperators o, msdb.dbo.sysnotifications sn ' END IF (@object_type = 'ALERTS') -- So we want a list of Alerts for the supplied OperatorID BEGIN SELECT @select_clause = N'SELECT alert_id = a.id, alert_name = a.name, ' IF (@enum_type = 'ALL') SELECT @from_clause = N'FROM msdb.dbo.sysalerts a LEFT OUTER JOIN msdb.dbo.sysnotifications sn ON (a.id = sn.alert_id) ' ELSE SELECT @from_clause = N'FROM msdb.dbo.sysalerts a, msdb.dbo.sysnotifications sn ' END -- Add the required use_xxx columns to the SELECT clause IF (@notification_method & 1 = 1) SELECT @select_clause = @select_clause + N'use_email = ISNULL((sn.notification_method & 1) / POWER(2, 0), 0), ' IF (@notification_method & 2 = 2) SELECT @select_clause = @select_clause + N'use_pager = ISNULL((sn.notification_method & 2) / POWER(2, 1), 0), ' IF (@notification_method & 4 = 4) SELECT @select_clause = @select_clause + N'use_netsend = ISNULL((sn.notification_method & 4) / POWER(2, 2), 0), ' -- Remove the trailing comma SELECT @select_clause = SUBSTRING(@select_clause, 1, (DATALENGTH(@select_clause) / 2) - 2) + N' ' -- Generate the WHERE clause IF (@object_type = 'OPERATORS') BEGIN IF (@enum_type = 'ALL') SELECT @from_clause = @from_clause + N' AND (sn.alert_id = ' + CONVERT(VARCHAR(10), @id) + N')' IF (@enum_type = 'ACTUAL') SELECT @where_clause = N'WHERE (o.id = sn.operator_id) AND (sn.alert_id = ' + CONVERT(VARCHAR(10), @id) + N') AND (sn.notification_method & ' + CONVERT(VARCHAR, @notification_method) + N' <> 0)' IF (@enum_type = 'TARGET') SELECT @where_clause = N'WHERE (o.id = sn.operator_id) AND (sn.operator_id = ' + CONVERT(VARCHAR(10), @target_id) + N') AND (sn.alert_id = ' + CONVERT(VARCHAR(10), @id) + N')' END IF (@object_type = 'ALERTS') BEGIN IF (@enum_type = 'ALL') SELECT @from_clause = @from_clause + N' AND (sn.operator_id = ' + CONVERT(VARCHAR(10), @id) + N')' IF (@enum_type = 'ACTUAL') SELECT @where_clause = N'WHERE (a.id = sn.alert_id) AND (sn.operator_id = ' + CONVERT(VARCHAR(10), @id) + N') AND (sn.notification_method & ' + CONVERT(VARCHAR, @notification_method) + N' <> 0)' IF (@enum_type = 'TARGET') SELECT @where_clause = N'WHERE (a.id = sn.alert_id) AND (sn.alert_id = ' + CONVERT(VARCHAR(10), @target_id) + N') AND (sn.operator_id = ' + CONVERT(VARCHAR(10), @id) + N')' END -- Add the has_email and has_pager columns to the SELECT clause IF (@object_type = 'OPERATORS') BEGIN SELECT @select_clause = @select_clause + N', has_email = PATINDEX(N''%[^ ]%'', ISNULL(o.email_address, N''''))' SELECT @select_clause = @select_clause + N', has_pager = PATINDEX(N''%[^ ]%'', ISNULL(o.pager_address, N''''))' SELECT @select_clause = @select_clause + N', has_netsend = PATINDEX(N''%[^ ]%'', ISNULL(o.netsend_address, N''''))' END IF (@object_type = 'ALERTS') BEGIN -- NOTE: We return counts so that the UI can detect 'unchecking' the last notification SELECT @select_clause = @select_clause + N', has_email = (SELECT COUNT(*) FROM sysnotifications WHERE (alert_id = a.id) AND ((notification_method & 1) = 1)) ' SELECT @select_clause = @select_clause + N', has_pager = (SELECT COUNT(*) FROM sysnotifications WHERE (alert_id = a.id) AND ((notification_method & 2) = 2)) ' SELECT @select_clause = @select_clause + N', has_netsend = (SELECT COUNT(*) FROM sysnotifications WHERE (alert_id = a.id) AND ((notification_method & 4) = 4)) ' END EXECUTE (@select_clause + @from_clause + @where_clause) RETURN(@@error) -- 0 means success END go PRINT '' PRINT 'Creating procedure sp_help_jobactivity...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_help_jobactivity') AND (type = 'P'))) DROP PROCEDURE sp_help_jobactivity go CREATE PROCEDURE sp_help_jobactivity @job_id UNIQUEIDENTIFIER = NULL, -- If provided should NOT also provide job_name @job_name sysname = NULL, -- If provided should NOT also provide job_id @session_id INT = NULL AS BEGIN DECLARE @retval INT DECLARE @session_id_as_char NVARCHAR(16) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters (except @owner_login_name) SELECT @job_name = LTRIM(RTRIM(@job_name)) -- Turn [nullable] empty string parameters into NULLs IF (@job_name = N'') SELECT @job_name = NULL IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL)) BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END IF @session_id IS NULL SELECT TOP(1) @session_id = session_id FROM syssessions ORDER by agent_start_date DESC ELSE IF NOT EXISTS( SELECT * FROM syssessions WHERE session_id = @session_id) BEGIN SELECT @session_id_as_char = CONVERT(NVARCHAR(16), @session_id) RAISERROR(14262, -1, -1, '@session_id', @session_id_as_char) RETURN(1) --failure END SELECT ja.session_id, ja.job_id, j.name AS job_name, ja.run_requested_date, ja.run_requested_source, ja.queued_date, ja.start_execution_date, ja.last_executed_step_id, ja.last_executed_step_date, ja.stop_execution_date, ja.next_scheduled_run_date, ja.job_history_id, jh.message, jh.run_status, jh.operator_id_emailed, jh.operator_id_netsent, jh.operator_id_paged FROM (msdb.dbo.sysjobactivity ja LEFT JOIN msdb.dbo.sysjobhistory jh ON ja.job_history_id = jh.instance_id) join msdb.dbo.sysjobs_view j on ja.job_id = j.job_id WHERE (@job_id IS NULL OR ja.job_id = @job_id) AND ja.session_id = @session_id RETURN(0) END go /**************************************************************/ /* */ /* T R I G G E R S */ /* */ /**************************************************************/ /**************************************************************/ /* DROP THE OLD 6.x TRIGGERS */ /* [multiple triggers of the same type are allowed in 7.0] */ /**************************************************************/ IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'NewOrChangedNotification') AND (type = 'TR'))) DROP TRIGGER NewOrChangedNotification IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'RemovedNotification') AND (type = 'TR'))) DROP TRIGGER RemovedNotification go /**************************************************************/ /* TRIG_NOTIFICATION_INS_OR_UPD */ /**************************************************************/ PRINT '' PRINT 'Creating trigger trig_notification_ins_or_upd...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_notification_ins_or_upd') AND (type = 'TR'))) DROP TRIGGER trig_notification_ins_or_upd go CREATE TRIGGER trig_notification_ins_or_upd ON msdb.dbo.sysnotifications FOR INSERT, UPDATE AS BEGIN SET NOCOUNT ON -- First, throw out 'non-notification' rows DELETE FROM msdb.dbo.sysnotifications WHERE (notification_method = 0) -- Reset the has_notification flag for the affected alerts UPDATE msdb.dbo.sysalerts SET has_notification = 0 FROM inserted i, msdb.dbo.sysalerts sa WHERE (i.alert_id = sa.id) -- Update sysalerts.has_notification (for email) UPDATE msdb.dbo.sysalerts SET has_notification = has_notification | 1 FROM msdb.dbo.sysalerts sa, msdb.dbo.sysnotifications sn, inserted i WHERE (sa.id = sn.alert_id) AND (sa.id = i.alert_id) AND ((sn.notification_method & 1) = 1) -- Update sysalerts.has_notification (for pager) UPDATE msdb.dbo.sysalerts SET has_notification = has_notification | 2 FROM msdb.dbo.sysalerts sa, msdb.dbo.sysnotifications sn, inserted i WHERE (sa.id = sn.alert_id) AND (sa.id = i.alert_id) AND ((sn.notification_method & 2) = 2) -- Update sysalerts.has_notification (for netsend) UPDATE msdb.dbo.sysalerts SET has_notification = has_notification | 4 FROM msdb.dbo.sysalerts sa, msdb.dbo.sysnotifications sn, inserted i WHERE (sa.id = sn.alert_id) AND (sa.id = i.alert_id) AND ((sn.notification_method & 4) = 4) END go /**************************************************************/ /* TRIG_NOTIFICATION_DELETE */ /**************************************************************/ PRINT '' PRINT 'Creating trigger trig_notification_delete...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_notification_delete') AND (type = 'TR'))) DROP TRIGGER trig_notification_delete go CREATE TRIGGER trig_notification_delete ON msdb.dbo.sysnotifications FOR DELETE AS BEGIN SET NOCOUNT ON -- Reset the has_notification flag for the affected alerts UPDATE msdb.dbo.sysalerts SET has_notification = 0 FROM deleted d, msdb.dbo.sysalerts sa WHERE (d.alert_id = sa.id) -- Update sysalerts.has_notification (for email) UPDATE msdb.dbo.sysalerts SET has_notification = has_notification | 1 FROM msdb.dbo.sysalerts sa, msdb.dbo.sysnotifications sn, deleted d WHERE (sa.id = sn.alert_id) AND (sa.id = d.alert_id) AND ((sn.notification_method & 1) = 1) -- Update sysalerts.has_notification (for pager) UPDATE msdb.dbo.sysalerts SET has_notification = has_notification | 2 FROM msdb.dbo.sysalerts sa, msdb.dbo.sysnotifications sn, deleted d WHERE (sa.id = sn.alert_id) AND (sa.id = d.alert_id) AND ((sn.notification_method & 2) = 2) -- Update sysalerts.has_notification (for netsend) UPDATE msdb.dbo.sysalerts SET has_notification = has_notification | 4 FROM msdb.dbo.sysalerts sa, msdb.dbo.sysnotifications sn, deleted d WHERE (sa.id = sn.alert_id) AND (sa.id = d.alert_id) AND ((sn.notification_method & 4) = 4) END go /**************************************************************/ /** **/ /** B A C K U P H I S T O R Y S U P P O R T **/ /** **/ /**************************************************************/ /**************************************************************/ /* T A B L E S */ /**************************************************************/ /**************************************************************/ /* BACKUPMEDIASET */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'backupmediaset'))) BEGIN PRINT '' PRINT 'Creating table backupmediaset...' CREATE TABLE backupmediaset ( media_set_id INT IDENTITY NOT NULL PRIMARY KEY, media_uuid UNIQUEIDENTIFIER NULL, -- Null if this media set only one media family media_family_count TINYINT NULL, -- Number of media families in the media set name NVARCHAR(128) NULL, description NVARCHAR(255) NULL, software_name NVARCHAR(128) NULL, software_vendor_id INT NULL, MTF_major_version TINYINT NULL, mirror_count TINYINT NULL, -- number of mirror plexes is_password_protected BIT NULL, is_compressed BIT NULL -- 1 if backup compression was used ) CREATE INDEX backupmediasetuuid ON backupmediaset (media_uuid) END ELSE BEGIN IF EXISTS ( select * from msdb.dbo.syscolumns where name='password_protected' and id = (select id from msdb.dbo.sysobjects where name='backupmediaset')) ALTER TABLE backupmediaset DROP COLUMN password_protected IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='is_password_protected' and id = (select id from msdb.dbo.sysobjects where name='backupmediaset')) ALTER TABLE backupmediaset ADD is_password_protected BIT NULL IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='is_compressed' and id = (select id from msdb.dbo.sysobjects where name='backupmediaset')) ALTER TABLE backupmediaset ADD is_compressed BIT NULL IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='mirror_count' and id = (select id from msdb.dbo.sysobjects where name='backupmediaset')) ALTER TABLE backupmediaset ADD mirror_count TINYINT NULL END go /**************************************************************/ /* BACKUPMEDIAFAMILY */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'backupmediafamily'))) BEGIN PRINT '' PRINT 'Creating table backupmediafamily...' CREATE TABLE backupmediafamily ( media_set_id INT NOT NULL REFERENCES backupmediaset(media_set_id), family_sequence_number TINYINT NOT NULL, -- Raid sequence number media_family_id UNIQUEIDENTIFIER NULL, -- This will be a uuid in MTF 2.0, allow space media_count INT NULL, -- Number of media in the family logical_device_name NVARCHAR(128) NULL, -- Name from sysdevices, if any physical_device_name NVARCHAR(260) NULL, -- To facilitate restores from online media (disk) device_type TINYINT NULL, -- Disk, tape, pipe, ... physical_block_size INT NULL, mirror TINYINT DEFAULT 0 NOT NULL PRIMARY KEY (media_set_id, family_sequence_number, mirror) ) CREATE INDEX backupmediafamilyuuid ON backupmediafamily (media_family_id) END ELSE BEGIN IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='mirror' and id = (select id from msdb.dbo.sysobjects where name='backupmediafamily')) BEGIN begin tran -- remove any old constraint, not involving mirror declare @pkName sysname DECLARE @sql NVARCHAR(4000) select @pkName=i.name from sys.indexes i, sys.all_objects o where o.object_id = object_id ('backupmediafamily') and o.object_id = i.object_id and i.is_primary_key = 1 IF (@pkName IS NOT NULL) begin select @sql = N'ALTER TABLE backupmediafamily DROP CONSTRAINT ' + @pkName EXEC (@sql) end ALTER TABLE backupmediafamily ADD mirror TINYINT DEFAULT 0 NOT NULL ALTER TABLE backupmediafamily ADD CONSTRAINT backupmediafamily_PK PRIMARY KEY (media_set_id, family_sequence_number, mirror) commit END END go /**************************************************************/ /* BACKUPSET - One row per backup operation. */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'backupset'))) BEGIN PRINT '' PRINT 'Creating table backupset...' CREATE TABLE backupset ( backup_set_id INT IDENTITY NOT NULL PRIMARY KEY, backup_set_uuid UNIQUEIDENTIFIER NOT NULL, media_set_id INT NOT NULL REFERENCES backupmediaset(media_set_id), first_family_number TINYINT NULL, -- family number & media number of the media first_media_number SMALLINT NULL, -- containing the start of this backup (first SSET) last_family_number TINYINT NULL, -- family number & media number of the media last_media_number SMALLINT NULL, -- containing the end of this backup (ESET after MBC) catalog_family_number TINYINT NULL, -- family number & media number of the media catalog_media_number SMALLINT NULL, -- containing the start of the 'directory' data stream position INT NULL, -- For FILE= expiration_date DATETIME NULL, -- From SSET... software_vendor_id INT NULL, -- Might want table for sw vendors name NVARCHAR(128) NULL, description NVARCHAR(255) NULL, user_name NVARCHAR(128) NULL, software_major_version TINYINT NULL, software_minor_version TINYINT NULL, software_build_version SMALLINT NULL, time_zone SMALLINT NULL, mtf_minor_version TINYINT NULL, -- From CONFIG_INFO... first_lsn NUMERIC(25,0) NULL, last_lsn NUMERIC(25,0) NULL, checkpoint_lsn NUMERIC(25,0) NULL, database_backup_lsn NUMERIC(25,0) NULL, database_creation_date DATETIME NULL, backup_start_date DATETIME NULL, backup_finish_date DATETIME NULL, type CHAR(1) NULL, sort_order SMALLINT NULL, code_page SMALLINT NULL, compatibility_level TINYINT NULL, database_version INT NULL, backup_size NUMERIC(20,0) NULL, database_name NVARCHAR(128) NULL, server_name NVARCHAR(128) NULL, machine_name NVARCHAR(128) NULL, flags INT NULL, unicode_locale INT NULL, unicode_compare_style INT NULL, collation_name NVARCHAR(128) NULL, is_password_protected BIT NULL, recovery_model NVARCHAR(60) NULL, has_bulk_logged_data BIT NULL, is_snapshot BIT NULL, is_readonly BIT NULL, is_single_user BIT NULL, has_backup_checksums BIT NULL, is_damaged BIT NULL, begins_log_chain BIT NULL, has_incomplete_metadata BIT NULL, is_force_offline BIT NULL, is_copy_only BIT NULL, first_recovery_fork_guid UNIQUEIDENTIFIER NULL, last_recovery_fork_guid UNIQUEIDENTIFIER NULL, fork_point_lsn NUMERIC(25,0) NULL, database_guid UNIQUEIDENTIFIER NULL, family_guid UNIQUEIDENTIFIER NULL, differential_base_lsn NUMERIC(25,0) NULL, differential_base_guid UNIQUEIDENTIFIER NULL, compressed_backup_size NUMERIC(20,0) NULL ) CREATE INDEX backupsetuuid ON backupset (backup_set_uuid) CREATE INDEX backupsetDate ON backupset (backup_finish_date) -- helps sp_delete_backuphistory END ELSE BEGIN IF EXISTS ( select * from msdb.dbo.syscolumns where name='password_protected' and id = (select id from msdb.dbo.sysobjects where name='backupset')) ALTER TABLE backupset DROP COLUMN password_protected IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='flags' and id = (select id from msdb.dbo.sysobjects where name='backupset')) ALTER TABLE backupset ADD flags INT NULL IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='collation_name' and id = (select id from msdb.dbo.sysobjects where name='backupset')) ALTER TABLE backupset ADD unicode_locale INT NULL, unicode_compare_style INT NULL, collation_name NVARCHAR(128) NULL IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='is_password_protected' and id = (select id from msdb.dbo.sysobjects where name='backupset')) ALTER TABLE backupset ADD is_password_protected BIT NULL IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='compressed_backup_size' and id = (select id from msdb.dbo.sysobjects where name='backupset')) ALTER TABLE backupset ADD compressed_backup_size NUMERIC(20,0) NULL IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='recovery_model' and id = (select id from msdb.dbo.sysobjects where name='backupset')) ALTER TABLE backupset ADD recovery_model NVARCHAR(60) NULL, has_bulk_logged_data BIT NULL, is_snapshot BIT NULL, is_readonly BIT NULL, is_single_user BIT NULL, has_backup_checksums BIT NULL, is_damaged BIT NULL, begins_log_chain BIT NULL, has_incomplete_metadata BIT NULL, is_force_offline BIT NULL, is_copy_only BIT NULL, first_recovery_fork_guid UNIQUEIDENTIFIER NULL, last_recovery_fork_guid UNIQUEIDENTIFIER NULL, fork_point_lsn NUMERIC(25,0) NULL, database_guid UNIQUEIDENTIFIER NULL, family_guid UNIQUEIDENTIFIER NULL, differential_base_lsn NUMERIC(25,0) NULL, differential_base_guid UNIQUEIDENTIFIER NULL IF NOT EXISTS ( SELECT * FROM msdb.sys.indexes WHERE (name = 'backupsetDate')) CREATE INDEX backupsetDate ON backupset (backup_finish_date) END go /**************************************************************/ -- BACKUPFILE/FILEGROUP -- One row per file/filegroup backed up /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'backupfilegroup'))) BEGIN PRINT '' PRINT 'Creating table backupfilegroup...' CREATE TABLE backupfilegroup ( backup_set_id INT NOT NULL REFERENCES backupset(backup_set_id), name NVARCHAR(128) NOT NULL, filegroup_id INT NOT NULL, filegroup_guid UNIQUEIDENTIFIER NULL, type CHAR(2) NOT NULL, type_desc NVARCHAR(60) NOT NULL, is_default BIT NOT NULL, is_readonly BIT NOT NULL, log_filegroup_guid UNIQUEIDENTIFIER NULL PRIMARY KEY (backup_set_id, filegroup_id) ) END go IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'backupfile'))) BEGIN PRINT '' PRINT 'Creating table backupfile...' CREATE TABLE backupfile ( backup_set_id INT NOT NULL REFERENCES backupset(backup_set_id), first_family_number TINYINT NULL, -- Family number & media number of he first media first_media_number SMALLINT NULL, -- containing this file filegroup_name NVARCHAR(128) NULL, page_size INT NULL, file_number NUMERIC(10,0) NOT NULL, backed_up_page_count NUMERIC(10,0) NULL, file_type CHAR(1) NULL, -- database or log source_file_block_size NUMERIC(10,0) NULL, file_size NUMERIC(20,0) NULL, logical_name NVARCHAR(128) NULL, physical_drive NVARCHAR(260) NULL, -- Drive or partition name physical_name NVARCHAR(260) NULL, -- Remainder of physical (OS) filename state TINYINT NULL, state_desc NVARCHAR(64) NULL, create_lsn NUMERIC(25,0) NULL, drop_lsn NUMERIC(25,0) NULL, file_guid UNIQUEIDENTIFIER NULL, read_only_lsn NUMERIC(25,0) NULL, read_write_lsn NUMERIC(25,0) NULL, differential_base_lsn NUMERIC(25,0) NULL, differential_base_guid UNIQUEIDENTIFIER NULL, backup_size NUMERIC(20,0) NULL, filegroup_guid UNIQUEIDENTIFIER NULL, is_readonly BIT NULL, is_present BIT NULL PRIMARY KEY (backup_set_id, file_number) ) END ELSE BEGIN IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='state' and id = (select id from msdb.dbo.sysobjects where name='backupfile')) BEGIN -- we want NVARCHAR instead of VARCHAR ALTER TABLE backupfile ALTER COLUMN physical_drive NVARCHAR(260) NULL ALTER TABLE backupfile ALTER COLUMN physical_name NVARCHAR(260) NULL ALTER TABLE backupfile ADD state TINYINT NULL, state_desc NVARCHAR(64) NULL, create_lsn NUMERIC(25,0) NULL, drop_lsn NUMERIC(25,0) NULL, file_guid UNIQUEIDENTIFIER NULL, read_only_lsn NUMERIC(25,0) NULL, read_write_lsn NUMERIC(25,0) NULL, differential_base_lsn NUMERIC(25,0) NULL, differential_base_guid UNIQUEIDENTIFIER NULL, backup_size NUMERIC(20,0) NULL, filegroup_guid UNIQUEIDENTIFIER NULL, is_readonly BIT NULL, is_present BIT NULL END END go /**************************************************************/ /* RESTOREHISTORY - One row per restore operation. */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'restorehistory'))) BEGIN PRINT '' PRINT 'Creating table restorehistory...' CREATE TABLE restorehistory ( restore_history_id INT NOT NULL IDENTITY PRIMARY KEY, restore_date DATETIME NULL, destination_database_name NVARCHAR(128) NULL, user_name NVARCHAR(128) NULL, backup_set_id INT NOT NULL REFERENCES backupset(backup_set_id), -- The backup set restored restore_type CHAR(1) NULL, -- Database, file, filegroup, log, verifyonly, ... -- Various options... replace BIT NULL, -- Replace(1), Noreplace(0) recovery BIT NULL, -- Recovery(1), Norecovery(0) restart BIT NULL, -- Restart(1), Norestart(0) stop_at DATETIME NULL, device_count TINYINT NULL, -- Can be less than number of media families stop_at_mark_name NVARCHAR(128) NULL, stop_before BIT NULL ) CREATE INDEX restorehistorybackupset ON restorehistory (backup_set_id) END IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE (name in ('stop_at_mark_name', 'stop_before')) AND (id = (SELECT id FROM msdb.dbo.sysobjects WHERE (name = 'restorehistory'))))) BEGIN IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='stop_before' and id = (select id from msdb.dbo.sysobjects where name='restorehistory')) BEGIN PRINT '' PRINT 'Adding columns to table restorehistory...' ALTER TABLE restorehistory ADD stop_at_mark_name NVARCHAR(128) NULL, stop_before BIT NULL END END go /**************************************************************/ /* RESTOREFILE - One row per file restored. */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'restorefile'))) BEGIN PRINT '' PRINT 'Creating table restorefile...' CREATE TABLE restorefile ( restore_history_id INT NOT NULL REFERENCES restorehistory(restore_history_id), file_number NUMERIC(10,0) NULL, -- Note: requires database to make unique destination_phys_drive NVARCHAR(260) NULL, destination_phys_name NVARCHAR(260) NULL ) END ELSE BEGIN ALTER TABLE restorefile ALTER COLUMN destination_phys_drive NVARCHAR(260) NULL ALTER TABLE restorefile ALTER COLUMN destination_phys_name NVARCHAR(260) NULL END go /**************************************************************/ /* RESTOREFILEGROUP - One row per filegroup restored. */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'restorefilegroup'))) BEGIN PRINT '' PRINT 'Creating table restorefilegroup...' CREATE TABLE restorefilegroup ( restore_history_id INT NOT NULL REFERENCES restorehistory(restore_history_id), filegroup_name NVARCHAR(128) NULL ) END go /**************************************************************/ /* LOGMARKHISTORY - One row per log mark generated */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'logmarkhistory'))) BEGIN PRINT '' PRINT 'Creating table logmarkhistory...' CREATE TABLE logmarkhistory ( database_name NVARCHAR(128) NOT NULL, mark_name NVARCHAR(128) NOT NULL, description NVARCHAR(255) NULL, user_name NVARCHAR(128) NOT NULL, lsn NUMERIC(25,0) NOT NULL, mark_time DATETIME NOT NULL ) CREATE INDEX logmarkhistory1 ON logmarkhistory (database_name, mark_name) CREATE INDEX logmarkhistory2 ON logmarkhistory (database_name, lsn) END go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'trig_backupset_delete') AND (OBJECTPROPERTY(id, 'IsTrigger') != 0))) BEGIN DROP TRIGGER trig_backupset_delete END go CREATE TRIGGER trig_backupset_delete ON msdb.dbo.backupset FOR DELETE AS BEGIN DELETE FROM msdb.dbo.logmarkhistory from deleted WHERE (msdb.dbo.logmarkhistory.database_name = deleted.database_name) AND (msdb.dbo.logmarkhistory.lsn >= deleted.first_lsn) AND (msdb.dbo.logmarkhistory.lsn < deleted.last_lsn) END go /**************************************************************/ /* suspect_pages */ /**************************************************************/ IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'badpagehistory'))) DROP TABLE badpagehistory go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'suspect_page_table'))) DROP TABLE suspect_page_table go IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'suspect_pages'))) BEGIN PRINT '' PRINT 'Creating table suspect_pages...' CREATE TABLE suspect_pages ( database_id INT NOT NULL, file_id INT NOT NULL, page_id bigint NOT NULL, -- we only use unsigned 32bits event_type INT NOT NULL, error_count INT NOT NULL, last_update_date DATETIME NOT NULL DEFAULT GETDATE() ) END go /**************************************************************/ /** **/ /** O B J E C T P E R M I S S I O N S **/ /** **/ /**************************************************************/ -------------------------------------------------------------- -- SQL Agent roles and procs -------------------------------------------------------------- PRINT '' PRINT 'Setting object permissions...' go -- Create the TargetServers role (for use by target servers when downloading jobs / uploading status) IF (EXISTS (SELECT * FROM msdb.dbo.sysusers WHERE (name = N'TargetServersRole') AND (issqlrole = 1))) BEGIN -- If there are no members in the role, then drop and re-create it IF ((SELECT COUNT(*) FROM msdb.dbo.sysusers su, msdb.dbo.sysmembers sm WHERE (su.uid = sm.groupuid) AND (su.name = N'TargetServersRole') AND (su.issqlrole = 1)) = 0) BEGIN EXECUTE msdb.dbo.sp_droprole @rolename = N'TargetServersRole' EXECUTE msdb.dbo.sp_addrole @rolename = N'TargetServersRole' END END ELSE EXECUTE msdb.dbo.sp_addrole @rolename = N'TargetServersRole' -- Create the SQLAgentUserRole role IF (EXISTS (SELECT * FROM msdb.dbo.sysusers WHERE (name = N'SQLAgentUserRole') AND (issqlrole = 1))) BEGIN -- If there are no members in the role, then drop and re-create it IF ((SELECT COUNT(*) FROM msdb.dbo.sysusers su, msdb.dbo.sysmembers sm WHERE (su.uid = sm.groupuid) AND (su.name = N'SQLAgentUserRole') AND (su.issqlrole = 1)) = 0) BEGIN EXECUTE msdb.dbo.sp_droprole @rolename = N'SQLAgentUserRole' EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentUserRole' END END ELSE EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentUserRole' -- Create the SQLAgentReaderRole role IF (EXISTS (SELECT * FROM msdb.dbo.sysusers WHERE (name = N'SQLAgentReaderRole') AND (issqlrole = 1))) BEGIN -- If there are no members in the role, then drop and re-create it IF ((SELECT COUNT(*) FROM msdb.dbo.sysusers su, msdb.dbo.sysmembers sm WHERE (su.uid = sm.groupuid) AND (su.name = N'SQLAgentReaderRole') AND (su.issqlrole = 1)) = 0) BEGIN EXECUTE msdb.dbo.sp_droprole @rolename = N'SQLAgentReaderRole' EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentReaderRole' END END ELSE EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentReaderRole' -- Create the SQLAgentOperatorRole role IF (EXISTS (SELECT * FROM msdb.dbo.sysusers WHERE (name = N'SQLAgentOperatorRole') AND (issqlrole = 1))) BEGIN -- If there are no members in the role, then drop and re-create it IF ((SELECT COUNT(*) FROM msdb.dbo.sysusers su, msdb.dbo.sysmembers sm WHERE (su.uid = sm.groupuid) AND (su.name = N'SQLAgentOperatorRole') AND (su.issqlrole = 1)) = 0) BEGIN EXECUTE msdb.dbo.sp_droprole @rolename = N'SQLAgentOperatorRole' EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentOperatorRole' END END ELSE EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentOperatorRole' -- Add roles to each other. -- SQLAgentReaderRole is also SQLAgentUserRole -- SQLAgentOperatorRole is also SQLAgentReaderRole and SQLAgentUserRole EXECUTE sp_addrolemember @rolename = 'SQLAgentUserRole' , @membername = 'SQLAgentReaderRole' EXECUTE sp_addrolemember @rolename = 'SQLAgentReaderRole' , @membername = 'SQLAgentOperatorRole' go GRANT EXECUTE ON sp_notify_operator TO SQLAgentUserRole -- Permissions a non-SA needs to create/update/delete a job GRANT EXECUTE ON sp_get_sqlagent_properties TO SQLAgentUserRole GRANT EXECUTE ON sp_help_category TO SQLAgentUserRole GRANT EXECUTE ON sp_enum_sqlagent_subsystems TO SQLAgentUserRole GRANT EXECUTE ON sp_add_jobserver TO SQLAgentUserRole GRANT EXECUTE ON sp_delete_jobserver TO SQLAgentUserRole GRANT SELECT ON syscategories TO SQLAgentUserRole GRANT EXECUTE ON sp_help_jobhistory TO SQLAgentUserRole GRANT EXECUTE ON sp_help_jobhistory_full TO SQLAgentUserRole GRANT EXECUTE ON sp_help_jobhistory_summary TO SQLAgentUserRole GRANT EXECUTE ON sp_help_jobhistory_sem TO SQLAgentUserRole GRANT EXECUTE ON sp_purge_jobhistory TO SQLAgentOperatorRole GRANT EXECUTE ON sp_add_jobstep TO SQLAgentUserRole GRANT EXECUTE ON sp_update_jobstep TO SQLAgentUserRole GRANT EXECUTE ON sp_delete_jobstep TO SQLAgentUserRole GRANT EXECUTE ON sp_help_jobstep TO SQLAgentUserRole GRANT EXECUTE ON sp_help_jobsteplog TO SQLAgentUserRole GRANT EXECUTE ON sp_delete_jobsteplog TO SQLAgentUserRole --Schedule related SP's GRANT EXECUTE ON sp_add_schedule TO SQLAgentUserRole GRANT EXECUTE ON sp_update_schedule TO SQLAgentUserRole GRANT EXECUTE ON sp_delete_schedule TO SQLAgentUserRole GRANT EXECUTE ON sp_attach_schedule TO SQLAgentUserRole GRANT EXECUTE ON sp_detach_schedule TO SQLAgentUserRole GRANT EXECUTE ON sp_help_schedule TO SQLAgentUserRole GRANT EXECUTE ON sp_help_jobcount TO SQLAgentUserRole GRANT EXECUTE ON sp_help_jobs_in_schedule TO SQLAgentUserRole GRANT EXECUTE ON sp_add_jobschedule TO SQLAgentUserRole GRANT EXECUTE ON sp_update_jobschedule TO SQLAgentUserRole GRANT EXECUTE ON sp_delete_jobschedule TO SQLAgentUserRole GRANT EXECUTE ON sp_help_jobschedule TO SQLAgentUserRole GRANT EXECUTE ON sp_add_job TO SQLAgentUserRole GRANT EXECUTE ON sp_update_job TO SQLAgentUserRole GRANT EXECUTE ON sp_delete_job TO SQLAgentUserRole GRANT EXECUTE ON sp_help_job TO SQLAgentUserRole GRANT EXECUTE ON sp_start_job TO SQLAgentUserRole GRANT EXECUTE ON sp_stop_job TO SQLAgentUserRole --alert spocs GRANT EXECUTE ON sp_help_alert TO SQLAgentOperatorRole --proxy sprocs GRANT EXECUTE ON sp_help_proxy TO SQLAgentUserRole GRANT EXECUTE ON sp_enum_login_for_proxy TO SQLAgentOperatorRole --other GRANT EXECUTE ON sp_help_jobserver TO SQLAgentUserRole GRANT EXECUTE ON sp_help_targetserver TO SQLAgentOperatorRole GRANT EXECUTE ON sp_help_notification TO SQLAgentOperatorRole GRANT EXECUTE ON sp_check_for_owned_jobs TO SQLAgentUserRole GRANT EXECUTE ON sp_check_for_owned_jobsteps TO SQLAgentUserRole GRANT EXECUTE ON sp_get_jobstep_db_username TO SQLAgentUserRole GRANT EXECUTE ON sp_get_job_alerts TO SQLAgentUserRole GRANT EXECUTE ON sp_uniquetaskname TO SQLAgentUserRole GRANT EXECUTE ON sp_addtask TO SQLAgentUserRole GRANT EXECUTE ON sp_droptask TO SQLAgentUserRole GRANT SELECT ON sysjobs_view TO SQLAgentUserRole GRANT SELECT ON sysschedules_localserver_view TO SQLAgentUserRole GRANT SELECT ON sysnotifications TO SQLAgentOperatorRole GRANT SELECT ON sysoperators TO SQLAgentOperatorRole GRANT SELECT ON sysalerts TO SQLAgentOperatorRole 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 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 sysoperators FROM SQLAgentUserRole REVOKE ALL ON sysnotifications FROM SQLAgentUserRole --DENY TargetServerRole permission that would allow modifying of jobs DENY ALL ON sp_add_jobserver TO TargetServersRole DENY ALL ON sp_delete_jobserver TO TargetServersRole DENY ALL ON sp_add_jobstep TO TargetServersRole DENY ALL ON sp_update_jobstep TO TargetServersRole DENY ALL ON sp_delete_jobstep TO TargetServersRole DENY ALL ON sp_add_jobschedule TO TargetServersRole DENY ALL ON sp_update_jobschedule TO TargetServersRole DENY ALL ON sp_delete_jobschedule TO TargetServersRole DENY ALL ON sp_add_job TO TargetServersRole DENY ALL ON sp_update_job TO TargetServersRole DENY ALL ON sp_delete_job TO TargetServersRole DENY ALL ON sp_start_job TO TargetServersRole DENY ALL ON sp_stop_job TO TargetServersRole DENY ALL ON sp_post_msx_operation TO TargetServersRole DENY ALL ON sp_addtask TO TargetServersRole DENY ALL ON sp_droptask TO TargetServersRole GRANT SELECT ON backupfile TO PUBLIC GRANT SELECT ON backupmediafamily TO PUBLIC GRANT SELECT ON backupmediaset TO PUBLIC GRANT SELECT ON backupset TO PUBLIC GRANT SELECT ON restorehistory TO PUBLIC GRANT SELECT ON restorefile TO PUBLIC GRANT SELECT ON restorefilegroup TO PUBLIC GRANT SELECT ON logmarkhistory TO PUBLIC GRANT SELECT ON suspect_pages TO PUBLIC GRANT SELECT, UPDATE, DELETE ON sysdownloadlist TO TargetServersRole GRANT SELECT, UPDATE ON sysjobservers TO TargetServersRole GRANT SELECT, UPDATE ON systargetservers TO TargetServersRole GRANT EXECUTE ON sp_downloaded_row_limiter TO TargetServersRole GRANT SELECT ON sysjobs TO TargetServersRole GRANT EXECUTE ON sp_help_jobstep TO TargetServersRole GRANT EXECUTE ON sp_help_jobschedule TO TargetServersRole GRANT EXECUTE ON sp_sqlagent_refresh_job TO TargetServersRole GRANT EXECUTE ON sp_sqlagent_probe_msx TO TargetServersRole GRANT EXECUTE ON sp_sqlagent_check_msx_version TO TargetServersRole GRANT EXECUTE ON sp_enlist_tsx TO TargetServersRole GRANT SELECT ON syssubsystems TO TargetServersRole GRANT EXECUTE ON sp_help_jobactivity TO SQLAgentUserRole GRANT EXECUTE ON sp_help_operator TO SQLAgentUserRole go USE msdb go /**************************************************************/ /**************************************************************/ /* BEGIN DTS */ /**************************************************************/ /**************************************************************/ /**************************************************************/ /* DTS TABLES */ /* These are never dropped since we dropped MSDB itself if */ /* this was an upgrade from pre-beta3, and we preserve beta3 */ /* packages. However, we need to add the owner_sid column */ /* if it's not there already, defaulting to sa ownership. */ /**************************************************************/ /**************************************************************/ /* SYSDTSCATEGORIES */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdtscategories'))) BEGIN PRINT '' PRINT 'Creating table sysdtscategories...' CREATE TABLE sysdtscategories ( name sysname NOT NULL, description NVARCHAR(1024) NULL, id UNIQUEIDENTIFIER NOT NULL, parentid UNIQUEIDENTIFIER NOT NULL, --// IID_NULL if a predefined root category CONSTRAINT pk_dtscategories PRIMARY KEY (id), CONSTRAINT uq_dtscategories_name_parent UNIQUE (name, parentid) ) /**************************************************************/ /* PREDEFINED DTS CATEGORIES */ /**************************************************************/ PRINT '' PRINT 'Adding predefined dts categories...' --// MUST BE IN SYNC with DTSPkg.h! --// These must be INSERTed explicitly as the IID_NULL parent does not exist. INSERT sysdtscategories VALUES (N'Local', 'DTS Packages stored on local SQL Server', 'B8C30000-A282-11D1-B7D9-00C04FB6EFD5', '00000000-0000-0000-0000-000000000000') INSERT sysdtscategories VALUES (N'Repository', 'DTS Packages stored on Repository', 'B8C30001-A282-11D1-B7D9-00C04FB6EFD5', '00000000-0000-0000-0000-000000000000') --// Default location for DTSPackage.SaveToSQLServer INSERT sysdtscategories VALUES (N'LocalDefault', 'Default local subcategory for DTS Packages', 'B8C30002-A282-11D1-B7D9-00C04FB6EFD5', 'B8C30000-A282-11D1-B7D9-00C04FB6EFD5') END GO /**************************************************************/ /* SYSDTSPACKAGES */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdtspackages'))) BEGIN PRINT '' PRINT 'Creating table sysdtspackages...' CREATE TABLE sysdtspackages ( name sysname NOT NULL, --// May have multiple ids id UNIQUEIDENTIFIER NOT NULL, --// May have multiple versionids versionid UNIQUEIDENTIFIER NOT NULL UNIQUE, description NVARCHAR(1024) NULL, categoryid UNIQUEIDENTIFIER NOT NULL REFERENCES sysdtscategories (id), createdate DATETIME, owner sysname, packagedata IMAGE, owner_sid VARBINARY(85) NOT NULL DEFAULT SUSER_SID(N'sa'), packagetype int NOT NULL DEFAULT 0 --// DTSPkgType_Default CONSTRAINT pk_dtspackages PRIMARY KEY (id, versionid) ) END ELSE BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'owner_sid' AND id = OBJECT_ID(N'sysdtspackages'))) BEGIN PRINT '' PRINT 'Altering table sysdtspackages for owner_sid and packagetype...' ALTER TABLE sysdtspackages ADD owner_sid VARBINARY(85) NOT NULL DEFAULT SUSER_SID(N'sa'), packagetype int NOT NULL DEFAULT 0 --// DTSPkgType_Default END IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'packagetype' AND id = OBJECT_ID(N'sysdtspackages'))) BEGIN PRINT '' PRINT 'Altering table sysdtspackages for packagetype...' ALTER TABLE sysdtspackages ADD packagetype int NOT NULL DEFAULT 0 --// DTSPkgType_Default END END GO /***************************************************************/ /* Create SSIS roles */ /***************************************************************/ if not exists (select * from dbo.sysusers where [name] = N'db_ssisadmin' and [issqlrole] = 1) BEGIN EXEC sp_addrole N'db_ssisadmin' END GO if not exists (select * from dbo.sysusers where [name] = N'db_ssisltduser' and [issqlrole] = 1) BEGIN EXEC sp_addrole N'db_ssisltduser' END GO if not exists (select * from dbo.sysusers where [name] = N'db_ssisoperator' and [issqlrole] = 1) BEGIN EXEC sp_addrole N'db_ssisoperator' END GO /**************************************************************/ /* SP_MAKE_DTSPACKAGENAME */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_dtsversion...' go IF OBJECT_ID(N'sp_get_dtsversion') IS NOT NULL DROP PROCEDURE sp_get_dtsversion go CREATE PROCEDURE sp_get_dtsversion AS /* Values for this are same as @@microsoftversion */ /* @@microsoftversion format is 0xaaiibbbb (aa = major, ii = minor, bb[bb] = build #) */ DECLARE @i INT select @i = 0x08000000 /* Must be in hex! */ /* Select the numeric value, and a conversion to make it readable */ select N'Microsoft SQLDTS Scripts' = @i, N'Version' = convert(binary(4), @i) GO GRANT EXECUTE ON sp_get_dtsversion TO [db_ssisadmin] GRANT EXECUTE ON sp_get_dtsversion TO [db_ssisltduser] GRANT EXECUTE ON sp_get_dtsversion TO [db_ssisoperator] GO /**************************************************************/ /* SP_MAKE_DTSPACKAGENAME */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_make_dtspackagename...' go IF OBJECT_ID(N'sp_make_dtspackagename') IS NOT NULL DROP PROCEDURE sp_make_dtspackagename go CREATE PROCEDURE sp_make_dtspackagename @categoryid UNIQUEIDENTIFIER, @name sysname OUTPUT, @flags int = 0 AS SET NOCOUNT ON --// If NULL catid, default to the LocalDefault category. IF (@categoryid IS NULL) SELECT @categoryid = 'B8C30002-A282-11d1-B7D9-00C04FB6EFD5' --// Validate category. We'll generate a unique name within category. DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @categoryid) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @categoryid) RAISERROR(14262, 16, 1, '@categoryid', @stringfromclsid) RETURN(1) -- Failure END --// Autogenerate the next name in our format. DECLARE @max sysname, @i INT, @spidchar NVARCHAR(20) --// Any logic we use may have collisions so let's get the max and wrap if we have to. --// @@spid is necessary for guaranteed uniqueness but makes it ugly so for now, don't use it. --// Note: use only 9 characters as it makes the pattern match easier without overflowing. SELECT @i = 0, @spidchar = '_' -- + LTRIM(STR(@@spid)) + '_' SELECT @max = MAX(name) FROM sysdtspackages WHERE name like 'DTS_' + @spidchar + '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' IF @max IS NOT NULL SELECT @i = CONVERT(INT, SUBSTRING(@max, (DATALENGTH(N'DTS_' + @spidchar) / 2) + 1, 9)) --// Wrap if needed. Find a gap in the names. IF @i < 999999999 BEGIN SELECT @i = @i + 1 END ELSE BEGIN SELECT @i = 1 DECLARE @existingname sysname DECLARE hC CURSOR LOCAL FOR SELECT name FROM sysdtspackages WHERE categoryid = @categoryid ORDER BY name FOR READ ONLY OPEN hC FETCH NEXT FROM hC INTO @existingname WHILE @@FETCH_STATUS = 0 AND @i < 999999999 BEGIN SELECT @name = 'DTS_' + @spidchar + REPLICATE('0', 9 - DATALENGTH(LTRIM(STR(@i)))) + LTRIM(STR(@i)) IF @existingname > @name BREAK SELECT @i = @i + 1 FETCH NEXT FROM hC INTO @existingname END CLOSE hC DEALLOCATE hC END --// Set the name. SELECT @name = 'DTS_' + @spidchar + REPLICATE('0', 9 - DATALENGTH(LTRIM(STR(@i)))) + LTRIM(STR(@i)) IF (@flags & 1) <> 0 SELECT @name GO GRANT EXECUTE ON sp_make_dtspackagename TO [db_ssisadmin] GRANT EXECUTE ON sp_make_dtspackagename TO [db_ssisltduser] GRANT EXECUTE ON sp_make_dtspackagename TO [db_ssisoperator] GO /**************************************************************/ /* SP_ADD_DTSPACKAGE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_dtspackage...' GO IF OBJECT_ID(N'sp_add_dtspackage') IS NOT NULL DROP PROCEDURE sp_add_dtspackage GO CREATE PROCEDURE sp_add_dtspackage @name sysname, @id UNIQUEIDENTIFIER, @versionid UNIQUEIDENTIFIER, @description NVARCHAR(255), @categoryid UNIQUEIDENTIFIER, @owner sysname, @packagedata IMAGE, @packagetype int = 0 --// DTSPkgType_Default AS SET NOCOUNT ON --// If NULL catid, default to the LocalDefault category. IF (@categoryid IS NULL) SELECT @categoryid = 'B8C30002-A282-11d1-B7D9-00C04FB6EFD5' --// Autogenerate name if it came in NULL. If it didn't, the below will validate uniqueness. IF DATALENGTH(@name) = 0 SELECT @name = NULL IF @name IS NULL BEGIN --// First see if they specified a new version based on id instead of name. if @id IS NOT NULL BEGIN SELECT @name = name FROM sysdtspackages WHERE @id = id IF @name IS NOT NULL GOTO AddPackage -- OK, add with the existing name END --// Name not available, autogenerate one. exec sp_make_dtspackagename @categoryid, @name OUTPUT GOTO AddPackage END --// Verify name unique within category. Allow a new versionid of the same name though. IF EXISTS (SELECT * FROM sysdtspackages WHERE name = @name AND categoryid = @categoryid AND id <> @id) BEGIN RAISERROR (14590, -1, -1, @name) RETURN(1) -- Failure END --// Verify that the same id is not getting a different name. IF EXISTS (SELECT * FROM sysdtspackages WHERE id = @id AND name <> @name) BEGIN DECLARE @stringfromclsid NVARCHAR(200) SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @id) RAISERROR (14597, -1, -1, @stringfromclsid) RETURN(1) -- Failure END --// Verify all versions of a package go in the same category. IF EXISTS (SELECT * FROM sysdtspackages WHERE id = @id AND categoryid <> @categoryid) BEGIN RAISERROR (14596, -1, -1, @name) RETURN(1) -- Failure END --// The real information is in the IMAGE; the rest is "documentary". --// Therefore, there is no need to verify anything. --// The REFERENCE in sysdtspackages will validate @categoryid. AddPackage: --// We will use the original owner_sid for all new versions - all must have the same owner. --// New packages will get the current login's SID as owner_sid. DECLARE @owner_sid VARBINARY(85) SELECT @owner_sid = MIN(owner_sid) FROM sysdtspackages WHERE id = @id IF @@rowcount = 0 OR @owner_sid IS NULL BEGIN SELECT @owner_sid = SUSER_SID() END ELSE BEGIN --// Only the owner of DTS Package ''%s'' or a member of the sysadmin role may create new versions of it. IF (@owner_sid <> SUSER_SID() AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR (14586, -1, -1, @name) RETURN(1) -- Failure END END --// Everything checks out, add the package or its new version. INSERT sysdtspackages ( name, id, versionid, description, categoryid, createdate, owner, packagedata, owner_sid, packagetype ) VALUES ( @name, @id, @versionid, @description, @categoryid, GETDATE(), @owner, @packagedata, @owner_sid, @packagetype ) RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_add_dtspackage TO [db_ssisadmin] GRANT EXECUTE ON sp_add_dtspackage TO [db_ssisltduser] GRANT EXECUTE ON sp_add_dtspackage TO [db_ssisoperator] GO /**************************************************************/ /* SP_DROP_DTSPACKAGE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_drop_dtspackage...' go IF OBJECT_ID(N'sp_drop_dtspackage') IS NOT NULL DROP PROCEDURE sp_drop_dtspackage go CREATE PROCEDURE sp_drop_dtspackage @name sysname, @id UNIQUEIDENTIFIER, @versionid UNIQUEIDENTIFIER AS SET NOCOUNT ON --// Does the specified package (uniquely) exist? Referencing by name only may not be unique. --// We do a bit of a hack here as SQL can't handle a DISTINCT clause with UNIQUEIDENTIFIER. --// @id will get the first id returned; if only name specified, see if there are more. DECLARE @findid UNIQUEIDENTIFIER SELECT @findid = id FROM sysdtspackages WHERE (@name IS NOT NULL OR @id IS NOT NULL OR @versionid IS NOT NULL) AND (@name IS NULL OR @name = name) AND (@id IS NULL OR @id = id) AND (@versionid IS NULL or @versionid = versionid) IF @@rowcount = 0 BEGIN DECLARE @pkgnotfound NVARCHAR(200) DECLARE @dts_package_res NVARCHAR(100) SELECT @pkgnotfound = FORMATMESSAGE(14599) + ' = ''' + ISNULL(@name, FORMATMESSAGE(14589)) + '''; ' + FORMATMESSAGE(14588) + ' {' SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @id IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @id) END + '}.{' SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @versionid IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @versionid) END + '}' SELECT @dts_package_res = FORMATMESSAGE(14594) RAISERROR(14262, 16, 1, @dts_package_res, @pkgnotfound) RETURN(1) -- Failure END ELSE IF @name IS NOT NULL AND @id IS NULL AND @versionid IS NULL AND EXISTS (SELECT * FROM sysdtspackages WHERE name = @name AND id <> @findid) BEGIN RAISERROR(14595, -1, -1, @name) RETURN(1) -- Failure END SELECT @id = @findid --// Only the owner of DTS Package ''%s'' or a member of the sysadmin role may drop it or any of its versions. --// sp_add_dtspackage ensures that all versions have the same owner_sid. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN IF (NOT EXISTS (SELECT * FROM sysdtspackages WHERE id = @id AND owner_sid = SUSER_SID())) BEGIN SELECT @name = name FROM sysdtspackages WHERE id = @id RAISERROR (14587, -1, -1, @name) RETURN(1) -- Failure END END --// If @versionid is NULL, drop all versions of name, else only the @versionid version. DELETE sysdtspackages WHERE id = @id AND (@versionid IS NULL OR @versionid = versionid) RETURN 0 -- SUCCESS go GRANT EXECUTE ON sp_drop_dtspackage TO [db_ssisadmin] GRANT EXECUTE ON sp_drop_dtspackage TO [db_ssisltduser] GRANT EXECUTE ON sp_drop_dtspackage TO [db_ssisoperator] go /**************************************************************/ /* SP_REASSIGN_DTSPACKAGEOWNER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_reassign_dtspackageowner...' go IF OBJECT_ID(N'sp_reassign_dtspackageowner') IS NOT NULL DROP PROCEDURE sp_reassign_dtspackageowner go CREATE PROCEDURE sp_reassign_dtspackageowner @name sysname, @id UNIQUEIDENTIFIER, @newloginname sysname AS SET NOCOUNT ON --// First, is this a valid login? IF SUSER_SID(@newloginname) IS NULL BEGIN RAISERROR(14262, -1, -1, '@newloginname', @newloginname) RETURN(1) -- Failure END --// Does the specified package (uniquely) exist? Referencing by name only may not be unique. --// We do a bit of a hack here as SQL can't handle a DISTINCT clause with UNIQUEIDENTIFIER. --// @id will get the first id returned; if only name specified, see if there are more. DECLARE @findid UNIQUEIDENTIFIER SELECT @findid = id FROM sysdtspackages WHERE (@name IS NOT NULL OR @id IS NOT NULL) AND (@name IS NULL OR @name = name) AND (@id IS NULL OR @id = id) IF @@rowcount = 0 BEGIN DECLARE @pkgnotfound NVARCHAR(200) DECLARE @dts_package_res NVARCHAR(100) SELECT @pkgnotfound = FORMATMESSAGE(14599) + ' = ''' + ISNULL(@name, FORMATMESSAGE(14589)) + '''; ' + FORMATMESSAGE(14588) + ' {' SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @id IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @id) END + '}.{' SELECT @pkgnotfound = @pkgnotfound + FORMATMESSAGE(14589) + '}' SELECT @dts_package_res = FORMATMESSAGE(14594) RAISERROR(14262, 16, 1, @dts_package_res, @pkgnotfound) RETURN(1) -- Failure END ELSE IF @name IS NOT NULL AND @id IS NULL AND EXISTS (SELECT * FROM sysdtspackages WHERE name = @name AND id <> @findid) BEGIN RAISERROR(14595, -1, -1, @name) RETURN(1) -- Failure END SELECT @id = @findid --// Only the owner of DTS Package ''%s'' or a member of the sysadmin role may reassign its ownership. --// sp_add_dtspackage ensures that all versions have the same owner_sid. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN IF (NOT EXISTS (SELECT * FROM sysdtspackages WHERE id = @id AND owner_sid = SUSER_SID())) BEGIN SELECT @name = name FROM sysdtspackages WHERE id = @id RAISERROR (14585, -1, -1, @name) RETURN(1) -- Failure END END --// Everything checks out, so reassign the owner. --// Note that @newloginname may be a sql server login rather than a network user, --// which is not quite the same as when a package is created. UPDATE sysdtspackages SET owner_sid = SUSER_SID(@newloginname), owner = @newloginname WHERE id = @id RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_reassign_dtspackageowner TO [db_ssisadmin] GRANT EXECUTE ON sp_reassign_dtspackageowner TO [db_ssisltduser] GRANT EXECUTE ON sp_reassign_dtspackageowner TO [db_ssisoperator] GO /**************************************************************/ /* SP_GET_DTSPACKAGE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_dtspackage...' go IF OBJECT_ID(N'sp_get_dtspackage') IS NOT NULL DROP PROCEDURE sp_get_dtspackage go CREATE PROCEDURE sp_get_dtspackage @name sysname, @id UNIQUEIDENTIFIER, @versionid UNIQUEIDENTIFIER AS SET NOCOUNT ON --// Does the specified package (uniquely) exist? Dropping by name only may not be unique. --// We do a bit of a hack here as SQL can't handle a DISTINCT clause with UNIQUEIDENTIFIER. --// @id will get the first id returned; if only name specified, see if there are more. DECLARE @findid UNIQUEIDENTIFIER SELECT @findid = id FROM sysdtspackages WHERE (@name IS NOT NULL OR @id IS NOT NULL OR @versionid IS NOT NULL) AND (@name IS NULL OR @name = name) AND (@id IS NULL OR @id = id) AND (@versionid IS NULL or @versionid = versionid) IF @@rowcount = 0 BEGIN DECLARE @pkgnotfound NVARCHAR(200) DECLARE @dts_package_res NVARCHAR(100) SELECT @pkgnotfound = FORMATMESSAGE(14599) + ' = ''' + ISNULL(@name, FORMATMESSAGE(14589)) + '''; ' + FORMATMESSAGE(14588) + ' {' SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @id IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @id) END + '}.{' SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @versionid IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @versionid) END + '}' SELECT @dts_package_res = FORMATMESSAGE(14594) RAISERROR(14262, 16, 1, @dts_package_res, @pkgnotfound) RETURN(1) -- Failure END ELSE IF @name IS NOT NULL AND @id IS NULL AND @versionid IS NULL AND EXISTS (SELECT * FROM sysdtspackages WHERE name = @name AND id <> @findid) BEGIN RAISERROR(14595, -1, -1, @name) RETURN(1) -- Failure END SELECT @id = @findid --// If @versionid is NULL, select all versions of name, else only the @versionid version. --// This must return the IMAGE as the rightmost column. SELECT name, id, versionid, description, createdate, owner, pkgsize = datalength(packagedata), packagedata, isowner = CASE WHEN (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 OR owner_sid = SUSER_SID()) THEN 1 ELSE 0 END, packagetype FROM sysdtspackages WHERE id = @id AND (@versionid IS NULL OR @versionid = versionid) ORDER BY name, createdate DESC RETURN 0 -- SUCCESS go GRANT EXECUTE ON sp_get_dtspackage TO [db_ssisadmin] GRANT EXECUTE ON sp_get_dtspackage TO [db_ssisltduser] GRANT EXECUTE ON sp_get_dtspackage TO [db_ssisoperator] go /**************************************************************/ /* SP_REASSIGN_DTSPACKAGECATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_reassign_dtspackagecategory...' go IF OBJECT_ID(N'sp_reassign_dtspackagecategory') IS NOT NULL DROP PROCEDURE sp_reassign_dtspackagecategory go CREATE PROCEDURE sp_reassign_dtspackagecategory @packageid UNIQUEIDENTIFIER, @categoryid UNIQUEIDENTIFIER AS SET NOCOUNT ON --// Does the package exist? DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * from sysdtspackages WHERE id = @packageid) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @packageid) RAISERROR(14262, 16, 1, '@packageid', @stringfromclsid) RETURN(1) -- Failure END --// Does the category exist? IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @categoryid) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @categoryid) RAISERROR(14262, 16, 1, '@categoryid', @stringfromclsid) RETURN(1) -- Failure END UPDATE sysdtspackages SET categoryid = @categoryid WHERE id = @packageid go /**************************************************************/ /* SP_ENUM_DTSPACKAGES */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_dtspackages...' go IF OBJECT_ID(N'sp_enum_dtspackages') IS NOT NULL DROP PROCEDURE sp_enum_dtspackages go CREATE PROCEDURE sp_enum_dtspackages @name_like sysname = '%', @description_like NVARCHAR(255) = '%', @categoryid UNIQUEIDENTIFIER = NULL, @flags INT = 0, --// Bitmask: 0x01 == return image data --// 0x02 == recursive (packagenames and categorynames only) --// 0x04 == all versions (default == only most-recent-versions) --// 0x08 == all prior versions versions (not most-recent; requires @id) @id UNIQUEIDENTIFIER = NULL, --// If non-NULL, enum versions of this package. @wanttype int = NULL --// If non-NULL, enum only packages of the given type AS IF (@flags & 0x02) <> 0 GOTO DO_RECURSE --// Just return the non-IMAGE stuff - sp_get_dtspackage will return the --// actual dtspackage info. DECLARE @latestversiondate datetime SELECT @latestversiondate = NULL IF (@flags & 0x08 = 0x08) BEGIN SELECT @latestversiondate = MAX(t.createdate) FROM sysdtspackages t WHERE t.id = @id IF @latestversiondate IS NULL BEGIN DECLARE @pkgnotfound NVARCHAR(200) DECLARE @dts_package_res NVARCHAR(100) SELECT @pkgnotfound = FORMATMESSAGE(14599) + ' = ' + FORMATMESSAGE(14589) + '; ' + FORMATMESSAGE(14588) + ' {' SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @id IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @id) END + '}.{' SELECT @pkgnotfound = @pkgnotfound + FORMATMESSAGE(14589) + '}' SELECT @dts_package_res = FORMATMESSAGE(14594) RAISERROR(14262, 16, 1, @dts_package_res, @pkgnotfound) RETURN(1) -- Failure END END SELECT p.name, p.id, p.versionid, p.description, p.createdate, p.owner, size = datalength(p.packagedata), packagedata = CASE (@flags & 0x01) WHEN 0 THEN NULL ELSE p.packagedata END, isowner = CASE WHEN (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 OR p.owner_sid = SUSER_SID()) THEN 1 ELSE 0 END, p.packagetype FROM sysdtspackages p WHERE (@name_like IS NULL OR p.name LIKE @name_like) AND (@description_like IS NULL OR p.description LIKE @description_like) AND (@categoryid IS NULL OR p.categoryid = @categoryid) AND (@id is NULL OR p.id = @id) -- These filter by version AND ( (@flags & 0x08 = 0x08 AND p.createdate < @latestversiondate) OR ( (@flags & 0x04 = 0x04) OR (@flags & 0x08 = 0 AND p.createdate = (SELECT MAX(t.createdate) FROM sysdtspackages t WHERE t.id = p.id)) ) ) AND (@wanttype is NULL or p.packagetype = @wanttype) ORDER BY id, createdate DESC RETURN 0 -- SUCCESS DO_RECURSE: DECLARE @packagesfound INT SELECT @packagesfound = 0 --// Starting parent category. If null, start at root. if (@categoryid IS NULL) SELECT @categoryid = '00000000-0000-0000-0000-000000000000' IF EXISTS (SELECT * FROM sysdtspackages p INNER JOIN sysdtscategories c ON p.categoryid = c.id WHERE p.categoryid = @categoryid AND (@name_like IS NULL OR p.name LIKE @name_like) AND (@description_like IS NULL OR p.description LIKE @description_like) ) SELECT @packagesfound = 1 IF (@packagesfound <> 0) BEGIN --// Identify the category and list its Packages. SELECT 'Level' = @@nestlevel, 'PackageName' = p.name, 'CategoryName' = c.name FROM sysdtspackages p INNER JOIN sysdtscategories c ON p.categoryid = c.id WHERE p.categoryid = @categoryid AND (@name_like IS NULL OR p.name LIKE @name_like) AND (@description_like IS NULL OR p.description LIKE @description_like) END --// List its subcategories' packages DECLARE @childid UNIQUEIDENTIFIER DECLARE hC CURSOR LOCAL FOR SELECT id FROM sysdtscategories c WHERE parentid = @categoryid ORDER BY c.name FOR READ ONLY OPEN hC FETCH NEXT FROM hC INTO @childid WHILE @@FETCH_STATUS = 0 BEGIN EXECUTE sp_enum_dtspackages @name_like, @description_like, @childid, @flags FETCH NEXT FROM hC INTO @childid END CLOSE hC DEALLOCATE hC RETURN 0 go GRANT EXECUTE ON sp_enum_dtspackages TO [db_ssisadmin] GRANT EXECUTE ON sp_enum_dtspackages TO [db_ssisltduser] GRANT EXECUTE ON sp_enum_dtspackages TO [db_ssisoperator] go /**************************************************************/ /* SP_ADD_DTSCATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_dtscategory...' go IF OBJECT_ID(N'sp_add_dtscategory') IS NOT NULL DROP PROCEDURE sp_add_dtscategory go CREATE PROCEDURE sp_add_dtscategory @name sysname, @description NVARCHAR(1024), @id UNIQUEIDENTIFIER, @parentid UNIQUEIDENTIFIER AS SET NOCOUNT ON --// If parentid is NULL, use 'Local' IF @parentid IS NULL SELECT @parentid = 'B8C30000-A282-11d1-B7D9-00C04FB6EFD5' --// First do some simple validation of "non-assert" cases. UI should validate others and the table --// definitions will act as an "assert", but we check here (with a nice message) for user-error stuff --// it would be hard for UI to validate. IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @parentid) BEGIN DECLARE @stringfromclsid NVARCHAR(200) SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @parentid) RAISERROR(14262, 16, 1, '@parentid', @stringfromclsid) RETURN(1) -- Failure END IF EXISTS (SELECT * FROM sysdtscategories WHERE name = @name AND parentid = @parentid) BEGIN RAISERROR(14591, 16, -1, @name) RETURN(1) -- Failure END --// id uniqueness is ensured by the primary key. INSERT sysdtscategories ( name, description, id, parentid ) VALUES ( @name, @description, @id, @parentid ) RETURN 0 -- SUCCESS go /**************************************************************/ /* SP_DROP_DTSCATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_drop_dtscategory...' go IF OBJECT_ID(N'sp_drop_dtscategory') IS NOT NULL DROP PROCEDURE sp_drop_dtscategory go CREATE PROCEDURE sp_drop_dtscategory @name_like sysname, @id UNIQUEIDENTIFIER = NULL, @flags INT = 0 --// Bitmask: 0x01 == recursive (drop all subcategories and packages) AS SET NOCOUNT ON --// Temp table in case recursion is needed. DECLARE @recurse TABLE (id UNIQUEIDENTIFIER, passcount INT DEFAULT(0)) IF (@name_like IS NOT NULL) BEGIN INSERT @recurse (id) SELECT id FROM sysdtscategories WHERE name LIKE @name_like IF @@rowcount = 0 BEGIN RAISERROR(14262, 16, 1, '@name_like', @name_like) RETURN(1) -- Failure END IF @@rowcount > 1 BEGIN RAISERROR(14592, 16, -1, @name_like) RETURN(1) -- Failure END SELECT @name_like = name, @id = id FROM sysdtscategories WHERE name LIKE @name_like END ELSE BEGIN --// Verify the id. @name_like will be NULL if we're here so no need to initialize. SELECT @name_like = name FROM sysdtscategories WHERE id = @id IF @name_like IS NULL BEGIN DECLARE @stringfromclsid NVARCHAR(200) SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @id) RAISERROR(14262, 16, 1, '@id', @stringfromclsid) RETURN(1) -- Failure END INSERT @recurse (id) VALUES (@id) END --// We now have a unique category. --// Cannot drop the predefined categories (or the root, which already failed above as IID_NULL --// is not an id in sysdtscategories). These will be at top level. IF @id IN ( 'B8C30000-A282-11d1-B7D9-00C04FB6EFD5' , 'B8C30001-A282-11d1-B7D9-00C04FB6EFD5' , 'B8C30002-A282-11d1-B7D9-00C04FB6EFD5' ) BEGIN RAISERROR(14598, 16, 1) RETURN(1) -- Failure END --// Check for subcategories or packages. IF EXISTS (SELECT * FROM sysdtspackages WHERE categoryid = @id) OR EXISTS (SELECT * FROM sysdtscategories WHERE parentid = @id) BEGIN --// It does. Make sure recursion was requested. IF (@flags & 0x01 = 0) BEGIN RAISERROR(14593, 16, -1, @name_like) RETURN(1) -- Failure END --// Fill up @recurse. UPDATE @recurse SET passcount = 0 WHILE (1 = 1) BEGIN UPDATE @recurse SET passcount = passcount + 1 INSERT @recurse (id, passcount) SELECT c.id, 0 FROM sysdtscategories c INNER JOIN @recurse r ON c.parentid = r.id WHERE passcount = 1 IF @@rowcount = 0 BREAK END END DELETE sysdtspackages FROM sysdtspackages INNER JOIN @recurse r ON sysdtspackages.categoryid = r.id DELETE sysdtscategories FROM sysdtscategories INNER JOIN @recurse r ON sysdtscategories.id = r.id RETURN(0) -- SUCCESS go /**************************************************************/ /* SP_MODIFY_DTSCATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_modify_dtscategory...' go IF OBJECT_ID(N'sp_modify_dtscategory') IS NOT NULL DROP PROCEDURE sp_modify_dtscategory go CREATE PROCEDURE sp_modify_dtscategory @id UNIQUEIDENTIFIER, @name sysname, @description NVARCHAR(1024), @parentid UNIQUEIDENTIFIER AS SET NOCOUNT ON --// Validate. DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @id) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @id) RAISERROR(14262, 16, 1, '@id', @stringfromclsid) RETURN(1) -- Failure END IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @parentid) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @parentid) RAISERROR(14262, 16, 1, '@parentid', @stringfromclsid) RETURN(1) -- Failure END --// Check the name uniqueness within parent, but make sure the id is different (we may just be renaming --// without reassigning parentage). IF EXISTS (SELECT * FROM sysdtscategories WHERE name = @name AND parentid = @parentid and id <> @id) BEGIN RAISERROR(14591, 16, -1, @name) RETURN(1) -- Failure END UPDATE sysdtscategories SET name = @name, description = @description, parentid = @parentid WHERE id = @id RETURN(0) -- SUCCESS go /**************************************************************/ /* SP_ENUM_DTSCATEGORIES */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_dtscategories...' go IF OBJECT_ID(N'sp_enum_dtscategories') IS NOT NULL DROP PROCEDURE sp_enum_dtscategories go CREATE PROCEDURE sp_enum_dtscategories @parentid UNIQUEIDENTIFIER = NULL, @flags INT = 0 --// Bitmask: 0x01 == recursive (enum all subcategories; names only) AS IF (@flags & 0x01) <> 0 GOTO DO_RECURSE --// Go to the root if no parentid specified IF @parentid IS NULL SELECT @parentid = '00000000-0000-0000-0000-000000000000' --// 'No results' is valid here. SELECT name, description, id FROM sysdtscategories WHERE parentid = @parentid ORDER BY name RETURN 0 DO_RECURSE: --// Identify the category. IF @@nestlevel <> 0 SELECT 'Level' = @@nestlevel, name FROM sysdtscategories WHERE id = @parentid --// List its subcategories DECLARE @childid UNIQUEIDENTIFIER DECLARE hC CURSOR LOCAL FOR SELECT id FROM sysdtscategories c WHERE parentid = @parentid ORDER BY c.name FOR READ ONLY OPEN hC FETCH NEXT FROM hC INTO @childid WHILE @@FETCH_STATUS = 0 BEGIN EXECUTE sp_enum_dtscategories @childid, @flags FETCH NEXT FROM hC INTO @childid END CLOSE hC DEALLOCATE hC RETURN 0 go /**************************************************************/ /* Drop Beta1 DTS Logging objects */ /**************************************************************/ if OBJECT_ID('sysdtspackagestepslog') IS NOT NULL BEGIN PRINT '' PRINT 'Dropping Beta1 logging tables and stored procedures...' DROP TABLE sysdtspackagestepslog IF OBJECT_ID('sysdtspackagelog') IS NOT NULL DROP TABLE sysdtspackagelog IF OBJECT_ID('sp_log_dtspackage') IS NOT NULL DROP PROCEDURE sp_log_dtspackage IF OBJECT_ID('sp_log_dtspackagesteps') IS NOT NULL DROP PROCEDURE sp_log_dtspackagesteps END /**************************************************************/ /* SYSDTSPACKAGELOG */ /**************************************************************/ if OBJECT_ID('sysdtspackagelog') IS NULL BEGIN PRINT '' PRINT 'Creating table sysdtspackagelog...' CREATE TABLE sysdtspackagelog ( name sysname NOT NULL, description NVARCHAR(1000) NULL, id UNIQUEIDENTIFIER NOT NULL, versionid UNIQUEIDENTIFIER NOT NULL, lineagefull UNIQUEIDENTIFIER NOT NULL PRIMARY KEY, lineageshort INT NOT NULL, starttime DATETIME NOT NULL, endtime DATETIME NULL, elapsedtime double precision NULL, computer sysname NOT NULL, operator sysname NOT NULL, logdate datetime NOT NULL DEFAULT GETDATE(), errorcode INT NULL, errordescription NVARCHAR(2000) NULL ) END /**************************************************************/ /* SYSDTSSTEPLOG */ /**************************************************************/ if OBJECT_ID('sysdtssteplog') IS NULL BEGIN PRINT '' PRINT 'Creating table sysdtssteplog...' CREATE TABLE sysdtssteplog ( stepexecutionid BIGINT IDENTITY (1, 1) NOT NULL PRIMARY KEY, lineagefull UNIQUEIDENTIFIER NOT NULL REFERENCES sysdtspackagelog(lineagefull) ON DELETE CASCADE, stepname sysname NOT NULL, stepexecstatus int NULL, stepexecresult int NULL, starttime DATETIME NOT NULL, endtime DATETIME NULL, elapsedtime double precision NULL, errorcode INT NULL, errordescription NVARCHAR(2000) NULL, progresscount BIGINT NULL ) END ELSE BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'stepexecresult' AND id = OBJECT_ID(N'sysdtssteplog'))) BEGIN PRINT '' PRINT 'Altering table sysdtssteplog...' ALTER TABLE sysdtssteplog ADD stepexecresult INT NULL DEFAULT 0 END END /**************************************************************/ /* SYSDTSTASKLOG */ /**************************************************************/ if OBJECT_ID('sysdtstasklog') IS NULL BEGIN PRINT '' PRINT 'Creating table sysdtstasklog...' CREATE TABLE sysdtstasklog ( stepexecutionid BIGINT NOT NULL REFERENCES sysdtssteplog (stepexecutionid) ON DELETE CASCADE, sequenceid INT NOT NULL, errorcode INT NOT NULL, description NVARCHAR(2000) NULL, PRIMARY KEY (stepexecutionid, sequenceid) ) END /**************************************************************/ /* SP_LOG_DTSPACKAGE_BEGIN */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_dtspackage_begin...' GO IF OBJECT_ID(N'sp_log_dtspackage_begin') IS NOT NULL DROP PROCEDURE sp_log_dtspackage_begin GO CREATE PROCEDURE sp_log_dtspackage_begin @name sysname, @description NVARCHAR(1000), @id UNIQUEIDENTIFIER, @versionid UNIQUEIDENTIFIER, @lineagefull UNIQUEIDENTIFIER, @lineageshort INT, @starttime DATETIME, @computer sysname, @operator sysname AS SET NOCOUNT ON INSERT sysdtspackagelog ( name, description, id, versionid, lineagefull, lineageshort, starttime, computer, operator ) VALUES ( @name, @description, @id, @versionid, @lineagefull, @lineageshort, @starttime, @computer, @operator ) RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_log_dtspackage_begin TO [db_ssisadmin] GRANT EXECUTE ON sp_log_dtspackage_begin TO [db_ssisltduser] GRANT EXECUTE ON sp_log_dtspackage_begin TO [db_ssisoperator] GO /**************************************************************/ /* SP_LOG_DTSPACKAGE_END */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_dtspackage_end...' GO IF OBJECT_ID(N'sp_log_dtspackage_end') IS NOT NULL DROP PROCEDURE sp_log_dtspackage_end GO CREATE PROCEDURE sp_log_dtspackage_end @lineagefull UNIQUEIDENTIFIER, @endtime DATETIME, @elapsedtime double precision, @errorcode INT, @errordescription NVARCHAR(2000) AS SET NOCOUNT ON --// Validate lineage. DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * FROM sysdtspackagelog WHERE lineagefull = @lineagefull) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @lineagefull) RAISERROR(14262, 16, 1, '@lineagefull', @stringfromclsid) RETURN(1) -- Failure END UPDATE sysdtspackagelog SET endtime = @endtime, elapsedtime = @elapsedtime, errorcode = @errorcode, errordescription = @errordescription WHERE lineagefull = @lineagefull RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_log_dtspackage_end TO [db_ssisadmin] GRANT EXECUTE ON sp_log_dtspackage_end TO [db_ssisltduser] GRANT EXECUTE ON sp_log_dtspackage_end TO [db_ssisoperator] GO /**************************************************************/ /* SP_LOG_DTSSTEP_BEGIN */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_dtsstep_begin...' GO IF OBJECT_ID(N'sp_log_dtsstep_begin') IS NOT NULL DROP PROCEDURE sp_log_dtsstep_begin GO CREATE PROCEDURE sp_log_dtsstep_begin @lineagefull UNIQUEIDENTIFIER, @stepname sysname, @starttime DATETIME AS SET NOCOUNT ON --// Validate lineage. DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * FROM sysdtspackagelog WHERE lineagefull = @lineagefull) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @lineagefull) RAISERROR(14262, 16, 1, '@lineagefull', @stringfromclsid) RETURN(1) -- Failure END INSERT sysdtssteplog ( lineagefull, stepname, starttime ) VALUES ( @lineagefull, @stepname, @starttime ) --// Return the @@identity for sp_log_dtstask and sp_logdtsstep_end SELECT @@IDENTITY RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_log_dtsstep_begin TO [db_ssisadmin] GRANT EXECUTE ON sp_log_dtsstep_begin TO [db_ssisltduser] GRANT EXECUTE ON sp_log_dtsstep_begin TO [db_ssisoperator] GO /**************************************************************/ /* SP_LOG_DTSSTEP_END */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_dtsstep_end...' GO IF OBJECT_ID(N'sp_log_dtsstep_end') IS NOT NULL DROP PROCEDURE sp_log_dtsstep_end GO CREATE PROCEDURE sp_log_dtsstep_end @stepexecutionid BIGINT, @stepexecstatus int, @stepexecresult int, @endtime DATETIME, @elapsedtime double precision, @errorcode INT, @errordescription NVARCHAR(2000), @progresscount BIGINT AS SET NOCOUNT ON --// Validate @stepexecutionid. DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * FROM sysdtssteplog WHERE stepexecutionid = @stepexecutionid) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @stepexecutionid) RAISERROR(14262, 16, 1, '@stepexecutionid', @stringfromclsid) RETURN(1) -- Failure END UPDATE sysdtssteplog SET stepexecstatus = @stepexecstatus, stepexecresult = @stepexecresult, endtime = @endtime, elapsedtime = @elapsedtime, errorcode = @errorcode, errordescription = @errordescription, progresscount = @progresscount WHERE stepexecutionid = @stepexecutionid RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_log_dtsstep_end TO [db_ssisadmin] GRANT EXECUTE ON sp_log_dtsstep_end TO [db_ssisltduser] GRANT EXECUTE ON sp_log_dtsstep_end TO [db_ssisoperator] GO /**************************************************************/ /* SP_LOG_DTSTASK */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_dtstask...' GO IF OBJECT_ID(N'sp_log_dtstask') IS NOT NULL DROP PROCEDURE sp_log_dtstask GO CREATE PROCEDURE sp_log_dtstask @stepexecutionid BIGINT, @sequenceid INT, @errorcode INT, @description NVARCHAR(2000) AS SET NOCOUNT ON --// Validate @stepexecutionid. DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * FROM sysdtssteplog WHERE stepexecutionid = @stepexecutionid) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @stepexecutionid) RAISERROR(14262, 16, 1, '@stepexecutionid', @stringfromclsid) RETURN(1) -- Failure END INSERT sysdtstasklog ( stepexecutionid, sequenceid, errorcode, description ) VALUES ( @stepexecutionid, @sequenceid, @errorcode, @description ) RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_log_dtstask TO [db_ssisadmin] GRANT EXECUTE ON sp_log_dtstask TO [db_ssisltduser] GRANT EXECUTE ON sp_log_dtstask TO [db_ssisoperator] GO /**************************************************************/ /* SP_ENUM_DTSPACKAGELOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_dtspackagelog...' GO IF OBJECT_ID(N'sp_enum_dtspackagelog') IS NOT NULL DROP PROCEDURE sp_enum_dtspackagelog GO CREATE PROCEDURE sp_enum_dtspackagelog @name sysname, @flags INT = 0, --// Bitmask: 0x01 == return only latest @id UNIQUEIDENTIFIER = NULL, --// If non-NULL, use instead of @name. @versionid UNIQUEIDENTIFIER = NULL, --// If non-NULL, use instead of @id or @name @lineagefull UNIQUEIDENTIFIER = NULL --// If non-NULL, use instead of @versionid or @id or @name AS SET NOCOUNT ON --// This is used for realtime viewing of package logs, so don't error if no entries --// found, simply return an empty result set. SELECT p.name, p.description, p.id, p.versionid, p.lineagefull, p.lineageshort, p.starttime, p.endtime, p.elapsedtime, p.computer, p.operator, p.logdate, p.errorcode, p.errordescription FROM sysdtspackagelog p WHERE ((@lineagefull IS NULL OR p.lineagefull = @lineagefull) AND (@versionid IS NULL OR p.versionid = @versionid) AND (@id IS NULL OR p.id = @id) AND (@name IS NULL OR p.name = @name)) AND ((@flags & 0x01) = 0 OR p.logdate = ( SELECT MAX(logdate) FROM sysdtspackagelog d WHERE (d.id = p.id) ) ) ORDER BY logdate RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_enum_dtspackagelog TO [db_ssisadmin] GRANT EXECUTE ON sp_enum_dtspackagelog TO [db_ssisltduser] GRANT EXECUTE ON sp_enum_dtspackagelog TO [db_ssisoperator] GO /**************************************************************/ /* SP_ENUM_DTSSTEPLOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_dtssteplog...' GO IF OBJECT_ID(N'sp_enum_dtssteplog') IS NOT NULL DROP PROCEDURE sp_enum_dtssteplog GO CREATE PROCEDURE sp_enum_dtssteplog @lineagefull UNIQUEIDENTIFIER = NULL, -- all steps in this package execution @stepexecutionid BIGINT = NULL AS SET NOCOUNT ON --// This is used for realtime viewing of package logs, so don't error if no entries --// found, simply return an empty result set. --// This query must be restricted within a single package execution (lineage); it may --// be further restricted by stepexecutionid to a single step within that package execution. SELECT stepexecutionid, lineagefull, stepname, stepexecstatus, stepexecresult, starttime, endtime, elapsedtime, errorcode, errordescription, progresscount FROM sysdtssteplog WHERE (@lineagefull IS NULL OR lineagefull = @lineagefull) AND (@stepexecutionid IS NULL OR stepexecutionid = @stepexecutionid) ORDER BY stepexecutionid RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_enum_dtssteplog TO [db_ssisadmin] GRANT EXECUTE ON sp_enum_dtssteplog TO [db_ssisltduser] GRANT EXECUTE ON sp_enum_dtssteplog TO [db_ssisoperator] GO /**************************************************************/ /* SP_ENUM_DTSTASKLOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_dtstasklog...' GO IF OBJECT_ID(N'sp_enum_dtstasklog') IS NOT NULL DROP PROCEDURE sp_enum_dtstasklog GO CREATE PROCEDURE sp_enum_dtstasklog @stepexecutionid BIGINT, @sequenceid INT = NULL AS SET NOCOUNT ON --// This is used for realtime viewing of package logs, so don't error if no entries --// found, simply return an empty result set. --// This query must be restricted within a single step execution; it may --// be further restricted by stepexecutionid to a single record within that step execution. SELECT -- stepexecutionid, -- this is always passed in so we don't need to return it. sequenceid, errorcode, description FROM sysdtstasklog WHERE (stepexecutionid IS NULL or stepexecutionid = @stepexecutionid) AND (@sequenceid IS NULL OR sequenceid = @sequenceid) ORDER BY sequenceid RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_enum_dtstasklog TO [db_ssisadmin] GRANT EXECUTE ON sp_enum_dtstasklog TO [db_ssisltduser] GRANT EXECUTE ON sp_enum_dtstasklog TO [db_ssisoperator] GO /**************************************************************/ /* SP_DUMP_DTSLOG_ALL */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_dump_dtslog_all...' GO IF OBJECT_ID(N'sp_dump_dtslog_all') IS NOT NULL DROP PROCEDURE sp_dump_dtslog_all GO CREATE PROCEDURE sp_dump_dtslog_all AS SET NOCOUNT ON --// sysadmin only. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END DELETE sysdtspackagelog RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_dump_dtslog_all TO [db_ssisadmin] GRANT EXECUTE ON sp_dump_dtslog_all TO [db_ssisltduser] GRANT EXECUTE ON sp_dump_dtslog_all TO [db_ssisoperator] GO /**************************************************************/ /* SP_DUMP_DTSPACKAGELOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_dump_dtspackagelog...' GO IF OBJECT_ID(N'sp_dump_dtspackagelog') IS NOT NULL DROP PROCEDURE sp_dump_dtspackagelog GO CREATE PROCEDURE sp_dump_dtspackagelog @name sysname, @flags INT = 0, --// Bitmask: 0x01 == preserve latest @id UNIQUEIDENTIFIER = NULL, --// If non-NULL, use instead of @name. @versionid UNIQUEIDENTIFIER = NULL, --// If non-NULL, use instead of @id or @name @lineagefull UNIQUEIDENTIFIER = NULL --// If non-NULL, use instead of @versionid or @id or @name AS SET NOCOUNT ON --// sysadmin only. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END --// Don't error if no entries found, as the desired result will be met. --// DELETE will CASCADE DELETE sysdtspackagelog FROM sysdtspackagelog p WHERE ((@lineagefull IS NULL OR p.lineagefull = @lineagefull) AND (@versionid IS NULL OR p.versionid = @versionid) AND (@id IS NULL OR p.id = @id) AND (@name IS NULL OR p.name = @name)) AND ((@flags & 0x01) = 0 OR p.logdate < ( SELECT MAX(logdate) FROM sysdtspackagelog d WHERE (d.id = p.id) ) ) RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_dump_dtspackagelog TO [db_ssisadmin] GRANT EXECUTE ON sp_dump_dtspackagelog TO [db_ssisltduser] GRANT EXECUTE ON sp_dump_dtspackagelog TO [db_ssisoperator] GO /**************************************************************/ /* SP_DUMP_DTSSTEPLOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_dump_dtssteplog...' GO IF OBJECT_ID(N'sp_dump_dtssteplog') IS NOT NULL DROP PROCEDURE sp_dump_dtssteplog GO CREATE PROCEDURE sp_dump_dtssteplog @lineagefull UNIQUEIDENTIFIER = NULL, -- all steps in this package execution @stepexecutionid BIGINT = NULL AS SET NOCOUNT ON --// sysadmin only. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END --// Don't error if no entries found, as the desired result will be met. --// DELETE will CASCADE DELETE sysdtssteplog WHERE (@lineagefull IS NULL OR lineagefull = @lineagefull) AND (@stepexecutionid IS NULL OR stepexecutionid = @stepexecutionid) RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_dump_dtssteplog TO [db_ssisadmin] GRANT EXECUTE ON sp_dump_dtssteplog TO [db_ssisltduser] GRANT EXECUTE ON sp_dump_dtssteplog TO [db_ssisoperator] GO /**************************************************************/ /* SP_DUMP_DTSTASKLOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_dump_dtstasklog...' GO IF OBJECT_ID(N'sp_dump_dtstasklog') IS NOT NULL DROP PROCEDURE sp_dump_dtstasklog GO CREATE PROCEDURE sp_dump_dtstasklog @stepexecutionid BIGINT, @sequenceid INT = NULL AS SET NOCOUNT ON --// sysadmin only. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END --// Don't error if no entries found, as the desired result will be met. DELETE sysdtstasklog WHERE (stepexecutionid IS NULL or stepexecutionid = @stepexecutionid) AND (@sequenceid IS NULL OR sequenceid = @sequenceid) RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_dump_dtstasklog TO [db_ssisadmin] GRANT EXECUTE ON sp_dump_dtstasklog TO [db_ssisltduser] GRANT EXECUTE ON sp_dump_dtstasklog TO [db_ssisoperator] GO /**************************************************************/ /* SP_DTS_SECURE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_dts_secure ...' GO IF OBJECT_ID(N'sp_dts_secure') IS NOT NULL DROP PROCEDURE sp_dts_secure GO CREATE PROCEDURE sp_dts_secure @flag INT AS SET NOCOUNT ON --// sysadmin only. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END IF(@flag <> 0) BEGIN REVOKE EXECUTE ON sp_get_dtsversion FROM PUBLIC REVOKE EXECUTE ON sp_make_dtspackagename FROM PUBLIC REVOKE EXECUTE ON sp_add_dtspackage FROM PUBLIC REVOKE EXECUTE ON sp_drop_dtspackage FROM PUBLIC REVOKE EXECUTE ON sp_reassign_dtspackageowner FROM PUBLIC REVOKE EXECUTE ON sp_get_dtspackage FROM PUBLIC REVOKE EXECUTE ON sp_enum_dtspackages FROM PUBLIC REVOKE EXECUTE ON sp_log_dtspackage_begin FROM PUBLIC REVOKE EXECUTE ON sp_log_dtspackage_end FROM PUBLIC REVOKE EXECUTE ON sp_log_dtsstep_begin FROM PUBLIC REVOKE EXECUTE ON sp_log_dtsstep_end FROM PUBLIC REVOKE EXECUTE ON sp_log_dtstask FROM PUBLIC REVOKE EXECUTE ON sp_enum_dtspackagelog FROM PUBLIC REVOKE EXECUTE ON sp_enum_dtssteplog FROM PUBLIC REVOKE EXECUTE ON sp_enum_dtstasklog FROM PUBLIC REVOKE EXECUTE ON sp_dump_dtslog_all FROM PUBLIC REVOKE EXECUTE ON sp_dump_dtspackagelog FROM PUBLIC REVOKE EXECUTE ON sp_dump_dtssteplog FROM PUBLIC REVOKE EXECUTE ON sp_dump_dtstasklog FROM PUBLIC END ELSE BEGIN GRANT EXECUTE ON sp_get_dtsversion TO PUBLIC GRANT EXECUTE ON sp_make_dtspackagename TO PUBLIC GRANT EXECUTE ON sp_add_dtspackage TO PUBLIC GRANT EXECUTE ON sp_drop_dtspackage TO PUBLIC GRANT EXECUTE ON sp_reassign_dtspackageowner TO PUBLIC GRANT EXECUTE ON sp_get_dtspackage TO PUBLIC GRANT EXECUTE ON sp_enum_dtspackages TO PUBLIC GRANT EXECUTE ON sp_log_dtspackage_begin TO PUBLIC GRANT EXECUTE ON sp_log_dtspackage_end TO PUBLIC GRANT EXECUTE ON sp_log_dtsstep_begin TO PUBLIC GRANT EXECUTE ON sp_log_dtsstep_end TO PUBLIC GRANT EXECUTE ON sp_log_dtstask TO PUBLIC GRANT EXECUTE ON sp_enum_dtspackagelog TO PUBLIC GRANT EXECUTE ON sp_enum_dtssteplog TO PUBLIC GRANT EXECUTE ON sp_enum_dtstasklog TO PUBLIC GRANT EXECUTE ON sp_dump_dtslog_all TO PUBLIC GRANT EXECUTE ON sp_dump_dtspackagelog TO PUBLIC GRANT EXECUTE ON sp_dump_dtssteplog TO PUBLIC GRANT EXECUTE ON sp_dump_dtstasklog TO PUBLIC END RETURN 0 GO /**************************************************************/ /* */ /* D A T A B A S E M A I L */ /* */ /**************************************************************/ /**************************************************************/ /* */ /* Database Mail Tables */ /* */ /**************************************************************/ ---------------------------------------------------------------- -- Database Mail: general configuraiton tables ---------------------------------------------------------------- IF (OBJECT_ID(N'dbo.sysmail_profile', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysmail_profile...' CREATE TABLE dbo.sysmail_profile ( profile_id int identity not null, name sysname not null, description nvarchar(256) null, last_mod_datetime datetime not null default getdate(), last_mod_user sysname not null default suser_sname() CONSTRAINT [SYSMAIL_PROFILE_IDMustBeUnique] PRIMARY KEY(profile_id), CONSTRAINT [SYSMAIL_PROFILE_NameMustBeUnique] UNIQUE (name) ) END ELSE BEGIN ALTER TABLE dbo.sysmail_profile ALTER COLUMN description nvarchar(256) null END go IF (OBJECT_ID(N'dbo.sysmail_principalprofile', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysmail_principalprofile...' CREATE TABLE dbo.sysmail_principalprofile ( profile_id int not null references sysmail_profile(profile_id) on delete cascade, principal_sid varbinary(85) not null, -- sys.database_principals.sid is_default bit not null default 0, last_mod_datetime datetime not null default getdate(), last_mod_user sysname not null default suser_sname() CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique] PRIMARY KEY(profile_id,principal_sid), ) END ELSE BEGIN -- add principal_sid column IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='principal_sid' and id = (SELECT OBJECT_ID(N'dbo.sysmail_principalprofile', 'U'))) BEGIN ALTER TABLE dbo.sysmail_principalprofile ADD principal_sid varbinary(85) not null default 0xFFFF END END go IF (OBJECT_ID(N'dbo.sysmail_account', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysmail_account...' CREATE TABLE dbo.sysmail_account ( account_id int identity not null, name sysname not null, description nvarchar(256) null, email_address nvarchar(128) not null, display_name nvarchar(128) null, replyto_address nvarchar(128) null, last_mod_datetime datetime not null default getdate(), last_mod_user sysname not null default suser_sname() CONSTRAINT [SYSMAIL_ACCOUNT_IDMustBeUnique] PRIMARY KEY(account_id), CONSTRAINT [SYSMAIL_ACCOUNT_NameMustBeUnique] UNIQUE (name) ) END ELSE BEGIN ALTER TABLE dbo.sysmail_account ALTER COLUMN description nvarchar(256) null ALTER TABLE dbo.sysmail_account ALTER COLUMN display_name nvarchar(128) null ALTER TABLE dbo.sysmail_account ALTER COLUMN replyto_address nvarchar(128) null END go IF (OBJECT_ID(N'dbo.sysmail_profileaccount', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysmail_profileaccount...' CREATE TABLE dbo.sysmail_profileaccount ( profile_id int not null, account_id int not null references sysmail_account(account_id) on delete cascade, sequence_number int null, last_mod_datetime datetime not null default getdate(), last_mod_user sysname not null default suser_sname() CONSTRAINT [SYSMAIL_ACCOUNT_ProfileAccountMustBeUnique] PRIMARY KEY(profile_id,account_id) ) END ELSE BEGIN ALTER TABLE dbo.sysmail_profileaccount ALTER COLUMN sequence_number int null END go IF (OBJECT_ID(N'dbo.sysmail_servertype', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysmail_servertype...' CREATE TABLE dbo.sysmail_servertype ( servertype sysname not null, is_incoming bit not null default 0, is_outgoing bit not null default 1, last_mod_datetime datetime not null default getdate(), last_mod_user sysname not null default suser_sname() CONSTRAINT [SYSMAIL_SERVERTYPE_TypeMustBeUnique] PRIMARY KEY(servertype), ) END go IF (OBJECT_ID(N'dbo.sysmail_server', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysmail_server...' CREATE TABLE dbo.sysmail_server ( account_id int not null references sysmail_account(account_id) on delete cascade, servertype sysname not null references sysmail_servertype(servertype), servername sysname not null, port int not null default 25, username nvarchar(128) null, credential_id int null, use_default_credentials bit not null default 0, enable_ssl bit not null default 0, flags int not null default 0, timeout int NULL, last_mod_datetime datetime not null default getdate(), last_mod_user sysname not null default suser_sname() CONSTRAINT [SYSMAIL_ACCOUNT_AccountServerTypeMustBeUnique] PRIMARY KEY(account_id,servertype) ) END ELSE -- check if we need to add missing columns BEGIN IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='use_default_credentials' and id = (SELECT OBJECT_ID(N'dbo.sysmail_server', 'U'))) BEGIN ALTER TABLE dbo.sysmail_server ADD use_default_credentials bit not null default 0 END IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='enable_ssl' and id = (SELECT OBJECT_ID(N'dbo.sysmail_server', 'U'))) BEGIN ALTER TABLE dbo.sysmail_server ADD enable_ssl bit not null default 0 END IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='flags' and id = (SELECT OBJECT_ID(N'dbo.sysmail_server', 'U'))) BEGIN ALTER TABLE dbo.sysmail_server ADD flags int not null default 0 END IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='timeout' and id = (SELECT OBJECT_ID(N'dbo.sysmail_server', 'U'))) BEGIN ALTER TABLE dbo.sysmail_server ADD timeout int NULL END END go IF (OBJECT_ID(N'dbo.sysmail_configuration', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysmail_configuration...' CREATE TABLE dbo.sysmail_configuration ( paramname nvarchar(256) not null, paramvalue nvarchar(256) null, description nvarchar(256) null, last_mod_datetime datetime not null default getdate(), last_mod_user sysname not null default suser_sname() CONSTRAINT [SYSMAIL_CONFIGURATION_ParamnameMustBeUnique] PRIMARY KEY(paramname) ) END ELSE BEGIN ALTER TABLE dbo.sysmail_configuration ALTER COLUMN paramvalue nvarchar(256) null ALTER TABLE dbo.sysmail_configuration ALTER COLUMN description nvarchar(256) null END go -- populate default configuration settings DECLARE @description NVARCHAR(256) SELECT @description = FORMATMESSAGE(14642) IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'DefaultAttachmentEncoding') INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'DefaultAttachmentEncoding', N'MIME', @description) ELSE UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'DefaultAttachmentEncoding' -- maximum size of an Database Mail atachement 1MB SELECT @description = FORMATMESSAGE(14644) IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'MaxFileSize') INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'MaxFileSize', N'1000000', @description) ELSE UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'MaxFileSize' SELECT @description = FORMATMESSAGE(14645) IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'ProhibitedExtensions') INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'ProhibitedExtensions', N'exe,dll,vbs,js', @description) ELSE UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'ProhibitedExtensions' SELECT @description = FORMATMESSAGE(14646) IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'AccountRetryAttempts') INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'AccountRetryAttempts', N'1', @description) ELSE UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'AccountRetryAttempts' SELECT @description = FORMATMESSAGE(14647) IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'AccountRetryDelay') INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'AccountRetryDelay', N'60', @description) ELSE UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'AccountRetryDelay' SELECT @description = FORMATMESSAGE(14648) IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'DatabaseMailExeMinimumLifeTime') INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'DatabaseMailExeMinimumLifeTime', N'600', @description) ELSE UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'DatabaseMailExeMinimumLifeTime' -- component logging level: normal - 1, extended - 2 (default), verbose - 3 SELECT @description = FORMATMESSAGE(14664) IF NOT EXISTS(SELECT * FROM dbo.sysmail_configuration WHERE paramname = N'LoggingLevel') INSERT INTO dbo.sysmail_configuration (paramname,paramvalue,description) VALUES (N'LoggingLevel', N'2', @description) ELSE UPDATE dbo.sysmail_configuration SET description = @description WHERE paramname = N'LoggingLevel' go ---------------------------------------------------------------- -- Database Mail: mail host database specific tables ---------------------------------------------------------------- ----------------------------------------------------------- -- TABLE sysmail_mailitems ----------------------------------------------------------- -- sysmail_mailitems : Contains one row for each mail sent using sp_send_dbmail. -- Contains mails that are waiting to be sent and the sent mail. -- sent_status determines its status -- -- mailitem_id : Id for the row. Auto-generated. -- profile_id : ID of profile to use to send the mail. -- recipients : People on the To list. -- copy_recipients : People on the Cc list. -- blind_copy_recipients : People on the Bcc list. -- subject : Subject of the email. -- body : Body of the email. -- body_format : Body format. 0 (Text), 1(Html) -- importance : Importance of the email: -- 0(Low), 1(Normal), 2(High). -- sensitivity : Sensitivity of the email: -- 0(Normal), 1(Personal), 2(Private), 3(Confidential). -- attachment_encoding : Encoding to use for mail and attachments: -- 0(MIME), 1(UUEncode), 2(BINHEX), 3(S/MIME). -- query : SQL query that was executed in this mail -- execute_query_database : The database to execute the query in -- attach_query_result_as_file : Option for attaching the query result -- as a file instead of in the mail body -- query_result_header : Option for including query result column headers -- query_result_width : The query result overall width in characters -- query_result_separator : The query result column separaror character -- exclude_query_output : Option for supressing query output being returned to -- the client that is sending the mail -- append_query_error : Option for appending query error messages to the mail item -- send_request_date : Date this mail item was created -- send_request_user : The user that created this mail item -- sent_account_id : The account_id that was used to send this mail item -- sent_status : The current status of the mail item. -- : 0(PendingSend), 1(SendSuccessful), 2(SendFailed), 3(AttemptingSendRetry) -- sent_date : Date the mail item was sent or failed to be sent -- from_adress : Optional override of the from header. -- reply_to : Optional setting of the reply-to header. ----------------------------------------------------------- IF(OBJECT_ID('dbo.sysmail_mailitems', 'U') IS NULL) BEGIN PRINT 'Creating TABLE sysmail_mailitems' CREATE TABLE sysmail_mailitems ( mailitem_id INT IDENTITY(1,1) NOT NULL, profile_id INT NOT NULL, recipients VARCHAR(MAX) NULL, copy_recipients VARCHAR(MAX) NULL, blind_copy_recipients VARCHAR(MAX) NULL, subject NVARCHAR(255) NULL, from_address VARCHAR(MAX) NULL, reply_to VARCHAR(MAX) NULL, body NVARCHAR(MAX) NULL, body_format VARCHAR(20) NULL, importance VARCHAR(6) NULL, sensitivity VARCHAR(12) NULL, file_attachments NVARCHAR(MAX) NULL, attachment_encoding VARCHAR(20) NULL, query NVARCHAR(MAX) NULL, execute_query_database sysname NULL, attach_query_result_as_file BIT NULL, query_result_header BIT NULL, query_result_width INT NULL, query_result_separator CHAR(1) NULL, exclude_query_output BIT NULL, append_query_error BIT NULL, send_request_date DATETIME NOT NULL DEFAULT GETDATE(), send_request_user sysname NOT NULL DEFAULT SUSER_SNAME(), sent_account_id INT NULL, sent_status TINYINT NULL DEFAULT 0, sent_date DATETIME NULL, last_mod_date DATETIME NOT NULL DEFAULT GETDATE(), last_mod_user sysname NOT NULL DEFAULT SUSER_SNAME(), CONSTRAINT [sysmail_mailitems_id_MustBeUnique] PRIMARY KEY(mailitem_id), CONSTRAINT [sysmail_OutMailMustHaveAtleastOneRecipient] CHECK (NOT (recipients IS NULL AND copy_recipients IS NULL AND blind_copy_recipients IS NULL)), CONSTRAINT [sysmail_OutMailRecipientCannotBeEmpty] CHECK (DATALENGTH(ISNULL(recipients, '')) + DATALENGTH(ISNULL(copy_recipients, '')) + DATALENGTH(ISNULL(blind_copy_recipients, '')) <> 0), CONSTRAINT [sysmail_OutMailAttachmentEncodingMustBeValid] CHECK (attachment_encoding IN ('MIME', 'S/MIME', 'BINHEX', 'UUENCODE')), CONSTRAINT [sysmail_OutMailImportanceMustBeValid] CHECK (importance IN ('LOW', 'NORMAL', 'HIGH')), CONSTRAINT [sysmail_OutMailSensitivityMustBeValid] CHECK (sensitivity IN('NORMAL', 'PERSONAL', 'PRIVATE', 'CONFIDENTIAL')) ) END ELSE BEGIN -- handle schema upgrade for sysmail_mailitems table IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='profile_id' and id = (SELECT OBJECT_ID(N'dbo.sysmail_mailitems', 'U'))) BEGIN ALTER TABLE dbo.sysmail_mailitems ADD profile_id INT NOT NULL DEFAULT -1 END IF NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='from_address' and id = (SELECT OBJECT_ID(N'dbo.sysmail_mailitems', 'U'))) BEGIN ALTER TABLE dbo.sysmail_mailitems ADD from_address VARCHAR(MAX) NULL ALTER TABLE dbo.sysmail_mailitems ADD reply_to VARCHAR(MAX) NULL END END GO /**************************************************************/ /* sysmail_allitems */ /**************************************************************/ PRINT '' PRINT 'Creating view sysmail_allitems...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_allitems') AND (type = 'V'))) DROP VIEW sysmail_allitems go CREATE VIEW sysmail_allitems AS SELECT mailitem_id, profile_id, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_date, send_request_user, sent_account_id, CASE sent_status WHEN 0 THEN 'unsent' WHEN 1 THEN 'sent' WHEN 3 THEN 'retrying' ELSE 'failed' END as sent_status, sent_date, last_mod_date, last_mod_user FROM msdb.dbo.sysmail_mailitems WHERE (send_request_user = SUSER_SNAME()) OR (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) GO /**************************************************************/ /* sysmail_sentitems */ /**************************************************************/ PRINT '' PRINT 'Creating view sysmail_sentitems...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_sentitems') AND (type = 'V'))) DROP VIEW sysmail_sentitems go CREATE VIEW sysmail_sentitems AS SELECT * FROM msdb.dbo.sysmail_allitems WHERE sent_status = 'sent' go /**************************************************************/ /* sysmail_unsentitems */ /**************************************************************/ PRINT '' PRINT 'Creating view sysmail_unsentitems...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_unsentitems') AND (type = 'V'))) DROP VIEW sysmail_unsentitems go CREATE VIEW sysmail_unsentitems AS SELECT * FROM msdb.dbo.sysmail_allitems WHERE (sent_status = 'unsent' OR sent_status = 'retrying') go /**************************************************************/ /* sysmail_faileditems */ /**************************************************************/ PRINT '' PRINT 'Creating view sysmail_faileditems...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_faileditems') AND (type = 'V'))) DROP VIEW sysmail_faileditems go CREATE VIEW sysmail_faileditems AS SELECT * FROM msdb.dbo.sysmail_allitems WHERE sent_status = 'failed' go ----------------------------------------------------------- -- procedure sysmail_delete_mailitems_sp ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sysmail_delete_mailitems_sp', 'P') IS NULL DROP PROCEDURE dbo.sysmail_delete_mailitems_sp GO ----- PRINT 'Creating sysmail_delete_mailitems_sp' ----- GO CREATE PROCEDURE sysmail_delete_mailitems_sp @sent_before DATETIME = NULL, -- sent before @sent_status varchar(8) = NULL -- sent status AS BEGIN SET @sent_status = LTRIM(RTRIM(@sent_status)) IF @sent_status = '' SET @sent_status = NULL IF ( (@sent_status IS NOT NULL) AND (LOWER(@sent_status collate SQL_Latin1_General_CP1_CS_AS) NOT IN ( 'unsent', 'sent', 'failed', 'retrying') ) ) BEGIN RAISERROR(14266, -1, -1, '@sent_status', 'unsent, sent, failed, retrying') RETURN(1) -- Failure END IF ( @sent_before IS NULL AND @sent_status IS NULL ) BEGIN RAISERROR(14608, -1, -1, '@sent_before', '@sent_status') RETURN(1) -- Failure END DELETE FROM msdb.dbo.sysmail_allitems WHERE ((@sent_before IS NULL) OR ( send_request_date < @sent_before)) AND ((@sent_status IS NULL) OR (sent_status = @sent_status)) DECLARE @localmessage nvarchar(255) SET @localmessage = FORMATMESSAGE(14665, SUSER_SNAME(), @@ROWCOUNT) exec msdb.dbo.sysmail_logmailevent_sp @event_type=1, @description=@localmessage END GO ----------------------------------------------------------- -- TABLE sysmail_attachments ----------------------------------------------------------- -- sysmail_attachments : Contains mail item attachments -- -- attachment_id : Id for the row. Auto-generated -- mailitem_id : Optional key to the mail items that this entry is a about -- filename : The filename of the attachment -- filesize : Size of the file -- encoded_attachment : The file data encoded in base64 ---------------------------------------------------------------- IF (OBJECT_ID('dbo.sysmail_attachments', 'U') IS NULL) BEGIN PRINT 'Creating TABLE sysmail_attachments' CREATE TABLE sysmail_attachments ( attachment_id INT IDENTITY(1, 1) NOT NULL, mailitem_id INT NOT NULL CONSTRAINT FK_sysmail_mailitems_mailitem_id FOREIGN KEY (mailitem_id) REFERENCES sysmail_mailitems(mailitem_id) ON DELETE CASCADE, filename NVARCHAR(260) NOT NULL, filesize INT NOT NULL, attachment VARBINARY(MAX) NULL, last_mod_date DATETIME NOT NULL DEFAULT GETDATE(), last_mod_user sysname NOT NULL DEFAULT SUSER_SNAME() ) END ELSE BEGIN BEGIN TRAN -- remove any old constraint declare @fkName sysname DECLARE @sql NVARCHAR(4000) SELECT @fkName = cstr.name FROM sys.tables AS tbl INNER JOIN sys.foreign_keys AS cstr ON cstr.parent_object_id=tbl.object_id LEFT OUTER JOIN sys.indexes AS ki ON ki.index_id = cstr.key_index_id and ki.object_id = cstr.referenced_object_id INNER JOIN sys.tables rtbl ON rtbl.object_id = cstr.referenced_object_id WHERE tbl.name=N'sysmail_attachments' IF (@fkName IS NOT NULL) BEGIN select @sql = N'ALTER TABLE sysmail_attachments DROP CONSTRAINT ' + @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 ' + @fkName EXEC (@sql) end END GO /**************************************************************/ /* sysmail_event_log */ /**************************************************************/ use msdb go PRINT '' PRINT 'Creating view sysmail_event_log...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_event_log') AND (type = 'V'))) DROP VIEW sysmail_event_log go CREATE VIEW sysmail_event_log AS SELECT log_id, CASE event_type WHEN 0 THEN 'success' WHEN 1 THEN 'information' WHEN 2 THEN 'warning' ELSE 'error' END as event_type, log_date, description, process_id, sl.mailitem_id, account_id, sl.last_mod_date, sl.last_mod_user FROM [dbo].[sysmail_log] sl WHERE (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (EXISTS ( SELECT mailitem_id FROM [dbo].[sysmail_allitems] ai WHERE sl.mailitem_id = ai.mailitem_id )) GO ----------------------------------------------------------- -- procedure sysmail_delete_log_sp ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sysmail_delete_log_sp', 'P') IS NULL DROP PROCEDURE dbo.sysmail_delete_log_sp GO ----- PRINT 'Creating sysmail_delete_log_sp' ----- GO CREATE PROCEDURE sysmail_delete_log_sp @logged_before DATETIME = NULL, @event_type varchar(15) = NULL AS BEGIN SET @event_type = LTRIM(RTRIM(@event_type)) IF @event_type = '' SET @event_type = NULL DECLARE @event_type_numeric INT IF ( (@event_type IS NOT NULL) AND (LOWER(@event_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ( 'success', 'warning', 'error', 'information' ) ) ) BEGIN RAISERROR(14266, -1, -1, '@event_type', 'success, warning, error, information') RETURN(1) -- Failure END IF ( @event_type IS NOT NULL) BEGIN SET @event_type_numeric = ( SELECT CASE WHEN @event_type = 'success' THEN 0 WHEN @event_type = 'information' THEN 1 WHEN @event_type = 'warning' THEN 2 ELSE 3 END ) END ELSE SET @event_type_numeric = NULL DELETE FROM msdb.dbo.sysmail_log WHERE ((@logged_before IS NULL) OR ( log_date < @logged_before)) AND ((@event_type_numeric IS NULL) OR (@event_type_numeric = event_type)) END GO ----------------------------------------------------------- -- sysmail_query_transfer : Table used to transfer data between a helper xp's and the calling sp's. -- Rows are created and deleted in the context of each call -- -- uid : guid for the row. Generated by the user -- text_data : Attachment data in binary form ---------------------------------------------------------------- IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_query_transfer') AND (type = 'U'))) BEGIN PRINT 'Creating TABLE sysmail_query_transfer' CREATE TABLE sysmail_query_transfer ( uid uniqueidentifier NOT NULL PRIMARY KEY, text_data NVARCHAR(max) NULL, create_date DATETIME NOT NULL DEFAULT GETDATE() ) END GO ----------------------------------------------------------- -- sysmail_attachments_transfer : Table used to transfer data between a helper xp's -- and the calling sp's. Rows are created and deleted -- in the context of each call -- -- uid : guid for the row. Generated by the user -- filename : Attachment file name -- filesize : Attachment file size in bytes -- attachment : Attachment data in binary form ---------------------------------------------------------------- IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_attachments_transfer') AND (type = 'U'))) BEGIN PRINT 'Creating TABLE sysmail_attachments_transfer' CREATE TABLE sysmail_attachments_transfer ( transfer_id INT IDENTITY(1, 1) NOT NULL PRIMARY KEY, uid uniqueidentifier NOT NULL, filename NVARCHAR(260) NOT NULL, filesize INT NOT NULL, attachment VARBINARY(MAX) NULL, create_date DATETIME NOT NULL DEFAULT GETDATE() ) END GO /*************************************************************************/ /* */ /* Database Mail Triggers */ /* */ /*************************************************************************/ ------------------------------------------------------------ -- Database Mail: triggers on general configuration tables ------------------------------------------------------------ PRINT '' PRINT 'Creating trigger trig_sysmail_profile...' IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_sysmail_profile') AND (type = 'TR'))) DROP TRIGGER dbo.trig_sysmail_profile go CREATE TRIGGER trig_sysmail_profile ON msdb.dbo.sysmail_profile FOR UPDATE AS BEGIN SET NOCOUNT ON IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_profile'), 'AFTER' , 'DML' ) <= 1) BEGIN UPDATE msdb.dbo.sysmail_profile SET last_mod_datetime = getdate(),last_mod_user = suser_sname() FROM sysmail_profile p, inserted i WHERE p.profile_id = i.profile_id END END go PRINT '' PRINT 'Creating trigger trig_principalprofile...' IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_principalprofile') AND (type = 'TR'))) DROP TRIGGER dbo.trig_principalprofile go CREATE TRIGGER trig_principalprofile ON msdb.dbo.sysmail_principalprofile FOR UPDATE AS BEGIN SET NOCOUNT ON IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_principalprofile'), 'AFTER' , 'DML' ) <= 1) BEGIN UPDATE msdb.dbo.sysmail_principalprofile SET last_mod_datetime = getdate(),last_mod_user = suser_sname() FROM sysmail_principalprofile p, inserted i WHERE p.profile_id = i.profile_id and p.principal_sid = i.principal_sid END END go PRINT '' PRINT 'Creating trigger trig_sysmail_account...' IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_sysmail_account') AND (type = 'TR'))) DROP TRIGGER dbo.trig_sysmail_account go CREATE TRIGGER trig_sysmail_account ON msdb.dbo.sysmail_account FOR UPDATE AS BEGIN SET NOCOUNT ON IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_account'), 'AFTER' , 'DML' ) <= 1) BEGIN UPDATE msdb.dbo.sysmail_account SET last_mod_datetime = getdate(),last_mod_user = suser_sname() FROM sysmail_account a, inserted i WHERE a.account_id = i.account_id END END go PRINT '' PRINT 'Creating trigger trig_sysmail_profileaccount...' IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_sysmail_profileaccount') AND (type = 'TR'))) DROP TRIGGER dbo.trig_sysmail_profileaccount go CREATE TRIGGER trig_sysmail_profileaccount ON msdb.dbo.sysmail_profileaccount FOR UPDATE AS BEGIN SET NOCOUNT ON IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_profileaccount'), 'AFTER' , 'DML' ) <= 1) BEGIN UPDATE msdb.dbo.sysmail_profileaccount SET last_mod_datetime = getdate(),last_mod_user = suser_sname() FROM sysmail_profileaccount p, inserted i WHERE p.profile_id = i.profile_id and p.account_id = i.account_id END END go PRINT '' PRINT 'Creating trigger trig_sysmail_profile_delete...' IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_sysmail_profile_delete') AND (type = 'TR'))) DROP TRIGGER dbo.trig_sysmail_profile_delete go CREATE TRIGGER trig_sysmail_profile_delete ON msdb.dbo.sysmail_profile FOR DELETE AS BEGIN DELETE FROM msdb.dbo.sysmail_profileaccount WHERE profile_id IN (SELECT profile_id FROM deleted) END go PRINT '' PRINT 'Creating trigger trig_sysmail_servertype...' IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_sysmail_servertype') AND (type = 'TR'))) DROP TRIGGER dbo.trig_sysmail_servertype go CREATE TRIGGER trig_sysmail_servertype ON msdb.dbo.sysmail_servertype FOR UPDATE AS BEGIN SET NOCOUNT ON IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_servertype'), 'AFTER' , 'DML' ) <= 1) BEGIN UPDATE msdb.dbo.sysmail_servertype SET last_mod_datetime = getdate(),last_mod_user = suser_sname() FROM sysmail_servertype s, inserted i where s.servertype = i.servertype END END go SET NOCOUNT ON IF NOT EXISTS(SELECT * FROM dbo.sysmail_servertype WHERE servertype = N'SMTP') BEGIN INSERT INTO dbo.sysmail_servertype (servertype) VALUES (N'SMTP') END SET NOCOUNT OFF go PRINT '' PRINT 'Creating trigger trig_sysmail_server...' IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_sysmail_server') AND (type = 'TR'))) DROP TRIGGER dbo.trig_sysmail_server go CREATE TRIGGER trig_sysmail_server ON msdb.dbo.sysmail_server FOR UPDATE AS BEGIN SET NOCOUNT ON IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_server'), 'AFTER' , 'DML' ) <= 1) BEGIN UPDATE msdb.dbo.sysmail_server SET last_mod_datetime = getdate(),last_mod_user = suser_sname() FROM sysmail_server s, inserted i WHERE s.account_id = i.account_id and s.servertype = i.servertype END END go PRINT '' PRINT 'Creating trigger trig_sysmail_configuration...' IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_sysmail_configuration') AND (type = 'TR'))) DROP TRIGGER dbo.trig_sysmail_configuration go CREATE TRIGGER trig_sysmail_configuration ON msdb.dbo.sysmail_configuration FOR UPDATE AS BEGIN SET NOCOUNT ON IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_configuration'), 'AFTER' , 'DML' ) <= 1) BEGIN UPDATE msdb.dbo.sysmail_configuration SET last_mod_datetime = getdate(),last_mod_user = suser_sname() FROM sysmail_configuration c, inserted i WHERE c.paramname = i.paramname END END go ------------------------------------------------------------------------- -- Database Mail: triggers on general mail host database specific tables ------------------------------------------------------------------------- IF (OBJECT_ID('dbo.trig_sysmail_mailitems', 'TR') IS NOT NULL) DROP TRIGGER dbo.trig_sysmail_mailitems GO CREATE TRIGGER trig_sysmail_mailitems ON msdb.dbo.sysmail_mailitems FOR UPDATE AS BEGIN SET NOCOUNT ON IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_mailitems'), 'AFTER' , 'DML' ) <= 1) BEGIN UPDATE msdb.dbo.sysmail_mailitems SET last_mod_date = GETDATE(), last_mod_user = SUSER_SNAME() FROM sysmail_mailitems m, inserted i WHERE m.mailitem_id = i.mailitem_id END END GO IF (OBJECT_ID('dbo.trig_sysmail_attachments', 'TR') IS NOT NULL) DROP TRIGGER dbo.trig_sysmail_attachments GO CREATE TRIGGER trig_sysmail_attachments ON msdb.dbo.sysmail_attachments FOR UPDATE AS BEGIN SET NOCOUNT ON IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_attachments'), 'AFTER' , 'DML' ) <= 1) BEGIN UPDATE msdb.dbo.sysmail_attachments SET last_mod_date = GETDATE(), last_mod_user = SUSER_SNAME() FROM sysmail_attachments a, inserted i WHERE a.attachment_id = i.attachment_id END END GO IF (OBJECT_ID('dbo.trig_sysmail_log', 'TR') IS NOT NULL) DROP TRIGGER dbo.trig_sysmail_log GO CREATE TRIGGER trig_sysmail_log ON msdb.dbo.sysmail_log FOR UPDATE AS BEGIN SET NOCOUNT ON IF (TRIGGER_NESTLEVEL( OBJECT_ID('dbo.trig_sysmail_log'), 'AFTER' , 'DML' ) <= 1) BEGIN UPDATE msdb.dbo.sysmail_log SET last_mod_date = GETDATE(), last_mod_user = SUSER_SNAME() FROM sysmail_log l, inserted i WHERE l.log_id = i.log_id END END GO /*********************************************************************************/ /* */ /* Database Mail Utility Functions */ /* */ /*********************************************************************************/ ----------------------------------------------------------- -- ConvertToInt : Converts a string to integer. Returns null -- if the input string is not a valid int. -- ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.ConvertToInt', 'FN') IS NULL DROP FUNCTION dbo.ConvertToInt GO CREATE FUNCTION dbo.ConvertToInt(@string nvarchar(255), @maxValue int, @defValue int) RETURNS int AS BEGIN DECLARE @value bigint SET @value = @defValue SET @string = LTRIM(RTRIM(@string)) -- Check if there is any character other than 0-9 in the string. IF ((@string IS NOT NULL AND @string <> N'') AND (@string NOT LIKE '%[^0-9]%')) BEGIN --INT's have a max of 10 digits IF(LEN(@string) <= 10) BEGIN -- Try converting to bigint. Return default if the value is bigger than @maxValue SET @value = CONVERT(bigint, @string) IF(@value > CONVERT(bigint, @maxValue)) SET @value = @defValue END END RETURN CONVERT(int, @value) END GO /*********************************************************************************/ /* */ /* Database Mail Stored Procedures */ /* */ /*********************************************************************************/ ------------------------------------------------------- -- Database Mail: configuration stored procedures ------------------------------------------------------- PRINT '' PRINT 'Creating procedure sysmail_verify_accountparams_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_verify_accountparams_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_verify_accountparams_sp go CREATE PROCEDURE dbo.sysmail_verify_accountparams_sp @use_default_credentials bit, @mailserver_type sysname OUTPUT, -- @mailserver_type must be provided. Usually SMTP @username nvarchar(128) OUTPUT, -- returns trimmed value, NULL if empty @password nvarchar(128) OUTPUT -- returns trimmed value, NULL if empty AS SET @username = LTRIM(RTRIM(@username)) SET @password = LTRIM(RTRIM(@password)) SET @mailserver_type = LTRIM(RTRIM(@mailserver_type)) IF(@username = N'') SET @username = NULL IF(@password = N'') SET @password = NULL IF(@mailserver_type = N'') SET @mailserver_type = NULL IF(@mailserver_type IS NULL) BEGIN RAISERROR(14614, -1, -1, @mailserver_type) RETURN (1) END -- default credentials should supercede any explicit credentials passed in IF((@use_default_credentials = 1) AND (@username IS NOT NULL)) BEGIN RAISERROR(14666, -1, -1) RETURN (1) END --If a password is specified then @username must be a non empty string IF((@password IS NOT NULL) AND (@username IS NULL)) BEGIN RAISERROR(14615, -1, -1) RETURN (1) END RETURN(0) -- SUCCESS go PRINT '' PRINT 'Creating procedure sysmail_verify_principal_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_verify_principal_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_verify_principal_sp go CREATE PROCEDURE dbo.sysmail_verify_principal_sp @principal_id int, @principal_name sysname, @allow_both_nulls bit, @principal_sid varbinary(85) OUTPUT AS IF @allow_both_nulls = 0 BEGIN -- at least one parameter must be supplied IF (@principal_id IS NULL AND @principal_name IS NULL) BEGIN RAISERROR(14604, -1, -1, 'principal') RETURN(1) END END DECLARE @principalid int IF (@principal_id IS NOT NULL AND @principal_name IS NOT NULL) -- both parameters supplied BEGIN SELECT @principalid=principal_id FROM msdb.sys.database_principals WHERE type in ('U','S','G') AND principal_id = @principal_id AND name = @principal_name IF (@principalid IS NULL) BEGIN RAISERROR(14605, -1, -1, 'principal') RETURN(2) END END ELSE IF (@principal_id IS NOT NULL) -- use id BEGIN SELECT @principalid=principal_id FROM msdb.sys.database_principals WHERE type in ('U','S','G') AND principal_id = @principal_id IF (@principalid IS NULL) BEGIN RAISERROR(14606, -1, -1, 'principal') RETURN(3) END END ELSE IF (@principal_name IS NOT NULL) -- use name BEGIN SELECT @principalid=principal_id FROM msdb.sys.database_principals WHERE type in ('U','S','G') AND name = @principal_name IF (@principalid IS NULL) BEGIN RAISERROR(14607, -1, -1, 'principal') RETURN(4) END END -- populate return variable SELECT @principal_sid = dbo.get_principal_sid(@principalid) RETURN(0) -- SUCCESS go PRINT '' PRINT 'Creating procedure sysmail_verify_profile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_verify_profile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_verify_profile_sp go CREATE PROCEDURE dbo.sysmail_verify_profile_sp @profile_id int, @profile_name sysname, @allow_both_nulls bit, @allow_id_name_mismatch bit, @profileid int OUTPUT AS IF @allow_both_nulls = 0 BEGIN -- at least one parameter must be supplied IF (@profile_id IS NULL AND @profile_name IS NULL) BEGIN RAISERROR(14604, -1, -1, 'profile') RETURN(1) END END IF ((@allow_id_name_mismatch = 0) AND (@profile_id IS NOT NULL AND @profile_name IS NOT NULL)) -- use both parameters BEGIN SELECT @profileid = profile_id FROM msdb.dbo.sysmail_profile WHERE profile_id=@profile_id AND name=@profile_name IF (@profileid IS NULL) -- id and name do not match BEGIN RAISERROR(14605, -1, -1, 'profile') RETURN(2) END END ELSE IF (@profile_id IS NOT NULL) -- use id BEGIN SELECT @profileid = profile_id FROM msdb.dbo.sysmail_profile WHERE profile_id=@profile_id IF (@profileid IS NULL) -- id is invalid BEGIN RAISERROR(14606, -1, -1, 'profile') RETURN(3) END END ELSE IF (@profile_name IS NOT NULL) -- use name BEGIN SELECT @profileid = profile_id FROM msdb.dbo.sysmail_profile WHERE name=@profile_name IF (@profileid IS NULL) -- name is invalid BEGIN RAISERROR(14607, -1, -1, 'profile') RETURN(4) END END RETURN(0) -- SUCCESS go PRINT '' PRINT 'Creating procedure sysmail_verify_account_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_verify_account_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_verify_account_sp go CREATE PROCEDURE dbo.sysmail_verify_account_sp @account_id int, @account_name sysname, @allow_both_nulls bit, @allow_id_name_mismatch bit, @accountid int OUTPUT AS IF @allow_both_nulls = 0 BEGIN -- at least one parameter must be supplied IF (@account_id IS NULL AND @account_name IS NULL) BEGIN RAISERROR(14604, -1, -1, 'account') RETURN(1) END END IF ((@allow_id_name_mismatch = 0) AND (@account_id IS NOT NULL AND @account_name IS NOT NULL)) -- use both parameters BEGIN SELECT @accountid = account_id FROM msdb.dbo.sysmail_account WHERE account_id=@account_id AND name=@account_name IF (@accountid IS NULL) -- id and name do not match BEGIN RAISERROR(14605, -1, -1, 'account') RETURN(2) END END ELSE IF (@account_id IS NOT NULL) -- use id BEGIN SELECT @accountid = account_id FROM msdb.dbo.sysmail_account WHERE account_id=@account_id IF (@accountid IS NULL) -- id is invalid BEGIN RAISERROR(14606, -1, -1, 'account') RETURN(3) END END ELSE IF (@account_name IS NOT NULL) -- use name BEGIN SELECT @accountid = account_id FROM msdb.dbo.sysmail_account WHERE name=@account_name IF (@accountid IS NULL) -- name is invalid BEGIN RAISERROR(14607, -1, -1, 'account') RETURN(4) END END RETURN(0) -- SUCCESS go PRINT '' PRINT 'Creating procedure sysmail_add_profile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_add_profile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_add_profile_sp go CREATE PROCEDURE dbo.sysmail_add_profile_sp @profile_name sysname, @description nvarchar(256) = NULL, @profile_id int = NULL OUTPUT AS SET NOCOUNT ON -- insert new profile record, rely on primary key constraint to error out INSERT INTO msdb.dbo.sysmail_profile (name,description) VALUES (@profile_name, @description) -- fetch back profile_id SELECT @profile_id = profile_id FROM msdb.dbo.sysmail_profile WHERE name = @profile_name RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_update_profile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_update_profile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_update_profile_sp go CREATE PROCEDURE dbo.sysmail_update_profile_sp @profile_id int = NULL, -- must provide either id or name @profile_name sysname = NULL, @description nvarchar(256) = NULL AS SET NOCOUNT ON DECLARE @rc int DECLARE @profileid int exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 0, 1, @profileid OUTPUT IF @rc <> 0 RETURN(1) IF (@profile_name IS NOT NULL AND @description IS NOT NULL) UPDATE msdb.dbo.sysmail_profile SET name=@profile_name, description = @description WHERE profile_id = @profileid ELSE IF (@profile_name IS NOT NULL) UPDATE msdb.dbo.sysmail_profile SET name=@profile_name WHERE profile_id = @profileid ELSE IF (@description IS NOT NULL) UPDATE msdb.dbo.sysmail_profile SET description = @description WHERE profile_id = @profileid ELSE BEGIN RAISERROR(14610, -1, -1) RETURN(1) END RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_delete_profile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_delete_profile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_delete_profile_sp go USE [msdb] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO CREATE PROCEDURE [dbo].[sysmail_delete_profile_sp] @profile_id int = NULL, -- must provide either id or name @profile_name sysname = NULL, @force_delete BIT = 1 AS SET NOCOUNT ON DECLARE @rc int DECLARE @profileid int exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 0, 0, @profileid OUTPUT IF @rc <> 0 RETURN(1) IF(EXISTS (select * from sysmail_unsentitems WHERE sysmail_unsentitems.profile_id = @profileid) AND @force_delete <> 1) BEGIN IF(@profile_name IS NULL) BEGIN select @profile_name = name from dbo.sysmail_profile WHERE profile_id = @profileid END RAISERROR(14668, -1, -1, @profile_name) RETURN (1) END UPDATE [msdb].[dbo].[sysmail_mailitems] SET [sent_status] = 2, [sent_date] = getdate() WHERE profile_id = @profileid AND sent_status <> 1 DELETE FROM msdb.dbo.sysmail_profile WHERE profile_id = @profileid RETURN(0) GO PRINT '' PRINT 'Creating procedure sysmail_help_profile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_help_profile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_help_profile_sp go CREATE PROCEDURE dbo.sysmail_help_profile_sp @profile_id int = NULL, @profile_name sysname = NULL AS SET NOCOUNT ON DECLARE @rc int DECLARE @profileid int exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 1, 0, @profileid OUTPUT IF @rc <> 0 RETURN(1) IF (@profileid IS NOT NULL) SELECT profile_id, name, description FROM msdb.dbo.sysmail_profile WHERE profile_id = @profileid ELSE -- don't filter the output SELECT profile_id, name, description FROM msdb.dbo.sysmail_profile RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_create_user_credential_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_create_user_credential_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_create_user_credential_sp go CREATE PROCEDURE dbo.sysmail_create_user_credential_sp @username nvarchar(128), @password nvarchar(128), @credential_id int OUTPUT AS SET NOCOUNT ON DECLARE @rc int DECLARE @credential_name UNIQUEIDENTIFIER DECLARE @credential_name_as_str varchar(40) DECLARE @sql NVARCHAR(max) -- create a GUID as the name for the credential SET @credential_name = newid() SET @credential_name_as_str = convert(varchar(40), @credential_name) SET @sql = N'CREATE CREDENTIAL [' + @credential_name_as_str + N'] WITH IDENTITY = ' + QUOTENAME(@username, '''') + N', SECRET = ' + QUOTENAME(ISNULL(@password, N''), '''') EXEC @rc = sp_executesql @statement = @sql IF(@rc <> 0) RETURN @rc SELECT @credential_id = credential_id FROM sys.credentials WHERE name = convert(sysname, @credential_name) IF(@credential_id IS NULL) BEGIN RAISERROR(14616, -1, -1, @credential_name_as_str) RETURN 1 END RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_alter_user_credential_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_alter_user_credential_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_alter_user_credential_sp go CREATE PROCEDURE dbo.sysmail_alter_user_credential_sp @credential_name sysname, @username nvarchar(128), @password nvarchar(128) AS SET NOCOUNT ON DECLARE @rc int DECLARE @sql NVARCHAR(max) -- alter credential DDL SET @sql = N'ALTER CREDENTIAL ' + QUOTENAME(@credential_name) + N' WITH IDENTITY = ' + QUOTENAME(@username, '''') + N', SECRET = ' + QUOTENAME(ISNULL(@password, N''), '''') EXEC @rc = sp_executesql @statement = @sql IF(@rc <> 0) RETURN @rc RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_drop_user_credential_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_drop_user_credential_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_drop_user_credential_sp go CREATE PROCEDURE dbo.sysmail_drop_user_credential_sp @credential_name sysname AS SET NOCOUNT ON DECLARE @rc int DECLARE @sql NVARCHAR(max) -- Drop credential DDL SET @sql = N'DROP CREDENTIAL ' + QUOTENAME(@credential_name) EXEC @rc = sp_executesql @statement = @sql IF(@rc <> 0) RETURN @rc RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_add_account_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_add_account_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_add_account_sp go CREATE PROCEDURE dbo.sysmail_add_account_sp @account_name sysname, @email_address nvarchar(128), @display_name nvarchar(128) = NULL, @replyto_address nvarchar(128) = NULL, @description nvarchar(256) = NULL, @mailserver_name sysname = NULL, -- the following fields are part of server definition @mailserver_type sysname = N'SMTP', @port int = 25, @username nvarchar(128) = NULL, @password nvarchar(128) = NULL, @use_default_credentials bit = 0, @enable_ssl bit = 0, @account_id int = NULL OUTPUT AS SET NOCOUNT ON DECLARE @rc int DECLARE @credential_id int EXEC @rc = msdb.dbo.sysmail_verify_accountparams_sp @use_default_credentials = @use_default_credentials, @mailserver_type = @mailserver_type OUTPUT, -- validates and returns trimmed value @username = @username OUTPUT, -- returns trimmed value, NULL if empty @password = @password OUTPUT -- returns trimmed value, NULL if empty IF(@rc <> 0) RETURN (1) --transact this in case sysmail_create_user_credential_sp fails BEGIN TRANSACTION -- insert new account record, rely on primary key constraint to error out INSERT INTO msdb.dbo.sysmail_account (name,description,email_address,display_name,replyto_address) VALUES (@account_name,@description,@email_address,@display_name,@replyto_address) IF (@@ERROR <> 0) BEGIN ROLLBACK TRANSACTION RETURN (2) END -- fetch back account_id SELECT @account_id = account_id FROM msdb.dbo.sysmail_account WHERE name = @account_name IF (@mailserver_name IS NULL) -- use local server as default SELECT @mailserver_name=@@SERVERNAME --create a credential in the credential store if a password needs to be stored IF(@username IS NOT NULL) BEGIN EXEC @rc = msdb.dbo.sysmail_create_user_credential_sp @username, @password, @credential_id OUTPUT IF(@rc <> 0) BEGIN ROLLBACK TRANSACTION RETURN (3) END END INSERT INTO msdb.dbo.sysmail_server (account_id,servertype,servername,port,username,credential_id,use_default_credentials,enable_ssl) VALUES (@account_id,@mailserver_type,@mailserver_name,@port,@username,@credential_id,@use_default_credentials,@enable_ssl) IF (@@ERROR <> 0) BEGIN ROLLBACK TRANSACTION RETURN (4) END COMMIT TRANSACTION RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_update_account_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_update_account_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_update_account_sp go USE [msdb] GO /****** Object: StoredProcedure [dbo].[sysmail_update_account_sp] Script Date: 06/26/2006 10:45:40 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO CREATE PROCEDURE [dbo].[sysmail_update_account_sp] @account_id int = NULL, -- must provide either id or name @account_name sysname = NULL, @email_address nvarchar(128) = NULL, @display_name nvarchar(128) = NULL, @replyto_address nvarchar(128) = NULL, @description nvarchar(256) = NULL, @mailserver_name sysname = NULL, @mailserver_type sysname = NULL, @port int = NULL, @username sysname = NULL, @password sysname = NULL, @use_default_credentials bit = NULL, @enable_ssl bit = NULL, @timeout int = NULL, @no_credential_change bit = NULL -- BOOL -- WITH EXECUTE AS OWNER --Allows access to sys.credentials AS SET NOCOUNT ON DECLARE @rc int DECLARE @accountid int DECLARE @credential_id int DECLARE @credential_name sysname SELECT @no_credential_change = ISNULL(@no_credential_change, 0) exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 0, 1, @accountid OUTPUT IF @rc <> 0 RETURN(1) IF(@email_address IS NULL) BEGIN SELECT @email_address = email_address FROM msdb.dbo.sysmail_account WHERE account_id=@accountid END IF(@display_name IS NULL) BEGIN SELECT @display_name = display_name FROM msdb.dbo.sysmail_account WHERE account_id=@accountid END IF(@replyto_address IS NULL) BEGIN SELECT @replyto_address = replyto_address FROM msdb.dbo.sysmail_account WHERE account_id=@accountid END IF(@description IS NULL) BEGIN SELECT @description = description FROM msdb.dbo.sysmail_account WHERE account_id=@accountid END IF(@port IS NULL) BEGIN SELECT @port = port FROM msdb.dbo.sysmail_server WHERE account_id=@accountid END IF(@use_default_credentials IS NULL) BEGIN SELECT @use_default_credentials = use_default_credentials FROM msdb.dbo.sysmail_server WHERE account_id=@accountid END IF(@enable_ssl IS NULL) BEGIN SELECT @enable_ssl = enable_ssl FROM msdb.dbo.sysmail_server WHERE account_id=@accountid END IF(@timeout IS NULL) BEGIN SELECT @timeout = timeout FROM msdb.dbo.sysmail_server WHERE account_id=@accountid END IF(@mailserver_type IS NULL) BEGIN SELECT @mailserver_type = servertype FROM msdb.dbo.sysmail_server WHERE account_id=@accountid END EXEC @rc = msdb.dbo.sysmail_verify_accountparams_sp @use_default_credentials = @use_default_credentials, @mailserver_type = @mailserver_type OUTPUT, -- validates and returns trimmed value @username = @username OUTPUT, -- returns trimmed value @password = @password OUTPUT -- returns empty string if @username is given and @password is null IF(@rc <> 0) RETURN (1) --transact this in case credential updates fail BEGIN TRAN -- update account table IF (@account_name IS NOT NULL) IF (@email_address IS NOT NULL) UPDATE msdb.dbo.sysmail_account SET name=@account_name, description=@description, email_address=@email_address, display_name=@display_name, replyto_address=@replyto_address WHERE account_id=@accountid ELSE UPDATE msdb.dbo.sysmail_account SET name=@account_name, description=@description, display_name=@display_name, replyto_address=@replyto_address WHERE account_id=@accountid ELSE IF (@email_address IS NOT NULL) UPDATE msdb.dbo.sysmail_account SET description=@description, email_address=@email_address, display_name=@display_name, replyto_address=@replyto_address WHERE account_id=@accountid ELSE UPDATE msdb.dbo.sysmail_account SET description=@description, display_name=@display_name, replyto_address=@replyto_address WHERE account_id=@accountid -- see if a credential has been stored for this account SELECT @credential_name = name, @credential_id = c.credential_id FROM sys.credentials as c JOIN msdb.dbo.sysmail_server as ms ON c.credential_id = ms.credential_id WHERE account_id = @accountid AND servertype = @mailserver_type --update the credential store IF((@credential_name IS NOT NULL) AND (@no_credential_change = 0)) BEGIN --Remove the unneed credential IF(@username IS NULL) BEGIN SET @credential_id = NULL EXEC @rc = msdb.dbo.sysmail_drop_user_credential_sp @credential_name = @credential_name END -- Update the credential ELSE BEGIN EXEC @rc = msdb.dbo.sysmail_alter_user_credential_sp @credential_name = @credential_name, @username = @username, @password = @password END IF(@rc <> 0) BEGIN ROLLBACK TRAN RETURN (1) END END -- create a new credential if one doesn't exist ELSE IF(@credential_name IS NULL AND @username IS NOT NULL) BEGIN EXEC @rc = msdb.dbo.sysmail_create_user_credential_sp @username = @username, @password = @password, @credential_id = @credential_id OUTPUT IF(@rc <> 0) BEGIN ROLLBACK TRAN RETURN (1) END END -- update server table IF (@no_credential_change = 0) BEGIN IF (@mailserver_name IS NOT NULL) UPDATE msdb.dbo.sysmail_server SET servername=@mailserver_name, port=@port, username=@username, credential_id = @credential_id, use_default_credentials = @use_default_credentials, enable_ssl = @enable_ssl, timeout = @timeout WHERE account_id=@accountid AND servertype=@mailserver_type ELSE UPDATE msdb.dbo.sysmail_server SET port=@port, username=@username, credential_id = @credential_id, use_default_credentials = @use_default_credentials, enable_ssl = @enable_ssl, timeout = @timeout WHERE account_id=@accountid AND servertype=@mailserver_type END ELSE BEGIN -- Since no_credential_change is true, @username is empty. Do not pass it. -- If we gave @username, sysmail_server would be set with the empty @username. IF (@mailserver_name IS NOT NULL) UPDATE msdb.dbo.sysmail_server SET servername=@mailserver_name, port=@port, credential_id = @credential_id, use_default_credentials = @use_default_credentials, enable_ssl = @enable_ssl, timeout = @timeout WHERE account_id=@accountid AND servertype=@mailserver_type ELSE UPDATE msdb.dbo.sysmail_server SET port=@port, credential_id = @credential_id, use_default_credentials = @use_default_credentials, enable_ssl = @enable_ssl, timeout = @timeout WHERE account_id=@accountid AND servertype=@mailserver_type END COMMIT TRAN RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_delete_account_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_delete_account_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_delete_account_sp go CREATE PROCEDURE dbo.sysmail_delete_account_sp @account_id int = NULL, -- must provide either id or name @account_name sysname = NULL AS SET NOCOUNT ON DECLARE @rc int DECLARE @accountid int DECLARE @credential_name sysname exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 0, 0, @accountid OUTPUT IF @rc <> 0 RETURN(1) -- Get all the credentials has been stored for this account DECLARE cur CURSOR FOR SELECT c.name FROM sys.credentials as c JOIN msdb.dbo.sysmail_server as ms ON c.credential_id = ms.credential_id WHERE account_id = @accountid OPEN cur FETCH NEXT FROM cur INTO @credential_name WHILE @@FETCH_STATUS = 0 BEGIN -- drop the credential EXEC msdb.dbo.sysmail_drop_user_credential_sp @credential_name = @credential_name FETCH NEXT FROM cur INTO @credential_name END CLOSE cur DEALLOCATE cur DELETE FROM msdb.dbo.sysmail_account WHERE account_id = @accountid RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_help_account_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_help_account_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_help_account_sp go CREATE PROCEDURE dbo.sysmail_help_account_sp @account_id int = NULL, @account_name sysname = NULL AS SET NOCOUNT ON DECLARE @rc int DECLARE @accountid int exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 1, 0, @accountid OUTPUT IF @rc <> 0 RETURN(1) IF (@accountid IS NOT NULL) SELECT a.account_id, a.name, a.description, a.email_address, a.display_name, a.replyto_address, s.servertype, s.servername, s.port, s.username, s.use_default_credentials, s.enable_ssl FROM msdb.dbo.sysmail_account a, msdb.dbo.sysmail_server s WHERE a.account_id = s.account_id AND a.account_id = @accountid ELSE SELECT a.account_id, a.name, a.description, a.email_address, a.display_name, a.replyto_address, s.servertype, s.servername, s.port, s.username, s.use_default_credentials, s.enable_ssl FROM msdb.dbo.sysmail_account a, msdb.dbo.sysmail_server s WHERE a.account_id = s.account_id RETURN(0) go -- Access to the complete account info. Required by databasemail.exe PRINT '' PRINT 'Creating procedure sysmail_help_admin_account_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_help_admin_account_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_help_admin_account_sp go CREATE PROCEDURE dbo.sysmail_help_admin_account_sp @account_id int AS SET NOCOUNT ON DECLARE @rc int, @acc_id int, @name sysname, @description nvarchar(256), @email_address nvarchar(128), @display_name nvarchar(128), @replyto_address nvarchar(128), @servertype sysname, @servername sysname, @port int, @username nvarchar(128), @passwordsize int, @cryptpassword varbinary(1024), @credential_id int, @use_default_credentials bit, @enable_ssl bit, @timeout int SET @passwordsize = 0 EXEC @rc = msdb.dbo.sysmail_verify_account_sp @account_id, NULL, 1, 0, NULL IF @rc <> 0 RETURN(1) SELECT @acc_id = a.account_id, @name = a.name, @description = a.description, @email_address = a.email_address, @display_name = a.display_name, @replyto_address= a.replyto_address, @servertype = s.servertype, @servername = s.servername, @port = s.port, @username = s.username, @credential_id = s.credential_id, @use_default_credentials = s.use_default_credentials, @enable_ssl = s.enable_ssl, @timeout = s.timeout FROM msdb.dbo.sysmail_account a, msdb.dbo.sysmail_server s WHERE (a.account_id = s.account_id) AND (a.account_id = @account_id) --get the encrypted password if required IF(@username IS NOT NULL) BEGIN DECLARE @cred TABLE([size] INT, blob VARBINARY(1024)); INSERT @cred EXEC @rc = master.dbo.sp_PostAgentInfo @credential_id IF @rc <> 0 BEGIN RETURN(1) END SELECT @passwordsize = [size], @cryptpassword = [blob] FROM @cred END --All done return result SELECT @acc_id as 'account_id', @name as 'name', @description as 'description', @email_address as 'email_address', @display_name as 'display_name', @replyto_address as 'replyto_address', @servertype as 'servertype', @servername as 'servername', @port as 'port', @username as 'username', @passwordsize as 'password_size', @cryptpassword as 'password_crypt', @use_default_credentials as 'use_default_credentials', @enable_ssl as 'enable_ssl', @timeout as 'timeout' RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_add_profileaccount_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_add_profileaccount_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_add_profileaccount_sp go CREATE PROCEDURE dbo.sysmail_add_profileaccount_sp @profile_id int = NULL, -- must provide id or name @profile_name sysname = NULL, @account_id int = NULL, -- must provide id or name @account_name sysname = NULL, @sequence_number int -- account with the lowest sequence number is picked as default AS SET NOCOUNT ON DECLARE @rc int DECLARE @profileid int DECLARE @accountid int exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 0, 0, @profileid OUTPUT IF @rc <> 0 RETURN(1) exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 0, 0, @accountid OUTPUT IF @rc <> 0 RETURN(2) -- insert new account record, rely on primary key constraint to error out INSERT INTO msdb.dbo.sysmail_profileaccount (profile_id,account_id,sequence_number) VALUES (@profileid,@accountid,@sequence_number) RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_update_profileaccount_sp...' IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_update_profileaccount_sp') AND (type = 'P'))) DROP PROCEDURE dbo.sysmail_update_profileaccount_sp go CREATE PROCEDURE dbo.sysmail_update_profileaccount_sp @profile_id int = NULL, -- must provide id or name @profile_name sysname = NULL, @account_id int = NULL, -- must provide id or name @account_name sysname = NULL, @sequence_number int -- account with the lowest sequence number is picked as default AS SET NOCOUNT ON DECLARE @rc int DECLARE @profileid int DECLARE @accountid int exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 0, 0, @profileid OUTPUT IF @rc <> 0 RETURN(1) exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 0, 0, @accountid OUTPUT IF @rc <> 0 RETURN(2) IF (@sequence_number IS NULL) BEGIN RAISERROR(14611, -1, -1) RETURN(3) END UPDATE msdb.dbo.sysmail_profileaccount SET sequence_number=@sequence_number WHERE profile_id=@profileid AND account_id=@accountid RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_delete_profileaccount_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_delete_profileaccount_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_delete_profileaccount_sp go CREATE PROCEDURE dbo.sysmail_delete_profileaccount_sp @profile_id int = NULL, -- must provide id or name @profile_name sysname = NULL, @account_id int = NULL, -- must provide id or name @account_name sysname = NULL AS SET NOCOUNT ON DECLARE @rc int DECLARE @profileid int DECLARE @accountid int exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 1, 0, @profileid OUTPUT IF @rc <> 0 RETURN(1) exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 1, 0, @accountid OUTPUT IF @rc <> 0 RETURN(2) IF (@profileid IS NOT NULL AND @accountid IS NOT NULL) -- both parameters supplied for deletion DELETE FROM msdb.dbo.sysmail_profileaccount WHERE profile_id=@profileid AND account_id=@accountid ELSE IF (@profileid IS NOT NULL) -- profile id is supplied DELETE FROM msdb.dbo.sysmail_profileaccount WHERE profile_id=@profileid ELSE IF (@accountid IS NOT NULL) -- account id is supplied DELETE FROM msdb.dbo.sysmail_profileaccount WHERE account_id=@accountid ELSE -- no parameters are supplied for deletion BEGIN RAISERROR(14608, -1, -1, 'profile', 'account') RETURN(3) END RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_help_profileaccount_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_help_profileaccount_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_help_profileaccount_sp go CREATE PROCEDURE dbo.sysmail_help_profileaccount_sp @profile_id int = NULL, -- must provide id or name @profile_name sysname = NULL, @account_id int = NULL, -- must provide id or name @account_name sysname = NULL AS SET NOCOUNT ON DECLARE @rc int DECLARE @profileid int DECLARE @accountid int exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 1, 0, @profileid OUTPUT IF @rc <> 0 RETURN(1) exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 1, 0, @accountid OUTPUT IF @rc <> 0 RETURN(2) IF (@profileid IS NOT NULL AND @accountid IS NOT NULL) SELECT p.profile_id,profile_name=p.name,a.account_id,account_name=a.name,c.sequence_number FROM msdb.dbo.sysmail_profile p, msdb.dbo.sysmail_account a, msdb.dbo.sysmail_profileaccount c WHERE p.profile_id=c.profile_id AND a.account_id=c.account_id AND c.profile_id=@profileid AND c.account_id=@accountid ELSE IF (@profileid IS NOT NULL) SELECT p.profile_id,profile_name=p.name,a.account_id,account_name=a.name,c.sequence_number FROM msdb.dbo.sysmail_profile p, msdb.dbo.sysmail_account a, msdb.dbo.sysmail_profileaccount c WHERE p.profile_id=c.profile_id AND a.account_id=c.account_id AND c.profile_id=@profileid ELSE IF (@accountid IS NOT NULL) SELECT p.profile_id,profile_name=p.name,a.account_id,account_name=a.name,c.sequence_number FROM msdb.dbo.sysmail_profile p, msdb.dbo.sysmail_account a, msdb.dbo.sysmail_profileaccount c WHERE p.profile_id=c.profile_id AND a.account_id=c.account_id AND c.account_id=@accountid ELSE SELECT p.profile_id,profile_name=p.name,a.account_id,account_name=a.name,c.sequence_number FROM msdb.dbo.sysmail_profile p, msdb.dbo.sysmail_account a, msdb.dbo.sysmail_profileaccount c WHERE p.profile_id=c.profile_id AND a.account_id=c.account_id RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_configure_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_configure_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_configure_sp go CREATE PROCEDURE dbo.sysmail_configure_sp @parameter_name nvarchar(256), @parameter_value nvarchar(256), @description nvarchar(256) = NULL AS SET NOCOUNT ON IF (@description IS NOT NULL) UPDATE msdb.dbo.sysmail_configuration SET paramvalue=@parameter_value, description=@description WHERE paramname=@parameter_name ELSE UPDATE msdb.dbo.sysmail_configuration SET paramvalue=@parameter_value WHERE paramname=@parameter_name RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_help_configure_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_help_configure_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_help_configure_sp go CREATE PROCEDURE dbo.sysmail_help_configure_sp @parameter_name nvarchar(256) = NULL AS SET NOCOUNT ON SELECT paramname, paramvalue, description FROM msdb.dbo.sysmail_configuration WHERE paramname = ISNULL(@parameter_name, paramname) RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_help_configure_value_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_help_configure_value_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_help_configure_value_sp go CREATE PROCEDURE dbo.sysmail_help_configure_value_sp @parameter_name nvarchar(256), @parameter_value nvarchar(256) OUTPUT AS SET NOCOUNT ON SET @parameter_value = NULL IF (@parameter_name IS NULL) BEGIN RAISERROR(14618, 16, 1, '@parameter_name') RETURN(1) END SELECT @parameter_value = paramvalue FROM msdb.dbo.sysmail_configuration WHERE paramname = @parameter_name RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_add_principalprofile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_add_principalprofile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_add_principalprofile_sp go CREATE PROCEDURE dbo.sysmail_add_principalprofile_sp @principal_id int = NULL, -- must provide id or name @principal_name sysname = NULL, @profile_id int = NULL, -- must provide id or name @profile_name sysname = NULL, @is_default bit AS SET NOCOUNT ON DECLARE @rc int DECLARE @principal_sid varbinary(85) DECLARE @profileid int IF (@principal_id IS NOT NULL AND @principal_id = 0) OR (@principal_name IS NOT NULL AND @principal_name = N'public') BEGIN IF (@principal_id IS NOT NULL AND @principal_id <> 0) OR (@principal_name IS NOT NULL AND @principal_name <> N'public') BEGIN RAISERROR(14605, -1, -1, 'principal') -- id and name do not match END SET @principal_sid = 0x00 -- public END ELSE BEGIN exec @rc = msdb.dbo.sysmail_verify_principal_sp @principal_id, @principal_name, 0, @principal_sid OUTPUT IF @rc <> 0 RETURN(2) END exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 0, 0, @profileid OUTPUT IF @rc <> 0 RETURN(3) -- insert new account record, rely on primary key constraint to error out INSERT INTO msdb.dbo.sysmail_principalprofile (principal_sid,profile_id,is_default) VALUES (@principal_sid,@profileid,@is_default) IF (@is_default IS NOT NULL AND @is_default = 1 ) BEGIN -- a principal can only have one default profile so reset other, if there are any UPDATE msdb.dbo.sysmail_principalprofile SET is_default=0 WHERE principal_sid = @principal_sid AND profile_id <> @profileid END RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_update_principalprofile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_update_principalprofile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_update_principalprofile_sp go CREATE PROCEDURE dbo.sysmail_update_principalprofile_sp @principal_id int = NULL, -- must provide id or name @principal_name sysname = NULL, @profile_id int = NULL, -- must provide id or name @profile_name sysname = NULL, @is_default bit AS SET NOCOUNT ON DECLARE @rc int DECLARE @principal_sid varbinary(85) DECLARE @profileid int IF (@principal_id IS NOT NULL AND @principal_id = 0) OR (@principal_name IS NOT NULL AND @principal_name = N'public') BEGIN IF (@principal_id IS NOT NULL AND @principal_id <> 0) OR (@principal_name IS NOT NULL AND @principal_name <> N'public') BEGIN RAISERROR(14605, -1, -1, 'principal') -- id and name do not match END SET @principal_sid = 0x00 -- public END ELSE BEGIN exec @rc = msdb.dbo.sysmail_verify_principal_sp @principal_id, @principal_name, 0, @principal_sid OUTPUT IF @rc <> 0 RETURN(1) END exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 0, 0, @profileid OUTPUT IF @rc <> 0 RETURN(2) UPDATE msdb.dbo.sysmail_principalprofile SET is_default=@is_default WHERE principal_sid = @principal_sid AND profile_id = @profileid IF (@is_default IS NOT NULL AND @is_default = 1) BEGIN -- a principal can only have one default profile so reset others (if there are any) UPDATE msdb.dbo.sysmail_principalprofile SET is_default=0 WHERE principal_sid = @principal_sid AND profile_id <> @profileid END RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_delete_principalprofile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_delete_principalprofile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_delete_principalprofile_sp go CREATE PROCEDURE dbo.sysmail_delete_principalprofile_sp @principal_id int = NULL, -- must provide id or name @principal_name sysname = NULL, @profile_id int = NULL, -- must provide id or name @profile_name sysname = NULL AS SET NOCOUNT ON DECLARE @rc int DECLARE @principal_sid varbinary(85) DECLARE @profileid int IF (@principal_id IS NOT NULL AND @principal_id = 0) OR (@principal_name IS NOT NULL AND @principal_name = N'public') BEGIN IF (@principal_id IS NOT NULL AND @principal_id <> 0) OR (@principal_name IS NOT NULL AND @principal_name <> N'public') BEGIN RAISERROR(14605, -1, -1, 'principal') -- id and name do not match END SET @principal_sid = 0x00 -- public END ELSE BEGIN IF (@principal_id IS NOT NULL OR @principal_name IS NOT NULL) BEGIN exec @rc = msdb.dbo.sysmail_verify_principal_sp @principal_id, @principal_name, 1, @principal_sid OUTPUT IF @rc <> 0 RETURN(1) END END exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 1, 0, @profileid OUTPUT IF @rc <> 0 RETURN(2) IF ((@principal_id IS NOT NULL OR @principal_name IS NOT NULL) AND @profileid IS NOT NULL) BEGIN DELETE FROM msdb.dbo.sysmail_principalprofile WHERE principal_sid=@principal_sid AND profile_id = @profileid END ELSE IF (@principal_id IS NOT NULL OR @principal_name IS NOT NULL) BEGIN DELETE FROM msdb.dbo.sysmail_principalprofile WHERE principal_sid=@principal_sid END ELSE IF (@profileid IS NOT NULL) BEGIN DELETE FROM msdb.dbo.sysmail_principalprofile WHERE profile_id = @profileid END ELSE -- no parameters are supplied for deletion BEGIN RAISERROR(14608, -1, -1, 'principal', 'profile') RETURN(6) END RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_help_principalprofile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_help_principalprofile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_help_principalprofile_sp go CREATE PROCEDURE dbo.sysmail_help_principalprofile_sp @principal_id int = NULL, -- must provide id or name @principal_name sysname = NULL, @profile_id int = NULL, -- must provide id or name @profile_name sysname = NULL AS SET NOCOUNT ON DECLARE @rc int DECLARE @principal_sid varbinary(85) DECLARE @profileid int exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 1, 0, @profileid OUTPUT IF @rc <> 0 RETURN(1) IF (@principal_id IS NOT NULL AND @principal_id = 0) OR (@principal_name IS NOT NULL AND @principal_name = N'public') BEGIN IF (@principal_id IS NOT NULL AND @principal_id <> 0) OR (@principal_name IS NOT NULL AND @principal_name <> N'public') BEGIN RAISERROR(14605, -1, -1, 'principal') -- id and name do not match END SET @principal_sid = 0x00 -- public IF (@profileid IS NOT NULL) BEGIN SELECT principal_id=0, principal_name = N'public', prof.profile_id, profile_name=prof.name, prin.is_default FROM msdb.dbo.sysmail_principalprofile prin, msdb.dbo.sysmail_profile prof WHERE prin.profile_id=prof.profile_id AND prin.principal_sid = @principal_sid AND prof.profile_id=@profileid END ELSE BEGIN SELECT principal_id=0, principal_name = N'public', prof.profile_id, profile_name=prof.name, prin.is_default FROM msdb.dbo.sysmail_principalprofile prin, msdb.dbo.sysmail_profile prof WHERE prin.profile_id=prof.profile_id AND prin.principal_sid = @principal_sid END END ELSE -- non-public profiles BEGIN IF (@principal_id IS NOT NULL OR @principal_name IS NOT NULL) BEGIN exec @rc = msdb.dbo.sysmail_verify_principal_sp @principal_id, @principal_name, 1, @principal_sid OUTPUT IF @rc <> 0 RETURN(2) END IF ((@principal_id IS NOT NULL OR @principal_name IS NOT NULL) AND @profileid IS NOT NULL) BEGIN SELECT principal_id=dbprin.principal_id, principal_name=dbprin.name, prof.profile_id, profile_name=prof.name, prinprof.is_default FROM sys.database_principals dbprin, msdb.dbo.sysmail_principalprofile prinprof, msdb.dbo.sysmail_profile prof WHERE dbprin.principal_id = dbo.get_principal_id(prinprof.principal_sid) AND (prinprof.principal_sid = @principal_sid OR prinprof.principal_sid = 0x00) AND prof.profile_id = prinprof.profile_id AND prinprof.profile_id = @profileid END ELSE IF (@principal_id IS NOT NULL OR @principal_name IS NOT NULL) BEGIN SELECT principal_id=dbprin.principal_id, principal_name=dbprin.name, prof.profile_id, profile_name=prof.name, prinprof.is_default FROM sys.database_principals dbprin, msdb.dbo.sysmail_principalprofile prinprof, msdb.dbo.sysmail_profile prof WHERE dbprin.principal_id = dbo.get_principal_id(prinprof.principal_sid) AND (prinprof.principal_sid = @principal_sid OR prinprof.principal_sid = 0x00) AND prof.profile_id = prinprof.profile_id END ELSE IF (@profileid IS NOT NULL) BEGIN SELECT principal_id=dbprin.principal_id, principal_name=dbprin.name, prof.profile_id, profile_name=prof.name, prinprof.is_default FROM sys.database_principals dbprin, msdb.dbo.sysmail_principalprofile prinprof, msdb.dbo.sysmail_profile prof WHERE dbprin.principal_id = dbo.get_principal_id(prinprof.principal_sid) AND prof.profile_id = prinprof.profile_id AND prinprof.profile_id = @profileid END ELSE -- no parameters are supplied for filtering BEGIN SELECT principal_id=dbprin.principal_id, principal_name=dbprin.name, prof.profile_id, profile_name=prof.name, prinprof.is_default FROM sys.database_principals dbprin, msdb.dbo.sysmail_principalprofile prinprof, msdb.dbo.sysmail_profile prof WHERE dbprin.principal_id = dbo.get_principal_id(prinprof.principal_sid) AND prof.profile_id = prinprof.profile_id END END RETURN(0) go ----------------------------------------------------------- -- Database Mail: mail host database stored procedures ----------------------------------------------------------- ----------------------------------------------------------- -- procedure sysmail_logmailevent_sp ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sysmail_logmailevent_sp', 'P') IS NULL DROP PROCEDURE dbo.sysmail_logmailevent_sp GO ----- PRINT 'Creating sysmail_logmailevent_sp' ----- GO -- sysmail_logmailevent_sp : inserts an entry in the sysmail_log table CREATE PROCEDURE sysmail_logmailevent_sp @event_type INT, @description NVARCHAR(max) = NULL, @process_id INT = NULL, @mailitem_id INT = NULL, @account_id INT = NULL AS SET NOCOUNT ON --Try and get the optional logging level for the DatabaseMail process DECLARE @loggingLevel nvarchar(256) EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'LoggingLevel', @parameter_value = @loggingLevel OUTPUT DECLARE @loggingLevelInt int SET @loggingLevelInt = dbo.ConvertToInt(@loggingLevel, 3, 2) IF (@event_type = 3) OR -- error (@event_type = 2 AND @loggingLevelInt >= 2) OR -- warning with extended logging (@event_type = 1 AND @loggingLevelInt >= 2) OR -- info with extended logging (@event_type = 0 AND @loggingLevelInt >= 3) -- success with verbose logging BEGIN INSERT sysmail_log(event_type, description, process_id, mailitem_id, account_id) VALUES(@event_type, @description, @process_id , @mailitem_id, @account_id) END RETURN 0 GO ----------------------------------------------------------- -- procedure sysmail_start_sp ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sysmail_start_sp', 'P') IS NULL DROP PROCEDURE dbo.sysmail_start_sp GO ----- PRINT 'Creating sysmail_start_sp' ----- GO -- sysmail_start_sp : allows databasemail to process mail from the queue CREATE PROCEDURE sysmail_start_sp AS SET NOCOUNT ON DECLARE @rc INT DECLARE @localmessage nvarchar(255) ALTER QUEUE ExternalMailQueue WITH STATUS = ON SELECT @rc = @@ERROR IF(@rc = 0) BEGIN ALTER QUEUE ExternalMailQueue WITH ACTIVATION (STATUS = ON); SET @localmessage = FORMATMESSAGE(14639, SUSER_SNAME()) exec msdb.dbo.sysmail_logmailevent_sp @event_type=1, @description=@localmessage END RETURN @rc GO ----------------------------------------------------------- -- procedure sysmail_stop_sp ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sysmail_stop_sp', 'P') IS NULL DROP PROCEDURE dbo.sysmail_stop_sp GO ----- PRINT 'Creating sysmail_stop_sp' ----- GO -- sysmail_stop_sp : stops the DatabaseMail process. Mail items remain in the queue until sqlmail started CREATE PROCEDURE sysmail_stop_sp AS SET NOCOUNT ON DECLARE @rc INT DECLARE @localmessage nvarchar(255) ALTER QUEUE ExternalMailQueue WITH ACTIVATION (STATUS = OFF); SELECT @rc = @@ERROR IF(@rc = 0) BEGIN ALTER QUEUE ExternalMailQueue WITH STATUS = OFF; SELECT @rc = @@ERROR IF(@rc = 0) BEGIN SET @localmessage = FORMATMESSAGE(14640, SUSER_SNAME()) exec msdb.dbo.sysmail_logmailevent_sp @event_type=1, @description=@localmessage END END RETURN @rc GO ----------------------------------------------------------- -- procedure sysmail_help_status_sp ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sysmail_help_status_sp', 'P') IS NULL DROP PROCEDURE dbo.sysmail_help_status_sp GO ----- PRINT 'Creating sysmail_help_status_sp' ----- GO CREATE PROCEDURE sysmail_help_status_sp WITH EXECUTE AS 'dbo' AS BEGIN IF NOT EXISTS (SELECT * FROM sys.service_queues WHERE name = N'ExternalMailQueue' AND is_receive_enabled = 1) SELECT 'STOPPED' AS Status ELSE SELECT 'STARTED' AS Status END GO ----------------------------------------------------------- -- procedure sysmail_help_queue_sp ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sysmail_help_queue_sp', 'P') IS NULL DROP PROCEDURE dbo.sysmail_help_queue_sp GO ----- PRINT 'Creating sysmail_help_queue_sp' ----- GO CREATE PROCEDURE sysmail_help_queue_sp @queue_type nvarchar(6) = NULL -- Type of queue AS BEGIN SELECT @queue_type = LTRIM(RTRIM(@queue_type)) IF @queue_type = '' SELECT @queue_type = NULL IF ( (@queue_type IS NOT NULL) AND (LOWER(@queue_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ( N'mail', N'status') ) ) BEGIN RAISERROR(14266, -1, -1, '@queue_type', 'mail, status') RETURN(1) -- Failure END DECLARE @depth int DECLARE @temp TABLE ( queue_type nvarchar(6), length INT NOT NULL, state nvarchar(64), last_empty_rowset_time DATETIME, last_activated_time DATETIME ) IF ( (@queue_type IS NULL) OR (LOWER(@queue_type collate SQL_Latin1_General_CP1_CS_AS) = N'mail' ) ) BEGIN SET @depth = (SELECT COUNT(*) FROM ExternalMailQueue WITH (NOWAIT NOLOCK READUNCOMMITTED)) INSERT INTO @temp SELECT N'mail', @depth, qm.state as state, qm.last_empty_rowset_time as last_empty_rowset_time, qm.last_activated_time as last_activated_time FROM sys.dm_broker_queue_monitors qm JOIN sys.service_queues sq ON sq.object_id = qm.queue_id WHERE sq.name = 'ExternalMailQueue' END IF ( (@queue_type IS NULL) OR (LOWER(@queue_type collate SQL_Latin1_General_CP1_CS_AS) = N'status' ) ) BEGIN SET @depth = (SELECT COUNT(*) FROM InternalMailQueue WITH (NOWAIT NOLOCK READUNCOMMITTED)) INSERT INTO @temp SELECT N'status', @depth, qm.state as state, qm.last_empty_rowset_time as last_empty_rowset_time, qm.last_activated_time as last_activated_time FROM sys.dm_broker_queue_monitors qm JOIN sys.service_queues sq ON sq.object_id = qm.queue_id WHERE sq.name = 'InternalMailQueue' END SELECT * from @temp END GO ----------------------------------------------------------- -- procedure sp_SendMailMessage ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_SendMailMessage', 'P') IS NULL DROP PROCEDURE dbo.sp_SendMailMessage GO ----- PRINT 'Creating sp_SendMailMessage' ----- GO -- sp_SendMailMessage : Sends a request on the mail items SSB queue CREATE PROCEDURE sp_SendMailMessage @contract_name sysname, -- Name of contract @message_type sysname, -- Type of message @request varchar(max) -- XML message to send WITH EXECUTE AS 'dbo' AS SET NOCOUNT ON DECLARE @conversationHandle uniqueidentifier; DECLARE @error int -- Start a conversation with the remote service BEGIN DIALOG @conversationHandle FROM SERVICE [InternalMailService] TO SERVICE 'ExternalMailService' ON CONTRACT @contract_name WITH ENCRYPTION=OFF -- Check error SET @error = @@ERROR IF @error <> 0 BEGIN RETURN @error END -- Send message ;SEND ON CONVERSATION @conversationHandle MESSAGE TYPE @message_type (@request) -- Check error SET @error = @@ERROR IF @error <> 0 BEGIN RETURN @error END RETURN 0 GO ----------------------------------------------------------- -- procedure sp_isprohibited ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_isprohibited', 'P') IS NULL DROP PROCEDURE dbo.sp_isprohibited GO ----- PRINT 'Creating sp_isprohibited' ----- GO -- sp_isprohibited : To test if the attachment is prohibited or not. -- CREATE PROCEDURE sp_isprohibited @attachment nvarchar(max), @prohibitedextensions nvarchar(1000) AS DECLARE @extensionIndex int DECLARE @extensionName nvarchar(255) IF (@attachment IS NOT NULL AND LEN(@attachment) > 0) BEGIN SET @prohibitedextensions = UPPER(@prohibitedextensions) -- find @extensionName: the substring between the last '.' and the end of the string SET @extensionIndex = 0 WHILE (1=1) BEGIN DECLARE @lastExtensionIndex int SET @lastExtensionIndex = CHARINDEX('.', @attachment, @extensionIndex+1) IF (@lastExtensionIndex = 0) BREAK SET @extensionIndex = @lastExtensionIndex END IF (@extensionIndex > 0) BEGIN SET @extensionName = SUBSTRING(@attachment, @extensionIndex + 1, (LEN(@attachment) - @extensionIndex)) SET @extensionName = UPPER(@extensionName) -- compare @extensionName with each extension in the comma-separated @prohibitedextensions list DECLARE @currentExtensionStart int DECLARE @currentExtensionEnd int SET @currentExtensionStart = 0 SET @currentExtensionEnd = 0 WHILE (@currentExtensionEnd < LEN(@prohibitedextensions)) BEGIN SET @currentExtensionEnd = CHARINDEX(',', @prohibitedextensions, @currentExtensionStart) IF (@currentExtensionEnd = 0) -- we have reached the last extension of the list, or the list was empty SET @currentExtensionEnd = LEN(@prohibitedextensions)+1 DECLARE @prohibitedExtension nvarchar(1000) SET @prohibitedExtension = SUBSTRING(@prohibitedextensions, @currentExtensionStart, @currentExtensionEnd - @currentExtensionStart) SET @prohibitedExtension = RTRIM(LTRIM(@prohibitedExtension)) IF( @extensionName = @prohibitedExtension ) RETURN 1 SET @currentExtensionStart = @currentExtensionEnd + 1 END END RETURN 0 END GO ----------------------------------------------------------- -- procedure sp_SendMailQueues ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_SendMailQueues', 'P') IS NULL DROP PROCEDURE dbo.sp_SendMailQueues GO ----- PRINT 'Creating sp_SendMailQueues' ----- GO -- sp_SendMailQueues : Writes a send mail request to the queue. -- CREATE PROCEDURE sp_SendMailQueues @message_data varchar(max) -- The request in XML AS BEGIN SET NOCOUNT ON DECLARE @contract_name nvarchar(128) DECLARE @message_type nvarchar(128) DECLARE @retValue int SET @message_type = '{//www.microsoft.com/databasemail/messages}SendMail' SET @contract_name = '//www.microsoft.com/databasemail/contracts/SendMail/v1.0' --Writes the message to the queue EXEC @retValue = sp_SendMailMessage @contract_name, @message_type, @message_data RETURN @retValue END GO ----------------------------------------------------------- -- procedure sp_ProcessResponse ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_ProcessResponse', 'P') IS NULL DROP PROCEDURE dbo.sp_ProcessResponse GO ----- PRINT 'Creating sp_ProcessResponse' ----- USE [msdb] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO -- Processes responses from dbmail -- CREATE PROCEDURE [dbo].[sp_ProcessResponse] @conv_handle uniqueidentifier, @message_type_name NVARCHAR(256), @xml_message_body NVARCHAR(max) AS BEGIN DECLARE @idoc INT, @mailitem_id INT, @sent_status INT, @rc INT, @index INT, @processId INT, @sent_date DATETIME, @localmessage NVARCHAR(max), @LogMessage NVARCHAR(max), @retry_hconv uniqueidentifier, @paramStr NVARCHAR(256), @accRetryDelay INT -------------------------- --Always send the response ;SEND ON CONVERSATION @conv_handle MESSAGE TYPE @message_type_name (@xml_message_body) -- -- Need to handle the case where a sent retry is requested. -- This is done by setting a conversation timer, The timer with go off in the external queue -- Get the handle to the xml document EXEC @rc = sp_xml_preparedocument @idoc OUTPUT, @xml_message_body, N'' IF(@rc <> 0) BEGIN --Log the error. The response has already sent to the Internal queue. -- This will update the mail with the latest staus SET @localmessage = FORMATMESSAGE(14655, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage GOTO ErrorHandler; END -- Execute a SELECT statement that uses the OPENXML rowset provider to get the MailItemId and sent status. SELECT @mailitem_id = MailItemId, @sent_status = SentStatus FROM OPENXML (@idoc, '/responses:SendMail', 1) WITH (MailItemId INT './MailItemId/@Id', SentStatus INT './SentStatus/@Status') --Close the handle to the xml document EXEC sp_xml_removedocument @idoc IF(@mailitem_id IS NULL OR @sent_status IS NULL) BEGIN --Log error and continue. SET @localmessage = FORMATMESSAGE(14652, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage GOTO ErrorHandler; END -- Update the status of the email item UPDATE msdb.dbo.sysmail_mailitems SET sent_status = @sent_status WHERE mailitem_id = @mailitem_id -- -- A send retry has been requested. Set a conversation timer IF(@sent_status = 3) BEGIN -- Get the associated mail item data for the given @conversation_handle (if it exists) SELECT @retry_hconv = conversation_handle FROM sysmail_send_retries as sr RIGHT JOIN sysmail_mailitems as mi ON sr.mailitem_id = mi.mailitem_id WHERE mi.mailitem_id = @mailitem_id --Must be the first retry attempt. Create a sysmail_send_retries record to track retries IF(@retry_hconv IS NULL) BEGIN INSERT sysmail_send_retries(conversation_handle, mailitem_id) --last_send_attempt_date VALUES(@conv_handle, @mailitem_id) END ELSE BEGIN --Update existing retry record UPDATE sysmail_send_retries SET last_send_attempt_date = GETDATE(), send_attempts = send_attempts + 1 WHERE mailitem_id = @mailitem_id END --Get the global retry delay time EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'AccountRetryDelay', @parameter_value = @paramStr OUTPUT --ConvertToInt will return the default if @paramStr is null SET @accRetryDelay = dbo.ConvertToInt(@paramStr, 0x7fffffff, 300) -- 5 min default --Now set the dialog timer. This triggers the send retry ;BEGIN CONVERSATION TIMER (@conv_handle) TIMEOUT = @accRetryDelay END ELSE BEGIN --Only end theconversation if a retry isn't being attempted END CONVERSATION @conv_handle END -- All done OK goto ExitProc; ----------------- -- Error Handler ----------------- ErrorHandler: ------------------ -- Exit Procedure ------------------ ExitProc: RETURN (@rc); END GO ----------------------------------------------------------- -- procedure sp_MailItemResultSets ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_MailItemResultSets', 'P') IS NULL DROP PROCEDURE dbo.sp_MailItemResultSets GO ----- PRINT 'Creating sp_MailItemResultSets' ----- GO -- sp_MailItemResultSets : -- Sends back multiple rowsets with the mail items data CREATE PROCEDURE sp_MailItemResultSets @mailitem_id INT, @profile_id INT, @conversation_handle uniqueidentifier, @service_contract_name NVARCHAR(256), @message_type_name NVARCHAR(256) AS BEGIN SET NOCOUNT ON -- -- Send back multiple rowsets with the mail items data ---- -- 1) MessageTypeName SELECT @message_type_name as 'message_type_name', @service_contract_name as 'service_contract_name', @conversation_handle as 'conversation_handle', @mailitem_id as 'mailitem_id' ----- -- 2) The mail item record from sysmail_mailitems. SELECT mi.mailitem_id, mi.profile_id, (SELECT name FROM msdb.dbo.sysmail_profile p WHERE p.profile_id = mi.profile_id) as 'profile_name', mi.recipients, mi.copy_recipients, mi.blind_copy_recipients, mi.subject, mi.body, mi.body_format, mi.importance, mi.sensitivity, ISNULL(sr.send_attempts, 0) as retry_attempt, ISNULL(mi.from_address, '') as from_address, ISNULL(mi.reply_to, '') as reply_to FROM sysmail_mailitems as mi LEFT JOIN sysmail_send_retries as sr ON sr.mailitem_id = mi.mailitem_id WHERE mi.mailitem_id = @mailitem_id ----- -- 3) Account information SELECT a.account_id, a.name FROM msdb.dbo.sysmail_profileaccount as pa JOIN msdb.dbo.sysmail_account as a ON pa.account_id = a.account_id WHERE pa.profile_id = @profile_id ORDER BY pa.sequence_number ----- -- 4) Attachments if any SELECT attachment_id, mailitem_id, filename, filesize, attachment FROM sysmail_attachments WHERE mailitem_id = @mailitem_id RETURN 0 END GO ----------------------------------------------------------- -- procedure sp_process_DialogTimer ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_process_DialogTimer', 'P') IS NULL DROP PROCEDURE dbo.sp_process_DialogTimer GO ----- PRINT 'Creating sp_process_DialogTimer' ----- GO -- Processes a DialogTimer message from the the queue. This is used for send mail retries. -- Returns the mail to be send if a retry is required or logs a failure if max retry count has been reached CREATE PROCEDURE sp_process_DialogTimer @conversation_handle uniqueidentifier, @service_contract_name NVARCHAR(256), @message_type_name NVARCHAR(256) AS BEGIN SET NOCOUNT ON -- Declare all variables DECLARE @mailitem_id INT, @profile_id INT, @send_attempts INT, @mail_request_date DATETIME, @localmessage NVARCHAR(255), @paramStr NVARCHAR(256), @accRetryAttempts INT -- Get the associated mail item data for the given @conversation_handle SELECT @mailitem_id = mi.mailitem_id, @profile_id = mi.profile_id, @mail_request_date = mi.send_request_date, @send_attempts = sr.send_attempts FROM sysmail_send_retries as sr JOIN sysmail_mailitems as mi ON sr.mailitem_id = mi.mailitem_id WHERE sr.conversation_handle = @conversation_handle -- If not able to find a mailitem_id return and move to the next message. -- This could happen if the mail items table was cleared before the retry was fired IF(@mailitem_id IS NULL) BEGIN --Log warning and continue -- "mailitem_id on conversation %s was not found in the sysmail_send_retries table. This mail item will not be sent." SET @localmessage = FORMATMESSAGE(14662, convert(NVARCHAR(50), @conversation_handle)) exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@localmessage -- Resource clean-up IF(@conversation_handle IS NOT NULL) BEGIN END CONVERSATION @conversation_handle; END -- return code has special meaning and will be propagated to the calling process RETURN 2; END --Get the retry attempt count from sysmailconfig. EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'AccountRetryAttempts', @parameter_value = @paramStr OUTPUT --ConvertToInt will return the default if @paramStr is null SET @accRetryAttempts = dbo.ConvertToInt(@paramStr, 0x7fffffff, 1) --Check the send attempts and log and error if send_attempts >= retry count. --This shouldn't happen unless the retry configuration was changed IF(@send_attempts > @accRetryAttempts) BEGIN --Log warning and continue -- "Mail Id %d has exceeded the retry count. This mail item will not be sent." SET @localmessage = FORMATMESSAGE(14663, @mailitem_id) exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@localmessage, @mailitem_id=@mailitem_id -- Resource clean-up IF(@conversation_handle IS NOT NULL) BEGIN END CONVERSATION @conversation_handle; END -- return code has special meaning and will be propagated to the calling process RETURN 3; END -- This returns the mail item to the client as multiple result sets EXEC sp_MailItemResultSets @mailitem_id = @mailitem_id, @profile_id = @profile_id, @conversation_handle = @conversation_handle, @service_contract_name = @service_contract_name, @message_type_name = @message_type_name RETURN 0 END GO ----------------------------------------------------------- -- procedure sp_readrequest ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_readrequest', 'P') IS NULL DROP PROCEDURE dbo.sp_readrequest GO ----- PRINT 'Creating sp_readrequest' ----- GO -- sp_readrequest : Reads a request from the the queue and returns its -- contents. CREATE PROCEDURE sp_readrequest @receive_timeout INT -- the max time this read will wait for new message AS BEGIN SET NOCOUNT ON -- Table to store message information. DECLARE @msgs TABLE ( [conversation_handle] uniqueidentifier, [service_contract_name] nvarchar(256), [message_type_name] nvarchar(256), [message_body] varbinary(max) ) -- Declare variables to store row values fetched from the cursor DECLARE @exit INT, @idoc INT, @mailitem_id INT, @profile_id INT, @conversation_handle uniqueidentifier, @service_contract_name NVARCHAR(256), @message_type_name NVARCHAR(256), @xml_message_body VARCHAR(max), @timediff INT, @rec_timeout INT, @start_time DATETIME, @localmessage NVARCHAR(256), @rc INT --Init variables SELECT @start_time = GETDATE(), @timediff = 0, @exit = 0, @rc = 0 WHILE (@timediff <= @receive_timeout) BEGIN -- Delete all messages from @msgs table DELETE FROM @msgs -- Pick all message from queue SET @rec_timeout = @receive_timeout - @timediff WAITFOR(RECEIVE conversation_handle, service_contract_name, message_type_name, message_body FROM ExternalMailQueue INTO @msgs), TIMEOUT @rec_timeout -- Check if there was some error in reading from queue SET @rc = @@ERROR IF (@rc <> 0) BEGIN IF(@rc < 4) -- make sure return code is not in reserved range (1-3) SET @rc = 4 --Note: we will get error no. 9617 if the service queue 'ExternalMailQueue' is currently disabled. BREAK END --If there is no message in the queue return 1 to indicate a timeout IF NOT EXISTS(SELECT * FROM @msgs) BEGIN SET @rc = 1 BREAK END -- Create a cursor to iterate through the messages. DECLARE msgs_cursor CURSOR FOR SELECT conversation_handle, service_contract_name, message_type_name, CONVERT(VARCHAR(MAX), message_body) FROM @msgs; -- Open the cursor OPEN msgs_cursor; -- Perform the first fetch and store the values in the variables. FETCH NEXT FROM msgs_cursor INTO @conversation_handle, @service_contract_name, @message_type_name, @xml_message_body -- Check @@FETCH_STATUS to see if there are any more rows to fetch. WHILE (@@FETCH_STATUS = 0) BEGIN -- Check if the message is a send mail message IF(@message_type_name = N'{//www.microsoft.com/databasemail/messages}SendMail') BEGIN -- Get the handle to the xml document EXEC @rc = sp_xml_preparedocument @idoc OUTPUT, @xml_message_body, N'' IF(@rc <> 0) BEGIN IF(@rc < 4) -- make sure return code is not in reserved rang (1-3) SET @rc = 4 END ELSE -- parse the document and process its contents BEGIN -- Execute a SELECT statement that uses the OPENXML rowset provider to get the MailItemId. SELECT @mailitem_id = MailItemId FROM OPENXML (@idoc, '/requests:SendMail', 1) WITH (MailItemId INT './MailItemId') --Close the handle to the xml document EXEC sp_xml_removedocument @idoc -- get account information SELECT @profile_id = profile_id FROM sysmail_mailitems WHERE mailitem_id = @mailitem_id IF(@profile_id IS NULL) -- mail item has been deleted from the database BEGIN -- log warning SET @localmessage = FORMATMESSAGE(14667, convert(NVARCHAR(50), @mailitem_id)) exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@localmessage -- Resource clean-up IF(@conversation_handle IS NOT NULL) END CONVERSATION @conversation_handle; -- return code has special meaning and will be propagated to the calling process SET @rc = 2 END ELSE BEGIN -- This returns the mail item to the client as multiple result sets EXEC sp_MailItemResultSets @mailitem_id = @mailitem_id, @profile_id = @profile_id, @conversation_handle = @conversation_handle, @service_contract_name = @service_contract_name, @message_type_name = @message_type_name SET @exit = 1 -- make sure we exit outer loop END END -- always break from the loop upon processing SendMail message BREAK END -- Check if the message is a dialog timer. This is used for account retries ELSE IF(@message_type_name = N'http://schemas.microsoft.com/SQL/ServiceBroker/DialogTimer') BEGIN -- Handle the retry case. - DialogTimer is used for send mail reties EXEC @rc = sp_process_DialogTimer @conversation_handle = @conversation_handle, @service_contract_name = @service_contract_name, @message_type_name = N'{//www.microsoft.com/databasemail/messages}SendMail' -- Always break from the loop upon processing DialogTimer message -- In case of failure return code from procedure call will simply be propagated to the calling process SET @exit = 1 -- make sure we exit outer loop BREAK END -- Error case ELSE IF (@message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/Error') -- Error in the conversation, hence ignore all the messages of this conversation. BREAK -- This is executed as long as fetch succeeds. FETCH NEXT FROM msgs_cursor INTO @conversation_handle, @service_contract_name, @message_type_name, @xml_message_body END CLOSE msgs_cursor; DEALLOCATE msgs_cursor; -- Check if we read any request or only SSB generated messages -- If a valid request is read with or without an error break out of loop IF (@exit = 1 OR @rc <> 0) BREAK --Keep track of how long this sp has been running select @timediff = DATEDIFF(ms, @start_time, getdate()) END -- propagate return code to the calling process RETURN @rc END GO ----------------------------------------------------------- -- procedure sp_GetAttachmentData ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_GetAttachmentData', 'P') IS NULL DROP PROCEDURE dbo.sp_GetAttachmentData GO ----- PRINT 'Creating sp_GetAttachmentData' ----- GO CREATE PROCEDURE sp_GetAttachmentData @attachments nvarchar(max), @temp_table_uid uniqueidentifier, @exclude_query_output BIT = 0 AS BEGIN SET NOCOUNT ON SET QUOTED_IDENTIFIER ON DECLARE @rc INT, @prohibitedExts NVARCHAR(1000), @attachFilePath NVARCHAR(260), @scIndex INT, @startLocation INT, @fileSizeStr NVARCHAR(256), @fileSize INT, @mailDbName sysname, @uidStr VARCHAR(36) --Get the maximum file size allowed for attachments from sysmailconfig. EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'MaxFileSize', @parameter_value = @fileSizeStr OUTPUT --ConvertToInt will return the default if @fileSizeStr is null SET @fileSize = dbo.ConvertToInt(@fileSizeStr, 0x7fffffff, 100000) --May need this if attaching files EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'ProhibitedExtensions', @parameter_value = @prohibitedExts OUTPUT SET @mailDbName = DB_NAME() SET @uidStr = CONVERT(VARCHAR(36), @temp_table_uid) SET @attachments = @attachments + ';' SET @startLocation = 0 SET @scIndex = CHARINDEX(';', @attachments, @startLocation) WHILE (@scIndex <> 0) BEGIN SET @attachFilePath = SUBSTRING(@attachments, @startLocation, (@scIndex - @startLocation)) -- Make sure we have an attachment file name to work with, and that it hasn't been truncated IF (@scIndex - @startLocation > 260 ) BEGIN RAISERROR(14628, 16, 1) RETURN 1 END IF ((@attachFilePath IS NULL) OR (LEN(@attachFilePath) = 0)) BEGIN RAISERROR(14628, 16, 1) RETURN 1 END --Check if attachment ext is allowed EXEC @rc = sp_isprohibited @attachFilePath, @prohibitedExts IF (@rc <> 0) BEGIN RAISERROR(14630, 16, 1, @attachFilePath, @prohibitedExts) RETURN 2 END DECLARE @no_output_int INT SET @no_output_int = CONVERT(int, @exclude_query_output) -- return code checked after select and delete calls EXEC @rc = master..xp_sysmail_attachment_load @message = @mailDbName, @attachments = @attachFilePath, @subject = @uidStr, @max_attachment_size = @fileSize, @no_output = @no_output_int IF (@rc <> 0) RETURN (@rc) --Get next substring index SET @startLocation = @scIndex + 1 SET @scIndex = CHARINDEX(';', @attachments, @startLocation) IF (@scIndex = 0) BREAK END RETURN 0 END GO ----------------------------------------------------------- -- procedure sp_RunMailQuery ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_RunMailQuery', 'P') IS NULL DROP PROCEDURE dbo.sp_RunMailQuery GO ----- PRINT 'Creating sp_RunMailQuery' ----- USE msdb GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO CREATE PROCEDURE [dbo].[sp_RunMailQuery] @query NVARCHAR(max), @attach_results BIT, @query_attachment_filename NVARCHAR(260) = NULL, @no_output BIT, @query_result_header BIT, @separator VARCHAR(1), @echo_error BIT, @dbuse sysname, @width INT, @temp_table_uid uniqueidentifier, @query_no_truncate BIT, @query_result_no_padding BIT AS BEGIN SET NOCOUNT ON SET QUOTED_IDENTIFIER ON DECLARE @rc INT, @prohibitedExts NVARCHAR(1000), @fileSizeStr NVARCHAR(256), @fileSize INT, @attach_res_int INT, @no_output_int INT, @no_header_int INT, @echo_error_int INT, @query_no_truncate_int INT, @query_result_no_padding_int INT, @mailDbName sysname, @uid uniqueidentifier, @uidStr VARCHAR(36) -- --Get config settings and verify parameters -- SET @query_attachment_filename = LTRIM(RTRIM(@query_attachment_filename)) --Get the maximum file size allowed for attachments from sysmailconfig. EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'MaxFileSize', @parameter_value = @fileSizeStr OUTPUT --ConvertToInt will return the default if @fileSizeStr is null SET @fileSize = dbo.ConvertToInt(@fileSizeStr, 0x7fffffff, 100000) IF (@attach_results = 1) BEGIN --Need this if attaching the query EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'ProhibitedExtensions', @parameter_value = @prohibitedExts OUTPUT -- If attaching query results to a file and a filename isn't given create one IF ((@query_attachment_filename IS NOT NULL) AND (LEN(@query_attachment_filename) > 0)) BEGIN EXEC @rc = sp_isprohibited @query_attachment_filename, @prohibitedExts IF (@rc <> 0) BEGIN RAISERROR(14630, 16, 1, @query_attachment_filename, @prohibitedExts) RETURN 2 END END ELSE BEGIN --If queryfilename is not specified, generate a random name (doesn't have to be unique) SET @query_attachment_filename = 'QueryResults' + CONVERT(varchar, ROUND(RAND() * 1000000, 0)) + '.txt' END END --Init variables used in the query execution SET @mailDbName = db_name() SET @uidStr = convert(varchar(36), @temp_table_uid) SET @attach_res_int = CONVERT(int, @attach_results) SET @no_output_int = CONVERT(int, @no_output) IF(@query_result_header = 0) SET @no_header_int = 1 ELSE SET @no_header_int = 0 SET @echo_error_int = CONVERT(int, @echo_error) SET @query_no_truncate_int = CONVERT(int, @query_no_truncate) SET @query_result_no_padding_int = CONVERT(int, @query_result_no_padding ) EXEC @rc = master..xp_sysmail_format_query @query = @query, @message = @mailDbName, @subject = @uidStr, @dbuse = @dbuse, @attachments = @query_attachment_filename, @attach_results = @attach_res_int, -- format params @separator = @separator, @no_header = @no_header_int, @no_output = @no_output_int, @echo_error = @echo_error_int, @max_attachment_size = @fileSize, @width = @width, @query_no_truncate = @query_no_truncate_int, @query_result_no_padding = @query_result_no_padding_int RETURN @rc END GO ----------------------------------------------------------- -- procedure sp_validate_user, used by sp_send_dbmail ----------------------------------------------------------- IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_validate_user'))) DROP PROCEDURE sp_validate_user go use msdb GO /****** Object: StoredProcedure [dbo].sp_validate_user ********/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO CREATE PROCEDURE [dbo].[sp_validate_user] @send_request_user sysname, @user_sid varbinary(85) OUTPUT WITH EXECUTE AS 'dbo' AS BEGIN SET NOCOUNT ON -- And make sure ARITHABORT is on. This is the default for yukon DB's SET ARITHABORT ON declare @groupSid varbinary(85) declare @temp table ([account name] sysname, [type] char(8), [privilege] char(9), [mapped login name] sysname, [permission path] sysname null) declare @sidlist table ([account name] sysname, [accountsid] varbinary(85) null, [permission path] sysname null) SET @user_sid = NULL SET @groupSid = NULL -- Lookup the Windows Group membership, if any, that grants this -- user access to SQL Server. xp_logininfo may fail if the sql -- server service account cannot talk to the domain controller to -- validate the windows username, or it may fail if the -- @send_request_user is not a valid windows user or group name. BEGIN TRY insert @temp exec master.dbo.xp_logininfo @send_request_user, 'all' -- For a given account name, Get account name -> group accountsid mapping to a temp table variable insert @sidlist select [account name], suser_sid([permission path]),[permission path] from @temp END TRY BEGIN CATCH RETURN 2 END CATCH -- for a given account name, there has to be atleast one account sid that is not null and -- there has to be atleast one mail profile for the list of account sids IF ((EXISTS(SELECT [account name] FROM @sidlist WHERE accountsid is not NULL) AND (EXISTS(SELECT profile_id FROM msdb.dbo.sysmail_principalprofile pp, @sidlist s WHERE s.accountsid = pp.principal_sid)))) BEGIN -- Get the first account's sid that meets following criteria -- 1) return first default profile (if available) -- 2) if no default profile is defined, then return the first non-default profile for this account SELECT TOP 1 @groupSid = accountsid FROM @sidlist s, msdb.dbo.sysmail_principalprofile pp WHERE s.accountsid is not NULL AND s.accountsid = pp.principal_sid ORDER BY is_default DESC END -- Lookup a default profile for the Group. If there is one, -- then use the group's mail profile. IF (@groupSid IS NOT NULL) BEGIN SET @user_sid = @groupSid RETURN 0 END RETURN 1 END GO ----------------------------------------------------------- -- procedure sp_send_dbmail ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_send_dbmail', 'P') IS NULL DROP PROCEDURE dbo.sp_send_dbmail GO ----- PRINT 'Creating sp_send_dbmail' ----- USE msdb GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO -- sp_send_dbmail : Sends a mail from Yukon outbox. -- CREATE PROCEDURE [dbo].[sp_send_dbmail] @profile_name sysname = NULL, @recipients VARCHAR(MAX) = NULL, @copy_recipients VARCHAR(MAX) = NULL, @blind_copy_recipients VARCHAR(MAX) = NULL, @subject NVARCHAR(255) = NULL, @body NVARCHAR(MAX) = NULL, @body_format VARCHAR(20) = NULL, @importance VARCHAR(6) = 'NORMAL', @sensitivity VARCHAR(12) = 'NORMAL', @file_attachments NVARCHAR(MAX) = NULL, @query NVARCHAR(MAX) = NULL, @execute_query_database sysname = NULL, @attach_query_result_as_file BIT = 0, @query_attachment_filename NVARCHAR(260) = NULL, @query_result_header BIT = 1, @query_result_width INT = 256, @query_result_separator CHAR(1) = ' ', @exclude_query_output BIT = 0, @append_query_error BIT = 0, @query_no_truncate BIT = 0, @query_result_no_padding BIT = 0, @mailitem_id INT = NULL OUTPUT, @from_address VARCHAR(max) = NULL, @reply_to VARCHAR(max) = NULL WITH EXECUTE AS 'dbo' AS BEGIN SET NOCOUNT ON -- And make sure ARITHABORT is on. This is the default for yukon DB's SET ARITHABORT ON --Declare variables used by the procedure internally DECLARE @profile_id INT, @temp_table_uid uniqueidentifier, @sendmailxml VARCHAR(max), @CR_str NVARCHAR(2), @localmessage NVARCHAR(255), @QueryResultsExist INT, @AttachmentsExist INT, @RetErrorMsg NVARCHAR(4000), --Impose a limit on the error message length to avoid memory abuse @rc INT, @procName sysname, @trancountSave INT, @tranStartedBool INT, @is_sysadmin BIT, @send_request_user sysname, @database_user_id INT, @sid varbinary(85) -- Initialize SELECT @rc = 0, @QueryResultsExist = 0, @AttachmentsExist = 0, @temp_table_uid = NEWID(), @procName = OBJECT_NAME(@@PROCID), @tranStartedBool = 0, @trancountSave = @@TRANCOUNT, @sid = NULL EXECUTE AS CALLER SELECT @is_sysadmin = IS_SRVROLEMEMBER('sysadmin'), @send_request_user = SUSER_SNAME(), @database_user_id = USER_ID() REVERT --Check if SSB is enabled in this database IF (ISNULL(DATABASEPROPERTYEX(DB_NAME(), N'IsBrokerEnabled'), 0) <> 1) BEGIN RAISERROR(14650, 16, 1) RETURN 1 END --Report error if the mail queue has been stopped. --sysmail_stop_sp/sysmail_start_sp changes the receive status of the SSB queue IF NOT EXISTS (SELECT * FROM sys.service_queues WHERE name = N'ExternalMailQueue' AND is_receive_enabled = 1) BEGIN RAISERROR(14641, 16, 1) RETURN 1 END -- Get the relevant profile_id -- IF (@profile_name IS NULL) BEGIN -- Use the global or users default if profile name is not supplied SELECT TOP (1) @profile_id = pp.profile_id FROM msdb.dbo.sysmail_principalprofile as pp WHERE (pp.is_default = 1) AND (dbo.get_principal_id(pp.principal_sid) = @database_user_id OR pp.principal_sid = 0x00) ORDER BY dbo.get_principal_id(pp.principal_sid) DESC --Was a profile found IF(@profile_id IS NULL) BEGIN -- Try a profile lookup based on Windows Group membership, if any EXEC @rc = msdb.dbo.sp_validate_user @send_request_user, @sid OUTPUT IF (@rc = 0) BEGIN SELECT TOP (1) @profile_id = pp.profile_id FROM msdb.dbo.sysmail_principalprofile as pp WHERE (pp.is_default = 1) AND (pp.principal_sid = @sid) ORDER BY dbo.get_principal_id(pp.principal_sid) DESC END IF(@profile_id IS NULL) BEGIN RAISERROR(14636, 16, 1) RETURN 1 END END END ELSE BEGIN --Get primary account if profile name is supplied EXEC @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id = NULL, @profile_name = @profile_name, @allow_both_nulls = 0, @allow_id_name_mismatch = 0, @profileid = @profile_id OUTPUT IF (@rc <> 0) RETURN @rc --Make sure this user has access to the specified profile. --sysadmins can send on any profiles IF ( @is_sysadmin <> 1) BEGIN --Not a sysadmin so check users access to profile iF NOT EXISTS(SELECT * FROM msdb.dbo.sysmail_principalprofile WHERE ((profile_id = @profile_id) AND (dbo.get_principal_id(principal_sid) = @database_user_id OR principal_sid = 0x00))) BEGIN EXEC msdb.dbo.sp_validate_user @send_request_user, @sid OUTPUT IF(@sid IS NULL) BEGIN RAISERROR(14607, -1, -1, 'profile') RETURN 1 END END END END --Attach results must be specified IF @attach_query_result_as_file IS NULL BEGIN RAISERROR(14618, 16, 1, 'attach_query_result_as_file') RETURN 2 END --No output must be specified IF @exclude_query_output IS NULL BEGIN RAISERROR(14618, 16, 1, 'exclude_query_output') RETURN 3 END --No header must be specified IF @query_result_header IS NULL BEGIN RAISERROR(14618, 16, 1, 'query_result_header') RETURN 4 END -- Check if query_result_separator is specifed IF @query_result_separator IS NULL OR DATALENGTH(@query_result_separator) = 0 BEGIN RAISERROR(14618, 16, 1, 'query_result_separator') RETURN 5 END --Echo error must be specified IF @append_query_error IS NULL BEGIN RAISERROR(14618, 16, 1, 'append_query_error') RETURN 6 END --@body_format can be TEXT (default) or HTML IF (@body_format IS NULL) BEGIN SET @body_format = 'TEXT' END ELSE BEGIN SET @body_format = UPPER(@body_format) IF @body_format NOT IN ('TEXT', 'HTML') BEGIN RAISERROR(14626, 16, 1, @body_format) RETURN 13 END END --Importance must be specified IF @importance IS NULL BEGIN RAISERROR(14618, 16, 1, 'importance') RETURN 15 END SET @importance = UPPER(@importance) --Importance must be one of the predefined values IF @importance NOT IN ('LOW', 'NORMAL', 'HIGH') BEGIN RAISERROR(14622, 16, 1, @importance) RETURN 16 END --Sensitivity must be specified IF @sensitivity IS NULL BEGIN RAISERROR(14618, 16, 1, 'sensitivity') RETURN 17 END SET @sensitivity = UPPER(@sensitivity) --Sensitivity must be one of predefined values IF @sensitivity NOT IN ('NORMAL', 'PERSONAL', 'PRIVATE', 'CONFIDENTIAL') BEGIN RAISERROR(14623, 16, 1, @sensitivity) RETURN 18 END --Message body cannot be null. Atleast one of message, subject, query, --attachments must be specified. IF( (@body IS NULL AND @query IS NULL AND @file_attachments IS NULL AND @subject IS NULL) OR ( (LEN(@body) IS NULL OR LEN(@body) <= 0) AND (LEN(@query) IS NULL OR LEN(@query) <= 0) AND (LEN(@file_attachments) IS NULL OR LEN(@file_attachments) <= 0) AND (LEN(@subject) IS NULL OR LEN(@subject) <= 0) ) ) BEGIN RAISERROR(14624, 16, 1, '@body, @query, @file_attachments, @subject') RETURN 19 END ELSE IF @subject IS NULL OR LEN(@subject) <= 0 SET @subject='SQL Server Message' --Recipients cannot be empty. Atleast one of the To, Cc, Bcc must be specified IF ( (@recipients IS NULL AND @copy_recipients IS NULL AND @blind_copy_recipients IS NULL ) OR ( (LEN(@recipients) IS NULL OR LEN(@recipients) <= 0) AND (LEN(@copy_recipients) IS NULL OR LEN(@copy_recipients) <= 0) AND (LEN(@blind_copy_recipients) IS NULL OR LEN(@blind_copy_recipients) <= 0) ) ) BEGIN RAISERROR(14624, 16, 1, '@recipients, @copy_recipients, @blind_copy_recipients') RETURN 20 END --If query is not specified, attach results and no header cannot be true. IF ( (@query IS NULL OR LEN(@query) <= 0) AND @attach_query_result_as_file = 1) BEGIN RAISERROR(14625, 16, 1) RETURN 21 END -- -- Execute Query if query is specified IF ((@query IS NOT NULL) AND (LEN(@query) > 0)) BEGIN EXECUTE AS CALLER EXEC @rc = sp_RunMailQuery @query = @query, @attach_results = @attach_query_result_as_file, @query_attachment_filename = @query_attachment_filename, @no_output = @exclude_query_output, @query_result_header = @query_result_header, @separator = @query_result_separator, @echo_error = @append_query_error, @dbuse = @execute_query_database, @width = @query_result_width, @temp_table_uid = @temp_table_uid, @query_no_truncate = @query_no_truncate, @query_result_no_padding = @query_result_no_padding -- This error indicates that query results size was over the configured MaxFileSize. -- Note, an error has already beed raised in this case IF(@rc = 101) GOTO ErrorHandler; REVERT -- Always check the transfer tables for data. They may also contain error messages -- Only one of the tables receives data in the call to sp_RunMailQuery IF(@attach_query_result_as_file = 1) BEGIN IF EXISTS(SELECT * FROM sysmail_attachments_transfer WHERE uid = @temp_table_uid) SET @AttachmentsExist = 1 END ELSE BEGIN IF EXISTS(SELECT * FROM sysmail_query_transfer WHERE uid = @temp_table_uid AND uid IS NOT NULL) SET @QueryResultsExist = 1 END -- Exit if there was an error and caller doesn't want the error appended to the mail IF (@rc <> 0 AND @append_query_error = 0) BEGIN --Error msg with be in either the attachment table or the query table --depending on the setting of @attach_query_result_as_file IF(@attach_query_result_as_file = 1) BEGIN --Copy query results from the attachments table to mail body SELECT @RetErrorMsg = CONVERT(NVARCHAR(4000), attachment) FROM sysmail_attachments_transfer WHERE uid = @temp_table_uid END ELSE BEGIN --Copy query results from the query table to mail body SELECT @RetErrorMsg = text_data FROM sysmail_query_transfer WHERE uid = @temp_table_uid END GOTO ErrorHandler; END SET @AttachmentsExist = @attach_query_result_as_file END ELSE BEGIN --If query is not specified, attach results cannot be true. IF (@attach_query_result_as_file = 1) BEGIN RAISERROR(14625, 16, 1) RETURN 21 END END --Get the prohibited extensions for attachments from sysmailconfig. IF ((@file_attachments IS NOT NULL) AND (LEN(@file_attachments) > 0)) BEGIN EXECUTE AS CALLER EXEC @rc = sp_GetAttachmentData @attachments = @file_attachments, @temp_table_uid = @temp_table_uid, @exclude_query_output = @exclude_query_output REVERT IF (@rc <> 0) GOTO ErrorHandler; IF EXISTS(SELECT * FROM sysmail_attachments_transfer WHERE uid = @temp_table_uid) SET @AttachmentsExist = 1 END -- Start a transaction if not already in one. -- Note: For rest of proc use GOTO ErrorHandler for falures if (@trancountSave = 0) BEGIN TRAN @procName SET @tranStartedBool = 1 -- Store complete mail message for history/status purposes INSERT sysmail_mailitems ( profile_id, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_user, from_address, reply_to ) VALUES ( @profile_id, @recipients, @copy_recipients, @blind_copy_recipients, @subject, @body, @body_format, @importance, @sensitivity, @file_attachments, 'MIME', @query, @execute_query_database, @attach_query_result_as_file, @query_result_header, @query_result_width, @query_result_separator, @exclude_query_output, @append_query_error, @send_request_user, @from_address, @reply_to ) SELECT @rc = @@ERROR, @mailitem_id = SCOPE_IDENTITY() IF(@rc <> 0) GOTO ErrorHandler; --Copy query into the message body IF(@QueryResultsExist = 1) BEGIN -- if the body is null initialize it UPDATE sysmail_mailitems SET body = N'' WHERE mailitem_id = @mailitem_id AND body is null --Add CR, a \r followed by \n, which is 0xd and then 0xa SET @CR_str = CHAR(13) + CHAR(10) UPDATE sysmail_mailitems SET body.WRITE(@CR_str, NULL, NULL) WHERE mailitem_id = @mailitem_id --Copy query results to mail body UPDATE sysmail_mailitems SET body.WRITE( (SELECT text_data from sysmail_query_transfer WHERE uid = @temp_table_uid), NULL, NULL ) WHERE mailitem_id = @mailitem_id END --Copy into the attachments table IF(@AttachmentsExist = 1) BEGIN --Copy temp attachments to sysmail_attachments INSERT INTO sysmail_attachments(mailitem_id, filename, filesize, attachment) SELECT @mailitem_id, filename, filesize, attachment FROM sysmail_attachments_transfer WHERE uid = @temp_table_uid END -- Create the primary SSB xml maessage SET @sendmailxml = '' + 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) PRINT @localmessage END -- -- See if the transaction needs to be commited -- IF (@trancountSave = 0 and @tranStartedBool = 1) COMMIT TRAN @procName -- All done OK goto ExitProc; ----------------- -- Error Handler ----------------- ErrorHandler: IF (@tranStartedBool = 1) ROLLBACK TRAN @procName ------------------ -- Exit Procedure ------------------ ExitProc: --Always delete query and attactment transfer records. --Note: Query results can also be returned in the sysmail_attachments_transfer table DELETE sysmail_attachments_transfer WHERE uid = @temp_table_uid DELETE sysmail_query_transfer WHERE uid = @temp_table_uid --Raise an error it the query execution fails -- This will only be the case when @append_query_error is set to 0 (false) IF( (@RetErrorMsg IS NOT NULL) AND (@exclude_query_output=0) ) BEGIN RAISERROR(14661, -1, -1, @RetErrorMsg) END RETURN (@rc) END GO ----------------------------------------------------------- -- procedure sp_ExternalMailQueueListener ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_ExternalMailQueueListener', 'P') IS NULL DROP PROCEDURE dbo.sp_ExternalMailQueueListener GO ----- PRINT 'Creating sp_ExternalMailQueueListener' ----- USE [msdb] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO -- Processes messages from the external mail queue -- CREATE PROCEDURE [dbo].[sp_ExternalMailQueueListener] AS BEGIN DECLARE @idoc INT, @mailitem_id INT, @sent_status INT, @sent_account_id INT, @rc INT, @processId INT, @sent_date DATETIME, @localmessage NVARCHAR(max), @conv_handle uniqueidentifier, @message_type_name NVARCHAR(256), @xml_message_body NVARCHAR(max), @LogMessage NVARCHAR(max) -- Table to store message information. DECLARE @msgs TABLE ( [conversation_handle] uniqueidentifier, [message_type_name] nvarchar(256), [message_body] varbinary(max) ) --RECEIVE messages from the external queue. --MailItem status messages are sent from the external sql mail process along with other SSB notifications and errors ;RECEIVE conversation_handle, message_type_name, message_body FROM InternalMailQueue INTO @msgs -- Check if there was some error in reading from queue SET @rc = @@ERROR IF (@rc <> 0) BEGIN --Log error and continue. Don't want to block the following messages on the queue SET @localmessage = FORMATMESSAGE(@@ERROR) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage GOTO ErrorHandler; END ----------------------------------- --Process sendmail status messages SELECT @conv_handle = conversation_handle, @message_type_name = message_type_name, @xml_message_body = CAST(message_body AS NVARCHAR(MAX)) FROM @msgs WHERE [message_type_name] = N'{//www.microsoft.com/databasemail/messages}SendMailStatus' IF(@message_type_name IS NOT NULL) BEGIN -- --Expecting the xml body to be n the following form: -- -- -- -- -- -- -- -- -- -- -- -- -- Get the handle to the xml document EXEC @rc = sp_xml_preparedocument @idoc OUTPUT, @xml_message_body, N'' IF(@rc <> 0) BEGIN --Log error and continue. Don't want to block the following messages on the queue SET @localmessage = FORMATMESSAGE(14655, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage GOTO ErrorHandler; END -- Execute a SELECT statement that uses the OPENXML rowset provider to get the MailItemId and sent status. SELECT @mailitem_id = MailItemId, @sent_status = SentStatus, @sent_account_id = SentAccountId, @sent_date = SentDate, @processId = CallingProcess, @LogMessage = LogMessage FROM OPENXML (@idoc, '/responses:SendMail', 1) WITH (MailItemId INT './MailItemId/@Id', SentStatus INT './SentStatus/@Status', SentAccountId INT './SentAccountId/@Id', SentDate DATETIME './SentDate/@Date', --The date was formated using ISO8601 CallingProcess INT './CallingProcess/@Id', LogMessage NVARCHAR(max) './Information/Failure/@Message') --Close the handle to the xml document EXEC sp_xml_removedocument @idoc IF(@mailitem_id IS NULL) BEGIN --Log error and continue. Don't want to block the following messages on the queue by rolling back the tran SET @localmessage = FORMATMESSAGE(14652, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage END ELSE BEGIN -- check sent_status is valid : 0(PendingSend), 1(SendSuccessful), 2(SendFailed), 3(AttemptingSendRetry) IF(@sent_status NOT IN (1, 2, 3)) BEGIN SET @localmessage = FORMATMESSAGE(14653, N'SentStatus', CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@localmessage --Set value to SendFailed SET @sent_status = 2 END --Make the @sent_account_id NULL if it is 0. IF(@sent_account_id IS NOT NULL AND @sent_account_id = 0) SET @sent_account_id = NULL -- -- Update the mail status if not a retry. Nothing else needs to be done in this case UPDATE sysmail_mailitems SET sent_status = CAST (@sent_status as TINYINT), sent_account_id = @sent_account_id, sent_date = @sent_date WHERE mailitem_id = @mailitem_id -- Report a failure if no record is found in the sysmail_mailitems table IF (@@ROWCOUNT = 0) BEGIN SET @localmessage = FORMATMESSAGE(14653, N'MailItemId', CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage END IF (@LogMessage IS NOT NULL) BEGIN exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@LogMessage, @process_id=@processId, @mailitem_id=@mailitem_id, @account_id=@sent_account_id END END END ------------------------------------------------------- --Process all other messages by logging to sysmail_log SET @conv_handle = NULL; --Always end the conversion if this message is received SELECT @conv_handle = conversation_handle FROM @msgs WHERE [message_type_name] = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog' IF(@conv_handle IS NOT NULL) BEGIN END CONVERSATION @conv_handle; END DECLARE @queuemessage nvarchar(max) DECLARE queue_messages_cursor CURSOR LOCAL FOR SELECT FORMATMESSAGE(14654, CONVERT(NVARCHAR(50), conversation_handle), message_type_name, message_body) FROM @msgs WHERE [message_type_name] NOT IN (N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog', N'{//www.microsoft.com/databasemail/messages}SendMailStatus') OPEN queue_messages_cursor FETCH NEXT FROM queue_messages_cursor INTO @queuemessage WHILE (@@fetch_status = 0) BEGIN exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@queuemessage FETCH NEXT FROM queue_messages_cursor INTO @queuemessage END CLOSE queue_messages_cursor DEALLOCATE queue_messages_cursor -- All done OK goto ExitProc; ----------------- -- Error Handler ----------------- ErrorHandler: ------------------ -- Exit Procedure ------------------ ExitProc: RETURN (@rc) END GO ---------------------------------------------------------- -- procedure sp_sysmail_activate ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_sysmail_activate', 'P') IS NULL DROP PROCEDURE dbo.sp_sysmail_activate GO ----- PRINT 'Creating sp_sysmail_activate' ----- USE [msdb] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- sp_sysmail_activate : Starts the DatabaseMail process if it isn't already running -- CREATE PROCEDURE [dbo].[sp_sysmail_activate] AS BEGIN DECLARE @mailDbName sysname DECLARE @mailDbId INT DECLARE @mailEngineLifeMin INT DECLARE @loggingLevel nvarchar(256) DECLARE @loggingLevelInt int DECLARE @parameter_value nvarchar(256) DECLARE @localmessage nvarchar(max) DECLARE @readFromConfigFile INT DECLARE @rc INT SET NOCOUNT ON EXEC sp_executesql @statement = N'RECEIVE TOP(0) * FROM msdb.dbo.ExternalMailQueue' EXEC @rc = msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'DatabaseMailExeMinimumLifeTime', @parameter_value = @parameter_value OUTPUT IF(@rc <> 0) RETURN (1) --ConvertToInt will return the default if @parameter_value is null or config value can't be converted --Setting max exe lifetime is 1 week (604800 secs). Can't see a reason for it to ever run longer that this SET @mailEngineLifeMin = dbo.ConvertToInt(@parameter_value, 604800, 600) EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'ReadFromConfigurationFile', @parameter_value = @parameter_value OUTPUT --Try to read the optional read from configuration file: SET @readFromConfigFile = dbo.ConvertToInt(@parameter_value, 1, 0) --Try and get the optional logging level for the DatabaseMail process EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'LoggingLevel', @parameter_value = @loggingLevel OUTPUT --Convert logging level into string value for passing into XP SET @loggingLevelInt = dbo.ConvertToInt(@loggingLevel, 3, 2) IF @loggingLevelInt = 1 SET @loggingLevel = 'Normal' ELSE IF @loggingLevelInt = 3 SET @loggingLevel = 'Verbose' ELSE -- default SET @loggingLevel = 'Extended' SET @mailDbName = DB_NAME() SET @mailDbId = DB_ID() EXEC @rc = master..xp_sysmail_activate @mailDbId, @mailDbName, @readFromConfigFile, @mailEngineLifeMin, @loggingLevel IF(@rc <> 0) BEGIN SET @localmessage = FORMATMESSAGE(14637) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage END ELSE BEGIN SET @localmessage = FORMATMESSAGE(14638) exec msdb.dbo.sysmail_logmailevent_sp @event_type=0, @description=@localmessage END RETURN @rc END GO -------------------------------------------------------------- -- Database Mail roles and permissions -------------------------------------------------------------- -- Create the DatabaseMailUserRole role IF (EXISTS (SELECT * FROM msdb.dbo.sysusers WHERE (name = N'DatabaseMailUserRole') AND (issqlrole = 1))) BEGIN -- If there are no members in the role, then drop and re-create it IF ((SELECT COUNT(*) FROM msdb.dbo.sysusers su, msdb.dbo.sysmembers sm WHERE (su.uid = sm.groupuid) AND (su.name = N'DatabaseMailUserRole') AND (su.issqlrole = 1)) = 0) BEGIN EXECUTE msdb.dbo.sp_droprole @rolename = N'DatabaseMailUserRole' EXECUTE msdb.dbo.sp_addrole @rolename = N'DatabaseMailUserRole' END END ELSE EXECUTE msdb.dbo.sp_addrole @rolename = N'DatabaseMailUserRole' go GRANT EXECUTE ON [dbo].[sp_send_dbmail] TO DatabaseMailUserRole GRANT EXECUTE ON [dbo].[sysmail_help_status_sp] TO DatabaseMailUserRole GRANT EXECUTE ON [dbo].[sysmail_delete_mailitems_sp] TO DatabaseMailUserRole GRANT SELECT ON [dbo].[sysmail_allitems] TO DatabaseMailUserRole GRANT SELECT ON [dbo].[sysmail_sentitems] TO DatabaseMailUserRole GRANT SELECT ON [dbo].[sysmail_unsentitems] TO DatabaseMailUserRole GRANT SELECT ON [dbo].[sysmail_faileditems] TO DatabaseMailUserRole GRANT SELECT ON [dbo].[sysmail_mailattachments] TO DatabaseMailUserRole GRANT SELECT ON [dbo].[sysmail_event_log] TO DatabaseMailUserRole go /*************************************************************************/ /* */ /* Database Mail SSB objects (Messages, Contracts, Queues, Services) */ /* */ /*************************************************************************/ PRINT '' PRINT 'Dropping Database Mail MESSAGES, CONTRACTS, QUEUES AND SERVICES...' PRINT '' -- Drop service InternalMailService if existing. IF EXISTS (SELECT * FROM sys.services WHERE name ='InternalMailService') BEGIN PRINT 'Dropping SERVICE InternalMailService' DROP SERVICE InternalMailService; END -- Drop service ExternalMailService if existing. IF EXISTS (SELECT * FROM sys.services WHERE name ='ExternalMailService') BEGIN PRINT 'Dropping SERVICE ExternalMailService' DROP SERVICE ExternalMailService; END -- Drop queue InternalMailQueue if existing. IF EXISTS (SELECT * FROM sys.objects WHERE name = 'InternalMailQueue' AND type = 'SQ') BEGIN PRINT 'Dropping QUEUE InternalMailQueue' DROP QUEUE InternalMailQueue; END -- Drop queue ExternalMailQueue if existing. IF EXISTS (SELECT * FROM sys.objects WHERE name = 'ExternalMailQueue' AND type = 'SQ') BEGIN PRINT 'Dropping QUEUE ExternalMailQueue' DROP QUEUE ExternalMailQueue; END --Drop Notification service for activation of DatabaseMail.exe IF EXISTS (SELECT * FROM sys.services WHERE name ='SQL/Notifications/SysMailNotification/v1.0') BEGIN PRINT 'Dropping SERVICE [SQL/Notifications/SysMailNotification/v1.0]' DROP SERVICE [SQL/Notifications/SysMailNotification/v1.0]; END --Drop SysMailNotificationQueue if existing IF EXISTS (SELECT * FROM sys.objects WHERE name = 'SysMailNotificationQueue' AND type = 'SQ') BEGIN PRINT 'Dropping QUEUE SysMailNotificationQueue' DROP QUEUE SysMailNotificationQueue; END -- Drop SendMail v1.0 contract if existing. IF EXISTS(SELECT * FROM sys.service_contracts WHERE name = '//www.microsoft.com/databasemail/contracts/SendMail/v1.0') BEGIN PRINT 'Dropping CONTRACT [//www.microsoft.com/databasemail/contracts/SendMail/v1.0]' DROP CONTRACT [//www.microsoft.com/databasemail/contracts/SendMail/v1.0]; END -- Drop SendMail message type if existing. IF EXISTS(SELECT * FROM sys.service_message_types WHERE name = '{//www.microsoft.com/databasemail/messages}SendMail') BEGIN PRINT 'Dropping MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMail]' DROP MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMail]; END -- Drop SendMailStatus message type if existing. IF EXISTS(SELECT * FROM sys.service_message_types WHERE name = '{//www.microsoft.com/databasemail/messages}SendMailStatus') BEGIN PRINT 'Dropping MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMailStatus]' DROP MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMailStatus]; END GO ------------------------------------------------------------------- -- Create Database Mail MESSAGES, CONTRACTS, QUEUES AND SERVICES ------------------------------------------------------------------- PRINT '' PRINT 'Creating MESSAGES, CONTRACTS, QUEUES AND SERVICES...' PRINT '' -- Create SendMail message type. PRINT 'Creating MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMail]' CREATE MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMail] VALIDATION = NONE CREATE MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMailStatus] VALIDATION = NONE -- Create SendMail contract. PRINT 'Creating CONTRACT [//www.microsoft.com/databasemail/contracts/SendMail/v1.0]' CREATE CONTRACT [//www.microsoft.com/databasemail/contracts/SendMail/v1.0] ( [{//www.microsoft.com/databasemail/messages}SendMail] SENT BY INITIATOR, [{//www.microsoft.com/databasemail/messages}SendMailStatus] SENT BY TARGET ) -- Create InternalMailQueue queue. PRINT 'Creating QUEUE InternalMailQueue' CREATE QUEUE InternalMailQueue WITH ACTIVATION (PROCEDURE_NAME = sp_ExternalMailQueueListener, MAX_QUEUE_READERS = 1, EXECUTE AS SELF); -- Create ExternalMailQueue queue. PRINT 'Creating QUEUE ExternalMailQueue' CREATE QUEUE ExternalMailQueue WITH ACTIVATION (PROCEDURE_NAME = sp_sysmail_activate, MAX_QUEUE_READERS = 1, EXECUTE AS SELF); -- Create InternalMailService service. PRINT 'Creating SERVICE InternalMailService ON QUEUE InternalMailQueue' CREATE SERVICE InternalMailService ON QUEUE InternalMailQueue ( [//www.microsoft.com/databasemail/contracts/SendMail/v1.0] -- ,[//www.microsoft.com/databasemail/contracts/TestProfile/v1.0] ); -- Create ExternalMailService service. PRINT 'Creating SERVICE ExternalMailService ON QUEUE ExternalMailQueue' CREATE SERVICE ExternalMailService ON QUEUE ExternalMailQueue ( [//www.microsoft.com/databasemail/contracts/SendMail/v1.0] -- ,[//www.microsoft.com/databasemail/contracts/TestProfile/v1.0] ); GO /**************************************************************/ /* */ /* M A I N T E N A N C E P L A N S */ /* */ /**************************************************************/ /**************************************************************/ /* sp_maintplan_delete_log */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_maintplan_delete_log...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_maintplan_delete_log') AND (type = 'P'))) DROP PROCEDURE sp_maintplan_delete_log go CREATE PROCEDURE sp_maintplan_delete_log @plan_id UNIQUEIDENTIFIER = NULL, @subplan_id UNIQUEIDENTIFIER = NULL, @oldest_time DATETIME = NULL AS BEGIN -- @plan_id and @subplan_id must be both NULL or only one exclusively set IF (@plan_id IS NOT NULL) AND (@subplan_id IS NOT NULL) BEGIN RAISERROR(12980, -1, -1, '@plan_id', '@subplan_id') RETURN(1) END --Scenario 1: User wants to delete all logs --Scenario 2: User wants to delete all logs older than X date --Scenario 3: User wants to delete all logs for a given plan --Scenario 4: User wants to delete all logs for a specific subplan --Scenario 5: User wants to delete all logs for a given plan older than X date --Scenario 6: User wants to delete all logs for a specific subplan older than X date -- Special case 1: Delete all logs IF (@plan_id IS NULL) AND (@subplan_id IS NULL) AND (@oldest_time IS NULL) BEGIN DELETE msdb.dbo.sysmaintplan_logdetail DELETE msdb.dbo.sysmaintplan_log RETURN (0) END DELETE msdb.dbo.sysmaintplan_log WHERE ( task_detail_id in (SELECT task_detail_id FROM msdb.dbo.sysmaintplan_log WHERE ((@plan_id IS NULL) OR (plan_id = @plan_id)) AND ((@subplan_id IS NULL) OR (subplan_id = @subplan_id)) AND ((@oldest_time IS NULL) OR (start_time < @oldest_time))) ) RETURN (0) END GO /**************************************************************/ /* sp_maintplan_delete_subplan */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_maintplan_delete_subplan...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_maintplan_delete_subplan') AND (type = 'P'))) DROP PROCEDURE sp_maintplan_delete_subplan go CREATE PROCEDURE sp_maintplan_delete_subplan @subplan_id UNIQUEIDENTIFIER, @delete_jobs BIT = 1 AS BEGIN DECLARE @retval INT DECLARE @job UNIQUEIDENTIFIER DECLARE @jobMsx UNIQUEIDENTIFIER SET NOCOUNT ON SET @retval = 0 -- Raise an error if the @subplan_id doesn't exist IF( NOT EXISTS(SELECT * FROM sysmaintplan_subplans WHERE subplan_id = @subplan_id)) BEGIN DECLARE @subplan_id_as_char VARCHAR(36) SELECT @subplan_id_as_char = CONVERT(VARCHAR(36), @subplan_id) RAISERROR(14262, -1, -1, '@subplan_id', @subplan_id_as_char) RETURN(1) END BEGIN TRAN --Is there an Agent Job/Schedule associated with this subplan? SELECT @job = job_id, @jobMsx = msx_job_id FROM msdb.dbo.sysmaintplan_subplans WHERE subplan_id = @subplan_id EXEC @retval = msdb.dbo.sp_maintplan_delete_log @subplan_id = @subplan_id IF (@retval <> 0) BEGIN ROLLBACK TRAN RETURN @retval END -- Delete the subplans table entry first since it has a foreign -- key constraint on its job_id existing in sysjobs. DELETE msdb.dbo.sysmaintplan_subplans WHERE (subplan_id = @subplan_id) IF (@delete_jobs = 1) BEGIN --delete the local job associated with this subplan IF (@job IS NOT NULL) BEGIN EXEC @retval = msdb.dbo.sp_delete_job @job_id = @job, @delete_unused_schedule = 1 IF (@retval <> 0) BEGIN ROLLBACK TRAN RETURN @retval END END --delete the multi-server job associated with this subplan. IF (@jobMsx IS NOT NULL) BEGIN EXEC @retval = msdb.dbo.sp_delete_job @job_id = @jobMsx, @delete_unused_schedule = 1 IF (@retval <> 0) BEGIN ROLLBACK TRAN RETURN @retval END END END COMMIT TRAN RETURN (0) END go /**************************************************************/ /* SP_MAINTPLAN_UPDATE_SUBPLAN_TSX */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_maintplan_update_subplan_tsx...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_maintplan_update_subplan_tsx') AND (type = 'P'))) DROP PROCEDURE sp_maintplan_update_subplan_tsx go -- This procedure is called when a maintenance plan subplan record -- needs to be created or updated to match a multi-server Agent job -- that has arrived from the master server. CREATE PROCEDURE sp_maintplan_update_subplan_tsx @subplan_id UNIQUEIDENTIFIER, @plan_id UNIQUEIDENTIFIER, @name sysname, @description NVARCHAR(512), @job_id UNIQUEIDENTIFIER AS BEGIN -- Find out what schedule, if any, is associated with the job. declare @schedule_id int select @schedule_id = (SELECT TOP(1) schedule_id FROM msdb.dbo.sysjobschedules WHERE (job_id = @job_id) ) exec sp_maintplan_update_subplan @subplan_id, @plan_id, @name, @description, @job_id, @schedule_id, @allow_create=1 -- Be sure to mark this subplan as coming from the master, not locally. update sysmaintplan_subplans set msx_plan = 1 where subplan_id = @subplan_id END go /**************************************************************/ /* SP_MAINTPLAN_SUBPLANS_BY_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_maintplan_subplans_by_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_maintplan_subplans_by_job') AND (type = 'P'))) DROP PROCEDURE sp_maintplan_subplans_by_job go -- If the given job_id is associated with a maintenance plan, -- then matching entries from sysmaintplan_subplans are returned. CREATE PROCEDURE sp_maintplan_subplans_by_job @job_id UNIQUEIDENTIFIER AS BEGIN select plans.name as 'plan_name', plans.id as 'plan_id', subplans.subplan_name, subplans.subplan_id from sysmaintplan_plans plans, sysmaintplan_subplans subplans where plans.id = subplans.plan_id and (job_id = @job_id or msx_job_id = @job_id) order by subplans.plan_id, subplans.subplan_id END go /**************************************************************/ /* sp_maintplan_open_logentry */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_maintplan_open_logentry...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_maintplan_open_logentry') AND (type = 'P'))) DROP PROCEDURE sp_maintplan_open_logentry go CREATE PROCEDURE sp_maintplan_open_logentry @plan_id UNIQUEIDENTIFIER, @subplan_id UNIQUEIDENTIFIER, @start_time DATETIME = NULL, @task_detail_id UNIQUEIDENTIFIER = NULL OUTPUT AS BEGIN --Set defaults IF (@start_time IS NULL) BEGIN SELECT @start_time = GETDATE() END SELECT @task_detail_id = NEWID() --Insert a new record into sysmaintplan_log table INSERT INTO msdb.dbo.sysmaintplan_log(task_detail_id, plan_id, subplan_id, start_time) VALUES(@task_detail_id, @plan_id, @subplan_id, @start_time) RETURN (@@ERROR) END GO /**************************************************************/ /* sp_maintplan_close_logentry */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_maintplan_close_logentry...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_maintplan_close_logentry') AND (type = 'P'))) DROP PROCEDURE sp_maintplan_close_logentry go CREATE PROCEDURE sp_maintplan_close_logentry @task_detail_id UNIQUEIDENTIFIER, @end_time DATETIME = NULL, @succeeded TINYINT AS BEGIN --Set defaults IF (@end_time IS NULL) BEGIN SELECT @end_time = GETDATE() END -- Raise an error if the @task_detail_id doesn't exist IF( NOT EXISTS(SELECT * FROM sysmaintplan_log WHERE (task_detail_id = @task_detail_id))) BEGIN DECLARE @task_detail_id_as_char VARCHAR(36) SELECT @task_detail_id_as_char = CONVERT(VARCHAR(36), @task_detail_id) RAISERROR(14262, -1, -1, '@task_detail_id', @task_detail_id_as_char) RETURN(1) END UPDATE msdb.dbo.sysmaintplan_log SET end_time = @end_time, succeeded = @succeeded WHERE (task_detail_id = @task_detail_id) RETURN (@@ERROR) END go /**************************************************************/ /* sp_maintplan_update_log */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_maintplan_update_log...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_maintplan_update_log') AND (type = 'P'))) DROP PROCEDURE sp_maintplan_update_log go CREATE PROCEDURE sp_maintplan_update_log --Updates the log_details table @task_detail_id UNIQUEIDENTIFIER, --Required @Line1 NVARCHAR(256), --Required @Line2 NVARCHAR(256) = NULL, @Line3 NVARCHAR(256) = NULL, @Line4 NVARCHAR(256) = NULL, @Line5 NVARCHAR(256) = NULL, @server_name sysname, --Required @succeeded TINYINT, --Required @start_time DATETIME, --Required @end_time DATETIME, --Required @error_number int=NULL, @error_message NVARCHAR(max) = NULL, @command NVARCHAR(max) = NULL AS BEGIN --Prep strings SET NOCOUNT ON SELECT @Line1 = LTRIM(RTRIM(@Line1)) SELECT @Line2 = LTRIM(RTRIM(@Line2)) SELECT @Line3 = LTRIM(RTRIM(@Line3)) SELECT @Line4 = LTRIM(RTRIM(@Line4)) SELECT @Line5 = LTRIM(RTRIM(@Line5)) INSERT INTO msdb.dbo.sysmaintplan_logdetail( task_detail_id, line1, line2, line3, line4, line5, server_name, start_time, end_time, error_number, error_message, command, succeeded) VALUES( @task_detail_id, @Line1, @Line2, @Line3, @Line4, @Line5, @server_name, @start_time, @end_time, @error_number, @error_message, @command, @succeeded) RETURN (@@ERROR) END GO /**************************************************************/ /* sp_maintplan_update_subplan */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_maintplan_update_subplan...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_maintplan_update_subplan') AND (type = 'P'))) DROP PROCEDURE sp_maintplan_update_subplan go CREATE PROCEDURE sp_maintplan_update_subplan @subplan_id UNIQUEIDENTIFIER, @plan_id UNIQUEIDENTIFIER = NULL, @name sysname = NULL, @description NVARCHAR(512) = NULL, @job_id UNIQUEIDENTIFIER = NULL, @schedule_id INT = NULL, @allow_create BIT = 0, @msx_job_id UNIQUEIDENTIFIER = NULL AS BEGIN SET NOCOUNT ON SELECT @name = LTRIM(RTRIM(@name)) SELECT @description = LTRIM(RTRIM(@description)) --Are we creating a new entry or updating an existing one? IF( NOT EXISTS(SELECT * FROM msdb.dbo.sysmaintplan_subplans WHERE subplan_id = @subplan_id) ) BEGIN -- Only allow creation of a record if user permits it IF(@allow_create = 0) BEGIN DECLARE @subplan_id_as_char VARCHAR(36) SELECT @subplan_id_as_char = CONVERT(VARCHAR(36), @subplan_id) RAISERROR(14262, -1, -1, '@subplan_id', @subplan_id_as_char) RETURN(1) END --Insert it's a new subplan IF (@name IS NULL) BEGIN RAISERROR(12981, -1, -1, '@name') RETURN(1) -- Failure END IF (@plan_id IS NULL) BEGIN RAISERROR(12981, -1, -1, '@plan_id') RETURN(1) -- Failure END INSERT INTO msdb.dbo.sysmaintplan_subplans( subplan_id, plan_id, subplan_description, subplan_name, job_id, schedule_id, msx_job_id) VALUES( @subplan_id, @plan_id, @description, @name, @job_id, @schedule_id, @msx_job_id) END ELSE BEGIN --Update the table DECLARE @s_subplan_name sysname DECLARE @s_job_id UNIQUEIDENTIFIER SELECT @s_subplan_name = subplan_name, @s_job_id = job_id FROM msdb.dbo.sysmaintplan_subplans WHERE (@subplan_id = subplan_id) --Determine if user wants to change these variables IF (@name IS NOT NULL) SELECT @s_subplan_name = @name IF (@job_id IS NOT NULL) SELECT @s_job_id = @job_id --UPDATE the record UPDATE msdb.dbo.sysmaintplan_subplans SET subplan_name = @s_subplan_name, subplan_description = @description, job_id = @s_job_id, schedule_id = @schedule_id, msx_job_id = @msx_job_id WHERE (subplan_id = @subplan_id) END RETURN (@@ERROR) END GO /**************************************************************/ /* sp_maintplan_delete_plan */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_maintplan_delete_plan...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_maintplan_delete_plan') AND (type = 'P'))) DROP PROCEDURE sp_maintplan_delete_plan go CREATE PROCEDURE sp_maintplan_delete_plan @plan_id UNIQUEIDENTIFIER AS BEGIN SET NOCOUNT ON DECLARE @sp_id UNIQUEIDENTIFIER DECLARE @retval INT SET @retval = 0 --Loop through Subplans DECLARE sp CURSOR LOCAL FOR SELECT subplan_id FROM msdb.dbo.sysmaintplan_subplans WHERE plan_id = @plan_id FOR READ ONLY OPEN sp FETCH NEXT FROM sp INTO @sp_id WHILE @@FETCH_STATUS = 0 BEGIN EXECUTE @retval = sp_maintplan_delete_subplan @subplan_id = @sp_id IF(@retval <> 0) BREAK FETCH NEXT FROM sp INTO @sp_id END CLOSE sp DEALLOCATE sp RETURN (@retval) END go /**************************************************************/ /* sp_maintplan_start */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_maintplan_start...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_maintplan_start') AND (type = 'P'))) DROP PROCEDURE sp_maintplan_start go CREATE PROCEDURE sp_maintplan_start @plan_id UNIQUEIDENTIFIER = NULL, @subplan_id UNIQUEIDENTIFIER = NULL AS BEGIN SET NOCOUNT ON DECLARE @jobid UNIQUEIDENTIFIER DECLARE @retval INT SET @retval = 0 -- A @plan_id or @subplan_id must be supplied IF (@plan_id IS NULL) AND (@subplan_id IS NULL) BEGIN RAISERROR(12982, -1, -1, '@plan_id', '@subplan_id') RETURN(1) END -- either @plan_id or @subplan_id must be exclusively set IF (@plan_id IS NOT NULL) AND (@subplan_id IS NOT NULL) BEGIN RAISERROR(12982, -1, -1, '@plan_id', '@subplan_id') RETURN(1) END IF (@subplan_id IS NOT NULL) BEGIN -- subplan_id supplied so simply start the subplan's job SELECT @jobid = job_id FROM msdb.dbo.sysmaintplan_subplans WHERE subplan_id = @subplan_id if(@jobid IS NOT NULL) BEGIN EXEC @retval = msdb.dbo.sp_start_job @job_id = @jobid END END ELSE BEGIN -- Loop through Subplans and fire off all associated jobs DECLARE spj CURSOR LOCAL FOR SELECT job_id FROM msdb.dbo.sysmaintplan_subplans WHERE plan_id = @plan_id FOR READ ONLY OPEN spj FETCH NEXT FROM spj INTO @jobid WHILE (@@FETCH_STATUS = 0) BEGIN EXEC @retval = msdb.dbo.sp_start_job @job_id = @jobid IF(@retval <> 0) BREAK FETCH NEXT FROM spj INTO @jobid END CLOSE spj DEALLOCATE spj END RETURN (@retval) END GO /**************************************************************/ /* sp_get_script */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_script...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_get_script') AND (type = 'P'))) DROP PROCEDURE sp_get_script go CREATE PROCEDURE sp_get_script @name sysname AS BEGIN exec master.dbo.xp_get_script @name END GO /*==================================================================*/ --TODO: The following SYSDBMAINT... tables and SP's will be removed /*==================================================================*/ /**************************************************************/ /* SYSDBMAINTPLANS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdbmaintplans') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysdbmaintplans...' CREATE TABLE sysdbmaintplans ( plan_id UNIQUEIDENTIFIER NOT NULL PRIMARY KEY CLUSTERED, plan_name sysname NOT NULL, date_created DATETIME NOT NULL DEFAULT (GETDATE()), owner sysname NOT NULL DEFAULT (ISNULL(NT_CLIENT(), SUSER_SNAME())), max_history_rows INT NOT NULL DEFAULT (0), remote_history_server sysname NOT NULL DEFAULT (''), max_remote_history_rows INT NOT NULL DEFAULT (0), user_defined_1 INT NULL, user_defined_2 NVARCHAR(100) NULL, user_defined_3 DATETIME NULL, user_defined_4 UNIQUEIDENTIFIER NULL ) END go -- Add row for "plan 0" IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysdbmaintplans WHERE (plan_id = CONVERT(UNIQUEIDENTIFIER, 0x00)))) INSERT INTO sysdbmaintplans(plan_id, plan_name, owner) VALUES (0x00, N'All ad-hoc plans', N'sa') go /**************************************************************/ /* SYSDBMAINTPLAN_JOBS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdbmaintplan_jobs') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysdbmaintplan_jobs...' CREATE TABLE sysdbmaintplan_jobs ( plan_id UNIQUEIDENTIFIER NOT NULL UNIQUE CLUSTERED (plan_id, job_id) FOREIGN KEY REFERENCES msdb.dbo.sysdbmaintplans (plan_id), job_id UNIQUEIDENTIFIER NOT NULL ) END go /**************************************************************/ /* SYSDBMAINTPLAN_DATABASES */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdbmaintplan_databases') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysdbmaintplan_databases...' CREATE TABLE sysdbmaintplan_databases ( plan_id UNIQUEIDENTIFIER NOT NULL UNIQUE CLUSTERED (plan_id, database_name) FOREIGN KEY REFERENCES msdb.dbo.sysdbmaintplans (plan_id), database_name sysname NOT NULL ) END go /**************************************************************/ /* SYSDBMAINTPLAN_HISTORY */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdbmaintplan_history') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysdbmaintplan_history...' CREATE TABLE sysdbmaintplan_history ( sequence_id INT NOT NULL IDENTITY UNIQUE NONCLUSTERED, plan_id UNIQUEIDENTIFIER NOT NULL DEFAULT('00000000-0000-0000-0000-000000000000'), plan_name sysname NOT NULL DEFAULT('All ad-hoc plans'), database_name sysname NULL, server_name sysname NOT NULL DEFAULT (CONVERT(sysname, ServerProperty('ServerName'))), activity NVARCHAR(128) NULL, succeeded BIT NOT NULL DEFAULT (1), end_time DATETIME NOT NULL DEFAULT (GETDATE()), duration INT NULL DEFAULT (0), start_time AS DATEADD (ss, -duration, end_time), error_number INT NOT NULL DEFAULT (0), message NVARCHAR(512) NULL ) CREATE CLUSTERED INDEX clust ON sysdbmaintplan_history(plan_id) END -- ALTER TABLE to correct default constraint ELSE BEGIN DECLARE @t TABLE ( constraint_type NVARCHAR(146) COLLATE database_default NULL, constraint_name sysname COLLATE database_default NULL, delete_action NVARCHAR(20) COLLATE database_default NULL, update_action NVARCHAR(20) COLLATE database_default NULL, status_enabled NVARCHAR(20) COLLATE database_default NULL, status_for_replication NVARCHAR(20) COLLATE database_default NULL, constraint_keys NVARCHAR(2126) COLLATE database_default NULL ) INSERT INTO @t EXEC sp_helpconstraint N'sysdbmaintplan_history', 'nomsg' DECLARE @constraint_name sysname DECLARE @sql NVARCHAR(4000) SELECT @constraint_name = constraint_name FROM @t WHERE constraint_type = N'DEFAULT on column server_name' AND constraint_keys = N'(@@servername)' -- default found IF (@constraint_name IS NOT NULL) BEGIN PRINT '' PRINT 'Alter sysdbmaintplan_history ...' SELECT @sql = N'ALTER TABLE sysdbmaintplan_history DROP CONSTRAINT ' + @constraint_name EXEC (@sql) ALTER TABLE sysdbmaintplan_history ADD CONSTRAINT servername_default DEFAULT (CONVERT(sysname, ServerProperty('ServerName'))) FOR server_name END END go /**************************************************************/ /* SPs for the maintenance plans */ /**************************************************************/ /**************************************************************/ /* sp_clear_dbmaintplan_by_db */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_clear_dbmaintplan_by_db...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_clear_dbmaintplan_by_db') AND (type = 'P'))) DROP PROCEDURE sp_clear_dbmaintplan_by_db GO CREATE PROCEDURE sp_clear_dbmaintplan_by_db @db_name sysname AS BEGIN DECLARE planid_cursor CURSOR FOR select plan_id from msdb.dbo.sysdbmaintplan_databases where database_name=@db_name OPEN planid_cursor declare @planid uniqueidentifier FETCH NEXT FROM planid_cursor INTO @planid WHILE (@@FETCH_STATUS <> -1) BEGIN IF (@@FETCH_STATUS <> -2) BEGIN delete from msdb.dbo.sysdbmaintplan_databases where plan_id=@planid AND database_name=@db_name if (NOT EXISTS(select * from msdb.dbo.sysdbmaintplan_databases where plan_id=@planid)) BEGIN --delete the job DECLARE jobid_cursor CURSOR FOR select job_id from msdb.dbo.sysdbmaintplan_jobs where plan_id=@planid OPEN jobid_cursor DECLARE @jobid uniqueidentifier FETCH NEXT FROM jobid_cursor INTO @jobid WHILE (@@FETCH_STATUS <> -1) BEGIN if (@@FETCH_STATUS <> -2) BEGIN execute msdb.dbo.sp_delete_job @jobid END FETCH NEXT FROM jobid_cursor into @jobid END CLOSE jobid_cursor DEALLOCATE jobid_cursor --delete the history delete from msdb.dbo.sysdbmaintplan_history where plan_id=@planid --delete the plan delete from msdb.dbo.sysdbmaintplans where plan_id=@planid END END FETCH NEXT FROM planid_cursor INTO @planid END CLOSE planid_cursor DEALLOCATE planid_cursor END GO /**************************************************************/ /* sp_add_maintenance_plan */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_maintenance_plan...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_maintenance_plan') AND (type = 'P'))) DROP PROCEDURE sp_add_maintenance_plan GO CREATE PROCEDURE sp_add_maintenance_plan @plan_name varchar(128), @plan_id UNIQUEIDENTIFIER OUTPUT AS BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysdbmaintplans WHERE plan_name=@plan_name)) BEGIN SELECT @plan_id=NEWID() INSERT INTO msdb.dbo.sysdbmaintplans (plan_id, plan_name) VALUES (@plan_id, @plan_name) END ELSE BEGIN RAISERROR(14261,-1,-1,'@plan_name',@plan_name) RETURN(1) -- failure END END GO /**************************************************************/ /* sp_delete_maintenance_plan */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_maintenance_plan...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_maintenance_plan') AND (type = 'P'))) DROP PROCEDURE sp_delete_maintenance_plan GO CREATE PROCEDURE sp_delete_maintenance_plan @plan_id UNIQUEIDENTIFIER AS BEGIN /*check if the plan_id is valid*/ IF (NOT EXISTS(SELECT * FROM sysdbmaintplans WHERE plan_id=@plan_id)) BEGIN DECLARE @syserr VARCHAR(100) SELECT @syserr=CONVERT(VARCHAR(100),@plan_id) RAISERROR(14262,-1,-1,'@plan_id',@syserr) RETURN(1) END /* clean the related records in sysdbmaintplan_database */ DELETE FROM msdb.dbo.sysdbmaintplan_databases WHERE plan_id=@plan_id /* clean the related records in sysdbmaintplan_jobs*/ DELETE FROM msdb.dbo.sysdbmaintplan_jobs WHERE plan_id=@plan_id /* clean sysdbmaintplans */ DELETE FROM msdb.dbo.sysdbmaintplans WHERE plan_id= @plan_id END GO /**************************************************************/ /* sp_add_maintenance_plan_db */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_maintenance_plan_db...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_maintenance_plan_db') AND (type = 'P'))) DROP PROCEDURE sp_add_maintenance_plan_db GO CREATE PROCEDURE sp_add_maintenance_plan_db @plan_id UNIQUEIDENTIFIER, @db_name sysname AS BEGIN DECLARE @syserr VARCHAR(100) /*check if the plan_id is valid */ IF (NOT EXISTS (SELECT plan_id FROM msdb.dbo.sysdbmaintplans WHERE plan_id=@plan_id)) BEGIN SELECT @syserr=CONVERT(VARCHAR(100),@plan_id) RAISERROR(14262,-1,-1,'@plan_id',@syserr) RETURN(1) END /*check if the database name is valid */ IF (NOT EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE name=@db_name)) BEGIN RAISERROR(14262,-1,-1,'@db_name',@db_name) RETURN(1) END /*check if the (plan_id, database) pair already exists*/ IF (EXISTS (SELECT * FROM sysdbmaintplan_databases WHERE plan_id=@plan_id AND database_name=@db_name)) BEGIN SELECT @syserr=CONVERT(VARCHAR(100),@plan_id)+' + '+@db_name RAISERROR(14261,-1,-1,'@plan_id+@db_name',@syserr) RETURN(1) END INSERT INTO msdb.dbo.sysdbmaintplan_databases (plan_id,database_name) VALUES (@plan_id, @db_name) END GO /**************************************************************/ /* sp_delete_maintenance_plan_db */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_maintenance_plan_db...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_maintenance_plan_db') AND (type = 'P'))) DROP PROCEDURE sp_delete_maintenance_plan_db go CREATE PROCEDURE sp_delete_maintenance_plan_db @plan_id uniqueidentifier, @db_name sysname AS BEGIN /*check if the (plan_id, db_name) exists in the table*/ IF (NOT EXISTS(SELECT * FROM msdb.dbo.sysdbmaintplan_databases WHERE @plan_id=plan_id AND @db_name=database_name)) BEGIN DECLARE @syserr VARCHAR(300) SELECT @syserr=CONVERT(VARCHAR(100),@plan_id)+' + '+@db_name RAISERROR(14262,-1,-1,'@plan_id+@db_name',@syserr) RETURN(1) END /*delete the pair*/ DELETE FROM msdb.dbo.sysdbmaintplan_databases WHERE plan_id=@plan_id AND database_name=@db_name END GO /**************************************************************/ /* sp_add_maintenance_plan_job */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_maintenance_plan_job...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_maintenance_plan_job') AND (type = 'P'))) DROP PROCEDURE sp_add_maintenance_plan_job GO CREATE PROCEDURE sp_add_maintenance_plan_job @plan_id UNIQUEIDENTIFIER, @job_id UNIQUEIDENTIFIER AS BEGIN DECLARE @syserr varchar(100) /*check if the @plan_id is valid*/ IF (NOT EXISTS(SELECT plan_id FROM msdb.dbo.sysdbmaintplans WHERE plan_id=@plan_id)) BEGIN SELECT @syserr=CONVERT(VARCHAR(100),@plan_id) RAISERROR(14262,-1,-1,'@plan_id',@syserr) RETURN(1) END /*check if the @job_id is valid*/ IF (NOT EXISTS(SELECT job_id FROM msdb.dbo.sysjobs WHERE job_id=@job_id)) BEGIN SELECT @syserr=CONVERT(VARCHAR(100),@job_id) RAISERROR(14262,-1,-1,'@job_id',@syserr) RETURN(1) END /*check if the job has at least one step calling xp_sqlmaint*/ DECLARE @maxind INT SELECT @maxind=(SELECT MAX(CHARINDEX('xp_sqlmaint', command)) FROM msdb.dbo.sysjobsteps WHERE @job_id=job_id) IF (@maxind<=0) BEGIN /*print N'Warning: The job is not for maitenance plan.' -- will add the new sysmessage here*/ SELECT @syserr=CONVERT(VARCHAR(100),@job_id) RAISERROR(14199,-1,-1,@syserr) RETURN(1) END INSERT INTO msdb.dbo.sysdbmaintplan_jobs(plan_id,job_id) VALUES (@plan_id, @job_id) --don't have to check duplicate here END GO /**************************************************************/ /* sp_delete_maintenance_plan_job */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_maintenance_plan_job...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_maintenance_plan_job') AND (type = 'P'))) DROP PROCEDURE sp_delete_maintenance_plan_job GO CREATE PROCEDURE sp_delete_maintenance_plan_job @plan_id uniqueidentifier, @job_id uniqueidentifier AS BEGIN /*check if the (plan_id, job_id) exists*/ IF (NOT EXISTS(SELECT * FROM sysdbmaintplan_jobs WHERE @plan_id=plan_id AND @job_id=job_id)) BEGIN DECLARE @syserr VARCHAR(300) SELECT @syserr=CONVERT(VARCHAR(100),@plan_id)+' + '+CONVERT(VARCHAR(100),@job_id) RAISERROR(14262,-1,-1,'@plan_id+@job_id',@syserr) RETURN(1) END DELETE FROM msdb.dbo.sysdbmaintplan_jobs WHERE plan_id=@plan_id AND job_id=@job_id END GO /**************************************************************/ /* sp_help_maintenance_plan */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_maintenance_plan...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_maintenance_plan') AND (type = 'P'))) DROP PROCEDURE sp_help_maintenance_plan GO CREATE PROCEDURE sp_help_maintenance_plan @plan_id UNIQUEIDENTIFIER = NULL AS BEGIN IF (@plan_id IS NOT NULL) BEGIN /*return the information about the plan itself*/ SELECT * FROM msdb.dbo.sysdbmaintplans WHERE plan_id=@plan_id /*return the information about databases this plan defined on*/ SELECT database_name FROM msdb.dbo.sysdbmaintplan_databases WHERE plan_id=@plan_id /*return the information about the jobs that relating to the plan*/ SELECT job_id FROM msdb.dbo.sysdbmaintplan_jobs WHERE plan_id=@plan_id END ELSE BEGIN SELECT * FROM msdb.dbo.sysdbmaintplans END END GO /**************************************************************/ /* */ /* B A C K U P H I S T O R Y */ /* */ /**************************************************************/ /**************************************************************/ /* sp_delete_database_backuphistory */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_database_backuphistory...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_database_backuphistory') AND (type = 'P'))) DROP PROCEDURE sp_delete_database_backuphistory go CREATE PROCEDURE sp_delete_database_backuphistory @database_name sysname AS BEGIN SET NOCOUNT ON DECLARE @backup_set_id TABLE (backup_set_id INT) DECLARE @media_set_id TABLE (media_set_id INT) DECLARE @restore_history_id TABLE (restore_history_id INT) INSERT INTO @backup_set_id (backup_set_id) SELECT DISTINCT backup_set_id FROM msdb.dbo.backupset WHERE database_name = @database_name INSERT INTO @media_set_id (media_set_id) SELECT DISTINCT media_set_id FROM msdb.dbo.backupset WHERE database_name = @database_name INSERT INTO @restore_history_id (restore_history_id) SELECT DISTINCT restore_history_id FROM msdb.dbo.restorehistory WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) BEGIN TRANSACTION DELETE FROM msdb.dbo.backupfile WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.backupfilegroup WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.restorefile WHERE restore_history_id IN (SELECT restore_history_id FROM @restore_history_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.restorefilegroup WHERE restore_history_id IN (SELECT restore_history_id FROM @restore_history_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.restorehistory WHERE restore_history_id IN (SELECT restore_history_id FROM @restore_history_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.backupset WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) IF (@@error > 0) GOTO Quit DELETE msdb.dbo.backupmediafamily FROM msdb.dbo.backupmediafamily bmf WHERE bmf.media_set_id IN (SELECT media_set_id FROM @media_set_id) AND ((SELECT COUNT(*) FROM msdb.dbo.backupset WHERE media_set_id = bmf.media_set_id) = 0) IF (@@error > 0) GOTO Quit DELETE msdb.dbo.backupmediaset FROM msdb.dbo.backupmediaset bms WHERE bms.media_set_id IN (SELECT media_set_id FROM @media_set_id) AND ((SELECT COUNT(*) FROM msdb.dbo.backupset WHERE media_set_id = bms.media_set_id) = 0) IF (@@error > 0) GOTO Quit COMMIT TRANSACTION RETURN Quit: ROLLBACK TRANSACTION END go /**************************************************************/ /* SP_DELETE_BACKUPHISTORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_backuphistory...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_backuphistory') AND (type = 'P'))) DROP PROCEDURE sp_delete_backuphistory go CREATE PROCEDURE sp_delete_backuphistory @oldest_date datetime AS BEGIN SET NOCOUNT ON DECLARE @backup_set_id TABLE (backup_set_id INT) DECLARE @media_set_id TABLE (media_set_id INT) DECLARE @restore_history_id TABLE (restore_history_id INT) INSERT INTO @backup_set_id (backup_set_id) SELECT DISTINCT backup_set_id FROM msdb.dbo.backupset WHERE backup_finish_date < @oldest_date INSERT INTO @media_set_id (media_set_id) SELECT DISTINCT media_set_id FROM msdb.dbo.backupset WHERE backup_finish_date < @oldest_date INSERT INTO @restore_history_id (restore_history_id) SELECT DISTINCT restore_history_id FROM msdb.dbo.restorehistory WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) BEGIN TRANSACTION DELETE FROM msdb.dbo.backupfile WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.backupfilegroup WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.restorefile WHERE restore_history_id IN (SELECT restore_history_id FROM @restore_history_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.restorefilegroup WHERE restore_history_id IN (SELECT restore_history_id FROM @restore_history_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.restorehistory WHERE restore_history_id IN (SELECT restore_history_id FROM @restore_history_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.backupset WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) IF (@@error > 0) GOTO Quit DELETE msdb.dbo.backupmediafamily FROM msdb.dbo.backupmediafamily bmf WHERE bmf.media_set_id IN (SELECT media_set_id FROM @media_set_id) AND ((SELECT COUNT(*) FROM msdb.dbo.backupset WHERE media_set_id = bmf.media_set_id) = 0) IF (@@error > 0) GOTO Quit DELETE msdb.dbo.backupmediaset FROM msdb.dbo.backupmediaset bms WHERE bms.media_set_id IN (SELECT media_set_id FROM @media_set_id) AND ((SELECT COUNT(*) FROM msdb.dbo.backupset WHERE media_set_id = bms.media_set_id) = 0) IF (@@error > 0) GOTO Quit COMMIT TRANSACTION RETURN Quit: ROLLBACK TRANSACTION END go /**********************************************************************/ /* TABLE : log_shipping_primaries */ /* Populated on the monitor server */ /* */ /**********************************************************************/ IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE (TABLE_NAME = N'log_shipping_primaries'))) BEGIN PRINT '' PRINT 'Creating table log_shipping_primaries...' CREATE TABLE log_shipping_primaries ( primary_id INT IDENTITY NOT NULL PRIMARY KEY, primary_server_name sysname NOT NULL, primary_database_name sysname NOT NULL, maintenance_plan_id UNIQUEIDENTIFIER NULL, backup_threshold INT NOT NULL, threshold_alert INT NOT NULL, threshold_alert_enabled BIT NOT NULL, /* 1 = enabled, 0 = disabled */ last_backup_filename NVARCHAR(500) NULL, last_updated DATETIME NULL, planned_outage_start_time INT NOT NULL, planned_outage_end_time INT NOT NULL, planned_outage_weekday_mask INT NOT NULL, source_directory NVARCHAR(500) NULL ) END ELSE BEGIN IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE (TABLE_NAME = N'log_shipping_primaries') AND (COLUMN_NAME = N'source_directory'))) BEGIN PRINT '' PRINT 'Adding columns to table log_shipping_primaries...' ALTER TABLE log_shipping_primaries ADD source_directory NVARCHAR(500) NULL END END go /**********************************************************************/ /* TABLE : log_shipping_secondaries */ /* Populated on the monitor server */ /* */ /**********************************************************************/ IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE (TABLE_NAME = N'log_shipping_secondaries'))) BEGIN PRINT '' PRINT 'Creating table log_shipping_secondaries...' CREATE TABLE log_shipping_secondaries ( primary_id INT FOREIGN KEY REFERENCES log_shipping_primaries (primary_id), secondary_server_name sysname, secondary_database_name sysname, last_copied_filename NVARCHAR(500), last_loaded_filename NVARCHAR(500), last_copied_last_updated DATETIME, last_loaded_last_updated DATETIME, secondary_plan_id UNIQUEIDENTIFIER, copy_enabled BIT, load_enabled BIT, /* 1 = load enabled, 0 = load disabled */ out_of_sync_threshold INT, threshold_alert INT, threshold_alert_enabled BIT, /*1 = enabled, 0 = disabled */ planned_outage_start_time INT, planned_outage_end_time INT, planned_outage_weekday_mask INT, allow_role_change BIT DEFAULT (0) ) END ELSE BEGIN IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE (TABLE_NAME = N'log_shipping_secondaries') AND (COLUMN_NAME = N'allow_role_change'))) BEGIN PRINT '' PRINT 'Adding columns to table log_shipping_secondaries...' ALTER TABLE log_shipping_secondaries ADD allow_role_change BIT DEFAULT (0) END END go /**************************************************************/ /* sp_add_log_shipping_monitor_jobs */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_log_shipping_monitor_jobs...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_add_log_shipping_monitor_jobs' AND type = N'P') ) drop procedure sp_add_log_shipping_monitor_jobs go CREATE PROCEDURE sp_add_log_shipping_monitor_jobs AS BEGIN SET NOCOUNT ON BEGIN TRANSACTION DECLARE @rv INT DECLARE @backup_job_name sysname SET @backup_job_name = N'Log Shipping Alert Job - Backup' IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @backup_job_name)) BEGIN EXECUTE @rv = msdb.dbo.sp_add_job @job_name = N'Log Shipping Alert Job - Backup' IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error EXECUTE @rv = msdb.dbo.sp_add_jobstep @job_name = N'Log Shipping Alert Job - Backup', @step_id = 1, @step_name = N'Log Shipping Alert - Backup', @command = N'EXECUTE msdb.dbo.sp_log_shipping_monitor_backup', @on_fail_action = 2, @flags = 4, @subsystem = N'TSQL', @on_success_step_id = 0, @on_success_action = 1, @on_fail_step_id = 0 IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error EXECUTE @rv = msdb.dbo.sp_add_jobschedule @job_name = @backup_job_name, @freq_type = 4, @freq_interval = 1, @freq_subday_type = 0x4, @freq_subday_interval = 1, -- run every minute @freq_relative_interval = 0, @name = @backup_job_name IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error EXECUTE @rv = msdb.dbo.sp_add_jobserver @job_name = @backup_job_name, @server_name = NULL IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error END DECLARE @restore_job_name sysname SET @restore_job_name = 'Log Shipping Alert Job - Restore' IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @restore_job_name)) BEGIN EXECUTE @rv = msdb.dbo.sp_add_job @job_name = @restore_job_name IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error EXECUTE @rv = msdb.dbo.sp_add_jobstep @job_name = @restore_job_name, @step_id = 1, @step_name = @restore_job_name, @command = N'EXECUTE msdb.dbo.sp_log_shipping_monitor_restore', @on_fail_action = 2, @flags = 4, @subsystem = N'TSQL', @on_success_step_id = 0, @on_success_action = 1, @on_fail_step_id = 0 IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error EXECUTE @rv = msdb.dbo.sp_add_jobschedule @job_name = @restore_job_name, @freq_type = 4, @freq_interval = 1, @freq_subday_type = 0x4, @freq_subday_interval = 1, -- run every minute @freq_relative_interval = 0, @name = @restore_job_name IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error EXECUTE @rv = msdb.dbo.sp_add_jobserver @job_name = @restore_job_name, @server_name = NULL IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error END COMMIT TRANSACTION RETURN rollback_quit: ROLLBACK TRANSACTION END go /**************************************************************/ /* sp_add_log_shipping_primary */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_log_shipping_primary...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_add_log_shipping_primary' AND type = N'P')) drop procedure sp_add_log_shipping_primary go CREATE PROCEDURE sp_add_log_shipping_primary @primary_server_name sysname, @primary_database_name sysname, @maintenance_plan_id UNIQUEIDENTIFIER = NULL, @backup_threshold INT = 60, @threshold_alert INT = 14420, @threshold_alert_enabled BIT = 1, @planned_outage_start_time INT = 0, @planned_outage_end_time INT = 0, @planned_outage_weekday_mask INT = 0, @primary_id INT = NULL OUTPUT AS BEGIN SET NOCOUNT ON IF EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name) BEGIN DECLARE @pair_name NVARCHAR SELECT @pair_name = @primary_server_name + N'.' + @primary_database_name RAISERROR (14261,16,1, N'primary_server_name.primary_database_name', @pair_name) RETURN (1) -- error END INSERT INTO msdb.dbo.log_shipping_primaries ( primary_server_name, primary_database_name, maintenance_plan_id, backup_threshold, threshold_alert, threshold_alert_enabled, last_backup_filename, last_updated, planned_outage_start_time, planned_outage_end_time, planned_outage_weekday_mask, source_directory) VALUES (@primary_server_name, @primary_database_name, @maintenance_plan_id, @backup_threshold, @threshold_alert, @threshold_alert_enabled, N'first_file_000000000000.trn', GETDATE (), @planned_outage_start_time, @planned_outage_end_time, @planned_outage_weekday_mask, NULL) SELECT @primary_id = @@IDENTITY EXECUTE msdb.dbo.sp_add_log_shipping_monitor_jobs END go /**************************************************************/ /* sp_add_log_shipping_secondary */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_log_shipping_secondary...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_add_log_shipping_secondary' AND type = N'P')) drop procedure sp_add_log_shipping_secondary go CREATE PROCEDURE sp_add_log_shipping_secondary @primary_id INT, @secondary_server_name sysname, @secondary_database_name sysname, @secondary_plan_id UNIQUEIDENTIFIER, @copy_enabled BIT = 1, @load_enabled BIT = 1, @out_of_sync_threshold INT = 60, @threshold_alert INT = 14421, @threshold_alert_enabled BIT = 1, @planned_outage_start_time INT = 0, @planned_outage_end_time INT = 0, @planned_outage_weekday_mask INT = 0, @allow_role_change BIT = 0 AS BEGIN SET NOCOUNT ON IF NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries where primary_id = @primary_id) BEGIN RAISERROR (14262, 16, 1, N'primary_id', N'msdb.dbo.log_shipping_primaries') RETURN(1) END INSERT INTO msdb.dbo.log_shipping_secondaries ( primary_id, secondary_server_name, secondary_database_name, last_copied_filename, last_loaded_filename, last_copied_last_updated, last_loaded_last_updated, secondary_plan_id, copy_enabled, load_enabled, out_of_sync_threshold, threshold_alert, threshold_alert_enabled, planned_outage_start_time, planned_outage_end_time, planned_outage_weekday_mask, allow_role_change) VALUES (@primary_id, @secondary_server_name, @secondary_database_name, N'first_file_000000000000.trn', N'first_file_000000000000.trn', GETDATE (), GETDATE (), @secondary_plan_id, @copy_enabled, @load_enabled, @out_of_sync_threshold, @threshold_alert, @threshold_alert_enabled, @planned_outage_start_time, @planned_outage_end_time, @planned_outage_weekday_mask, @allow_role_change) END go /**************************************************************/ /* sp_delete_log_shipping_monitor_jobs */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_log_shipping_monitor_jobs...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_delete_log_shipping_monitor_jobs' AND type = N'P') ) drop procedure sp_delete_log_shipping_monitor_jobs go CREATE PROCEDURE sp_delete_log_shipping_monitor_jobs AS BEGIN DECLARE @backup_job_name sysname SET NOCOUNT ON SET @backup_job_name = N'Log Shipping Alert Job - Backup' IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @backup_job_name)) EXECUTE msdb.dbo.sp_delete_job @job_name = N'Log Shipping Alert Job - Backup' DECLARE @restore_job_name sysname SET @restore_job_name = 'Log Shipping Alert Job - Restore' IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @restore_job_name)) EXECUTE msdb.dbo.sp_delete_job @job_name = N'Log Shipping Alert Job - Restore' END go /**************************************************************/ /* sp_delete_log_shipping_primary */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_log_shipping_primary...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_delete_log_shipping_primary' AND type = N'P') ) drop procedure sp_delete_log_shipping_primary go CREATE PROCEDURE sp_delete_log_shipping_primary @primary_server_name sysname, @primary_database_name sysname, @delete_secondaries BIT = 0 AS BEGIN DECLARE @primary_id INT SET NOCOUNT ON SELECT @primary_id = primary_id FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name IF (@primary_id IS NULL) RETURN (0) BEGIN TRANSACTION IF (EXISTS (SELECT * FROM msdb.dbo.log_shipping_secondaries WHERE primary_id = @primary_id)) BEGIN IF (@delete_secondaries = 0) BEGIN RAISERROR (14429,-1,-1) goto rollback_quit END DELETE FROM msdb.dbo.log_shipping_secondaries WHERE primary_id = @primary_id IF (@@ERROR <> 0) GOTO rollback_quit END DELETE FROM msdb.dbo.log_shipping_primaries WHERE primary_id = @primary_id IF (@@ERROR <> 0) GOTO rollback_quit COMMIT TRANSACTION DECLARE @i INT SELECT @i = COUNT(*) FROM msdb.dbo.log_shipping_primaries IF (@i=0) EXECUTE msdb.dbo.sp_delete_log_shipping_monitor_jobs RETURN (0) rollback_quit: ROLLBACK TRANSACTION RETURN(1) -- error END go /**************************************************************/ /* sp_delete_log_shipping_secondary */ /**************************************************************/ PRINT '' PRINT 'Creating sp_delete_log_shipping_secondary...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_delete_log_shipping_secondary' AND type = N'P') ) drop procedure sp_delete_log_shipping_secondary go CREATE PROCEDURE sp_delete_log_shipping_secondary @secondary_server_name sysname, @secondary_database_name sysname AS BEGIN SET NOCOUNT ON DELETE FROM msdb.dbo.log_shipping_secondaries WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name END go /**************************************************************/ /* sp_log_shipping_in_sync */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_shipping_in_sync...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_log_shipping_in_sync' AND type = N'P') ) drop procedure sp_log_shipping_in_sync go CREATE PROCEDURE sp_log_shipping_in_sync @last_updated DATETIME, @compare_with DATETIME, @threshold INT, @outage_start_time INT, @outage_end_time INT, @outage_weekday_mask INT, @enabled BIT = 1, @delta INT = NULL OUTPUT AS BEGIN SET NOCOUNT ON DECLARE @cur_time INT SELECT @delta = DATEDIFF (mi, @last_updated, @compare_with) -- in sync IF (@delta <= @threshold) RETURN (0) -- in sync IF (@enabled = 0) RETURN(0) -- in sync IF (@outage_weekday_mask & DATEPART(dw, GETDATE ()) > 0) -- potentially in outage window BEGIN SELECT @cur_time = DATEPART (hh, GETDATE()) * 10000 + DATEPART (mi, GETDATE()) * 100 + DATEPART (ss, GETDATE()) -- outage doesn't span midnight IF (@outage_start_time < @outage_end_time) BEGIN IF (@cur_time >= @outage_start_time AND @cur_time < @outage_end_time) RETURN(1) -- in outage END -- outage does span midnight ELSE IF (@outage_start_time > @outage_end_time) BEGIN IF (@cur_time >= @outage_start_time OR @cur_time < @outage_end_time) RETURN(1) -- in outage END END RETURN(-1 ) -- not in outage, not in sync END go /**************************************************************/ /* sp_log_shipping_get_date_from_file */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_shipping_get_date_from_file...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_log_shipping_get_date_from_file' AND type = N'P') ) drop procedure sp_log_shipping_get_date_from_file go CREATE PROCEDURE sp_log_shipping_get_date_from_file @db_name sysname, @filename NVARCHAR (500), @file_date DATETIME OUTPUT AS BEGIN SET NOCOUNT ON DECLARE @tempname NVARCHAR (500) IF (LEN (@filename) - (LEN(@db_name) + LEN ('_tlog_')) <= 0) RETURN(1) -- filename string isn't long enough SELECT @tempname = RIGHT (@filename, LEN (@filename) - (LEN(@db_name) + LEN ('_tlog_'))) IF (CHARINDEX ('.',@tempname,0) > 0) SELECT @tempname = LEFT (@tempname, CHARINDEX ('.',@tempname,0) - 1) IF (LEN (@tempname) <> 8 AND LEN (@tempname) <> 12) RETURN (1) -- error must be yyyymmddhhmm or yyyymmdd IF (ISNUMERIC (@tempname) = 0 OR CHARINDEX ('.',@tempname,0) <> 0 OR CONVERT (FLOAT,SUBSTRING (@tempname, 1,8)) < 1 ) RETURN (1) -- must be numeric, can't contain any '.' etc SELECT @file_date = CONVERT (DATETIME,SUBSTRING (@tempname, 1,8),112) IF (LEN (@tempname) = 12) BEGIN SELECT @file_date = DATEADD (hh, CONVERT (INT, SUBSTRING (@tempname,9,2)),@file_date) SELECT @file_date = DATEADD (mi, CONVERT (INT, SUBSTRING (@tempname,11,2)),@file_date) END RETURN (0) -- success END go /**************************************************************/ /* sp_get_log_shipping_monitor_info */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_log_shipping_monitor_info...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_get_log_shipping_monitor_info' AND type = N'P') ) drop procedure sp_get_log_shipping_monitor_info go CREATE PROCEDURE sp_get_log_shipping_monitor_info @primary_server_name sysname = N'%', @primary_database_name sysname = N'%', @secondary_server_name sysname = N'%', @secondary_database_name sysname = N'%' AS BEGIN SET NOCOUNT ON DECLARE @lsp TABLE ( primary_server_name sysname COLLATE database_default NOT NULL, primary_database_name sysname COLLATE database_default NOT NULL, secondary_server_name sysname COLLATE database_default NOT NULL, secondary_database_name sysname COLLATE database_default NOT NULL, backup_threshold INT NOT NULL, backup_threshold_alert INT NOT NULL, backup_threshold_alert_enabled BIT NOT NULL, last_backup_filename NVARCHAR(500) COLLATE database_default NOT NULL, last_backup_last_updated DATETIME NOT NULL, backup_outage_start_time INT NOT NULL, backup_outage_end_time INT NOT NULL, backup_outage_weekday_mask INT NOT NULL, backup_in_sync INT NULL, -- 0 = in sync, -1 = out of sync, 1 = in outage window backup_delta INT NULL, last_copied_filename NVARCHAR(500) COLLATE database_default NOT NULL, last_copied_last_updated DATETIME NOT NULL, last_loaded_filename NVARCHAR(500) COLLATE database_default NOT NULL, last_loaded_last_updated DATETIME NOT NULL, copy_delta INT NULL, copy_enabled BIT NOT NULL, load_enabled BIT NOT NULL, out_of_sync_threshold INT NOT NULL, load_threshold_alert INT NOT NULL, load_threshold_alert_enabled BIT NOT NULL, load_outage_start_time INT NOT NULL, load_outage_end_time INT NOT NULL, load_outage_weekday_mask INT NOT NULL, load_in_sync INT NULL, -- 0 = in sync, -1 = out of sync, 1 = in outage window load_delta INT NULL, maintenance_plan_id UNIQUEIDENTIFIER NULL, secondary_plan_id UNIQUEIDENTIFIER NOT NULL) INSERT INTO @lsp SELECT primary_server_name, primary_database_name, secondary_server_name, secondary_database_name, backup_threshold, p.threshold_alert, p.threshold_alert_enabled, last_backup_filename, p.last_updated, p.planned_outage_start_time, p.planned_outage_end_time, p.planned_outage_weekday_mask, NULL, NULL, last_copied_filename, last_copied_last_updated, last_loaded_filename, last_loaded_last_updated, NULL, copy_enabled, load_enabled, out_of_sync_threshold, s.threshold_alert, s.threshold_alert_enabled, s.planned_outage_start_time, s.planned_outage_weekday_mask, s.planned_outage_end_time, NULL, NULL, maintenance_plan_id, secondary_plan_id FROM msdb.dbo.log_shipping_primaries p, msdb.dbo.log_shipping_secondaries s WHERE p.primary_id = s.primary_id AND primary_server_name LIKE @primary_server_name AND primary_database_name LIKE @primary_database_name AND secondary_server_name LIKE @secondary_server_name AND secondary_database_name LIKE @secondary_database_name DECLARE @load_in_sync INT DECLARE @backup_in_sync INT DECLARE @_primary_server_name sysname DECLARE @_primary_database_name sysname DECLARE @_secondary_server_name sysname DECLARE @_secondary_database_name sysname DECLARE @last_loaded_last_updated DATETIME DECLARE @last_loaded_filename NVARCHAR (500) DECLARE @last_copied_filename NVARCHAR (500) DECLARE @last_backup_last_updated DATETIME DECLARE @last_backup_filename NVARCHAR (500) DECLARE @backup_outage_start_time INT DECLARE @backup_outage_end_time INT DECLARE @backup_outage_weekday_mask INT DECLARE @backup_threshold INT DECLARE @backup_threshold_alert_enabled BIT DECLARE @load_outage_start_time INT DECLARE @load_outage_end_time INT DECLARE @load_outage_weekday_mask INT DECLARE @load_threshold INT DECLARE @load_threshold_alert_enabled BIT DECLARE @backupdt DATETIME DECLARE @restoredt DATETIME DECLARE @copydt DATETIME DECLARE @rv INT DECLARE @dt DATETIME DECLARE @copy_delta INT DECLARE @load_delta INT DECLARE @backup_delta INT DECLARE @last_copied_last_updated DATETIME SELECT @dt = GETDATE () DECLARE sync_update CURSOR FOR SELECT primary_server_name, primary_database_name, secondary_server_name, secondary_database_name, last_backup_filename, last_backup_last_updated, last_loaded_filename, last_loaded_last_updated, backup_outage_start_time, backup_outage_end_time, backup_outage_weekday_mask, backup_threshold, backup_threshold_alert_enabled, load_outage_start_time, load_outage_end_time, out_of_sync_threshold, load_outage_weekday_mask, load_threshold_alert_enabled, last_copied_filename, last_copied_last_updated FROM @lsp FOR READ ONLY OPEN sync_update loop: FETCH NEXT FROM sync_update INTO @_primary_server_name, @_primary_database_name, @_secondary_server_name, @_secondary_database_name, @last_backup_filename, @last_backup_last_updated, @last_loaded_filename, @last_loaded_last_updated, @backup_outage_start_time, @backup_outage_end_time, @backup_outage_weekday_mask, @backup_threshold, @backup_threshold_alert_enabled, @load_outage_start_time, @load_outage_end_time, @load_threshold, @load_outage_weekday_mask, @load_threshold_alert_enabled, @last_copied_filename, @last_copied_last_updated IF @@fetch_status <> 0 GOTO _loop EXECUTE @rv = sp_log_shipping_get_date_from_file @_primary_database_name, @last_backup_filename, @backupdt OUTPUT IF (@rv <> 0) SELECT @backupdt = @last_backup_last_updated EXECUTE @rv = sp_log_shipping_get_date_from_file @_primary_database_name, @last_loaded_filename, @restoredt OUTPUT IF (@rv <> 0) SELECT @restoredt = @last_loaded_last_updated EXECUTE @rv = sp_log_shipping_get_date_from_file @_primary_database_name, @last_copied_filename, @copydt OUTPUT IF (@rv <> 0) SELECT @copydt = @last_copied_last_updated EXECUTE @load_in_sync = msdb.dbo.sp_log_shipping_in_sync @restoredt, @backupdt, @load_threshold, @load_outage_start_time, @load_outage_end_time, @load_outage_weekday_mask, @load_threshold_alert_enabled, @load_delta OUTPUT EXECUTE @backup_in_sync = msdb.dbo.sp_log_shipping_in_sync @last_backup_last_updated, @dt, @backup_threshold, @backup_outage_start_time, @backup_outage_end_time, @backup_outage_weekday_mask, @backup_threshold_alert_enabled, @backup_delta OUTPUT EXECUTE msdb.dbo.sp_log_shipping_in_sync @copydt, @backupdt, 1,0,0,0,0, @copy_delta OUTPUT UPDATE @lsp SET backup_in_sync = @backup_in_sync, load_in_sync = @load_in_sync, copy_delta = @copy_delta, load_delta = @load_delta, backup_delta = @backup_delta WHERE primary_server_name = @_primary_server_name AND secondary_server_name = @_secondary_server_name AND primary_database_name = @_primary_database_name AND secondary_database_name = @_secondary_database_name GOTO loop _loop: CLOSE sync_update DEALLOCATE sync_update SELECT * FROM @lsp END go /**************************************************************/ /* sp_update_log_shipping_monitor_info */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_log_shipping_monitor_info...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_update_log_shipping_monitor_info' AND type = N'P') ) DROP PROCEDURE sp_update_log_shipping_monitor_info go CREATE PROCEDURE sp_update_log_shipping_monitor_info @primary_server_name sysname, @primary_database_name sysname, @secondary_server_name sysname, @secondary_database_name sysname, @backup_threshold INT = NULL, @backup_threshold_alert INT = NULL, @backup_threshold_alert_enabled BIT = NULL, @backup_outage_start_time INT = NULL, @backup_outage_end_time INT = NULL, @backup_outage_weekday_mask INT = NULL, @copy_enabled BIT = NULL, @load_enabled BIT = NULL, @out_of_sync_threshold INT = NULL, @out_of_sync_threshold_alert INT = NULL, @out_of_sync_threshold_alert_enabled BIT = NULL, @out_of_sync_outage_start_time INT = NULL, @out_of_sync_outage_end_time INT = NULL, @out_of_sync_outage_weekday_mask INT = NULL AS BEGIN SET NOCOUNT ON DECLARE @_backup_threshold INT DECLARE @_backup_threshold_alert INT DECLARE @_backup_threshold_alert_enabled BIT DECLARE @_backup_outage_start_time INT DECLARE @_backup_outage_end_time INT DECLARE @_backup_outage_weekday_mask INT DECLARE @_copy_enabled BIT DECLARE @_load_enabled BIT DECLARE @_out_of_sync_threshold INT DECLARE @_out_of_sync_threshold_alert INT DECLARE @_out_of_sync_threshold_alert_enabled BIT DECLARE @_out_of_sync_outage_start_time INT DECLARE @_out_of_sync_outage_end_time INT DECLARE @_out_of_sync_outage_weekday_mask INT -- check that the primary exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name)) BEGIN DECLARE @pp sysname SELECT @pp = @primary_server_name + N'.' + @primary_database_name RAISERROR (14262, 16, 1, N'primary_server_name.primary_database_name', @pp) RETURN (1) -- error END -- check that the secondary exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_secondaries WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name)) BEGIN DECLARE @sp sysname SELECT @sp = @secondary_server_name + N'.' + @secondary_database_name RAISERROR (14262, 16, 1, N'secondary_server_name.secondary_database_name', @sp) RETURN (1) -- error END -- load the original variables SELECT @_backup_threshold = backup_threshold, @_backup_threshold_alert = p.threshold_alert, @_backup_threshold_alert_enabled = p.threshold_alert_enabled, @_backup_outage_start_time = p.planned_outage_start_time, @_backup_outage_end_time = p.planned_outage_end_time, @_backup_outage_weekday_mask = p.planned_outage_weekday_mask, @_copy_enabled = copy_enabled, @_load_enabled = load_enabled, @_out_of_sync_threshold = out_of_sync_threshold, @_out_of_sync_threshold_alert = s.threshold_alert, @_out_of_sync_threshold_alert_enabled = s.threshold_alert_enabled, @_out_of_sync_outage_start_time = s.planned_outage_start_time, @_out_of_sync_outage_weekday_mask = s.planned_outage_weekday_mask, @_out_of_sync_outage_end_time = s.planned_outage_end_time FROM msdb.dbo.log_shipping_primaries p, msdb.dbo.log_shipping_secondaries s WHERE p.primary_id = s.primary_id AND primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name AND secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name SELECT @_backup_threshold = ISNULL (@backup_threshold, @_backup_threshold) SELECT @_backup_threshold_alert = ISNULL (@backup_threshold_alert, @_backup_threshold_alert) SELECT @_backup_threshold_alert_enabled = ISNULL (@backup_threshold_alert_enabled, @_backup_threshold_alert_enabled) SELECT @_backup_outage_start_time = ISNULL (@backup_outage_start_time, @_backup_outage_start_time) SELECT @_backup_outage_end_time = ISNULL (@backup_outage_end_time, @_backup_outage_end_time) SELECT @_backup_outage_weekday_mask = ISNULL (@backup_outage_weekday_mask, @_backup_outage_weekday_mask) SELECT @_copy_enabled = ISNULL (@copy_enabled, @_copy_enabled) SELECT @_load_enabled = ISNULL (@load_enabled, @_load_enabled) SELECT @_out_of_sync_threshold = ISNULL (@out_of_sync_threshold, @_out_of_sync_threshold) SELECT @_out_of_sync_threshold_alert = ISNULL (@out_of_sync_threshold_alert, @_out_of_sync_threshold_alert) SELECT @_out_of_sync_threshold_alert_enabled = ISNULL (@out_of_sync_threshold_alert_enabled, @_out_of_sync_threshold_alert_enabled) SELECT @_out_of_sync_outage_start_time = ISNULL (@out_of_sync_outage_start_time, @_out_of_sync_outage_start_time) SELECT @_out_of_sync_outage_end_time = ISNULL (@out_of_sync_outage_end_time, @_out_of_sync_outage_end_time) SELECT @_out_of_sync_outage_weekday_mask = ISNULL (@out_of_sync_outage_weekday_mask, @_out_of_sync_outage_weekday_mask) -- updates UPDATE msdb.dbo.log_shipping_primaries SET backup_threshold = @_backup_threshold, threshold_alert = @_backup_threshold_alert, threshold_alert_enabled = @_backup_threshold_alert_enabled, planned_outage_start_time = @_backup_outage_start_time, planned_outage_end_time = @_backup_outage_end_time, planned_outage_weekday_mask = @_backup_outage_weekday_mask WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name UPDATE msdb.dbo.log_shipping_secondaries SET copy_enabled = @_copy_enabled, load_enabled = @_load_enabled, out_of_sync_threshold = @_out_of_sync_threshold, threshold_alert = @_out_of_sync_threshold_alert, threshold_alert_enabled = @_out_of_sync_threshold_alert_enabled, planned_outage_start_time = @_out_of_sync_outage_start_time, planned_outage_end_time = @_out_of_sync_outage_weekday_mask, planned_outage_weekday_mask = @_out_of_sync_outage_end_time WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name RETURN(0) END go /**************************************************************/ /* sp_delete_log_shipping_monitor_info */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_log_shipping_monitor_info...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_delete_log_shipping_monitor_info' AND type = N'P') ) DROP PROCEDURE sp_delete_log_shipping_monitor_info go CREATE PROCEDURE sp_delete_log_shipping_monitor_info @primary_server_name sysname, @primary_database_name sysname, @secondary_server_name sysname, @secondary_database_name sysname AS BEGIN -- check that the primary exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name)) BEGIN DECLARE @pp sysname SELECT @pp = @primary_server_name + N'.' + @primary_database_name RAISERROR (14262, 16, 1, N'primary_server_name.primary_database_name', @pp) RETURN (1) -- error END -- check that the secondary exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_secondaries WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name)) BEGIN DECLARE @sp sysname SELECT @sp = @secondary_server_name + N'.' + @secondary_database_name RAISERROR (14262, 16, 1, N'secondary_server_name.secondary_database_name', @sp) RETURN (1) -- error END BEGIN TRANSACTION -- delete the secondary DELETE FROM msdb.dbo.log_shipping_secondaries WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name IF (@@error <> 0) goto rollback_quit -- if there are no more secondaries for this primary then delete it IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries p, msdb.dbo.log_shipping_secondaries s WHERE p.primary_id = s.primary_id AND primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name)) BEGIN DELETE FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name IF (@@error <> 0) goto rollback_quit END COMMIT TRANSACTION RETURN (0) rollback_quit: ROLLBACK TRANSACTION RETURN(1) -- Failure END go /**************************************************************/ /* sp_remove_log_shipping_monitor_account */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_remove_log_shipping_monitor_account...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_remove_log_shipping_monitor_account' AND type = N'P') ) DROP PROCEDURE sp_remove_log_shipping_monitor_account go CREATE PROCEDURE sp_remove_log_shipping_monitor_account AS BEGIN SET NOCOUNT ON EXECUTE sp_dropuser N'log_shipping_monitor_probe' EXECUTE sp_droplogin N'log_shipping_monitor_probe' END go /**************************************************************/ /* sp_log_shipping_monitor_backup */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_shipping_monitor_backup...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_log_shipping_monitor_backup' AND type = N'P') ) drop procedure sp_log_shipping_monitor_backup go CREATE PROCEDURE sp_log_shipping_monitor_backup AS BEGIN DECLARE @primary_id sysname DECLARE @primary_server_name sysname DECLARE @primary_database_name sysname DECLARE @maintenance_plan_id UNIQUEIDENTIFIER DECLARE @backup_threshold INT DECLARE @threshold_alert INT DECLARE @threshold_alert_enabled BIT DECLARE @last_backup_filename sysname DECLARE @last_updated DATETIME DECLARE @planned_outage_start_time INT DECLARE @planned_outage_end_time INT DECLARE @planned_outage_weekday_mask INT DECLARE @sync_status INT DECLARE @backup_delta INT DECLARE @delta_string NVARCHAR (10) DECLARE @dt DATETIME SELECT @dt = GETDATE () SET NOCOUNT ON DECLARE bmlsp_cur CURSOR FOR SELECT primary_id, primary_server_name, primary_database_name, maintenance_plan_id, backup_threshold, threshold_alert, threshold_alert_enabled, last_backup_filename, last_updated, planned_outage_start_time, planned_outage_end_time, planned_outage_weekday_mask FROM msdb.dbo.log_shipping_primaries FOR READ ONLY OPEN bmlsp_cur loop: FETCH NEXT FROM bmlsp_cur INTO @primary_id, @primary_server_name, @primary_database_name, @maintenance_plan_id, @backup_threshold, @threshold_alert, @threshold_alert_enabled, @last_backup_filename, @last_updated, @planned_outage_start_time, @planned_outage_end_time, @planned_outage_weekday_mask IF @@FETCH_STATUS <> 0 -- nothing more to fetch, finish the loop GOTO _loop EXECUTE @sync_status = sp_log_shipping_in_sync @last_updated, @dt, @backup_threshold, @planned_outage_start_time, @planned_outage_end_time, @planned_outage_weekday_mask, @threshold_alert_enabled, @backup_delta OUTPUT IF (@sync_status < 0) BEGIN SELECT @delta_string = CONVERT (NVARCHAR(10), @backup_delta) RAISERROR (@threshold_alert, 16, 1, @primary_server_name, @primary_database_name, @delta_string) END GOTO loop _loop: CLOSE bmlsp_cur DEALLOCATE bmlsp_cur END go /**************************************************************/ /* sp_log_shipping_monitor_restore */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_shipping_monitor_restore...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_log_shipping_monitor_restore' AND type = N'P') ) drop procedure sp_log_shipping_monitor_restore go CREATE PROCEDURE sp_log_shipping_monitor_restore AS BEGIN SET NOCOUNT ON DECLARE @primary_id INT DECLARE @secondary_server_name sysname DECLARE @secondary_database_name sysname DECLARE @secondary_plan_id UNIQUEIDENTIFIER DECLARE @out_of_sync_threshold INT DECLARE @threshold_alert INT DECLARE @threshold_alert_enabled BIT DECLARE @last_loaded_filename NVARCHAR (500) DECLARE @last_backup_filename NVARCHAR (500) DECLARE @primary_database_name sysname DECLARE @last_loaded_last_updated DATETIME DECLARE @last_backup_last_updated DATETIME DECLARE @planned_outage_start_time INT DECLARE @planned_outage_end_time INT DECLARE @planned_outage_weekday_mask INT DECLARE @sync_status INT DECLARE @sync_delta INT DECLARE @delta_string NVARCHAR(10) SET NOCOUNT ON DECLARE @backupdt DATETIME DECLARE @restoredt DATETIME DECLARE @rv INT DECLARE rmlsp_cur CURSOR FOR SELECT s.primary_id, s.secondary_server_name, s.secondary_database_name, s.secondary_plan_id, s.out_of_sync_threshold, s.threshold_alert, s.threshold_alert_enabled, s.last_loaded_filename, s.last_loaded_last_updated, p.last_backup_filename, p.last_updated, p.primary_database_name, s.planned_outage_start_time, s.planned_outage_end_time, s.planned_outage_weekday_mask FROM msdb.dbo.log_shipping_secondaries s INNER JOIN msdb.dbo.log_shipping_primaries p ON s.primary_id = p.primary_id FOR READ ONLY OPEN rmlsp_cur loop: FETCH NEXT FROM rmlsp_cur INTO @primary_id, @secondary_server_name, @secondary_database_name, @secondary_plan_id, @out_of_sync_threshold, @threshold_alert, @threshold_alert_enabled, @last_loaded_filename, @last_loaded_last_updated, @last_backup_filename, @last_backup_last_updated, @primary_database_name, @planned_outage_start_time, @planned_outage_end_time, @planned_outage_weekday_mask IF @@FETCH_STATUS <> 0 -- nothing more to fetch, finish the loop GOTO _loop EXECUTE @rv = sp_log_shipping_get_date_from_file @primary_database_name, @last_backup_filename, @backupdt OUTPUT IF (@rv <> 0) SELECT @backupdt = @last_backup_last_updated EXECUTE @rv = sp_log_shipping_get_date_from_file @primary_database_name, @last_loaded_filename, @restoredt OUTPUT IF (@rv <> 0) SELECT @restoredt = @last_loaded_last_updated EXECUTE @sync_status = sp_log_shipping_in_sync @restoredt, @backupdt, @out_of_sync_threshold, @planned_outage_start_time, @planned_outage_end_time, @planned_outage_weekday_mask, @threshold_alert_enabled, @sync_delta OUTPUT IF (@sync_status < 0) BEGIN SELECT @delta_string = CONVERT (NVARCHAR(10), @sync_delta) RAISERROR (@threshold_alert, 16, 1, @secondary_server_name, @secondary_database_name, @delta_string) END GOTO loop _loop: CLOSE rmlsp_cur DEALLOCATE rmlsp_cur END go /**************************************************************/ /* sp_change_monitor_role */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_change_monitor_role...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_change_monitor_role' AND type = N'P') ) DROP PROCEDURE sp_change_monitor_role go CREATE PROCEDURE sp_change_monitor_role @primary_server sysname, @secondary_server sysname, @database sysname, @new_source NVARCHAR (128) AS BEGIN SET NOCOUNT ON BEGIN TRANSACTION -- drop the secondary DELETE FROM msdb.dbo.log_shipping_secondaries WHERE secondary_server_name = @secondary_server AND secondary_database_name = @database IF (@@ROWCOUNT <> 1) BEGIN ROLLBACK TRANSACTION RAISERROR (14442,-1,-1) return(1) END -- let everyone know that we are the new primary UPDATE msdb.dbo.log_shipping_primaries SET primary_server_name = @secondary_server, primary_database_name = @database, source_directory = @new_source WHERE primary_server_name = @primary_server AND primary_database_name = @database IF (@@ROWCOUNT <> 1) BEGIN ROLLBACK TRANSACTION RAISERROR (14442,-1,-1) return(1) END COMMIT TRANSACTION END go /**************************************************************/ /* sp_create_log_shipping_monitor_account */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_create_log_shipping_monitor_account...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_create_log_shipping_monitor_account' AND type = N'P') ) drop procedure sp_create_log_shipping_monitor_account go CREATE PROCEDURE sp_create_log_shipping_monitor_account @password sysname AS BEGIN DECLARE @rv INT SET NOCOUNT ON -- raise an error if the password already exists if exists(select * from master.dbo.syslogins where loginname = N'log_shipping_monitor_probe') begin raiserror(15025,-1,-1,N'log_shipping_monitor_probe') RETURN (1) -- error end IF (@password = N'') BEGIN EXECUTE @rv = sp_addlogin N'log_shipping_monitor_probe', @defdb = N'msdb' IF @@error <>0 or @rv <> 0 RETURN (1) -- error END ELSE BEGIN EXECUTE @rv = sp_addlogin N'log_shipping_monitor_probe', @password, N'msdb' IF @@error <>0 or @rv <> 0 RETURN (1) -- error END EXECUTE @rv = sp_grantdbaccess N'log_shipping_monitor_probe', N'log_shipping_monitor_probe' IF @@error <>0 or @rv <> 0 RETURN (1) -- error GRANT UPDATE ON log_shipping_primaries TO log_shipping_monitor_probe GRANT UPDATE ON log_shipping_secondaries TO log_shipping_monitor_probe GRANT SELECT ON log_shipping_primaries TO log_shipping_monitor_probe GRANT SELECT ON log_shipping_secondaries TO log_shipping_monitor_probe RETURN (0) END go /**************************************************************/ /* INTEGRATION SERVICES SECTION */ /**************************************************************/ USE msdb GO if not exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sysssispackages]')) BEGIN CREATE TABLE [dbo].[sysssispackages] ( [name] [sysname] NOT NULL , [id] [uniqueidentifier] NOT NULL , [description] [nvarchar] (1024) NULL , [createdate] [datetime] NOT NULL , [folderid] [uniqueidentifier] NOT NULL , [ownersid] [varbinary] (85) NOT NULL , [packagedata] [image] NOT NULL , [packageformat] [int] NOT NULL, [packagetype] [int] NOT NULL CONSTRAINT [DF__sysssispackages] DEFAULT (0), [vermajor] [int] NOT NULL, [verminor] [int] NOT NULL, [verbuild] [int] NOT NULL, [vercomments] [nvarchar] (1024) NULL, [verid] [uniqueidentifier] NOT NULL, [isencrypted] [bit] NOT NULL CONSTRAINT [DF__sysssispackages_2] DEFAULT (0), [readrolesid] [varbinary] (85) NULL, [writerolesid] [varbinary] (85) NULL, CONSTRAINT [pk_sysssispackages] PRIMARY KEY NONCLUSTERED ( [folderid], [name] ) ON [PRIMARY] , ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] END else BEGIN IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='isencrypted' and id = (select id from msdb.dbo.sysobjects where name='sysssispackages')) BEGIN ALTER TABLE [dbo].[sysssispackages] ADD [isencrypted] [bit] NOT NULL CONSTRAINT [DF__sysssispackages_2] DEFAULT (0) ALTER TABLE [dbo].[sysssispackages] ADD [readrolesid] [varbinary] (85) NULL ALTER TABLE [dbo].[sysssispackages] ADD [writerolesid] [varbinary] (85) NULL END ELSE BEGIN IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='readrolesid' and id = (select id from msdb.dbo.sysobjects where name='sysssispackages')) BEGIN ALTER TABLE [dbo].[sysssispackages] DROP COLUMN [readrole] ALTER TABLE [dbo].[sysssispackages] DROP COLUMN [writerole] ALTER TABLE [dbo].[sysssispackages] ADD [readrolesid] [varbinary] (85) NULL ALTER TABLE [dbo].[sysssispackages] ADD [writerolesid] [varbinary] (85) NULL END END END GO /**************************************************************/ /* sysmaintplan_plans */ /**************************************************************/ PRINT '' PRINT 'Creating view sysmaintplan_plans...' go IF (NOT OBJECT_ID(N'dbo.sysmaintplan_plans', 'V') IS NULL) DROP VIEW sysmaintplan_plans go CREATE VIEW sysmaintplan_plans AS SELECT s.name AS [name], s.id AS [id], s.description AS [description], s.createdate AS [create_date], suser_sname(s.ownersid) AS [owner], s.vermajor AS [version_major], s.verminor AS [version_minor], s.verbuild AS [version_build], s.vercomments AS [version_comments], ISNULL((select TOP 1 msx_plan from sysmaintplan_subplans where plan_id = s.id), 0) AS [from_msx], CASE WHEN (NOT EXISTS (select TOP 1 msx_job_id from sysmaintplan_subplans subplans, sysjobservers jobservers where plan_id = s.id and msx_job_id is not null and subplans.msx_job_id = jobservers.job_id and server_id != 0)) then 0 else 1 END AS [has_targets] FROM msdb.dbo.sysssispackages AS s WHERE (s.folderid = '08aa12d5-8f98-4dab-a4fc-980b150a5dc8' and s.packagetype = 6) go if not exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sysssispackagefolders]')) BEGIN CREATE TABLE [dbo].[sysssispackagefolders] ( [folderid] [uniqueidentifier] NOT NULL , [parentfolderid] [uniqueidentifier] NULL , [foldername] [sysname] NOT NULL , CONSTRAINT [PK_sysssispackagefolders] PRIMARY KEY NONCLUSTERED ( [folderid] ) ON [PRIMARY], CONSTRAINT [U_sysssispackagefoldersuniquepath] UNIQUE NONCLUSTERED ( [parentfolderid], [foldername] ) ON [PRIMARY] ) ON [PRIMARY] END GO -- WARNING! IMPORTANT! If you change sysssislog table schema, -- be sure to update \dts\src\dtr\runtime\logproviders.cpp !!! if not exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sysssislog]')) BEGIN CREATE TABLE [dbo].[sysssislog] ( [id] [int] NOT NULL IDENTITY PRIMARY KEY, [event] [sysname] NOT NULL, [computer] [nvarchar] (128) NOT NULL, [operator] [nvarchar] (128) NOT NULL, [source] [nvarchar] (1024) NOT NULL, [sourceid] [uniqueidentifier] NOT NULL, [executionid] [uniqueidentifier] NOT NULL, [starttime] [datetime] NOT NULL, [endtime] [datetime] NOT NULL, [datacode] [int] NOT NULL, [databytes] [image] NULL, [message] [nvarchar] (2048) NOT NULL, ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] END GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_addlogentry]')) drop procedure [dbo].[sp_ssis_addlogentry] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_ssis_addlogentry] @event sysname, @computer nvarchar(128), @operator nvarchar(128), @source nvarchar(1024), @sourceid uniqueidentifier, @executionid uniqueidentifier, @starttime datetime, @endtime datetime, @datacode int, @databytes image, @message nvarchar(2048) AS INSERT INTO sysssislog ( event, computer, operator, source, sourceid, executionid, starttime, endtime, datacode, databytes, message ) VALUES ( @event, @computer, @operator, @source, @sourceid, @executionid, @starttime, @endtime, @datacode, @databytes, @message ) RETURN 0 ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_listpackages]')) drop procedure [dbo].[sp_ssis_listpackages] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_ssis_listpackages] @folderid uniqueidentifier AS SELECT name, id, description, createdate, folderid, datalength(packagedata), vermajor, verminor, verbuild, vercomments, verid FROM sysssispackages WHERE [folderid] = @folderid ORDER BY name ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_listfolders]')) drop procedure [dbo].[sp_ssis_listfolders] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_ssis_listfolders] @parentfolderid uniqueidentifier = NULL AS SELECT folderid, parentfolderid, foldername FROM sysssispackagefolders WHERE [parentfolderid] = @parentfolderid OR (@parentfolderid IS NULL AND [parentfolderid] IS NULL) ORDER BY foldername ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_deletepackage]')) drop procedure [dbo].[sp_ssis_deletepackage] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_ssis_deletepackage] @name sysname, @folderid uniqueidentifier AS DECLARE @sid varbinary(85) DECLARE @writerolesid varbinary(85) DECLARE @writerole nvarchar(128) SELECT @sid = [ownersid], @writerolesid = [writerolesid] FROM sysssispackages WHERE [name] = @name AND [folderid] = @folderid IF @sid IS NOT NULL BEGIN --// The row exists, check security IF @writerolesid IS NOT NULL BEGIN SELECT @writerole = [name] FROM sys.database_principals WHERE [type] = ''R'' AND [sid] = @writerolesid IF @writerole IS NULL SET @writerole = ''db_ssisadmin'' END IF @writerole IS NULL BEGIN IF (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN IF (@sid<>SUSER_SID()) OR (IS_MEMBER(''db_ssisltduser'')<>1) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END END END ELSE BEGIN -- If writerrole is set for this package, -- Allow sysadmins and the members of writer role to delete this package IF (IS_MEMBER(@writerole)<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN IF (@sid<>SUSER_SID()) OR (IS_MEMBER(''db_ssisltduser'')<>1) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END END END END DELETE FROM sysssispackages WHERE [name] = @name AND [folderid] = @folderid ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_deletefolder]')) drop procedure [dbo].[sp_ssis_deletefolder] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_ssis_deletefolder] @folderid uniqueidentifier AS DECLARE @name sysname DECLARE @count int IF @folderid = ''00000000-0000-0000-0000-000000000000'' BEGIN RAISERROR (14307, -1, -1, ''00000000-0000-0000-0000-000000000000'') RETURN 1 -- Failure END SELECT @name = [foldername] FROM sysssispackagefolders WHERE [folderid] = @folderid IF @name IS NOT NULL BEGIN --// The row exists, check security IF (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN IF (IS_MEMBER(''db_ssisltduser'')<>1) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END END END -- Get the number of packages in this folder SELECT @count = count(*) FROM sysssispackages WHERE [folderid] = @folderid -- Are there any packages in this folder IF @count > 0 BEGIN -- Yes, do not delete RAISERROR (14593, -1, -1, @name) RETURN 1 -- Failure END -- Get the number of folders in this folder SELECT @count = count(*) FROM sysssispackagefolders WHERE [parentfolderid] = @folderid -- Are there any folders in this folder IF @count > 0 BEGIN -- Yes, do not delete RAISERROR (14593, -1, -1, @name) RETURN 1 -- Failure END DELETE FROM sysssispackagefolders WHERE [folderid] = @folderid ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_getpackage]')) drop procedure [dbo].[sp_ssis_getpackage] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_ssis_getpackage] @name sysname, @folderid uniqueidentifier AS DECLARE @sid varbinary(85) DECLARE @isencrypted bit DECLARE @readrolesid varbinary(85) DECLARE @readrole nvarchar(128) --// Check security, if the row exists SELECT @sid = [ownersid], @readrolesid = [readrolesid] FROM sysssispackages WHERE [name] = @name AND [folderid] = @folderid IF @sid IS NOT NULL BEGIN IF @readrolesid IS NOT NULL BEGIN SELECT @readrole = [name] FROM sys.database_principals WHERE [type] = ''R'' AND [sid] = @readrolesid IF @readrole IS NULL SET @readrole = ''db_ssisadmin'' END IF @readrole IS NOT NULL BEGIN IF (IS_MEMBER(@readrole)<>1) AND (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN IF (IS_MEMBER(''db_ssisltduser'')<>1) OR (@sid<>SUSER_SID()) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END END END ELSE BEGIN IF (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) AND (IS_MEMBER(''db_ssisoperator'')<>1) BEGIN IF (IS_MEMBER(''db_ssisltduser'')<>1) OR (@sid<>SUSER_SID()) BEGIN RAISERROR (14586, -1, -1, @name) RETURN 1 -- Failure END END END END SELECT packagedata FROM sysssispackages WHERE [name] = @name AND [folderid] = @folderid ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_getfolder]')) drop procedure [dbo].[sp_ssis_getfolder] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_ssis_getfolder] @name sysname, @parentfolderid uniqueidentifier AS SELECT folder.folderid, folder.foldername, folder.parentfolderid, parent.foldername FROM sysssispackagefolders folder LEFT OUTER JOIN sysssispackagefolders parent ON folder.parentfolderid = parent.folderid WHERE folder.foldername = @name AND (folder.parentfolderid = @parentfolderid OR (@parentfolderid IS NULL AND folder.parentfolderid IS NULL)) ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_putpackage]')) drop procedure [dbo].[sp_ssis_putpackage] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_ssis_putpackage] @name sysname, @id uniqueidentifier, @description nvarchar(1024), @createdate datetime, @folderid uniqueidentifier, @packagedata image, @packageformat int, @packagetype int, @vermajor int, @verminor int, @verbuild int, @vercomments nvarchar(1024), @verid uniqueidentifier AS SET NOCOUNT ON DECLARE @sid varbinary(85) DECLARE @writerolesid varbinary(85) DECLARE @writerole nvarchar(128) --// Determine if we should INSERT or UPDATE SELECT @sid = [ownersid], @writerolesid = [writerolesid] FROM sysssispackages WHERE [name] = @name AND [folderid] = @folderid IF @sid IS NOT NULL BEGIN --// The row exists, check security IF @writerolesid IS NOT NULL BEGIN SELECT @writerole = [name] FROM sys.database_principals WHERE [type] = ''R'' AND [sid] = @writerolesid IF @writerole IS NULL SET @writerole = ''db_ssisadmin'' END IF @writerole IS NULL BEGIN IF (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN IF (@sid<>SUSER_SID()) OR (IS_MEMBER(''db_ssisltduser'')<>1) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END END END ELSE BEGIN IF (IS_MEMBER(@writerole)<>1) AND (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN IF (@sid<>SUSER_SID()) OR (IS_MEMBER(''db_ssisltduser'')<>1) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END END END --// Security check passed, UPDATE now UPDATE sysssispackages SET id = @id, description = @description, createdate = @createdate, packagedata = @packagedata, packageformat = @packageformat, packagetype = @packagetype, vermajor = @vermajor, verminor = @verminor, verbuild = @verbuild, vercomments = @vercomments, verid = @verid WHERE name = @name AND folderid = @folderid END ELSE BEGIN --// The row does not exist, check security IF (IS_MEMBER(''db_ssisltduser'')<>1) AND (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END --// Security check passed, INSERT now INSERT INTO sysssispackages ( name, id, description, createdate, folderid, ownersid, packagedata, packageformat, packagetype, vermajor, verminor, verbuild, vercomments, verid ) VALUES ( @name, @id, @description, @createdate, @folderid, SUSER_SID(), @packagedata, @packageformat, @packagetype, @vermajor, @verminor, @verbuild, @vercomments, @verid ) END RETURN 0 -- SUCCESS ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_checkexists]')) drop procedure [dbo].[sp_ssis_checkexists] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_ssis_checkexists] @name sysname, @folderid uniqueidentifier AS SET NOCOUNT ON SELECT TOP 1 1 FROM sysssispackages WHERE [name] = @name AND [folderid] = @folderid RETURN 0 -- SUCCESS ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_addfolder]')) drop procedure [dbo].[sp_ssis_addfolder] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_ssis_addfolder] @parentfolderid uniqueidentifier, @name sysname, @folderid uniqueidentifier = NULL AS --Check security IF (IS_MEMBER(''db_ssisltduser'')<>1) AND (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN RAISERROR (14591, -1, -1, @name) RETURN 1 -- Failure END --// Security check passed, INSERT now INSERT INTO sysssispackagefolders (folderid, parentfolderid, foldername) VALUES (ISNULL(@folderid, NEWID()), @parentfolderid, @name) ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_renamefolder]')) drop procedure [dbo].[sp_ssis_renamefolder] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_ssis_renamefolder] @folderid uniqueidentifier, @name sysname AS --Check security IF (IS_MEMBER(''db_ssisltduser'')<>1) AND (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN RAISERROR (14591, -1, -1, @name) RETURN 1 -- Failure END --// Security check passed, INSERT now UPDATE sysssispackagefolders SET [foldername] = @name WHERE [folderid] = @folderid ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_setpackageroles]')) DROP PROCEDURE [dbo].[sp_ssis_setpackageroles] GO execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_ssis_setpackageroles] @name sysname, @folderid uniqueidentifier, @readrole nvarchar (128), @writerole nvarchar (128) AS SET NOCOUNT ON DECLARE @sid varbinary(85) --// Determine if we should INSERT or UPDATE SELECT @sid = ownersid FROM sysssispackages WHERE name = @name AND folderid = @folderid IF @sid IS NOT NULL BEGIN --// The row exists, check security IF (IS_MEMBER(''db_ssisadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN IF (@sid<>SUSER_SID()) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END END --// Security check passed, UPDATE now DECLARE @readrolesid varbinary(85) DECLARE @writerolesid varbinary(85) SELECT @readrolesid = [sid] FROM sys.database_principals WHERE [type] = ''R'' AND [name] = @readrole SELECT @writerolesid = [sid] FROM sys.database_principals WHERE [type] = ''R'' AND [name] = @writerole IF @readrolesid IS NULL AND @readrole IS NOT NULL BEGIN RAISERROR (15014, -1, -1, @readrole) RETURN 1 END IF @writerolesid IS NULL AND @writerole IS NOT NULL BEGIN RAISERROR (15014, -1, -1, @writerole) RETURN 1 END UPDATE sysssispackages SET [readrolesid] = @readrolesid, [writerolesid] = @writerolesid WHERE name = @name AND folderid = @folderid END ELSE BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END RETURN 0 -- SUCCESS ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_ssis_getpackageroles]')) DROP PROCEDURE [dbo].[sp_ssis_getpackageroles] GO execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_ssis_getpackageroles] @name sysname, @folderid uniqueidentifier AS DECLARE @readrolesid varbinary(85) DECLARE @writerolesid varbinary(85) DECLARE @readrole nvarchar(128) DECLARE @writerole nvarchar(128) SELECT @readrolesid = [readrolesid], @writerolesid = [writerolesid] FROM sysssispackages WHERE [name] = @name AND [folderid] = @folderid SELECT @readrole = [name] FROM sys.database_principals WHERE [type] = ''R'' AND [sid] = @readrolesid SELECT @writerole = [name] FROM sys.database_principals WHERE [type] = ''R'' AND [sid] = @writerolesid SELECT @readrole AS readrole, @writerole AS writerole ' GO GRANT EXECUTE ON [dbo].[sp_ssis_addlogentry] TO [db_ssisadmin] GRANT EXECUTE ON [dbo].[sp_ssis_addlogentry] TO [db_ssisltduser] GRANT EXECUTE ON [dbo].[sp_ssis_deletepackage] TO [db_ssisadmin] GRANT EXECUTE ON [dbo].[sp_ssis_deletepackage] TO [db_ssisltduser] GRANT EXECUTE ON [dbo].[sp_ssis_deletepackage] TO [db_ssisoperator] GRANT EXECUTE ON [dbo].[sp_ssis_getpackage] TO [db_ssisadmin] GRANT EXECUTE ON [dbo].[sp_ssis_getpackage] TO [db_ssisltduser] GRANT EXECUTE ON [dbo].[sp_ssis_getpackage] TO [db_ssisoperator] GRANT EXECUTE ON [dbo].[sp_ssis_listpackages] TO [db_ssisadmin] GRANT EXECUTE ON [dbo].[sp_ssis_listpackages] TO [db_ssisltduser] GRANT EXECUTE ON [dbo].[sp_ssis_listpackages] TO [db_ssisoperator] GRANT EXECUTE ON [dbo].[sp_ssis_putpackage] TO [db_ssisadmin] GRANT EXECUTE ON [dbo].[sp_ssis_putpackage] TO [db_ssisltduser] GRANT EXECUTE ON [dbo].[sp_ssis_putpackage] TO [db_ssisoperator] GRANT EXECUTE ON [dbo].[sp_ssis_checkexists] TO [db_ssisadmin] GRANT EXECUTE ON [dbo].[sp_ssis_checkexists] TO [db_ssisltduser] GRANT EXECUTE ON [dbo].[sp_ssis_checkexists] TO [db_ssisoperator] GRANT EXECUTE ON [dbo].[sp_ssis_listfolders] TO [db_ssisadmin] GRANT EXECUTE ON [dbo].[sp_ssis_listfolders] TO [db_ssisltduser] GRANT EXECUTE ON [dbo].[sp_ssis_listfolders] TO [db_ssisoperator] GRANT EXECUTE ON [dbo].[sp_ssis_deletefolder] TO [db_ssisadmin] GRANT EXECUTE ON [dbo].[sp_ssis_deletefolder] TO [db_ssisltduser] GRANT EXECUTE ON [dbo].[sp_ssis_addfolder] TO [db_ssisadmin] GRANT EXECUTE ON [dbo].[sp_ssis_addfolder] TO [db_ssisltduser] GRANT EXECUTE ON [dbo].[sp_ssis_renamefolder] TO [db_ssisadmin] GRANT EXECUTE ON [dbo].[sp_ssis_renamefolder] TO [db_ssisltduser] GRANT EXECUTE ON [dbo].[sp_ssis_getfolder] TO [db_ssisadmin] GRANT EXECUTE ON [dbo].[sp_ssis_getfolder] TO [db_ssisltduser] GRANT EXECUTE ON [dbo].[sp_ssis_getfolder] TO [db_ssisoperator] GRANT EXECUTE ON [dbo].[sp_ssis_setpackageroles] TO [db_ssisadmin] GRANT EXECUTE ON [dbo].[sp_ssis_setpackageroles] TO [db_ssisltduser] GRANT EXECUTE ON [dbo].[sp_ssis_getpackageroles] TO [db_ssisadmin] GRANT EXECUTE ON [dbo].[sp_ssis_getpackageroles] TO [db_ssisltduser] GO GRANT ALL ON [dbo].[sysssislog] TO [db_ssisadmin] GRANT INSERT ON [dbo].[sysssislog] TO [db_ssisltduser] GRANT SELECT ON [dbo].[sysssislog] TO [db_ssisltduser] GRANT INSERT ON [dbo].[sysssislog] TO [db_ssisoperator] GRANT SELECT ON [dbo].[sysssislog] TO [db_ssisoperator] GO -- Maintenance Plans -- Allow SQLAgent on target servers to gather information about -- maintenance plans from the master. GRANT EXECUTE ON sp_maintplan_subplans_by_job TO SQLAgentUserRole GRANT EXECUTE ON sp_maintplan_subplans_by_job TO TargetServersRole /**************************************************************/ /* */ /* D A T A C O L L E C T O R */ /* */ /**************************************************************/ USE msdb GO --------------------------------------------------------------- -- Data Collector: Security: Database Principals --------------------------------------------------------------- PRINT '' PRINT 'Create dc_operator role...' IF ( NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'dc_operator' AND type = 'R')) BEGIN CREATE ROLE [dc_operator] END ELSE -- if the role exists check to see if it has members BEGIN IF NOT EXISTS (SELECT rm.member_principal_id FROM sys.database_principals dp INNER JOIN sys.database_role_members rm ON rm.role_principal_id = dp.principal_id WHERE name = N'dc_operator' AND type = 'R') BEGIN -- if the role has no members drop and recreate it DROP ROLE [dc_operator] CREATE ROLE [dc_operator] END END GO EXECUTE sp_addrolemember @rolename = 'db_ssisltduser' , @membername = 'dc_operator' GO EXECUTE sp_addrolemember @rolename = 'db_ssisoperator' , @membername = 'dc_operator' GO EXECUTE sp_addrolemember @rolename = 'SQLAgentUserRole' , @membername = 'dc_operator' GO PRINT '' PRINT 'Create dc_admin role...' IF ( NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'dc_admin' AND type = 'R')) BEGIN CREATE ROLE [dc_admin] END ELSE -- if the role exists check to see if it has members BEGIN IF NOT EXISTS (SELECT rm.member_principal_id FROM sys.database_principals dp INNER JOIN sys.database_role_members rm ON rm.role_principal_id = dp.principal_id WHERE name = N'dc_admin' AND type = 'R') BEGIN -- if the role has no members drop and recreate it DROP ROLE [dc_admin] CREATE ROLE [dc_admin] END END GO EXECUTE sp_addrolemember @rolename = 'dc_operator' , @membername = 'dc_admin' GO PRINT '' PRINT 'Create dc_proxy role...' IF ( NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'dc_proxy' AND type = 'R')) BEGIN CREATE ROLE [dc_proxy] END ELSE -- if the role exists check to see if it has members BEGIN IF NOT EXISTS (SELECT rm.member_principal_id FROM sys.database_principals dp INNER JOIN sys.database_role_members rm ON rm.role_principal_id = dp.principal_id WHERE name = N'dc_proxy' AND type = 'R') BEGIN -- if the role has no members drop and recreate it DROP ROLE [dc_proxy] CREATE ROLE [dc_proxy] END END GO EXECUTE sp_addrolemember @rolename = 'db_ssisltduser' , @membername = 'dc_proxy' GO EXECUTE sp_addrolemember @rolename = 'db_ssisoperator' , @membername = 'dc_proxy' GO PRINT '' PRINT 'Create loginless user that has ownership of data collector agent-related securables...' IF (NOT EXISTS(SELECT * FROM sys.database_principals WHERE NAME = 'MS_DataCollectorInternalUser')) BEGIN CREATE USER [MS_DataCollectorInternalUser] WITHOUT LOGIN END GO EXECUTE sp_addrolemember @rolename = 'db_ssisoperator' , @membername = 'MS_DataCollectorInternalUser' GO EXECUTE sp_addrolemember @rolename = 'SQLAgentUserRole' , @membername = 'MS_DataCollectorInternalUser' GO EXECUTE sp_addrolemember @rolename = 'dc_admin' , @membername = 'MS_DataCollectorInternalUser' GO GRANT IMPERSONATE ON USER::[MS_DataCollectorInternalUser] TO [dc_admin]; GO --------------------------------------------------------------- -- Configuration store --------------------------------------------------------------- IF (OBJECT_ID(N'[dbo].[syscollector_config_store_internal]', 'U') IS NULL) BEGIN PRINT 'Creating table [dbo].[syscollector_config_store_internal]...' CREATE TABLE [dbo].[syscollector_config_store_internal] ( parameter_name nvarchar(128) NOT NULL, parameter_value sql_variant NULL, CONSTRAINT [PK_syscollector_config_store_internal_paremeter_name] PRIMARY KEY CLUSTERED (parameter_name ASC) ) END GO IF (NOT OBJECT_ID(N'[dbo].[syscollector_config_store]', 'V') IS NULL) BEGIN PRINT 'Dropping view [dbo].[syscollector_config_store]...' DROP VIEW [dbo].[syscollector_config_store] END GO PRINT 'Creating view [dbo].[syscollector_config_store]...' GO CREATE VIEW [dbo].[syscollector_config_store] AS SELECT s.parameter_name, s.parameter_value FROM [dbo].[syscollector_config_store_internal] s GO -- populate config store with known name and value pairs IF (NOT EXISTS(SELECT * FROM [dbo].[syscollector_config_store_internal] WHERE parameter_name = N'MDWInstance')) BEGIN INSERT INTO [dbo].[syscollector_config_store_internal] ( parameter_name, parameter_value ) VALUES ( N'MDWInstance', NULL ) END IF (NOT EXISTS(SELECT * FROM [dbo].[syscollector_config_store_internal] WHERE parameter_name = N'MDWDatabase')) BEGIN INSERT INTO [dbo].[syscollector_config_store_internal] ( parameter_name, parameter_value ) VALUES ( N'MDWDatabase', NULL ) END IF (NOT EXISTS(SELECT * FROM [dbo].[syscollector_config_store_internal] WHERE parameter_name = 'CollectorEnabled')) BEGIN INSERT INTO [dbo].[syscollector_config_store_internal] ( parameter_name, parameter_value ) VALUES ( 'CollectorEnabled', 0 ) END IF (NOT EXISTS(SELECT * FROM [dbo].[syscollector_config_store_internal] WHERE parameter_name = 'CacheWindow')) BEGIN INSERT INTO [dbo].[syscollector_config_store_internal] ( parameter_name, parameter_value ) VALUES ( 'CacheWindow', 1 --By default, the collector will keep 1 window's worth of uploads ) END IF (NOT EXISTS(SELECT * FROM [dbo].[syscollector_config_store_internal] WHERE parameter_name = 'CacheDirectory')) BEGIN INSERT INTO [dbo].[syscollector_config_store_internal] ( parameter_name, parameter_value ) VALUES ( 'CacheDirectory', NULL ) END GO --------------------------------------------------------------- -- Access collector level properties --------------------------------------------------------------- IF (NOT OBJECT_ID('[dbo].[sp_syscollector_verify_collector_state]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_verify_collector_state]...' DROP PROCEDURE [dbo].[sp_syscollector_verify_collector_state] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_verify_collector_state]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_verify_collector_state] @desired_state int WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser' AS BEGIN DECLARE @collector_enabled INT SET @collector_enabled = CONVERT(int, (SELECT parameter_value FROM dbo.syscollector_config_store_internal WHERE parameter_name = 'CollectorEnabled')) IF (@collector_enabled IS NULL) BEGIN RAISERROR(14691, -1, -1) RETURN(1) END IF (@collector_enabled = 0) AND (@desired_state = 1) BEGIN RAISERROR(14681, -1, -1) RETURN(1) END IF (@collector_enabled = 1) AND (@desired_state = 0) BEGIN RAISERROR(14690, -1, -1) RETURN(1) END RETURN(0) END GO IF (NOT OBJECT_ID('dbo.sp_syscollector_set_warehouse_instance_name', 'P') IS NULL) BEGIN DROP PROCEDURE [dbo].[sp_syscollector_set_warehouse_instance_name] END GO PRINT '' PRINT 'Creating procedure [dbo].[sp_syscollector_set_warehouse_instance_name]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_set_warehouse_instance_name] @instance_name sysname = NULL AS BEGIN -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_admin') RETURN(1) -- Failure END -- Check if the collector is disabled DECLARE @retVal int EXEC @retVal = [dbo].[sp_syscollector_verify_collector_state] @desired_state = 0 IF (@retVal <> 0) RETURN (1) UPDATE [msdb].[dbo].[syscollector_config_store_internal] SET parameter_value = @instance_name WHERE parameter_name = N'MDWInstance' RETURN (0) END GO IF (NOT OBJECT_ID('dbo.sp_syscollector_set_warehouse_database_name', 'P') IS NULL) BEGIN DROP PROCEDURE [dbo].[sp_syscollector_set_warehouse_database_name] END GO PRINT '' PRINT 'Creating procedure [dbo].[sp_syscollector_set_warehouse_database_name]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_set_warehouse_database_name] @database_name sysname = NULL AS BEGIN -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_admin') RETURN(1) -- Failure END -- Check if the collector is disabled DECLARE @retVal int EXEC @retVal = [dbo].[sp_syscollector_verify_collector_state] @desired_state = 0 IF (@retVal <> 0) RETURN (1) UPDATE [msdb].[dbo].[syscollector_config_store_internal] SET parameter_value = @database_name WHERE parameter_name = N'MDWDatabase' RETURN (0) END GO IF (NOT OBJECT_ID('dbo.sp_syscollector_set_cache_directory', 'P') IS NULL) BEGIN DROP PROCEDURE [dbo].[sp_syscollector_set_cache_directory] END GO PRINT '' PRINT 'Creating procedure [dbo].[sp_syscollector_set_cache_directory]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_set_cache_directory] @cache_directory nvarchar(255) = NULL AS BEGIN -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_admin') RETURN(1) -- Failure END SET @cache_directory = NULLIF(LTRIM(RTRIM(@cache_directory)), N'') -- Check if the collector is disabled DECLARE @retVal int EXEC @retVal = [dbo].[sp_syscollector_verify_collector_state] @desired_state = 0 IF (@retVal <> 0) RETURN (1) UPDATE [msdb].[dbo].[syscollector_config_store_internal] SET parameter_value = @cache_directory WHERE parameter_name = N'CacheDirectory' RETURN (0) END GO IF (NOT OBJECT_ID('dbo.sp_syscollector_set_cache_window', 'P') IS NULL) BEGIN DROP PROCEDURE [dbo].[sp_syscollector_set_cache_window] END GO PRINT '' PRINT 'Creating procedure [dbo].[sp_syscollector_set_cache_window]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_set_cache_window] @cache_window int = 1 AS BEGIN -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_admin') RETURN(1) -- Failure END -- Check if the collector is disabled DECLARE @retVal int EXEC @retVal = [dbo].[sp_syscollector_verify_collector_state] @desired_state = 0 IF (@retVal <> 0) RETURN (1) IF (@cache_window < -1) BEGIN RAISERROR(14687, -1, -1, @cache_window) RETURN(1) END UPDATE [msdb].[dbo].[syscollector_config_store_internal] SET parameter_value = @cache_window WHERE parameter_name = N'CacheWindow' RETURN (0) END GO IF (NOT OBJECT_ID('dbo.sp_syscollector_get_warehouse_connection_string', 'P') IS NULL) BEGIN DROP PROCEDURE [dbo].[sp_syscollector_get_warehouse_connection_string] END GO PRINT '' PRINT 'Creating procedure [dbo].[sp_syscollector_get_warehouse_connection_string]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_get_warehouse_connection_string] @connection_string nvarchar(512) = NULL OUTPUT AS BEGIN DECLARE @instance_name sysname DECLARE @database_name sysname DECLARE @user_name sysname DECLARE @password sysname SELECT @instance_name = CONVERT(sysname,parameter_value) FROM [msdb].[dbo].[syscollector_config_store_internal] WHERE parameter_name = N'MDWInstance' IF (@instance_name IS NULL) BEGIN RAISERROR(14686, -1, -1) RETURN (1) END -- '"' is the delimiter for the sql client connection string SET @instance_name = QUOTENAME(@instance_name, '"') SELECT @database_name = CONVERT(sysname,parameter_value) FROM [msdb].[dbo].[syscollector_config_store_internal] WHERE parameter_name = N'MDWDatabase' IF (@database_name IS NULL) BEGIN RAISERROR(14686, -1, -1) RETURN (1) END SET @database_name = QUOTENAME(@database_name, '"') SET @connection_string = N'Data Source=' + @instance_name + N';Application Name="Data Collector - MDW";Initial Catalog=' + @database_name SET @connection_string = @connection_string + N';Use Encryption for Data=true;Trust Server Certificate=true;Provider=SQLNCLI10;Integrated Security=SSPI;Connect Timeout=60;'; RETURN (0) END GO -- -- Return the highest version of the Management Data Warehouse that this -- Collector is no compatible with. Anything higher than this version -- is fine. -- -- If you change this number, make sure to change the corresponding versions -- in HighestIncompatibleMDWVersion.cs -- IF (NOT OBJECT_ID('dbo.fn_syscollector_highest_incompatible_mdw_version', 'FN') IS NULL) BEGIN DROP FUNCTION [dbo].[fn_syscollector_highest_incompatible_mdw_version] END GO PRINT '' PRINT 'Creating function [dbo].[fn_syscollector_highest_incompatible_mdw_version]...' GO CREATE FUNCTION [dbo].[fn_syscollector_highest_incompatible_mdw_version]() RETURNS nvarchar(50) BEGIN RETURN '10.00.1300.13' -- CTP6 END GO --------------------------------------------------------------- -- Collection set --------------------------------------------------------------- IF (OBJECT_ID(N'[dbo].[syscollector_collection_sets_internal]', 'U') IS NULL) BEGIN PRINT 'Creating table [dbo].[syscollector_collection_sets_internal]...' CREATE TABLE [dbo].[syscollector_collection_sets_internal] ( collection_set_id int IDENTITY NOT NULL, collection_set_uid uniqueidentifier NOT NULL, schedule_uid uniqueidentifier NULL, -- schedule to run collection or upload name sysname NOT NULL, -- name of the collection set, must be unique name_id int NULL, -- sysmessage id of the name of the set (for localizing system collection set) target nvarchar(max) NULL, -- future use is_running bit default 0 NOT NULL, -- is the collection set active proxy_id int NULL, -- proxy to use to run the collection set is_system bit NOT NULL, -- indicates MS-shipped collection set collection_job_id uniqueidentifier NULL, -- id of the collection job upload_job_id uniqueidentifier NULL, -- id of the upload job collection_mode smallint default 0 NOT NULL, -- 0 - cached, 1 - non-cached logging_level smallint default 2 NOT NULL, -- 0 - errors only, 1 - errors & warnings, 2 - detailed description nvarchar(4000) NULL, -- description of the set description_id int NULL, -- sysmessage id of the description of the set (for localizing system collection set) days_until_expiration smallint NOT NULL, -- how long to keep the data from this collection set dump_on_any_error bit default 0 NOT NULL, -- configure SQL dumper to dump on any SSIS errors dump_on_codes nvarchar(max) NULL, -- configure SQL dumper to dump when we hit one of the specified SSIS errors CONSTRAINT [PK_syscollector_collection_sets_internal] PRIMARY KEY CLUSTERED (collection_set_id ASC), CONSTRAINT [UQ_syscollector_collection_sets_internal_name] UNIQUE (name) ) ALTER TABLE syscollector_collection_sets_internal ADD CONSTRAINT [FK_syscollector_collection_sets_internal_sysproxies] FOREIGN KEY(proxy_id) REFERENCES sysproxies (proxy_id) ALTER TABLE syscollector_collection_sets_internal ADD CONSTRAINT [FK_syscollector_collection_sets_collection_sysjobs] FOREIGN KEY(collection_job_id) REFERENCES sysjobs (job_id) ALTER TABLE syscollector_collection_sets_internal ADD CONSTRAINT [FK_syscollector_collection_sets_upload_sysjobs] FOREIGN KEY(upload_job_id) REFERENCES sysjobs(job_id) END ELSE BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'name_id' AND id = OBJECT_ID(N'[dbo].[syscollector_collection_sets_internal]'))) BEGIN PRINT 'Altering table [dbo].[syscollector_collection_sets_internal]...' ALTER TABLE [dbo].[syscollector_collection_sets_internal] ADD name_id int NULL END IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'description_id' AND id = OBJECT_ID(N'[dbo].[syscollector_collection_sets_internal]'))) BEGIN PRINT 'Altering table [dbo].[syscollector_collection_sets_internal]...' ALTER TABLE [dbo].[syscollector_collection_sets_internal] ADD description_id int NULL END IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'dump_on_any_error' AND id = OBJECT_ID(N'[dbo].[syscollector_collection_sets_internal]'))) BEGIN PRINT 'Altering table [dbo].[syscollector_collection_sets_internal]...' ALTER TABLE [dbo].[syscollector_collection_sets_internal] ADD dump_on_any_error bit default 0 END IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'dump_on_codes' AND id = OBJECT_ID(N'[dbo].[syscollector_collection_sets_internal]'))) BEGIN PRINT 'Altering table [dbo].[syscollector_collection_sets_internal]...' ALTER TABLE [dbo].[syscollector_collection_sets_internal] ADD dump_on_codes nvarchar(max) NULL END END GO IF (NOT OBJECT_ID(N'[dbo].[syscollector_collection_sets]', 'V') IS NULL) BEGIN PRINT 'Dropping view [dbo].[syscollector_collection_sets]...' DROP VIEW [dbo].[syscollector_collection_sets] END GO PRINT 'Creating view [dbo].[syscollector_collection_sets]...' GO CREATE VIEW [dbo].[syscollector_collection_sets] AS SELECT s.collection_set_id, s.collection_set_uid, CASE WHEN s.name_id IS NULL THEN s.name ELSE FORMATMESSAGE(s.name_id) END AS name, s.target, s.is_system, s.is_running, s.collection_mode, s.proxy_id, s.schedule_uid, s.collection_job_id, s.upload_job_id, s.logging_level, s.days_until_expiration, CASE WHEN s.description_id IS NULL THEN s.description ELSE FORMATMESSAGE(s.description_id) END AS description, s.dump_on_any_error, s.dump_on_codes FROM [dbo].[syscollector_collection_sets_internal] AS s GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_verify_collection_set]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_verify_collection_set]...' DROP PROCEDURE [dbo].[sp_syscollector_verify_collection_set] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_verify_collection_set]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_verify_collection_set] @collection_set_id int = NULL OUTPUT, @name sysname = NULL OUTPUT AS BEGIN IF (@name IS NOT NULL) BEGIN -- Remove any leading/trailing spaces from parameters SET @name = NULLIF(LTRIM(RTRIM(@name)), N'') END IF (@collection_set_id IS NULL AND @name IS NULL) BEGIN RAISERROR(14624, -1, -1, '@collection_set_id, @name') RETURN(1) END IF (@collection_set_id IS NOT NULL AND @name IS NOT NULL) BEGIN IF (NOT EXISTS(SELECT * FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id AND name = @name)) BEGIN DECLARE @errMsg NVARCHAR(196) SELECT @errMsg = CONVERT(NVARCHAR(36), @collection_set_id) + ', ' + @name RAISERROR(14262, -1, -1, '@collection_set_id, @name', @errMsg) RETURN(1) END END -- Check id ELSE IF (@collection_set_id IS NOT NULL) BEGIN SELECT @name = name FROM dbo.syscollector_collection_sets WHERE (collection_set_id = @collection_set_id) -- the view would take care of all the permissions issues. IF (@name IS NULL) BEGIN DECLARE @collection_set_id_as_char VARCHAR(36) SELECT @collection_set_id_as_char = CONVERT(VARCHAR(36), @collection_set_id) RAISERROR(14262, -1, -1, '@collection_set_id', @collection_set_id_as_char) RETURN(1) -- Failure END END -- Check name ELSE IF (@name IS NOT NULL) BEGIN -- get the corresponding collection_set_id (if the collection set exists) SELECT @collection_set_id = collection_set_id FROM dbo.syscollector_collection_sets WHERE (name = @name) -- the view would take care of all the permissions issues. IF (@collection_set_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END END RETURN (0) END GO -- Create one schedule that starts when SQL Agent starts so that all continuous -- collection jobs can attach to it, the schedule has to be accessible to the internal dc user that owns agent objects -- EXECUTE AS USER = 'MS_DataCollectorInternalUser'; IF (NOT EXISTS (SELECT * FROM sysschedules_localserver_view WHERE name = N'RunAsSQLAgentServiceStartSchedule')) BEGIN EXEC dbo.sp_add_schedule @schedule_name = N'RunAsSQLAgentServiceStartSchedule', @freq_type = 0x40, -- FREQTYPE_AUTOSTART @freq_interval = 1 END -- REVERT; GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_create_jobs]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_create_jobs]...' DROP PROCEDURE [dbo].[sp_syscollector_create_jobs] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_create_jobs]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_create_jobs] @collection_set_id int, @collection_set_uid uniqueidentifier, @collection_set_name sysname, @proxy_id int = NULL, @schedule_id int = NULL, @collection_mode smallint, @collection_job_id uniqueidentifier OUTPUT, @upload_job_id uniqueidentifier OUTPUT AS BEGIN SET NOCOUNT ON DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_syscollector_create_jobs ELSE BEGIN TRANSACTION BEGIN TRY -- job step names and commands shared between collection modes DECLARE @collection_set_id_as_char nvarchar(36) DECLARE @collection_step_command nvarchar(512) DECLARE @upload_step_command nvarchar(512) DECLARE @autostop_step_command nvarchar(512) DECLARE @purge_step_command nvarchar(1024) DECLARE @collection_step_name sysname DECLARE @upload_step_name sysname DECLARE @autostop_step_name sysname DECLARE @purge_step_name sysname DECLARE @job_name sysname DECLARE @job_id uniqueidentifier DECLARE @description nvarchar(512) IF(@collection_set_id IS NOT NULL) BEGIN SET @collection_set_id_as_char = CONVERT(NVARCHAR(36), @collection_set_id) SET @collection_step_command = N'dcexec -c -s ' + @collection_set_id_as_char + N' -i "$(ESCAPE_DQUOTE(MACH))\$(ESCAPE_DQUOTE(INST))"' + N' -m ' + CONVERT(NVARCHAR(36), @collection_mode); SET @upload_step_command = N'dcexec -u -s ' + @collection_set_id_as_char + N' -i "$(ESCAPE_DQUOTE(MACH))\$(ESCAPE_DQUOTE(INST))"'; SET @autostop_step_command = N'exec dbo.sp_syscollector_stop_collection_set @collection_set_id=' + @collection_set_id_as_char + N', @stop_collection_job = 0'; -- do not stop the collection job, otherwise you will abort yourself! SET @purge_step_command = N' EXEC [dbo].[sp_syscollector_purge_collection_logs] ' END -- verify that the proxy_id exists IF (@proxy_id IS NOT NULL) BEGIN DECLARE @proxy_name sysname DECLARE @retVal int -- this will throw an error of proxy_id does not exist EXEC @retVal = msdb.dbo.sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retVal <> 0) RETURN (0) END -- add jobs, job steps and attach schedule separately for different modes IF (@collection_mode = 1) -- non-cached mode BEGIN -- create 1 job and 2 steps, first for collection & upload, second for log purging SET @job_name = N'collection_set_' + @collection_set_id_as_char + '_noncached_collect_and_upload' SET @collection_step_name = @job_name + '_collect' SET @upload_step_name = @job_name + '_upload' SET @purge_step_name = @job_name + '_purge_logs' SET @description = N'Data Collector job for collection set ' + QUOTENAME(@collection_set_name) -- add agent job and job server EXEC dbo.sp_add_job @job_name = @job_name, @category_id = 8, -- N'Data Collector' @enabled = 0, @description = @description, @job_id = @job_id OUTPUT EXEC dbo.sp_add_jobserver @job_id = @job_id, @server_name = N'(local)' -- add both collect and upload job steps to the same job EXEC dbo.sp_add_jobstep @job_id = @job_id, @step_name = @collection_step_name, @subsystem = 'CMDEXEC', @command = @collection_step_command, @on_success_action = 3, -- go to the next job step (purge the log) @on_fail_action = 2, -- quit with failure @proxy_id = @proxy_id, @flags = 16 -- Write log to table (append to existing history) EXEC dbo.sp_add_jobstep @job_id = @job_id, @step_name = @purge_step_name, @subsystem = 'TSQL', @database_name = 'msdb', @command = @purge_step_command, @on_success_action = 3, -- go to the next job step (upload) @on_fail_action = 3, -- go to the next job step (upload) @proxy_id = NULL, @flags = 16 -- write log to table (append to existing history) EXEC dbo.sp_add_jobstep @job_id = @job_id, @step_name = @upload_step_name, @subsystem = 'CMDEXEC', @command = @upload_step_command, @on_success_action = 1, -- quit with success @on_fail_action = 2, -- quit with failure @proxy_id = @proxy_id, @flags = 16 -- Write log to table (append to existing history) IF @schedule_id IS NOT NULL BEGIN -- attach the schedule EXEC dbo.sp_attach_schedule @job_id = @job_id, @schedule_id = @schedule_id END SET @upload_job_id = @job_id SET @collection_job_id = @job_id END IF (@collection_mode = 0) -- cached mode BEGIN -- create 2 jobs for collect and upload -- add to collect job an extra step that autostops collection called in case collect job fails DECLARE @upload_job_name sysname DECLARE @collection_job_name sysname SET @upload_job_name = N'collection_set_' + @collection_set_id_as_char + '_upload' SET @collection_job_name = N'collection_set_' + @collection_set_id_as_char + '_collection' SET @collection_step_name = @collection_job_name + '_collect' SET @autostop_step_name = @collection_job_name + '_autostop' SET @upload_step_name = @upload_job_name + '_upload' SET @purge_step_name = @upload_job_name + '_purge_logs' -- modify the collection step to pass in the stop event name passed in by agent SET @collection_step_command = @collection_step_command + N' -e $' + N'(ESCAPE_NONE(' + N'STOPEVENT))' -- add agent job and job server EXEC dbo.sp_add_job @job_name = @upload_job_name, @category_id = 8, -- N'Data Collector' @enabled = 0, @job_id = @upload_job_id OUTPUT EXEC dbo.sp_add_jobserver @job_id = @upload_job_id, @server_name = N'(local)' EXEC dbo.sp_add_job @job_name = @collection_job_name, @category_id = 8, -- N'Data Collector' @enabled = 0, @job_id = @collection_job_id OUTPUT EXEC dbo.sp_add_jobserver @job_id = @collection_job_id, @server_name = N'(local)' -- add upload job step to upload job and collection job -- step to collection job separately EXEC dbo.sp_add_jobstep @job_id = @upload_job_id, @step_name = @purge_step_name, @subsystem = 'TSQL', @database_name = 'msdb', @command = @purge_step_command, @on_success_action = 3, -- go to next job step (upload) @on_fail_action = 3, -- go to next job step (upload) @proxy_id = NULL, @flags = 16 -- write log to table (append to existing history) EXEC dbo.sp_add_jobstep @job_id = @upload_job_id, @step_name = @upload_step_name, @subsystem = 'CMDEXEC', @command = @upload_step_command, @on_success_action = 1, -- quit with success @on_fail_action = 2, -- quit with failure @proxy_id = @proxy_id EXEC dbo.sp_add_jobstep @job_id = @collection_job_id, @step_name = @collection_step_name, @subsystem = 'CMDEXEC', @command = @collection_step_command, @on_success_action = 1, -- quit with success @on_fail_action = 3, -- go to next job step (auto-stop) @proxy_id = @proxy_id, @flags = 80 -- 16 (write log to table (append to existing history) -- + 64 (create a stop event and pass it to the command line) EXEC dbo.sp_add_jobstep @job_id = @collection_job_id, @step_name = @autostop_step_name, @subsystem = 'TSQL', @database_name = 'msdb', @command = @autostop_step_command, @on_success_action = 2, -- quit with failure @on_fail_action = 2, -- quit with failure @proxy_id = NULL, @flags = 16 -- write log to table (append to existing history) -- attach the input schedule to the upload job EXEC dbo.sp_attach_schedule @job_id = @upload_job_id, @schedule_id = @schedule_id -- attach the RunAsSQLAgentServiceStartSchedule to the collection job EXEC dbo.sp_attach_schedule @job_id = @collection_job_id, @schedule_name = N'RunAsSQLAgentServiceStartSchedule' END IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_syscollector_create_jobs DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_create_collection_set]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_create_collection_set]...' DROP PROCEDURE [dbo].[sp_syscollector_create_collection_set] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_create_collection_set]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_create_collection_set] @name sysname, @target nvarchar(128) = NULL, @collection_mode smallint = 0, -- 0: cached, 1: non-cached @days_until_expiration smallint = 730, -- two years @proxy_id int = NULL, -- mutual exclusive; must specify either proxy_id or proxy_name to identify the proxy @proxy_name sysname = NULL, @schedule_uid uniqueidentifier = NULL, @schedule_name sysname = NULL, -- mutual exclusive; must specify either schedule_uid or schedule_name to identify the schedule @logging_level smallint = 1, @description nvarchar(4000) = NULL, @collection_set_id int OUTPUT, @collection_set_uid uniqueidentifier = NULL OUTPUT WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser' AS BEGIN DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_create_collection_set ELSE BEGIN TRANSACTION BEGIN TRY -- Security check (role membership) EXECUTE AS CALLER; IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN REVERT; RAISERROR(14677, -1, -1, 'dc_admin') RETURN (1) END REVERT; -- Remove any leading/trailing spaces from parameters SET @name = NULLIF(LTRIM(RTRIM(@name)), N'') SET @proxy_name = NULLIF(LTRIM(RTRIM(@proxy_name)), N'') SET @schedule_name = NULLIF(LTRIM(RTRIM(@schedule_name)), N'') SET @target = NULLIF(LTRIM(RTRIM(@target)), N'') SET @description = LTRIM(RTRIM(@description)) IF (@name IS NULL) BEGIN RAISERROR(21263, -1, -1, '@name') RETURN (1) END -- can't have both name and uid for the schedule IF (@schedule_uid IS NOT NULL) AND (@schedule_name IS NOT NULL) BEGIN RAISERROR(14373, -1, -1, '@schedule_uid', '@schedule_name') RETURN (1) END -- Execute the check for the schedule as caller to ensure only schedules owned by caller can be attached EXECUTE AS CALLER; DECLARE @schedule_id int IF (@schedule_uid IS NOT NULL) BEGIN SElECT @schedule_id = schedule_id FROM sysschedules_localserver_view WHERE @schedule_uid = schedule_uid IF (@schedule_id IS NULL) BEGIN DECLARE @schedule_uid_as_char VARCHAR(36) SELECT @schedule_uid_as_char = CONVERT(VARCHAR(36), @schedule_uid) REVERT; RAISERROR(14262, -1, -1, N'@schedule_uid', @schedule_uid_as_char) RETURN (1) END END ELSE IF (@schedule_name IS NOT NULL) BEGIN SELECT @schedule_id = schedule_id, @schedule_uid = schedule_uid FROM sysschedules_localserver_view WHERE name = @schedule_name IF (@schedule_id IS NULL) BEGIN REVERT; RAISERROR(14262, -1, -1, N'@schedule_name', @schedule_name) RETURN (1) END END REVERT; -- if collection_mode is cached, make sure schedule_id is not null IF (@collection_mode = 0 AND @schedule_id IS NULL) BEGIN RAISERROR(14683, -1, -1) RETURN (1) END IF (@proxy_id IS NOT NULL) OR (@proxy_name IS NOT NULL) BEGIN -- check if the proxy exists EXEC sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT -- check if proxy_id is granted to dc_admin IF (@proxy_id NOT IN (SELECT proxy_id FROM sysproxylogin WHERE sid = USER_SID(USER_ID('dc_admin')) ) ) BEGIN RAISERROR(14523, -1, -1, N'dc_admin', @proxy_name) RETURN (1) END END IF (@collection_mode < 0 OR @collection_mode > 1) BEGIN RAISERROR(14266, -1, -1, '@collection_mode', '0, 1') RETURN (1) END IF (@logging_level < 0 OR @logging_level > 2) BEGIN RAISERROR(14266, -1, -1, '@logging_level', '0, 1, or 2') RETURN (1) END IF (@collection_set_uid IS NULL) BEGIN SET @collection_set_uid = NEWID() END IF (@days_until_expiration < 0) BEGIN RAISERROR(14266, -1, -1, '@days_until_expiration', '>= 0') RETURN (1) END INSERT INTO [dbo].[syscollector_collection_sets_internal] ( collection_set_uid, schedule_uid, name, target, is_running, proxy_id, is_system, upload_job_id, collection_job_id, collection_mode, logging_level, days_until_expiration, description ) VALUES ( @collection_set_uid, @schedule_uid, @name, @target, 0, @proxy_id, 0, NULL, NULL, @collection_mode, @logging_level, @days_until_expiration, @description ) SET @collection_set_id = SCOPE_IDENTITY() IF (@collection_set_id IS NULL) BEGIN DECLARE @collection_set_id_as_char VARCHAR(36) SELECT @collection_set_id_as_char = CONVERT(VARCHAR(36), @collection_set_id) RAISERROR(14262, -1, -1, '@collection_set_id', @collection_set_id_as_char) RETURN (1) END IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_create_collection_set DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_update_job_proxy]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_update_job_proxy]...' DROP PROCEDURE [dbo].[sp_syscollector_update_job_proxy] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_update_job_proxy]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_update_job_proxy] @job_id uniqueidentifier, @proxy_id int = NULL, @proxy_name sysname = NULL AS BEGIN -- update the proxy id for the all job steps DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_syscollector_update_proxy ELSE BEGIN TRANSACTION BEGIN TRY DECLARE @step_id INT DECLARE cursor_job_steps CURSOR FOR SELECT step_id FROM dbo.sysjobsteps WHERE job_id = @job_id AND subsystem = N'CMDEXEC' OPEN cursor_job_steps FETCH NEXT FROM cursor_job_steps INTO @step_id WHILE @@FETCH_STATUS = 0 BEGIN EXEC dbo.sp_update_jobstep @job_id = @job_id, @step_id = @step_id, @proxy_id = @proxy_id, @proxy_name = @proxy_name FETCH NEXT FROM cursor_job_steps INTO @step_id END CLOSE cursor_job_steps DEALLOCATE cursor_job_steps IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_syscollector_update_proxy DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_update_collection_set_internal]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_update_collection_set_internal]...' DROP PROCEDURE [dbo].[sp_syscollector_update_collection_set_internal] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_update_collection_set_internal]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_update_collection_set_internal] @collection_set_id int, @collection_set_uid uniqueidentifier, @name sysname, @new_name sysname, @target nvarchar(128), @collection_mode smallint, @days_until_expiration smallint, @proxy_id int, @proxy_name sysname, @schedule_uid uniqueidentifier, @schedule_name sysname, @logging_level smallint, @description nvarchar(4000), @schedule_id int, @old_collection_mode smallint, @old_proxy_id int, @old_collection_job_id uniqueidentifier, @old_upload_job_id uniqueidentifier AS BEGIN DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_update_collection_set ELSE BEGIN TRANSACTION BEGIN TRY DECLARE @old_upload_schedule_id int DECLARE @old_upload_schedule_uid uniqueidentifier SELECT @old_upload_schedule_id = sv.schedule_id, @old_upload_schedule_uid = cs.schedule_uid FROM dbo.syscollector_collection_sets cs JOIN sysschedules_localserver_view sv ON (cs.schedule_uid = sv.schedule_uid) WHERE collection_set_id = @collection_set_id -- update job names, schedule, and collection mode in a transaction to maintain a consistent state in case of failures IF (@collection_mode IS NOT NULL AND @collection_mode != @old_collection_mode) BEGIN IF (@schedule_id IS NULL) BEGIN -- if no schedules is supplied as a parameter to this update SP, -- we can use the one that is already in the collection set table SET @schedule_uid = @old_upload_schedule_uid SELECT @schedule_id = schedule_id FROM sysschedules_localserver_view WHERE @schedule_uid = schedule_uid END IF (@schedule_name IS NOT NULL AND @schedule_name = N'') BEGIN SET @schedule_id = NULL END -- make sure there exists a schedule we can use IF (@old_collection_mode = 1 AND @schedule_id IS NULL) -- a switch from non-cached to cached mode require a schedule BEGIN -- no schedules specified in input or collection set table, raise error RAISERROR(14683, -1, -1) RETURN (1) END -- Only update the jobs if we have jobs already created. Otherwise the right -- jobs will be created when the collection set starts for the first time. IF (@old_collection_job_id IS NOT NULL AND @old_upload_job_id IS NOT NULL) BEGIN -- create new jobs DECLARE @collection_job_id uniqueidentifier DECLARE @upload_job_id uniqueidentifier DECLARE @collection_set_name sysname; SET @collection_set_name = ISNULL(@new_name, @name); EXEC [dbo].[sp_syscollector_create_jobs] @collection_set_id = @collection_set_id, @collection_set_uid = @collection_set_uid, @collection_set_name = @collection_set_name, @proxy_id = @proxy_id, @schedule_id = @schedule_id, @collection_mode = @collection_mode, @collection_job_id = @collection_job_id OUTPUT, @upload_job_id = @upload_job_id OUTPUT UPDATE [dbo].[syscollector_collection_sets_internal] SET upload_job_id = @upload_job_id, collection_job_id = @collection_job_id WHERE @collection_set_id = collection_set_id -- drop old upload and collection jobs EXEC dbo.sp_syscollector_delete_jobs @collection_job_id = @old_collection_job_id, @upload_job_id = @old_upload_job_id, @schedule_id = @old_upload_schedule_id, @collection_mode = @old_collection_mode END END ELSE -- collection mode unchanged, we do not have to recreate the jobs BEGIN -- we need to update the proxy id for all job steps IF (@old_proxy_id <> @proxy_id) OR (@old_proxy_id IS NULL AND @proxy_id IS NOT NULL) BEGIN IF (@old_collection_job_id IS NOT NULL) BEGIN EXEC dbo.sp_syscollector_update_job_proxy @job_id = @old_collection_job_id, @proxy_id = @proxy_id END IF (@old_upload_job_id IS NOT NULL) BEGIN EXEC dbo.sp_syscollector_update_job_proxy @job_id = @old_upload_job_id, @proxy_id = @proxy_id END END IF (@proxy_name = N'' AND @old_proxy_id IS NOT NULL) BEGIN IF (@old_collection_job_id IS NOT NULL) BEGIN EXEC dbo.sp_syscollector_update_job_proxy @job_id = @old_collection_job_id, @proxy_name = @proxy_name END IF (@old_upload_job_id IS NOT NULL) BEGIN EXEC dbo.sp_syscollector_update_job_proxy @job_id = @old_upload_job_id, @proxy_name = @proxy_name END END -- need to update the schedule IF (@old_upload_schedule_id <> @schedule_id) OR (@old_upload_schedule_id IS NULL AND @schedule_id IS NOT NULL) BEGIN -- detach the old schedule IF (@old_upload_job_id IS NOT NULL) AND (@old_upload_schedule_id IS NOT NULL) BEGIN EXEC dbo.sp_detach_schedule @job_id = @old_upload_job_id, @schedule_id = @old_upload_schedule_id, @delete_unused_schedule = 0 END -- attach the new schedule IF (@old_upload_job_id IS NOT NULL) BEGIN EXEC dbo.sp_attach_schedule @job_id = @old_upload_job_id, @schedule_id = @schedule_id END END -- special case - remove the existing schedule IF (@schedule_name = N'') AND (@old_upload_schedule_id IS NOT NULL) BEGIN EXEC dbo.sp_detach_schedule @job_id = @old_upload_job_id, @schedule_id = @old_upload_schedule_id, @delete_unused_schedule = 0 END END -- after the all operations succeed, update the sollection_sets table DECLARE @new_proxy_id int SET @new_proxy_id = @proxy_id IF (@proxy_name = N'') SET @new_proxy_id = NULL UPDATE [dbo].[syscollector_collection_sets_internal] SET name = ISNULL(@new_name, name), target = ISNULL(@target, target), proxy_id = @new_proxy_id, collection_mode = ISNULL(@collection_mode, collection_mode), logging_level = ISNULL(@logging_level, logging_level), days_until_expiration = ISNULL(@days_until_expiration, days_until_expiration) WHERE @collection_set_id = collection_set_id IF (@schedule_uid IS NOT NULL OR @schedule_name IS NOT NULL) BEGIN IF (@schedule_name = N'') SET @schedule_uid = NULL UPDATE [dbo].[syscollector_collection_sets_internal] SET schedule_uid = @schedule_uid WHERE @collection_set_id = collection_set_id END IF (@description IS NOT NULL) BEGIN IF (@description = N'') SET @description = NULL UPDATE [dbo].[syscollector_collection_sets_internal] SET description = @description WHERE @collection_set_id = collection_set_id END IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_update_collection_set DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_update_collection_set]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_update_collection_set]...' DROP PROCEDURE [dbo].[sp_syscollector_update_collection_set] END GO -- Updates a collection set. These are the steps involved -- 1- Security checks -- 2- Stop the collection set if currently running (sp_syscollector_stop_collection_set) -- 3- Perform the actual update transactionally (sp_syscollector_update_collection_set_internal) -- 4- Restart the collection set if it was running (sp_syscollector_start_collection_set PRINT 'Creating procedure [dbo].[sp_syscollector_update_collection_set]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_update_collection_set] @collection_set_id int = NULL, @name sysname = NULL, @new_name sysname = NULL, @target nvarchar(128) = NULL, @collection_mode smallint = NULL, -- 0: cached, 1: non-cached @days_until_expiration smallint = NULL, @proxy_id int = NULL, -- mutual exclusive; must specify either proxy_id or proxy_name to identify the proxy @proxy_name sysname = NULL, -- @proxy_name = N'' is a special case to allow change of an existing proxy with NULL @schedule_uid uniqueidentifier = NULL, -- mutual exclusive; must specify either schedule_uid or schedule_name to identify the schedule @schedule_name sysname = NULL, -- @schedule_name = N'' is a special case to allow change of an existing schedule with NULL @logging_level smallint = NULL, @description nvarchar(4000) = NULL -- @description = N'' is a special case to allow change of an existing description with NULL WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser' AS BEGIN -- Security checks will be performed against caller's security context EXECUTE AS CALLER; -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN REVERT; RAISERROR(14677, -1, -1, 'dc_operator') RETURN (1) END -- Security checks (restrict functionality for non-dc_admin-s) IF (((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1)) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) AND (@new_name IS NOT NULL)) BEGIN REVERT; RAISERROR(14676, -1, -1, '@new_name', 'dc_admin') RETURN (1) END IF (((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1)) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) AND (@target IS NOT NULL)) BEGIN REVERT; RAISERROR(14676, -1, -1, '@target', 'dc_admin') RETURN (1) END IF (((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1)) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) AND (@proxy_id IS NOT NULL)) BEGIN REVERT; RAISERROR(14676, -1, -1, '@proxy_id', 'dc_admin') RETURN (1) END IF (((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1)) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) AND (@collection_mode IS NOT NULL)) BEGIN REVERT; RAISERROR(14676, -1, -1, '@collection_mode', 'dc_admin') RETURN (1) END IF (((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1)) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) AND (@description IS NOT NULL)) BEGIN REVERT; RAISERROR(14676, -1, -1, '@description', 'dc_admin') RETURN (1) END IF (((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1)) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) AND (@days_until_expiration IS NOT NULL)) BEGIN REVERT; RAISERROR(14676, -1, -1, '@days_until_expiration', 'dc_admin') RETURN (1) -- Failure END -- Security checks done, reverting now to internal data collector user security context REVERT; -- check for inconsistencies/errors in the parameters DECLARE @retVal int EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT IF (@retVal <> 0) RETURN (1) IF (@collection_mode IS NOT NULL AND (@collection_mode < 0 OR @collection_mode > 1)) BEGIN RAISERROR(14266, -1, -1, '@collection_mode', '0, 1') RETURN (1) END IF (@logging_level IS NOT NULL AND (@logging_level < 0 OR @logging_level > 2)) BEGIN RAISERROR(14266, -1, -1, '@logging_level', '0, 1, or 2') RETURN(1) END IF (LEN(@new_name) = 0) BEGIN RAISERROR(21263, -1, -1, '@new_name') RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SET @target = NULLIF(LTRIM(RTRIM(@target)), N'') SET @new_name = NULLIF(LTRIM(RTRIM(@new_name)), N'') SET @description = LTRIM(RTRIM(@description)) DECLARE @is_system bit DECLARE @is_running bit DECLARE @collection_set_uid uniqueidentifier DECLARE @old_collection_mode smallint DECLARE @old_upload_job_id uniqueidentifier DECLARE @old_collection_job_id uniqueidentifier DECLARE @old_proxy_id int SELECT @is_running = is_running, @is_system = is_system, @collection_set_uid = collection_set_uid, @old_collection_mode = collection_mode, @old_collection_job_id = collection_job_id, @old_upload_job_id = upload_job_id, @old_proxy_id = proxy_id FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id IF (@is_system = 1 AND ( @new_name IS NOT NULL OR @description IS NOT NULL)) BEGIN -- cannot update, delete, or add new collection items to a system collection set RAISERROR(14696, -1, -1); RETURN (1) END IF (@proxy_id IS NOT NULL) OR (@proxy_name IS NOT NULL AND @proxy_name <> N'') BEGIN -- verify the proxy exists EXEC sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT -- check if proxy_id is granted to dc_admin IF (@proxy_id NOT IN (SELECT proxy_id FROM sysproxylogin WHERE sid = USER_SID(USER_ID('dc_admin')) ) ) BEGIN RAISERROR(14523, -1, -1, N'dc_admin', @proxy_name) RETURN (1) END END ELSE -- if no proxy_id provided, get the existing proxy_id, might need it later to create new jobs BEGIN SET @proxy_id = @old_proxy_id END -- can't have both uid and name passed for the schedule IF (@schedule_uid IS NOT NULL) AND (@schedule_name IS NOT NULL AND @schedule_name <> N'') BEGIN RAISERROR(14373, -1, -1, '@schedule_uid', '@schedule_name') RETURN (1) END -- check if it attempts to remove a schedule when the collection mode is cached IF (@schedule_name = N'' AND @collection_mode = 0) OR (@collection_mode IS NULL AND @old_collection_mode = 0 AND @schedule_name = N'') BEGIN RAISERROR(14683, -1, -1) RETURN (1) END -- Execute the check for the schedule as caller to ensure only schedules owned by caller can be attached EXECUTE AS CALLER; DECLARE @schedule_id int SET @schedule_id = NULL IF (@schedule_uid IS NOT NULL) BEGIN SElECT @schedule_id = schedule_id FROM sysschedules_localserver_view WHERE @schedule_uid = schedule_uid IF (@schedule_id IS NULL) BEGIN DECLARE @schedule_uid_as_char VARCHAR(36) SELECT @schedule_uid_as_char = CONVERT(VARCHAR(36), @schedule_uid) REVERT; RAISERROR(14262, -1, -1, N'@schedule_uid', @schedule_uid_as_char) RETURN (1) END END ELSE IF (@schedule_name IS NOT NULL AND @schedule_name <> N'') -- @schedule_name is not null BEGIN SELECT @schedule_id = schedule_id, @schedule_uid = schedule_uid FROM sysschedules_localserver_view WHERE name = @schedule_name IF (@schedule_id IS NULL) BEGIN REVERT; RAISERROR(14262, -1, -1, N'@schedule_name', @schedule_name) RETURN (1) END END REVERT; -- Stop the collection set if it is currently running IF (@is_running = 1 AND ( @new_name IS NOT NULL OR @target IS NOT NULL OR @proxy_id IS NOT NULL OR @logging_level IS NOT NULL OR @collection_mode IS NOT NULL)) BEGIN EXEC @retVal = sp_syscollector_stop_collection_set @collection_set_id = @collection_set_id IF (@retVal <> 0) RETURN (1) END -- Passed all necessary checks, go ahead with the update EXEC @retVal = sp_syscollector_update_collection_set_internal @collection_set_id = @collection_set_id, @collection_set_uid = @collection_set_uid, @name = @name, @new_name = @new_name, @target = @target, @collection_mode = @collection_mode, @days_until_expiration = @days_until_expiration, @proxy_id = @proxy_id, @proxy_name = @proxy_name, @schedule_uid = @schedule_uid, @schedule_name = @schedule_name, @logging_level = @logging_level, @description = @description, @schedule_id = @schedule_id, @old_collection_mode = @old_collection_mode, @old_proxy_id = @old_proxy_id, @old_collection_job_id = @old_collection_job_id, @old_upload_job_id = @old_upload_job_id IF (@retVal <> 0) RETURN (1) -- Restart the collection set if it has been already running IF (@is_running = 1) BEGIN EXEC @retVal = sp_syscollector_start_collection_set @collection_set_id = @collection_set_id IF (@retVal <> 0) RETURN (1) END RETURN (0) END GO IF (NOT OBJECT_ID('dbo.sp_syscollector_configure_sql_dumper', 'P') IS NULL) BEGIN DROP PROCEDURE [dbo].[sp_syscollector_configure_sql_dumper] END GO PRINT '' PRINT 'Creating procedure [dbo].[sp_syscollector_configure_sql_dumper]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_configure_sql_dumper] @collection_set_id int = NULL, @name sysname = NULL, @dump_on_any_error bit = NULL, -- configure SQL dumper to dump on any SSIS errors @dump_on_codes nvarchar(max) = NULL -- configure SQL dumper to dump when we hit one of the specified SSIS errors. Set to N'' to remove the codes. AS BEGIN -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_admin') RETURN(1) -- Failure END DECLARE @retVal int EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT IF (@retVal <> 0) RETURN (1) DECLARE @is_running bit SELECT @is_running = is_running FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id IF (@is_running = 1) BEGIN RAISERROR(14711, 0, 1) END IF (@dump_on_codes = N'') BEGIN UPDATE [dbo].[syscollector_collection_sets_internal] SET dump_on_codes = NULL WHERE @collection_set_id = collection_set_id END ELSE IF (@dump_on_codes IS NOT NULL) BEGIN UPDATE [msdb].[dbo].[syscollector_collection_sets_internal] SET dump_on_codes = @dump_on_codes WHERE @collection_set_id = collection_set_id END IF (@dump_on_any_error IS NOT NULL) BEGIN UPDATE [msdb].[dbo].[syscollector_collection_sets_internal] SET dump_on_any_error = @dump_on_any_error WHERE @collection_set_id = collection_set_id END RETURN (0) END GO --------------------------------------------------------------- -- Collector type --------------------------------------------------------------- IF (OBJECT_ID(N'[dbo].[syscollector_collector_types_internal]', 'U') IS NULL) BEGIN PRINT 'Creating table [dbo].[syscollector_collector_types_internal]...' CREATE TABLE [dbo].[syscollector_collector_types_internal] ( collector_type_uid uniqueidentifier NOT NULL, name sysname NOT NULL, parameter_schema xml NULL, parameter_formatter xml NULL, schema_collection sysname NULL, collection_package_name sysname NOT NULL, collection_package_folderid uniqueidentifier NOT NULL, upload_package_name sysname NOT NULL, upload_package_folderid uniqueidentifier NOT NULL, is_system bit default 0 NOT NULL, CONSTRAINT [PK_syscollector_collector_types_internal] PRIMARY KEY CLUSTERED (collector_type_uid ASC), CONSTRAINT [UQ_syscollector_collection_types_internal_name] UNIQUE (name) ) ALTER TABLE syscollector_collector_types_internal ADD CONSTRAINT [FK_syscollector_collector_types_internal_upload_sysssispackages] FOREIGN KEY(upload_package_folderid, upload_package_name) REFERENCES sysssispackages (folderid, [name]) ALTER TABLE syscollector_collector_types_internal ADD CONSTRAINT [FK_syscollector_collector_types_internal_collection_sysssispackages] FOREIGN KEY(collection_package_folderid, collection_package_name) REFERENCES sysssispackages (folderid, [name]) END GO -- [fn_syscollector_get_package_path] -- This function returns the full path of a SSIS package given the package id IF (NOT OBJECT_ID('[dbo].[fn_syscollector_get_package_path]', 'FN') IS NULL) BEGIN PRINT 'Dropping function [dbo].[fn_syscollector_get_package_path]...' DROP FUNCTION [dbo].[fn_syscollector_get_package_path] END GO PRINT 'Creating function [dbo].[fn_syscollector_get_package_path]...' GO CREATE FUNCTION [dbo].[fn_syscollector_get_package_path] ( @package_id uniqueidentifier ) RETURNS NVARCHAR(4000) AS BEGIN IF @package_id IS NULL RETURN NULL DECLARE @package_path nvarchar(4000) DECLARE @prevfolderid uniqueidentifier DECLARE @folderid uniqueidentifier DECLARE @package_name sysname SET @package_path = '' SELECT @package_name = name, @folderid = folderid FROM dbo.sysssispackages WHERE id = @package_id WHILE (@folderid != '00000000-0000-0000-0000-000000000000') BEGIN SET @prevfolderid = @folderid DECLARE @foldername sysname SELECT @foldername = foldername, @folderid = parentfolderid FROM dbo.sysssispackagefolders WHERE folderid = @prevfolderid SET @package_path = @foldername + N'\\' + @package_path END SET @package_path = N'\\' + @package_path + @package_name RETURN @package_path END GO IF (NOT OBJECT_ID(N'[dbo].[syscollector_collector_types]', 'V') IS NULL) BEGIN PRINT 'Dropping view [dbo].[syscollector_collector_types]...' DROP VIEW [dbo].[syscollector_collector_types] END GO PRINT 'Creating view [dbo].[syscollector_collector_types]...' GO CREATE VIEW [dbo].[syscollector_collector_types] AS SELECT t.collector_type_uid, t.name, t.parameter_schema, t.parameter_formatter, s1.id AS collection_package_id, dbo.fn_syscollector_get_package_path(s1.id) AS collection_package_path, s1.name AS collection_package_name, s2.id AS upload_package_id, dbo.fn_syscollector_get_package_path(s2.id) AS upload_package_path, s2.name AS upload_package_name, t.is_system FROM [dbo].[syscollector_collector_types_internal] AS t, sysssispackages s1, sysssispackages s2 WHERE t.collection_package_folderid = s1.folderid AND t.collection_package_name = s1.name AND t.upload_package_folderid = s2.folderid AND t.upload_package_name = s2.name GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_verify_collector_type]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_verify_collector_type]...' DROP PROCEDURE [dbo].[sp_syscollector_verify_collector_type] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_verify_collector_type]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_verify_collector_type] @collector_type_uid uniqueidentifier = NULL OUTPUT, @name sysname = NULL OUTPUT AS BEGIN IF (@name IS NOT NULL) BEGIN -- Remove any leading/trailing spaces from parameters SET @name = NULLIF(LTRIM(RTRIM(@name)), N'') END IF (@collector_type_uid IS NULL AND @name IS NULL) BEGIN RAISERROR(14624, -1, -1, '@collector_type_uid, @name') RETURN(1) END IF (@collector_type_uid IS NOT NULL AND @name IS NOT NULL) BEGIN IF (NOT EXISTS(SELECT * FROM dbo.syscollector_collector_types WHERE collector_type_uid = @collector_type_uid AND name = @name)) BEGIN DECLARE @errMsg NVARCHAR(196) SELECT @errMsg = CONVERT(NVARCHAR(36), @collector_type_uid) + ', ' + @name RAISERROR(14262, -1, -1, '@collector_type_uid, @name', @errMsg) RETURN(1) END END -- Check id ELSE IF (@collector_type_uid IS NOT NULL) BEGIN SELECT @name = name FROM dbo.syscollector_collector_types WHERE (collector_type_uid = @collector_type_uid) -- the view would take care of all the permissions issues. IF (@name IS NULL) BEGIN DECLARE @collector_type_uid_as_char VARCHAR(36) SELECT @collector_type_uid_as_char = CONVERT(VARCHAR(36), @collector_type_uid) RAISERROR(14262, -1, -1, '@collector_type_uid', @collector_type_uid_as_char) RETURN(1) -- Failure END END -- Check name ELSE IF (@name IS NOT NULL) BEGIN -- get the corresponding collector_type_uid (if the collector type exists) SELECT @collector_type_uid = collector_type_uid FROM dbo.syscollector_collector_types WHERE (name = @name) -- the view would take care of all the permissions issues. IF (@collector_type_uid IS NULL) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END END RETURN (0) END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_create_collector_type]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_create_collector_type]...' DROP PROCEDURE [dbo].[sp_syscollector_create_collector_type] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_create_collector_type]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_create_collector_type] @collector_type_uid uniqueidentifier = NULL OUTPUT, @name sysname, @parameter_schema xml = NULL, @parameter_formatter xml = NULL, @collection_package_id uniqueidentifier, @upload_package_id uniqueidentifier AS BEGIN DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_create_collector_type ELSE BEGIN TRANSACTION BEGIN TRY -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_admin') RETURN (1) END SET @name = NULLIF(LTRIM(RTRIM(@name)), N'') IF (@name IS NULL) BEGIN RAISERROR(21263, -1, -1, '@name', @name) RETURN (1) END IF (@collector_type_uid IS NULL) BEGIN SET @collector_type_uid = NEWID() END IF (NOT EXISTS(SELECT * from sysssispackages WHERE @collection_package_id = id)) BEGIN DECLARE @collection_package_id_as_char VARCHAR(36) SELECT @collection_package_id_as_char = CONVERT(VARCHAR(36), @collection_package_id) RAISERROR(14262, -1, -1, '@collection_package_id', @collection_package_id_as_char) RETURN (1) END IF (NOT EXISTS(SELECT * from sysssispackages WHERE @upload_package_id = id)) BEGIN DECLARE @upload_package_id_as_char VARCHAR(36) SELECT @upload_package_id_as_char = CONVERT(VARCHAR(36), @upload_package_id) RAISERROR(14262, -1, -1, '@upload_package_id', @upload_package_id_as_char) RETURN (1) END DECLARE @collection_package_name sysname DECLARE @collection_package_folderid uniqueidentifier DECLARE @upload_package_name sysname DECLARE @upload_package_folderid uniqueidentifier SELECT @collection_package_name = name, @collection_package_folderid = folderid FROM sysssispackages WHERE @collection_package_id = id SELECT @upload_package_name = name, @upload_package_folderid = folderid FROM sysssispackages WHERE @upload_package_id = id DECLARE @schema_collection sysname IF (@parameter_schema IS NOT NULL) BEGIN SET @schema_collection = N'schema_collection_' + @name WHILE (EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = @schema_collection)) BEGIN SET @schema_collection = LEFT(@schema_collection, 119) + '_' + RIGHT(STR(FLOOR(RAND() * 100000000)),8) END DECLARE @retVal int DECLARE @sql_string nvarchar(2048) DECLARE @param_definition nvarchar(16) SET @param_definition = N'@schema xml' SET @sql_string = N'CREATE XML SCHEMA COLLECTION ' + QUOTENAME(@schema_collection, '[') + N' AS @schema; ' SET @sql_string = @sql_string + N'GRANT EXECUTE ON XML SCHEMA COLLECTION::[dbo].' + QUOTENAME(@schema_collection, '[') + N' TO dc_admin; ' SET @sql_string = @sql_string + N'GRANT VIEW DEFINITION ON XML SCHEMA COLLECTION::[dbo].' + QUOTENAME(@schema_collection, '[') + N' TO dc_admin; ' EXEC sp_executesql @sql_string, @param_definition, @schema = @parameter_schema END INSERT INTO [dbo].[syscollector_collector_types_internal] ( collector_type_uid, name, parameter_schema, parameter_formatter, schema_collection, collection_package_name, collection_package_folderid, upload_package_name, upload_package_folderid ) VALUES ( @collector_type_uid, @name, @parameter_schema, @parameter_formatter, @schema_collection, @collection_package_name, @collection_package_folderid, @upload_package_name, @upload_package_folderid ) IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_create_collector_type DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_update_collector_type]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_update_collector_type]...' DROP PROCEDURE [dbo].[sp_syscollector_update_collector_type] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_update_collector_type]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_update_collector_type] @collector_type_uid uniqueidentifier = NULL, @name sysname = NULL, @parameter_schema xml = NULL, @parameter_formatter xml = NULL, @collection_package_id uniqueidentifier, @upload_package_id uniqueidentifier AS BEGIN DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_update_collector_type ELSE BEGIN TRANSACTION BEGIN TRY -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_admin') RETURN (1) END -- Check the validity of the name/uid pair DECLARE @retVal int EXEC @retVal = dbo.sp_syscollector_verify_collector_type @collector_type_uid OUTPUT, @name OUTPUT IF (@retVal <> 0) RETURN (1) DECLARE @old_parameter_schema xml DECLARE @old_parameter_formatter xml DECLARE @old_collection_package_id uniqueidentifier DECLARE @old_upload_package_id uniqueidentifier SELECT @old_parameter_schema = parameter_schema, @old_parameter_formatter = parameter_formatter, @old_collection_package_id = collection_package_id, @old_upload_package_id = upload_package_id FROM [dbo].[syscollector_collector_types] WHERE name = @name AND collector_type_uid = @collector_type_uid IF (@collection_package_id IS NULL) BEGIN SET @collection_package_id = @old_collection_package_id END ELSE IF (NOT EXISTS(SELECT * from sysssispackages WHERE @collection_package_id = id)) BEGIN DECLARE @collection_package_id_as_char VARCHAR(36) SELECT @collection_package_id_as_char = CONVERT(VARCHAR(36), @collection_package_id) RAISERROR(14262, -1, -1, '@collection_package_id', @collection_package_id_as_char) RETURN (1) END IF (@upload_package_id IS NULL) BEGIN SET @upload_package_id = @old_upload_package_id END ELSE IF (NOT EXISTS(SELECT * from sysssispackages WHERE @upload_package_id = id)) BEGIN DECLARE @upload_package_id_as_char VARCHAR(36) SELECT @upload_package_id_as_char = CONVERT(VARCHAR(36), @upload_package_id) RAISERROR(14262, -1, -1, '@upload_package_id', @upload_package_id_as_char) RETURN (1) END DECLARE @collection_package_name sysname DECLARE @collection_package_folderid uniqueidentifier DECLARE @upload_package_name sysname DECLARE @upload_package_folderid uniqueidentifier SELECT @collection_package_name = name, @collection_package_folderid = folderid FROM sysssispackages WHERE @collection_package_id = id SELECT @upload_package_name = name, @upload_package_folderid = folderid FROM sysssispackages WHERE @upload_package_id = id DECLARE @schema_collection sysname IF (@parameter_schema IS NULL) BEGIN SET @parameter_schema = @old_parameter_schema END ELSE BEGIN SELECT @schema_collection = schema_collection FROM [dbo].[syscollector_collector_types_internal] WHERE name = @name AND collector_type_uid = @collector_type_uid -- if a previous xml schema collection existed with the same name, drop it in favor of the new schema IF (EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = @schema_collection)) BEGIN DECLARE @sql_drop_schema nvarchar(512) SET @sql_drop_schema = N'DROP XML SCHEMA COLLECTION ' + QUOTENAME(@schema_collection) EXECUTE sp_executesql @sql_drop_schema END -- recreate it with the new schema DECLARE @sql_create_schema nvarchar(2048) DECLARE @param_definition nvarchar(16) SET @param_definition = N'@schema xml' SET @sql_create_schema = N'CREATE XML SCHEMA COLLECTION ' + QUOTENAME(@schema_collection) + N' AS @schema; ' SET @sql_create_schema = @sql_create_schema + N'GRANT EXECUTE ON XML SCHEMA COLLECTION::[dbo].' + QUOTENAME(@schema_collection) + N' TO dc_admin; ' SET @sql_create_schema = @sql_create_schema + N'GRANT VIEW DEFINITION ON XML SCHEMA COLLECTION::[dbo].' + QUOTENAME(@schema_collection) + N' TO dc_admin; ' EXEC sp_executesql @sql_create_schema, @param_definition, @schema = @parameter_schema END UPDATE [dbo].[syscollector_collector_types_internal] SET parameter_schema = @parameter_schema, parameter_formatter = @parameter_formatter, schema_collection = @schema_collection, collection_package_name = @collection_package_name, collection_package_folderid = @collection_package_folderid, upload_package_name = @upload_package_name, upload_package_folderid = @upload_package_folderid WHERE @collector_type_uid = collector_type_uid AND @name = name IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_update_collector_type DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_validate_xml]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_validate_xml]...' DROP PROCEDURE [dbo].[sp_syscollector_validate_xml] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_validate_xml]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_validate_xml] @collector_type_uid uniqueidentifier = NULL, @name sysname = NULL, @parameters xml AS BEGIN DECLARE @retVal int EXEC @retVal = dbo.sp_syscollector_verify_collector_type @collector_type_uid OUTPUT, @name OUTPUT IF (@retVal <> 0) RETURN (1) DECLARE @schema_collection sysname SET @schema_collection = (SELECT schema_collection FROM syscollector_collector_types_internal WHERE @collector_type_uid = collector_type_uid) IF (@schema_collection IS NULL) BEGIN RAISERROR(21263, -1, -1, '@schema_collection') RETURN (1) END -- @sql_string should have enough buffer to include 2n+2 max size for QUOTENAME(@schema_collection). DECLARE @sql_string nvarchar(328) DECLARE @param_definition nvarchar(16) SET @param_definition = N'@param xml' SET @sql_string = N'DECLARE @validated_xml XML (DOCUMENT ' + QUOTENAME(@schema_collection, '[') + N'); SELECT @validated_xml = @param'; EXEC @retVal = sp_executesql @sql_string, @param_definition, @param = @parameters IF (@retVal <> 0) BEGIN RETURN (1) END END GO --------------------------------------------------------------- -- Collection item --------------------------------------------------------------- IF (OBJECT_ID(N'[dbo].[syscollector_collection_items_internal]', 'U') IS NULL) BEGIN PRINT 'Creating table [dbo].[syscollector_collection_items_internal]...' CREATE TABLE [dbo].[syscollector_collection_items_internal] ( collection_set_id int NOT NULL, collection_item_id int IDENTITY NOT NULL, collector_type_uid uniqueidentifier NOT NULL, name sysname NOT NULL, name_id int NULL, -- sysmessage id of the name of the item (for localizing system collection set) frequency int NOT NULL, parameters xml NULL, CONSTRAINT [PK_syscollector_collection_items_internal] PRIMARY KEY CLUSTERED (collection_set_id ASC, collection_item_id ASC), CONSTRAINT [UQ_syscollector_collection_items_internal_name] UNIQUE (name) ) ALTER TABLE syscollector_collection_items_internal ADD CONSTRAINT [FK_syscollector_collection_items_internal_syscollector_collection_sets_internal] FOREIGN KEY(collection_set_id) REFERENCES syscollector_collection_sets_internal (collection_set_id) ON DELETE CASCADE ALTER TABLE syscollector_collection_items_internal ADD CONSTRAINT [FK_syscollector_collection_items_internal_syscollector_collector_types_internal] FOREIGN KEY(collector_type_uid) REFERENCES syscollector_collector_types_internal (collector_type_uid) ON DELETE CASCADE END ELSE BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'name_id' AND id = OBJECT_ID(N'[dbo].[syscollector_collection_items_internal]'))) BEGIN PRINT 'Altering table [dbo].[syscollector_collection_items_internal]...' ALTER TABLE [dbo].[syscollector_collection_items_internal] ADD name_id int NULL END END GO IF (NOT OBJECT_ID(N'[dbo].[syscollector_collection_items]', 'V') IS NULL) BEGIN PRINT 'Dropping view [dbo].[syscollector_collection_items]...' DROP VIEW [dbo].[syscollector_collection_items] END GO PRINT 'Creating view [dbo].[syscollector_collection_items]...' GO CREATE VIEW [dbo].[syscollector_collection_items] AS SELECT i.collection_set_id, i.collection_item_id, i.collector_type_uid, CASE WHEN i.name_id IS NULL THEN i.name ELSE FORMATMESSAGE(i.name_id) END AS name, i.frequency, i.parameters FROM [dbo].[syscollector_collection_items_internal] i GO -- This is a stored procedure of collector_type, but it is created here because it -- makes references to the collection item view IF (NOT OBJECT_ID('[dbo].[sp_syscollector_delete_collector_type]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_collector_type]...' DROP PROCEDURE [dbo].[sp_syscollector_delete_collector_type] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_delete_collector_type]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_delete_collector_type] @collector_type_uid uniqueidentifier = NULL, @name sysname = NULL AS BEGIN DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_delete_collector_type ELSE BEGIN TRANSACTION BEGIN TRY -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_admin') RETURN (1) END DECLARE @retVal int EXEC @retVal = dbo.sp_syscollector_verify_collector_type @collector_type_uid OUTPUT, @name OUTPUT IF (@retVal <> 0) RETURN (1) IF (EXISTS(SELECT * from dbo.syscollector_collection_items WHERE @collector_type_uid = collector_type_uid)) BEGIN RAISERROR(14673, -1, -1, @name) RETURN (1) END DECLARE @schema_collection sysname -- @sql_string should have enough buffer to include 2n+2 max size for QUOTENAME(@schema_collection). DECLARE @sql_string nvarchar(285) SET @schema_collection = (SELECT schema_collection FROM syscollector_collector_types_internal WHERE @collector_type_uid = collector_type_uid) SET @sql_string = N'DROP XML SCHEMA COLLECTION ' + QUOTENAME(@schema_collection, '['); EXEC sp_executesql @sql_string DELETE [dbo].[syscollector_collector_types_internal] WHERE collector_type_uid = @collector_type_uid IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_delete_collector_type DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_verify_collection_item]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_verify_collection_item]...' DROP PROCEDURE [dbo].[sp_syscollector_verify_collection_item] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_verify_collection_item]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_verify_collection_item] @collection_item_id int = NULL OUTPUT, @name sysname = NULL OUTPUT AS BEGIN IF (@name IS NOT NULL) BEGIN -- Remove any leading/trailing spaces from parameters SET @name = NULLIF(LTRIM(RTRIM(@name)), N'') END IF (@collection_item_id IS NULL AND @name IS NULL) BEGIN RAISERROR(14624, -1, -1, '@collection_item_id, @name') RETURN(1) END IF (@collection_item_id IS NOT NULL AND @name IS NOT NULL) BEGIN IF (NOT EXISTS(SELECT * FROM dbo.syscollector_collection_items WHERE collection_item_id = @collection_item_id AND name = @name)) BEGIN DECLARE @errMsg NVARCHAR(196) SELECT @errMsg = CONVERT(NVARCHAR(36), @collection_item_id) + ', ' + @name RAISERROR(14262, -1, -1, '@collection_item_id, @name', @errMsg) RETURN(1) END END -- Check id ELSE IF (@collection_item_id IS NOT NULL) BEGIN SELECT @name = name FROM dbo.syscollector_collection_items WHERE (collection_item_id = @collection_item_id) -- the view would take care of all the permissions issues. IF (@name IS NULL) BEGIN DECLARE @collection_item_id_as_char VARCHAR(36) SELECT @collection_item_id_as_char = CONVERT(VARCHAR(36), @collection_item_id) RAISERROR(14262, -1, -1, '@collection_item_id', @collection_item_id_as_char) RETURN(1) -- Failure END END -- Check name ELSE IF (@name IS NOT NULL) BEGIN -- get the corresponding collection_item_id (if the collection_item exists) SELECT @collection_item_id = collection_item_id FROM dbo.syscollector_collection_items WHERE (name = @name) -- the view would take care of all the permissions issues. IF (@collection_item_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END END RETURN (0) END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_create_collection_item]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_create_collection_item]...' DROP PROCEDURE [dbo].[sp_syscollector_create_collection_item] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_create_collection_item]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_create_collection_item] @collection_set_id int, @collector_type_uid uniqueidentifier, @name sysname, @frequency int = 5, -- set by default to the minimum frequency @parameters xml = NULL, @collection_item_id int OUTPUT AS BEGIN DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_create_collection_item ELSE BEGIN TRANSACTION BEGIN TRY -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_admin') RETURN (1) END DECLARE @is_system bit SELECT @is_system = is_system FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id IF (@is_system = 1) BEGIN -- cannot update, delete, or add new collection items to a system collection set RAISERROR(14696, -1, -1); RETURN (1) END SET @name = NULLIF(LTRIM(RTRIM(@name)), N'') IF (@name IS NULL) BEGIN RAISERROR(21263, -1, -1, '@name') RETURN (1) END IF (@frequency < 5) BEGIN DECLARE @frequency_as_char VARCHAR(36) SELECT @frequency_as_char = CONVERT(VARCHAR(36), @frequency) RAISERROR(21405, 16, -1, @frequency_as_char, '@frequency', 5) RETURN (1) END IF (NOT EXISTS(SELECT * from dbo.syscollector_collector_types WHERE @collector_type_uid = collector_type_uid)) BEGIN DECLARE @collector_type_uid_as_char VARCHAR(36) SELECT @collector_type_uid_as_char = CONVERT(VARCHAR(36), @collector_type_uid) RAISERROR(14262, -1, -1, '@collector_type_uid', @collector_type_uid_as_char) RETURN (1) END IF (NOT EXISTS(SELECT * from dbo.syscollector_collection_sets WHERE @collection_set_id = collection_set_id)) BEGIN DECLARE @collection_set_id_as_char VARCHAR(36) SELECT @collection_set_id_as_char = CONVERT(VARCHAR(36), @collection_set_id) RAISERROR(14262, -1, -1, '@collection_set_id', @collection_set_id_as_char) RETURN (1) END DECLARE @is_running bit SELECT @is_running = is_running FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id IF (@is_running = 1) BEGIN RAISERROR(14675, -1, -1, @name) RETURN (1) END IF (@parameters IS NULL) BEGIN DECLARE @parameter_schema xml SELECT @parameter_schema = parameter_schema FROM syscollector_collector_types WHERE collector_type_uid = @collector_type_uid IF (@parameter_schema IS NOT NULL) -- only allows parameters to be null if the collector type has a null schema BEGIN RAISERROR(21263, -1, -1, '@parameters') RETURN (1) END END ELSE IF (LTRIM(RTRIM(CONVERT(nvarchar(max), @parameters))) <> N'') -- don't check if the parameters are empty string BEGIN EXEC dbo.sp_syscollector_validate_xml @collector_type_uid = @collector_type_uid, @parameters = @parameters END INSERT INTO [dbo].[syscollector_collection_items_internal] ( collection_set_id, collector_type_uid, name, frequency, parameters ) VALUES ( @collection_set_id, @collector_type_uid, @name, @frequency, @parameters ) SET @collection_item_id = SCOPE_IDENTITY() IF (@collection_item_id IS NULL) BEGIN DECLARE @collection_item_id_as_char VARCHAR(36) SELECT @collection_item_id_as_char = CONVERT(VARCHAR(36), @collection_item_id) RAISERROR(14262, -1, -1, '@collection_item_id', @collection_item_id_as_char) RETURN (1) END IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_create_collection_item DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_update_collection_item_internal]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_update_collection_item_internal]...' DROP PROCEDURE [dbo].[sp_syscollector_update_collection_item_internal] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_update_collection_item_internal]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_update_collection_item_internal] @collection_item_id int = NULL, @name sysname = NULL, @new_name sysname = NULL, @frequency int = NULL, @parameters xml = NULL AS BEGIN DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_update_collection_item ELSE BEGIN TRANSACTION BEGIN TRY UPDATE [dbo].[syscollector_collection_items_internal] SET name = ISNULL(@new_name, name), frequency = ISNULL(@frequency, frequency), parameters = ISNULL(@parameters, parameters) WHERE @collection_item_id = collection_item_id IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_update_collection_item DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_update_collection_item]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_update_collection_item]...' DROP PROCEDURE [dbo].[sp_syscollector_update_collection_item] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_update_collection_item]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_update_collection_item] @collection_item_id int = NULL, @name sysname = NULL, @new_name sysname = NULL, @frequency int = NULL, @parameters xml = NULL AS BEGIN -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_operator') RETURN(1) -- Failure END -- Security checks (restrict functionality for non-dc_admin-s) IF ((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) AND (@new_name IS NOT NULL)) BEGIN RAISERROR(14676, -1, -1, '@new_name', 'dc_admin') RETURN (1) -- Failure END IF ((NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) AND (@parameters IS NOT NULL)) BEGIN RAISERROR(14676, -1, -1, '@parameters', 'dc_admin') RETURN (1) -- Failure END DECLARE @retVal int EXEC @retVal = dbo.sp_syscollector_verify_collection_item @collection_item_id OUTPUT, @name OUTPUT IF (@retVal <> 0) RETURN (@retVal) IF (@frequency < 5) BEGIN DECLARE @frequency_as_char VARCHAR(36) SELECT @frequency_as_char = CONVERT(VARCHAR(36), @frequency) RAISERROR(21405, 16, -1, @frequency_as_char, '@frequency', 5) RETURN (1) END IF (LEN(@new_name) = 0) -- can't rename to an empty string BEGIN RAISERROR(21263, -1, -1, '@new_name') RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SET @new_name = LTRIM(RTRIM(@new_name)) DECLARE @collection_set_name sysname DECLARE @is_system bit DECLARE @is_running bit DECLARE @collector_type_uid uniqueidentifier DECLARE @collection_set_id int SELECT @is_running = s.is_running, @is_system = s.is_system, @collection_set_name = s.name, @collector_type_uid = i.collector_type_uid, @collection_set_id = s.collection_set_id FROM dbo.syscollector_collection_sets s, dbo.syscollector_collection_items i WHERE s.collection_set_id = i.collection_set_id AND i.collection_item_id = @collection_item_id IF (@is_system = 1 AND (@new_name IS NOT NULL OR @parameters IS NOT NULL)) BEGIN -- cannot update, delete, or add new collection items to a system collection set RAISERROR(14696, -1, -1); RETURN (1) END IF (@parameters IS NOT NULL) BEGIN EXEC @retVal = dbo.sp_syscollector_validate_xml @collector_type_uid = @collector_type_uid, @parameters = @parameters IF (@retVal <> 0) RETURN (@retVal) END -- if the collection item is running, stop it before update IF (@is_running = 1) BEGIN EXEC @retVal = sp_syscollector_stop_collection_set @collection_set_id = @collection_set_id IF (@retVal <> 0) RETURN(1) END -- all conditions go, perform the update EXEC @retVal = sp_syscollector_update_collection_item_internal @collection_item_id = @collection_item_id, @name = @name, @new_name = @new_name, @frequency = @frequency, @parameters = @parameters -- if you stopped the collection set, restart it IF (@is_running = 1) BEGIN EXEC @retVal = sp_syscollector_start_collection_set @collection_set_id = @collection_set_id IF (@retVal <> 0) RETURN (1) END RETURN (0) END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_delete_collection_item_internal]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_collection_item_internal]...' DROP PROCEDURE [dbo].[sp_syscollector_delete_collection_item_internal] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_delete_collection_item_internal]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_delete_collection_item_internal] @collection_item_id int, @name sysname AS BEGIN DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_delete_collection_item ELSE BEGIN TRANSACTION BEGIN TRY DELETE [dbo].[syscollector_collection_items_internal] WHERE collection_item_id = @collection_item_id AND name = @name IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_delete_collection_item DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_delete_collection_item]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_collection_item]...' DROP PROCEDURE [dbo].[sp_syscollector_delete_collection_item] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_delete_collection_item]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_delete_collection_item] @collection_item_id int = NULL, @name sysname = NULL AS BEGIN -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_admin') RETURN(1) -- Failure END DECLARE @retVal int EXEC @retVal = dbo.sp_syscollector_verify_collection_item @collection_item_id OUTPUT, @name OUTPUT IF (@retVal <> 0) RETURN (1) DECLARE @is_system bit DECLARE @is_running bit DECLARE @collection_set_id int SELECT @is_running = s.is_running, @is_system = s.is_system, @collection_set_id = s.collection_set_id FROM dbo.syscollector_collection_sets s, dbo.syscollector_collection_items i WHERE i.collection_item_id = @collection_item_id AND s.collection_set_id = i.collection_set_id IF (@is_system = 1) BEGIN -- cannot update, delete, or add new collection items to a system collection set RAISERROR(14696, -1, -1); RETURN(1) END IF (@is_running = 1) BEGIN -- stop the collection set if it was running EXEC @retVal = sp_syscollector_stop_collection_set @collection_set_id = @collection_set_id IF (@retVal <> 0) RETURN (1) END -- all checks go, perform delete EXEC @retVal = sp_syscollector_delete_collection_item_internal @collection_item_id = @collection_item_id, @name = @name IF (@retVal <> 0) RETURN (1) RETURN (0) END GO --------------------------------------------------------------- -- Collection Set runtime procedures --------------------------------------------------------------- IF (NOT OBJECT_ID('dbo.sp_syscollector_start_collection_set', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_start_collection_set]...' DROP PROCEDURE [dbo].[sp_syscollector_start_collection_set] END GO PRINT '' PRINT 'Creating procedure [dbo].[sp_syscollector_start_collection_set]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_start_collection_set] @collection_set_id int = NULL, @name sysname = NULL WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser' AS BEGIN SET NOCOUNT ON DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_start_collection_set ELSE BEGIN TRANSACTION BEGIN TRY -- Security check (role membership) EXECUTE AS CALLER; IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN REVERT; RAISERROR(14677, -1, -1, 'dc_operator') RETURN(1) -- Failure END REVERT; -- check if SQL Server Agent is enabled DECLARE @agent_enabled int SELECT @agent_enabled = CAST(value_in_use AS int) FROM sys.configurations WHERE name = N'Agent XPs' IF @agent_enabled <> 1 BEGIN RAISERROR(14688, -1, -1) RETURN (1) END -- check if MDW is setup DECLARE @instance_name sysname SELECT @instance_name = CONVERT(sysname,parameter_value) FROM [msdb].[dbo].[syscollector_config_store_internal] WHERE parameter_name = N'MDWInstance' IF (@instance_name IS NULL) BEGIN RAISERROR(14689, -1, -1) RETURN (1) END DECLARE @database_name sysname SELECT @database_name = CONVERT(sysname,parameter_value) FROM [msdb].[dbo].[syscollector_config_store_internal] WHERE parameter_name = N'MDWDatabase' IF (@database_name IS NULL) BEGIN RAISERROR(14689, -1, -1) RETURN (1) END -- Verify the input parameters DECLARE @retVal int EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT IF (@retVal <> 0) RETURN (1) -- Check if the collection set does not have any collection items IF NOT EXISTS( SELECT i.collection_item_id FROM [dbo].[syscollector_collection_sets] AS s INNER JOIN [dbo].[syscollector_collection_items] AS i ON(s.collection_set_id = i.collection_set_id) WHERE s.collection_set_id = @collection_set_id ) BEGIN RAISERROR(14685, 10, -1, @name) -- Raise a warning message IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END DECLARE @proxy_id int; DECLARE @collection_job_id uniqueidentifier DECLARE @upload_job_id uniqueidentifier DECLARE @schedule_uid uniqueidentifier; SELECT @collection_job_id = collection_job_id, @upload_job_id = upload_job_id, @proxy_id = proxy_id, @schedule_uid = schedule_uid FROM [dbo].[syscollector_collection_sets_internal] WHERE collection_set_id = @collection_set_id; -- Check if the set does not have a proxy IF (@proxy_id IS NULL) BEGIN -- to start a collection set without a proxy, the caller has to be a sysadmin EXECUTE AS CALLER; IF (NOT (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)) BEGIN REVERT; RAISERROR(14692, -1, -1, @name) RETURN (1) END REVERT; END -- Starting a collection set requires a schedule IF @schedule_uid IS NULL BEGIN RAISERROR(14693, -1, -1) RETURN (1) END -- Check if we have jobs created, and if not, create them IF (@collection_job_id IS NULL AND @upload_job_id IS NULL) BEGIN -- Jobs not created yet, go and crete them -- We need to get some data from collection_sets table -- before we do that. DECLARE @collection_set_uid uniqueidentifier; DECLARE @schedule_id int; DECLARE @collection_mode int; SELECT @collection_set_uid = collection_set_uid, @collection_mode = collection_mode FROM [dbo].[syscollector_collection_sets_internal] WHERE collection_set_id = @collection_set_id; -- Sanity check -- Make sure the proxy and schedule are still there, someone could have -- remove them between when the collection set was created and now. IF (@proxy_id IS NOT NULL) BEGIN DECLARE @proxy_name sysname -- this will throw if the id does not exist EXEC @retVal = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retVal <> 0) RETURN (1) END SELECT @schedule_id = schedule_id FROM sysschedules_localserver_view WHERE @schedule_uid = schedule_uid EXEC @retVal = sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = NULL, @schedule_id = @schedule_id, @owner_sid = NULL, @orig_server_id = NULL IF (@retVal <> 0) RETURN (1) -- Go add the jobs EXEC [dbo].[sp_syscollector_create_jobs] @collection_set_id = @collection_set_id, @collection_set_uid = @collection_set_uid, @collection_set_name = @name, @proxy_id = @proxy_id, @schedule_id = @schedule_id, @collection_mode = @collection_mode, @collection_job_id = @collection_job_id OUTPUT, @upload_job_id = @upload_job_id OUTPUT -- Finally, update the collection_sets table UPDATE [dbo].[syscollector_collection_sets_internal] SET upload_job_id = @upload_job_id, collection_job_id = @collection_job_id WHERE @collection_set_id = collection_set_id END -- Update the is_running column for this collection set -- There is a trigger defined for that table that turns on -- the collection and upload jobs in response to that bit -- changing. UPDATE [dbo].[syscollector_collection_sets_internal] SET is_running = 1 WHERE collection_set_id = @collection_set_id IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_start_collection_set DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO IF (NOT OBJECT_ID('dbo.sp_syscollector_stop_collection_set', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_stop_collection_set]...' DROP PROCEDURE [dbo].[sp_syscollector_stop_collection_set] END GO PRINT '' PRINT 'Creating procedure [dbo].[sp_syscollector_stop_collection_set]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_stop_collection_set] @collection_set_id int = NULL, @name sysname = NULL, @stop_collection_job bit = 1 -- Do we need to stop the collection job, YES by default AS BEGIN SET NOCOUNT ON -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_operator') RETURN(1) -- Failure END -- Verify the input parameters DECLARE @retVal int EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT IF (@retVal <> 0) RETURN (1) IF (@stop_collection_job = 1) BEGIN DECLARE @collection_mode INT DECLARE @collection_job_id UNIQUEIDENTIFIER SELECT @collection_mode = collection_mode, @collection_job_id = collection_job_id FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id DECLARE @is_collection_job_running INT EXECUTE [dbo].[sp_syscollector_get_collection_set_execution_status] @collection_set_id = @collection_set_id, @is_collection_running = @is_collection_job_running OUTPUT -- Stop the collection job if we are in cached mode, this should signal the runtime to exit IF (@is_collection_job_running = 1 -- Collection job is running AND @collection_mode = 0 AND @stop_collection_job = 1) BEGIN EXEC sp_stop_job @job_id = @collection_job_id END END -- Update the is_running column for this collection set -- There is a trigger defined for that table that turns off -- the collection and uplaod jobs in response to that bit -- changing. UPDATE [dbo].[syscollector_collection_sets_internal] SET is_running = 0 WHERE collection_set_id = @collection_set_id RETURN (0) END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_get_collection_set_execution_status]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_get_collection_set_execution_status]...' DROP PROCEDURE [dbo].[sp_syscollector_get_collection_set_execution_status] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_get_collection_set_execution_status]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_get_collection_set_execution_status] @collection_set_id int, @is_running int = NULL OUTPUT, @is_collection_running int = NULL OUTPUT, @collection_job_state int = NULL OUTPUT, @is_upload_running int = NULL OUTPUT, @upload_job_state int = NULL OUTPUT WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser' AS BEGIN DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_get_execution_status ELSE BEGIN TRANSACTION BEGIN TRY DECLARE @xp_results TABLE (job_id UNIQUEIDENTIFIER NOT NULL, last_run_date INT NOT NULL, last_run_time INT NOT NULL, next_run_date INT NOT NULL, next_run_time INT NOT NULL, next_run_schedule_id INT NOT NULL, requested_to_run INT NOT NULL, -- BOOL request_source INT NOT NULL, request_source_id sysname COLLATE database_default NULL, running INT NOT NULL, -- BOOL current_step INT NOT NULL, current_retry_attempt INT NOT NULL, job_state INT NOT NULL) DECLARE @is_sysadmin INT SELECT @is_sysadmin = ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) DECLARE @collection_job_id UNIQUEIDENTIFIER DECLARE @upload_job_id UNIQUEIDENTIFIER SELECT @collection_job_id = collection_job_id, @upload_job_id = upload_job_id FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id DECLARE @agent_enabled int SELECT @agent_enabled = CAST(value_in_use AS int) FROM sys.configurations WHERE name = N'Agent XPs' IF (@agent_enabled <> 0) BEGIN INSERT INTO @xp_results EXECUTE master.dbo.xp_sqlagent_enum_jobs @is_sysadmin, N'', @upload_job_id INSERT INTO @xp_results EXECUTE master.dbo.xp_sqlagent_enum_jobs @is_sysadmin, N'', @collection_job_id END SELECT @is_running = is_running FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id SELECT @is_collection_running = running, @collection_job_state = job_state FROM @xp_results WHERE job_id = @collection_job_id SELECT @is_upload_running = running, @upload_job_state = job_state FROM @xp_results WHERE job_id = @upload_job_id IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_get_execution_status DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO IF (NOT OBJECT_ID('dbo.sp_syscollector_upload_collection_set', 'P') IS NULL) BEGIN DROP PROCEDURE [dbo].[sp_syscollector_upload_collection_set] END GO PRINT '' PRINT 'Creating procedure [dbo].[sp_syscollector_upload_collection_set]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_upload_collection_set] @collection_set_id int = NULL, @name sysname = NULL WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser' AS BEGIN SET NOCOUNT ON -- Security check (role membership) EXECUTE AS CALLER; IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN REVERT; RAISERROR(14677, -1, -1, 'dc_operator') RETURN(1) -- Failure END REVERT; -- Verify the input parameters DECLARE @retVal int EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT IF (@retVal <> 0) RETURN (1) -- Make sure the collection set is running and is in the right mode DECLARE @is_running bit DECLARE @collection_mode smallint SELECT @is_running = is_running, @collection_mode = collection_mode FROM [dbo].[syscollector_collection_sets] WHERE collection_set_id = @collection_set_id IF (@collection_mode <> 0) BEGIN RAISERROR(14694, -1, -1, @name) RETURN(1) END IF (@is_running = 0) BEGIN RAISERROR(14674, -1, -1, @name) RETURN(1) END -- Make sure the collector is enabled EXEC @retVal = [dbo].[sp_syscollector_verify_collector_state] @desired_state = 1 IF (@retVal <> 0) RETURN (1) -- Check if the upload job is currently running DECLARE @is_upload_job_running INT EXECUTE [dbo].[sp_syscollector_get_collection_set_execution_status] @collection_set_id = @collection_set_id, @is_upload_running = @is_upload_job_running OUTPUT IF (@is_upload_job_running = 0) BEGIN -- Job is not running, we can trigger it now DECLARE @job_id UNIQUEIDENTIFIER SELECT @job_id = upload_job_id FROM [dbo].[syscollector_collection_sets] WHERE collection_set_id = @collection_set_id EXEC @retVal = sp_start_job @job_id = @job_id IF (@retVal <> 0) RETURN (1) END RETURN (0) END GO IF (NOT OBJECT_ID('dbo.sp_syscollector_run_collection_set', 'P') IS NULL) BEGIN DROP PROCEDURE [dbo].[sp_syscollector_run_collection_set] END GO PRINT '' PRINT 'Creating procedure [dbo].[sp_syscollector_run_collection_set]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_run_collection_set] @collection_set_id int = NULL, @name sysname = NULL WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser' AS BEGIN SET NOCOUNT ON DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_run_collection_set ELSE BEGIN TRANSACTION BEGIN TRY -- Security check (role membership) EXECUTE AS CALLER; IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN REVERT; RAISERROR(14677, -1, -1, 'dc_operator') RETURN(1) -- Failure END REVERT; -- Verify the input parameters DECLARE @retVal int EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT IF (@retVal <> 0) RETURN (1) -- Make sure the collection set is in the right mode DECLARE @collection_mode smallint DECLARE @collection_set_uid uniqueidentifier; SELECT @collection_set_uid = collection_set_uid, @collection_mode = collection_mode FROM [dbo].[syscollector_collection_sets] WHERE collection_set_id = @collection_set_id IF (@collection_mode <> 1) BEGIN RAISERROR(14695, -1, -1, @name) RETURN(1) END -- Make sure the collector is enabled EXEC @retVal = [dbo].[sp_syscollector_verify_collector_state] @desired_state = 1 IF (@retVal <> 0) RETURN (1) -- check if SQL Server Agent is enabled DECLARE @agent_enabled int SELECT @agent_enabled = CAST(value_in_use AS int) FROM sys.configurations WHERE name = N'Agent XPs' IF @agent_enabled <> 1 BEGIN RAISERROR(14688, -1, -1) RETURN (1) END -- check if MDW is setup DECLARE @instance_name sysname SELECT @instance_name = CONVERT(sysname,parameter_value) FROM [msdb].[dbo].[syscollector_config_store_internal] WHERE parameter_name = N'MDWInstance' IF (@instance_name IS NULL) BEGIN RAISERROR(14689, -1, -1) RETURN (1) END DECLARE @database_name sysname SELECT @database_name = CONVERT(sysname,parameter_value) FROM [msdb].[dbo].[syscollector_config_store_internal] WHERE parameter_name = N'MDWDatabase' IF (@database_name IS NULL) BEGIN RAISERROR(14689, -1, -1) RETURN (1) END -- Make sure the jobs are created for the collection set -- Verify the input parameters EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT IF (@retVal <> 0) RETURN (1) -- Check if the collection set does not have any collection items IF NOT EXISTS( SELECT i.collection_item_id FROM [dbo].[syscollector_collection_sets] AS s INNER JOIN [dbo].[syscollector_collection_items] AS i ON(s.collection_set_id = i.collection_set_id) WHERE s.collection_set_id = @collection_set_id ) BEGIN RAISERROR(14685, 10, -1, @name) -- Raise a warning message IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END DECLARE @proxy_id int; DECLARE @collection_job_id uniqueidentifier DECLARE @upload_job_id uniqueidentifier SELECT @collection_job_id = collection_job_id, @upload_job_id = upload_job_id, @proxy_id = proxy_id FROM [dbo].[syscollector_collection_sets_internal] WHERE collection_set_id = @collection_set_id; -- Check if the set does not have a proxy IF (@proxy_id IS NULL) BEGIN -- to start a collection set without a proxy, the caller has to be a sysadmin EXECUTE AS CALLER; IF (NOT (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)) BEGIN REVERT; RAISERROR(14692, -1, -1, @name) RETURN (1) END REVERT; END -- Check if we have jobs created, and if not, create them DECLARE @jobs_just_created bit SET @jobs_just_created = 0 -- False until further notice IF (@collection_job_id IS NULL AND @upload_job_id IS NULL) BEGIN DECLARE @schedule_id int; DECLARE @schedule_uid uniqueidentifier; SELECT @schedule_uid = schedule_uid FROM [dbo].[syscollector_collection_sets_internal] WHERE collection_set_id = @collection_set_id; IF (@schedule_uid IS NOT NULL) BEGIN SELECT @schedule_id = schedule_id FROM sysschedules_localserver_view WHERE @schedule_uid = schedule_uid END -- Sanity check -- Make sure the proxy and schedule are still there, someone could have -- remove them between when the collection set was created and now. IF (@proxy_id IS NOT NULL) BEGIN DECLARE @proxy_name sysname -- this will throw an error of proxy_id does not exist EXEC @retVal = msdb.dbo.sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retVal <> 0) RETURN (0) END IF (@schedule_uid IS NOT NULL) BEGIN EXEC @retVal = sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = NULL, @schedule_id = @schedule_id, @owner_sid = NULL, @orig_server_id = NULL IF (@retVal <> 0) RETURN (1) END -- Go add the jobs EXEC [dbo].[sp_syscollector_create_jobs] @collection_set_id = @collection_set_id, @collection_set_uid = @collection_set_uid, @collection_set_name = @name, @proxy_id = @proxy_id, @schedule_id = @schedule_id, @collection_mode = @collection_mode, @collection_job_id = @collection_job_id OUTPUT, @upload_job_id = @upload_job_id OUTPUT -- Finally, update the collection_sets table UPDATE [dbo].[syscollector_collection_sets_internal] SET upload_job_id = @upload_job_id, collection_job_id = @collection_job_id WHERE @collection_set_id = collection_set_id SET @jobs_just_created = 1 -- Record the fact that we have just created the job here END IF (@jobs_just_created = 1) BEGIN -- We created the jobs here in this transaction, post a request for agent to start as soon as we commit EXEC @retVal = sp_start_job @job_id = @upload_job_id IF (@retVal <> 0) RETURN (1) END ELSE BEGIN -- The jobs were created previously, we need to guard against it already executing by the schedule -- So, check if the job is currently running before asking agent to start it DECLARE @is_upload_job_running INT EXECUTE [dbo].[sp_syscollector_get_collection_set_execution_status] @collection_set_id = @collection_set_id, @is_upload_running = @is_upload_job_running OUTPUT IF (@is_upload_job_running = 0) BEGIN -- Job is not running, we can trigger it now -- We run only one job because for this (non-cached) mode there is only one job. The same id is stored -- as collection and upload job id EXEC @retVal = sp_start_job @job_id = @upload_job_id IF (@retVal <> 0) RETURN (1) END END IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_run_collection_set DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO PRINT 'Creating trigger [dbo].[syscollector_collection_set_is_running_update_trigger] on [dbo].[syscollector_collection_sets_internal]' GO IF NOT OBJECT_ID('dbo.syscollector_collection_set_is_running_update_trigger', 'TR') IS NULL DROP TRIGGER [dbo].[syscollector_collection_set_is_running_update_trigger] GO CREATE TRIGGER [dbo].[syscollector_collection_set_is_running_update_trigger] on [dbo].[syscollector_collection_sets_internal] WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser' FOR UPDATE AS BEGIN DECLARE @collection_set_id INT DECLARE @is_running BIT DECLARE @old_is_running BIT DECLARE @collection_mode SMALLINT IF (NOT UPDATE (is_running)) RETURN DECLARE @collector_enabled int SET @collector_enabled = CONVERT(int, (SELECT parameter_value FROM dbo.syscollector_config_store_internal WHERE parameter_name = 'CollectorEnabled')) IF @collector_enabled = 0 BEGIN -- flipping the is_running bit has no effect when the collector is disabled RAISERROR(14682, 10, -1) -- severity 10 emits a warning END ELSE BEGIN DECLARE inserted_cursor CURSOR LOCAL FOR SELECT collection_set_id, is_running, collection_mode FROM inserted OPEN inserted_cursor FETCH inserted_cursor INTO @collection_set_id, @is_running, @collection_mode WHILE @@FETCH_STATUS = 0 BEGIN SELECT @old_is_running = is_running FROM deleted WHERE collection_set_id = @collection_set_id -- If there is a change in the state, handle accordingly IF (@old_is_running <> @is_running) BEGIN IF (@is_running = 0) BEGIN EXEC dbo.sp_syscollector_stop_collection_set_jobs @collection_set_id = @collection_set_id END ELSE IF (@is_running = 1) BEGIN EXEC dbo.sp_syscollector_start_collection_set_jobs @collection_set_id = @collection_set_id END END FETCH inserted_cursor INTO @collection_set_id, @is_running, @collection_mode END CLOSE inserted_cursor DEALLOCATE inserted_cursor END END GO PRINT '' PRINT 'Creating stored procedure syscollector_stop_collection_set_jobs...' IF (NOT OBJECT_ID('dbo.sp_syscollector_stop_collection_set_jobs', 'P') IS NULL) DROP PROCEDURE [dbo].[sp_syscollector_stop_collection_set_jobs] GO CREATE PROCEDURE [dbo].[sp_syscollector_stop_collection_set_jobs] @collection_set_id int AS BEGIN SET NOCOUNT ON -- Collection set stopped. Make sure the following happens: -- 1. Detach upload schedule -- 2. Collection job is stopped -- 3. Upload job is kicked once if it is not running now -- 4. Collection and upload jobs are disabled -- 5. Attach upload schedule DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_stop_collection_set_jobs ELSE BEGIN TRANSACTION BEGIN TRY DECLARE @collection_job_id uniqueidentifier DECLARE @upload_job_id uniqueidentifier DECLARE @schedule_uid uniqueidentifier DECLARE @collection_mode smallint SELECT @collection_job_id = collection_job_id, @upload_job_id = upload_job_id, @collection_mode = collection_mode, @schedule_uid = schedule_uid FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id DECLARE @schedule_id int IF (@collection_mode != 1) -- detach schedule for continuous and snapshot modes BEGIN SELECT @schedule_id = schedule_id from sysschedules_localserver_view WHERE @schedule_uid = schedule_uid IF (@schedule_id IS NULL) BEGIN DECLARE @schedule_uid_as_char VARCHAR(36) SELECT @schedule_uid_as_char = CONVERT(VARCHAR(36), @schedule_uid) RAISERROR(14262, -1, -1, '@schedule_uid', @schedule_uid_as_char) RETURN (1) END -- Detach schedule EXEC dbo.sp_detach_schedule @job_id = @upload_job_id, @schedule_id = @schedule_id, @delete_unused_schedule = 0 -- do not delete schedule, might need to attach it back again END DECLARE @is_upload_job_running INT EXECUTE [dbo].[sp_syscollector_get_collection_set_execution_status] @collection_set_id = @collection_set_id, @is_upload_running = @is_upload_job_running OUTPUT -- Upload job (needs to be kicked off for continuous collection mode) IF (@is_upload_job_running = 0 -- If the upload job is not already in progress AND @collection_mode = 0) -- don't do it for adhoc or snapshot, they will handle it on their own BEGIN EXEC sp_start_job @job_id = @upload_job_id, @error_flag = 0 END -- Disable both jobs EXEC sp_update_job @job_id = @collection_job_id, @enabled = 0 EXEC sp_update_job @job_id = @upload_job_id, @enabled = 0 IF (@collection_mode != 1) -- attach schedule for continuous and snapshot modes BEGIN -- Attach schedule EXEC dbo.sp_attach_schedule @job_id = @upload_job_id, @schedule_id = @schedule_id END -- Log the stop of the collection set EXEC sp_syscollector_event_oncollectionstop @collection_set_id = @collection_set_id IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_stop_collection_set_jobs DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO PRINT '' PRINT 'Creating stored procedure syscollector_start_collection_set_jobs...' IF (NOT OBJECT_ID('dbo.sp_syscollector_start_collection_set_jobs', 'P') IS NULL) DROP PROCEDURE [dbo].[sp_syscollector_start_collection_set_jobs] GO CREATE PROCEDURE [dbo].[sp_syscollector_start_collection_set_jobs] @collection_set_id int AS BEGIN SET NOCOUNT ON -- Collection set started. Make sure the following happens: -- 1. Collection and upload jobs are enabled -- 2. Collection job is started if it is defined as running continously DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_start_collection_set_jobs ELSE BEGIN TRANSACTION BEGIN TRY -- Log the start of the collection set DECLARE @log_id bigint EXEC sp_syscollector_event_oncollectionstart @collection_set_id = @collection_set_id, @log_id = @log_id OUTPUT -- Enable both jobs DECLARE @collection_job_id uniqueidentifier DECLARE @upload_job_id uniqueidentifier DECLARE @collection_mode smallint SELECT @collection_job_id = collection_job_id, @upload_job_id = upload_job_id, @collection_mode = collection_mode FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id EXEC sp_update_job @job_id = @collection_job_id, @enabled = 1 EXEC sp_update_job @job_id = @upload_job_id, @enabled = 1 -- Start the collection job if you are in ad hoc or continuous modes IF (@collection_mode = 1 OR @collection_mode = 0) BEGIN EXEC sp_start_job @job_id = @collection_job_id, @error_flag = 0 END IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_start_collection_set_jobs DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO --------------------------------------------------------------- -- Collection Set execution log --------------------------------------------------------------- IF (OBJECT_ID('dbo.syscollector_execution_log_internal', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table syscollector_execution_log_internal...' CREATE TABLE [dbo].[syscollector_execution_log_internal] ( log_id bigint IDENTITY(1,1) NOT NULL, -- Unique log entry id. Each execution log gets a new one parent_log_id bigint NULL, -- Id of the parent execution context. NULL for the root node. collection_set_id int NOT NULL, -- Id of the collection set that owns this entry collection_item_id int NULL, -- Collection item id start_time datetime NOT NULL, -- Collection set or package execution start time last_iteration_time datetime NULL, -- For continously running packages, last time an interation has been started finish_time datetime NULL, -- Collection set or package execution end time runtime_execution_mode smallint NULL, -- 0 - Collection, 1 - Upload status smallint NOT NULL, -- 0 - Running, 1 - Finished, 2 - Error, 3 - Warning operator nvarchar(128) NOT NULL, -- Name of the user running the collection set or package package_id uniqueidentifier NULL, -- Id of a package, NULL for collection sets package_execution_id uniqueidentifier NULL, -- Execution Id generated by SSIS for each package execution. Use to join events from sysssislog. NULL for collection sets failure_message nvarchar(2048) NULL -- Message that indicates package failure. NULL if no failure CONSTRAINT [PK_syscollector_execution_log] PRIMARY KEY CLUSTERED (log_id ASC), CONSTRAINT [FK_syscollector_execution_log_collection_set_id] FOREIGN KEY (collection_set_id) REFERENCES [dbo].[syscollector_collection_sets_internal] (collection_set_id), ) END go PRINT '' PRINT 'Creating view syscollector_execution_log...' IF (NOT OBJECT_ID('dbo.syscollector_execution_log', 'V') IS NULL) DROP VIEW [dbo].[syscollector_execution_log] go CREATE VIEW [dbo].[syscollector_execution_log] AS SELECT log_id, ISNULL(parent_log_id, 0) as parent_log_id, collection_set_id, collection_item_id, start_time, last_iteration_time, finish_time, runtime_execution_mode, [status], operator, package_id, msdb.dbo.fn_syscollector_get_package_path(package_id) as package_name, package_execution_id, failure_message FROM dbo.syscollector_execution_log_internal; GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_delete_jobs]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_jobs]...' DROP PROCEDURE [dbo].[sp_syscollector_delete_jobs] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_delete_jobs]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_delete_jobs] @collection_job_id uniqueidentifier, @upload_job_id uniqueidentifier, @schedule_id int = NULL, @collection_mode smallint AS BEGIN -- delete the jobs corresponding to the collection set DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_syscollector_delete_jobs ELSE BEGIN TRANSACTION BEGIN TRY IF (@collection_mode = 1) -- non-cached mode BEGIN IF (@upload_job_id IS NOT NULL) BEGIN -- note, upload job id = collection job id in this mode IF (@schedule_id IS NOT NULL) BEGIN EXEC dbo.sp_detach_schedule @job_id = @upload_job_id, @schedule_id = @schedule_id, @delete_unused_schedule = 0 END EXEC dbo.sp_delete_jobserver @job_id = @upload_job_id, @server_name = N'(local)' EXEC dbo.sp_delete_job @job_id = @upload_job_id END END ELSE -- cached mode BEGIN -- detach schedules, delete job servers, then delete jobs IF (@upload_job_id IS NOT NULL) BEGIN EXEC dbo.sp_detach_schedule @job_id = @upload_job_id, @schedule_id = @schedule_id, @delete_unused_schedule = 0 EXEC dbo.sp_delete_jobserver @job_id = @upload_job_id, @server_name = N'(local)' EXEC dbo.sp_delete_job @job_id = @upload_job_id END IF (@collection_job_id IS NOT NULL) BEGIN EXEC dbo.sp_detach_schedule @job_id = @collection_job_id, @schedule_name = N'RunAsSQLAgentServiceStartSchedule', @delete_unused_schedule = 0 EXEC dbo.sp_delete_jobserver @job_id = @collection_job_id, @server_name = N'(local)' EXEC dbo.sp_delete_job @job_id = @collection_job_id END END IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_syscollector_delete_jobs DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO --------------------------------------------------------------- -- Collection Set execution stats --------------------------------------------------------------- IF (OBJECT_ID('dbo.syscollector_execution_stats_internal', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table syscollector_execution_stats_internal...' CREATE TABLE [dbo].[syscollector_execution_stats_internal] ( log_id bigint NOT NULL, -- Log_id of the package that inserts the row task_name nvarchar(128) NOT NULL, -- Name of the task/component in the package that reports the execution stats execution_row_count_in int NULL, -- Number of rows that entered the data flow from its source transformations execution_row_count_out int NULL, -- Number of rows that exited the data flow into its destination transformations execution_row_count_errors int NULL, -- Number of rows that were re-directed to an error output due to processing errors execution_time_ms int NULL, -- Execution time of the data flow log_time datetime NOT NULL -- Date and time when this entry was logged CONSTRAINT [PK_syscollector_execution_stats] PRIMARY KEY CLUSTERED (log_id ASC, task_name ASC, log_time DESC), CONSTRAINT [FK_syscollector_execution_stats_log_id] FOREIGN KEY (log_id) REFERENCES [dbo].[syscollector_execution_log_internal] (log_id) ON DELETE CASCADE ) END go PRINT '' PRINT 'Creating view syscollector_execution_stats...' IF (NOT OBJECT_ID('dbo.syscollector_execution_stats', 'V') IS NULL) DROP VIEW [dbo].[syscollector_execution_stats] go CREATE VIEW [dbo].[syscollector_execution_stats] AS SELECT log_id, task_name, execution_row_count_in, execution_row_count_out, execution_row_count_errors, execution_time_ms, log_time FROM dbo.syscollector_execution_stats_internal go --------------------------------------------------------------- -- Logging stored procedures --------------------------------------------------------------- PRINT '' PRINT 'Creating stored procedure sp_syscollector_verify_event_log_id...' IF (NOT OBJECT_ID('dbo.sp_syscollector_verify_event_log_id', 'P') IS NULL) DROP PROCEDURE [dbo].[sp_syscollector_verify_event_log_id] go CREATE PROCEDURE [dbo].[sp_syscollector_verify_event_log_id] @log_id bigint, @allow_collection_set_id bit = 0 AS BEGIN SET NOCOUNT ON DECLARE @log_id_as_char VARCHAR(36) IF (@log_id IS NULL) BEGIN RAISERROR(14606, -1, -1, '@log_id') RETURN (1) END ELSE IF @allow_collection_set_id = 0 BEGIN IF (NOT EXISTS (SELECT log_id FROM dbo.syscollector_execution_log WHERE log_id = @log_id AND package_id IS NOT NULL)) BEGIN SELECT @log_id_as_char = CONVERT(VARCHAR(36), @log_id) RAISERROR(14262, -1, -1, '@log_id', @log_id_as_char) RETURN (1) END END ELSE BEGIN IF (NOT EXISTS (SELECT log_id FROM dbo.syscollector_execution_log WHERE log_id = @log_id)) BEGIN SELECT @log_id_as_char = CONVERT(VARCHAR(36), @log_id) RAISERROR(14262, -1, -1, '@log_id', @log_id_as_char) RETURN (1) END END RETURN (0) END go PRINT '' PRINT 'Creating stored procedure sp_syscollector_event_oncollectionstart...' IF (NOT OBJECT_ID('dbo.sp_syscollector_event_oncollectionstart', 'P') IS NULL) DROP PROCEDURE [dbo].[sp_syscollector_event_oncollectionstart] go CREATE PROCEDURE [dbo].[sp_syscollector_event_oncollectionstart] @collection_set_id int, @operator nvarchar(128) = NULL, @log_id bigint OUTPUT AS BEGIN SET NOCOUNT ON -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_operator') RETURN(1) -- Failure END -- Verify parameters -- -- Check the collection_set_id IF (@collection_set_id IS NULL) BEGIN RAISERROR(14606, -1, -1, '@collection_set_id') RETURN (1) END ELSE IF (NOT EXISTS (SELECT collection_set_id FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id)) BEGIN DECLARE @collection_set_id_as_char VARCHAR(36) SELECT @collection_set_id_as_char = CONVERT(VARCHAR(36), @collection_set_id) RAISERROR(14262, -1, -1, '@collection_set_id', @collection_set_id_as_char) RETURN (1) END -- Default operator to currently logged in user SET @operator = NULLIF(LTRIM(RTRIM(@operator)), '') SET @operator = ISNULL(@operator, suser_sname()) -- Insert the log record -- INSERT INTO dbo.syscollector_execution_log_internal ( parent_log_id, collection_set_id, start_time, last_iteration_time, finish_time, runtime_execution_mode, [status], operator, package_id, package_execution_id, failure_message ) VALUES ( NULL, @collection_set_id, GETDATE(), NULL, NULL, NULL, 0, -- Running @operator, NULL, NULL, NULL ) SET @log_id = SCOPE_IDENTITY() RETURN (0) END go PRINT '' PRINT 'Creating stored procedure sp_syscollector_event_oncollectionstop...' IF (NOT OBJECT_ID('dbo.sp_syscollector_event_oncollectionstop', 'P') IS NULL) DROP PROCEDURE [dbo].[sp_syscollector_event_oncollectionstop] go CREATE PROCEDURE [dbo].[sp_syscollector_event_oncollectionstop] @collection_set_id int AS BEGIN SET NOCOUNT ON -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_proxy') RETURN(1) -- Failure END -- Check the collection_set_id IF (@collection_set_id IS NULL) BEGIN RAISERROR(14606, -1, -1, '@collection_set_id') RETURN (1) END ELSE IF (NOT EXISTS (SELECT collection_set_id FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id)) BEGIN DECLARE @collection_set_id_as_char VARCHAR(36) SELECT @collection_set_id_as_char = CONVERT(VARCHAR(36), @collection_set_id) RAISERROR(14262, -1, -1, '@collection_set_id', @collection_set_id_as_char) RETURN (1) END -- Find the log_id -- It will be a log entry for the same collection set, with no parent and not finished DECLARE @log_id bigint SELECT TOP 1 @log_id = log_id FROM dbo.syscollector_execution_log_internal WHERE collection_set_id = @collection_set_id AND parent_log_id IS NULL AND finish_time IS NULL ORDER BY start_time DESC IF (@log_id IS NULL) BEGIN -- Raise a warning message RAISERROR(14606, 9, -1, '@log_id') END ELSE BEGIN -- Mark the log as finished UPDATE dbo.syscollector_execution_log_internal SET finish_time = GETDATE(), [status] = CASE WHEN [status] = 0 THEN 1 -- Mark complete if it was running ELSE [status] -- Leave the error status unchanged END WHERE log_id = @log_id END RETURN (0) END go PRINT '' PRINT 'Creating stored procedure sp_syscollector_event_oncollectionbegin...' IF (NOT OBJECT_ID('dbo.sp_syscollector_event_oncollectionbegin', 'P') IS NULL) DROP PROCEDURE [dbo].[sp_syscollector_event_oncollectionbegin] go CREATE PROCEDURE [dbo].[sp_syscollector_event_oncollectionbegin] @collection_set_id int, @mode smallint = NULL, @operator nvarchar(128) = NULL, @log_id bigint OUTPUT AS BEGIN SET NOCOUNT ON -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_proxy') RETURN(1) -- Failure END -- Verify parameters -- -- Check the collection_set_id IF (@collection_set_id IS NULL) BEGIN RAISERROR(14606, -1, -1, '@collection_set_id') RETURN (1) END ELSE IF (NOT EXISTS (SELECT collection_set_id FROM dbo.syscollector_collection_sets WHERE collection_set_id = @collection_set_id)) BEGIN DECLARE @collection_set_id_as_char VARCHAR(36) SELECT @collection_set_id_as_char = CONVERT(VARCHAR(36), @collection_set_id) RAISERROR(14262, -1, -1, '@collection_set_id', @collection_set_id_as_char) RETURN (1) END -- Default operator to currently logged in user SET @operator = NULLIF(LTRIM(RTRIM(@operator)), '') SET @operator = ISNULL(@operator, suser_sname()) -- Default mode to Collection SET @mode = ISNULL(@mode, 0) -- Find the parent log id. -- It will be a log entry for the same collection set, with no parent and not finished DECLARE @parent_log_id bigint SELECT TOP 1 @parent_log_id = log_id FROM dbo.syscollector_execution_log_internal WHERE collection_set_id = @collection_set_id AND parent_log_id IS NULL AND (@mode = 1 OR finish_time IS NULL) ORDER BY start_time DESC -- Insert the log record -- INSERT INTO dbo.syscollector_execution_log_internal ( parent_log_id, collection_set_id, collection_item_id, start_time, last_iteration_time, finish_time, runtime_execution_mode, [status], operator, package_id, package_execution_id, failure_message ) VALUES ( @parent_log_id, @collection_set_id, NULL, GETDATE(), NULL, NULL, @mode, 0, -- Running @operator, NULL, NULL, NULL ) SET @log_id = SCOPE_IDENTITY() RETURN (0) END go PRINT '' PRINT 'Creating stored procedure sp_syscollector_event_oncollectionend...' IF (NOT OBJECT_ID('dbo.sp_syscollector_event_oncollectionend', 'P') IS NULL) DROP PROCEDURE [dbo].[sp_syscollector_event_oncollectionend] go CREATE PROCEDURE [dbo].[sp_syscollector_event_oncollectionend] @log_id bigint AS BEGIN SET NOCOUNT ON -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_proxy') RETURN(1) -- Failure END -- Check the log_id DECLARE @retVal INT EXEC @retVal = dbo.sp_syscollector_verify_event_log_id @log_id, 1 IF (@retVal <> 0) RETURN (@retVal) -- Mark the log as finished UPDATE dbo.syscollector_execution_log_internal SET finish_time = GETDATE(), [status] = CASE WHEN [status] = 0 THEN 1 -- Mark complete if it was running ELSE [status] -- Leave the error status unchanged END WHERE log_id = @log_id RETURN (0) END go PRINT '' PRINT 'Creating stored procedure sp_syscollector_event_onpackagebegin...' IF (NOT OBJECT_ID('dbo.sp_syscollector_event_onpackagebegin', 'P') IS NULL) DROP PROCEDURE [dbo].[sp_syscollector_event_onpackagebegin] go CREATE PROCEDURE [dbo].[sp_syscollector_event_onpackagebegin] @parent_log_id bigint, @package_id uniqueidentifier, @package_execution_id uniqueidentifier, @collection_item_id int = NULL, @mode smallint = NULL, @operator nvarchar(128) = NULL, @log_id bigint OUTPUT AS BEGIN SET NOCOUNT ON -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_proxy') RETURN(1) -- Failure END -- Verify parameters -- -- Check the @parent_log_id IF (@parent_log_id IS NULL) BEGIN RAISERROR(14606, -1, -1, '@parent_log_id') RETURN (1) END ELSE IF (NOT EXISTS (SELECT log_id FROM dbo.syscollector_execution_log WHERE log_id = @parent_log_id)) BEGIN DECLARE @parent_log_id_as_char VARCHAR(36) SELECT @parent_log_id_as_char = CONVERT(VARCHAR(36), @parent_log_id) RAISERROR(14262, -1, -1, '@parent_log_id', @parent_log_id_as_char) RETURN (1) END -- Check the @package_id IF (@package_id IS NULL) BEGIN RAISERROR(14606, -1, -1, '@package_id') RETURN (1) END -- The 84CEC861... package is an id of our special Master package that is allowed to start -- the log without being saved to sysssispackages ELSE IF (@package_id != N'84CEC861-D619-433D-86FB-0BB851AF454A' AND NOT EXISTS (SELECT id FROM dbo.sysssispackages WHERE id = @package_id)) BEGIN DECLARE @package_id_as_char VARCHAR(50) SELECT @package_id_as_char = CONVERT(VARCHAR(50), @package_id) RAISERROR(14262, -1, -1, '@package_id', @package_id_as_char) RETURN (1) END -- Default operator to currently logged in user SET @operator = NULLIF(LTRIM(RTRIM(@operator)), '') SET @operator = ISNULL(@operator, suser_sname()) -- Default mode to Collection SET @mode = ISNULL(@mode, 0) -- Find out the collection_set_id from the parent DECLARE @collection_set_id INT SELECT @collection_set_id = collection_set_id FROM dbo.syscollector_execution_log WHERE log_id = @parent_log_id -- Check the @package_execution_id IF (@package_execution_id IS NULL) BEGIN RAISERROR(14606, -1, -1, '@package_execution_id') RETURN (1) END -- Insert the log record -- INSERT INTO dbo.syscollector_execution_log_internal ( parent_log_id, collection_set_id, collection_item_id, start_time, last_iteration_time, finish_time, runtime_execution_mode, [status], operator, package_id, package_execution_id, failure_message ) VALUES ( @parent_log_id, @collection_set_id, @collection_item_id, GETDATE(), NULL, NULL, @mode, 0, -- Running @operator, @package_id, @package_execution_id, NULL ) SET @log_id = SCOPE_IDENTITY() RETURN (0) END go PRINT '' PRINT 'Creating stored procedure sp_syscollector_event_onpackageend...' IF (NOT OBJECT_ID('dbo.sp_syscollector_event_onpackageend', 'P') IS NULL) DROP PROCEDURE [dbo].[sp_syscollector_event_onpackageend] go CREATE PROCEDURE [dbo].[sp_syscollector_event_onpackageend] @log_id bigint AS BEGIN SET NOCOUNT ON -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_proxy') RETURN(1) -- Failure END -- Check the log_id DECLARE @retVal INT EXEC @retVal = dbo.sp_syscollector_verify_event_log_id @log_id IF (@retVal <> 0) RETURN (@retVal) -- Mark the log as finished UPDATE dbo.syscollector_execution_log_internal SET finish_time = GETDATE(), [status] = CASE WHEN [status] = 0 THEN 1 -- Mark complete if it was running ELSE [status] -- Leave the error status unchanged END WHERE log_id = @log_id DECLARE @runtime_execution_mode smallint DECLARE @status smallint SELECT @status = [status], @runtime_execution_mode = runtime_execution_mode FROM dbo.syscollector_execution_log_internal WHERE log_id = @log_id -- status was successful and this is logged by an upload package IF @status = 1 AND @runtime_execution_mode = 1 BEGIN -- if the package ended succesfully, update the top most log to warning if it had failure -- this is because if there were a previous upload failure but the latest upload were successful, -- we want indicated a warning rather than a failure throughout the lifetime of this collection set DECLARE @parent_log_id BIGINT SELECT @parent_log_id = parent_log_id FROM dbo.syscollector_execution_log_internal WHERE log_id = @log_id; WHILE @parent_log_id IS NOT NULL BEGIN -- get the next parent SET @log_id = @parent_log_id SELECT @parent_log_id = parent_log_id FROM dbo.syscollector_execution_log_internal WHERE log_id = @log_id; END UPDATE dbo.syscollector_execution_log_internal SET [status] = CASE WHEN [status] = 2 THEN 3 -- Mark warning if it indicated a failure ELSE [status] -- Leave the original status unchanged END WHERE log_id = @log_id END RETURN (0) END go PRINT '' PRINT 'Creating stored procedure sp_syscollector_event_onpackageupdate...' IF (NOT OBJECT_ID('dbo.sp_syscollector_event_onpackageupdate', 'P') IS NULL) DROP PROCEDURE [dbo].[sp_syscollector_event_onpackageupdate] go CREATE PROCEDURE [dbo].[sp_syscollector_event_onpackageupdate] @log_id bigint AS BEGIN SET NOCOUNT ON -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_proxy') RETURN(1) -- Failure END -- Check the log_id DECLARE @retVal INT EXEC @retVal = dbo.sp_syscollector_verify_event_log_id @log_id IF (@retVal <> 0) RETURN (@retVal) -- Update the log UPDATE dbo.syscollector_execution_log_internal SET last_iteration_time = GETDATE() WHERE log_id = @log_id RETURN (0) END go PRINT '' PRINT 'Creating stored procedure sp_syscollector_event_onerror...' IF (NOT OBJECT_ID('dbo.sp_syscollector_event_onerror', 'P') IS NULL) DROP PROCEDURE [dbo].[sp_syscollector_event_onerror] go CREATE PROCEDURE [dbo].[sp_syscollector_event_onerror] @log_id bigint, @message nvarchar(2048) = NULL AS BEGIN SET NOCOUNT ON -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_proxy') RETURN(1) -- Failure END DECLARE @TranCounter INT SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_event_onerror ELSE BEGIN TRANSACTION BEGIN TRY -- Check the log_id -- If @message is passed, we can allow to enter the error for a collection set -- otherwise we will rely on the entries in sysssislog table to get the error message. DECLARE @retVal INT IF (@message IS NULL) BEGIN EXEC @retVal = dbo.sp_syscollector_verify_event_log_id @log_id, 0 END ELSE BEGIN EXEC @retVal = dbo.sp_syscollector_verify_event_log_id @log_id, 1 END IF (@retVal <> 0) RETURN (@retVal) DECLARE @failure_message NVARCHAR(2048) ,@execution_id UNIQUEIDENTIFIER IF @message IS NULL BEGIN -- If no message is provided, find the last task that has failed -- for this package in the sysssislog table. -- Store the message as the failure_message for our package log. SELECT @execution_id = package_execution_id FROM dbo.syscollector_execution_log WHERE log_id = @log_id SELECT TOP 1 @failure_message = [message] FROM dbo.sysssislog WHERE executionid = @execution_id AND (UPPER([event] COLLATE SQL_Latin1_General_CP1_CS_AS) = 'ONERROR') ORDER BY endtime DESC END ELSE BEGIN -- Otherwise use the provided message SET @failure_message = @message END -- Update the execution log UPDATE dbo.syscollector_execution_log_internal SET [status] = 2 -- Mark as Failed ,failure_message = @failure_message WHERE log_id = @log_id -- Update all parent logs with the failure status SELECT @log_id = parent_log_id FROM dbo.syscollector_execution_log_internal WHERE log_id = @log_id; WHILE @log_id IS NOT NULL BEGIN UPDATE dbo.syscollector_execution_log_internal SET [status] = 2 -- Mark as Failed WHERE log_id = @log_id; -- get the next parent SELECT @log_id = parent_log_id FROM dbo.syscollector_execution_log_internal WHERE log_id = @log_id; END IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_event_onerror DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END go PRINT '' PRINT 'Creating stored procedure sp_syscollector_event_onstatsupdate...' IF (NOT OBJECT_ID('dbo.sp_syscollector_event_onstatsupdate', 'P') IS NULL) DROP PROCEDURE [dbo].[sp_syscollector_event_onstatsupdate] go CREATE PROCEDURE [dbo].[sp_syscollector_event_onstatsupdate] @log_id bigint, @task_name nvarchar(128), @row_count_in int = NULL, @row_count_out int = NULL, @row_count_error int = NULL, @execution_time_ms int = NULL AS BEGIN SET NOCOUNT ON -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_proxy') RETURN(1) -- Failure END -- Check the log_id DECLARE @retVal INT EXEC @retVal = dbo.sp_syscollector_verify_event_log_id @log_id IF (@retVal <> 0) RETURN (@retVal) -- Check task name IF (@task_name IS NOT NULL) BEGIN SET @task_name = NULLIF(LTRIM(RTRIM(@task_name)), N'') END IF (@task_name IS NULL) BEGIN RAISERROR(14606, -1, -1, '@task_name') RETURN (1) END -- Insert the log entry INSERT INTO dbo.syscollector_execution_stats_internal ( log_id, task_name, execution_row_count_in, execution_row_count_out, execution_row_count_errors, execution_time_ms, log_time ) VALUES ( @log_id, @task_name, @row_count_in, @row_count_out, @row_count_error, NULLIF(@execution_time_ms, 0), GETDATE() ) RETURN (0) END go --------------------------------------------------------------- -- Data Collector log viewing functions and views --------------------------------------------------------------- -- [fn_syscollector_find_collection_set_root] -- This function finds the first log entry for a given collection set -- run. It retunrs log_id of that entry. PRINT '' PRINT 'Creating function [dbo].[fn_syscollector_find_collection_set_root] ...' IF (NOT OBJECT_ID(N'dbo.fn_syscollector_find_collection_set_root', 'FN') IS NULL) DROP FUNCTION [dbo].[fn_syscollector_find_collection_set_root] go CREATE FUNCTION [dbo].[fn_syscollector_find_collection_set_root] ( @log_id BIGINT ) RETURNS BIGINT WITH RETURNS NULL ON NULL INPUT AS BEGIN DECLARE @root_id BIGINT; -- Derive result using a CTE as the table is self-referencing WITH graph AS ( -- select the anchor (specified) node SELECT log_id, parent_log_id FROM dbo.syscollector_execution_log WHERE log_id = @log_id UNION ALL -- select the parent node recursively SELECT node.log_id, node.parent_log_id FROM dbo.syscollector_execution_log node INNER JOIN graph AS leaf ON (node.log_id = leaf.parent_log_id) ) SELECT @root_id = log_id FROM graph WHERE parent_log_id = 0; --Return result RETURN ISNULL(@root_id, @log_id) END go -- [fn_syscollector_get_execution_log_tree] -- This function returns a set of log entries related to a single run -- of a collection set. The entries are ordered in a hierarchy, starting from -- the collection set first log entry and then down through all packages -- that were executed as part of the collection set. PRINT '' PRINT 'Creating function [dbo].[fn_syscollector_get_execution_log_tree] ...' IF (NOT OBJECT_ID(N'dbo.fn_syscollector_get_execution_log_tree', 'IF') IS NULL) DROP FUNCTION [dbo].[fn_syscollector_get_execution_log_tree] go CREATE FUNCTION [dbo].[fn_syscollector_get_execution_log_tree] ( @log_id BIGINT, @from_collection_set BIT = 1 ) RETURNS TABLE AS RETURN ( -- Derive result using a CTE as the table is self-referencing WITH graph AS ( -- select the anchor (specified) node SELECT log_id, parent_log_id, collection_set_id, start_time, last_iteration_time, finish_time, runtime_execution_mode, operator, [status], package_id, package_execution_id, failure_message, 0 AS depth FROM dbo.syscollector_execution_log WHERE log_id = CASE @from_collection_set WHEN 1 THEN dbo.fn_syscollector_find_collection_set_root(@log_id) ELSE @log_id END -- select the child nodes recursively UNION ALL SELECT leaf.log_id, leaf.parent_log_id, leaf.collection_set_id, leaf.start_time, leaf.last_iteration_time, leaf.finish_time, leaf.runtime_execution_mode, leaf.operator, leaf.[status], leaf.package_id, leaf.package_execution_id, leaf.failure_message, node.depth + 1 AS depth FROM dbo.syscollector_execution_log AS leaf INNER JOIN graph AS node ON (node.log_id = leaf.parent_log_id) ) SELECT log_id, parent_log_id, collection_set_id, start_time, last_iteration_time, finish_time, CASE WHEN finish_time IS NOT NULL THEN DATEDIFF(ss, start_time, finish_time) WHEN last_iteration_time IS NOT NULL THEN DATEDIFF(ss, start_time, last_iteration_time) ELSE 0 END AS duration, runtime_execution_mode, operator, [status], package_id, package_execution_id, failure_message, depth FROM graph ) go -- [fn_syscollector_get_execution_stats] -- This function returns stats for each execution of a package. -- The stats should be logged for each iteration within the package. PRINT '' PRINT 'Creating function [dbo].[fn_syscollector_get_execution_stats] ...' IF (NOT OBJECT_ID(N'dbo.fn_syscollector_get_execution_stats', 'IF') IS NULL) DROP FUNCTION [dbo].[fn_syscollector_get_execution_stats] go CREATE FUNCTION [dbo].[fn_syscollector_get_execution_stats] ( @log_id BIGINT ) RETURNS TABLE AS RETURN ( SELECT log_id, task_name, AVG(execution_row_count_in) AS avg_row_count_in, MIN(execution_row_count_in) AS min_row_count_in, MAX(execution_row_count_in) AS max_row_count_in, AVG(execution_row_count_out) AS avg_row_count_out, MIN(execution_row_count_out) AS min_row_count_out, MAX(execution_row_count_out) AS max_row_count_out, AVG(execution_row_count_errors) AS avg_row_count_errors, MIN(execution_row_count_errors) AS min_row_count_errors, MAX(execution_row_count_errors) AS max_row_count_errors, AVG(execution_time_ms) AS avg_duration, MIN(execution_time_ms) AS min_duration, MAX(execution_time_ms) AS max_duration FROM dbo.syscollector_execution_stats WHERE log_id = @log_id GROUP BY log_id, task_name ) go -- [fn_syscollector_get_execution_details] -- This function returns detailed log entries from SSIS log for each package -- execution. The log id passed as input is an id of a log entry for that package -- from syscollector_execution_log view. PRINT '' PRINT 'Creating function [dbo].[fn_syscollector_get_execution_details] ...' IF (NOT OBJECT_ID(N'dbo.fn_syscollector_get_execution_details', 'IF') IS NULL) DROP FUNCTION [dbo].[fn_syscollector_get_execution_details] go CREATE FUNCTION [dbo].[fn_syscollector_get_execution_details] ( @log_id BIGINT ) RETURNS TABLE AS RETURN ( SELECT TOP (100) PERCENT l.source, l.event, l.message, l.starttime AS start_time, l.endtime AS finish_time, l.datacode, l.databytes FROM sysssislog l JOIN dbo.syscollector_execution_log e ON (e.package_execution_id = l.executionid) WHERE e.log_id = @log_id ORDER BY l.starttime ) go -- [syscollector_execution_log_full] -- An expanded log view that shows the log entries in a hierarchical order. PRINT '' PRINT 'Creating view syscollector_execution_log_full...' IF (NOT OBJECT_ID('dbo.syscollector_execution_log_full', 'V') IS NULL) DROP VIEW [dbo].[syscollector_execution_log_full] go CREATE VIEW [dbo].[syscollector_execution_log_full] AS SELECT t.log_id, ISNULL(t.parent_log_id, 0) as parent_log_id, CASE WHEN t.package_id IS NULL THEN SPACE(t.depth * 4) + c.name WHEN t.package_id = N'84CEC861-D619-433D-86FB-0BB851AF454A' THEN SPACE(t.depth * 4) + N'Master' ELSE SPACE(t.depth * 4) + p.name END AS [name], t.[status], t.runtime_execution_mode, t.start_time, t.last_iteration_time, t.finish_time, t.duration, t.failure_message, t.operator, t.package_execution_id, t.collection_set_id FROM dbo.syscollector_execution_log_internal l CROSS APPLY dbo.fn_syscollector_get_execution_log_tree(l.log_id, 0) t LEFT OUTER JOIN dbo.syscollector_collection_sets c ON( c.collection_set_id = t.collection_set_id) LEFT OUTER JOIN dbo.sysssispackages p ON (p.id = t.package_id AND p.id != N'84CEC861-D619-433D-86FB-0BB851AF454A') WHERE l.parent_log_id IS NULL go --------------------------------------------------------------- -- Data Collection log clean-up --------------------------------------------------------------- -- [sp_syscollector_delete_execution_log_tree] -- This stored procedure removes all log entries related to a single -- run of a collection set. It also removes corresponding log entries -- from SSIS log tables. PRINT '' IF (NOT OBJECT_ID(N'dbo.sp_syscollector_delete_execution_log_tree', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_execution_log_tree] ...' DROP PROCEDURE [dbo].[sp_syscollector_delete_execution_log_tree] END go PRINT 'Creating procedure [dbo].[sp_syscollector_delete_execution_log_tree] ...' GO CREATE PROCEDURE [dbo].[sp_syscollector_delete_execution_log_tree] @log_id BIGINT, @from_collection_set BIT = 1 AS BEGIN -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_operator') RETURN(1) -- Failure END SET NOCOUNT ON; CREATE TABLE #log_ids (log_id BIGINT); WITH graph AS ( SELECT log_id FROM dbo.syscollector_execution_log WHERE log_id = CASE @from_collection_set WHEN 1 THEN dbo.fn_syscollector_find_collection_set_root(@log_id) ELSE @log_id END UNION ALL SELECT leaf.log_id FROM dbo.syscollector_execution_log AS leaf INNER JOIN graph AS node ON (node.log_id = leaf.parent_log_id) ) INSERT INTO #log_ids SELECT log_id FROM graph -- Delete all ssis log records pertaining to the selected logs DELETE FROM dbo.sysssislog FROM dbo.sysssislog AS s INNER JOIN dbo.syscollector_execution_log_internal AS l ON (l.package_execution_id = s.executionid) INNER JOIN #log_ids AS i ON i.log_id = l.log_id -- Then delete the actual logs DELETE FROM syscollector_execution_log_internal FROM syscollector_execution_log_internal AS l INNER Join #log_ids AS i ON i.log_id = l.log_id DROP TABLE #log_ids RETURN (0) END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_delete_collection_set_internal]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_collection_set_internal]...' DROP PROCEDURE [dbo].[sp_syscollector_delete_collection_set_internal] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_delete_collection_set_internal]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_delete_collection_set_internal] @collection_set_id int, @name sysname, @collection_job_id uniqueidentifier, @upload_job_id uniqueidentifier, @collection_mode smallint AS BEGIN DECLARE @TranCounter int SET @TranCounter = @@TRANCOUNT IF (@TranCounter > 0) SAVE TRANSACTION tran_delete_collection_set ELSE BEGIN TRANSACTION BEGIN TRY -- clean log before deleting collection set DECLARE @log_id bigint SET @log_id = (SELECT TOP(1) log_id FROM dbo.syscollector_execution_log WHERE collection_set_id = @collection_set_id) WHILE (@log_id IS NOT NULL) BEGIN EXEC dbo.sp_syscollector_delete_execution_log_tree @log_id = @log_id SET @log_id = (SELECT TOP(1) log_id FROM dbo.syscollector_execution_log WHERE collection_set_id = @collection_set_id) END DECLARE @schedule_id int SELECT @schedule_id = schedule_id FROM dbo.syscollector_collection_sets cs JOIN sysschedules_localserver_view sv ON (cs.schedule_uid = sv.schedule_uid) WHERE collection_set_id = @collection_set_id DELETE [dbo].[syscollector_collection_sets_internal] WHERE collection_set_id = @collection_set_id EXEC dbo.sp_syscollector_delete_jobs @collection_job_id = @collection_job_id, @upload_job_id = @upload_job_id, @schedule_id = @schedule_id, @collection_mode = @collection_mode IF (@TranCounter = 0) COMMIT TRANSACTION RETURN (0) END TRY BEGIN CATCH IF (@TranCounter = 0 OR XACT_STATE() = -1) ROLLBACK TRANSACTION ELSE IF (XACT_STATE() = 1) ROLLBACK TRANSACTION tran_delete_collection_set DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(200); SELECT @ErrorLine = ERROR_LINE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(), @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); RAISERROR (14684, @ErrorSeverity, -1 , @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine, @ErrorMessage); RETURN (1) END CATCH END GO -- This is a stored procedure of collection_set, but it is created here because it -- makes references to the collection item view, execution log, -- and the [sp_syscollector_delete_execution_log_tree] stored proc IF (NOT OBJECT_ID('[dbo].[sp_syscollector_delete_collection_set]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_delete_collection_set]...' DROP PROCEDURE [dbo].[sp_syscollector_delete_collection_set] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_delete_collection_set]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_delete_collection_set] @collection_set_id int = NULL, @name sysname = NULL WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser' AS BEGIN -- Security check (role membership) EXECUTE AS CALLER; IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN REVERT; RAISERROR(14677, -1, -1, 'dc_admin') RETURN (1) END REVERT; DECLARE @retVal int EXEC @retVal = dbo.sp_syscollector_verify_collection_set @collection_set_id OUTPUT, @name OUTPUT IF (@retVal <> 0) RETURN (1) DECLARE @is_system bit DECLARE @is_running bit DECLARE @upload_job_id uniqueidentifier DECLARE @collection_job_id uniqueidentifier DECLARE @collection_mode smallint SELECT @is_running = is_running, @is_system = is_system, @upload_job_id = upload_job_id, @collection_job_id = collection_job_id, @collection_mode = collection_mode FROM [dbo].[syscollector_collection_sets] WHERE collection_set_id = @collection_set_id IF (@is_system = 1) BEGIN -- cannot update, delete, or add new collection items to a system collection set RAISERROR(14696, -1, -1); RETURN (1) END IF (@is_running = 1) BEGIN EXEC @retVal = sp_syscollector_stop_collection_set @collection_set_id = @collection_set_id IF (@retVal <> 0) RETURN (1) END -- All checks are go -- Do the actual delete EXEC @retVal = sp_syscollector_delete_collection_set_internal @collection_set_id = @collection_set_id, @name = @name, @collection_job_id = @collection_job_id, @upload_job_id = @upload_job_id, @collection_mode = @collection_mode RETURN (0) END GO IF (NOT OBJECT_ID('dbo.sp_syscollector_purge_collection_logs', 'P') IS NULL) BEGIN PRINT '' PRINT 'Dropping stored procedure sp_syscollector_purge_collection_logs...' DROP PROCEDURE [dbo].[sp_syscollector_purge_collection_logs] END GO -- [sp_syscollector_purge_collection_logs] -- The sp cleans any log record with an expired finish date -- Expiration is measured from the date provided to the sp -- and defaults to TODAY. PRINT '' PRINT 'Creating stored procedure sp_syscollector_purge_collection_logs...' GO CREATE PROCEDURE [dbo].[sp_syscollector_purge_collection_logs] @reference_date datetime = NULL AS BEGIN SET NOCOUNT ON -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'dc_proxy'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14677, -1, -1, 'dc_proxy') RETURN(1) -- Failure END IF (@reference_date IS NULL) BEGIN SET @reference_date = GETDATE() END -- An expired log record is any record of a collection set that is older than -- the reference date minus the collection set's days_until_expiration CREATE TABLE #purged_log_ids (log_id BIGINT) INSERT INTO #purged_log_ids SELECT log_id FROM syscollector_execution_log_internal as l INNER JOIN syscollector_collection_sets s ON l.collection_set_id = s.collection_set_id WHERE s.days_until_expiration > 0 AND @reference_date >= DATEADD(DAY, s.days_until_expiration, l.finish_time) -- Delete all ssis log records pertaining to expired logs DELETE FROM dbo.sysssislog FROM dbo.sysssislog AS s INNER JOIN dbo.syscollector_execution_log_internal AS l ON (l.package_execution_id = s.executionid) INNER JOIN #purged_log_ids AS i ON i.log_id = l.log_id -- Then delete the actual logs DELETE FROM syscollector_execution_log_internal FROM syscollector_execution_log_internal AS l INNER Join #purged_log_ids AS i ON i.log_id = l.log_id DROP TABLE #purged_log_ids -- Go for another round to cleanup the orphans -- Ideally, the log heirarchy guarantees that a finish time by a parent log will always -- be higher than the finish time of any of its descendants. -- The purge step however does not delete log records with a null finish time -- A child log can have a null finish time while its parent is closed if there is an -- error in execution that causes the log to stay open. -- If such a child log exists, its parent will be purged leaving it as an orphan -- get orphan records and all their descendants in a cursor and purge them DECLARE orphaned_log_cursor INSENSITIVE CURSOR FOR SELECT log_id FROM syscollector_execution_log_internal WHERE parent_log_id NOT IN ( SELECT log_id FROM syscollector_execution_log_internal ) FOR READ ONLY DECLARE @log_id BIGINT -- for every orphan, delete all its remaining tree -- this is supposedly a very small fraction of the entire log OPEN orphaned_log_cursor FETCH orphaned_log_cursor INTO @log_id WHILE @@FETCH_STATUS = 0 BEGIN EXEC sp_syscollector_delete_execution_log_tree @log_id = @log_id, @from_collection_set = 0 FETCH orphaned_log_cursor INTO @log_id END CLOSE orphaned_log_cursor DEALLOCATE orphaned_log_cursor END GO --------------------------------------------------------------- -- Start and stop Data Collector --------------------------------------------------------------- IF (NOT OBJECT_ID('[dbo].[sp_syscollector_enable_collector]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_enable_collector]...' DROP PROCEDURE [dbo].[sp_syscollector_enable_collector] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_enable_collector]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_enable_collector] WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser' AS BEGIN -- Security check (role membership) EXECUTE AS CALLER; IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN REVERT; RAISERROR(14677, -1, -1, 'dc_operator') RETURN(1) -- Failure END REVERT; BEGIN TRANSACTION DECLARE @was_enabled int; SELECT @was_enabled = ISNULL(CONVERT(int, parameter_value),0) FROM [dbo].[syscollector_config_store_internal] WHERE parameter_name = 'CollectorEnabled' IF (@was_enabled = 0) BEGIN UPDATE [dbo].[syscollector_config_store_internal] SET parameter_value = 1 WHERE parameter_name = 'CollectorEnabled' DECLARE @collection_set_id int DECLARE collection_set_cursor CURSOR LOCAL FOR SELECT collection_set_id FROM dbo.syscollector_collection_sets WHERE is_running = 1 OPEN collection_set_cursor FETCH collection_set_cursor INTO @collection_set_id WHILE @@FETCH_STATUS = 0 BEGIN EXEC dbo.sp_syscollector_start_collection_set_jobs @collection_set_id = @collection_set_id FETCH collection_set_cursor INTO @collection_set_id END CLOSE collection_set_cursor DEALLOCATE collection_set_cursor END COMMIT TRANSACTION END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_disable_collector]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_disable_collector]...' DROP PROCEDURE [dbo].[sp_syscollector_disable_collector] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_disable_collector]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_disable_collector] WITH EXECUTE AS OWNER -- 'MS_DataCollectorInternalUser' AS BEGIN -- Security check (role membership) EXECUTE AS CALLER; IF (NOT (ISNULL(IS_MEMBER(N'dc_operator'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN REVERT; RAISERROR(14677, -1, -1, 'dc_operator') RETURN(1) -- Failure END REVERT; BEGIN TRANSACTION DECLARE @was_enabled int; SELECT @was_enabled = ISNULL(CONVERT(int, parameter_value),0) FROM [dbo].[syscollector_config_store_internal] WHERE parameter_name = 'CollectorEnabled' IF (@was_enabled <> 0) BEGIN UPDATE [dbo].[syscollector_config_store_internal] SET parameter_value = 0 WHERE parameter_name = 'CollectorEnabled' DECLARE @collection_set_id INT DECLARE @collection_mode SMALLINT DECLARE @collection_job_id UNIQUEIDENTIFIER DECLARE collection_set_cursor CURSOR LOCAL FOR SELECT collection_set_id, collection_mode, collection_job_id FROM dbo.syscollector_collection_sets WHERE is_running = 1 OPEN collection_set_cursor FETCH collection_set_cursor INTO @collection_set_id, @collection_mode, @collection_job_id WHILE @@FETCH_STATUS = 0 BEGIN -- If this collection set is running in cached mode, and the collection job is running, we need to stop the job explicitly here DECLARE @is_collection_job_running INT EXECUTE [dbo].[sp_syscollector_get_collection_set_execution_status] @collection_set_id = @collection_set_id, @is_collection_running = @is_collection_job_running OUTPUT IF (@is_collection_job_running = 1 AND @collection_mode = 0) -- Cached mode BEGIN EXEC sp_stop_job @job_id = @collection_job_id END -- Now, disable the jobs and detach them from the upload schedules EXEC dbo.sp_syscollector_stop_collection_set_jobs @collection_set_id = @collection_set_id FETCH collection_set_cursor INTO @collection_set_id, @collection_mode, @collection_job_id END CLOSE collection_set_cursor DEALLOCATE collection_set_cursor END COMMIT TRANSACTION END GO IF (NOT OBJECT_ID('[dbo].[sp_syscollector_get_trace_info]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_get_trace_info]' DROP PROCEDURE [dbo].[sp_syscollector_get_trace_info] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_get_trace_info]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_get_trace_info] @trace_path nvarchar(512), @use_default int AS BEGIN SELECT CONVERT(nvarchar(30), t.start_time, 126) as start_time, CASE t.status WHEN 1 THEN 1 ELSE 0 END AS is_running, ISNULL(t.dropped_event_count,0) as dropped_event_count, t.id FROM sys.traces t WHERE (@use_default=1 and t.is_default=1) OR (@use_default=0 AND t.path LIKE (@trace_path + N'%.trc')) END GO --------------------------------------------------------------- -- Data Collector: Helper procedures --------------------------------------------------------------- -- Procedure to retrieve a query plan from cache IF (NOT OBJECT_ID('[dbo].[sp_syscollector_text_query_plan_lookpup]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_text_query_plan_lookpup]' DROP PROCEDURE [dbo].[sp_syscollector_text_query_plan_lookpup] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_text_query_plan_lookpup]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_text_query_plan_lookpup] @plan_handle varbinary(64), @statement_start_offset int, @statement_end_offset int AS BEGIN SET NOCOUNT ON SELECT @plan_handle AS plan_handle, @statement_start_offset AS statement_start_offset, @statement_end_offset AS statement_end_offset, [dbid] AS database_id, [objectid] AS object_id, OBJECT_NAME(objectid, dbid) AS object_name, [query_plan] AS query_plan FROM [sys].[dm_exec_text_query_plan](@plan_handle, @statement_start_offset, @statement_end_offset) dm END GO -- Procedure to retrieve a query text from cache IF (NOT OBJECT_ID('[dbo].[sp_syscollector_sql_text_lookup]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_sql_text_lookup]' DROP PROCEDURE [dbo].[sp_syscollector_sql_text_lookup] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_sql_text_lookup]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_sql_text_lookup] @sql_handle varbinary(64) AS BEGIN SET NOCOUNT ON SELECT @sql_handle as sql_handle, dm.[dbid] AS database_id, dm.[objectid] AS object_id, OBJECT_NAME(objectid, dbid) AS object_name, CASE dm.[encrypted] WHEN 1 THEN N'Query SQL Text Encrypted' ELSE dm.[text] END AS sql_text FROM [sys].[dm_exec_sql_text](@sql_handle) dm END GO --------------------------------------------------------------- -- Install out-of-the-box objects --------------------------------------------------------------- PRINT 'Installing out of the box Collector objects' PRINT '' -- We need agent XP's to be on for many parts of the coming installation script -- Enable them here once and return them to their original state when done DECLARE @advopt_old_value int DECLARE @comp_old_value int EXEC #sp_enable_component 'Agent XPs', @advopt_old_value out, @comp_old_value out -- We need the old values to endure beyond batches -- insert them into temp tables IF (OBJECT_ID('tempdb..#advopt_old_value', 'U') IS NOT NULL) BEGIN DROP TABLE #advopt_old_value END SELECT @advopt_old_value AS advopt_old_value INTO #advopt_old_value IF (OBJECT_ID('tempdb..#comp_old_value', 'U') IS NOT NULL) BEGIN DROP TABLE #comp_old_value END SELECT @comp_old_value AS comp_old_value INTO #comp_old_value GO -- disable the collector first EXEC sp_syscollector_disable_collector GO --------------------------------------------------------------- -- Out-of-the-box SSIS folders for Data Collector --------------------------------------------------------------- PRINT 'Creating SSIS folders...' -- create 'Data Collector' folder under the root IF(NOT EXISTS(SELECT * FROM dbo.sysssispackagefolders WHERE folderid = '8877FE4B-A938-4a51-84B9-C5BDAD74B0AD')) BEGIN EXEC dbo.sp_ssis_addfolder @parentfolderid = '00000000-0000-0000-0000-000000000000', @name = 'Data Collector', @folderid = '8877FE4B-A938-4a51-84B9-C5BDAD74B0AD' END GO -- create 'Generated' folder under 'Data Collector' IF(NOT EXISTS(SELECT * FROM dbo.sysssispackagefolders WHERE folderid = '39163C42-602B-42C9-B4F7-1843614F9625')) BEGIN EXEC dbo.sp_ssis_addfolder @parentfolderid = '8877FE4B-A938-4a51-84B9-C5BDAD74B0AD', @name = 'Generated', @folderid = '39163C42-602B-42c9-B4F7-1843614F9625' END GO --------------------------------------------------------------- -- Loading instmdw.sql --------------------------------------------------------------- -- a data collector table to store BLOB IF (OBJECT_ID(N'[dbo].[syscollector_blobs_internal]', 'U') IS NULL) BEGIN PRINT 'Creating table [dbo].[syscollector_blobs_internal]...' CREATE TABLE [dbo].[syscollector_blobs_internal] ( parameter_name nvarchar(128) NOT NULL, parameter_value varbinary(max) NOT NULL, CONSTRAINT [PK_syscollector_blobs_internal_paremeter_name] PRIMARY KEY CLUSTERED (parameter_name ASC) ) END GO -- an SP to read a parameter value from the syscollector BLOB table -- this stored procedure is called by the wizard to retrieve the instmdw.sql when setting up MDW IF (NOT OBJECT_ID('[dbo].[sp_syscollector_get_instmdw]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_get_instmdw]' DROP PROCEDURE [dbo].[sp_syscollector_get_instmdw] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_get_instmdw]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_get_instmdw] AS BEGIN -- only dc_admin and dbo can setup MDW IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14712, -1, -1) WITH LOG RETURN(1) -- Failure END -- if the script has not been loaded, load it now IF (NOT EXISTS(SELECT parameter_name FROM syscollector_blobs_internal WHERE parameter_name = N'InstMDWScript')) BEGIN EXECUTE sp_syscollector_upload_instmdw END SELECT cast(parameter_value as nvarchar(max)) FROM syscollector_blobs_internal WHERE parameter_name = N'InstMDWScript' END GO -- the script that would upload or update instmdw.sql -- this is used when a hotfix, service pack, or upgrade is issued in instmdw.sql -- user can specify the path to the new instmdw.sql to be installed or updated to. IF (NOT OBJECT_ID('[dbo].[sp_syscollector_upload_instmdw]', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [dbo].[sp_syscollector_upload_instmdw]' DROP PROCEDURE [dbo].[sp_syscollector_upload_instmdw] END GO PRINT 'Creating procedure [dbo].[sp_syscollector_upload_instmdw]...' GO CREATE PROCEDURE [dbo].[sp_syscollector_upload_instmdw] @installpath nvarchar(2048) = NULL AS BEGIN -- only dc_admin and dbo can setup MDW IF (NOT (ISNULL(IS_MEMBER(N'dc_admin'), 0) = 1) AND NOT (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1)) BEGIN RAISERROR(14712, -1, -1) WITH LOG RETURN(1) -- Failure END IF (@installpath IS NULL) BEGIN EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @installpath OUTPUT; IF RIGHT(@installpath, 1) != N'\' set @installpath = @installpath + N'\' SET @installpath = @installpath + N'Install\' END DECLARE @filename nvarchar(2048); SET @filename = @installpath + N'instmdw.sql' PRINT 'Uploading instmdw.sql from disk: ' + @filename CREATE TABLE #bulkuploadinstmdwscript ( [instmdwscript] nvarchar(max) NOT NULL ); DECLARE @stmt_bulkinsert nvarchar(2048); SET @stmt_bulkinsert = N' BULK INSERT #bulkuploadinstmdwscript FROM ' -- Escape any embedded single quotes (we can't use QUOTENAME here b/c it can't handle strings > 128 chars) + '''' + REPLACE(@filename COLLATE database_default, N'''', N'''''') + ''' WITH ( DATAFILETYPE = ''char'', FIELDTERMINATOR = ''dc:stub:ft'', ROWTERMINATOR = ''dc:stub:rt'', CODEPAGE = ''RAW'' ); '; EXECUTE sp_executesql @stmt_bulkinsert; DECLARE @bytesLoaded int SELECT @bytesLoaded = ISNULL (DATALENGTH ([instmdwscript]), 0) FROM #bulkuploadinstmdwscript PRINT 'Loaded ' + CONVERT (nvarchar, @bytesLoaded) + ' bytes from ' + '''' + REPLACE(@filename COLLATE database_default, N'''', N'''''') + '''' DECLARE @scriptdata varbinary(max) SELECT @scriptdata = convert(varbinary(max), [instmdwscript]) FROM #bulkuploadinstmdwscript; IF (EXISTS(SELECT * FROM [dbo].[syscollector_blobs_internal] WHERE parameter_name = N'InstMDWScript')) BEGIN UPDATE [dbo].[syscollector_blobs_internal] SET parameter_value = @scriptdata WHERE parameter_name = N'InstMDWScript' END ELSE BEGIN INSERT INTO [dbo].[syscollector_blobs_internal] ( parameter_name, parameter_value ) VALUES ( N'InstMDWScript', @scriptdata ) END DROP TABLE #bulkuploadinstmdwscript END GO --------------------------------------------------------------- -- Out-of-the-box data collector packages - loading SSIS packages --------------------------------------------------------------- CREATE PROCEDURE #syscollector_upload_package_from_file @filename nvarchar(2048), @packagename sysname, @packageid uniqueidentifier, @versionid uniqueidentifier AS BEGIN RAISERROR ('Uploading data collector package from disk: %s', 0, 1, @filename) WITH NOWAIT; CREATE TABLE #bulkpackage ( [packagexml] xml NOT NULL ); DECLARE @stmt_bulkinsert nvarchar(2048); SET @stmt_bulkinsert = N' BULK INSERT #bulkpackage FROM ' -- Escape any embedded single quotes (we can't use QUOTENAME here b/c it can't handle strings > 128 chars) + '''' + REPLACE(@filename COLLATE database_default, N'''', N'''''') + ''' WITH ( DATAFILETYPE = ''char'', FIELDTERMINATOR = ''dc:stub:ft'', ROWTERMINATOR = ''dc:stub:rt'', CODEPAGE = ''RAW'' ); '; EXECUTE sp_executesql @stmt_bulkinsert; DECLARE @bytesLoaded int SELECT @bytesLoaded = ISNULL (DATALENGTH ([packagexml]), 0) FROM #bulkpackage PRINT 'Loaded ' + CONVERT (varchar, @bytesLoaded) + ' bytes from ' + '''' + REPLACE(@filename COLLATE database_default, N'''', N'''''') + '''' DECLARE @packagebin varbinary(max); SELECT @packagebin = convert(varbinary(max),[packagexml]) FROM #bulkpackage; DECLARE @loadtime datetime; SET @loadtime = getdate(); DROP TABLE #bulkpackage EXECUTE sp_ssis_putpackage @name = @packagename , @id = @packageid , @description = N'System Data Collector Package' , @createdate = @loadtime , @folderid = '8877FE4B-A938-4a51-84B9-C5BDAD74B0AD' , @packagedata = @packagebin , @packageformat = 1 , @packagetype = 5 -- DTSPKT_DTSDESIGNER100 , @vermajor = 1 , @verminor = 0 , @verbuild = 0 , @vercomments = N'' , @verid = @versionid ; END; GO CREATE PROCEDURE #syscollector_upload_package @packagename sysname, @packageid uniqueidentifier, @versionid uniqueidentifier AS BEGIN DECLARE @installpath nvarchar(2048); EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @installpath OUTPUT; IF RIGHT(@installpath,1) != N'\' set @installpath = @installpath + N'\' SET @installpath = @installpath + N'Install\' DECLARE @filename nvarchar(2048); SET @filename = @installpath + @packagename + N'.dtsx' RAISERROR ('Uploading data collector package from disk: %s', 0, 1, @filename) WITH NOWAIT; EXEC #syscollector_upload_package_from_file @filename=@filename, @packagename=@packagename, @packageid=@packageid, @versionid=@versionid; END; GO -- -- Data Collector placeholder comment, dont move or remove. -- 875f7de1-9e83-4be1-8b96-2df4a8533e88 -- -- Load SSIS packages needed by collector types -- Temporarily enable the 'Agent XPs' config option so that sp_ssis_putpackage can -- succeed when SQLAgent is stopped. EXECUTE #syscollector_upload_package @packagename='SqlTraceCollect' , @packageid='0E149FC9-1046-4DE6-98BF-4B22ED6F6C42' , @versionid='244F0904-5CC6-49B8-AE90-905AEEA8BAF3'; EXECUTE #syscollector_upload_package @packagename='SqlTraceUpload' , @packageid='F389A8E6-5A17-4056-ABFD-C8B823F2092E' , @versionid='2D32AB4C-9929-4A85-A94E-7A01D8F40016'; EXECUTE #syscollector_upload_package @packagename='TSQLQueryCollect' , @packageid='292B1476-0F46-4490-A9B7-6DB724DE3C0B' , @versionid='E24C6D00-94C6-457B-BED4-1F9F018F3273'; EXECUTE #syscollector_upload_package @packagename='TSQLQueryUpload' , @packageid='6EB73801-39CF-489C-B682-497350C939F0' , @versionid='DA1210BC-C31B-43C6-B255-D8DDEB288CA1'; EXECUTE #syscollector_upload_package @packagename='PerfCountersCollect' , @packageid='C2EAABC1-5BF3-4127-BEB3-26E94D026E7D' , @versionid='09A5B959-21B3-44E1-A37F-4A62BE5D6244'; EXECUTE #syscollector_upload_package @packagename='PerfCountersUpload' , @packageid='08D854CB-0D45-4E96-92C6-227A5DCD7066' , @versionid='22A676DD-2025-493A-AD6B-C0186ABD556F'; EXECUTE #syscollector_upload_package @packagename='QueryActivityCollect' , @packageid='0B68FC9D-23DC-48F3-A937-90A0A8943D0E' , @versionid='75A3A143-2059-433B-A11C-C8E0C80A83CF'; EXECUTE #syscollector_upload_package @packagename='QueryActivityUpload' , @packageid='833DB628-8E19-47A3-92C5-FB1779B52E76' , @versionid='B1D79132-C6E6-46AA-8B14-E0AE4C4BA7BB'; GO -- Cleanup the temp stored proc that we used to upload the SSIS packages DROP PROCEDURE #syscollector_upload_package_from_file DROP PROCEDURE #syscollector_upload_package GO --------------------------------------------------------------- -- Out-of-the-box collector type objects - definition for types --------------------------------------------------------------- PRINT 'Creating or updating Collection Types...' GO -- Performance counters collector type DECLARE @collector_type_uid uniqueidentifier DECLARE @name sysname DECLARE @parameter_schema xml DECLARE @parameter_formatter xml DECLARE @collection_package_id uniqueidentifier DECLARE @upload_package_id uniqueidentifier SET @collector_type_uid = '294605dd-21de-40b2-b20f-f3e170ea1ec3' SET @name = 'Performance Counters Collector Type' SET @parameter_schema = ' ' 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 -- 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 /**************************************************************/ /* 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 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 RETURN dbo.fn_sysdac_get_username(SUSER_SID()); 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 /**********************************************************************/ /* 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_upgrade_instance', 'P') IS NULL) BEGIN RAISERROR('Dropping Stored Procedure sp_sysdac_upgrade_instance', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysdac_upgrade_instance; END; GO IF (NOT OBJECT_ID ('dbo.sp_sysdac_update_history_entry', 'P') IS NULL) BEGIN RAISERROR('Dropping Stored Procedure sp_sysdac_update_history_entry', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysdac_update_history_entry; END; GO IF (NOT OBJECT_ID ('dbo.sp_sysdac_delete_instance', 'P') IS NULL) BEGIN RAISERROR('Dropping Stored Procedure sp_sysdac_delete_instance', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysdac_delete_instance; END; GO IF (NOT OBJECT_ID ('dbo.sp_sysdac_rename_database', 'P') IS NULL) BEGIN RAISERROR('Dropping Stored Procedure sp_sysdac_rename_database', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysdac_rename_database; END; GO IF (NOT OBJECT_ID ('dbo.sp_sysdac_drop_database', 'P') IS NULL) BEGIN RAISERROR('Dropping Stored Procedure sp_sysdac_drop_database', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysdac_drop_database; END; GO IF (NOT OBJECT_ID ('dbo.sp_sysdac_drop_login', 'P') IS NULL) BEGIN RAISERROR('Dropping Stored Procedure sp_sysdac_drop_login', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysdac_drop_login; END; GO IF (NOT OBJECT_ID ('dbo.sp_sysdac_ensure_dac_creator', 'P') IS NULL) BEGIN RAISERROR('Dropping Stored Procedure sp_sysdac_ensure_dac_creator', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysdac_ensure_dac_creator; END; GO IF (NOT OBJECT_ID ('dbo.sp_sysdac_rollback_all_pending_objects', 'P') IS NULL) BEGIN RAISERROR('Dropping Stored Procedure sp_sysdac_rollback_all_pending_objects', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysdac_rollback_all_pending_objects; END; GO IF (NOT OBJECT_ID ('dbo.sp_sysdac_rollback_pending_object', 'P') IS NULL) BEGIN RAISERROR('Dropping Stored Procedure sp_sysdac_rollback_pending_object', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysdac_rollback_pending_object; END; GO IF (NOT OBJECT_ID ('dbo.sp_sysdac_setreadonly_database', 'P') IS NULL) BEGIN RAISERROR('Dropping Stored Procedure sp_sysdac_setreadonly_database', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysdac_setreadonly_database; END; GO IF (NOT OBJECT_ID ('dbo.sp_sysdac_resolve_pending_entry', 'P') IS NULL) BEGIN RAISERROR('Dropping Stored Procedure sp_sysdac_resolve_pending_entry', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysdac_resolve_pending_entry; END; GO IF (NOT OBJECT_ID ('dbo.sp_sysdac_rollback_committed_step', 'P') IS NULL) BEGIN RAISERROR('Dropping Stored Procedure sp_sysdac_rollback_committed_step', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysdac_rollback_committed_step; END; GO /* Procedure [sp_sysdac_ensure_dac_creator] This sproc checks the caller is a dac creator and raise's an error if not */ RAISERROR('Creating procedure [dbo].[sp_sysdac_ensure_dac_creator]...', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE dbo.sp_sysdac_ensure_dac_creator as BEGIN -- only users that can create a dac can add parts IF (dbo.fn_sysdac_is_dac_creator() != 1) BEGIN RAISERROR(36010, -1, -1); RETURN(1); -- failure END END go /* Procedure [sp_sysdac_add_instance] This proc creates a new DacInstance in dbo.sysdac_instances_internal table Parameters: @type_name - Name of the Dac type @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_add_history_entry] This proc creates a new row in dbo.sysdac_history_internal table Parameters: @sequence_id --step # of deployment/deletion process @instance_id --unique transaction ID = DAC package id @part_name --name of the dacpart corresponding to this part @action_type --type of the action being performed 0-deploy 1-detach 2-delete @dac_object_type --type of the object affected 0-dacpac,1-login,2-database @required -- indicates if the steps is needed(value of 1) to be committed for the success of the action @dac_object_name_pretran --name of the object before the current step completes @dac_object_name_posttran --name of the object after the current step completes @sqlscript --sql script for performing the associated object @payload --payload data associated with an object. Mostly used for Dacpac @comments --comments associated with the history @error_string --error while running associated sql @action_id --action identifier for each attempt of deploy/delete/detach/upgrade */ RAISERROR('Creating procedure [dbo].[sp_sysdac_add_history_entry]...', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysdac_add_history_entry] @sequence_id int, @instance_id UniqueIdentifier = NULL, @action_type tinyint = NULL, @action_status tinyint = NULL, @dac_object_type tinyint = NULL, @required bit = NULL, @dac_object_name_pretran sysname = N'', @dac_object_name_posttran sysname = N'', @sqlscript nvarchar(max) = N'', @payload varbinary(max) = NULL, @comments varchar(max) = N'', @error_string nvarchar(max) = N'', @action_id int = NULL OUTPUT AS SET NOCOUNT ON; BEGIN DECLARE @retval INT DECLARE @null_column sysname SET @null_column = NULL IF (@instance_id IS NULL) SET @null_column = '@instance_id' ELSE IF (@action_type IS NULL) SET @null_column = '@action_type' ELSE IF (@action_status IS NULL) SET @null_column = '@action_status' ELSE IF (@dac_object_type IS NULL) SET @null_column = '@dac_object_type' ELSE IF (@required IS NULL) SET @null_column = '@required' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysdac_add_history_entry') RETURN(1) END -- comments is optional. make sure it is non-null IF (@comments IS NULL) BEGIN SET @comments = N'' END -- INSTALL service: only users that can create a dac can add history entries IF (@action_type <= 4) BEGIN IF (dbo.fn_sysdac_is_dac_creator() != 1) BEGIN RAISERROR(36010, -1, -1); RETURN(1); -- failure END END -- UNINSTALL service : Ensure that the package being referred to exists by using the package view. We only continue if we can see -- the specified package. The package will only be visible if we are the associated dbo or sysadmin and it exists ELSE IF NOT EXISTS (SELECT * from dbo.sysdac_instances WHERE instance_id = @instance_id) BEGIN RAISERROR(36004, -1, -1) RETURN(1) END BEGIN TRAN --If the action_id value is not set by the user, this is a new entry and the proc --should calculate the next value which is one more than the current max IF (@action_id IS NULL) BEGIN SET @action_id = ( SELECT ISNULL(MAX(action_id) + 1, 0) FROM dbo.sysdac_history_internal WITH (UPDLOCK, HOLDLOCK)) END INSERT INTO [dbo].[sysdac_history_internal] (action_id, sequence_id, instance_id, action_type, dac_object_type, action_status, required, dac_object_name_pretran, dac_object_name_posttran, sqlscript, payload, comments, error_string) VALUES (@action_id, @sequence_id, @instance_id, @action_type, @dac_object_type, @action_status, @required, @dac_object_name_pretran, @dac_object_name_posttran, @sqlscript, @payload, @comments, @error_string) COMMIT SELECT @retval = @@error RETURN(@retval) END GO /* Procedure [sp_sysdac_delete_instance] This proc deletes DacInstance with the input instance_id in dbo.sysdac_instances_internal table Parameters: @instance_id - Guid identifier of a Dac Package */ RAISERROR('Creating procedure [dbo].[sp_sysdac_delete_instance]...', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysdac_delete_instance] @instance_id UniqueIdentifier AS SET NOCOUNT ON; BEGIN DECLARE @retval INT DECLARE @partId INT IF @instance_id IS NULL BEGIN RAISERROR(14043, -1, -1, 'instance_id', 'sp_sysdac_delete_instance') RETURN(1) END -- Ensure that the package being referred to exists by using the package view. We only continue if we can see -- the specified package. The package will only be visible if we are the associated dbo or sysadmin and it exists IF NOT EXISTS (SELECT * from dbo.sysdac_instances WHERE instance_id = @instance_id) BEGIN RAISERROR(36004, -1, -1) RETURN(1) END --Delete the entry of DacInstance DELETE FROM sysdac_instances_internal WHERE instance_id=@instance_id SELECT @retval = @@error RETURN(@retval) END GO /* Procedure [sp_sysdac_upgrade_instance] This proc upgrades existing DacInstance metadata with the new version DacInstance. Parameters: @source_instance_id - Instance identifier of the original Dac Instance @instance_id - identifer of the new Dac Instance that's installed as a temporary instance @instance_name - name of the new Dac Instance that's installed as a temporary instance @database_name - database name of the original DAC */ RAISERROR('Creating procedure [dbo].[sp_sysdac_upgrade_instance]...', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysdac_upgrade_instance] @source_instance_id UniqueIdentifier = NULL, @instance_id UniqueIdentifier = NULL, @instance_name sysname, @database_name sysname AS SET NOCOUNT ON; BEGIN DECLARE @retval INT DECLARE @null_column sysname SET @null_column = NULL IF (@source_instance_id IS NULL) SET @null_column = '@source_instance_id' ELSE IF (@instance_id IS NULL ) SET @null_column = '@instance_id' ELSE IF( @database_name IS NULL) SET @null_column = '@database_name' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysdac_upgrade_instance') RETURN(1) END -- Ensure that the package being referred to exists by using the package view. We only continue if we can see -- the specified package. The package will only be visible if we are the associated dbo or sysadmin and it exists IF NOT EXISTS (SELECT * from dbo.sysdac_instances WHERE instance_id = @instance_id) BEGIN RAISERROR(36004, -1, -1) RETURN(1) END --Ensure that the package being referred exists IF NOT EXISTS (SELECT * from dbo.sysdac_instances_internal WHERE instance_id = @instance_id) BEGIN RAISERROR(36004, -1, -1) RETURN(1) END BEGIN TRAN --Delete the source DacInstance first EXEC dbo.sp_sysdac_delete_instance @instance_id = @instance_id --Update the new version DacInstance metadata with the original DacInstance UPDATE [dbo].[sysdac_instances_internal] SET instance_id = @instance_id, instance_name = @instance_name WHERE instance_id = @source_instance_id COMMIT SELECT @retval = @@error RETURN(@retval) END GO /* Procedure [sp_sysdac_drop_database] This proc drops DAC databases Parameters @database_name : Name of the Database to be dropped */ RAISERROR('Creating procedure [dbo].[sp_sysdac_drop_database]...', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysdac_drop_database] @database_name sysname AS SET NOCOUNT ON; BEGIN IF EXISTS(SELECT name FROM sys.databases WHERE name = @database_name) BEGIN DECLARE @engineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int); DECLARE @quoteddbname nvarchar(258) SET @quoteddbname = QUOTENAME(@database_name) DECLARE @sqlstatement nvarchar(1000) IF (@engineEdition != 5) BEGIN SET @sqlstatement = 'ALTER DATABASE ' + @quoteddbname + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE' EXEC (@sqlstatement) END SET @sqlstatement = 'DROP DATABASE ' + @quoteddbname IF (@engineEdition = 5) BEGIN DECLARE @dbname SYSNAME SET @dbname = db_name() RAISERROR (36012, 0, 1, @dbname, @sqlstatement); SELECT @dbname as databasename, @sqlstatement as sqlscript END ELSE BEGIN EXEC (@sqlstatement) END END RETURN(@@error) END GO /* Procedure [sp_sysdac_rename_database] This proc renames DAC databases Parameters @@database_name : original name of the database @new_name : new name of the database */ RAISERROR('Creating procedure [dbo].[sp_sysdac_rename_database]...', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysdac_rename_database] @database_name sysname, @new_name sysname AS SET NOCOUNT ON; BEGIN DECLARE @sqlstatement nvarchar(1000) -- Alter the database to single user mode DECLARE @quoted_database_name nvarchar(258) SET @quoted_database_name = QUOTENAME(@database_name) SET @sqlstatement = 'ALTER DATABASE ' + @quoted_database_name + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE' EXEC (@sqlstatement) -- Rename the database EXEC sp_rename @objname=@quoted_database_name, @newname=@new_name, @objtype='DATABASE' -- Revert the database back to multi user mode DECLARE @quoted_new_name nvarchar(258) SET @quoted_new_name = QUOTENAME(@new_name) SET @sqlstatement = 'ALTER DATABASE ' + @quoted_new_name + ' SET MULTI_USER WITH ROLLBACK IMMEDIATE' EXEC (@sqlstatement) RETURN(@@error) END GO /* Procedure [sp_sysdac_setreadonly_database] This proc renames DAC databases Parameters @database_name : original name of the database @readonly : (0) to set readonly, (1) to set readwrite. Default value is 0 */ RAISERROR('Creating procedure [dbo].[sp_sysdac_setreadonly_database]...', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysdac_setreadonly_database] @database_name sysname, @readonly bit = 0 AS SET NOCOUNT ON; BEGIN DECLARE @sqlstatement nvarchar(1000) DECLARE @quoted_database_name nvarchar(258) SET @quoted_database_name = QUOTENAME(@database_name) IF (@readonly = 0) SET @sqlstatement = 'ALTER DATABASE ' + @quoted_database_name + ' SET READ_ONLY WITH ROLLBACK IMMEDIATE' ELSE IF (@readonly = 1) SET @sqlstatement = 'ALTER DATABASE ' + @quoted_database_name + ' SET READ_WRITE WITH ROLLBACK IMMEDIATE' EXEC (@sqlstatement) RETURN(@@error) END GO /* Procedure [sp_sysdac_update_history_entry] This proc update entry in dbo.sysdac_history_internal table Parameters: @action_id --action identifier for each attempt of deploy/delete/detach/upgrade @instance_id --unique transaction ID = DAC package id @action_type --type of the action being performed 0-deploy 1-detach 2-delete @dac_object_type --type of the object affected 0-dacpac,1-login,2-database @action_status --pending/success/fail @dac_object_name_pretran --name of the object before the current step completes @dac_object_name_posttran --name of the object after the current step completes @sqlscript --sql script for performing the associated object @error_string --error while running associated sql */ RAISERROR('Creating procedure [dbo].[sp_sysdac_update_history_entry]...', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysdac_update_history_entry] @action_id int, @instance_id UniqueIdentifier = NULL, @action_type tinyint = NULL, @dac_object_type tinyint = NULL, @action_status tinyint = NULL, @dac_object_name_pretran sysname = N'', @dac_object_name_posttran sysname = N'', @sqlscript nvarchar(max) = N'', @error_string nvarchar(max) = N'' AS SET NOCOUNT ON; BEGIN DECLARE @retval INT DECLARE @null_column sysname SET @null_column = NULL IF (@instance_id IS NULL) SET @null_column = '@instance_id' ELSE IF (@action_type IS NULL) SET @null_column = '@action_type' ELSE IF (@dac_object_type IS NULL) SET @null_column = '@dac_object_type' ELSE IF (@action_status IS NULL) --action_status should be non-pending (success/failure) SET @null_column = '@action_status' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysdac_update_history_entry') RETURN(1) END -- Only allow users who created history entry or 'sysadmins' to update the row DECLARE @username SYSNAME SET @username = (SELECT created_by FROM dbo.sysdac_history_internal WHERE instance_id = @instance_id AND action_id = @action_id AND action_type = @action_type AND dac_object_type = @dac_object_type AND dac_object_name_pretran = @dac_object_name_pretran AND dac_object_name_posttran = @dac_object_name_posttran) IF ((@username != [dbo].[fn_sysdac_get_currentusername]()) AND ([dbo].[fn_sysdac_is_currentuser_sa]() != 1)) BEGIN RAISERROR(36011, -1, -1); RETURN(1); -- failure END UPDATE [dbo].[sysdac_history_internal] SET action_status = @action_status, sqlscript = @sqlscript, error_string = @error_string, date_modified = (SELECT GETDATE()) WHERE action_id = @action_id AND action_type = @action_type AND dac_object_type = @dac_object_type AND dac_object_name_pretran = @dac_object_name_pretran AND dac_object_name_posttran = @dac_object_name_posttran SELECT @retval = @@error RETURN(@retval) END GO /* Procedure [sp_sysdac_resolve_pending_entry] This proc resolves a pending entry to either completed or failed Parameters: @action_id --action identifier for each attempt of deploy/delete/detach/upgrade @sequence_id --step # of deployment/deletion process */ RAISERROR('Creating procedure [dbo].[sp_sysdac_resolve_pending_entry]...', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysdac_resolve_pending_entry] @action_id INT, @sequence_id INT AS SET NOCOUNT ON; BEGIN DECLARE @null_column sysname SET @null_column = NULL IF (@action_id IS NULL) SET @null_column = '@action_id' ELSE IF (@sequence_id IS NULL) SET @null_column = '@sequence_id' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysdac_resolve_pending_entry') RETURN(1) END DECLARE @instance_id UNIQUEIDENTIFIER DECLARE @action_type TINYINT DECLARE @dac_object_type TINYINT DECLARE @action_status TINYINT DECLARE @dac_object_name_pretran SYSNAME DECLARE @dac_object_name_posttran SYSNAME SELECT @instance_id = instance_id, @action_type = action_type, @dac_object_type = dac_object_type, @dac_object_name_pretran = dac_object_name_pretran, @dac_object_name_posttran = dac_object_name_posttran FROM sysdac_history_internal WHERE action_id = @action_id AND sequence_id = @sequence_id --Below are the constants set based on history table DECLARE @create TINYINT DECLARE @rename TINYINT DECLARE @database TINYINT DECLARE @success TINYINT DECLARE @rollback TINYINT DECLARE @fail TINYINT DECLARE @register TINYINT DECLARE @unregister TINYINT DECLARE @upgrade TINYINT DECLARE @readonly TINYINT DECLARE @readwrite TINYINT DECLARE @disconnectusers TINYINT DECLARE @readonlymode INT SET @create = 1 SET @rename = 2 SET @database = 2 SET @success = 2 SET @rollback = 4 SET @fail = 3 SET @register = 3 SET @unregister = 14 SET @upgrade = 15 SET @readonly = 12 SET @readwrite = 16 SET @disconnectusers = 17 SET @readonlymode = 1024 SET @action_status = @fail --initialize result of the action to failure and adjust if below cases succeed! IF @action_type = @create AND @dac_object_type = @database --database create BEGIN IF EXISTS(SELECT 1 FROM sys.sysdatabases WHERE name = @dac_object_name_pretran) SET @action_status = @success END ELSE IF @action_type = @rename AND @dac_object_type = @database --database rename BEGIN IF (EXISTS(SELECT 1 FROM sys.sysdatabases WHERE name = @dac_object_name_posttran)) AND (NOT EXISTS(SELECT 1 FROM sys.sysdatabases WHERE name = @dac_object_name_pretran)) SET @action_status = @success END ELSE IF @action_type = @register --register DAC BEGIN IF (EXISTS(SELECT 1 FROM dbo.sysdac_instances_internal WHERE instance_name = @dac_object_name_pretran)) SET @action_status = @success END ELSE IF @action_type = @unregister --unregister DAC BEGIN IF (NOT EXISTS(SELECT 1 FROM dbo.sysdac_instances_internal WHERE instance_name = @dac_object_name_pretran)) SET @action_status = @success END ELSE IF @action_type = @upgrade --upgrade DAC BEGIN IF (EXISTS(SELECT 1 FROM dbo.sysdac_instances_internal WHERE instance_name = @dac_object_name_posttran)) AND (NOT EXISTS(SELECT 1 FROM dbo.sysdac_instances_internal WHERE instance_name = @dac_object_name_pretran)) SET @action_status = @success END ELSE IF @action_type = @readonly OR @action_type = @disconnectusers -- readonly/disconnect users state BEGIN IF (EXISTS(SELECT 1 FROM sys.sysdatabases WHERE ((status & @readonlymode) = @readonlymode) AND name=@dac_object_name_pretran)) SET @action_status = @success END ELSE IF @action_type = @readwrite -- readwrite state BEGIN IF (EXISTS(SELECT 1 FROM sys.sysdatabases WHERE ((status & @readonlymode) != @readonlymode) AND name=@dac_object_name_pretran)) SET @action_status = @success END UPDATE sysdac_history_internal SET action_status = @action_status WHERE action_id = @action_id AND sequence_id = @sequence_id END GO /* Procedure [sp_sysdac_rollback_committed_step] This proc rollsback a given committed step. Parameters: @action_id --action identifier for each attempt of deploy/delete/detach/upgrade @sequence_id --step # of deployment/deletion process */ RAISERROR('Creating procedure [dbo].[sp_sysdac_rollback_committed_step]...', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysdac_rollback_committed_step] @action_id INT, @sequence_id INT AS SET NOCOUNT ON; BEGIN DECLARE @retval INT DECLARE @null_column sysname SET @null_column = NULL IF (@action_id IS NULL) SET @null_column = '@action_id' ELSE IF (@sequence_id IS NULL) SET @null_column = '@sequence_id' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysdac_rollback_committed_step') RETURN(1) END DECLARE @instance_id UNIQUEIDENTIFIER DECLARE @part_name NVARCHAR(128) DECLARE @action_type TINYINT DECLARE @dac_object_type TINYINT DECLARE @action_status TINYINT DECLARE @dac_object_name_pretran SYSNAME DECLARE @dac_object_name_posttran SYSNAME DECLARE @sqlstatement NVARCHAR(1000) SELECT @instance_id = instance_id, @action_id = action_id, @action_type = action_type, @sequence_id = sequence_id, @dac_object_type = dac_object_type, @action_status = action_status, @dac_object_name_pretran = dac_object_name_pretran, @dac_object_name_posttran = dac_object_name_posttran FROM sysdac_history_internal WHERE action_id = @action_id AND sequence_id = @sequence_id --Below are the constants set based on history table DECLARE @create TINYINT DECLARE @rename TINYINT DECLARE @register TINYINT DECLARE @database TINYINT DECLARE @rollback TINYINT DECLARE @rollback_pending TINYINT DECLARE @rollback_success TINYINT DECLARE @setreadonly TINYINT DECLARE @setreadwrite TINYINT SET @create = 1 SET @rename = 2 SET @register = 3 SET @database = 2 SET @rollback = 4 SET @rollback_pending = 0 SET @rollback_success = 1 SET @setreadonly = 12 SET @setreadwrite = 16 IF @action_type = @create AND @dac_object_type = @database --database create BEGIN RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_pending, NULL) WITH NOWAIT EXEC dbo.sp_sysdac_drop_database @database_name = @dac_object_name_pretran RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_success, NULL) WITH NOWAIT END ELSE IF @action_type = @rename AND @dac_object_type = @database --database rename BEGIN RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_pending, NULL) WITH NOWAIT EXEC dbo.sp_sysdac_rename_database @dac_object_name_posttran, @dac_object_name_pretran RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_success, NULL) WITH NOWAIT END ELSE IF @action_type = @register --register DAC BEGIN SET @instance_id = ( SELECT instance_id FROM dbo.sysdac_instances_internal WHERE instance_name = @dac_object_name_pretran) RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_pending, NULL) WITH NOWAIT EXEC dbo.sp_sysdac_delete_instance @instance_id = @instance_id RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_success, NULL) WITH NOWAIT END ELSE IF @action_type = @setreadonly --readonly step BEGIN RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_pending, NULL) WITH NOWAIT EXEC dbo.sp_sysdac_setreadonly_database @database_name = @dac_object_name_pretran, @readonly = 1 RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_success, NULL) WITH NOWAIT END ELSE IF @action_type = @setreadwrite --readonly step BEGIN RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_pending, NULL) WITH NOWAIT EXEC dbo.sp_sysdac_setreadonly_database @database_name = @dac_object_name_pretran, @readonly = 0 RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_success, NULL) WITH NOWAIT END --mark the entry as rolledback UPDATE sysdac_history_internal SET action_status = @rollback WHERE action_id = @action_id AND sequence_id = @sequence_id SELECT @retval = @@error RETURN(@retval) END GO /* Procedure [sp_sysdac_rollback_pending_object] This proc rollsback the pending object of a given action id. Parameters: @action_id --action identifier for each attempt of deploy/delete/detach/upgrade */ RAISERROR('Creating procedure [dbo].[sp_sysdac_rollback_pending_object]...', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysdac_rollback_pending_object] @action_id INT AS SET NOCOUNT ON; BEGIN IF (@action_id IS NULL) BEGIN RAISERROR(14043, -1, -1, '@action_id', 'sp_sysdac_rollback_pending_object') RETURN(1) END DECLARE @sequence_id INT DECLARE @action_status TINYINT --Below are the constants set based on history table DECLARE @header_id bit DECLARE @pending TINYINT DECLARE @success TINYINT DECLARE @true bit DECLARE @rollback TINYINT DECLARE @fail TINYINT DECLARE @rollback_failure TINYINT SET @header_id = 0 SET @pending = 1 SET @success = 2 SET @true = 1 SET @rollback = 4 SET @fail = 3 SET @rollback_failure = 2 --if step 0 is not pending, exit IF ((SELECT action_status FROM sysdac_history_internal WHERE action_id = @action_id AND sequence_id = @header_id) != @pending) RETURN; --STEP 1. Resolve pending entry SET @sequence_id = (SELECT TOP 1 sequence_id FROM sysdac_history_internal WHERE sequence_id != @header_id AND action_id = @action_id AND action_status = @pending) IF (@sequence_id IS NOT NULL) EXEC dbo.sp_sysdac_resolve_pending_entry @action_id = @action_id, @sequence_id = @sequence_id --check if all required steps are committed(success). If so, mark the action success and return! IF NOT EXISTS (SELECT 1 FROM sysdac_history_internal WHERE action_id = @action_id AND sequence_id != @header_id AND required = @true AND action_status != @success) BEGIN UPDATE dbo.sysdac_history_internal SET action_status = @success WHERE action_id = @action_id AND sequence_id = @header_id RETURN END BEGIN TRY --STEP 2. rollback commit entries WHILE EXISTS( SELECT 1 FROM sysdac_history_internal WHERE action_status = @success AND action_id = @action_id AND sequence_id > 0) BEGIN SELECT TOP 1 @sequence_id = sequence_id, @action_status = action_status FROM sysdac_history_internal WHERE action_status = @success AND action_id = @action_id AND sequence_id != @header_id ORDER BY sequence_id DESC EXEC dbo.sp_sysdac_rollback_committed_step @action_id = @action_id, @sequence_id = @sequence_id END --Mark the header entry as rolledback SET @action_status = @rollback END TRY BEGIN CATCH DECLARE @error_message NVARCHAR(4000); SELECT @error_message = ERROR_MESSAGE() RAISERROR(N'%d, %d, %s', -1, 1, @sequence_id, @rollback_failure, @error_message) WITH NOWAIT --Mark the header entry as failed SET @action_status = @fail END CATCH --STEP 3. Mark the header entry with final action status UPDATE dbo.sysdac_history_internal SET action_status = @action_status WHERE action_id = @action_id AND sequence_id = @header_id END GO /* Procedure [sp_sysdac_rollback_all_pending_objects] This proc rollsback all the pending actions. Parameters: @return_scripts -- if set to 1, stored procedure will return as a resultset a set of sql scripts needed to be additionally executed to complete the rollback. Rollback procedure might not be able to execute them due to sql server limitations. */ RAISERROR('Creating procedure [dbo].[sp_sysdac_rollback_all_pending_objects]...', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysdac_rollback_all_pending_objects] (@return_scripts TINYINT = 0) AS SET NOCOUNT ON; BEGIN DECLARE @action_id INT DECLARE @sequence_id INT --Below are the constants set based on history table DECLARE @header_id bit DECLARE @pending TINYINT SET @header_id = 0 SET @pending = 1 CREATE TABLE #additional_scripts(databasename sysname, sqlscript VARCHAR(MAX)) WHILE EXISTS (SELECT 1 FROM sysdac_history_internal WHERE sequence_id = @header_id AND action_status = @pending) BEGIN SET @action_id = (SELECT TOP 1 action_id FROM sysdac_history_internal WHERE sequence_id = @header_id AND action_status = @pending) INSERT INTO #additional_scripts EXEC dbo.sp_sysdac_rollback_pending_object @action_id = @action_id END IF (@return_scripts = 1) BEGIN SELECT databasename, sqlscript FROM #additional_scripts END END GO ----------------------------------------------------------- -- Security for Dac objects ----------------------------------------------------------- -- everybody can see the dacs view. These are filtered views so no info disclosure by it being granted to public GRANT SELECT ON dbo.sysdac_instances to PUBLIC -- everybody can see if they are a dac creator or not GRANT EXECUTE ON dbo.fn_sysdac_is_dac_creator to PUBLIC GRANT EXECUTE ON dbo.fn_sysdac_is_login_creator to PUBLIC GRANT EXECUTE ON dbo.fn_sysdac_is_currentuser_sa to PUBLIC GRANT EXECUTE ON dbo.sp_sysdac_ensure_dac_creator to PUBLIC -- we allow anybody to execute the sprocs, they are guarded by the is_dac_creator function GRANT EXECUTE ON dbo.sp_sysdac_rename_database to PUBLIC GRANT EXECUTE ON dbo.sp_sysdac_drop_database to PUBLIC GRANT EXECUTE ON dbo.sp_sysdac_upgrade_instance to PUBLIC GRANT EXECUTE ON dbo.sp_sysdac_delete_instance to PUBLIC GRANT EXECUTE ON dbo.sp_sysdac_add_instance to PUBLIC GRANT EXECUTE ON dbo.sp_sysdac_add_history_entry to PUBLIC GRANT EXECUTE ON dbo.sp_sysdac_update_history_entry to PUBLIC --End DAC Script RAISERROR('', 0, 1) WITH NOWAIT; RAISERROR('------------------------------------', 0, 1) WITH NOWAIT; RAISERROR('Execution of InstDac.SQL complete', 0, 1) WITH NOWAIT; RAISERROR('------------------------------------', 0, 1) WITH NOWAIT; GO /**************************************************************/ /* END DAC SCRIPTS */ /**************************************************************/ --------------------------------------------------------------- -- Out-of-the-box system Collection Sets --------------------------------------------------------------- PRINT 'Creating system Collection Sets...' ------------------------------------------------ -- System collection set: Disk Usage ------------------------------------------------ DECLARE @collection_set_name NVARCHAR(128); DECLARE @description NVARCHAR(4000); DECLARE @collection_set_id int; DECLARE @collection_set_uid uniqueidentifier; DECLARE @collection_mode smallint; DECLARE @schedule_name sysname; DECLARE @days_until_expiration smallint; DECLARE @name_id int; DECLARE @description_id int; -- The GUID below identifies this collection set and is used to locate the data collected by this collection set. -- The name and description retrived via FORMATMESSAGE will not be localized for the initial mkmastr build of msdb, -- but that is OK; the public interface to collection set detils (syscollector_collection_sets) is a view that will -- dynamically pull the correct localized strings via its own FORMATMESSAGE calls. We just need a unique string to -- store in the table. SET @name_id = 14701; SET @description_id = 14700; SET @collection_set_uid = N'7B191952-8ECF-4E12-AEB2-EF646EF79FEF'; SET @collection_mode = 1; -- Non-cached SET @schedule_name = N'CollectorSchedule_Every_6h'; SET @days_until_expiration = 730; SET @description = FORMATMESSAGE(@description_id); SET @collection_set_name = ISNULL (FORMATMESSAGE(@name_id), 'Disk Usage'); IF EXISTS (SELECT * FROM dbo.syscollector_collection_sets_internal WHERE collection_set_uid = @collection_set_uid) BEGIN RAISERROR ('Updating system Collection Set "%s"...', 0, 1, @collection_set_name) WITH NOWAIT; -- We are updating an existing collection set -- get its ID SELECT @collection_set_id = collection_set_id FROM syscollector_collection_sets WHERE collection_set_uid = @collection_set_uid; -- Temporarily clear the is_system flag so that we can modify the collection set definition UPDATE syscollector_collection_sets SET is_system = 0 WHERE collection_set_id = @collection_set_id -- Don't override the current expiration period or schedule settings, since the user may have customized these EXEC dbo.sp_syscollector_update_collection_set @collection_set_id = @collection_set_id, @new_name = @collection_set_name, @schedule_name = @schedule_name, -- avoid mismatch in uid if schedule was deleted and recreated @collection_mode = @collection_mode, @logging_level = 0, @description = @description; END ELSE BEGIN RAISERROR ('Creating system Collection Set "%s"...', 0, 1, @collection_set_name) WITH NOWAIT; EXEC dbo.sp_syscollector_create_collection_set @collection_set_uid = @collection_set_uid, @name = @collection_set_name, @schedule_name = @schedule_name, @collection_mode = @collection_mode, @days_until_expiration = @days_until_expiration, @description = @description, @logging_level = 0, @collection_set_id = @collection_set_id OUTPUT; END -- for localization of collection set name and description UPDATE syscollector_collection_sets_internal SET name_id = @name_id, description_id = @description_id WHERE collection_set_uid = @collection_set_uid; -- Add collection items DECLARE @collection_item_name NVARCHAR(128); DECLARE @collection_item_old_name NVARCHAR(128); DECLARE @collection_item_id int; DECLARE @frequency int; -- Item 1: disk_usage DMV query DECLARE @parameters xml; SELECT @parameters = convert(xml, N' 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, si.bpool_commit_target AS bpool_commit_target, si.bpool_committed AS bpool_committed, si.bpool_visible AS bpool_visible FROM sys.dm_os_process_memory AS pm CROSS JOIN sys.dm_os_sys_memory AS sm -- single-row DMV CROSS JOIN sys.dm_os_sys_info AS si; -- single-row DMV 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, single_pages_kb, 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, SUM(single_pages_kb) as single_pages_kb, SUM(multi_pages_kb) as multi_pages_kb, SUM(virtual_memory_reserved_kb) as virtual_memory_reserved_kb, SUM(virtual_memory_committed_kb) as virtual_memory_committed_kb, SUM(awe_allocated_kb) as awe_allocated_kb, SUM(shared_memory_reserved_kb) as shared_memory_reserved_kb, SUM(shared_memory_committed_kb) as shared_memory_committed_kb FROM sys.dm_os_memory_clerks GROUP BY type, memory_node_id 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 ('BackupDevice', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('BrokerPriority', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('BrokerService', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Certificate', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Computer', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Credential', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('CryptographicProvider', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Database', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('DatabaseAuditSpecification', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('DatabaseDdlTrigger', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('DatabaseRole', 7); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('DataFile', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Default', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('DeployedDac', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Endpoint', 7); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Utility', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('FileGroup', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('FullTextCatalog', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('FullTextIndex', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('FullTextStopList', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IDatabaseMaintenanceFacet', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IDatabaseOptions', 6); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IDatabasePerformanceFacet', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IDatabaseSecurityFacet', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ILoginOptions', 7); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IMultipartNameFacet', 7); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('INameFacet', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ITableOptions', 7); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IUserOptions', 7); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IViewOptions', 7); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Index', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerAuditFacet', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerConfigurationFacet', 6); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerInformation', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerPerformanceFacet', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerProtocolSettingsFacet', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerSecurityFacet', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerSetupFacet', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerSelectionFacet', 0); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IServerSettings', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ISurfaceAreaConfigurationForAnalysisServer', 0); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ISurfaceAreaConfigurationForReportingServices', 0); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ISurfaceAreaFacet', 6); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('LinkedServer', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('LogFile', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Login', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('MessageType', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('PartitionFunction', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('PartitionScheme', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Processor', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('PlanGuide', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('RemoteServiceBinding', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ResourceGovernor', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ResourcePool', 7); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Rule', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Schema', 7); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Server', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ServerAuditSpecification', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ServerDdlTrigger', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ServiceContract', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ServiceQueue', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ServiceRoute', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Statistic', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('StoredProcedure', 7); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('SymmetricKey', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Synonym', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Table', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Trigger', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('User', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('UserDefinedAggregate', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('UserDefinedDataType', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('UserDefinedFunction', 7); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('UserDefinedTableType', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('UserDefinedType', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('View', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('Volume', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('WorkloadGroup', 7); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('XmlSchemaCollection', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('IDataFilePerformanceFacet', 4); INSERT @temp_syspolicy_management_facets (name, execution_mode) VALUES ('ILogFilePerformanceFacet', 4); -- Facets can be updated and inserted, however deleting a facet is dangerous because of references from conditions -- Update the modes on the facets UPDATE facets SET facets.execution_mode = tempFacets.execution_mode FROM @temp_syspolicy_management_facets tempFacets, [msdb].[dbo].[syspolicy_management_facets] facets WHERE tempFacets.name = facets.name AND tempFacets.execution_mode <> facets.execution_mode; -- Now populate the syspolicy_management_facets table with the facets that are missing in the table INSERT [msdb].[dbo].[syspolicy_management_facets] (name, execution_mode) SELECT tempFacets.name, tempFacets.execution_mode FROM @temp_syspolicy_management_facets tempFacets WHERE tempFacets.name NOT IN (SELECT distinct facets.name FROM [msdb].[dbo].[syspolicy_management_facets] facets) GO --------------------------------------------------------------- -- Relational storage for policy objects --------------------------------------------------------------- IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_facet_events') BEGIN PRINT 'Creating table [dbo].[syspolicy_facet_events]...'; CREATE TABLE [dbo].[syspolicy_facet_events] ( management_facet_id int NOT NULL REFERENCES [dbo].[syspolicy_management_facets], event_name sysname NOT NULL, -- this is the name of the event target_type sysname NOT NULL, -- type of the target (TABLE, PROCEDURE etc.) target_type_alias sysname NOT NULL); -- this is an alias for the type of the target -- it can happen that the same target type -- shows up as different strings in the event CREATE CLUSTERED INDEX [IX_facet_events_target_type_alias] ON [dbo].[syspolicy_facet_events] (target_type_alias); CREATE UNIQUE INDEX [UX_facet_events] ON [dbo].[syspolicy_facet_events] (management_facet_id, event_name, target_type, target_type_alias); END GO -- The facet events are unique to an installation, thus delete what is there and then insert the new events -- This technique will work for both a new install and an upgrade. DELETE FROM [msdb].[dbo].[syspolicy_facet_events] -- this is a pseudo event, so we're inserting it before the -- consistency check is in place INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'SAC_ENDPOINT_CHANGE',N'SERVER',N'SERVER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ISurfaceAreaFacet' GO CREATE TRIGGER [syspolicy_validate_events] on [dbo].[syspolicy_facet_events] AFTER INSERT, UPDATE AS BEGIN -- make sure that caller is dbo and all events inserted are real events. IF (USER_ID() != 1) OR EXISTS (SELECT event_name FROM inserted WHERE event_name NOT IN(SELECT type_name from sys.event_notification_event_types)) BEGIN RAISERROR(N'Unknown event name inserted into [dbo].[syspolicy_facet_events]', 1,1) ROLLBACK TRANSACTION END END GO -- Insert the facet events INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_APPLICATION_ROLE',N'APPLICATION ROLE',N'APPLICATION ROLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ApplicationRole' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_APPLICATION_ROLE',N'APPLICATION ROLE',N'APPLICATION ROLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ApplicationRole' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_ASYMMETRIC_KEY',N'ASYMMETRICKEY',N'ASYMMETRIC KEY' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'AsymmetricKey' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_ASYMMETRIC_KEY',N'ASYMMETRICKEY',N'ASYMMETRIC KEY' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'AsymmetricKey' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'ASYMMETRICKEY',N'ASYMMETRIC KEY' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'AsymmetricKey' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_ROLE',N'ROLE',N'ROLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'DatabaseRole' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_ROLE',N'ROLE',N'ROLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'DatabaseRole' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'ROLE',N'ROLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'DatabaseRole' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_ENDPOINT',N'ENDPOINT',N'ENDPOINT' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'Endpoint' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_ENDPOINT',N'ENDPOINT',N'ENDPOINT' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'Endpoint' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_AUTHORIZATION_SERVER',N'ENDPOINT',N'ENDPOINT' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'Endpoint' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_DATABASE',N'DATABASE',N'DATABASE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IDatabaseOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_DATABASE',N'DATABASE',N'DATABASE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IDatabaseOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'DATABASE',N'DATABASE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IDatabaseOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_FUNCTION',N'FUNCTION',N'FUNCTION' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_FUNCTION',N'FUNCTION',N'FUNCTION' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'RENAME',N'FUNCTION',N'FUNCTION' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_PROCEDURE',N'PROCEDURE',N'PROCEDURE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_PROCEDURE',N'PROCEDURE',N'PROCEDURE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'RENAME',N'PROCEDURE',N'PROCEDURE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_SYNONYM',N'SYNONYM',N'SYNONYM' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_TABLE',N'TABLE',N'TABLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_TABLE',N'TABLE',N'TABLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'RENAME',N'TABLE',N'TABLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_TYPE',N'TYPE',N'TYPE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'RENAME',N'TYPE',N'TYPE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_VIEW',N'VIEW',N'VIEW' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_VIEW',N'VIEW',N'VIEW' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'RENAME',N'VIEW',N'VIEW' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_XML_SCHEMA_COLLECTION',N'XMLSCHEMACOLLECTION',N'XMLSCHEMACOLLECTION' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_XML_SCHEMA_COLLECTION',N'XMLSCHEMACOLLECTION',N'XMLSCHEMACOLLECTION' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'RENAME',N'XMLSCHEMACOLLECTION',N'XMLSCHEMACOLLECTION' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SCHEMA',N'TABLE',N'TABLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SCHEMA',N'VIEW',N'VIEW' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SCHEMA',N'FUNCTION',N'FUNCTION' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SCHEMA',N'PROCEDURE',N'PROCEDURE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SCHEMA',N'SYNONYM',N'SYNONYM' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SCHEMA',N'TYPE',N'TYPE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SCHEMA',N'XMLSCHEMACOLLECTION',N'XMLSCHEMACOLLECTION' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IMultipartNameFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'AUDIT_SERVER_OPERATION_EVENT',N'SERVER',N'SERVER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IServerConfigurationFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'AUDIT_SERVER_OPERATION_EVENT',N'SERVER',N'SERVER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ISurfaceAreaFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_ENDPOINT',N'SERVER',N'SERVER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ISurfaceAreaFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_ENDPOINT',N'SERVER',N'SERVER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ISurfaceAreaFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'DROP_ENDPOINT',N'SERVER',N'SERVER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ISurfaceAreaFacet' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_LOGIN',N'LOGIN',N'LOGIN' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ILoginOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_LOGIN',N'LOGIN',N'LOGIN' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ILoginOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_RESOURCE_POOL',N'RESOURCEPOOL',N'RESOURCE POOL' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ResourcePool' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_RESOURCE_POOL',N'RESOURCEPOOL',N'RESOURCE POOL' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ResourcePool' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SCHEMA',N'SCHEMA',N'SCHEMA' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'Schema' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_SCHEMA',N'SCHEMA',N'SCHEMA' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'Schema' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'SCHEMA',N'SCHEMA' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'Schema' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_PROCEDURE',N'PROCEDURE',N'PROCEDURE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'StoredProcedure' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_PROCEDURE',N'PROCEDURE',N'PROCEDURE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'StoredProcedure' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'RENAME',N'PROCEDURE',N'PROCEDURE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'StoredProcedure' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'PROCEDURE',N'PROCEDURE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'StoredProcedure' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SCHEMA',N'PROCEDURE',N'PROCEDURE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'StoredProcedure' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_TABLE',N'TABLE',N'TABLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ITableOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_TABLE',N'TABLE',N'TABLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ITableOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'RENAME',N'TABLE',N'TABLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ITableOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'TABLE',N'TABLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ITableOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SCHEMA',N'TABLE',N'TABLE' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'ITableOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_USER',N'USER',N'ASYMMETRIC KEY USER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IUserOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_USER',N'USER',N'CERTIFICATE USER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IUserOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_USER',N'USER',N'GROUP USER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IUserOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_USER',N'USER',N'SQL USER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IUserOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_USER',N'USER',N'WINDOWS USER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IUserOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_USER',N'USER',N'ASYMMETRIC KEY USER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IUserOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_USER',N'USER',N'CERTIFICATE USER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IUserOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_USER',N'USER',N'GROUP USER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IUserOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_USER',N'USER',N'SQL USER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IUserOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_USER',N'USER',N'WINDOWS USER' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IUserOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_FUNCTION',N'FUNCTION',N'FUNCTION' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'UserDefinedFunction' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_FUNCTION',N'FUNCTION',N'FUNCTION' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'UserDefinedFunction' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'RENAME',N'FUNCTION',N'FUNCTION' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'UserDefinedFunction' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'FUNCTION',N'FUNCTION' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'UserDefinedFunction' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SCHEMA',N'FUNCTION',N'FUNCTION' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'UserDefinedFunction' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_VIEW',N'VIEW',N'VIEW' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IViewOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_VIEW',N'VIEW',N'VIEW' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IViewOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'RENAME',N'VIEW',N'VIEW' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IViewOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_AUTHORIZATION_DATABASE',N'VIEW',N'VIEW' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IViewOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_SCHEMA',N'VIEW',N'VIEW' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'IViewOptions' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'CREATE_WORKLOAD_GROUP',N'WORKLOADGROUP',N'WORKLOAD GROUP' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'WorkloadGroup' INSERT [msdb].[dbo].[syspolicy_facet_events] (management_facet_id,event_name,target_type,target_type_alias) SELECT management_facet_id,N'ALTER_WORKLOAD_GROUP',N'WORKLOADGROUP',N'WORKLOAD GROUP' FROM [msdb].[dbo].[syspolicy_management_facets] WHERE name = 'WorkloadGroup' GO --------------------------------------------------------------- -- Condition object --------------------------------------------------------------- IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_conditions_internal') BEGIN PRINT 'Creating table [dbo].[syspolicy_conditions_internal]...' CREATE TABLE [dbo].[syspolicy_conditions_internal] ( condition_id int IDENTITY(1,1), name sysname NOT NULL, date_created datetime default GETDATE(), description nvarchar(max) NOT NULL default (''), created_by sysname NOT NULL default SUSER_SNAME(), modified_by sysname NULL, date_modified datetime NULL, facet_id int, expression nvarchar(max), is_name_condition smallint default (0), obj_name sysname NULL, is_system bit NOT NULL default (0), CONSTRAINT [PK_syspolicy_conditions] PRIMARY KEY CLUSTERED (condition_id ASC), CONSTRAINT [UQ_syspolicy_conditions_name] UNIQUE(name) ); ALTER TABLE [dbo].[syspolicy_conditions_internal] ADD CONSTRAINT [FK_syspolicy_conditions_internal_facet] FOREIGN KEY(facet_id) REFERENCES [dbo].[syspolicy_management_facets] (management_facet_id); END GO PRINT 'Creating view [dbo].[syspolicy_conditions]...' GO CREATE VIEW [dbo].[syspolicy_conditions] AS SELECT c.condition_id, c.name, c.date_created, c.description, c.created_by, c.modified_by, c.date_modified, c.is_name_condition, mf.name AS facet, c.expression, c.obj_name, c.is_system FROM [dbo].[syspolicy_conditions_internal] c LEFT OUTER JOIN [dbo].[syspolicy_management_facets] mf ON c.facet_id = mf.management_facet_id GO CREATE PROCEDURE [dbo].[sp_syspolicy_check_membership] @role sysname, @raiserror bit = 1 AS BEGIN -- make sure that the caller is dbo or @role IF ( IS_MEMBER(@role) != 1 AND USER_ID() != 1) BEGIN IF (@raiserror = 1) BEGIN RAISERROR(15003, -1, -1, @role); END RETURN 15003; END RETURN 0; END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_add_condition]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_add_condition] @name sysname, @description nvarchar(max) = N'', @facet nvarchar(max), @expression nvarchar(max), @is_name_condition smallint = 0, @obj_name sysname = NULL, @condition_id int = NULL OUTPUT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval INT DECLARE @facet_id INT DECLARE @null_column sysname SET @null_column = NULL IF (@name IS NULL OR @name = N'') SET @null_column = '@name' ELSE IF( @description IS NULL) SET @null_column = '@description' ELSE IF( @facet IS NULL) SET @null_column = '@facet' ELSE IF( @expression IS NULL) SET @null_column = '@expression' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_syspolicy_add_condition') RETURN(1) END IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_conditions WHERE name = @name) BEGIN RAISERROR(34010, -1, -1, 'Condition', @name) RETURN(1) END SET @facet_id = (SELECT management_facet_id FROM [dbo].[syspolicy_management_facets] WHERE name = @facet) IF (@facet_id IS NULL) BEGIN RAISERROR (34014, -1, -1) RETURN(1) END INSERT INTO msdb.dbo.syspolicy_conditions_internal(name, description,facet_id,expression,is_name_condition,obj_name) VALUES(@name, @description,@facet_id,@expression,@is_name_condition,@obj_name) SELECT @retval = @@error SET @condition_id = SCOPE_IDENTITY() RETURN(@retval) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_verify_condition_identifiers]...' GO ----------------------------------------------------------- -- This procedure verifies if a condition exists -- The caller can pass either the condition name or the id ----------------------------------------------------------- CREATE PROCEDURE [dbo].[sp_syspolicy_verify_condition_identifiers] @condition_name sysname = NULL OUTPUT, @condition_id int = NULL OUTPUT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END IF ((@condition_name IS NULL) AND (@condition_id IS NULL)) OR ((@condition_name IS NOT NULL) AND (@condition_id IS NOT NULL)) BEGIN RAISERROR(14524, -1, -1, '@condition_name', '@condition_id') RETURN(1) -- Failure END -- Check id IF (@condition_id IS NOT NULL) BEGIN SELECT @condition_name = name FROM msdb.dbo.syspolicy_conditions WHERE (condition_id = @condition_id) -- the view would take care of all the permissions issues. IF (@condition_name IS NULL) BEGIN DECLARE @condition_id_as_char VARCHAR(36) SELECT @condition_id_as_char = CONVERT(VARCHAR(36), @condition_id) RAISERROR(14262, -1, -1, '@condition_id', @condition_id_as_char) RETURN(1) -- Failure END END ELSE -- Check name IF (@condition_name IS NOT NULL) BEGIN -- get the corresponding condition_id (if the condition exists) SELECT @condition_id = condition_id FROM msdb.dbo.syspolicy_conditions WHERE (name = @condition_name) -- the view would take care of all the permissions issues. IF (@condition_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@condition_name', @condition_name) RETURN(1) -- Failure END END RETURN (0) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_update_condition]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_update_condition] @name sysname = NULL, @condition_id int = NULL, @facet nvarchar(max) = NULL, @expression nvarchar(max) = NULL, @description nvarchar(max) = NULL, @is_name_condition smallint = NULL, @obj_name sysname = NULL AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval INT DECLARE @facet_id INT EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @name, @condition_id OUTPUT IF (@retval <> 0) RETURN (1) IF (@facet IS NOT NULL) BEGIN SET @facet_id = (SELECT management_facet_id FROM [dbo].[syspolicy_management_facets] WHERE name = @facet) IF (@facet_id IS NULL) BEGIN RAISERROR (34014, -1, -1) RETURN(1) END END UPDATE msdb.[dbo].[syspolicy_conditions_internal] SET description = ISNULL(@description, description), facet_id = ISNULL(@facet_id, facet_id), expression = ISNULL(@expression, expression), is_name_condition = ISNULL(@is_name_condition, is_name_condition), obj_name = ISNULL(@obj_name, obj_name) WHERE condition_id = @condition_id END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_condition]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_delete_condition] @name sysname = NULL, @condition_id int = NULL AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval INT EXEC @retval = sp_syspolicy_verify_condition_identifiers @name, @condition_id OUTPUT IF (@retval <> 0) RETURN (1) IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_policies WHERE condition_id = @condition_id) BEGIN RAISERROR(34012,-1,-1,'Condition','Policy') RETURN (1) END DELETE msdb.dbo.syspolicy_conditions_internal WHERE condition_id = @condition_id RETURN (0) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_rename_condition]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_rename_condition] @name sysname = NULL, @condition_id int = NULL, @new_name sysname = NULL AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END IF (@new_name IS NULL or LEN(@new_name) = 0) BEGIN RAISERROR(21263, -1, -1, '@new_name') RETURN(1) -- Failure END DECLARE @retval INT EXEC @retval = sp_syspolicy_verify_condition_identifiers @name, @condition_id OUTPUT IF (@retval <> 0) RETURN (1) UPDATE msdb.[dbo].[syspolicy_conditions_internal] SET name = @new_name WHERE condition_id = @condition_id SELECT @retval = @@error RETURN(@retval) END GO --------------------------------------------------------------- -- table for Policy category --------------------------------------------------------------- IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_policy_categories_internal') BEGIN PRINT 'Creating table [dbo].[syspolicy_policy_categories_internal]...'; CREATE TABLE [dbo].[syspolicy_policy_categories_internal] ( policy_category_id int IDENTITY(1,1), name sysname, mandate_database_subscriptions bit default 1 NOT NULL, CONSTRAINT [PK_syspolicy_policy_categories] PRIMARY KEY CLUSTERED (policy_category_id ASC), CONSTRAINT [UQ_syspolicy_policy_categories_name] UNIQUE(name) ); END GO PRINT 'Creating view [dbo].[syspolicy_policy_categories]...' GO CREATE VIEW [dbo].[syspolicy_policy_categories] AS SELECT policy_category_id, name, mandate_database_subscriptions FROM [dbo].[syspolicy_policy_categories_internal] GO --------------------------------------------------------------- -- ObjectSet object --------------------------------------------------------------- IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_object_sets_internal') BEGIN PRINT 'Creating table [dbo].[syspolicy_object_sets_internal]...' CREATE TABLE [dbo].[syspolicy_object_sets_internal] ( object_set_id int NOT NULL IDENTITY(1,1), object_set_name sysname NOT NULL, facet_id int, is_system bit NOT NULL default (0), CONSTRAINT [PK_syspolicy_object_sets] PRIMARY KEY CLUSTERED (object_set_id), CONSTRAINT [UQ_syspolicy_object_sets_name] UNIQUE(object_set_name) ) ALTER TABLE [dbo].[syspolicy_object_sets_internal] ADD CONSTRAINT [FK_syspolicy_object_sets_syspolicy_management_facets] FOREIGN KEY(facet_id) REFERENCES [dbo].[syspolicy_management_facets] (management_facet_id) ON DELETE CASCADE END GO PRINT 'Creating view [dbo].[syspolicy_object_sets]...' GO CREATE VIEW [dbo].[syspolicy_object_sets] AS SELECT os.object_set_id, os.object_set_name, os.facet_id, facet.name as facet_name, os.is_system FROM [dbo].[syspolicy_object_sets_internal] AS os INNER JOIN [dbo].[syspolicy_management_facets] AS facet ON os.facet_id = facet.management_facet_id GO ----------------------------------------------------------- -- This procedure verifies if a object set definition exists -- The caller can pass either the name or the id ----------------------------------------------------------- PRINT 'Creating procedure [dbo].[sp_syspolicy_verify_object_set_identifiers]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_verify_object_set_identifiers] @name sysname = NULL OUTPUT, @object_set_id int = NULL OUTPUT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END IF ((@name IS NULL) AND (@object_set_id IS NULL)) OR ((@name IS NOT NULL) AND (@object_set_id IS NOT NULL)) BEGIN -- TODO: Verify the error message is appropriate for object sets as well and not specific to policies RAISERROR(14524, -1, -1, '@name', '@object_set_id') RETURN(1) -- Failure END -- Check id IF (@object_set_id IS NOT NULL) BEGIN SELECT @name = object_set_name FROM msdb.dbo.syspolicy_object_sets WHERE (object_set_id = @object_set_id) -- the view would take care of all the permissions issues. IF (@name IS NULL) BEGIN -- TODO: Where did 36 come from? Is this the total lenght of characters for an int? DECLARE @object_set_id_as_char VARCHAR(36) SELECT @object_set_id_as_char = CONVERT(VARCHAR(36), @object_set_id) RAISERROR(14262, -1, -1, '@object_set_id', @object_set_id_as_char) RETURN(1) -- Failure END END ELSE -- Check name IF (@name IS NOT NULL) BEGIN -- get the corresponding object_set_id (if the object_set exists) SELECT @object_set_id = object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE (object_set_name = @name) -- the view would take care of all the permissions issues. IF (@object_set_id IS NULL) BEGIN -- TODO: Verify the error message is appropriate for object sets as well and not specific to policies RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END END RETURN (0) END GO ----------------------------------------------------------- -- This procedure verifies if an object set is refernced (cannot be deleted) ----------------------------------------------------------- PRINT 'Creating procedure [dbo].[sp_syspolicy_verify_object_set_references]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_verify_object_set_references] @object_set_id int, @is_referenced int OUTPUT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END SELECT @is_referenced = count(*) FROM dbo.syspolicy_policies WHERE object_set_id = @object_set_id END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_add_object_set]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_add_object_set] @object_set_name sysname, @facet nvarchar (max), @object_set_id int OUTPUT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval INT DECLARE @facet_id INT DECLARE @null_column sysname IF( @facet IS NULL) SET @null_column = '@facet' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_syspolicy_add_object_set') RETURN(1) END SET @facet_id = (SELECT management_facet_id FROM [dbo].[syspolicy_management_facets] WHERE name = @facet) IF (@facet_id IS NULL) BEGIN RAISERROR (34014, -1, -1) RETURN(1) END INSERT INTO msdb.[dbo].[syspolicy_object_sets_internal] (object_set_name, facet_id) VALUES (@object_set_name, @facet_id) SELECT @retval = @@error SET @object_set_id = SCOPE_IDENTITY() RETURN(@retval) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_object_set]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_delete_object_set] @object_set_name sysname = NULL, @object_set_id int = NULL AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval INT EXEC @retval = sp_syspolicy_verify_object_set_identifiers @object_set_name, @object_set_id OUTPUT IF (@retval <> 0) RETURN (1) DELETE msdb.[dbo].[syspolicy_object_sets_internal] WHERE object_set_id = @object_set_id RETURN (0) END GO --------------------------------------------------------------- -- PolicyDefinition object --------------------------------------------------------------- IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_policies_internal') BEGIN PRINT 'Creating table [dbo].[syspolicy_policies_internal]...'; CREATE TABLE [dbo].[syspolicy_policies_internal] ( policy_id int IDENTITY(1,1), name sysname NOT NULL, condition_id int NOT NULL, root_condition_id int NULL, date_created datetime NOT NULL default GETDATE(), execution_mode int NOT NULL default (0), policy_category_id int NULL, schedule_uid uniqueidentifier NULL, description nvarchar(max) NOT NULL default (''), help_text nvarchar(4000) NOT NULL default (''), help_link nvarchar(2083) NOT NULL default (''), object_set_id INT NULL, is_enabled bit default 0 NOT NULL, job_id uniqueidentifier NULL, created_by sysname NOT NULL default SUSER_SNAME(), modified_by sysname NULL, date_modified datetime NULL, is_system bit NOT NULL default (0), CONSTRAINT [PK_syspolicy_policies] PRIMARY KEY CLUSTERED (policy_id ASC), CONSTRAINT [UQ_syspolicy_policies_name] UNIQUE(name) ); ALTER TABLE [dbo].[syspolicy_policies_internal] ADD CONSTRAINT [FK_syspolicy_policies_syspolicy_conditions] FOREIGN KEY(condition_id) REFERENCES [dbo].[syspolicy_conditions_internal] (condition_id); ALTER TABLE [dbo].[syspolicy_policies_internal] ADD CONSTRAINT [FK_syspolicy_policies_syspolicy_policy_categories] FOREIGN KEY(policy_category_id) REFERENCES [dbo].[syspolicy_policy_categories_internal] (policy_category_id); ALTER TABLE [dbo].[syspolicy_policies_internal] ADD CONSTRAINT [FK_syspolicy_policies_syspolicy_root_conditions] FOREIGN KEY(root_condition_id) REFERENCES [dbo].[syspolicy_conditions_internal] (condition_id); ALTER TABLE [dbo].[syspolicy_policies_internal] ADD CONSTRAINT [FK_syspolicy_policies_sysjobs] FOREIGN KEY(job_id) REFERENCES [dbo].[sysjobs] (job_id); ALTER TABLE [dbo].[syspolicy_policies_internal] ADD CONSTRAINT [FK_syspolicy_policies_syspolicy_object_sets] FOREIGN KEY(object_set_id) REFERENCES [dbo].[syspolicy_object_sets_internal] (object_set_id); END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_create_job]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_create_job] @schedule_uid uniqueidentifier, @is_enabled bit = 0, @jobID uniqueidentifier OUTPUT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @job_name sysname -- create unique job name SET @job_name = N'syspolicy_check_schedule_' + LEFT(CONVERT(nvarchar(100), @schedule_uid), 100) WHILE (EXISTS (SELECT * FROM msdb..sysjobs WHERE name = @job_name)) BEGIN SET @job_name = N'syspolicy_check_schedule_' + LEFT(CONVERT(nvarchar(91), @schedule_uid), 91) + '_' + RIGHT(STR(FLOOR(RAND() * 100000000)),8) END EXEC msdb.dbo.sp_add_job @job_name=@job_name, @enabled=@is_enabled, @notify_level_eventlog=0, @notify_level_email=2, @notify_level_netsend=2, @notify_level_page=2, @delete_level=0, @category_id=0, -- [Uncategorized (Local)] @job_id = @jobID OUTPUT EXEC msdb.dbo.sp_add_jobserver @job_name=@job_name, @server_name = @@servername EXEC msdb.dbo.sp_add_jobstep @job_id=@jobID, @step_name=N'Verify that automation is enabled.', @step_id=1, @cmdexec_success_code=0, @on_fail_action=1, @on_fail_step_id=0, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'TSQL', @command=N'IF (msdb.dbo.fn_syspolicy_is_automation_enabled() != 1) BEGIN RAISERROR(34022, 16, 1) END', @database_name=N'master', @flags=0 DECLARE @command nvarchar(max) SET @command = [dbo].[fn_syspolicy_get_ps_command] (@schedule_uid) EXEC msdb.dbo.sp_add_jobstep @job_id=@jobID, @step_name=N'Evaluate policies.', @step_id=2, @cmdexec_success_code=0, @on_success_action=1, @on_fail_action=2, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'PowerShell', @command=@command, @flags=0 EXEC msdb.dbo.sp_update_jobstep @job_id = @jobID, @step_id = 1, @on_success_action=4, @on_success_step_id=2 DECLARE @schedule_id int SELECT @schedule_id = schedule_id from msdb.dbo.sysschedules where schedule_uid = @schedule_uid EXEC msdb.dbo.sp_attach_schedule @job_name = @job_name, @schedule_id = @schedule_id END GO PRINT 'Creating function [dbo].[fn_syspolicy_get_ps_command] ...' GO CREATE FUNCTION [dbo].[fn_syspolicy_get_ps_command] (@schedule_uid uniqueidentifier) RETURNS nvarchar(max) AS BEGIN DECLARE @schedule_uid_string nvarchar(max); SET @schedule_uid_string = CONVERT(nvarchar(36), @schedule_uid); -- translate to PSPath root name, for default instances -- we need to add \default as instance name DECLARE @root_name nvarchar(100); SET @root_name = @@SERVERNAME IF( 0 = CHARINDEX('\', @@SERVERNAME)) SET @root_name = @root_name + N'\default'; DECLARE @command nvarchar(max); SET @command = N'dir SQLSERVER:\SQLPolicy\' + @root_name + N'\Policies | where { $_.ScheduleUid -eq "' + @schedule_uid_string + N'" } | where { $_.Enabled -eq 1} | where {$_.AutomatedPolicyEvaluationMode -eq 4} | Invoke-PolicyEvaluation -AdHocPolicyEvaluationMode 2 -TargetServerName ' + @@SERVERNAME RETURN @command END GO PRINT 'Creating trigger [dbo].[syspolicy_insert_job_create_trigger] on [dbo].[syspolicy_policies_internal]' GO CREATE TRIGGER [dbo].[syspolicy_insert_job_create_trigger] on [dbo].[syspolicy_policies_internal] FOR INSERT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END DECLARE @jobID uniqueidentifier DECLARE @schedule_uid uniqueidentifier DECLARE @is_enabled bit -- verify that values in inserted.schedule_uid are valid IF EXISTS ( SELECT * FROM inserted i WHERE i.schedule_uid NOT IN (SELECT schedule_uid FROM msdb.dbo.sysschedules ) AND ((i.execution_mode & 4) = 4)) BEGIN ROLLBACK -- Failure RAISERROR (14365, -1, -1) RETURN END -- find all schedules referenced by the inserted policies for which -- there is no agent job that executes the policies DECLARE schedule_cursor CURSOR LOCAL FOR SELECT DISTINCT i.schedule_uid FROM inserted i WHERE ((i.execution_mode & 4) = 4) AND NOT EXISTS (SELECT * FROM msdb.dbo.syspolicy_policies p WHERE p.policy_id NOT IN (SELECT policy_id FROM inserted) AND p.schedule_uid = i.schedule_uid AND ((p.execution_mode & 4) = 4) ) -- iterate through the cursor and create a job for every schedule OPEN schedule_cursor FETCH schedule_cursor INTO @schedule_uid WHILE @@FETCH_STATUS = 0 BEGIN -- figure out if the job is enabled or not SELECT @is_enabled = COUNT(*) FROM inserted i WHERE i.schedule_uid = @schedule_uid AND i.is_enabled = 1 -- explicitly nullify jobID, -- (if we need to create more than 1 job, it will not be null and sp_add_job will think we're getting job from MSX) SET @jobID = NULL -- create the job that is going to execute the schedule EXEC [msdb].[dbo].[sp_syspolicy_create_job] @schedule_uid, @is_enabled, @jobID OUTPUT -- update the job_id back into the policies table UPDATE p SET p.job_id = @jobID FROM msdb.dbo.syspolicy_policies_internal p INNER JOIN inserted i ON p.policy_id = i.policy_id WHERE i.schedule_uid = @schedule_uid AND (i.execution_mode & 4) = 4 FETCH schedule_cursor INTO @schedule_uid END CLOSE schedule_cursor DEALLOCATE schedule_cursor -- in case we haven't created the job we still need to update -- the policies with their jobID UPDATE p SET p.job_id = ( SELECT TOP 1 p2.job_id FROM msdb.dbo.syspolicy_policies p2 WHERE p2.schedule_uid = p.schedule_uid AND p2.job_id IS NOT NULL) FROM msdb.dbo.syspolicy_policies_internal p INNER JOIN inserted i ON p.policy_id = i.policy_id WHERE ((p.execution_mode & 4) = 4) AND p.job_id IS NULL -- See what jobs we need to enable. -- This can happen because we might create a new policy that -- is enabled and there is already a job for it, but the existing -- job is disabled DECLARE jobs_to_enable CURSOR LOCAL FOR SELECT DISTINCT j.job_id FROM dbo.sysjobs_view j JOIN msdb.dbo.syspolicy_policies p ON p.job_id = j.job_id JOIN inserted i ON p.policy_id = i.policy_id WHERE ((i.execution_mode & 4) = 4) AND j.enabled = 0 AND EXISTS ( SELECT * from msdb.dbo.syspolicy_policies p2 WHERE p2.job_id = j.job_id AND p2.is_enabled = 1) OPEN jobs_to_enable FETCH jobs_to_enable INTO @jobID WHILE @@FETCH_STATUS = 0 BEGIN EXEC msdb.dbo.sp_update_job @job_id = @jobID, @enabled = 1 FETCH jobs_to_enable INTO @jobID END CLOSE jobs_to_enable DEALLOCATE jobs_to_enable -- enable events infrastructure IF EXISTS ( SELECT * FROM inserted i WHERE ((i.execution_mode & 1) = 1)) BEGIN EXEC sys.sp_syspolicy_update_ddl_trigger END IF EXISTS (SELECT * FROM inserted i WHERE ((i.execution_mode & 2) = 2)) BEGIN EXEC sys.sp_syspolicy_update_event_notification END -- update owner information UPDATE msdb.dbo.syspolicy_policies_internal SET created_by = original_login(), date_created = getdate (), date_modified = NULL, modified_by = NULL FROM inserted i, msdb.dbo.syspolicy_policies_internal policies WHERE i.policy_id = policies.policy_id -- protect against non-scheduled automation jobs -- that have expressions that execute script DECLARE @row_count int SELECT @row_count = count(*) FROM syspolicy_conditions c INNER JOIN inserted i ON (i.condition_id = c.condition_id OR i.root_condition_id = c.condition_id) WHERE i.is_enabled != 0 AND i.execution_mode != 4 AND (1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteSql"]' ) OR 1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteWql"]' ) ) OPTION (FORCE ORDER); SELECT @row_count = @row_count + count(*) FROM dbo.syspolicy_target_set_levels l INNER JOIN dbo.syspolicy_target_sets s ON s.target_set_id = l.target_set_id INNER JOIN syspolicy_conditions c on c.condition_id = l.condition_id INNER JOIN syspolicy_object_sets_internal os ON os.object_set_id = s.object_set_id INNER JOIN inserted i ON os.object_set_id = i.object_set_id WHERE i.is_enabled != 0 AND i.execution_mode != 4 AND (1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteSql"]' ) OR 1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteWql"]' ) ) OPTION (FORCE ORDER); IF (@row_count > 0) BEGIN RAISERROR(34017, -1, -1); ROLLBACK TRANSACTION; END END -- create trigger GO PRINT 'Creating trigger [dbo].[syspolicy_update_job_update_trigger] on [dbo].[syspolicy_policies_internal]' GO CREATE TRIGGER [dbo].[syspolicy_update_job_update_trigger] on [dbo].[syspolicy_policies_internal] FOR UPDATE AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END -- This is to prevent indirect entrance of the trigger IF (TRIGGER_NESTLEVEL() > 1) RETURN -- verify that values in inserted.schedule_uid are valid IF EXISTS ( SELECT * FROM inserted i WHERE i.schedule_uid NOT IN (SELECT schedule_uid FROM msdb.dbo.sysschedules ) AND ((i.execution_mode & 4) = 4)) BEGIN ROLLBACK -- Failure RAISERROR (14365, -1, -1) RETURN END -- update eventing infrastructure IF(UPDATE(execution_mode) OR UPDATE(is_enabled)) BEGIN IF EXISTS (SELECT * FROM inserted i INNER JOIN deleted d ON i.policy_id = d.policy_id WHERE (((i.execution_mode & 1) = 1) OR ((d.execution_mode & 1) = 1)) AND (i.is_enabled != d.is_enabled OR i.execution_mode != d.execution_mode)) BEGIN EXEC sys.sp_syspolicy_update_ddl_trigger END IF EXISTS (SELECT * FROM inserted i INNER JOIN deleted d ON i.policy_id = d.policy_id WHERE (((i.execution_mode & 2) = 2) OR ((d.execution_mode & 2) = 2)) AND (i.is_enabled != d.is_enabled OR i.execution_mode != d.execution_mode)) BEGIN EXEC sys.sp_syspolicy_update_event_notification END END DECLARE @jobID uniqueidentifier DECLARE @is_enabled bit DECLARE @schedule_uid uniqueidentifier -- set the job_id to NULL for all policies whose schedule_uid has changed -- so that we can create a job if needed UPDATE p SET p.job_id = NULL FROM msdb.dbo.syspolicy_policies p INNER JOIN inserted i ON p.policy_id = i.policy_id INNER JOIN deleted d ON d.policy_id = p.policy_id WHERE i.schedule_uid != d.schedule_uid -- find all schedules referenced by the inserted policies for which -- there is no agent job that executes the policies DECLARE schedule_cursor CURSOR LOCAL FOR SELECT DISTINCT i.schedule_uid FROM inserted i WHERE ((i.execution_mode & 4) = 4) AND NOT EXISTS (SELECT * FROM msdb.dbo.syspolicy_policies p WHERE p.schedule_uid = i.schedule_uid AND ((p.execution_mode & 4) = 4) AND p.job_id IS NOT NULL) -- iterate through the cursor and create a job for every schedule OPEN schedule_cursor FETCH schedule_cursor INTO @schedule_uid WHILE @@FETCH_STATUS = 0 BEGIN -- figure out if the job is enabled or not SELECT @is_enabled = COUNT(*) FROM inserted i WHERE i.schedule_uid = @schedule_uid AND i.is_enabled = 1 -- create the job that is going to execute the schedule EXEC [msdb].[dbo].[sp_syspolicy_create_job] @schedule_uid, @is_enabled, @jobID OUTPUT -- update the job_id back into the policies table UPDATE p SET p.job_id = @jobID FROM msdb.dbo.syspolicy_policies_internal p INNER JOIN inserted i ON p.policy_id = i.policy_id WHERE i.schedule_uid = @schedule_uid AND (i.execution_mode & 4) = 4 FETCH schedule_cursor INTO @schedule_uid END CLOSE schedule_cursor DEALLOCATE schedule_cursor -- in case we haven't created the job we still need to update -- the policies with their jobID UPDATE p SET p.job_id = ( SELECT TOP 1 p2.job_id FROM msdb.dbo.syspolicy_policies p2 WHERE p2.schedule_uid = p.schedule_uid AND p2.job_id IS NOT NULL) FROM msdb.dbo.syspolicy_policies p INNER JOIN inserted i ON p.policy_id = i.policy_id WHERE ((p.execution_mode & 4) = 4) AND p.job_id IS NULL -- if the execution_mode has changed then we need to clear the job references UPDATE p SET p.job_id = NULL FROM msdb.dbo.syspolicy_policies_internal p INNER JOIN inserted i ON p.policy_id = i.policy_id INNER JOIN deleted d ON p.policy_id = d.policy_id WHERE ((i.execution_mode & 4) != 4) AND ((d.execution_mode & 4) = 4) AND p.job_id IS NOT NULL -- See what jobs we need to enable. -- This can happen because we might create a new policy that -- is enabled and there is already a job for it, but the existing -- job is disabled DECLARE jobs_to_enable CURSOR LOCAL FOR SELECT DISTINCT j.job_id FROM dbo.sysjobs_view j JOIN msdb.dbo.syspolicy_policies p ON p.job_id = j.job_id JOIN inserted i ON p.policy_id = i.policy_id WHERE ((i.execution_mode & 4) = 4) AND j.enabled = 0 AND EXISTS ( SELECT * from msdb.dbo.syspolicy_policies p2 WHERE p2.job_id = j.job_id AND p2.is_enabled = 1) OPEN jobs_to_enable FETCH jobs_to_enable INTO @jobID WHILE @@FETCH_STATUS = 0 BEGIN EXEC msdb.dbo.sp_update_job @job_id = @jobID, @enabled = 1 FETCH jobs_to_enable INTO @jobID END CLOSE jobs_to_enable DEALLOCATE jobs_to_enable -- Find out what jobs have to be deleted because the policy's schedule -- has changed IF (UPDATE(schedule_uid)) BEGIN DECLARE deleted_cursor CURSOR LOCAL FOR SELECT DISTINCT d.job_id FROM deleted d WHERE ((d.execution_mode & 4) = 4) AND d.job_id NOT IN (SELECT job_id FROM msdb.dbo.syspolicy_policies p WHERE ((p.execution_mode & 4) = 4)) OPEN deleted_cursor FETCH deleted_cursor INTO @jobID WHILE (@@FETCH_STATUS=0) BEGIN -- delete the job(s), but do not delete the shared schedule IF (@jobID IS NOT NULL) EXEC msdb.dbo.sp_delete_job @job_id = @jobID, @delete_unused_schedule = 0 FETCH deleted_cursor INTO @jobID END -- while (@@FETCH_STATUS=0) CLOSE deleted_cursor DEALLOCATE deleted_cursor END -- UPDATE(schedule_uid) -- See what jobs we need to disable. -- This can happen because we do not need to delete the job, but -- all policies that reference it are disabled. DECLARE jobs_to_disable CURSOR LOCAL FOR SELECT DISTINCT j.job_id FROM dbo.sysjobs_view j JOIN msdb.dbo.syspolicy_policies p ON p.job_id = j.job_id WHERE j.enabled = 1 AND NOT EXISTS ( SELECT * from msdb.dbo.syspolicy_policies p2 WHERE p2.job_id = j.job_id AND p2.is_enabled = 1 AND ((p2.execution_mode & 4) = 4)) OPEN jobs_to_disable FETCH jobs_to_disable INTO @jobID WHILE @@FETCH_STATUS = 0 BEGIN EXEC msdb.dbo.sp_update_job @job_id = @jobID, @enabled = 0 FETCH jobs_to_disable INTO @jobID END CLOSE jobs_to_disable DEALLOCATE jobs_to_disable UPDATE msdb.dbo.syspolicy_policies_internal SET modified_by = original_login(), date_modified = GETDATE() FROM inserted i, msdb.dbo.syspolicy_policies_internal policies WHERE i.policy_id = policies.policy_id END -- update trigger GO PRINT 'Creating trigger [dbo].[syspolicy_insert_policy_trigger] on [dbo].[syspolicy_policies_internal]' GO CREATE TRIGGER [dbo].[syspolicy_insert_policy_trigger] on [dbo].[syspolicy_policies_internal] FOR INSERT AS BEGIN DECLARE @object_set_id int, @name sysname SELECT TOP 1 @object_set_id = i.object_set_id, @name = i.name FROM inserted i WHERE 1 < (SELECT count(*) FROM syspolicy_policies p WHERE p.object_set_id = i.object_set_id) IF @@ROWCOUNT > 0 BEGIN DECLARE @os_name sysname, @policy_name sysname SELECT TOP 1 @os_name = os.object_set_name, @policy_name = p.name FROM syspolicy_object_sets os INNER JOIN syspolicy_policies p ON (os.object_set_id = p.object_set_id) WHERE os.object_set_id = @object_set_id AND p.name <> @name RAISERROR(34013, -1, -1, 'ObjectSet', @os_name, @policy_name) ROLLBACK TRANSACTION END END GO PRINT 'Creating trigger [dbo].[syspolicy_update_policy_trigger] on [dbo].[syspolicy_policies_internal]' GO CREATE TRIGGER [dbo].[syspolicy_update_policy_trigger] on [dbo].[syspolicy_policies_internal] FOR UPDATE AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END -- This is to prevent indirect execution of the trigger IF (TRIGGER_NESTLEVEL() > 1) RETURN IF( UPDATE(condition_id) ) BEGIN -- delete all health state records for active policies whose -- condition has changed DELETE FROM [dbo].[syspolicy_system_health_state_internal] FROM [dbo].[syspolicy_system_health_state_internal] phs INNER JOIN inserted i ON phs.policy_id = i.policy_id INNER JOIN deleted d ON phs.policy_id = d.policy_id WHERE d.condition_id != i.condition_id AND i.is_enabled = 1 END IF( UPDATE(object_set_id) ) BEGIN DECLARE @object_set_id int, @numref int, @new_object_set_id int, @name sysname DECLARE os_cursor CURSOR LOCAL FOR SELECT i.object_set_id, d.object_set_id, i.name FROM inserted i INNER JOIN deleted d ON (i.policy_id = d.policy_id) WHERE (d.object_set_id IS NOT NULL AND i.object_set_id IS NULL) OR (i.object_set_id IS NOT NULL AND d.object_set_id IS NULL) OR (d.object_set_id != i.object_set_id) OPEN os_cursor FETCH os_cursor INTO @new_object_set_id, @object_set_id, @name WHILE @@FETCH_STATUS = 0 BEGIN IF (@object_set_id IS NOT NULL) BEGIN EXEC sp_syspolicy_verify_object_set_references @object_set_id, @numref OUTPUT IF (@numref = 0) EXEC sp_syspolicy_delete_object_set @object_set_id=@object_set_id END IF (@new_object_set_id IS NOT NULL) BEGIN EXEC sp_syspolicy_verify_object_set_references @new_object_set_id, @numref OUTPUT IF (@numref > 1) BEGIN DECLARE @os_name sysname, @policy_name sysname SELECT TOP 1 @os_name = os.object_set_name, @policy_name = p.name FROM syspolicy_object_sets os INNER JOIN syspolicy_policies p ON (os.object_set_id = p.object_set_id) WHERE os.object_set_id = @object_set_id AND p.name <> @name RAISERROR(34013, -1, -1, 'ObjectSet', @os_name, @policy_name) ROLLBACK TRANSACTION END END FETCH os_cursor INTO @new_object_set_id, @object_set_id, @name END CLOSE os_cursor DEALLOCATE os_cursor END IF( UPDATE(is_enabled) ) BEGIN -- delete all health state records for policies that -- have been disabled DELETE FROM [dbo].[syspolicy_system_health_state_internal] FROM [dbo].[syspolicy_system_health_state_internal] phs INNER JOIN inserted i ON phs.policy_id = i.policy_id INNER JOIN deleted d ON phs.policy_id = d.policy_id WHERE d.is_enabled = 1 AND i.is_enabled = 0 END IF( UPDATE(is_enabled) ) BEGIN DECLARE @row_count int SELECT @row_count = count(*) FROM syspolicy_conditions c INNER JOIN inserted i ON (i.condition_id = c.condition_id OR i.root_condition_id = c.condition_id) WHERE i.is_enabled != 0 AND i.execution_mode != 4 AND (1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteSql"]' ) OR 1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteWql"]' ) ) OPTION (FORCE ORDER) SELECT @row_count = @row_count + count(*) FROM dbo.syspolicy_target_set_levels l INNER JOIN dbo.syspolicy_target_sets s ON s.target_set_id = l.target_set_id INNER JOIN syspolicy_conditions c on c.condition_id = l.condition_id INNER JOIN syspolicy_object_sets_internal os on os.object_set_id = s.object_set_id INNER JOIN inserted i ON os.object_set_id = i.object_set_id WHERE i.is_enabled != 0 AND i.execution_mode != 4 AND (1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteSql"]' ) OR 1 = CONVERT(xml, c.expression).exist('//FunctionType/text()[.="ExecuteWql"]' ) ) OPTION (FORCE ORDER) IF (@row_count > 0) BEGIN RAISERROR(34017, -1, -1) ROLLBACK TRANSACTION END END END GO PRINT 'Creating trigger [dbo].[syspolicy_delete_job_delete_trigger] on [dbo].[syspolicy_policies_internal]' GO CREATE TRIGGER [dbo].[syspolicy_delete_job_delete_trigger] on [dbo].[syspolicy_policies_internal] FOR DELETE AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END DECLARE @jobID uniqueidentifier -- Declare the cursor to iterate over the jobs that are only referenced -- by deleted policies. The jobs that are still referenced by active policies -- should not be deleted. DECLARE deleted_cursor CURSOR LOCAL FOR SELECT DISTINCT d.job_id FROM deleted d WHERE ((d.execution_mode & 4) = 4) AND NOT EXISTS (SELECT * FROM msdb.dbo.syspolicy_policies p WHERE p.job_id = d.job_id AND ((p.execution_mode & 4) = 4) AND p.policy_id NOT IN (SELECT d2.policy_id FROM deleted d2)) OPEN deleted_cursor FETCH deleted_cursor INTO @jobID WHILE (@@FETCH_STATUS=0) BEGIN -- delete the job(s), but do not delete the shared schedule IF (@jobID IS NOT NULL) EXEC msdb.dbo.sp_delete_job @job_id = @jobID, @delete_unused_schedule = 0 FETCH deleted_cursor INTO @jobID END -- while (@@FETCH_STATUS=0) CLOSE deleted_cursor DEALLOCATE deleted_cursor -- See what jobs we need to disable. -- This can happen because we do not need to delete the job, but -- all policies that reference it are disabled. DECLARE jobs_to_disable CURSOR LOCAL FOR SELECT DISTINCT j.job_id FROM dbo.sysjobs_view j JOIN deleted d ON d.job_id = j.job_id WHERE j.enabled = 1 AND NOT EXISTS ( SELECT * from msdb.dbo.syspolicy_policies p2 WHERE p2.job_id = j.job_id AND p2.is_enabled = 1 AND ((p2.execution_mode & 4) = 4)) OPEN jobs_to_disable FETCH jobs_to_disable INTO @jobID WHILE @@FETCH_STATUS = 0 BEGIN EXEC msdb.dbo.sp_update_job @job_id = @jobID, @enabled = 0 FETCH jobs_to_disable INTO @jobID END CLOSE jobs_to_disable DEALLOCATE jobs_to_disable -- update eventing infrastructure IF EXISTS ( SELECT * FROM deleted d WHERE ((d.execution_mode & 1) = 1)) BEGIN EXEC sys.sp_syspolicy_update_ddl_trigger END IF EXISTS (SELECT * FROM deleted d WHERE ((d.execution_mode & 2) = 2)) BEGIN EXEC sys.sp_syspolicy_update_event_notification END END GO PRINT 'Creating trigger [dbo].[syspolicy_instead_delete_policy_trigger] on [dbo].[syspolicy_policies_internal]' GO CREATE TRIGGER [dbo].[syspolicy_instead_delete_policy_trigger] on [dbo].[syspolicy_policies_internal] INSTEAD OF DELETE AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END -- This trigger deletes references in given order to protect from deadlocks DELETE msdb.dbo.syspolicy_policy_execution_history_internal WHERE policy_id in (SELECT policy_id FROM deleted) DELETE msdb.dbo.syspolicy_system_health_state_internal WHERE policy_id in (SELECT policy_id FROM deleted) DELETE msdb.dbo.syspolicy_policies_internal WHERE policy_id in (SELECT policy_id FROM deleted) END GO PRINT 'Creating view [dbo].[syspolicy_policies]...' GO CREATE VIEW [dbo].[syspolicy_policies] AS SELECT policy_id, name, condition_id, root_condition_id, date_created, execution_mode, policy_category_id, schedule_uid, description, help_text, help_link, object_set_id, is_enabled, job_id, created_by, modified_by, date_modified, is_system FROM [dbo].[syspolicy_policies_internal] GO PRINT '' PRINT 'Creating trigger syspolicy_insert_condition_trigger...' GO CREATE TRIGGER dbo.syspolicy_insert_condition_trigger ON msdb.dbo.syspolicy_conditions_internal AFTER INSERT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END UPDATE msdb.dbo.syspolicy_conditions_internal SET created_by = original_login() FROM inserted i INNER JOIN msdb.dbo.syspolicy_conditions_internal conditions ON i.condition_id = conditions.condition_id END GO PRINT '' PRINT 'Creating trigger dbo.syspolicy_for_update_condition_trigger...' GO CREATE TRIGGER dbo.syspolicy_for_update_condition_trigger ON msdb.dbo.syspolicy_conditions_internal FOR UPDATE AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END -- This is to prevent indirect entrance of the trigger IF (TRIGGER_NESTLEVEL() > 1) RETURN -- do not allow expression to be changed to a script -- if the policy is enabled IF UPDATE(expression) BEGIN DECLARE @row_count int SELECT @row_count = count(*) FROM inserted i INNER JOIN syspolicy_policies p ON (i.condition_id = p.condition_id OR p.root_condition_id = i.condition_id) WHERE p.is_enabled != 0 AND p.execution_mode != 4 AND (1 = CONVERT(xml, i.expression).exist('//FunctionType/text()[.="ExecuteSql"]' ) OR 1 = CONVERT(xml, i.expression).exist('//FunctionType/text()[.="ExecuteWql"]' ) ) OPTION (FORCE ORDER) SELECT @row_count = @row_count + count(*) FROM dbo.syspolicy_target_set_levels l INNER JOIN dbo.syspolicy_target_sets s ON s.target_set_id = l.target_set_id INNER JOIN inserted i on i.condition_id = l.condition_id INNER JOIN syspolicy_object_sets_internal os ON s.object_set_id = os.object_set_id INNER JOIN syspolicy_policies p ON os.object_set_id = p.object_set_id WHERE p.is_enabled != 0 AND p.execution_mode != 4 AND (1 = CONVERT(xml, i.expression).exist('//FunctionType/text()[.="ExecuteSql"]' ) OR 1 = CONVERT(xml, i.expression).exist('//FunctionType/text()[.="ExecuteWql"]' ) ) OPTION (FORCE ORDER) IF (@row_count > 0) BEGIN RAISERROR(34017, -1, -1) ROLLBACK TRANSACTION END END END GO PRINT '' PRINT 'Creating trigger syspolicy_after_update_condition_trigger...' GO CREATE TRIGGER dbo.syspolicy_after_update_condition_trigger ON msdb.dbo.syspolicy_conditions_internal AFTER UPDATE AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END -- This is to prevent indirect entrance of the trigger IF (TRIGGER_NESTLEVEL() > 1) RETURN UPDATE msdb.dbo.syspolicy_conditions_internal SET modified_by = original_login(), date_modified = GETDATE() FROM inserted i INNER JOIN msdb.dbo.syspolicy_conditions_internal c ON i.condition_id = c.condition_id -- update health state table by deleting all the records for -- policies whose expression has been modified IF UPDATE(expression) BEGIN DELETE FROM dbo.syspolicy_system_health_state_internal FROM dbo.syspolicy_system_health_state_internal phs INNER JOIN dbo.syspolicy_policies p ON phs.policy_id = p.policy_id INNER JOIN inserted i ON p.condition_id = i.condition_id INNER JOIN deleted d ON p.condition_id = d.condition_id WHERE d.expression != i.expression END END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_verify_policy_category_identifiers]...' GO ----------------------------------------------------------- -- This procedure verifies if a policy category exists -- The caller can pass either the policy category name or the id ----------------------------------------------------------- CREATE PROCEDURE [dbo].[sp_syspolicy_verify_policy_category_identifiers] @policy_category_name sysname = NULL OUTPUT, @policy_category_id int = NULL OUTPUT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END IF ((@policy_category_name IS NULL) AND (@policy_category_id IS NULL)) OR ((@policy_category_name IS NOT NULL) AND (@policy_category_id IS NOT NULL)) BEGIN RAISERROR(14524, -1, -1, '@policy_category_name', '@policy_category_id') RETURN(1) -- Failure END -- Check id IF (@policy_category_id IS NOT NULL) BEGIN SELECT @policy_category_name = name FROM msdb.dbo.syspolicy_policy_categories WHERE (policy_category_id = @policy_category_id) -- the view would take care of all the permissions issues. IF (@policy_category_name IS NULL) BEGIN DECLARE @policy_category_id_as_char VARCHAR(36) SELECT @policy_category_id_as_char = CONVERT(VARCHAR(36), @policy_category_id) RAISERROR(14262, -1, -1, '@policy_category_id', @policy_category_id_as_char) RETURN(1) -- Failure END END ELSE -- Check name IF (@policy_category_name IS NOT NULL) BEGIN -- get the corresponding policy_category_id (if the condition exists) SELECT @policy_category_id = policy_category_id FROM msdb.dbo.syspolicy_policy_categories WHERE (name = @policy_category_name) -- the view would take care of all the permissions issues. IF (@policy_category_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@policy_category_name', @policy_category_name) RETURN(1) -- Failure END END RETURN (0) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_add_policy_category]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_add_policy_category] @name sysname, @mandate_database_subscriptions bit = 1, @policy_category_id int OUTPUT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval INT DECLARE @null_column sysname SET @null_column = NULL IF(@name IS NULL OR @name = N'') SET @null_column = '@name' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_syspolicy_add_policy_category') RETURN(1) END IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_policy_categories_internal WHERE name = @name) BEGIN RAISERROR(34010, -1, -1, 'Policy Category', @name) RETURN(1) END INSERT INTO msdb.dbo.syspolicy_policy_categories_internal(name, mandate_database_subscriptions) VALUES (@name, @mandate_database_subscriptions) SELECT @retval = @@error SET @policy_category_id = SCOPE_IDENTITY() RETURN(@retval) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_policy_category]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_delete_policy_category] @name sysname = NULL, @policy_category_id int = NULL AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval INT EXEC @retval = sp_syspolicy_verify_policy_category_identifiers @name, @policy_category_id OUTPUT IF (@retval <> 0) RETURN (1) IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_policy_category_subscriptions WHERE policy_category_id = @policy_category_id) BEGIN RAISERROR(34012,-1,-1,'Policy Category','Policy Subscription') RETURN (1) END IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_policies WHERE policy_category_id = @policy_category_id) BEGIN RAISERROR(34012,-1,-1,'Policy Category','Policy') RETURN (1) END DELETE msdb.dbo.syspolicy_policy_categories_internal WHERE policy_category_id = @policy_category_id SET @retval = @@error RETURN @retval END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_rename_policy_category]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_rename_policy_category] @name sysname = NULL, @policy_category_id int = NULL, @new_name sysname = NULL AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END IF (@new_name IS NULL or LEN(@new_name) = 0) BEGIN RAISERROR(21263, -1, -1, '@new_name') RETURN(1) -- Failure END DECLARE @retval INT EXEC @retval = sp_syspolicy_verify_policy_category_identifiers @name, @policy_category_id OUTPUT IF (@retval <> 0) RETURN (1) UPDATE msdb.[dbo].[syspolicy_policy_categories_internal ] SET name = @new_name WHERE policy_category_id = @policy_category_id SELECT @retval = @@error RETURN(@retval) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_update_policy_category]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_update_policy_category] @name sysname = NULL, @policy_category_id int = NULL, @mandate_database_subscriptions bit = NULL AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval INT EXEC @retval = sp_syspolicy_verify_policy_category_identifiers @name, @policy_category_id OUTPUT IF (@retval <> 0) RETURN (1) UPDATE msdb.[dbo].[syspolicy_policy_categories_internal ] SET mandate_database_subscriptions = ISNULL(@mandate_database_subscriptions, mandate_database_subscriptions) WHERE policy_category_id = @policy_category_id SELECT @retval = @@error RETURN(@retval) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_add_policy]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_add_policy] @name sysname, @condition_id int = NULL, @condition_name sysname = NULL, @schedule_uid uniqueidentifier = NULL, @policy_category sysname = NULL, @description nvarchar(max) = N'', @help_text nvarchar(4000) = N'', @help_link nvarchar(2083) = N'', @execution_mode int, @is_enabled bit = 0, @root_condition_id int = NULL, @root_condition_name sysname = NULL, @object_set sysname = NULL, @policy_id int = NULL OUTPUT WITH EXECUTE AS OWNER AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval INT DECLARE @null_column sysname SET @null_column = NULL IF (@name IS NULL OR @name = N'') SET @null_column = '@name' ELSE IF (@execution_mode IS NULL ) SET @null_column = '@execution_mode' ELSE IF( @is_enabled IS NULL) SET @null_column = '@is_enabled' ELSE IF( @description IS NULL) SET @null_column = '@description' ELSE IF( @help_text IS NULL) SET @null_column = '@help_text' ELSE IF( @help_link IS NULL) SET @null_column = '@help_link' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_syspolicy_add_policy') RETURN(1) END IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_policies WHERE name = @name) BEGIN RAISERROR(34010, -1, -1, 'Policy', @name) RETURN(1) END SET @schedule_uid = ISNULL (@schedule_uid, '{00000000-0000-0000-0000-000000000000}') --Check for the execution mode value IF (@execution_mode NOT IN (0,1,2,4,5,6)) BEGIN RAISERROR(34004, -1, -1, @execution_mode) RETURN (1) END IF (@schedule_uid = '{00000000-0000-0000-0000-000000000000}' AND (@execution_mode & 4) = 4) BEGIN RAISERROR (34011, -1, -1, 'schedule_uid', 4) RETURN(1) END IF (@is_enabled = 1 AND @execution_mode = 0) BEGIN RAISERROR (34011, -1, -1, 'is_enabled', @execution_mode) RETURN(1) END -- Turn [nullable] empty string parameters into NULLs IF @condition_name = '' SELECT @condition_name = NULL IF @policy_category = '' SELECT @policy_category = NULL IF @root_condition_name = '' SELECT @root_condition_name = NULL -- verify that the condition exists EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @condition_name = @condition_name OUTPUT, @condition_id = @condition_id OUTPUT IF (@retval <> 0) RETURN(1) -- convert @object_set into id if needed DECLARE @object_set_id INT DECLARE @object_set_facet_id INT IF (@object_set IS NOT NULL) BEGIN SELECT @object_set_id = object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name = @object_set IF @object_set_id IS NULL BEGIN -- TODO: RAISERROR that specified object set doesn't exist RAISERROR(N'specified object set does not exists', -1, -1) RETURN(1) -- Failure END ELSE BEGIN SELECT @object_set_facet_id = facet_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name = @object_set -- Ensure the object set has been created from the same facet that the policy condition has been created IF (@object_set_facet_id <> (SELECT facet_id FROM msdb.dbo.syspolicy_conditions_internal WHERE condition_id = @condition_id)) BEGIN -- TODO: RAISEERROR that specified object_set isn't created from the facet that the policy condition has been created from RAISERROR(N'specified object set does not match facet the policy condition was created off', -1, -1) RETURN(1) -- Failure END END END IF (@root_condition_name IS NOT NULL) OR (@root_condition_id IS NOT NULL) BEGIN -- verify that the root condition exists EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @condition_name = @root_condition_name OUTPUT, @condition_id = @root_condition_id OUTPUT IF (@retval <> 0) RETURN(1) -- Check execution mode for compatibility with root_condition IF (@execution_mode = 1) OR (@execution_mode = 2) -- Enforce or Check on Change BEGIN RAISERROR (34011, -1, -1, 'root_condition', @execution_mode) RETURN(1) END END -- verify schedule IF (@schedule_uid != '{00000000-0000-0000-0000-000000000000}') BEGIN IF NOT EXISTS (SELECT * FROM msdb.dbo.sysschedules WHERE schedule_uid = @schedule_uid) BEGIN RAISERROR(14365, -1, -1) RETURN(1) -- Failure END END -- convert group_name into id if needed DECLARE @policy_category_id INT IF ( (@policy_category IS NOT NULL) ) BEGIN IF NOT EXISTS (SELECT * from msdb.dbo.syspolicy_policy_categories WHERE name = @policy_category) BEGIN RAISERROR(34015, -1, -1,@policy_category) RETURN(1) -- Failure END ELSE SELECT @policy_category_id = policy_category_id FROM msdb.dbo.syspolicy_policy_categories WHERE name = @policy_category END INSERT INTO msdb.dbo.syspolicy_policies_internal (name, execution_mode, schedule_uid, policy_category_id, description, help_text, help_link, condition_id, root_condition_id, object_set_id, is_enabled) VALUES (@name, @execution_mode, @schedule_uid, @policy_category_id, @description, @help_text, @help_link, @condition_id, @root_condition_id, @object_set_id, @is_enabled) SELECT @retval = @@error SET @policy_id = SCOPE_IDENTITY() RETURN(@retval) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_verify_policy_identifiers]...' GO ----------------------------------------------------------- -- This procedure verifies if a policy definition exists -- The caller can pass either the name or the id ----------------------------------------------------------- CREATE PROCEDURE [dbo].[sp_syspolicy_verify_policy_identifiers] @name sysname = NULL OUTPUT, @policy_id int = NULL OUTPUT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END IF ((@name IS NULL) AND (@policy_id IS NULL)) OR ((@name IS NOT NULL) AND (@policy_id IS NOT NULL)) BEGIN RAISERROR(14524, -1, -1, '@name', '@policy_id') RETURN(1) -- Failure END -- Check id IF (@policy_id IS NOT NULL) BEGIN SELECT @name = name FROM msdb.dbo.syspolicy_policies WHERE (policy_id = @policy_id) -- the view would take care of all the permissions issues. IF (@name IS NULL) BEGIN DECLARE @policy_id_as_char VARCHAR(36) SELECT @policy_id_as_char = CONVERT(VARCHAR(36), @policy_id) RAISERROR(14262, -1, -1, '@policy_id', @policy_id_as_char) RETURN(1) -- Failure END END ELSE -- Check name IF (@name IS NOT NULL) BEGIN -- get the corresponding policy_id (if the policy exists) SELECT @policy_id = policy_id FROM msdb.dbo.syspolicy_policies WHERE (name = @name) -- the view would take care of all the permissions issues. IF (@policy_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END END RETURN (0) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_rename_policy]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_rename_policy] @name sysname = NULL, @policy_id int = NULL, @new_name sysname = NULL AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END IF (@new_name IS NULL or LEN(@new_name) = 0) BEGIN RAISERROR(21263, -1, -1, '@new_name') RETURN(1) -- Failure END DECLARE @retval INT EXEC @retval = sp_syspolicy_verify_policy_identifiers @name, @policy_id OUTPUT IF (@retval <> 0) RETURN (1) UPDATE msdb.[dbo].[syspolicy_policies_internal] SET name = @new_name WHERE policy_id = @policy_id SELECT @retval = @@error RETURN(@retval) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_update_policy]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_update_policy] @name sysname = NULL, @policy_id int = NULL, @condition_id int=NULL, @condition_name sysname = NULL, @execution_mode int=NULL, @policy_category sysname = NULL, @schedule_uid uniqueidentifier = NULL, @description nvarchar(max) = NULL, @help_text nvarchar(4000) = NULL, @help_link nvarchar(2083) = NULL, @root_condition_id int = -1, @root_condition_name sysname = NULL, @object_set_id int = -1, @object_set sysname = NULL, @is_enabled bit = NULL WITH EXECUTE AS OWNER AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END --Check for the execution mode value IF (((@execution_mode IS NOT NULL)) AND (@execution_mode NOT IN (0,1,2,4,5,6))) BEGIN RAISERROR(34004, -1, -1, @execution_mode) RETURN (1) END -- Turn [nullable] empty string parameters into NULLs IF @name = '' SELECT @name = NULL IF @condition_name = '' SELECT @condition_name = NULL IF @root_condition_name = '' BEGIN SELECT @root_condition_name = NULL IF @root_condition_id = -1 -- root_condition is being reset SELECT @root_condition_id = NULL END IF @object_set = '' BEGIN SELECT @object_set = NULL IF @object_set_id = -1 -- object_set is being reset SELECT @object_set_id = NULL END DECLARE @retval INT EXEC @retval = msdb.dbo.sp_syspolicy_verify_policy_identifiers @name, @policy_id OUTPUT IF (@retval <> 0) RETURN (1) SELECT @execution_mode = ISNULL(@execution_mode, execution_mode), @is_enabled = ISNULL(@is_enabled, is_enabled) FROM msdb.dbo.syspolicy_policies WHERE policy_id = @policy_id IF(@condition_id IS NOT NULL or @condition_name IS NOT NULL) BEGIN EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @condition_name = @condition_name OUTPUT, @condition_id = @condition_id OUTPUT IF (@retval <> 0) RETURN (1) END IF((@root_condition_id IS NOT NULL and @root_condition_id != -1) or @root_condition_name IS NOT NULL) BEGIN IF (@root_condition_id = -1 and @root_condition_name IS NOT NULL) SET @root_condition_id = NULL EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @condition_name = @root_condition_name OUTPUT, @condition_id = @root_condition_id OUTPUT IF (@retval <> 0) RETURN (1) END SET @schedule_uid = ISNULL (@schedule_uid, '{00000000-0000-0000-0000-000000000000}') IF (@schedule_uid = '{00000000-0000-0000-0000-000000000000}' AND (@execution_mode & 4) = 4) BEGIN RAISERROR (34011, -1, -1, 'schedule_uid', 4) RETURN(1) END IF (@is_enabled = 1 AND @execution_mode = 0) BEGIN RAISERROR (34011, -1, -1, 'is_enabled', @execution_mode) RETURN(1) END IF (@schedule_uid != '{00000000-0000-0000-0000-000000000000}') BEGIN -- verify the schedule exists IF NOT EXISTS (SELECT schedule_id FROM msdb.dbo.sysschedules WHERE schedule_uid = @schedule_uid) BEGIN RAISERROR (14365, -1, -1) RETURN(1) END END DECLARE @object_set_facet_id INT IF ((@object_set_id IS NOT NULL and @object_set_id != -1) or @object_set IS NOT NULL) BEGIN IF (@object_set_id = -1 and @object_set IS NOT NULL) SET @object_set_id = NULL EXEC @retval = msdb.dbo.sp_syspolicy_verify_object_set_identifiers @name = @object_set OUTPUT, @object_set_id = @object_set_id OUTPUT IF (@retval <> 0) RETURN (1) SELECT @object_set_facet_id = facet_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name = @object_set -- Ensure the object set has been created from the same facet that the policy condition has been created IF (@object_set_facet_id <> (SELECT facet_id FROM msdb.dbo.syspolicy_conditions_internal WHERE condition_id = @condition_id)) BEGIN -- TODO: RAISEERROR that specified object_set isn't created from the facet that the policy condition has been created from RAISERROR(N'specified object set does not match facet the policy condition was created off', -1, -1) RETURN(1) -- Failure END END DECLARE @policy_category_id INT SET @policy_category_id = NULL BEGIN TRANSACTION DECLARE @old_policy_category_id INT SELECT @old_policy_category_id = policy_category_id FROM syspolicy_policies WHERE policy_id = @policy_id IF ( (@policy_category IS NOT NULL and @policy_category != '') ) BEGIN IF NOT EXISTS (SELECT * from syspolicy_policy_categories WHERE name = @policy_category) BEGIN RAISERROR(34015, -1, -1,@policy_category) RETURN(1) -- Failure END ELSE SELECT @policy_category_id = policy_category_id FROM msdb.dbo.syspolicy_policy_categories WHERE name = @policy_category END -- If the caller gave us an empty string for the -- @policy_category, then that means to remove the group. DECLARE @new_policy_category_id INT SELECT @new_policy_category_id = @old_policy_category_id IF ( (@policy_category = '') ) SELECT @new_policy_category_id = NULL ELSE IF (@policy_category_id IS NOT NULL) SELECT @new_policy_category_id = @policy_category_id UPDATE msdb.dbo.syspolicy_policies_internal SET condition_id = ISNULL(@condition_id, condition_id), root_condition_id = CASE @root_condition_id WHEN -1 THEN root_condition_id ELSE @root_condition_id END, execution_mode = ISNULL(@execution_mode, execution_mode ), schedule_uid = @schedule_uid, policy_category_id = @new_policy_category_id, description = ISNULL(@description, description), help_text = ISNULL(@help_text, help_text), help_link = ISNULL(@help_link, help_link), is_enabled = ISNULL(@is_enabled, is_enabled), object_set_id = CASE @object_set_id WHEN -1 THEN object_set_id ELSE @object_set_id END WHERE policy_id = @policy_id COMMIT TRANSACTION RETURN (0) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_policy]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_delete_policy] @name sysname = NULL, @policy_id int = NULL WITH EXECUTE AS OWNER AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval INT EXEC @retval = sp_syspolicy_verify_policy_identifiers @name, @policy_id OUTPUT IF (@retval <> 0) RETURN (1) DELETE msdb.dbo.syspolicy_policies_internal WHERE policy_id = @policy_id RETURN (0) END GO IF NOT EXISTS (SELECT * FROM sys.types where name = 'syspolicy_target_filters_type') BEGIN PRINT 'Creating type [dbo].[syspolicy_target_filters_type]...' CREATE TYPE [dbo].[syspolicy_target_filters_type] AS TABLE ( target_filter_id int, policy_id int, type sysname NOT NULL, filter nvarchar(max) NOT NULL, type_skeleton sysname NOT NULL ) END GO PRINT 'Creating function [dbo].[syspolicy_fn_get_bad_filters]...' GO -- This function returns filters that are not supported -- It is used to prevent unsupported filters from being -- created. It will only reject well formed filters, in -- other words it will not perform a full syntax check. CREATE FUNCTION [dbo].[syspolicy_fn_get_bad_filters] ( @inserted [dbo].[syspolicy_target_filters_type] READONLY ) RETURNS TABLE AS RETURN ( SELECT filter FROM @inserted WHERE -- do not accept filters for the next level filter LIKE N'Server/%/%\[@%=%\]%' ESCAPE '\' AND -- take out cases when the property contains the pattern filter NOT LIKE 'Server/%\[%\[%\]%\]%' ESCAPE '\' ) GO --------------------------------------------------------------- -- Target Set object --------------------------------------------------------------- IF OBJECT_ID ('[dbo].[syspolicy_target_sets_internal]') IS NULL BEGIN PRINT 'Creating table [dbo].[syspolicy_target_sets_internal]...' CREATE TABLE [dbo].[syspolicy_target_sets_internal] ( target_set_id int NOT NULL IDENTITY(1,1), object_set_id int NOT NULL, type_skeleton nvarchar(440) NOT NULL, type sysname NOT NULL, enabled bit NOT NULL, -- TODO: Verify if the primary access method of this table is based on policy_id then perhaps the clustered intdex should be on the policy id? CONSTRAINT [PK_syspolicy_target_sets] PRIMARY KEY CLUSTERED (target_set_id), ) ALTER TABLE [dbo].[syspolicy_target_sets_internal] ADD CONSTRAINT [FK_syspolicy_target_sets_syspolicy_object_sets] FOREIGN KEY(object_set_id) REFERENCES [dbo].[syspolicy_object_sets_internal] (object_set_id) ON DELETE CASCADE CREATE UNIQUE INDEX [UX_syspolicy_target_sets] ON [dbo].[syspolicy_target_sets_internal](object_set_id, type_skeleton) END GO PRINT 'Creating view [dbo].[syspolicy_target_sets]...' GO CREATE VIEW [dbo].[syspolicy_target_sets] AS SELECT target_set_id, object_set_id, type_skeleton, type, enabled FROM [dbo].[syspolicy_target_sets_internal] GO IF OBJECT_ID ('[dbo].[syspolicy_target_set_levels_internal]') IS NULL BEGIN PRINT 'Creating table [dbo].[syspolicy_target_set_levels_internal]...' CREATE TABLE [dbo].[syspolicy_target_set_levels_internal] ( target_set_level_id int NOT NULL IDENTITY(1,1), target_set_id int NOT NULL, type_skeleton nvarchar(440) NOT NULL, condition_id int NULL, level_name sysname NOT NULL, CONSTRAINT [PK_syspolicy_target_set_levels_internal] PRIMARY KEY CLUSTERED (target_set_level_id), ) ALTER TABLE [dbo].[syspolicy_target_set_levels_internal] ADD CONSTRAINT [FK_syspolicy_levels_target_sets] FOREIGN KEY(target_set_id) REFERENCES [dbo].[syspolicy_target_sets_internal] (target_set_id) ON DELETE CASCADE ALTER TABLE [dbo].[syspolicy_target_set_levels_internal] ADD CONSTRAINT [FK_syspolicy_levels_conditions] FOREIGN KEY(condition_id) REFERENCES [dbo].[syspolicy_conditions_internal] (condition_id) CREATE UNIQUE INDEX [UX_syspolicy_levels] ON [dbo].[syspolicy_target_sets_internal](target_set_id, type_skeleton) END GO PRINT 'Creating view [dbo].[syspolicy_target_set_levels]...' GO CREATE VIEW [dbo].[syspolicy_target_set_levels] AS SELECT target_set_level_id, target_set_id, type_skeleton, condition_id, level_name FROM [dbo].[syspolicy_target_set_levels_internal] GO PRINT 'Creating procedure [dbo].[sp_syspolicy_add_target_set]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_add_target_set] @object_set_id int = NULL, @object_set_name sysname = NULL, @type_skeleton nvarchar(max), @type sysname, @enabled bit, @target_set_id int OUTPUT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval INT EXEC @retval = dbo.sp_syspolicy_verify_object_set_identifiers @name = @object_set_name OUTPUT, @object_set_id = @object_set_id OUTPUT if( @retval <> 0) RETURN(1) INSERT INTO msdb.[dbo].[syspolicy_target_sets_internal] (object_set_id, type_skeleton, type, enabled) VALUES (@object_set_id, @type_skeleton, @type, @enabled) SELECT @retval = @@error SET @target_set_id = SCOPE_IDENTITY() RETURN(@retval) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_update_target_set]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_update_target_set] @target_set_id int, @enabled bit AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END UPDATE [msdb].[dbo].[syspolicy_target_sets_internal] SET enabled = @enabled WHERE target_set_id = @target_set_id IF (@@ROWCOUNT = 0) BEGIN DECLARE @target_set_id_as_char VARCHAR(36) SELECT @target_set_id_as_char = CONVERT(VARCHAR(36), @target_set_id) RAISERROR(14262, -1, -1, '@target_set_id', @target_set_id_as_char) RETURN (1) END RETURN (0) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_target_set]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_delete_target_set] @target_set_id int AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DELETE msdb.[dbo].[syspolicy_target_sets_internal] WHERE target_set_id = @target_set_id IF (@@ROWCOUNT = 0) BEGIN DECLARE @target_set_id_as_char VARCHAR(36) SELECT @target_set_id_as_char = CONVERT(VARCHAR(36), @target_set_id) RAISERROR(14262, -1, -1, '@target_set_id', @target_set_id_as_char) RETURN (1) END RETURN (0) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_add_target_set_level]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_add_target_set_level] @target_set_id int, @type_skeleton nvarchar(max), @condition_id int = NULL, @condition_name sysname = NULL, @level_name sysname, @target_set_level_id int OUTPUT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval INT IF NOT EXISTS (SELECT * FROM syspolicy_target_sets WHERE target_set_id = @target_set_id) RETURN (1) IF (@condition_name = '') SET @condition_name = NULL IF(@condition_id IS NOT NULL or @condition_name IS NOT NULL) BEGIN EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @condition_name = @condition_name OUTPUT, @condition_id = @condition_id OUTPUT IF (@retval <> 0) RETURN (1) END INSERT INTO msdb.[dbo].[syspolicy_target_set_levels_internal] (target_set_id, type_skeleton, condition_id, level_name) VALUES (@target_set_id, @type_skeleton, @condition_id, @level_name) SELECT @retval = @@error SET @target_set_level_id = SCOPE_IDENTITY() RETURN(@retval) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_update_target_set_level]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_update_target_set_level] @target_set_id int, @type_skeleton nvarchar(max), @condition_id int = NULL, @condition_name sysname = NULL AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval int IF @condition_name = '' SET @condition_name = NULL IF(@condition_id IS NOT NULL or @condition_name IS NOT NULL) BEGIN EXEC @retval = msdb.dbo.sp_syspolicy_verify_condition_identifiers @condition_name = @condition_name OUTPUT, @condition_id = @condition_id OUTPUT IF (@retval <> 0) RETURN (1) END UPDATE msdb.[dbo].[syspolicy_target_set_levels_internal] SET condition_id = @condition_id WHERE target_set_id = @target_set_id AND type_skeleton = @type_skeleton IF (@@ROWCOUNT = 0) BEGIN DECLARE @id nvarchar(max) SET @id = '@target_set_id='+LTRIM(STR(@target_set_id))+' @type_skeleton='''+@type_skeleton+'''' RAISERROR(14262, -1, -1, 'Target Set Level', @id) RETURN (1) END RETURN (0) END GO PRINT 'Creating function [dbo].[syspolicy_fn_eventing_filter]' GO CREATE FUNCTION [dbo].[syspolicy_fn_eventing_filter] (@target_set_id INT) RETURNS INT AS BEGIN DECLARE @cnt int, @level sysname, @condition_id int, @ret int SELECT @cnt = count(*) FROM msdb.dbo.syspolicy_target_set_levels WHERE target_set_id = @target_set_id AND condition_id IS NOT NULL IF @cnt = 0 RETURN 1 ELSE IF @cnt > 1 RETURN 0 ELSE BEGIN SELECT @level = level_name, @condition_id = condition_id FROM msdb.dbo.syspolicy_target_set_levels WHERE target_set_id = @target_set_id AND condition_id IS NOT NULL IF @level != 'Database' RETURN 0 IF @condition_id IS NOT NULL BEGIN IF EXISTS (SELECT * FROM msdb.dbo.syspolicy_conditions WHERE condition_id = @condition_id AND (1 = CONVERT(xml, expression).exist('//FunctionType/text()[.="ExecuteSql"]') OR 1 = CONVERT(xml, expression).exist('//FunctionType/text()[.="ExecuteWql"]') ) ) RETURN 0 END SELECT @ret = is_name_condition FROM msdb.dbo.syspolicy_conditions WHERE condition_id = @condition_id END RETURN @ret END GO PRINT 'Creating function [dbo].[syspolicy_fn_filter_complete]' GO CREATE FUNCTION [dbo].[syspolicy_fn_filter_complete] (@target_set_id INT) RETURNS INT AS BEGIN DECLARE @target_set_skeleton nvarchar(max), @skeleton nvarchar(max), @level sysname, @dummy nvarchar(max), @ret int, @i int, @p int SELECT @target_set_skeleton = type_skeleton, @i=0, @p=CHARINDEX('/',type_skeleton) FROM msdb.dbo.syspolicy_target_sets WHERE target_set_id = @target_set_id IF @@ROWCOUNT != 1 RETURN 0 IF @target_set_skeleton = 'Server' RETURN 1 -- Count the number of levels in the skeleton past the root WHILE (@p <> 0) BEGIN SET @i = @i + 1 SET @p = CHARINDEX('/', @target_set_skeleton, @p + 1) END -- Compare the number of levels in the skeleton with those in TSL IF (@i = (SELECT COUNT(*) FROM msdb.dbo.syspolicy_target_set_levels WHERE target_set_id = @target_set_id)) RETURN 1 RETURN 0 END GO PRINT 'Creating trigger [dbo].[syspolicy_insert_target_set_level_trigger] on [dbo].[syspolicy_target_set_levels_internal]' GO CREATE TRIGGER [dbo].[syspolicy_insert_target_set_level_trigger] ON [dbo].[syspolicy_target_set_levels_internal] FOR INSERT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END DECLARE @update_notifications INT DECLARE @update_ddl_trigger INT SET @update_notifications = 0 SET @update_ddl_trigger = 0 DECLARE @level sysname SET @level = NULL -- Don't allow setting non-db levels for runtime policies SELECT TOP 1 @level = i.level_name FROM inserted i JOIN dbo.syspolicy_target_sets s ON (i.target_set_id = s.target_set_id) JOIN msdb.dbo.syspolicy_object_sets os ON s.object_set_id = os.object_set_id JOIN msdb.dbo.syspolicy_policies p ON (os.object_set_id = p.object_set_id) WHERE 1 = dbo.syspolicy_fn_filter_complete (i.target_set_id) AND ((p.execution_mode & 3) > 0 AND 0 = dbo.syspolicy_fn_eventing_filter (i.target_set_id)) IF @level IS NOT NULL BEGIN RAISERROR(34016, -1, -1, @level) ROLLBACK TRANSACTION END SELECT @update_notifications = SUM (p.execution_mode & 2), @update_ddl_trigger = SUM (p.execution_mode & 1) FROM inserted i JOIN dbo.syspolicy_target_sets s ON (i.target_set_id = s.target_set_id) JOIN msdb.dbo.syspolicy_object_sets_internal os ON (s.object_set_id = os.object_set_id) JOIN msdb.dbo.syspolicy_policies p ON (os.object_set_id = p.object_set_id) WHERE 1 = dbo.syspolicy_fn_filter_complete (i.target_set_id) AND ((p.execution_mode & 3) > 0 AND p.is_enabled = 1 AND 1 = dbo.syspolicy_fn_eventing_filter (i.target_set_id)) IF (@update_ddl_trigger > 0) EXEC sys.sp_syspolicy_update_ddl_trigger IF (@update_notifications > 0) EXEC sys.sp_syspolicy_update_event_notification END GO PRINT 'Creating trigger [dbo].[syspolicy_update_target_set_level_trigger] on [dbo].[syspolicy_target_set_levels_internal]' GO CREATE TRIGGER [dbo].[syspolicy_update_target_set_level_trigger] ON [dbo].[syspolicy_target_set_levels_internal] FOR UPDATE AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END DECLARE @update_notifications INT DECLARE @update_ddl_trigger INT SET @update_notifications = 0 SET @update_ddl_trigger = 0 IF UPDATE(condition_id) BEGIN DECLARE @level sysname SET @level = NULL -- Don't allow setting non-db levels for runtime policies SELECT TOP 1 @level = i.level_name FROM inserted i JOIN dbo.syspolicy_target_sets s ON (i.target_set_id = s.target_set_id) JOIN msdb.dbo.syspolicy_object_sets os ON s.object_set_id = os.object_set_id JOIN msdb.dbo.syspolicy_policies p ON (os.object_set_id = p.object_set_id) WHERE 1 = dbo.syspolicy_fn_filter_complete (i.target_set_id) AND ((p.execution_mode & 3) > 0 AND 0 = dbo.syspolicy_fn_eventing_filter (i.target_set_id)) IF @level IS NOT NULL BEGIN RAISERROR(34016, -1, -1, @level) ROLLBACK TRANSACTION END END SELECT @update_notifications = SUM (p.execution_mode & 2), @update_ddl_trigger = SUM (p.execution_mode & 1) FROM inserted i JOIN dbo.syspolicy_target_sets s ON (i.target_set_id = s.target_set_id) JOIN msdb.dbo.syspolicy_object_sets os ON s.object_set_id = os.object_set_id JOIN msdb.dbo.syspolicy_policies p ON (os.object_set_id = p.object_set_id) WHERE 1 = dbo.syspolicy_fn_filter_complete (i.target_set_id) AND ((p.execution_mode & 3) > 0 AND p.is_enabled = 1 AND 1 = dbo.syspolicy_fn_eventing_filter (i.target_set_id)) IF (@update_ddl_trigger > 0) EXEC sys.sp_syspolicy_update_ddl_trigger IF (@update_notifications > 0) EXEC sys.sp_syspolicy_update_event_notification END GO PRINT 'Creating trigger [dbo].[syspolicy_insert_target_set_trigger] on [dbo].[syspolicy_target_sets_internal]' GO CREATE TRIGGER [dbo].[syspolicy_insert_target_set_trigger] ON [dbo].[syspolicy_target_sets_internal] FOR INSERT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END DECLARE @update_notifications INT DECLARE @update_ddl_trigger INT SET @update_notifications = 0 SET @update_ddl_trigger = 0 -- Only need to check Server TargetSets, as they don't have levels SELECT @update_notifications = SUM (p.execution_mode & 2), @update_ddl_trigger = SUM (p.execution_mode & 1) FROM inserted i JOIN msdb.dbo.syspolicy_object_sets_internal os ON i.object_set_id = os.object_set_id JOIN msdb.dbo.syspolicy_policies p ON (os.object_set_id = p.object_set_id) WHERE i.type = 'SERVER' AND ((p.execution_mode & 3) > 0 AND p.is_enabled = 1) IF (@update_ddl_trigger > 0) EXEC sys.sp_syspolicy_update_ddl_trigger IF (@update_notifications > 0) EXEC sys.sp_syspolicy_update_event_notification END GO PRINT 'Creating trigger [dbo].[syspolicy_delete_target_set_trigger] on [dbo].[syspolicy_target_sets_internal]' GO CREATE TRIGGER [dbo].[syspolicy_delete_target_set_trigger] ON [dbo].[syspolicy_target_sets_internal] FOR DELETE AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END DECLARE @update_notifications INT DECLARE @update_ddl_trigger INT SET @update_notifications = 0 SET @update_ddl_trigger = 0 -- If this is cascade delete, there will be no policies to join SELECT @update_notifications = SUM (p.execution_mode & 2), @update_ddl_trigger = SUM (p.execution_mode & 1) FROM deleted d JOIN msdb.dbo.syspolicy_object_sets_internal os ON d.object_set_id = os.object_set_id JOIN msdb.dbo.syspolicy_policies p ON (os.object_set_id = p.object_set_id) WHERE ((p.execution_mode & 3) > 0 AND p.is_enabled = 1) IF (@update_ddl_trigger > 0) EXEC sys.sp_syspolicy_update_ddl_trigger IF (@update_notifications > 0) EXEC sys.sp_syspolicy_update_event_notification END GO --------------------------------------------------------------- -- Policy category subscription object --------------------------------------------------------------- IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_policy_category_subscriptions_internal') BEGIN PRINT 'Creating table [dbo].[syspolicy_policy_category_subscriptions_internal]...'; CREATE TABLE [dbo].[syspolicy_policy_category_subscriptions_internal] ( policy_category_subscription_id int IDENTITY(1,1), target_type sysname NOT NULL, target_object sysname NOT NULL, policy_category_id int NOT NULL, CONSTRAINT [PK_syspolicy_policy_category_subscriptions] PRIMARY KEY CLUSTERED (policy_category_subscription_id ASC) ); CREATE UNIQUE INDEX [UX_syspolicy_policy_category_subscriptions] ON [dbo].[syspolicy_policy_category_subscriptions_internal](policy_category_id, target_object, target_type) ALTER TABLE [dbo].[syspolicy_policy_category_subscriptions_internal] ADD CONSTRAINT [FK_syspolicy_policy_category_subscriptions_syspolicy_policy_categories] FOREIGN KEY(policy_category_id) REFERENCES [dbo].[syspolicy_policy_categories_internal] (policy_category_id) ON DELETE CASCADE; END GO PRINT 'Creating view [dbo].[syspolicy_policy_category_subscriptions]...' GO CREATE VIEW [dbo].[syspolicy_policy_category_subscriptions] AS SELECT policy_category_subscription_id, target_type, target_object, policy_category_id FROM [dbo].[syspolicy_policy_category_subscriptions_internal] GO PRINT 'Creating procedure [dbo].[sp_syspolicy_add_policy_category_subscription]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_add_policy_category_subscription] @target_type sysname, @target_object sysname, @policy_category sysname, @policy_category_subscription_id int = NULL OUTPUT WITH EXECUTE AS OWNER AS BEGIN DECLARE @retval int IF(@target_type IS NOT NULL) BEGIN IF(@target_type <> 'DATABASE') BEGIN RAISERROR(34018,-1,-1,@target_type); RETURN(1) END END IF(NOT EXISTS(SELECT * FROM sys.databases WHERE name=@target_object)) BEGIN RAISERROR(34019,-1,-1,@target_object); RETURN(1) END -- convert category_name into id if needed DECLARE @policy_category_id INT BEGIN TRANSACTION IF ( (@policy_category IS NOT NULL AND @policy_category != '') ) BEGIN IF NOT EXISTS (SELECT * from syspolicy_policy_categories WHERE name = @policy_category) BEGIN INSERT INTO syspolicy_policy_categories_internal(name) VALUES (@policy_category) SELECT @policy_category_id = SCOPE_IDENTITY() END ELSE SELECT @policy_category_id = policy_category_id FROM syspolicy_policy_categories WHERE name = @policy_category END INSERT INTO msdb.[dbo].[syspolicy_policy_category_subscriptions_internal] (target_type, target_object, policy_category_id) VALUES (@target_type, @target_object, @policy_category_id) SELECT @retval = @@error SET @policy_category_subscription_id = SCOPE_IDENTITY() COMMIT TRANSACTION RETURN(@retval) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_update_policy_category_subscription]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_update_policy_category_subscription] @policy_category_subscription_id int, @target_type sysname = NULL, @target_object sysname = NULL, @policy_category sysname = NULL AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END -- Turn [nullable] empty string parameters into NULLs IF @target_type = '' SELECT @target_type = NULL IF @target_object = '' SELECT @target_object = NULL IF @policy_category = '' SELECT @policy_category = NULL IF(@target_type IS NOT NULL) BEGIN IF(LOWER(@target_type) <> 'database') BEGIN RAISERROR(34018,-1,-1,@target_type); RETURN(1) END END IF(@target_object IS NOT NULL AND NOT EXISTS(SELECT * FROM sys.databases WHERE name=@target_object)) BEGIN RAISERROR(34019,-1,-1,@target_object); RETURN(1) END DECLARE @policy_category_id INT SET @policy_category_id = NULL BEGIN TRANSACTION DECLARE @old_policy_category_id INT SET @old_policy_category_id = NULL IF ( (@policy_category IS NOT NULL) ) BEGIN IF NOT EXISTS (SELECT * from syspolicy_policy_categories WHERE name = @policy_category) BEGIN -- add a new policy category INSERT INTO syspolicy_policy_categories_internal(name) VALUES (@policy_category) SELECT @policy_category_id = SCOPE_IDENTITY() SELECT @old_policy_category_id = policy_category_id FROM syspolicy_policy_category_subscriptions WHERE policy_category_subscription_id = @policy_category_subscription_id END ELSE SELECT @policy_category_id = policy_category_id FROM syspolicy_policy_categories WHERE name = @policy_category END DECLARE @group_usage_count INT SELECT @group_usage_count = COUNT(*) FROM syspolicy_policy_category_subscriptions WHERE policy_category_id = @old_policy_category_id SELECT @group_usage_count = @group_usage_count + COUNT(*) FROM syspolicy_policies WHERE policy_category_id = @old_policy_category_id UPDATE msdb.[dbo].[syspolicy_policy_category_subscriptions_internal] SET target_type = ISNULL(@target_type, target_type), target_object = ISNULL(@target_object, target_object), policy_category_id = ISNULL(@policy_category_id, policy_category_id) WHERE policy_category_subscription_id = @policy_category_subscription_id IF (@@ROWCOUNT = 0) BEGIN DECLARE @policy_category_subscription_id_as_char VARCHAR(36) SELECT @policy_category_subscription_id_as_char = CONVERT(VARCHAR(36), @policy_category_subscription_id) RAISERROR(14262, -1, -1, '@policy_category_subscription_id', @policy_category_subscription_id_as_char) ROLLBACK TRANSACTION RETURN(1) -- Failure END -- delete the old entry if it was used only by this policy DELETE syspolicy_policy_categories_internal WHERE policy_category_id = @old_policy_category_id AND 1 = @group_usage_count COMMIT TRANSACTION RETURN (0) END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_policy_category_subscription]...' GO CREATE PROCEDURE [dbo].[sp_syspolicy_delete_policy_category_subscription] @policy_category_subscription_id int WITH EXECUTE AS OWNER AS BEGIN DECLARE @old_policy_category_id INT SELECT @old_policy_category_id = policy_category_id FROM dbo.syspolicy_policy_category_subscriptions WHERE policy_category_subscription_id = @policy_category_subscription_id DECLARE @group_usage_count INT SELECT @group_usage_count = COUNT(name) FROM syspolicy_policies pd WHERE pd.policy_category_id = @old_policy_category_id DECLARE @subscription_group_usage_count INT SELECT @subscription_group_usage_count = COUNT(*) FROM syspolicy_policy_category_subscriptions WHERE policy_category_id = @old_policy_category_id SELECT @group_usage_count = @group_usage_count + @subscription_group_usage_count DELETE msdb.dbo.syspolicy_policy_category_subscriptions_internal WHERE policy_category_subscription_id = @policy_category_subscription_id IF (@@ROWCOUNT = 0) BEGIN DECLARE @policy_category_subscription_id_as_char VARCHAR(36) SELECT @policy_category_subscription_id_as_char = CONVERT(VARCHAR(36), @policy_category_subscription_id) RAISERROR(14262, -1, -1, '@policy_category_subscription_id', @policy_category_subscription_id_as_char) RETURN(1) -- Failure END RETURN (0) END GO GO IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_system_health_state_internal') BEGIN PRINT 'Creating table [dbo].[syspolicy_system_health_state_internal]...' CREATE TABLE [dbo].[syspolicy_system_health_state_internal]( health_state_id bigint IDENTITY PRIMARY KEY CLUSTERED, policy_id int NOT NULL REFERENCES [dbo].[syspolicy_policies_internal], last_run_date datetime NOT NULL, target_query_expression_with_id nvarchar(400) NOT NULL, target_query_expression nvarchar(max) NOT NULL, result bit NOT NULL); CREATE INDEX IX_syspolicy_system_health_state_internal_policy_id ON [dbo].[syspolicy_system_health_state_internal](policy_id); CREATE INDEX IX_syspolicy_system_health_state_internal_target_query_expression_with_id ON [dbo].[syspolicy_system_health_state_internal](target_query_expression_with_id); END GO CREATE VIEW [dbo].[syspolicy_system_health_state] AS SELECT health_state_id, policy_id, last_run_date, target_query_expression_with_id, target_query_expression, result FROM [dbo].[syspolicy_system_health_state_internal] GO IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_policy_execution_history_internal') BEGIN PRINT 'Creating table [dbo].[syspolicy_policy_execution_history_internal]...'; CREATE TABLE syspolicy_policy_execution_history_internal ( history_id bigint IDENTITY PRIMARY KEY CLUSTERED, policy_id int NOT NULL REFERENCES syspolicy_policies_internal, start_date datetime NOT NULL DEFAULT (GETDATE()), end_date datetime NULL, result bit NOT NULL DEFAULT (0), is_full_run bit NOT NULL DEFAULT (1), exception_message nvarchar(max) NULL, exception nvarchar(max) NULL ); CREATE INDEX IX_syspolicy_policy_execution_history_internal_end_date_policy_id ON [dbo].[syspolicy_policy_execution_history_internal](policy_id, end_date); CREATE INDEX IX_syspolicy_policy_execution_history_internal_policy_id ON [dbo].[syspolicy_policy_execution_history_internal](policy_id); END PRINT 'Creating trigger [syspolicy_update_system_health_state]...' GO CREATE TRIGGER [syspolicy_update_system_health_state] ON [dbo].[syspolicy_policy_execution_history_internal] AFTER UPDATE AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END -- if the entire policy has been checked delete all entries -- regarding that policy DELETE FROM [dbo].[syspolicy_system_health_state_internal] WHERE policy_id in (SELECT policy_id FROM inserted WHERE is_full_run = 1) -- Note: in the queries below new records are added only for -- policies that are enabled for automation -- if the policy is evaluated against a single target -- delete the old entry DELETE FROM [dbo].[syspolicy_system_health_state_internal] WHERE policy_id in (SELECT i.policy_id FROM inserted i WHERE i.is_full_run = 0) AND target_query_expression_with_id in (SELECT target_query_expression_with_id FROM [dbo].[syspolicy_policy_execution_history_details_internal] d INNER JOIN inserted i2 ON i2.history_id = d.history_id WHERE i2.is_full_run = 0) -- insert the detail rows, but only for failures -- this is done both for the full runs and for the partial runs -- we will not insert anything if this is a ghost record, i.e. -- target_query_expression_with_id is null -- this will happen when we log prevent policies INSERT INTO [dbo].[syspolicy_system_health_state_internal] (policy_id, last_run_date, target_query_expression_with_id, target_query_expression, result) SELECT i.policy_id, d.execution_date, d.target_query_expression_with_id, d.target_query_expression, d.result FROM inserted i INNER JOIN [dbo].[syspolicy_policy_execution_history_details_internal] d on i.history_id = d.history_id INNER JOIN [dbo].[syspolicy_policies] p on i.policy_id = p.policy_id WHERE d.result = 0 AND p.is_enabled = 1 AND d.target_query_expression_with_id != N'' -- delete all the success detail rows with no expression -- these are rows inserted so that we can update the system health state -- make an exception if the global switch says we should keep those records IF( 0 = (SELECT ISNULL(convert(bit, current_value), 0) FROM msdb.dbo.syspolicy_configuration WHERE name = 'LogOnSuccess')) BEGIN DELETE FROM d FROM [dbo].[syspolicy_policy_execution_history_details_internal] d INNER JOIN inserted i ON i.history_id = d.history_id WHERE d.result_detail = N'' END END GO CREATE VIEW [dbo].[syspolicy_policy_execution_history] AS SELECT history_id, policy_id, start_date, end_date, result, exception_message, exception FROM [dbo].[syspolicy_policy_execution_history_internal] GO IF NOT EXISTS (SELECT * FROM sys.tables where name = 'syspolicy_policy_execution_history_details_internal') BEGIN PRINT 'Creating table [dbo].[syspolicy_policy_execution_history_details_internal]...'; CREATE TABLE syspolicy_policy_execution_history_details_internal ( detail_id bigint IDENTITY, history_id bigint NOT NULL REFERENCES syspolicy_policy_execution_history_internal ON DELETE CASCADE, target_query_expression nvarchar(4000) NOT NULL, target_query_expression_with_id nvarchar(4000) NOT NULL, execution_date datetime NOT NULL DEFAULT (GETDATE()), result bit NOT NULL, result_detail nvarchar(max) NULL, exception_message nvarchar(max) NULL, exception nvarchar(max) NULL, CONSTRAINT [PK_syspolicy_policy_execution_history_details_id] PRIMARY KEY CLUSTERED(history_id, detail_id) ) END GO CREATE VIEW [dbo].[syspolicy_policy_execution_history_details] AS SELECT detail_id, history_id, target_query_expression, execution_date, result, result_detail, exception_message, exception FROM [dbo].[syspolicy_policy_execution_history_details_internal] GO PRINT 'Creating procedure [dbo].[sp_syspolicy_log_policy_execution_start]...' GO CREATE PROC [dbo].[sp_syspolicy_log_policy_execution_start] @policy_id int, @is_full_run bit, @history_id bigint OUTPUT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole', 0 IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @ret int SET @history_id = 0 EXEC @ret = dbo.sp_syspolicy_verify_policy_identifiers NULL, @policy_id IF @ret <> 0 RETURN -1 INSERT syspolicy_policy_execution_history_internal (policy_id, is_full_run) VALUES (@policy_id, @is_full_run) SET @history_id = SCOPE_IDENTITY () END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_log_policy_execution_end]...' GO CREATE PROC [dbo].[sp_syspolicy_log_policy_execution_end] @history_id bigint, @result bit, @exception_message nvarchar(max) = NULL, @exception nvarchar(max) = NULL AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole', 0 IF ( 0!= @retval_check) BEGIN RETURN @retval_check END UPDATE syspolicy_policy_execution_history_internal SET result = @result, end_date = GETDATE(), exception_message = @exception_message, exception = @exception WHERE history_id = @history_id END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_log_policy_execution_detail]...' GO CREATE PROC [dbo].[sp_syspolicy_log_policy_execution_detail] @history_id bigint, @target_query_expression nvarchar(4000), @target_query_expression_with_id nvarchar(4000), @result bit, @result_detail nvarchar(max), @exception_message nvarchar(max) = NULL, @exception nvarchar(max) = NULL AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole', 0 IF ( 0!= @retval_check) BEGIN RETURN @retval_check END BEGIN TRANSACTION DECLARE @is_valid_entry INT -- take an update lock on this table first to prevent deadlock SELECT @is_valid_entry = count(*) FROM syspolicy_policy_execution_history_internal WITH (UPDLOCK) WHERE history_id = @history_id INSERT syspolicy_policy_execution_history_details_internal ( history_id, target_query_expression, target_query_expression_with_id, result, result_detail, exception_message, exception) VALUES ( @history_id, @target_query_expression, @target_query_expression_with_id, @result, @result_detail, @exception_message, @exception) IF( @@TRANCOUNT > 0) COMMIT END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_delete_policy_execution_history]...' GO CREATE PROC [dbo].[sp_syspolicy_delete_policy_execution_history] @policy_id int, @oldest_date datetime AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END IF @oldest_date IS NULL BEGIN IF (@policy_id IS NULL) DELETE syspolicy_policy_execution_history_internal ELSE DELETE syspolicy_policy_execution_history_internal WHERE policy_id = @policy_id END ELSE BEGIN IF (@policy_id IS NULL) DELETE syspolicy_policy_execution_history_internal WHERE start_date < @oldest_date ELSE DELETE syspolicy_policy_execution_history_internal WHERE policy_id = @policy_id AND start_date < @oldest_date END END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_mark_system]...' GO CREATE PROC dbo.sp_syspolicy_mark_system @type sysname, @name sysname=NULL, @object_id int=NULL, @marker bit=NULL AS BEGIN -- If @marker IS NULL simple return the state DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END DECLARE @retval int IF (UPPER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'POLICY') BEGIN EXEC @retval = sp_syspolicy_verify_policy_identifiers @name, @object_id OUTPUT IF (@retval <> 0) RETURN (1) IF @marker IS NULL BEGIN SELECT policy_id, name, is_system FROM syspolicy_policies_internal WHERE policy_id = @object_id END ELSE BEGIN UPDATE msdb.dbo.syspolicy_policies_internal SET is_system = @marker WHERE policy_id = @object_id END END ELSE IF (UPPER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'CONDITION') BEGIN EXEC @retval = sp_syspolicy_verify_condition_identifiers @name, @object_id OUTPUT IF (@retval <> 0) RETURN (1) IF @marker IS NULL BEGIN SELECT condition_id, name, is_system FROM syspolicy_conditions_internal WHERE condition_id = @object_id END ELSE BEGIN UPDATE msdb.dbo.syspolicy_conditions_internal SET is_system = @marker WHERE condition_id = @object_id END END ELSE IF (UPPER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'OBJECTSET') BEGIN EXEC @retval = sp_syspolicy_verify_object_set_identifiers @name, @object_id OUTPUT IF (@retval <> 0) RETURN (1) IF @marker IS NULL BEGIN SELECT object_set_id, object_set_name, is_system FROM syspolicy_object_sets_internal WHERE object_set_id = @object_id END ELSE BEGIN UPDATE msdb.dbo.syspolicy_object_sets_internal SET is_system = @marker WHERE object_set_id = @object_id END END ELSE BEGIN RAISERROR(14262, -1, -1, '@type', @type) RETURN(1) -- Failure END SELECT @retval = @@error RETURN(@retval) END GO ----------------------------------------------------------- -- event processing ----------------------------------------------------------- PRINT 'Creating function [dbo].[syspolicy_fn_get_type_name]...' GO CREATE FUNCTION [dbo].[syspolicy_fn_get_type_name](@event_type_name sysname) RETURNS sysname AS BEGIN RETURN (CASE LOWER(@event_type_name) WHEN 'procedure' THEN 'StoredProcedure' WHEN 'function' THEN 'UserDefinedFunction' WHEN 'type' THEN 'UserDefinedType' WHEN 'sql user' THEN 'User' WHEN 'certificate user' THEN 'User' WHEN 'asymmetric key user' THEN 'User' WHEN 'windows user' THEN 'User' WHEN 'group user' THEN 'User' WHEN 'application role' THEN 'ApplicationRole' ELSE UPPER(SUBSTRING(@event_type_name, 1,1)) + LOWER(SUBSTRING(@event_type_name, 2,LEN(@event_type_name))) END) END GO IF OBJECT_ID ('[dbo].[syspolicy_execution_internal]') IS NULL BEGIN PRINT 'Creating table [dbo].[syspolicy_execution_internal]...' CREATE TABLE [dbo].[syspolicy_execution_internal] ( policy_id int, synchronous bit, event_data xml) END GO IF OBJECT_ID ('[dbo].[syspolicy_execution_trigger]') IS NOT NULL BEGIN DROP TRIGGER [dbo].[syspolicy_execution_trigger] END GO CREATE TRIGGER [dbo].[syspolicy_execution_trigger] ON [dbo].[syspolicy_execution_internal] INSTEAD OF INSERT AS BEGIN DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN; END IF NOT EXISTS (SELECT * FROM inserted) RETURN DECLARE @policy_id int DECLARE @synchronous bit DECLARE @event_data xml DECLARE affected_policies CURSOR LOCAL FOR SELECT policy_id, synchronous, event_data FROM inserted OPEN affected_policies FETCH NEXT FROM affected_policies INTO @policy_id, @synchronous, @event_data DECLARE @err int SET @err = 0 WHILE (@@FETCH_STATUS = 0 AND (@synchronous = 0 OR @err = 0)) BEGIN DECLARE @pol_name sysname SELECT @pol_name = p.name FROM dbo.syspolicy_policies p WHERE p.policy_id = @policy_id IF (@synchronous = 0) BEGIN -- trace what policy is processing this event DECLARE @msg nvarchar(1000) SET @msg = N'Policy ''' + @pol_name + ''' was activated by an event.' RAISERROR(@msg, 1, 1) with log END -- execute the policy EXEC @err = msdb.sys.sp_syspolicy_execute_policy @policy_name =@pol_name, @event_data = @event_data, @synchronous = @synchronous -- move to the next policy if we're checking the policy -- or if we are in enforce mode and we haven't failed IF( @synchronous = 0 OR @err = 0) FETCH NEXT FROM affected_policies INTO @policy_id, @synchronous, @event_data END CLOSE affected_policies DEALLOCATE affected_policies END GO PRINT 'Creating procedure [dbo].[sp_syspolicy_dispatch_event]...' GO -- These settings are necessary to read XML. SET ANSI_NULLS ON SET ANSI_PADDING ON SET ANSI_WARNINGS ON SET ARITHABORT ON SET CONCAT_NULL_YIELDS_NULL ON SET NUMERIC_ROUNDABORT OFF SET QUOTED_IDENTIFIER ON GO -- procedure that processes an event and decides -- what binding should handle it CREATE PROCEDURE [dbo].[sp_syspolicy_dispatch_event] @event_data xml, @synchronous bit AS BEGIN -- disable these as the caller may not have SHOWPLAN permission SET STATISTICS XML OFF SET STATISTICS PROFILE OFF DECLARE @retval_check int; EXECUTE @retval_check = [dbo].[sp_syspolicy_check_membership] 'PolicyAdministratorRole' IF ( 0!= @retval_check) BEGIN RETURN @retval_check END IF ( @synchronous = 0) PRINT CONVERT(nvarchar(max), @event_data) DECLARE @event_type sysname DECLARE @object_type sysname DECLARE @database sysname DECLARE @mode int DECLARE @filter_expression nvarchar(4000) DECLARE @filter_expression_skeleton nvarchar(4000) SET @mode = (case @synchronous when 1 then 1 else 2 end) -- These settings are necessary to read XML. SET ANSI_NULLS ON SET ANSI_PADDING ON SET ANSI_WARNINGS ON SET ARITHABORT ON SET CONCAT_NULL_YIELDS_NULL ON SET NUMERIC_ROUNDABORT OFF SET QUOTED_IDENTIFIER ON SET NOCOUNT ON SELECT @event_type = T.c.value('(EventType/text())[1]', 'sysname') , @database = T.c.value('(DatabaseName/text())[1]', 'sysname') , @object_type = T.c.value('(ObjectType/text())[1]', 'sysname') FROM @event_data.nodes('/EVENT_INSTANCE') T(c) -- we are going to ignore events that affect subobjects IF (@event_type = N'ALTER_DATABASE' AND 1 = @event_data.exist('EVENT_INSTANCE/AlterDatabaseActionList')) OR (@event_type = N'ALTER_TABLE' AND 1 = @event_data.exist('EVENT_INSTANCE/AlterTableActionList')) BEGIN RETURN; END -- convert trace numerical objecttypes to string IF (ISNUMERIC(@object_type) = 1) select @object_type = name from master.dbo.spt_values where type = 'EOB' and number = @object_type -- these events do not have ObjectType and ObjectName IF ((@object_type IS NULL) AND @event_type IN ('CREATE_DATABASE', 'DROP_DATABASE', 'ALTER_DATABASE')) BEGIN SET @object_type = 'DATABASE' END INSERT msdb.dbo.syspolicy_execution_internal SELECT p.policy_id , @synchronous, @event_data FROM dbo.syspolicy_policies p -- give me all the policies INNER JOIN dbo.syspolicy_conditions_internal c ON c.condition_id = p.condition_id -- and their conditions INNER JOIN dbo.syspolicy_facet_events fe ON c.facet_id = fe.management_facet_id -- and the facet events that are affected by the condition INNER JOIN dbo.syspolicy_target_sets ts ON ts.object_set_id = p.object_set_id AND ts.type = fe.target_type -- and the target sets in the object set of the policy, with event types that are affected by the condition LEFT JOIN dbo.syspolicy_policy_category_subscriptions pgs ON pgs.policy_category_id = p.policy_category_id -- and the policy category subscriptions, if any LEFT JOIN dbo.syspolicy_target_set_levels tsl ON tsl.target_set_id = ts.target_set_id AND tsl.level_name = 'Database' -- and the database target set levels associated with any of the target sets, if any LEFT JOIN dbo.syspolicy_conditions_internal lc ON lc.condition_id = tsl.condition_id -- and any conditions on the target set level, if any LEFT JOIN dbo.syspolicy_policy_categories cat ON p.policy_category_id = cat.policy_category_id -- and the policy categories, if any WHERE fe.event_name=@event_type AND -- event type matches the fired event p.is_enabled = 1 AND -- policy is enabled fe.target_type_alias = @object_type AND -- target type matches the object in the event ts.enabled = 1 AND -- target set is enabled -- 1 means Enforce, 2 means CheckOnChange (p.execution_mode & @mode) = @mode AND -- policy mode matches the requested mode ((p.policy_category_id IS NULL) OR (cat.mandate_database_subscriptions = 1) OR ( ts.type_skeleton NOT LIKE 'Server/Database%') OR (@database IS NOT NULL AND pgs.target_object = @database)) AND ((@database IS NULL) OR (@database IS NOT NULL AND (tsl.condition_id IS NULL OR (tsl.condition_id IS NOT NULL AND ((lc.is_name_condition=1 AND @database = lc.obj_name) OR (lc.is_name_condition=2 AND @database LIKE lc.obj_name) OR (lc.is_name_condition=3 AND @database != lc.obj_name) OR (lc.is_name_condition=4 AND @database NOT LIKE lc.obj_name)) ) ) ) ) -- NOTE: if we haven't subscribed via an Endpoint facet on those events -- we know for sure they will not be processed by the ServerAreaFacet policies -- because syspolicy_facet_events expects @target_type to be SERVER -- so the filter will leave them out, and we are going to generate a fake -- event to make those policies run IF( @synchronous = 0 AND (@event_type IN ('ALTER_ENDPOINT', 'CREATE_ENDPOINT', 'DROP_ENDPOINT'))) BEGIN DECLARE @fake_event_data xml SET @fake_event_data = CONVERT(xml, '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 ------------------------------------------------------------------------------- -- End policy objects ------------------------------------------------------------------------------- USE [msdb] GO IF OBJECT_ID('[dbo].[sp_read_settings]', 'P') IS NOT NULL DROP PROC [dbo].[sp_read_settings] GO CREATE PROCEDURE [dbo].[sp_read_settings] @name sysname = NULL OUTPUT, @setting_id int = NULL OUTPUT AS BEGIN IF ((@name IS NULL) AND (@setting_id IS NULL)) OR ((@name IS NOT NULL) AND (@setting_id IS NOT NULL)) BEGIN RAISERROR(14524, -1, -1, '@name', '@setting_id') RETURN(1) -- Failure END IF (@setting_id IS NOT NULL) BEGIN SELECT @name = CASE @setting_id WHEN 1 THEN 'ExtendedProtection' WHEN 2 THEN 'ForceEncryption' WHEN 3 THEN 'AcceptedSPNs' ELSE NULL END IF (@name IS NULL) RETURN (2) -- Unknown key END ELSE BEGIN IF (@name collate SQL_Latin1_General_CP1_CI_AS) != 'ExtendedProtection' AND (@name collate SQL_Latin1_General_CP1_CI_AS) != 'ForceEncryption' AND (@name collate SQL_Latin1_General_CP1_CI_AS) != 'AcceptedSPNs' RETURN (2) -- Unknown key END DECLARE @hive nvarchar(32), @key nvarchar(256) SET @hive=N'HKEY_LOCAL_MACHINE' SET @key=N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\SuperSocketNetLib' Execute master.sys.xp_instance_regread @hive, @key, @name RETURN (0) END GO EXEC sp_MS_marksystemobject 'dbo.sp_read_settings' GO /**************************************************************/ /* BEGIN UTILITY SCRIPTS */ /**************************************************************/ -- These settings are necessary for the Utility. SET ANSI_NULLS ON SET ANSI_NULL_DFLT_ON ON SET ANSI_PADDING ON SET ANSI_WARNINGS ON SET ARITHABORT ON SET CONCAT_NULL_YIELDS_NULL ON SET NUMERIC_ROUNDABORT OFF SET QUOTED_IDENTIFIER ON GO ----------------------------------------------------------- -- Security for Utility objects ----------------------------------------------------------- IF ( NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'UtilityCMRReader' AND type = 'R')) BEGIN CREATE ROLE [UtilityCMRReader] END ELSE -- if the role exists check to see if it has members BEGIN IF NOT EXISTS (SELECT rm.member_principal_id FROM sys.database_principals dp INNER JOIN sys.database_role_members rm ON rm.role_principal_id = dp.principal_id WHERE name = N'UtilityCMRReader' AND type = 'R') BEGIN -- if the role has no members drop and recreate it DROP ROLE [UtilityCMRReader] CREATE ROLE [UtilityCMRReader] END END GO IF ( NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'UtilityIMRWriter' AND type = 'R')) BEGIN CREATE ROLE [UtilityIMRWriter] END ELSE -- if the role exists check to see if it has members BEGIN IF NOT EXISTS (SELECT rm.member_principal_id FROM sys.database_principals dp INNER JOIN sys.database_role_members rm ON rm.role_principal_id = dp.principal_id WHERE name = N'UtilityIMRWriter' AND type = 'R') BEGIN -- if the role has no members drop and recreate it DROP ROLE [UtilityIMRWriter] CREATE ROLE [UtilityIMRWriter] END END GO IF ( NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'UtilityIMRReader' AND type = 'R')) BEGIN CREATE ROLE [UtilityIMRReader] END ELSE -- if the role exists check to see if it has members BEGIN IF NOT EXISTS (SELECT rm.member_principal_id FROM sys.database_principals dp INNER JOIN sys.database_role_members rm ON rm.role_principal_id = dp.principal_id WHERE name = N'UtilityIMRReader' AND type = 'R') BEGIN -- if the role has no members drop and recreate it DROP ROLE [UtilityIMRReader] CREATE ROLE [UtilityIMRReader] END END GO -- A UtilityIMRWriter is also a UtilityIMRReader EXECUTE sp_addrolemember @rolename = 'UtilityIMRReader' , @membername = 'UtilityIMRWriter' GO /**************************************************************************/ /* Create the Utility Control Point configuration table */ /* This table stores configuration values for the UCP. */ /**************************************************************************/ IF(OBJECT_ID(N'[dbo].[sysutility_ucp_configuration_internal]', 'U') IS NULL) BEGIN RAISERROR ('Creating table [dbo].[sysutility_ucp_configuration_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_ucp_configuration_internal] ( name sysname PRIMARY KEY CLUSTERED NOT NULL, current_value sql_variant NULL); INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value) VALUES (N'UtilityName', ''); INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value) VALUES (N'MdwDatabaseName', ''); INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value) VALUES (N'UtilityDescription', ''); INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value) VALUES (N'UtilityDateCreated', ''); INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value) VALUES (N'UtilityCreatedBy', ''); INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value) VALUES (N'OverUtilizationTrailingWindow', 1); INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value) VALUES (N'OverUtilizationOccurenceFrequency', 25); INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value) VALUES (N'UnderUtilizationTrailingWindow', 168); INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value) VALUES (N'UnderUtilizationOccurenceFrequency', 90); INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value) VALUES (N'MdwRetentionLengthInDaysForMinutesHistory', 2); INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value) VALUES (N'MdwRetentionLengthInDaysForHoursHistory', 31); INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value) VALUES (N'MdwRetentionLengthInDaysForDaysHistory', 366); INSERT INTO [dbo].[sysutility_ucp_configuration_internal] (name, current_value) VALUES (N'UtilityVersion', N'1.0.0.0'); END GO /**********************************************************************/ /* Create the Utility Control Point configuration view */ /**********************************************************************/ IF(OBJECT_ID(N'[dbo].[sysutility_ucp_configuration]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_configuration]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_configuration] END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_configuration]...', 0, 1) WITH NOWAIT; GO CREATE VIEW [dbo].[sysutility_ucp_configuration] AS SELECT name, current_value FROM [dbo].[sysutility_ucp_configuration_internal] GO /**********************************************************************/ /* Create the utility control point policy configuration view */ /* This view returns the occurrence frequency and trailing window for */ /* both over and under utilization resource health policy types */ /**********************************************************************/ IF (OBJECT_ID(N'[dbo].[sysutility_ucp_policy_configuration]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_policy_configuration]', 0, 1) WITH NOWAIT; DROP VIEW dbo.sysutility_ucp_policy_configuration END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_policy_configuration]...', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_policy_configuration AS ( SELECT 1 AS utilization_type , CAST(UnderUtilizationOccurenceFrequency AS INT) AS occurence_frequency , CAST(UnderUtilizationTrailingWindow AS INT) AS trailing_window FROM (SELECT name, current_value FROM msdb.dbo.sysutility_ucp_configuration) config PIVOT (MAX(current_value) FOR name IN (UnderUtilizationOccurenceFrequency, UnderUtilizationTrailingWindow)) pvt UNION ALL SELECT 2 AS utilization_type , CAST(OverUtilizationOccurenceFrequency AS INT) AS occurence_frequency , CAST(OverUtilizationTrailingWindow AS INT) AS trailing_window FROM (SELECT name, current_value FROM msdb.dbo.sysutility_ucp_configuration) config PIVOT (MAX(current_value) FOR name IN (OverUtilizationOccurenceFrequency, OverUtilizationTrailingWindow)) pvt ) GO /******************************************************************** Function fn_sysutility_get_is_instance_ucp Description: Returns 1 if the local instance is a UCP, 0 otherwise ********************************************************************/ IF OBJECT_ID(N'[dbo].[fn_sysutility_get_is_instance_ucp]') IS NOT NULL BEGIN RAISERROR('Dropping [dbo].[fn_sysutility_get_is_instance_ucp]', 0, 1) WITH NOWAIT; DROP FUNCTION [dbo].[fn_sysutility_get_is_instance_ucp]; END GO RAISERROR('Creating [dbo].[fn_sysutility_get_is_instance_ucp]', 0, 1) WITH NOWAIT; GO CREATE FUNCTION [dbo].[fn_sysutility_get_is_instance_ucp]() RETURNS BIT AS BEGIN RETURN ( SELECT CASE WHEN ISNULL ((SELECT CAST (current_value as sysname) FROM msdb.dbo.sysutility_ucp_configuration_internal WHERE name = 'UtilityName'), '') = '' THEN 0 ELSE 1 END) END; GO /******************************************************************** Function fn_sysutility_get_culture_invariant_conversion_style_internal Description: Returns an integer that can be passed to CONVERT's "style" parameter in order to round-trip a value of the specified type to or from a string without data loss and in a culture-invariant way. ********************************************************************/ IF OBJECT_ID('dbo.fn_sysutility_get_culture_invariant_conversion_style_internal') IS NOT NULL BEGIN DROP FUNCTION dbo.fn_sysutility_get_culture_invariant_conversion_style_internal; END; GO CREATE FUNCTION dbo.fn_sysutility_get_culture_invariant_conversion_style_internal (@data_type varchar(30)) RETURNS tinyint AS BEGIN RETURN CASE -- ISO8601, e.g. "yyyy-mm-ddThh:mi:ss.mmm" WHEN @data_type IN ('datetime', 'datetimeoffset', 'smalldatetime', 'datetime2', 'date', 'time') THEN 126 -- scientific notation, 16 digits WHEN @data_type IN ('real', 'float') THEN 2 -- e.g. "0x12AB" WHEN @data_type IN ('binary', 'varbinary') THEN 1 -- all other types including bit, integer types, (n)varchar, decimal/numeric ELSE 0 END; END; GO /******************************************************************** Create "stub" objects that stand in for Utility MDW objects. We have views in msdb that wrap tables, views, and functions in the MDW database. The sysutility_mdw database is only created when an instance is made a UCP, so at the time of instmsdb.sql execution sysutility_mdw doesn't yet exist. This is a problem because views and inline functions in msdb cannot be created unless all of the objects (and columns) that they reference do exist. The current solution is to create these "stub" objects in msdb, create synonyms pointing to the stub objects, then reference the synonyms within any functions or views in msdb that need to reference MDW tables. If the instance is made a UCP, the Create UCP process executes the sp_sysutility_ucp_initialize_mdw stored proc, which will redirect the synonyms to reference the true MDW tables after the sysutility_mdw database has been created. These stub objects should only be modified when the structure of one of the corresponding MDW objects is modified. Note that these objects will never hold any data, so there is no reason to bother with build-to-build schema upgrade steps; we can safely drop and recreate them whenever instmsdb.sql is executed. ********************************************************************/ ------ BEGIN UTILITY MDW STUB OBJECTS ------ -- Stub for MDW object [snapshots].[sysutility_ucp_core.latest_dacs] IF OBJECT_ID(N'[dbo].[sysutility_ucp_dacs_stub]') IS NOT NULL BEGIN RAISERROR('Dropping table [dbo].[sysutility_ucp_dacs_stub]', 0, 1) WITH NOWAIT; DROP TABLE [dbo].[sysutility_ucp_dacs_stub]; END; GO RAISERROR('Creating table [sysutility_ucp_dacs_stub]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_dacs_stub] ( [dac_id] INT IDENTITY, -- todo (VSTS #345036): This column will be removed [physical_server_name] SYSNAME, [server_instance_name] SYSNAME, -- the server-qualified instance name [dac_name] SYSNAME, [dac_deploy_date] DATETIME, [dac_description] NVARCHAR(4000) NULL, [urn] NVARCHAR(4000), [powershell_path] NVARCHAR(4000), [processing_time] DATETIMEOFFSET(7), batch_time DATETIMEOFFSET(7), -- todo (VSTS #345040) (no measure columns in dimension tables) [dac_percent_total_cpu_utilization] REAL ) GO -- Stub for MDW object [snapshots].[sysutility_ucp_core.latest_volumes] IF OBJECT_ID(N'[dbo].[sysutility_ucp_volumes_stub]') IS NOT NULL BEGIN RAISERROR('Dropping table [dbo].[sysutility_ucp_volumes_stub]', 0, 1) WITH NOWAIT; DROP TABLE [dbo].[sysutility_ucp_volumes_stub]; END; GO RAISERROR('Creating table [sysutility_ucp_latest_volumes_stub]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_volumes_stub] ( [ID] INT IDENTITY, -- todo (VSTS #345036): This column will be removed virtual_server_name SYSNAME, physical_server_name SYSNAME, volume_device_id SYSNAME, volume_name SYSNAME, -- todo (VSTS #345040) (no measure columns in dimension tables) total_space_available real, free_space real, total_space_utilized real, percent_total_space_utilization real, processing_time DATETIMEOFFSET(7), batch_time DATETIMEOFFSET(7) ) -- Stub for MDW object [snapshots].[sysutility_ucp_core.latest_computers] IF OBJECT_ID(N'[dbo].[sysutility_ucp_computers_stub]') IS NOT NULL BEGIN RAISERROR('Dropping table [dbo].[sysutility_ucp_computers_stub]', 0, 1) WITH NOWAIT; DROP TABLE [dbo].[sysutility_ucp_computers_stub]; END; GO RAISERROR('Creating table [sysutility_ucp_computers_stub]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_computers_stub] ( [id] INT IDENTITY, -- todo (VSTS #345036): This column will be removed virtual_server_name SYSNAME, physical_server_name SYSNAME, -- differs from server_name for clustered servers is_clustered_server INT, num_processors INT, cpu_name NVARCHAR(128), cpu_caption NVARCHAR(128), cpu_family NVARCHAR(128), cpu_architecture NVARCHAR(64), cpu_max_clock_speed DECIMAL(10), cpu_clock_speed DECIMAL(10), l2_cache_size DECIMAL(10), l3_cache_size DECIMAL(10), urn NVARCHAR(4000), powershell_path NVARCHAR(4000), processing_time DATETIMEOFFSET(7), batch_time DATETIMEOFFSET(7), percent_total_cpu_utilization REAL -- todo (VSTS #345040) (no measure columns in dimension tables) ) -- Stub for MDW object [snapshots].[sysutility_ucp_latest_smo_servers] IF OBJECT_ID(N'[dbo].[sysutility_ucp_smo_servers_stub]') IS NOT NULL BEGIN RAISERROR('Dropping table [dbo].[sysutility_ucp_smo_servers_stub]', 0, 1) WITH NOWAIT; DROP TABLE [dbo].[sysutility_ucp_smo_servers_stub]; END; GO RAISERROR ('Creating table [dbo].[sysutility_ucp_smo_servers_stub]', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_ucp_smo_servers_stub] ( [urn] NVARCHAR(512), [powershell_path] NVARCHAR(4000), [processing_time] DATETIMEOFFSET(7), [batch_time] DATETIMEOFFSET(7), [AuditLevel] SMALLINT, [BackupDirectory] NVARCHAR(260), [BrowserServiceAccount] NVARCHAR(128), [BrowserStartMode] SMALLINT, [BuildClrVersionString] NVARCHAR(20), [BuildNumber] INT, [Collation] NVARCHAR(128), [CollationID] INT, [ComparisonStyle] INT, [ComputerNamePhysicalNetBIOS] NVARCHAR(128), [DefaultFile] NVARCHAR(260), [DefaultLog] NVARCHAR(260), [Edition] NVARCHAR(64), [EngineEdition] SMALLINT, [ErrorLogPath] NVARCHAR(260), [FilestreamShareName] NVARCHAR(260), [InstallDataDirectory] NVARCHAR(260), [InstallSharedDirectory] NVARCHAR(260), [InstanceName] NVARCHAR(128), [IsCaseSensitive] BIT, [IsClustered] BIT, [IsFullTextInstalled] BIT, [IsSingleUser] BIT, [Language] NVARCHAR(64), [MailProfile] NVARCHAR(128), [MasterDBLogPath] NVARCHAR(260), [MasterDBPath] NVARCHAR(260), [MaxPrecision] TINYINT, [Name] NVARCHAR(128), [NamedPipesEnabled] BIT, [NetName] NVARCHAR(128), [NumberOfLogFiles] INT, [OSVersion] NVARCHAR(32), [PerfMonMode] SMALLINT, [PhysicalMemory] INT, [Platform] NVARCHAR(32), [Processors] SMALLINT, [ProcessorUsage] INT, [Product] NVARCHAR(48), [ProductLevel] NVARCHAR(32), [ResourceVersionString] NVARCHAR(32), [RootDirectory] NVARCHAR(260), [ServerType] SMALLINT, [ServiceAccount] NVARCHAR(128), [ServiceInstanceId] NVARCHAR(64), [ServiceName] NVARCHAR(64), [ServiceStartMode] SMALLINT, [SqlCharSet] SMALLINT, [SqlCharSetName] NVARCHAR(32), [SqlDomainGroup] NVARCHAR(260), [SqlSortOrder] SMALLINT, [SqlSortOrderName] NVARCHAR(64), [Status] SMALLINT, [TapeLoadWaitTime] INT, [TcpEnabled] BIT, [VersionMajor] INT, [VersionMinor] INT, [VersionString] NVARCHAR(32) ); GO -- Stub for MDW object [snapshots].[sysutility_ucp_databases] IF OBJECT_ID(N'[dbo].[sysutility_ucp_databases_stub]') IS NOT NULL BEGIN RAISERROR('Dropping table [dbo].[sysutility_ucp_databases_stub]', 0, 1) WITH NOWAIT; DROP TABLE [dbo].[sysutility_ucp_databases_stub]; END; GO RAISERROR ('Creating table [dbo].[sysutility_ucp_databases_stub]', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_ucp_databases_stub] ( [urn] NVARCHAR(512) , [powershell_path] NVARCHAR(MAX) , [processing_time] DATETIMEOFFSET(7) , [batch_time] DATETIMEOFFSET(7) , [server_instance_name] SYSNAME , [parent_urn] NVARCHAR(320) , [Collation] NVARCHAR(128) , [CompatibilityLevel] SMALLINT , [CreateDate] DATETIME , [EncryptionEnabled] BIT , [Name] NVARCHAR(128) , [RecoveryModel] SMALLINT , [Trustworthy] BIT ); GO -- Stub for MDW object [snapshots].[sysutility_ucp_filegroups] IF OBJECT_ID(N'[dbo].[sysutility_ucp_filegroups_stub]') IS NOT NULL BEGIN RAISERROR('Dropping table [dbo].[sysutility_ucp_filegroups_stub]', 0, 1) WITH NOWAIT; DROP TABLE [dbo].[sysutility_ucp_filegroups_stub]; END; GO RAISERROR ('Creating table [dbo].[sysutility_ucp_filegroups_stub]', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_ucp_filegroups_stub] ( [urn] NVARCHAR(780) , [powershell_path] NVARCHAR(MAX) , [processing_time] DATETIMEOFFSET(7) , [batch_time] DATETIMEOFFSET(7) , [server_instance_name] SYSNAME , [database_name] SYSNAME , [parent_urn] NVARCHAR(512) , [Name] NVARCHAR(128) ); GO -- Stub for MDW object [snapshots].[sysutility_ucp_datafiles] IF OBJECT_ID(N'[dbo].[sysutility_ucp_datafiles_stub]') IS NOT NULL BEGIN RAISERROR('Dropping table [dbo].[sysutility_ucp_datafiles_stub]', 0, 1) WITH NOWAIT; DROP TABLE [dbo].[sysutility_ucp_datafiles_stub]; END; GO RAISERROR ('Creating table [dbo].[sysutility_ucp_datafiles_stub]', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_ucp_datafiles_stub] ( [urn] NVARCHAR(1500) , [powershell_path] NVARCHAR(MAX) , [processing_time] DATETIMEOFFSET(7) , [batch_time] DATETIMEOFFSET(7) , [server_instance_name] SYSNAME , [database_name] SYSNAME , [filegroup_name] SYSNAME , [parent_urn] NVARCHAR(780) , [physical_server_name] SYSNAME , [volume_name] SYSNAME , [volume_device_id] SYSNAME , [Growth] REAL , [GrowthType] SMALLINT , [MaxSize] REAL , [Name] NVARCHAR(128) , [Size] REAL , [UsedSpace] REAL , [FileName] NVARCHAR(260) , [VolumeFreeSpace] BIGINT , [available_space] REAL ); GO -- Stub for MDW object [snapshots].[sysutility_ucp_logfiles] IF OBJECT_ID(N'[dbo].[sysutility_ucp_logfiles_stub]') IS NOT NULL BEGIN RAISERROR('Dropping table [dbo].[sysutility_ucp_logfiles_stub]', 0, 1) WITH NOWAIT; DROP TABLE [dbo].[sysutility_ucp_logfiles_stub]; END; GO RAISERROR ('Creating table [dbo].[sysutility_ucp_logfiles_stub]', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_ucp_logfiles_stub] ( [urn] NVARCHAR(1500) , [powershell_path] NVARCHAR(MAX) , [processing_time] DATETIMEOFFSET(7) , [batch_time] DATETIMEOFFSET(7) , [server_instance_name] SYSNAME , [database_name] SYSNAME , [parent_urn] NVARCHAR(780) , [physical_server_name] SYSNAME , [volume_name] SYSNAME , [volume_device_id] SYSNAME , [Growth] REAL , [GrowthType] SMALLINT , [MaxSize] REAL , [Name] NVARCHAR(128) , [Size] REAL , [UsedSpace] REAL , [FileName] NVARCHAR(260) , [VolumeFreeSpace] BIGINT , [available_space] REAL ) GO -- Stub for MDW object [snapshots].[sysutility_ucp_cpu_utilization] IF OBJECT_ID(N'[dbo].[sysutility_ucp_cpu_utilization_stub]') IS NOT NULL BEGIN RAISERROR('Dropping table [dbo].[sysutility_ucp_cpu_utilization_stub]', 0, 1) WITH NOWAIT; DROP TABLE [dbo].[sysutility_ucp_cpu_utilization_stub]; END; GO RAISERROR ('Creating table [dbo].[sysutility_ucp_cpu_utilization_stub]', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_ucp_cpu_utilization_stub] ( [processing_time] DATETIMEOFFSET(7), [aggregation_type] TINYINT NOT NULL, [object_type] TINYINT NOT NULL, -- Dimension keys [physical_server_name] SYSNAME DEFAULT '', [server_instance_name] SYSNAME DEFAULT '', [database_name] SYSNAME DEFAULT '', -- The actual measure columns. percent_total_cpu_utilization REAL ) GO -- Stub for MDW object [snapshots].[sysutility_ucp_logfiles] IF OBJECT_ID(N'[dbo].[sysutility_ucp_space_utilization_stub]') IS NOT NULL BEGIN RAISERROR('Dropping table [dbo].[sysutility_ucp_space_utilization_stub]', 0, 1) WITH NOWAIT; DROP TABLE [dbo].[sysutility_ucp_space_utilization_stub]; END; GO RAISERROR ('Creating table [dbo].[sysutility_ucp_space_utilization_stub]', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_ucp_space_utilization_stub] ( [processing_time] DATETIMEOFFSET(7) NOT NULL, [aggregation_type] TINYINT NOT NULL, [object_type] TINYINT NOT NULL, -- The dimension columns [virtual_server_name] SYSNAME DEFAULT '', [server_instance_name] SYSNAME DEFAULT '', [volume_device_id] SYSNAME DEFAULT '', [database_name] SYSNAME DEFAULT '', [filegroup_name] SYSNAME DEFAULT '', [dbfile_name] SYSNAME DEFAULT '', [used_space_bytes] REAL, [allocated_space_bytes] REAL, [total_space_bytes] REAL, [available_space_bytes] REAL ) GO ------ END UTILITY MDW STUB OBJECTS ------ /******************************************************************** Procedure [sp_sysutility_ucp_recreate_synonym_internal] Description: Helper proc to create/recreate a synonym ********************************************************************/ IF OBJECT_ID(N'[dbo].[sp_sysutility_ucp_recreate_synonym_internal]') IS NOT NULL BEGIN RAISERROR('Dropping [dbo].[sp_sysutility_ucp_recreate_synonym_internal] procedure', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].[sp_sysutility_ucp_recreate_synonym_internal]; END GO RAISERROR('Creating [dbo].[sp_sysutility_ucp_recreate_synonym_internal] procedure', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_ucp_recreate_synonym_internal] @synonym_name sysname, @database_name sysname, @schema_name sysname, @object_name sysname WITH EXECUTE AS CALLER AS BEGIN DECLARE @null_column nvarchar(100) SET @null_column = NULL IF (@synonym_name IS NULL OR @synonym_name = N'') SET @null_column = '@synonym_name' ELSE IF (@object_name IS NULL OR @object_name = N'') SET @null_column = '@object_name' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_recreate_synonym') RETURN(1) END IF EXISTS (SELECT name FROM sys.objects WHERE object_id = OBJECT_ID(@synonym_name) ) BEGIN RAISERROR('Dropping @synonym_name synonym', 0, 1) WITH NOWAIT; DECLARE @drop_statement nvarchar(600) SET @drop_statement = N'DROP SYNONYM [dbo].' + QUOTENAME(@synonym_name) RAISERROR ('Executing: %s', 0, 1, @drop_statement); EXEC sp_executesql @drop_statement END DECLARE @full_object_name nvarchar(776) = QUOTENAME(@database_name) + '.' + QUOTENAME(@schema_name) + '.' + QUOTENAME(@object_name) DECLARE @create_statement nvarchar(1060) SET @create_statement = N'CREATE SYNONYM [dbo].' + QUOTENAME(@synonym_name) + ' FOR ' + @full_object_name RAISERROR ('Executing: %s', 0, 1, @create_statement); EXEC sp_executesql @create_statement END GO /******************************************************************** Procedure [sp_sysutility_ucp_initialize_mdw] Description: This procedure will create the Utility synonyms. This proc is called by the Utility OM as part of the Create UCP process. It is also called whenever instmsdb.sql is executed. If the instance is a UCP and if sysutility_mdw exists, the synonyms will be created to reference objects in the Utility MDW database. Otherwise, the synonyms will reference stub objects in msdb. This allows the Utility msdb objects to be created even when sysutility_mdw does not exist. Parameters: @mdw_database_name - Name of the Utility MDW database ('sysutility_mdw') @require_mdw - If set to 1, the sysutility_mdw database must exist or an error is thrown. When this procedure is executed within instmsdb.sql, the param is set to 0 so that it tolerates a missing MDW db. When the procedure is executed by the Utility OM, this parameter is not passed (and defaults to 1), so the MDW database must exist. ********************************************************************/ IF OBJECT_ID ('[dbo].[sp_sysutility_ucp_initialize_mdw]') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_initialize_mdw]', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].[sp_sysutility_ucp_initialize_mdw] END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_initialize_mdw]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_ucp_initialize_mdw] @mdw_database_name sysname, @require_mdw bit = 1 WITH EXECUTE AS OWNER AS BEGIN -- Check if @mdw_database_name is NULL or empty IF (@mdw_database_name IS NULL OR @mdw_database_name = N'') BEGIN RAISERROR(14043, -1, -1, 'mdw_database_name', 'sp_sysutility_ucp_initialize_mdw') RETURN(1) END IF (@require_mdw = 1) AND NOT EXISTS (SELECT * FROM master.dbo.sysdatabases WHERE name = @mdw_database_name) BEGIN RAISERROR(37002, -1, -1, @mdw_database_name) RETURN(1) END DECLARE @database sysname; DECLARE @schema sysname; DECLARE @is_ucp bit; -- If the sysutility_mdw database has been installed and the instance appears to be a UCP, we should -- point the synonyms at the MDW objects. Otherwise, the synonyms should reference the stub objects -- (with a "_stub" suffix) that we just created. Note that during UCP creation, this proc is called -- at an interim step before fn_sysutility_get_is_instance_ucp returns 1. However, @require_mdw will -- be set to 1 in this case, telling us that we should redirect the synonyms to the MDW db even though . -- the instance is not (yet) completely set up as a UCP. IF (DB_ID (@mdw_database_name) IS NOT NULL) AND ((@require_mdw = 1) OR (dbo.fn_sysutility_get_is_instance_ucp() = 1)) BEGIN -- This instance is a UCP; synonyms should reference objects in sysutility_mdw SET @database = @mdw_database_name; SET @schema = 'sysutility_ucp_core'; -- Dimensions EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_computers', @database, @schema, 'latest_computers'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_volumes', @database, @schema, 'latest_volumes'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_dacs', @database, @schema, 'latest_dacs'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_smo_servers', @database, @schema, 'latest_smo_servers'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_databases', @database, @schema, 'latest_databases'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_filegroups', @database, @schema, 'latest_filegroups'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_datafiles', @database, @schema, 'latest_datafiles'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_logfiles', @database, @schema, 'latest_logfiles'; -- Measures EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_cpu_utilization', @database, @schema, 'cpu_utilization'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_space_utilization', @database, @schema, 'space_utilization'; -- Now that msdb is set up, call a setup proc in MDW to do any runtime initialization that is -- needed in that database. Only exec the proc if it exists -- it won't exist yet when instmsdb.sql -- is run on upgrade from CTP3 to RTM, because the MDW initialization proc was added post-CTP3 and -- upgrade executes instmsdb.sql prior to instmdw.sql. This proc will be re-executed by the post-upgrade -- script post_upgrade_ucp_cmdw.sql, and at that time the MDW proc will have been created. DECLARE @sql nvarchar(max); DECLARE @mdw_proc_name nvarchar(max); SET @mdw_proc_name = QUOTENAME(@mdw_database_name) + '.sysutility_ucp_core.sp_initialize_mdw_internal'; SET @sql = 'EXEC ' + @mdw_proc_name; IF OBJECT_ID (@mdw_proc_name) IS NOT NULL BEGIN RAISERROR ('Executing %s', 0, 1, @mdw_proc_name) WITH NOWAIT; EXEC (@sql); END ELSE BEGIN RAISERROR ('Skipping execution of %s', 0, 1, @mdw_proc_name) WITH NOWAIT; END; END ELSE BEGIN -- This instance is not a UCP; synonyms should reference msdb stub objects SET @database = 'msdb'; SET @schema = 'dbo'; -- Dimensions EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_computers', @database, @schema, 'sysutility_ucp_computers_stub'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_volumes', @database, @schema, 'sysutility_ucp_volumes_stub'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_dacs', @database, @schema, 'sysutility_ucp_dacs_stub'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_smo_servers', @database, @schema, 'sysutility_ucp_smo_servers_stub'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_databases', @database, @schema, 'sysutility_ucp_databases_stub'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_filegroups', @database, @schema, 'sysutility_ucp_filegroups_stub'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_datafiles', @database, @schema, 'sysutility_ucp_datafiles_stub'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_logfiles', @database, @schema, 'sysutility_ucp_logfiles_stub'; -- Measures EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_cpu_utilization', @database, @schema, 'sysutility_ucp_cpu_utilization_stub'; EXEC dbo.sp_sysutility_ucp_recreate_synonym_internal N'syn_sysutility_ucp_space_utilization', @database, @schema, 'sysutility_ucp_space_utilization_stub'; END; END GO -- Create the Utility synonyms. For details, see comments prior to "BEGIN UTILITY MDW STUB OBJECTS", -- above. If instmsdb is being run as part of mkmaster or for QFE/SP installation on an existing -- non-UCP SQL instance, this will redirect the synonyms to the msdb stub objects. If the script -- is being executed on a UCP, the synonyms will be redirected to the sysutility_mdw objects. EXEC msdb.dbo.sp_sysutility_ucp_initialize_mdw @mdw_database_name = 'sysutility_mdw', @require_mdw = 0; GO --------------------------------------------------------------------- -- SP to setup the managed instance proxy for a network account --------------------------------------------------------------------- IF OBJECT_ID ('dbo.sp_sysutility_mi_configure_proxy_account') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_configure_proxy_account]', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].[sp_sysutility_mi_configure_proxy_account] END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_mi_configure_proxy_account]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_mi_configure_proxy_account] @proxy_name sysname, @credential_name sysname, @network_account sysname, @password sysname AS BEGIN DECLARE @retval INT DECLARE @null_column sysname DECLARE @expression NVARCHAR(MAX) = N'' DECLARE @network_account_sid varbinary(85) SET @null_column = NULL IF (@proxy_name IS NULL OR @proxy_name = N'') SET @null_column = '@proxy_name' ELSE IF (@credential_name IS NULL OR @credential_name = N'') SET @null_column = '@credential_name' ELSE IF (@network_account IS NULL OR @network_account = N'') SET @null_column = '@network_account' ELSE IF (@password IS NULL OR @password = N'') SET @null_column = '@password' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_mi_configure_proxy_account') RETURN(1) END SET @network_account_sid = SUSER_SID(@network_account, 0) -- case insensensitive lookup SET @network_account = SUSER_SNAME(@network_account_sid) -- get the caseing of the user that the server recognizes IF NOT EXISTS (SELECT sid FROM msdb.sys.syslogins WHERE sid = @network_account_sid) BEGIN SET @expression = N'CREATE LOGIN '+ QUOTENAME(@network_account) +' FROM WINDOWS;' EXEC sp_executesql @expression END DECLARE @create_credential nvarchar(4000) DECLARE @print_credential nvarchar(4000) IF EXISTS(SELECT * FROM master.sys.credentials WHERE name = @credential_name) BEGIN set @create_credential = 'DROP CREDENTIAL ' + QUOTENAME(@credential_name) RAISERROR (@create_credential, 0, 1) WITH NOWAIT; EXEC sp_executesql @create_credential END set @create_credential = 'CREATE CREDENTIAL ' + QUOTENAME(@credential_name) + ' WITH IDENTITY=N' + QUOTENAME(@network_account, '''') + ', SECRET=N' + QUOTENAME(@password, '''') set @print_credential = 'CREATE CREDENTIAL ' + QUOTENAME(@credential_name) + ' WITH IDENTITY=N' + QUOTENAME(@network_account, '''') RAISERROR (@print_credential, 0, 1) WITH NOWAIT; EXEC sp_executesql @create_credential IF EXISTS(SELECT * FROM dbo.sysproxies WHERE (name = @proxy_name)) BEGIN EXEC dbo.sp_delete_proxy @proxy_name=@proxy_name END EXEC dbo.sp_add_proxy @proxy_name=@proxy_name, @credential_name=@credential_name, @enabled=1 EXEC dbo.sp_grant_login_to_proxy @msdb_role=N'dc_admin', @proxy_name=@proxy_name -- Grant the cmdexec subsystem to the proxy. This is the subsystem that DC uses to perform upload. EXEC dbo.sp_grant_proxy_to_subsystem @proxy_name=@proxy_name, @subsystem_id=3 -- Allow the account to see the table schemas. This is because DC checks to make sure the mdw -- schema matches the schema on the client. -- One cannot grant privledges to oneself. -- Since the caller is creating users by virtue of this sproc, it already can view server state -- So, only grant veiw server state if the network_account is not the caller IF( SUSER_SID() <> @network_account_sid ) BEGIN -- GRANT VIEW SERVER STATE requires the expression to be executed in master. SET @expression = N'use master; GRANT VIEW SERVER STATE TO ' + QUOTENAME(@network_account) RAISERROR (@expression, 0, 1) WITH NOWAIT; EXEC sp_executesql @expression END -- Add a user to the msdb database so that the proxy can be associated with the appropriate roles. -- The user might already be associated with a user in msdb. If so, find that user name so that -- roles can be assigned to it. DECLARE @user_name SYSNAME = (SELECT name FROM msdb.sys.database_principals WHERE sid = @network_account_sid) -- The "special principles" are not allowed to have roles added to them. -- The database Users in the "special" category are dbo, sys, and INFORMATION_SCHEMA. -- dbo is the only one that can have logins associated with it. -- The following only checks dbo because the network_account has an associated login. -- The else case (the user is msdb dbo), then they are effectively sysadmin in msdb and have -- the required permissions for the proxy, and there is not need to grant roles anyway. IF ((@user_name IS NULL) OR (@user_name <> N'dbo')) BEGIN -- This login doesn't have a user associated with it. -- Go ahead and create a user for it in msdb IF( @user_name IS NULL ) BEGIN SET @user_name = @network_account SET @expression = N'CREATE USER ' + QUOTENAME(@user_name) EXEC sp_executesql @expression END; -- Allow the user to view the msdb database metadata. This allows DC (and ssis) to verify -- the proxy's privledges. -- One cannot grant privledges to oneself. IF( SUSER_SID() <> @network_account_sid ) BEGIN SET @expression = N'GRANT VIEW DEFINITION TO ' + QUOTENAME(@network_account) RAISERROR (@expression, 0, 1) WITH NOWAIT; EXEC sp_executesql @expression END -- Adding roles is idempotent, so go ahead and add them. -- This role necessary for the proxy EXEC sp_addrolemember @rolename=N'dc_proxy', @membername=@user_name -- It needs to read the Utility tables. It requires execute permissions on the dac performance sp, so writer role is required. EXEC sp_addrolemember @rolename=N'UtilityIMRWriter', @membername=@user_name END END GO --------------------------------------------------------------------- -- SP to provision the proxy for a network account on the CMR --------------------------------------------------------------------- IF OBJECT_ID ('dbo.sp_sysutility_ucp_provision_proxy_account') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_provision_proxy_account]', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].[sp_sysutility_ucp_provision_proxy_account] END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_provision_proxy_account]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_ucp_provision_proxy_account] @network_account sysname, @mdw_database_name sysname AS BEGIN DECLARE @retval INT DECLARE @null_column sysname DECLARE @expression NVARCHAR(MAX) = N'' DECLARE @network_account_sid varbinary(85) SET @null_column = NULL IF (@network_account IS NULL OR @network_account = N'') SET @null_column = '@network_account' ELSE IF (@mdw_database_name IS NULL OR @mdw_database_name = N'') SET @null_column = '@mdw_database_name' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_provision_proxy_account') RETURN(1) END IF NOT EXISTS (SELECT * FROM master.dbo.sysdatabases WHERE name = @mdw_database_name) BEGIN RAISERROR(37002, -1, -1, @mdw_database_name) RETURN(1) END SET @network_account_sid = SUSER_SID(@network_account, 0) -- case insensensitive lookup SET @network_account = SUSER_SNAME(@network_account_sid) -- get the caseing of the user that the server recognizes IF NOT EXISTS (SELECT sid FROM msdb.sys.syslogins WHERE sid = @network_account_sid) BEGIN SET @expression = N'USE msdb; CREATE LOGIN '+ QUOTENAME(@network_account) + ' FROM WINDOWS;' EXEC sp_executesql @expression END DECLARE @is_sysadmin INT SELECT @is_sysadmin = 0 EXECUTE msdb.dbo.sp_sqlagent_has_server_access @login_name = @network_account, @is_sysadmin_member = @is_sysadmin OUTPUT IF (@is_sysadmin = 0) BEGIN DECLARE @print_expression nvarchar(500) SET @print_expression = @network_account + ' is NOT a SQL sysadmin' RAISERROR (@print_expression, 0, 1) WITH NOWAIT; IF NOT EXISTS(SELECT * FROM msdb.sys.database_principals WHERE sid = @network_account_sid) BEGIN SET @expression = N'USE msdb; CREATE USER ' + QUOTENAME(@network_account) +';' EXEC sp_executesql @expression END; EXEC msdb.dbo.sp_addrolemember @rolename='dc_proxy', @membername=@network_account DECLARE @grant_expression nvarchar(4000) IF NOT EXISTS (SELECT name from master.sys.databases WHERE @network_account_sid = owner_sid AND database_id = DB_ID(@mdw_database_name)) BEGIN set @grant_expression = 'IF NOT EXISTS(SELECT * FROM ' + QUOTENAME(@mdw_database_name) +'.[sys].[database_principals] WHERE sid = SUSER_SID(' + QUOTENAME(@network_account, '''') +', 0)) BEGIN RAISERROR (''Creating user ' + QUOTENAME(@network_account) + ' in ' + QUOTENAME(@mdw_database_name) + ''', 0, 1) WITH NOWAIT; USE ' + QUOTENAME(@mdw_database_name) + '; CREATE USER ' + QUOTENAME(@network_account) + '; END; RAISERROR (''Add to UtilityMDWWriter role'', 0, 1) WITH NOWAIT; EXEC ' + QUOTENAME(@mdw_database_name) + '.[dbo].[sp_addrolemember] @rolename=''UtilityMDWWriter'', @membername=' + QUOTENAME(@network_account) + ';' RAISERROR (@grant_expression, 0, 1) WITH NOWAIT; EXEC sp_executesql @grant_expression END END END GO ---------------------------------------------------------------------- -- SP to create the proxy account validator job ---------------------------------------------------------------------- IF OBJECT_ID ('dbo.sp_sysutility_mi_validate_proxy_account') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_validate_proxy_account]', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].[sp_sysutility_mi_validate_proxy_account] END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_mi_validate_proxy_account]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_mi_validate_proxy_account] @proxy_name sysname, @credential_name sysname, @network_account sysname, @password sysname AS BEGIN DECLARE @retval INT DECLARE @null_column sysname SET @null_column = NULL IF (@proxy_name IS NULL OR @proxy_name = N'') SET @null_column = '@proxy_name' ELSE IF (@credential_name IS NULL OR @credential_name = N'') SET @null_column = '@credential_name' ELSE IF (@network_account IS NULL OR @network_account = N'') SET @null_column = '@network_account' ELSE IF (@password IS NULL OR @password = N'') SET @null_column = '@password' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_mi_validate_proxy_account') RETURN(1) END DECLARE @instance_name nvarchar(128) SET @instance_name = ISNULL(CONVERT(nvarchar(128), SERVERPROPERTY('InstanceName')), N'MSSQLSERVER') DECLARE @job_name sysname DECLARE @job_id uniqueidentifier DECLARE @description nvarchar(512) -- Delete the job if it already exists SET @job_name = N'sysutility_check_proxy_credentials' WHILE (EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @job_name)) BEGIN EXEC sp_delete_job @job_name=@job_name END DECLARE @credential_statement nvarchar(4000) DECLARE @print_credential nvarchar(4000) IF EXISTS(select * from master.sys.credentials where name = @credential_name) BEGIN set @credential_statement = 'DROP CREDENTIAL ' + QUOTENAME(@credential_name) RAISERROR (@credential_statement, 0, 1) WITH NOWAIT; EXEC sp_executesql @credential_statement END set @credential_statement = 'CREATE CREDENTIAL ' + QUOTENAME(@credential_name) + ' WITH IDENTITY=N' + QUOTENAME(@network_account, '''') + ', SECRET=N' + QUOTENAME(@password, '''') set @print_credential = 'CREATE CREDENTIAL ' + QUOTENAME(@credential_name) + ' WITH IDENTITY=N' + QUOTENAME(@network_account, '''') RAISERROR (@print_credential, 0, 1) WITH NOWAIT; EXEC sp_executesql @credential_statement IF EXISTS(SELECT * FROM dbo.sysproxies WHERE (name = @proxy_name)) BEGIN EXEC dbo.sp_delete_proxy @proxy_name=@proxy_name END -- Create the proxy and grant it to the cmdExec subsytem EXEC dbo.sp_add_proxy @proxy_name=@proxy_name, @credential_name=@credential_name, @enabled=1 EXEC dbo.sp_grant_proxy_to_subsystem @proxy_name=@proxy_name, @subsystem_id=3 -- Create the job EXEC msdb.dbo.sp_add_job @job_name=@job_name, @enabled=1, @notify_level_eventlog=0, @notify_level_email=2, @notify_level_netsend=2, @notify_level_page=2, @delete_level=0, @category_id=0, @job_id = @job_id OUTPUT DECLARE @server_name SYSNAME = CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')) EXEC msdb.dbo.sp_add_jobserver @job_name=@job_name, @server_name = @server_name DECLARE @collection_step_command nvarchar(512) SET @collection_step_command = N'time /T' EXEC msdb.dbo.sp_add_jobstep @job_id=@job_id, @step_name=N'Validate proxy account', @step_id=1, @cmdexec_success_code=0, @on_fail_action=2, @on_fail_step_id=0, @on_success_action=1, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'CMDEXEC', @command=@collection_step_command, @proxy_name=@proxy_name, @flags=16 END GO ---------------------------------------------------------------------- -- A function that returns a powershell script which is used for -- ensuring that the WMI queries necessary for data collection in the -- Utility work correctly. The function is used for validation of the -- Managed Instance and is run within a job step. It is exposed as a -- to ease testing and troublshooting. ---------------------------------------------------------------------- IF (OBJECT_ID(N'[dbo].[fn_sysutility_mi_get_validate_wmi_script]') IS NOT NULL) BEGIN RAISERROR('Dropping [dbo].[fn_sysutility_mi_get_validate_wmi_script] function', 0, 1) WITH NOWAIT; DROP FUNCTION [dbo].[fn_sysutility_mi_get_validate_wmi_script]; END GO RAISERROR('Creating [dbo].[fn_sysutility_mi_get_validate_wmi_script] function', 0, 1) WITH NOWAIT; GO CREATE FUNCTION [dbo].[fn_sysutility_mi_get_validate_wmi_script]() RETURNS NVARCHAR(MAX) AS BEGIN RETURN '# This script verifies that the following WMI objects are queriable $objectsToValidate = "Win32_MountPoint", "Win32_PerfRawData_PerfProc_Process", "Win32_PerfRawData_PerfOS_Processor", "Win32_Processor", "Win32_Volume", "Win32_LogicalDisk" # The errorHappend variable keeps track of whether any class failed the check $errorHappened=$false # The SQL Agent Powershell subsystem does not have an interactive host associated # with it, thus standard Write-Host and other host-based cmdlets have no place # to write to. This knowledge is used to tell if the script is in an Agent # or if it is running on a standard PowerShell host. $isNotConsole = ($host.Name -ne "ConsoleHost") function Get-IsAgentStep { $global:isNotConsole } # Writing to the agent logs is easiest to achieve with [Console]::Error.WriteLine # If the script is in Agent, write through the Console directly. If the script # is not in Agent (someone is using it to debug), then just output to the pipeline. function Write-AgentLog($object) { if(Get-IsAgentStep) { [Console]::Error.WriteLine($object) } else { $object } } # Query the given WMI object and report pass or fail on the object. function Validate-WmiObject([string] $wmiObject) { process { Write-AgentLog "#Running Command:" Write-AgentLog "Get-WmiObject $wmiObject | Out-Null" # Use ErrorVariable and ErrorAction SilentlyContinue so that all of the # objects can be tested without stopping the script or having spurrious messages # in the Agent logs. Get-WmiObject $wmiObject -ErrorVariable wmiError -ErrorAction SilentlyContinue | Out-Null # Check the error message and report pass or fail if($wmiError) { $global:errorHappened=$true Write-AgentLog "#Command FAILED. Exception : $wmiError" } else { Write-AgentLog "#Command PASSED." } } } # Validate all of the Wmi objects. If any one of them fail, then # report an error. function Validate-AllWmiObjects { $objectsToValidate | %{ Validate-WmiObject $_ } if($global:errorHappened) { Write-Error -ErrorAction Stop "One or more WMI classes failed the test" } } # Automatically check the status of the objects if the script is running in Agent # Otherwise, allow the user to call the Validate functions interactively. if(Get-IsAgentStep) { Validate-AllWmiObjects } ' END GO ---------------------------------------------------------------------- -- A sp that creates a job that ensures that WMI works appropreately for -- Utility data collection. ---------------------------------------------------------------------- IF OBJECT_ID ('dbo.sp_sysutility_mi_create_job_validate_wmi') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_create_job_validate_wmi]', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].[sp_sysutility_mi_create_job_validate_wmi] END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_mi_create_job_validate_wmi]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_mi_create_job_validate_wmi] AS BEGIN DECLARE @job_name sysname = N'sysutility_mi_validate_wmi' DECLARE @job_id uniqueidentifier DECLARE @description nvarchar(512) = N'' DECLARE @psScript NVARCHAR(MAX) = (SELECT [dbo].[fn_sysutility_mi_get_validate_wmi_script]()); -- Delete the job if it already exists WHILE (EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @job_name)) BEGIN EXEC sp_delete_job @job_name=@job_name END -- Create the job EXEC msdb.dbo.sp_add_job @job_name=@job_name, @enabled=1, @notify_level_eventlog=0, @notify_level_email=2, @notify_level_netsend=2, @notify_level_page=2, @delete_level=0, @category_id=0, @job_id = @job_id OUTPUT EXEC msdb.dbo.sp_add_jobserver @job_name=@job_name, @server_name = @@SERVERNAME -- Add the validation step EXEC msdb.dbo.sp_add_jobstep @job_id=@job_id, @step_name=N'Validate WMI configuration', @step_id=1, @cmdexec_success_code=0, @on_fail_action=2, @on_fail_step_id=0, @on_success_action=1, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'Powershell', @command=@psScript END GO ------------------------------------------------------------------------ -- SP to create the job which creates the UtilityCacheDirectory ------------------------------------------------------------------------ IF OBJECT_ID ('dbo.sp_sysutility_mi_create_cache_directory') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_create_cache_directory]', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].[sp_sysutility_mi_create_cache_directory] END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_mi_create_cache_directory]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_mi_create_cache_directory] @network_account sysname, @agent_service_account sysname AS BEGIN DECLARE @instance_name nvarchar(128) DECLARE @null_column sysname SET @null_column = NULL IF (@network_account IS NULL OR @network_account = N'') SET @null_column = '@network_account' ELSE IF (@agent_service_account IS NULL OR @agent_service_account = N'') SET @null_column = '@agent_service_account' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_create_cache_directory_job') RETURN(1) END SET @instance_name = ISNULL(CONVERT(nvarchar(128), SERVERPROPERTY('InstanceName')), N'MSSQLSERVER') DECLARE @job_name sysname DECLARE @job_id uniqueidentifier DECLARE @description nvarchar(512) DECLARE @cachepath nvarchar(2048); -- SQL Eye reports that xp_instance_regread can truncate the cachepath -- but xp_instance_regread doesn't handle LOB types to use nvarchar(MAX) EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'WorkingDirectory', @cachepath OUTPUT; set @cachepath=@cachepath + '\UtilityDC' RAISERROR (@cachepath, 0, 1) WITH NOWAIT; -- create unique job name SET @job_name = N'sysutility_create_cache_directory' WHILE (EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @job_name)) BEGIN EXEC sp_delete_job @job_name=@job_name END EXEC msdb.dbo.sp_add_job @job_name=@job_name, @enabled=1, @notify_level_eventlog=0, @notify_level_email=2, @notify_level_netsend=2, @notify_level_page=2, @delete_level=0, @category_id=0, @job_id = @job_id OUTPUT DECLARE @server_name SYSNAME = CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')) EXEC msdb.dbo.sp_add_jobserver @job_name=@job_name, @server_name = @server_name DECLARE @command nvarchar(MAX) SET @command = N'if exist "' + @cachepath + '" rmdir /S /Q "' + @cachepath + '"' EXEC msdb.dbo.sp_add_jobstep @job_id=@job_id, @step_name=N'Delete existing cache directory', @step_id=1, @cmdexec_success_code=0, @on_fail_action=2, @on_fail_step_id=0, @on_success_action=3, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'CMDEXEC', @command=@command, @flags=16 IF NOT (@network_account LIKE @agent_service_account) BEGIN -- If network_account (proxy) and agent_service_account are different, we shall ACL the cache RAISERROR ('network_account is different from agent_service_account', 0, 1) WITH NOWAIT; SET @command = N'md "' + @cachepath + '"' EXEC msdb.dbo.sp_add_jobstep @job_id=@job_id, @step_name=N'Create cache directory', @step_id=2, @cmdexec_success_code=0, @on_fail_action=2, @on_fail_step_id=0, @on_success_action=3, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'CMDEXEC', @command=@command, @flags=16 SET @command = N'cacls "' + @cachepath + '" /E /G ' + @network_account + ':C' EXEC msdb.dbo.sp_add_jobstep @job_id=@job_id, @step_name=N'ACL cache directory', @step_id=3, @cmdexec_success_code=0, @on_fail_action=2, @on_fail_step_id=0, @on_success_action=1, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'CMDEXEC', @command=@command, @flags=16 END ELSE BEGIN -- If network_account (proxy) and agent_service_account are the same, then there is no need to ACL the cache -- as the account already has write access to it courtesy the agent service account provisioning. -- In this case explicit provisioning of cache with the account leads to error. RAISERROR ('network_account is the same as the agent_service_account', 0, 1) WITH NOWAIT; SET @command = N'md "' + @cachepath + '"' EXEC msdb.dbo.sp_add_jobstep @job_id=@job_id, @step_name=N'Create cache directory', @step_id=2, @cmdexec_success_code=0, @on_fail_action=2, @on_fail_step_id=0, @on_success_action=1, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'CMDEXEC', @command=@command, @flags=16 END END GO --------------------------------------------------------------------- -- Table to hold information about a managed instance and its -- configuration --------------------------------------------------------------------- IF NOT EXISTS (SELECT name FROM [sys].[objects] WHERE object_id = OBJECT_ID(N'[dbo].[sysutility_mi_configuration_internal]') AND type in (N'U')) BEGIN RAISERROR('Creating [dbo].[sysutility_mi_configuration_internal] table', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_mi_configuration_internal] ( -- configuration_id + pk enforces a single row in the table configuration_id AS (1), ucp_instance_name SYSNAME NULL, mdw_database_name SYSNAME NULL, CONSTRAINT pk_sysutility_mi_configuration_internal_configuration_id PRIMARY KEY (configuration_id) -- this table should only contain one row ); END GO IF (NOT OBJECT_ID('[dbo].[sysutility_mi_configuration]', 'V') IS NULL) BEGIN RAISERROR('Dropping [dbo].[sysutility_mi_configuration] view', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_mi_configuration] END GO RAISERROR('Creating [dbo].[sysutility_mi_configuration] view', 0, 1) WITH NOWAIT; GO CREATE VIEW [dbo].[sysutility_mi_configuration] AS SELECT config.ucp_instance_name, config.mdw_database_name, t.upload_schema_version FROM -- The upload_schema_version represents the contract between the UCP and MI for data upload -- Change this value when a breaking change with a (downlevel) UCP may be introduced in the MI -- upload code. (SELECT 100 AS upload_schema_version) t LEFT OUTER JOIN [dbo].[sysutility_mi_configuration_internal] config ON 1=1 GO ---------------------------------------------------------- -- Stored PROCs to add/removed/check the UCP configuration ---------------------------------------------------------- IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_sysutility_mi_add_ucp_registration]') AND type in (N'P', N'PC')) BEGIN RAISERROR('Dropping [dbo].[sp_sysutility_mi_add_ucp_registration] procedure', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].[sp_sysutility_mi_add_ucp_registration]; END GO RAISERROR('Creating [dbo].[sp_sysutility_mi_add_ucp_registration] procedure', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_mi_add_ucp_registration] @ucp_instance_name SYSNAME, @mdw_database_name SYSNAME WITH EXECUTE AS OWNER AS BEGIN DECLARE @null_column SYSNAME = NULL SET NOCOUNT ON; SET XACT_ABORT ON; IF (@ucp_instance_name IS NULL) SET @null_column = '@ucp_instance_name' ELSE IF (@mdw_database_name IS NULL) SET @null_column = '@mdw_database_name' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_mi_add_ucp_registration') RETURN(1) END BEGIN TRANSACTION; IF EXISTS (SELECT * FROM [msdb].[dbo].[sysutility_mi_configuration_internal]) BEGIN UPDATE [msdb].[dbo].[sysutility_mi_configuration_internal] SET ucp_instance_name = @ucp_instance_name, mdw_database_name = @mdw_database_name END ELSE BEGIN INSERT INTO [msdb].[dbo].[sysutility_mi_configuration_internal] (ucp_instance_name, mdw_database_name) VALUES (@ucp_instance_name, @mdw_database_name); END COMMIT TRANSACTION; ---- Add the MiUcpName registry key values. ---- If the value is already present this XP will update them. EXEC master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility', N'MiUcpName', N'REG_SZ', @ucp_instance_name END GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_sysutility_mi_remove_ucp_registration]') AND type in (N'P', N'PC')) BEGIN RAISERROR('Dropping [dbo].[sp_sysutility_mi_remove_ucp_registration] procedure', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].[sp_sysutility_mi_remove_ucp_registration]; END GO RAISERROR('Creating [dbo].[sp_sysutility_mi_remove_ucp_registration] procedure', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_mi_remove_ucp_registration] WITH EXECUTE AS OWNER AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; BEGIN TRANSACTION; IF EXISTS (SELECT * FROM [msdb].[dbo].[sysutility_mi_configuration_internal]) BEGIN UPDATE [msdb].[dbo].[sysutility_mi_configuration_internal] SET ucp_instance_name = NULL, mdw_database_name = NULL END ELSE BEGIN INSERT INTO [msdb].[dbo].[sysutility_mi_configuration_internal] (ucp_instance_name, mdw_database_name) VALUES (NULL, NULL); END COMMIT TRANSACTION; ---- If the above part fails it will not execute the following XPs. ---- The following XP calls are not transactional, so they are put outside ---- the transaction. ---- Remove the MiUcpName registry key if it is present DECLARE @mi_ucp_name nvarchar(1024) EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility', N'MiUcpName', @mi_ucp_name OUTPUT IF @mi_ucp_name IS NOT NULL BEGIN EXEC master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility', N'MiUcpName' END ---- Remove the registry key if this instance is NOT a UCP. ---- If this instance is a UCP we cannot remove the key entirely as ---- the version number is still stored under the key. IF (msdb.dbo.fn_sysutility_get_is_instance_ucp() = 0) BEGIN EXEC master.dbo.xp_instance_regdeletekey N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility' END END GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fn_sysutility_ucp_get_instance_is_mi]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) BEGIN RAISERROR('Dropping [dbo].[fn_sysutility_ucp_get_instance_is_mi] function', 0, 1) WITH NOWAIT; DROP FUNCTION [dbo].[fn_sysutility_ucp_get_instance_is_mi]; END GO RAISERROR('Creating [dbo].[fn_sysutility_ucp_get_instance_is_mi] function', 0, 1) WITH NOWAIT; GO CREATE FUNCTION [dbo].[fn_sysutility_ucp_get_instance_is_mi]() RETURNS BIT WITH EXECUTE AS OWNER AS BEGIN DECLARE @status BIT = (SELECT CASE WHEN ((ucp_instance_name IS NOT NULL) AND (mdw_database_name IS NOT NULL)) THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END FROM sysutility_mi_configuration_internal config) RETURN ISNULL(@status,0) END GO ---------------------------------------------------------------------------------- -- Table sysutility_mi_dac_execution_statistics_internal -- Staging table used to store execution statistics for DACs on the local instance. This table stores -- relatively transient data; in the worst case, dropping and recreating it will result in the loss of -- up to one upload interval (15 minutes) of DAC execution statistics. The data in this table is created -- and updated by sp_sysutility_mi_collect_dac_execution_statistics_internal, which runs every 15 seconds -- on each MI. The data is harvested every 15 min by sp_sysutility_mi_get_dac_execution_statistics_internal, -- then moved to the UCP's CMDW database by the Utility collection set. ---------------------------------------------------------------------------------- IF OBJECT_ID ('dbo.sysutility_mi_dac_execution_statistics_internal', 'U') IS NOT NULL BEGIN RAISERROR('Dropping [dbo].[sysutility_mi_dac_execution_statistics_internal] table', 0, 1) WITH NOWAIT; DROP TABLE [dbo].[sysutility_mi_dac_execution_statistics_internal]; END GO RAISERROR('Creating [dbo].[sysutility_mi_dac_execution_statistics_internal] table', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_mi_dac_execution_statistics_internal] ( dac_instance_name sysname NOT NULL, database_name sysname UNIQUE NOT NULL, -- database name, from sysdac_instances database_id int UNIQUE NOT NULL, -- database ID, from sysdac_instances date_created datetime NULL, -- DAC creation date, from sysdac_instances [description] nvarchar(4000) NULL, -- DAC description, from sysdac_instances first_collection_time datetimeoffset NULL, -- date and time of the first update by [sp_sysutility_mi_collect_dac_execution_statistics_internal] last_collection_time datetimeoffset NULL, -- date and time of the last update by [sp_sysutility_mi_collect_dac_execution_statistics_internal] last_upload_time datetimeoffset NULL, -- date and time of the last upload of this data by DC lifetime_cpu_time_ms bigint NULL, -- cumulative CPU time observed within this DAC since [first_collection_time] cpu_time_ms_at_last_upload bigint NULL, -- [lifetime_cpu_time_ms] value at the last DC upload CONSTRAINT PK_sysutility_mi_dac_execution_statistics_internal PRIMARY KEY CLUSTERED (dac_instance_name) ); GO ---------------------------------------------------------------------------------- -- Table dbo.sysutility_mi_session_statistics_internal -- This table is effectively part of the implementation of sp_sysutility_mi_collect_dac_execution_statistics_internal. -- We can safely drop it because it holds ephemeral data that does not need to be preserved across -- an msdb upgrade. The only reason we need a separate table here is because we must temporarily -- store execution statistics at the session level in order to approximate the database/DAC execution -- statistics (SQL doesn't automatically roll execution stats up to the database level). ---------------------------------------------------------------------------------- IF (OBJECT_ID ('dbo.sysutility_mi_session_statistics_internal', 'U') IS NOT NULL) BEGIN RAISERROR('Dropping table [dbo].[sysutility_mi_session_statistics_internal]', 0, 1) WITH NOWAIT; DROP TABLE [dbo].[sysutility_mi_session_statistics_internal]; END; GO IF (OBJECT_ID ('dbo.sysutility_mi_session_statistics_internal', 'U') IS NULL) BEGIN RAISERROR('Creating table [dbo].[sysutility_mi_session_statistics_internal]', 0, 1) WITH NOWAIT; CREATE TABLE dbo.sysutility_mi_session_statistics_internal ( collection_time datetimeoffset NOT NULL, session_id int NOT NULL, dac_instance_name sysname NOT NULL, database_name sysname NOT NULL, login_time datetime NOT NULL, cumulative_cpu_ms int NOT NULL, CONSTRAINT PK_sysutility_mi_session_statistics_internal PRIMARY KEY CLUSTERED (collection_time, session_id, dac_instance_name, database_name, login_time) ); END; GO ------------------------------------------------------------------------------------- -- Configuration table to snapshot partitioning to enhance the performance of caching job -- time_id = 1 means the previous snapshot cut, time_id = 0, means the current snapshot_id cut -- Preserve the table during upgrade scenarios (create if it doesn't exist) ------------------------------------------------------------------------------------- IF(OBJECT_ID(N'[dbo].[sysutility_ucp_snapshot_partitions_internal]', 'U') IS NULL) BEGIN RAISERROR('Creating [dbo].[sysutility_ucp_snapshot_partitions_internal] table', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_ucp_snapshot_partitions_internal] ( time_id INT, latest_consistent_snapshot_id INT, ); END GO ---------------------------------------------------------------------------------- -- Procedure dbo.sp_sysutility_mi_collect_dac_execution_statistics_internal -- Captures execution statistics (currently, just CPU usage) by sessions within each DAC since the last -- execution of this sp. The incremental new CPU usage is added to the the lifetime DAC total CPU usage -- recorded in the cache table [sysutility_mi_dac_execution_statistics_internal]. This sp is executed -- by the Agent job that runs every 15 seconds on a Utility managed instance. The output (the execution -- stats in the cache table) is harvested by sp_sysutility_mi_get_dac_execution_statistics_internal as -- part of Utility's Data Collector collection set. -- -- Parameters: None -- Output Resultsets: None ---------------------------------------------------------------------------------- IF OBJECT_ID('dbo.sp_sysutility_mi_collect_dac_execution_statistics_internal', 'P') IS NOT NULL BEGIN RAISERROR('Dropping [dbo].[sp_sysutility_mi_collect_dac_execution_statistics_internal] procedure', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].[sp_sysutility_mi_collect_dac_execution_statistics_internal]; END GO RAISERROR('Creating [dbo].[sp_sysutility_mi_collect_dac_execution_statistics_internal] procedure', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_mi_collect_dac_execution_statistics_internal] AS BEGIN DECLARE @current_collection_time datetimeoffset = SYSDATETIMEOFFSET(); DECLARE @previous_collection_time datetimeoffset; -- At this point the session stats table should contain only rows from the prior collection time (or no -- rows, in which case @previous_collection_time will be set to NULL). Retrieve the prior collection time. SELECT TOP 1 @previous_collection_time = collection_time FROM dbo.sysutility_mi_session_statistics_internal ORDER BY collection_time DESC; -- Get a list of the DACs deployed on this instance. In the sysdac_instances view, some of these values -- are unindexed computed columns. Store them in a temp table so that we get indexed retrieval by dbid -- or db/instance name and stats on the columns we'll use as join columns. CREATE TABLE #dacs ( dac_instance_name sysname PRIMARY KEY, database_name sysname UNIQUE, database_id int UNIQUE, date_created datetime, [description] nvarchar(4000)); INSERT INTO #dacs SELECT DISTINCT instance_name, database_name, DB_ID(database_name), date_created, [description] FROM dbo.sysdac_instances -- Filter out "orphaned" DACs that have had their database deleted or renamed WHERE DB_ID(database_name) IS NOT NULL; -- Step 1: Capture execution stats for all current sessions in DAC databases. Now this table should -- hold two snapshots: one that we just inserted (referred to as the "current" data from here on), and -- the immediately prior snapshot of the same data from ~15 seconds ago ("previous"). Note that we -- include a dummy row with a fake spid number for any DACs that don't have any active sessions. This -- is because of a downstream requirement that we return a row for all DACs, even if there are no stats -- to report for the DAC. INSERT INTO dbo.sysutility_mi_session_statistics_internal (collection_time, session_id, dac_instance_name, database_name, login_time, cumulative_cpu_ms) SELECT @current_collection_time, ISNULL (sp.spid, -10) AS session_id, #dacs.dac_instance_name, #dacs.database_name, ISNULL (sp.login_time, GETDATE()) AS login_time, ISNULL (SUM(sp.cpu), 0) AS cumulative_cpu_ms FROM #dacs LEFT OUTER JOIN sys.sysprocesses AS sp ON #dacs.database_id = sp.[dbid] GROUP BY ISNULL (sp.spid, -10), #dacs.dac_instance_name, #dacs.database_name, ISNULL (sp.login_time, GETDATE()); -- Step 2: If this is the first execution, set @prev_collection_time to @cur_collection_time. -- This has the effect of generating stats that indicate no work done for all DACs. This is -- the best we can do on the first execution because we need to snapshots in order to calculate -- correct resource consumption over a time interval. We can't just return here, because we -- need at least a "stub" row for each DAC, so they all DACs will appear in the UI if a DC -- upload runs before our next collection time. IF (@previous_collection_time IS NULL) BEGIN SET @previous_collection_time = @current_collection_time; END; -- Step 3: Determine the amount of new CPU time used by each DAC in the just-completed ~15 second interval -- (this defines a CTE that is used in the following step). WITH interval_dac_statistics AS ( SELECT #dacs.dac_instance_name, -- We may not have observed any sessions from a DAC in this time interval; if so, it gets a CPU -- value of zero. ISNULL ( -- If prev.cumulative_cpu_ms is NULL, the session must be new; all of its used CPU time must have -- accrued within the just-ended time interval. SUM (cur.cumulative_cpu_ms - ISNULL (prev.cumulative_cpu_ms, 0)), 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. */ /* */ /**********************************************************************/ /* Function [fn_sysutility_ucp_get_edition_is_ucp_capable_internal] Returns 1 if this SQL instance's edition allows it to become a UCP. */ IF OBJECT_ID ('dbo.fn_sysutility_ucp_get_edition_is_ucp_capable_internal') IS NOT NULL BEGIN RAISERROR ('Dropping function [dbo].[fn_sysutility_ucp_get_edition_is_ucp_capable_internal]', 0, 1) WITH NOWAIT; DROP FUNCTION dbo.fn_sysutility_ucp_get_edition_is_ucp_capable_internal; END; GO RAISERROR ('Creating function [dbo].[fn_sysutility_ucp_get_edition_is_ucp_capable_internal]', 0, 1) WITH NOWAIT; GO CREATE FUNCTION dbo.fn_sysutility_ucp_get_edition_is_ucp_capable_internal () RETURNS bit AS BEGIN DECLARE @is_instance_ucp_capable bit = 1; -- The integer value below corresponds to a SQLBOOT property that identifies whether -- the SKU supports the UCP feature. DECLARE @sqlbootvalue int; EXEC @sqlbootvalue = master.dbo.xp_qv '1675385081', @@SERVICENAME; IF (@sqlbootvalue != 2) BEGIN SET @is_instance_ucp_capable = 0; END; RETURN @is_instance_ucp_capable; END GO /* Procedure [sp_sysutility_ucp_validate_prerequisites] The procedure validates that the local instance can be used as a UCP */ IF OBJECT_ID ('dbo.sp_sysutility_ucp_validate_prerequisites') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_validate_prerequisites]', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_validate_prerequisites END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_validate_prerequisites]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_ucp_validate_prerequisites] WITH EXECUTE AS OWNER AS BEGIN IF (dbo.fn_sysutility_ucp_get_edition_is_ucp_capable_internal() = 1) BEGIN RAISERROR ('Instance is able to be used as a Utility Control Point.', 0, 1) WITH NOWAIT; END ELSE BEGIN DECLARE @edition nvarchar(128); SELECT @edition = CONVERT(nvarchar(128), SERVERPROPERTY('Edition')); RAISERROR(37004, -1, -1, @edition); RETURN(1); END; END GO /**********************************************************************/ /* */ /* Run the process to turn the local instance into a UCP */ /* */ /**********************************************************************/ /* Procedure [sp_sysutility_ucp_create] The procedure runs the process that turns the local instance into a UCP */ IF OBJECT_ID ('dbo.sp_sysutility_ucp_create') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_create]', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_create END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_create]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_ucp_create] WITH EXECUTE AS OWNER AS BEGIN /* Validate that the UCP can be created on the local instance. */ EXEC [dbo].[sp_sysutility_ucp_validate_prerequisites] END GO /**********************************************************************/ /* */ /* Validate the instance can be managed. */ /* */ /**********************************************************************/ /* Procedure [sp_sysutility_mi_validate_enrollment_preconditions] The procedure validates that the local instance can be made managed */ IF OBJECT_ID ('dbo.sp_sysutility_mi_validate_enrollment_preconditions') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_validate_enrollment_preconditions]', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_mi_validate_enrollment_preconditions END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_mi_validate_enrollment_preconditions]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_mi_validate_enrollment_preconditions] WITH EXECUTE AS OWNER AS BEGIN /* Get the Edition value */ DECLARE @edition NVARCHAR(64) SELECT @edition = Convert(NVARCHAR, SERVERPROPERTY('edition')) /* Check SQLBOOT to ensure this instance edition can be used as a UCP. */ DECLARE @sqlbootvalue int EXEC @sqlbootvalue = master.dbo.xp_qv '3090395820', @@SERVICENAME IF (@sqlbootvalue = 2) RAISERROR ('Instance can be managed by a Utility Control Point.', 0, 1) WITH NOWAIT; ELSE RAISERROR(37005, -1, -1, @edition) RETURN(1) END GO /**********************************************************************/ /* */ /* Run the process to make the local instance managed by a UCP. */ /* */ /**********************************************************************/ /* Procedure [sp_sysutility_mi_enroll] The procedure runs the process that makes the local instance managed by a UCP. */ IF OBJECT_ID ('dbo.sp_sysutility_mi_enroll') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_enroll]', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_mi_enroll END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_mi_enroll]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_mi_enroll] WITH EXECUTE AS OWNER AS BEGIN /* Validate that the local instance can be managed by a UCP. */ EXEC [dbo].[sp_sysutility_mi_validate_enrollment_preconditions] END GO /**********************************************************************/ /* Object types handled by the UCP */ /* */ /* We treat DACs and Databases as synonymous for this purpose */ /**********************************************************************/ IF(OBJECT_ID(N'[dbo].[sysutility_ucp_supported_object_types_internal]', 'U') IS NULL) BEGIN RAISERROR ('Creating table [dbo].[sysutility_ucp_supported_object_types_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_supported_object_types_internal] ( [object_type] INT, [object_name] NVARCHAR(32), CONSTRAINT PK_sysutility_ucp_supported_object_types_internal PRIMARY KEY([object_type]) ) INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (0, 'Utility') INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (1, 'Computer') INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (2, 'Volume') INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (3, 'Instance') INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (4, 'Database') INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (5, 'FileGroup') INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (6, 'DataFile') INSERT INTO [sysutility_ucp_supported_object_types_internal] VALUES (7, 'LogFile') END GO /**********************************************************************/ /* Create the managed instance table */ /* */ /* */ /**********************************************************************/ IF(OBJECT_ID(N'[dbo].[sysutility_ucp_managed_instances_internal]', 'U') IS NULL) BEGIN RAISERROR ('Creating table [dbo].[sysutility_ucp_managed_instances_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_managed_instances_internal] ( instance_id int IDENTITY(1,1), instance_name sysname, virtual_server_name sysname, date_created datetimeoffset(7) NOT NULL default SYSDATETIMEOFFSET(), created_by sysname NOT NULL default SUSER_SNAME(), agent_proxy_account sysname NOT NULL, cache_directory nvarchar(520), management_state int NOT NULL default (0), CONSTRAINT [UQ_sysutility_ucp_mi_id] UNIQUE (instance_id ASC), CONSTRAINT [PK_sysutility_ucp_mi_name] PRIMARY KEY CLUSTERED (instance_name) ); END GO /**********************************************************************/ /* create the managed instance view */ /* */ /**********************************************************************/ IF(OBJECT_ID(N'[dbo].[sysutility_ucp_managed_instances]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_managed_instances]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_managed_instances] END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_managed_instances]...', 0, 1) WITH NOWAIT; GO CREATE VIEW [dbo].[sysutility_ucp_managed_instances] AS SELECT instance_id, instance_name, virtual_server_name, date_created, created_by, agent_proxy_account, cache_directory, management_state FROM [dbo].[sysutility_ucp_managed_instances_internal] GO /**********************************************************************/ /* Add managed instance to the store. */ /* */ /* */ /**********************************************************************/ /* Procedure [sp_sysutility_ucp_add_mi] This proc creates a new ManagedInstance in dbo.sysutility_ucp_managed_instances_internal table Parameters: @instance_name - @agent_proxy_account - @cache_directory - @management_state - @instance_id */ IF OBJECT_ID ('dbo.sp_sysutility_ucp_add_mi') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_add_mi]', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_add_mi END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_add_mi]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_ucp_add_mi] @instance_name sysname, @virtual_server_name sysname, @agent_proxy_account sysname, @cache_directory nvarchar(520), @management_state int, @instance_id int = NULL OUTPUT WITH EXECUTE AS OWNER AS BEGIN SET NOCOUNT ON DECLARE @retval INT DECLARE @null_column nvarchar(600) SET @null_column = NULL IF (@instance_name IS NULL OR @instance_name = N'') SET @null_column = '@instance_name' ELSE IF (@virtual_server_name IS NULL OR @virtual_server_name = N'') SET @null_column = '@virtual_server_name' ELSE IF (@management_state IS NULL) SET @null_column = '@management_state' ELSE IF (@agent_proxy_account IS NULL OR @agent_proxy_account = N'') SET @null_column = '@agent_proxy_account' -- @cache_directory can be null or empty IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_add_mi') RETURN(1) END IF EXISTS (SELECT * FROM dbo.sysutility_ucp_managed_instances_internal WHERE (instance_name = @instance_name)) BEGIN RAISERROR(34010, -1, -1, 'Managed_Instance', @instance_name) RETURN(1) END INSERT INTO [dbo].[sysutility_ucp_managed_instances_internal] (instance_name, virtual_server_name, agent_proxy_account, cache_directory, management_state) VALUES (@instance_name, @virtual_server_name, @agent_proxy_account, @cache_directory, @management_state) SELECT @retval = @@error SET @instance_id = SCOPE_IDENTITY() RETURN(@retval) END GO /**************************************************************************/ /* create the Utility Processing State table */ /* This table is a single-row table containing internal state information */ /* for UCP processing. The two columns that it stores currently are */ /* latest_processing_time: the time at which data from the "live" tables */ /* is copied over to the "cache" tables". */ /* latest_health_state_id: a sequence number that all the health_state */ /* tables use to represent the "latest" health */ /* state calculation. */ /* */ /**************************************************************************/ IF(OBJECT_ID(N'[dbo].[sysutility_ucp_processing_state_internal]', 'U') IS NULL) BEGIN RAISERROR ('Creating table [dbo].[sysutility_ucp_processing_state_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_ucp_processing_state_internal] ( latest_processing_time DATETIMEOFFSET(7), latest_health_state_id INT, next_health_state_id INT, [id] AS 1, CONSTRAINT CK_sysutility_ucp_processing_state_internal CHECK (latest_health_state_id <= next_health_state_id), CONSTRAINT PK_sysutility_ucp_processing_state_internal PRIMARY KEY([id]) -- enforce single row in this table ); INSERT INTO [dbo].[sysutility_ucp_processing_state_internal](latest_processing_time, latest_health_state_id, next_health_state_id) VALUES (SYSDATETIMEOFFSET(), 0, 1); END; GO /**********************************************************************/ /* create the Utility registration creation stored procedure */ /* */ /**********************************************************************/ IF OBJECT_ID ('dbo.sp_sysutility_ucp_initialize') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_initialize]', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].[sp_sysutility_ucp_initialize] END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_initialize]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_ucp_initialize] @utility_name sysname, @mdw_database_name sysname, @description nvarchar(1024) = N'' WITH EXECUTE AS OWNER AS BEGIN DECLARE @retval INT DECLARE @null_column sysname SET @null_column = NULL IF (@utility_name IS NULL OR @utility_name = N'') SET @null_column = '@utility_name' ELSE IF (@mdw_database_name IS NULL OR @mdw_database_name = N'') SET @null_column = '@mdw_database_name' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_initialize') RETURN(1) END -- Make sure that the Utility wasn't already created DECLARE @utilityName sysname set @utilityName = (SELECT CAST (current_value as sysname) FROM msdb.dbo.sysutility_ucp_configuration_internal where name = 'UtilityName') IF (@utilityName IS NOT NULL AND @utilityName != N'') BEGIN RAISERROR(37003, -1, -1) RETURN(1) END IF NOT EXISTS (SELECT * FROM master.dbo.sysdatabases WHERE name = @mdw_database_name) BEGIN RAISERROR(37002, -1, -1, @mdw_database_name) RETURN(1) END UPDATE dbo.sysutility_ucp_configuration_internal SET current_value = @utility_name WHERE name = N'UtilityName' UPDATE dbo.sysutility_ucp_configuration_internal SET current_value = @mdw_database_name WHERE name = N'MdwDatabaseName' UPDATE dbo.sysutility_ucp_configuration_internal SET current_value = SYSDATETIMEOFFSET() WHERE name = N'UtilityDateCreated' UPDATE dbo.sysutility_ucp_configuration_internal SET current_value = SUSER_SNAME() WHERE name = N'UtilityCreatedBy' IF (@description IS NOT NULL AND @description != N'') BEGIN UPDATE dbo.sysutility_ucp_configuration_internal SET current_value = @description WHERE name = N'UtilityDescription' END DECLARE @utility_version SYSNAME set @utility_version = (SELECT CAST(current_value AS SYSNAME) FROM [msdb].[dbo].[sysutility_ucp_configuration_internal] WHERE name = N'UtilityVersion') ---- Add the UtilityVersion, UcpName and the UcpFriendlyName registry key values. EXEC master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility', N'UtilityVersion', N'REG_SZ', @utility_version EXEC master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility', N'UcpName', N'REG_SZ', @@SERVERNAME EXEC master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\Utility', N'UcpFriendlyName', N'REG_SZ', @utility_name END GO /**********************************************************************/ /* Create procedure sp_sysutility_ucp_update_utility_configuration */ /**********************************************************************/ IF OBJECT_ID ('[dbo].sp_sysutility_ucp_update_utility_configuration') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_update_utility_configuration]', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].sp_sysutility_ucp_update_utility_configuration END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_update_utility_configuration]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_ucp_update_utility_configuration] @name SYSNAME, @value SQL_VARIANT WITH EXECUTE AS OWNER AS BEGIN DECLARE @retval INT DECLARE @null_column SYSNAME SET @null_column = NULL IF (@name IS NULL OR @name = N'') SET @null_column = '@name' ELSE IF (@value IS NULL) SET @null_column = '@value' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_update_utility_configuration') RETURN(1) END IF NOT EXISTS (SELECT 1 FROM dbo.sysutility_ucp_configuration_internal WHERE name = @name) BEGIN RAISERROR(14027, -1, -1, @name) RETURN(1) END UPDATE dbo.sysutility_ucp_configuration_internal SET current_value = @value WHERE name = @name SELECT @retval = @@error RETURN(@retval) END GO /**********************************************************************/ /* Create procedure fn_sysutility_ucp_accepts_upload_schema_version */ /* This procedure is used to identify whether an MI's upload schema */ /* is compatible with this UCP. If a breaking change is introduced */ /* that would cause an incompatibility between an MI and UCP. This */ /* function should be updated accordingly along with the */ /* mi_configuration view */ /* */ /* REASON CODES: */ /* -1 : Schema version is too low, upgrade MI */ /* 0 : Schema is accepted */ /* 1 : Schema version is too high, upgrade UCP */ /**********************************************************************/ IF OBJECT_ID ('[dbo].fn_sysutility_ucp_accepts_upload_schema_version') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[fn_sysutility_ucp_accepts_upload_schema_version]', 0, 1) WITH NOWAIT; DROP FUNCTION [dbo].fn_sysutility_ucp_accepts_upload_schema_version END; GO RAISERROR ('Creating procedure [dbo].[fn_sysutility_ucp_accepts_upload_schema_version]', 0, 1) WITH NOWAIT; GO CREATE FUNCTION [dbo].[fn_sysutility_ucp_accepts_upload_schema_version] ( @upload_schema_version INT ) RETURNS INT AS BEGIN DECLARE @accepted_min_version INT = 100; DECLARE @accepted_max_version INT = 100; -- Assume that the version is compatable DECLARE @retvalue INT = 0; IF(@upload_schema_version < @accepted_min_version) SET @retvalue = -1 ELSE IF(@upload_schema_version > @accepted_max_version) SET @retvalue = 1 RETURN @retvalue END GO /**********************************************************************/ /* Create the resource health policies table */ /**********************************************************************/ IF(OBJECT_ID(N'[dbo].[sysutility_ucp_health_policies_internal]', 'U') IS NULL) BEGIN RAISERROR ('Creating table [dbo].[sysutility_ucp_health_policies_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_ucp_health_policies_internal] ( health_policy_id INT IDENTITY(1,1), policy_name SYSNAME NOT NULL, rollup_object_urn NVARCHAR(4000) NOT NULL, rollup_object_type INT NOT NULL, target_type INT NOT NULL, resource_type INT NOT NULL, utilization_type INT NOT NULL, utilization_threshold FLOAT NOT NULL, is_global_policy BIT DEFAULT 0 CONSTRAINT [PK_sysutility_ucp_policies_internal_id] PRIMARY KEY CLUSTERED (health_policy_id ASC), ); CREATE NONCLUSTERED INDEX [NCI_sysutility_resource_health_policies_urn_types] ON [dbo].[sysutility_ucp_health_policies_internal]([rollup_object_type], [target_type], [resource_type], [utilization_type], [policy_name]) INCLUDE([rollup_object_urn]); END GO /**********************************************************************/ /* Create the resource health policies view */ /**********************************************************************/ IF object_id(N'dbo.sysutility_ucp_policies', 'V') IS NOT NULL DROP VIEW dbo.sysutility_ucp_policies GO CREATE VIEW dbo.sysutility_ucp_policies AS SELECT rhp.health_policy_id AS health_policy_id, p.policy_id AS policy_id, rhp.policy_name AS policy_name, rhp.rollup_object_type AS rollup_object_type, rhp.rollup_object_urn AS rollup_object_urn, rhp.target_type AS target_type, rhp.resource_type AS resource_type, rhp.utilization_type AS utilization_type, rhp.utilization_threshold AS utilization_threshold, rhp.is_global_policy AS is_global_policy FROM [msdb].[dbo].[sysutility_ucp_health_policies_internal] rhp INNER JOIN msdb.dbo.syspolicy_policies p ON p.name = rhp.policy_name GO /**********************************************************************/ /* Create procedure sp_sysutility_ucp_add_policy */ /* Creates the resource health policy record for specified as input details */ /**********************************************************************/ IF OBJECT_ID ('dbo.sp_sysutility_ucp_add_policy') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_add_policy]', 0, 1) WITH NOWAIT; DROP PROCEDURE sp_sysutility_ucp_add_policy END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_add_policy]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_ucp_add_policy] @policy_name SYSNAME, @rollup_object_type INT, @rollup_object_urn NVARCHAR(4000), @target_type INT, @resource_type INT, @utilization_type INT, @utilization_threshold FLOAT, @resource_health_policy_id INT = NULL OUTPUT WITH EXECUTE AS OWNER AS BEGIN DECLARE @retval INT DECLARE @null_column SYSNAME SET @null_column = NULL IF (@policy_name IS NULL OR @policy_name = N'') SET @null_column = '@policy_name' ELSE IF (@rollup_object_type IS NULL OR @rollup_object_type < 1 OR @rollup_object_type > 3) SET @null_column = '@rollup_object_type' ELSE IF (@rollup_object_urn IS NULL OR @rollup_object_urn = N'') SET @null_column = '@rollup_object_urn' ELSE IF (@target_type IS NULL OR @target_type < 1 OR @target_type > 6) SET @null_column = '@target_type' ELSE IF (@resource_type IS NULL OR @resource_type < 1 OR @resource_type > 5) SET @null_column = '@resource_type' ELSE IF (@utilization_type IS NULL OR @utilization_type < 1 OR @utilization_type > 2) SET @null_column = '@utilization_type' ELSE IF (@utilization_threshold IS NULL OR @utilization_threshold < 0 OR @utilization_threshold > 100) SET @null_column = '@utilization_threshold' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_add_policy') RETURN(1) END IF NOT EXISTS (SELECT * FROM dbo.syspolicy_policies WHERE name = @policy_name) BEGIN RAISERROR(14027, -1, -1, @policy_name) RETURN(1) END INSERT INTO dbo.sysutility_ucp_health_policies_internal(policy_name, rollup_object_type, rollup_object_urn, target_type, resource_type, utilization_type, utilization_threshold) VALUES(@policy_name, @rollup_object_type, @rollup_object_urn, @target_type, @resource_type, @utilization_type, @utilization_threshold) SELECT @retval = @@error SET @resource_health_policy_id = SCOPE_IDENTITY() RETURN(@retval) END GO /**********************************************************************/ /* Create procedure sp_sysutility_ucp_delete_policy */ /* Deletes the resource health policy record for the id specified as input */ /**********************************************************************/ IF OBJECT_ID ('dbo.sp_sysutility_ucp_delete_policy') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_delete_policy]', 0, 1) WITH NOWAIT; DROP PROCEDURE sp_sysutility_ucp_delete_policy END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_delete_policy]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_ucp_delete_policy] @resource_health_policy_id INT WITH EXECUTE AS OWNER AS BEGIN DECLARE @retval INT DECLARE @null_column SYSNAME SET @null_column = NULL IF (@resource_health_policy_id IS NULL OR @resource_health_policy_id = 0) SET @null_column = '@resource_health_policy_id' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_delete_policy') RETURN(1) END IF NOT EXISTS (SELECT * FROM dbo.sysutility_ucp_health_policies_internal WHERE health_policy_id = @resource_health_policy_id AND is_global_policy = 0) BEGIN RAISERROR(22981, -1, -1) RETURN(1) END DELETE dbo.sysutility_ucp_health_policies_internal WHERE health_policy_id = @resource_health_policy_id SELECT @retval = @@error RETURN(@retval) END GO /**********************************************************************/ /* Create procedure sp_sysutility_ucp_update_policy */ /* Updates the resource health policy record with input utilization threshold */ /**********************************************************************/ IF OBJECT_ID ('dbo.sp_sysutility_ucp_update_policy') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_ucp_update_policy]', 0, 1) WITH NOWAIT; DROP PROCEDURE sp_sysutility_ucp_update_policy END; GO RAISERROR ('Creating procedure [dbo].[sp_sysutility_ucp_update_policy]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_ucp_update_policy] @resource_health_policy_id INT , @utilization_threshold INT WITH EXECUTE AS OWNER AS BEGIN DECLARE @retval INT DECLARE @null_column SYSNAME SET @null_column = NULL IF (@resource_health_policy_id IS NULL OR @resource_health_policy_id = 0) SET @null_column = '@resource_health_policy_id' ELSE IF (@utilization_threshold IS NULL OR @utilization_threshold < 0 OR @utilization_threshold > 100) SET @null_column = '@utilization_threshold' IF @null_column IS NOT NULL BEGIN RAISERROR(14043, -1, -1, @null_column, 'sp_sysutility_ucp_update_policy') RETURN(1) END IF NOT EXISTS (SELECT * FROM dbo.sysutility_ucp_health_policies_internal WHERE health_policy_id = @resource_health_policy_id) BEGIN RAISERROR(22981, -1, -1) RETURN(1) END UPDATE dbo.sysutility_ucp_health_policies_internal SET utilization_threshold = @utilization_threshold WHERE health_policy_id = @resource_health_policy_id SELECT @retval = @@error RETURN(@retval) END GO /**********************************************************************/ /* Create the sysutility_ucp_policy_check_conditions_internal table */ /* This table contains metadata information to build the check condition */ /* Following are the values supported by each of the below attributes */ /* target_type: Computer - 1, DataFile - 2, LogFile - 3, Server - 4, DeployedDac - 5, Volume - 6 */ /* resource_type: StorageSpace - 1, StorageIO - 2, Processor - 3, Memory - 4, NetworkIO - 5 */ /* utilization_type: UnderUtilization - 1, OverUtilization - 2 */ /* operator_type: = AND - 1, OR - 2, - 3, != - 4, < - 5, > - 6, <= - 7, >= - 8 */ /**********************************************************************/ IF(OBJECT_ID(N'[dbo].[sysutility_ucp_policy_check_conditions_internal]', 'U') IS NULL) BEGIN RAISERROR ('Creating table [dbo].[sysutility_ucp_policy_check_conditions_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_ucp_policy_check_conditions_internal] ( target_type INT NOT NULL, resource_type INT NOT NULL, utilization_type INT NOT NULL, facet_name SYSNAME NOT NULL, attribute_name SYSNAME NOT NULL, operator_type INT NOT NULL, property_name SYSNAME NOT NULL CONSTRAINT [PK_sysutility_ucp_policy_check_condition_internal_type] PRIMARY KEY CLUSTERED (resource_type, target_type, utilization_type, facet_name, attribute_name ASC), ); END GO /**********************************************************************/ /* Create the sysutility_ucp_policy_check_conditions */ /**********************************************************************/ IF object_id(N'dbo.sysutility_ucp_policy_check_conditions', 'V') IS NOT NULL DROP VIEW dbo.sysutility_ucp_policy_check_conditions GO CREATE VIEW dbo.sysutility_ucp_policy_check_conditions AS SELECT cc.target_type AS target_type, cc.resource_type AS resource_type, cc.utilization_type AS utilization_type, cc.facet_name AS facet_name, cc.attribute_name AS attribute_name, cc.operator_type AS operator_type, cc.property_name AS property_name FROM msdb.[dbo].[sysutility_ucp_policy_check_conditions_internal] cc GO DELETE FROM msdb.dbo.sysutility_ucp_policy_check_conditions_internal GO INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(1, 3, 1, 'Computer', 'ProcessorUtilization', 8, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(1, 3, 2, 'Computer', 'ProcessorUtilization', 7, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(2, 1, 1, 'IDataFilePerformanceFacet', 'SpaceUtilization', 8, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(2, 1, 2, 'IDataFilePerformanceFacet', 'SpaceUtilization', 7, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(3, 1, 1, 'ILogFilePerformanceFacet', 'SpaceUtilization', 8, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(3, 1, 2, 'ILogFilePerformanceFacet', 'SpaceUtilization', 7, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(4, 3, 1, 'Server', 'ProcessorUsage', 8, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(4, 3, 2, 'Server', 'ProcessorUsage', 7, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(5, 3, 1, 'DeployedDac', 'ProcessorUtilization', 8, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(5, 3, 2, 'DeployedDac', 'ProcessorUtilization', 7, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(6, 1, 1, 'Volume', 'TotalSpaceUtilization', 8, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_check_conditions_internal VALUES(6, 1, 2, 'Volume', 'TotalSpaceUtilization', 7, 'UtilizationThreshold') GO /**********************************************************************/ /* Create the sysutility_ucp_policy_target_conditions_internal table */ /* This table contains metadata information to build the target set condition */ /* Following are the values supported by each of the below attributes */ /* rollup_object_type: DeployedDac - 1, ManagedInstance - 2, Computer - 3 */ /* target_type: Computer - 1, DataFile - 2, LogFile - 3, Server - 4, DeployedDac - 5, Volume - 6 */ /* resource_type: StorageSpace - 1, StorageIO - 2, Processor - 3, Memory - 4, NetworkIO - 5 */ /* utilization_type: UnderUtilization - 1, OverUtilization - 2 */ /* operator_type: = AND - 1, OR - 2, - 3, != - 4, < - 5, > - 6, <= - 7, >= - 8 */ /**********************************************************************/ IF(OBJECT_ID(N'[dbo].[sysutility_ucp_policy_target_conditions_internal]', 'U') IS NULL) BEGIN RAISERROR ( 'Creating table [dbo].[sysutility_ucp_policy_target_conditions_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [dbo].[sysutility_ucp_policy_target_conditions_internal] ( rollup_object_type INT NOT NULL, target_type INT NOT NULL, resource_type INT NOT NULL, utilization_type INT NOT NULL, facet_name SYSNAME NOT NULL, attribute_name SYSNAME NOT NULL, operator_type INT NOT NULL, property_name SYSNAME NOT NULL CONSTRAINT [PK_sysutility_ucp_policy_target_condition_internal_type] PRIMARY KEY CLUSTERED (rollup_object_type, resource_type, target_type, utilization_type, facet_name, attribute_name ASC), ); END GO /**********************************************************************/ /* Create the sysutility_ucp_policy_target_conditions */ /**********************************************************************/ IF object_id(N'dbo.sysutility_ucp_policy_target_conditions', 'V') IS NOT NULL DROP VIEW dbo.sysutility_ucp_policy_target_conditions GO CREATE VIEW dbo.sysutility_ucp_policy_target_conditions AS SELECT tc.rollup_object_type AS rollup_object_type, tc.target_type AS target_type, tc.resource_type AS resource_type, tc.utilization_type AS utilization_type, tc.facet_name AS facet_name, tc.attribute_name AS attribute_name, tc.operator_type as operator_type, tc.property_name as property_name FROM msdb.[dbo].[sysutility_ucp_policy_target_conditions_internal] tc GO DELETE FROM msdb.dbo.sysutility_ucp_policy_target_conditions_internal GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 5, 3, 0, 'DeployedDac', 'ServerInstanceName', 3, 'DacServerInstanceName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 5, 3, 0, 'DeployedDac', 'Name', 3, 'DacName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 5, 3, 1, 'DeployedDac', 'ProcessorUtilization', 5, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 5, 3, 2, 'DeployedDac', 'ProcessorUtilization', 6, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 2, 1, 0, 'Server', 'InstanceName', 3, 'DacInstanceName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 2, 1, 0, 'Server', 'NetName', 3, 'DacComputerName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 2, 1, 0, 'Database', 'Name', 3, 'DacDatabaseName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 3, 1, 0, 'Server', 'InstanceName', 3, 'DacInstanceName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 3, 1, 0, 'Server', 'NetName', 3, 'DacComputerName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(1, 3, 1, 0, 'Database', 'Name', 3, 'DacDatabaseName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 4, 3, 0, 'Server', 'InstanceName', 3, 'ServerInstanceName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 4, 3, 0, 'Server', 'NetName', 3, 'ServerNetName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 4, 3, 1, 'Server', 'ProcessorUsage', 5, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 4, 3, 2, 'Server', 'ProcessorUsage', 6, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 2, 1, 0, 'Server', 'InstanceName', 3, 'ServerInstanceName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 2, 1, 0, 'Server', 'NetName', 3, 'ServerNetName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 3, 1, 0, 'Server', 'InstanceName', 3, 'ServerInstanceName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(2, 3, 1, 0, 'Server', 'NetName', 3, 'ServerNetName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(3, 1, 3, 0, 'Computer', 'Name', 3, 'ComputerName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(3, 1, 3, 1, 'Computer', 'ProcessorUtilization', 5, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(3, 1, 3, 2, 'Computer', 'ProcessorUtilization', 6, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(3, 6, 1, 0, 'Computer', 'Name', 3, 'ComputerName') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(3, 6, 1, 1, 'Volume', 'TotalSpaceUtilization', 5, 'UtilizationThreshold') GO INSERT INTO msdb.dbo.sysutility_ucp_policy_target_conditions_internal VALUES(3, 6, 1, 2, 'Volume', 'TotalSpaceUtilization', 6, 'UtilizationThreshold') GO /**********************************************************************/ /* Create table sysutility_ucp_policy_violations_internal */ /* This table stores violations for health polices from latest policy evaluation */ /**********************************************************************/ IF(OBJECT_ID(N'dbo.sysutility_ucp_policy_violations_internal', 'U') IS NULL) BEGIN RAISERROR ('Creating table dbo.sysutility_ucp_policy_violations_internal', 0, 1) WITH NOWAIT; CREATE TABLE sysutility_ucp_policy_violations_internal ( health_policy_id INT NOT NULL, policy_id INT NOT NULL, policy_name SYSNAME NULL, history_id INT NOT NULL, detail_id INT NOT NULL, target_query_expression NVARCHAR(MAX) NULL, target_query_expression_with_id NVARCHAR(MAX) NULL, execution_date DATETIME NULL, result INT NULL, CONSTRAINT [PK_sysutility_ucp_policy_violations_internal] PRIMARY KEY CLUSTERED (policy_id, history_id, detail_id) ) END GO /**********************************************************************/ /* Create the health policy violations view */ /* This view fetches violations for health polices from latest policy evaluation */ /**********************************************************************/ IF object_id(N'dbo.sysutility_ucp_policy_violations', 'V') IS NOT NULL DROP VIEW dbo.sysutility_ucp_policy_violations GO CREATE VIEW dbo.sysutility_ucp_policy_violations AS SELECT pv.health_policy_id , pv.policy_id , pv.policy_name , pv.history_id , pv.detail_id , pv.target_query_expression , pv.target_query_expression_with_id , pv.execution_date , pv.result FROM dbo.sysutility_ucp_policy_violations_internal pv GO /**********************************************************************/ /* Create procedure sp_sysutility_ucp_get_policy_violations */ /* This SP is used to fetch the violations for health polices from */ /* latest policy evaluation and cache them in the intermediate table */ /**********************************************************************/ IF OBJECT_ID ('dbo.sp_sysutility_ucp_get_policy_violations') IS NOT NULL BEGIN RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_get_policy_violations', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_get_policy_violations END; GO RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_get_policy_violations', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE dbo.sp_sysutility_ucp_get_policy_violations WITH EXECUTE AS OWNER AS BEGIN -- Clear the existing policy violations TRUNCATE TABLE dbo.sysutility_ucp_policy_violations_internal -- Cache the latest policy violations for non-volatile resources -- The health state for non-volatile resource is determined based on -- the latest policy violation against the target (file, volume) type. INSERT INTO dbo.sysutility_ucp_policy_violations_internal SELECT p.health_policy_id , p.policy_id , p.policy_name , d.history_id , d.detail_id , d.target_query_expression , d.target_query_expression_with_id , d.execution_date , d.result FROM msdb.dbo.sysutility_ucp_policies p INNER JOIN msdb.dbo.syspolicy_policy_execution_history_internal h ON h.policy_id = p.policy_id INNER JOIN msdb.dbo.syspolicy_policy_execution_history_details_internal d ON d.history_id = h.history_id WHERE p.resource_type = 1 -- Filter non-volatile resources (currently storage type only) -- PBM stores the end_date in local time so convert the 'latest_processing_time' datetimeoffset to local datetime before compare AND h.end_date >= (SELECT CONVERT(DATETIME, latest_processing_time) FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal]) AND h.is_full_run = 1 AND h.result = 0 AND d.result = 0; -- Get the policy evaluation count for volatile resources over the trailing window. -- The health state for volatile resource is determined based on the policy -- violation against the target (cpu) type over a trailing window and should -- exeed the occurrence frequency percent. E.g. a tartget can be considered -- as over utilized if its violating the policy for last 3 out of 4 evaluations -- (1 hour trailing window and 70 % occurrence frequency) SELECT p.policy_id , MAX(h.end_date) execution_date , CASE WHEN 0 = COUNT(*) THEN 1 ELSE COUNT(*) END AS evaluation_count , p.utilization_type , p.health_policy_id , p.policy_name , pc.occurence_frequency INTO #policy_evaluations FROM msdb.dbo.sysutility_ucp_policies p INNER JOIN msdb.dbo.syspolicy_policy_execution_history_internal h ON p.policy_id = h.policy_id INNER JOIN msdb.dbo.sysutility_ucp_policy_configuration pc ON p.utilization_type = pc.utilization_type WHERE h.end_date >= DATEADD(MI, -60*pc.trailing_window, CURRENT_TIMESTAMP) AND h.is_full_run = 1 AND p.resource_type = 3 -- Filter volatile resources (currently cpu type only) GROUP BY p.policy_id , p.utilization_type , p.health_policy_id , p.policy_name , pc.occurence_frequency; -- Get the policy violation count for the target types over the trailing window -- Note: -- 1. If the trailing window is size increased, this computation will continue to -- use the exiting violations in the history against the newly configured window size. -- It will only be effective after the full trailing window size is reached. -- 2. If the occurrence frequency is changed, it will be effective in the next run of the -- health state computation. SELECT p.policy_id , d.target_query_expression , COUNT(*) AS violation_count , MAX(h.history_id) as history_id , MAX(d.detail_id) AS detail_id INTO #policy_violations FROM msdb.dbo.sysutility_ucp_policies p INNER JOIN msdb.dbo.syspolicy_policy_execution_history_internal h ON p.policy_id = h.policy_id INNER JOIN msdb.dbo.syspolicy_policy_execution_history_details_internal d ON d.history_id = h.history_id INNER JOIN msdb.dbo.sysutility_ucp_policy_configuration pc ON p.utilization_type = pc.utilization_type WHERE h.end_date >= DATEADD(MI, -60*pc.trailing_window, CURRENT_TIMESTAMP) AND h.is_full_run = 1 AND h.result = 0 AND d.result = 0 AND p.resource_type = 3 -- Filter volatile resources (currently cpu type only) GROUP BY p.policy_id, d.target_query_expression; INSERT INTO dbo.sysutility_ucp_policy_violations_internal SELECT pe.health_policy_id , pe.policy_id , pe.policy_name , pv.history_id , pv.detail_id , pv.target_query_expression , N'' AS target_query_expression_with_id , pe.execution_date , 0 AS result FROM #policy_evaluations pe INNER JOIN #policy_violations pv ON pe.policy_id = pv.policy_id WHERE pe.occurence_frequency <= ((pv.violation_count * 100) / pe.evaluation_count); END GO --********************************************************************** -- Create function fn_encode_sqlname_for_powershell -- Function description: -- Encodes the sql names making it suitable for powershell representation -- This is required as some of the characters conflict with PS commands -- More details: http://msdn.microsoft.com/en-us/library/cc281841.aspx --Parameter description: --1. @sql_name - the sql name that needs encoding --********************************************************************* IF(OBJECT_ID(N'dbo.fn_encode_sqlname_for_powershell', 'FN') IS NOT NULL) BEGIN RAISERROR ('Dropping function dbo.fn_encode_sqlname_for_powershell', 0, 1) WITH NOWAIT; DROP FUNCTION dbo.fn_encode_sqlname_for_powershell END GO RAISERROR ('Creating function dbo.fn_encode_sqlname_for_powershell', 0, 1) WITH NOWAIT; GO CREATE FUNCTION dbo.fn_encode_sqlname_for_powershell ( @sql_name SYSNAME ) RETURNS SYSNAME AS BEGIN DECLARE @encoded_name SYSNAME = @sql_name SET @encoded_name = REPLACE(@encoded_name, N'%', N'%25') SET @encoded_name = REPLACE(@encoded_name, N'\', N'%5C') SET @encoded_name = REPLACE(@encoded_name, N'/', N'%2F') SET @encoded_name = REPLACE(@encoded_name, N':', N'%3A') SET @encoded_name = REPLACE(@encoded_name, N'<', N'%3C') SET @encoded_name = REPLACE(@encoded_name, N'>', N'%3E') SET @encoded_name = REPLACE(@encoded_name, N'*', N'%2A') SET @encoded_name = REPLACE(@encoded_name, N'?', N'%3F') SET @encoded_name = REPLACE(@encoded_name, N'[', N'%5B') SET @encoded_name = REPLACE(@encoded_name, N']', N'%5D') SET @encoded_name = REPLACE(@encoded_name, N'|', N'%7C') RETURN @encoded_name END GO --********************************************************************** -- Create procedure sp_sysutility_ucp_delete_policy_history -- Procedure description: -- This SP purges the utility resource health policy evaluation results history and details -- For volatile resources, it deletes the records older than the specified trailing window time frame -- For non-volatile resources, it deletes the records older than the current processing time -- Since this SP deletes (many) records, it could possibly affect the performance due to impact on indexes -- We should consider droping and recreating the indexes if the purged records are huge in number. --********************************************************************** IF OBJECT_ID ('dbo.sp_sysutility_ucp_delete_policy_history') IS NOT NULL BEGIN RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_delete_policy_history', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_delete_policy_history END; RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_delete_policy_history', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE dbo.sp_sysutility_ucp_delete_policy_history WITH EXECUTE AS OWNER AS BEGIN SET NOCOUNT ON; DECLARE @over_utilization_trailing_window INT = 1 DECLARE @under_utilization_trailing_window INT = 1 DECLARE @rows_affected bigint; DECLARE @delete_batch_size int; -- As we delete the master record in the history table which cascades -- to foreign key records in details table; keep the delete batch size to 100. SET @delete_batch_size = 100; SET @rows_affected = -1; -- Get the configured over utilization trailing window SELECT @over_utilization_trailing_window = CAST(ci.current_value AS INT) FROM msdb.dbo.sysutility_ucp_configuration_internal ci WHERE ci.name = 'OverUtilizationTrailingWindow' -- Get the configured under utilization trailing window SELECT @under_utilization_trailing_window = CAST(ci.current_value AS INT) FROM msdb.dbo.sysutility_ucp_configuration_internal ci WHERE ci.name = 'UnderUtilizationTrailingWindow' -- Purge volatile resource policy evaluation history against over utilization trailing window DECLARE @max_end_date datetime; SET @max_end_date = DATEADD(HH, -@over_utilization_trailing_window, CURRENT_TIMESTAMP); SET @rows_affected = -1; WHILE (@rows_affected != 0) BEGIN -- We use sp_executesql here because the values of @delete_batch_size and @max_end_date could -- influence plan selection. These are variables that have unknown values when the plan for the -- proc is compiled. By deferring compilation until the variables have taken on their final values, -- we give the optimizer information that it needs to choose the best possible plan. We could also -- use an OPTION(RECOMPILE) hint to accomplish the same thing, but the sp_executesql approach avoids -- paying the plan compile cost for each loop iteration. EXEC sp_executesql N' DELETE TOP (@delete_batch_size) h FROM msdb.dbo.syspolicy_policy_execution_history_internal h INNER JOIN msdb.dbo.sysutility_ucp_policies p ON p.policy_id = h.policy_id WHERE p.resource_type = 3 -- processor resource type AND p.utilization_type = 2 -- over-utilization AND h.end_date < @max_end_date', N'@delete_batch_size int, @max_end_date datetime', @delete_batch_size = @delete_batch_size, @max_end_date = @max_end_date; SET @rows_affected = @@ROWCOUNT; END; -- Purge volatile resource policy evaluation history against under utilization trailing window SET @max_end_date = DATEADD(HH, -@under_utilization_trailing_window, CURRENT_TIMESTAMP); SET @rows_affected = -1; WHILE (@rows_affected != 0) BEGIN EXEC sp_executesql N' DELETE TOP (@delete_batch_size) h FROM msdb.dbo.syspolicy_policy_execution_history_internal h INNER JOIN msdb.dbo.sysutility_ucp_policies p ON p.policy_id = h.policy_id WHERE p.resource_type = 3 -- processor resource type AND p.utilization_type = 1 -- under-utilization AND h.end_date < @max_end_date', N'@delete_batch_size int, @max_end_date datetime', @delete_batch_size = @delete_batch_size, @max_end_date = @max_end_date; SET @rows_affected = @@ROWCOUNT; END; -- Purge non-volatile resource policy evaluation history older than the current processing_time recorded -- The latest policy evaluation results are not purged to avoid potential conflicts with the health -- state computation running simultaneoulsy in the caching (master) job during the same time schedule. SET @rows_affected = -1; -- PBM stores the end_date in local time so convert the 'latest_processing_time' datetimeoffset to a local datetime SELECT @max_end_date = CONVERT(DATETIME, latest_processing_time) FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal]; WHILE (@rows_affected != 0) BEGIN EXEC sp_executesql N' DELETE TOP (@delete_batch_size) h FROM msdb.dbo.syspolicy_policy_execution_history_internal h INNER JOIN msdb.dbo.sysutility_ucp_policies p ON p.policy_id = h.policy_id WHERE p.resource_type = 1 -- storage space resource type AND h.end_date < @max_end_date', N'@delete_batch_size int, @max_end_date datetime', @delete_batch_size = @delete_batch_size, @max_end_date = @max_end_date; SET @rows_affected = @@ROWCOUNT; END; END GO --********************************************************************** -- Create function fn_sysutility_ucp_get_global_health_policy -- Function description: -- Identifies the global policy for a given object type -- 1. Check if there is a global policy for that object type in a target type -- 2. Check if there is a global policy for that object type at utility level -- Parameter description: -- 1. @rollup_object_type - type of the object (0: global, 1: dac, 2: server) -- 2. @target_type - target resource object type (e.g. file, logfile, volume, computer etc) -- 3. @resource_type - type of resource monitored (1: storage space, 2: storage I/O, 3: processor, 4: memory, 5: network I/O) -- 4. @utilization_type - type of the utilization (1: under utilization, 2: over utilization) --********************************************************************** IF(OBJECT_ID(N'dbo.[fn_sysutility_ucp_get_global_health_policy]', 'FN') IS NOT NULL) BEGIN RAISERROR ('Dropping function dbo.[fn_sysutility_ucp_get_global_health_policy]', 0, 1) WITH NOWAIT; DROP FUNCTION dbo.[fn_sysutility_ucp_get_global_health_policy] END GO RAISERROR ('Creating function dbo.[fn_sysutility_ucp_get_global_health_policy]', 0, 1) WITH NOWAIT; GO CREATE FUNCTION dbo.[fn_sysutility_ucp_get_global_health_policy]( @rollup_object_type INT , @target_type INT , @resource_type INT , @utilization_type INT ) RETURNS INT AS BEGIN DECLARE @health_policy_id INT -- Check if there is a global policy for that object type in a target type SELECT @health_policy_id = hp.health_policy_id FROM msdb.dbo.sysutility_ucp_policies hp WHERE hp.rollup_object_type = @rollup_object_type AND hp.target_type = @target_type AND hp.resource_type = @resource_type AND hp.utilization_type = @utilization_type AND hp.is_global_policy = 1 -- If not found, check if there is a global policy for that object type at utility level. -- This is the last resort, must find the global policy here. IF @health_policy_id = 0 OR @health_policy_id IS NULL BEGIN SELECT @health_policy_id = hp.health_policy_id FROM msdb.dbo.sysutility_ucp_policies hp WHERE hp.rollup_object_type = 0 AND hp.target_type = @target_type AND hp.resource_type = @resource_type AND hp.utilization_type = @utilization_type AND hp.is_global_policy = 1 END RETURN @health_policy_id END GO --********************************************************************** --********************************************************************** -- Create function fn_sysutility_ucp_get_applicable_policy -- Function description: -- Identifies the applciable policy for a given object represented by URN -- 1. Checking if there is an overridden policy for that object -- 2. Checking if there is a global policy for that object type -- Parameter description: -- 1. @rollup_object_urn - urn of the object -- 2. @rollup_object_type - type of the object (0: global, 1: dac, 2: server) -- 3. @target_type - target resource object type (e.g. file, logfile, volume, computer etc) -- 4. @resource_type - type of resource monitored (1: storage space, 2: storage I/O, 3: processor, 4: memory, 5: network I/O) -- 5. @utilization_type - type of the utilization (1: under utilization, 2: over utilization) --********************************************************************** IF(OBJECT_ID(N'dbo.[fn_sysutility_ucp_get_applicable_policy]', 'FN') IS NOT NULL) BEGIN RAISERROR ('Dropping function dbo.[fn_sysutility_ucp_get_applicable_policy]', 0, 1) WITH NOWAIT; DROP FUNCTION dbo.[fn_sysutility_ucp_get_applicable_policy] END GO RAISERROR ('Creating function dbo.[fn_sysutility_ucp_get_applicable_policy]', 0, 1) WITH NOWAIT; GO CREATE FUNCTION dbo.[fn_sysutility_ucp_get_applicable_policy]( @rollup_object_urn NVARCHAR(4000) , @rollup_object_type INT , @target_type INT , @resource_type INT , @utilization_type INT ) RETURNS INT AS BEGIN DECLARE @health_policy_id INT -- Check if there is an overridden policy for the rollup object SELECT @health_policy_id = hp.health_policy_id FROM msdb.dbo.sysutility_ucp_policies hp WHERE hp.rollup_object_urn = @rollup_object_urn AND hp.rollup_object_type = @rollup_object_type AND hp.target_type = @target_type AND hp.resource_type = @resource_type AND hp.utilization_type = @utilization_type -- If no overridden policy exist, get the global policy -- Check if the specific rollup_object has the global policy IF @health_policy_id = 0 OR @health_policy_id IS NULL BEGIN SELECT @health_policy_id = msdb.dbo.fn_sysutility_ucp_get_global_health_policy(@rollup_object_type , @target_type , @resource_type , @utilization_type) END RETURN @health_policy_id END GO /**********************************************************************/ /* Create function fn_sysutility_ucp_get_aggregated_failure_count */ /**********************************************************************/ IF(OBJECT_ID(N'[dbo].[fn_sysutility_ucp_get_aggregated_failure_count]', 'FN') IS NOT NULL) BEGIN RAISERROR ('Dropping function [fn_sysutility_ucp_get_aggregated_failure_count]', 0, 1) WITH NOWAIT; DROP FUNCTION [dbo].[fn_sysutility_ucp_get_aggregated_failure_count] END GO RAISERROR ('Creating function [fn_sysutility_ucp_get_aggregated_failure_count]', 0, 1) WITH NOWAIT; GO CREATE FUNCTION [dbo].[fn_sysutility_ucp_get_aggregated_failure_count](@policy_name SYSNAME, @target_query_expression NVARCHAR(max)) RETURNS INT AS BEGIN DECLARE @count INT SET @count = 0; SELECT @count = COUNT(hs.result) FROM msdb.dbo.sysutility_ucp_policy_violations hs INNER JOIN msdb.dbo.syspolicy_policies p ON hs.policy_id = p.policy_id WHERE (hs.target_query_expression_with_id LIKE +'%'+@target_query_expression+'%' ESCAPE '\' OR hs.target_query_expression LIKE +'%'+@target_query_expression+'%') AND hs.result = 0 AND p.name = @policy_name RETURN @count END GO /**********************************************************************/ /* Create function fn_sysutility_ucp_get_policy_violations */ /**********************************************************************/ IF(OBJECT_ID(N'[dbo].[fn_sysutility_ucp_get_policy_violations]', 'TF') IS NOT NULL) BEGIN RAISERROR ('Dropping function [fn_sysutility_ucp_get_policy_violations]', 0, 1) WITH NOWAIT; DROP FUNCTION [dbo].[fn_sysutility_ucp_get_policy_violations] END GO RAISERROR ('Creating function [fn_sysutility_ucp_get_policy_violations]', 0, 1) WITH NOWAIT; GO CREATE FUNCTION [dbo].[fn_sysutility_ucp_get_policy_violations](@policy_name SYSNAME, @target_query_expression NVARCHAR(max)) RETURNS @data TABLE ( health_state_id BIGINT ) AS BEGIN INSERT INTO @data SELECT hs.detail_id FROM msdb.dbo.sysutility_ucp_policy_violations hs INNER JOIN msdb.dbo.syspolicy_policies p ON hs.policy_id = p.policy_id WHERE (hs.target_query_expression_with_id LIKE +'%'+@target_query_expression+'%' ESCAPE '\' OR hs.target_query_expression LIKE +'%'+@target_query_expression+'%') AND hs.result = 0 AND p.name = @policy_name RETURN END GO /********************************************************************************/ /********************************************************************************/ /* Create function [fn_sysutility_ucp_get_file_space_utilization_history] which returns */ /* a table containing storage utilization history */ -- @object_type : -- 0 --> Utility-level -- 1 --> Server (computer) level -- 2 --> Volume level -- 3 --> Instance level -- 4 --> Database level -- 5 --> Filegroup level -- 6 --> DataFile level -- 7 --> LogFile level -- @virtual_server_name: virtual computer name -- (logical name for the failover cluster if this is part of a cluster) -- otherwise, the name of the standalone computer -- @volume_device_id: device_id of the volume -- @server_instance_name: SQL instance name (server-qualified name) -- @database_name: Name of the database -- @filegroup_name: name of the filegroup -- @dbfile_name: Name of the datafile/logfile -- @start_time : starting time for interval -- @end_time : end time for the interval -- @aggregation_interval: This attribute is helpful setting starttime for the timespan. -- 1 --> No aggregation (raw data) -- 2 --> Hourly aggregation -- 3 --> Daily aggregation -- -- NOTE: total_space_bytes may be NULL for database, filegroup, instance objects. -- We treat that as equivalent to 0 for the purposes of this function, although -- it's not entirely clear that that's appropriate. /********************************************************************************/ IF(OBJECT_ID(N'[dbo].[fn_sysutility_ucp_get_file_space_utilization_history]') IS NOT NULL) BEGIN RAISERROR ('Dropping function [fn_sysutility_ucp_get_file_space_utilization_history]', 0, 1) WITH NOWAIT; DROP FUNCTION [dbo].[fn_sysutility_ucp_get_file_space_utilization_history] END GO RAISERROR ('Creating function [fn_sysutility_ucp_get_file_space_utilization_history]', 0, 1) WITH NOWAIT; GO CREATE FUNCTION [dbo].[fn_sysutility_ucp_get_file_space_utilization_history]( @object_type TINYINT, @virtual_server_name SYSNAME, @volume_device_id SYSNAME, @server_instance_name SYSNAME, @database_name SYSNAME, @filegroup_name SYSNAME, @database_file_name SYSNAME, @start_time DATETIMEOFFSET(7), @end_time DATETIMEOFFSET(7), @aggregation_interval TINYINT ) RETURNS TABLE AS RETURN ( SELECT CASE WHEN ISNULL(total_space_bytes, 0) = 0 THEN 0 ELSE (used_space_bytes * 100)/total_space_bytes END AS storage_utilization_percent, CONVERT(BIGINT, used_space_bytes) AS storage_utilization_in_bytes, CONVERT(BIGINT, ISNULL(total_space_bytes, 0)) AS storage_capacity_in_bytes, processing_time as sample_time FROM dbo.syn_sysutility_ucp_space_utilization WHERE @object_type = object_type AND @aggregation_interval = aggregation_type AND (processing_time BETWEEN @start_time AND @end_time) AND ISNULL(@virtual_server_name, '') = virtual_server_name AND ISNULL(@volume_device_id, '') = volume_device_id AND ISNULL(@server_instance_name, '') = server_instance_name AND ISNULL(@database_name, '') = database_name AND ISNULL(@filegroup_name, '') = [filegroup_name] AND ISNULL(@database_file_name, '') = [dbfile_name] ) GO /********************************************************************************/ /* Create function [fn_sysutility_ucp_get_cpu_utilization_history] which returns */ /* a table containing processor utilization history */ -- @object_type : -- 1 --> Server (computer) level -- 2 --> Instance level -- 3 --> Database level (DAC) -- @physical_server_name: (physical) computer name -- @server_instance_name: SQL instance name (server-qualified name) -- @dac_name: Name of the DAC instance (also the database_name) -- @start_time : starting time for interval -- @end_time : end time for the interval -- @aggregation_interval: This attribute is helpful setting starttime for the timespan. -- 1 --> No aggregation (raw data) -- 2 --> Hourly aggregation -- 3 --> Daily aggregation /********************************************************************************/ IF(OBJECT_ID(N'[dbo].[fn_sysutility_ucp_get_cpu_utilization_history]') IS NOT NULL) BEGIN RAISERROR ('Dropping function [fn_sysutility_ucp_get_cpu_utilization_history]', 0, 1) WITH NOWAIT; DROP FUNCTION [dbo].[fn_sysutility_ucp_get_cpu_utilization_history] END GO RAISERROR ('Creating function [fn_sysutility_ucp_get_cpu_utilization_history]', 0, 1) WITH NOWAIT; GO CREATE FUNCTION [dbo].[fn_sysutility_ucp_get_cpu_utilization_history]( @object_type TINYINT, @physical_server_name SYSNAME, @server_instance_name SYSNAME, @dac_name SYSNAME, @start_time DATETIMEOFFSET(7), @end_time DATETIMEOFFSET(7), @aggregation_interval TINYINT ) RETURNS TABLE AS RETURN ( SELECT percent_total_cpu_utilization AS processor_utilization_percent, processing_time AS sample_time FROM dbo.syn_sysutility_ucp_cpu_utilization WHERE @object_type = object_type AND @aggregation_interval = aggregation_type AND (processing_time BETWEEN @start_time AND @end_time) AND ISNULL(@physical_server_name, '') = physical_server_name AND ISNULL(@server_instance_name, '') = server_instance_name AND ISNULL(@dac_name, '') = database_name ) GO -------------------------------------------------------------------------------------------------- -- View that exposes the latest properties for all installed DACs. -- This view wraps a synonym that gets redirected to a MDW table after sysutility_mdw is created. -------------------------------------------------------------------------------------------------- IF OBJECT_ID(N'[dbo].[sysutility_ucp_deployed_dacs]', 'V') IS NOT NULL BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_deployed_dacs]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_deployed_dacs]; END; GO RAISERROR ('Creating view [dbo].[sysutility_ucp_deployed_dacs]', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_deployed_dacs AS SELECT dac_id, -- todo (VSTS #345036): This column will be removed dac_name, dac_deploy_date AS dac_deployed_date, dac_description AS dac_description, dac_percent_total_cpu_utilization AS dac_percent_total_cpu_utilization, server_instance_name AS dac_server_instance_name, physical_server_name AS dac_physical_server_name, batch_time AS dac_collection_time, processing_time AS dac_processing_time, urn, powershell_path FROM dbo.syn_sysutility_ucp_dacs; GO /**********************************************************************/ /* create the computer object view that utilizes the data collected */ /* in the MDW */ /* */ /* We keep track of two names here - the "physical_server_name" and */ /* the "virtual_server_name". In most cases, these two have the same */ /* value. However, in the case of failover-cluster-instances, the */ /* virtual_server_name refers to the logical name for the cluster, */ /* while the physical_server_name refers to the name of the computer */ /* within that cluster. */ /* In general, we use physical_server_name across the board, except */ /* for "shared" resources like space-utilization, where the virtual */ /* name is more appropriate */ /**********************************************************************/ IF OBJECT_ID(N'dbo.sysutility_ucp_computers', 'V') IS NOT NULL BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_computers]', 0, 1) WITH NOWAIT; DROP VIEW dbo.sysutility_ucp_computers; END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_computers]', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_computers AS SELECT server_table.id AS computer_id -- todo (VSTS #345036): This column will be removed , server_table.virtual_server_name AS virtual_server_name , server_table.physical_server_name AS physical_server_name , server_table.is_clustered_server AS is_clustered , server_table.percent_total_cpu_utilization AS processor_utilization , server_table.cpu_name AS cpu_name , server_table.cpu_max_clock_speed AS cpu_max_clock_speed , server_table.processing_time AS processing_time , urn , powershell_path FROM [dbo].[syn_sysutility_ucp_computers] as server_table GO -- ============================================= -- Create View : sysutility_ucp_volumes -- ============================================= IF OBJECT_ID(N'dbo.sysutility_ucp_volumes', 'V') IS NOT NULL BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_volumes]', 0, 1) WITH NOWAIT; DROP VIEW dbo.sysutility_ucp_volumes END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_volumes]', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_volumes AS SELECT [ID] AS volume_id -- todo (VSTS #345036): This column will be removed , physical_server_name AS physical_server_name , virtual_server_name AS virtual_server_name , volume_name , volume_device_id , total_space_available AS total_space , total_space_utilized AS total_space_used , percent_total_space_utilization AS total_space_utilization FROM dbo.syn_sysutility_ucp_volumes; GO -- ============================================= -- Create View : sysutility_ucp_utility_space_utilization -- ============================================= IF OBJECT_ID(N'dbo.sysutility_ucp_utility_space_utilization', 'V') IS NOT NULL BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_utility_space_utilization]', 0, 1) WITH NOWAIT; DROP VIEW dbo.sysutility_ucp_utility_space_utilization END; GO RAISERROR ('Creating view [dbo].[sysutility_ucp_utility_space_utilization]', 0, 1) WITH NOWAIT; GO --- --- Gets the total utility space utilization. --- The funny left-outer-join is to account for cases where there is no "utility-wide" entry yet --- typically, right at bootstrap time --- CREATE VIEW dbo.sysutility_ucp_utility_space_utilization AS SELECT ISNULL(S2.total_space_bytes, 0) AS total_utility_storage, ISNULL(S2.used_space_bytes, 0) AS total_utilized_space FROM (SELECT 1 AS x) AS S1 LEFT OUTER JOIN (SELECT total_space_bytes, used_space_bytes FROM dbo.syn_sysutility_ucp_space_utilization WHERE object_type = 0 AND -- utility-wide information aggregation_type = 0 AND -- detail-information processing_time = (SELECT latest_processing_time FROM msdb.dbo.sysutility_ucp_processing_state_internal) ) AS S2 ON (1=1) GO -- ============================================= -- Create View : [sysutility_ucp_instances] -- All of the properties are auto-generated using the below select statement -- select ', [' +property_name+']' from [msdb].[dbo].[sysutility_mi_smo_property_configurations_internal] where object_type = 1 -- ============================================= IF OBJECT_ID(N'dbo.sysutility_ucp_instances', 'V') IS NOT NULL BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_instances]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_instances] END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_instances]', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_instances AS SELECT [urn] , [powershell_path] , [processing_time] , [batch_time] AS [collection_time] , [AuditLevel] , [BackupDirectory] , [BrowserServiceAccount] , [BrowserStartMode] , [BuildClrVersionString] , [BuildNumber] , [Collation] , [CollationID] , [ComparisonStyle] , [ComputerNamePhysicalNetBIOS] , [DefaultFile] , [DefaultLog] , [Edition] , [EngineEdition] , [ErrorLogPath] , [FilestreamShareName] , [InstallDataDirectory] , [InstallSharedDirectory] , [InstanceName] , [IsCaseSensitive] , [IsClustered] , [IsFullTextInstalled] , [IsSingleUser] , [Language] , [MailProfile] , [MasterDBLogPath] , [MasterDBPath] , [MaxPrecision] , [Name] , [NamedPipesEnabled] , [NetName] , [NumberOfLogFiles] , [OSVersion] , [PerfMonMode] , [PhysicalMemory] , [Platform] , [Processors] , [ProcessorUsage] , [Product] , [ProductLevel] , [ResourceVersionString] , [RootDirectory] , [ServerType] , [ServiceAccount] , [ServiceInstanceId] , [ServiceName] , [ServiceStartMode] , [SqlCharSet] , [SqlCharSetName] , [SqlDomainGroup] , [SqlSortOrder] , [SqlSortOrderName] , [Status] , [TapeLoadWaitTime] , [TcpEnabled] , [VersionMajor] , [VersionMinor] , [VersionString] FROM dbo.syn_sysutility_ucp_smo_servers; GO -- ============================================= -- Create View : [sysutility_ucp_databases] -- All of the properties are auto-generated using the below select statement -- select ', [' +property_name+']' from [msdb].[dbo].[sysutility_mi_smo_properties_to_collect_internal] where object_type = 2 -- ============================================= IF OBJECT_ID(N'[dbo].[sysutility_ucp_databases]', 'V') IS NOT NULL BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_databases]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_databases] END; GO RAISERROR ('Creating view [dbo].[sysutility_ucp_databases]', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_databases AS SELECT S.urn , S.parent_urn , S.Collation , S.CompatibilityLevel , S.CreateDate , S.EncryptionEnabled , S.Name , S.server_instance_name , S.powershell_path , S.RecoveryModel , [S].[Trustworthy] , [S].processing_time FROM [dbo].[syn_sysutility_ucp_databases] AS S GO -- ============================================= -- Create View : [sysutility_ucp_filegroups] -- All of the properties are auto-generated using the below select statement -- select ', [' +property_name+']' from [msdb].[dbo].[sysutility_mi_smo_properties_to_collect_internal] where object_type = 4 -- ============================================= IF OBJECT_ID(N'[dbo].[sysutility_ucp_filegroups]', 'V') IS NOT NULL BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_filegroups]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_filegroups] END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_filegroups]', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_filegroups AS SELECT [S].[urn] , [S].[parent_urn] , [S].[Name] , [S].[server_instance_name] , [S].[database_name] , [S].[powershell_path] , [S].[processing_time] FROM [dbo].[syn_sysutility_ucp_filegroups] S GO -- ============================================= -- Create View : [sysutility_ucp_datafiles] -- All of the properties are auto-generated using the below select statement -- select ', [' +property_name+']' from [msdb].[dbo].[sysutility_mi_smo_properties_to_collect_internal] where object_type = 5 -- ============================================= IF OBJECT_ID(N'[dbo].[sysutility_ucp_datafiles]', 'V') IS NOT NULL BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_datafiles]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_datafiles] END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_datafiles]', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_datafiles AS SELECT [S].[urn] , [S].[parent_urn] , [S].[Growth] , [S].[GrowthType] , [S].[MaxSize] , [S].[Name] , [S].[Size] , [S].[UsedSpace] , [S].[FileName] , [S].[VolumeFreeSpace] , [S].[server_instance_name] , [S].[database_name] , [S].[filegroup_name] , [S].[powershell_path] , [S].[volume_name] , [S].[volume_device_id] , [S].[physical_server_name] , [S].[available_space] -- in bytes , CASE WHEN [S].[available_space] = 0.0 THEN 0.0 ELSE ([S].[UsedSpace] * 100)/[S].[available_space] END AS percent_utilization , [S].[processing_time] FROM [dbo].[syn_sysutility_ucp_datafiles] S GO -- ============================================= -- Create View : [sysutility_ucp_logfiles] -- All of the properties are auto-generated using the below select statement -- select ', [' +property_name+']' from [msdb].[dbo].[sysutility_mi_smo_properties_to_collect_internal] where object_type = 3 -- ============================================= IF OBJECT_ID(N'[dbo].[sysutility_ucp_logfiles]', 'V') IS NOT NULL BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_logfiles]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_logfiles] END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_logfiles]', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_logfiles AS SELECT [S].[urn] , [S].[parent_urn] , [S].[Growth] , [S].[GrowthType] , [S].[MaxSize] , [S].[Name] , [S].[Size] , [S].[UsedSpace] , [S].[FileName] , [S].[VolumeFreeSpace] , [S].[server_instance_name] , [S].[database_name] , [S].[powershell_path] , [S].[volume_name] , [S].[volume_device_id] , [S].[physical_server_name] , [S].[available_space] -- in bytes , CASE WHEN [S].[available_space] = 0.0 THEN 0.0 ELSE ([S].[UsedSpace] * 100)/[S].[available_space] END AS percent_utilization , [S].[processing_time] FROM [dbo].[syn_sysutility_ucp_logfiles] S GO -- ============================================= -- Create View : [sysutility_ucp_database_files] -- Simple (union) view over sysutility_ucp_datafiles and sysutility_ucp_logfiles -- ============================================= IF OBJECT_ID(N'[dbo].[sysutility_ucp_database_files]', N'V') IS NOT NULL BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_database_files]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_database_files] END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_database_files]', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_database_files AS SELECT [S].[server_instance_name], [S].[database_name], [S].[filegroup_name], [S].[Name] AS [Name], [S].[volume_name], [S].[volume_device_id], [S].[FileName], [S].[Growth], [S].[GrowthType], [S].[processing_time], [S].[powershell_path], 1 AS [file_type], [S].[MaxSize], [S].[Size], [S].[UsedSpace], [S].[available_space], [S].[percent_utilization] FROM [dbo].[sysutility_ucp_datafiles] AS S UNION ALL SELECT [S].[server_instance_name], [S].[database_name], N'' AS [filegroup_name], [S].[Name] AS [Name], [S].[volume_name], [S].[volume_device_id], [S].[FileName], [S].[Growth], [S].[GrowthType], [S].[processing_time], [S].[powershell_path], 2 AS [file_type], [S].[MaxSize], [S].[Size], [S].[UsedSpace], [S].[available_space], [S].[percent_utilization] FROM [dbo].[sysutility_ucp_logfiles] AS S GO /****************************************************************************/ /* */ /* Creating a utilization view on top of several object views in CMR which */ /* collects the data and log file related data for all databases in the */ /* utility */ /* */ /****************************************************************************/ IF (OBJECT_ID(N'[dbo].[sysutility_ucp_mi_database_file_space_utilizations]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_mi_database_file_space_utilizations]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_mi_database_file_space_utilizations] END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_mi_database_file_space_utilizations]', 0, 1) WITH NOWAIT; GO CREATE VIEW [dbo].[sysutility_ucp_mi_database_file_space_utilizations] AS SELECT df.server_instance_name, df.database_name, df.filegroup_name, df.Name, df.volume_name, df.volume_device_id, df.FileName AS databasefile_name, df.percent_utilization AS current_utilization, df.UsedSpace AS used_space, df.available_space AS available_space, 10 AS under_utilization, 70 AS over_utilization, df.file_type, df.GrowthType AS growth_type FROM msdb.dbo.sysutility_ucp_database_files AS df GO /****************************************************************************/ /* */ /* Creating a utilization view on top of several object views in CMR which */ /* collects the data and log file related data for all deployed dacs in the */ /* utility */ /* */ /****************************************************************************/ IF (OBJECT_ID(N'[dbo].[sysutility_ucp_dac_database_file_space_utilizations]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_dac_database_file_space_utilizations]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_dac_database_file_space_utilizations] END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_dac_database_file_space_utilizations]', 0, 1) WITH NOWAIT; GO CREATE VIEW [dbo].[sysutility_ucp_dac_database_file_space_utilizations] AS SELECT dd.dac_server_instance_name AS server_instance_name, dd.dac_name AS dac_name, df.[filegroup_name], df.[Name], df.volume_name, df.volume_device_id, df.FileName AS databasefile_name, df.percent_utilization AS current_utilization, df.UsedSpace AS used_space, df.available_space, 10 AS under_utilization, 70 AS over_utilization, df.file_type, df.GrowthType AS growth_type FROM msdb.dbo.sysutility_ucp_deployed_dacs AS dd, msdb.dbo.sysutility_ucp_database_files AS df WHERE dd.dac_server_instance_name = df.server_instance_name AND dd.dac_name = df.database_name GO /****************************************************************************/ /* */ /* Creating a utilization view on top of several object views in CMR which */ /* collects the volume related data for all databases in the utility */ /* */ /****************************************************************************/ IF (OBJECT_ID(N'[dbo].[sysutility_ucp_mi_volume_space_utilizations]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_mi_volume_space_utilizations]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_mi_volume_space_utilizations] END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_mi_volume_space_utilizations]', 0, 1) WITH NOWAIT; GO CREATE VIEW [dbo].[sysutility_ucp_mi_volume_space_utilizations] AS( -- TODO VSTS 280036(rnagpal) : Temporarily Keeping under_utilization to 10 and over_utilization to 70 for now -- since we might reintroduce them in near future which will not require any interface change for the -- Utility object model / UI. Presently, we are not exposing under and over utilization thresholds in UI -- so they are not exposed. If that remains the same till KJ CTP2, i'll remove them. SELECT vol.physical_server_name AS physical_server_name, svr.Name as server_instance_name, vol.volume_name AS volume_name, vol.volume_device_id AS volume_device_id, vol.total_space_utilization AS current_utilization, vol.total_space_used AS used_space, vol.total_space AS available_space, 10 AS under_utilization, 70 AS over_utilization FROM msdb.dbo.sysutility_ucp_volumes AS vol, msdb.dbo.sysutility_ucp_instances AS svr WHERE vol.physical_server_name = svr.ComputerNamePhysicalNetBIOS) GO /****************************************************************************/ /* */ /* Creating a utilization view on top of several object views in CMR which */ /* collects the volume related data for all deployed dacs in the utility */ /* */ /****************************************************************************/ IF (OBJECT_ID(N'[dbo].[sysutility_ucp_dac_volume_space_utilizations]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_dac_volume_space_utilizations]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_dac_volume_space_utilizations] END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_dac_volume_space_utilizations]', 0, 1) WITH NOWAIT; GO CREATE VIEW [dbo].[sysutility_ucp_dac_volume_space_utilizations] AS( -- TODO VSTS 280036(rnagpal) : Temporarily Keeping under_utilization to 10 and over_utilization to 70 for now -- since we might reintroduce them in near future which will not require any interface change for the -- Utility object model / UI. Presently, we are not exposing under and over utilization thresholds in UI -- so they are not exposed. If that remains the same till KJ CTP2, i'll remove them. SELECT vol.physical_server_name AS physical_server_name, dd.dac_name AS dac_name, dd.dac_server_instance_name AS server_instance_name, vol.volume_name AS volume_name, vol.volume_device_id AS volume_device_id, vol.total_space_utilization AS current_utilization, vol.total_space_used AS used_space, vol.total_space AS available_space, 10 AS under_utilization, 70 AS over_utilization FROM msdb.dbo.sysutility_ucp_volumes AS vol, msdb.dbo.sysutility_ucp_deployed_dacs AS dd WHERE vol.physical_server_name = dd.dac_physical_server_name) GO /****************************************************************************/ /* */ /* Creating a utilization view on top of several object views in CMR which */ /* collects the server related data for all instances in the utility */ /* */ /****************************************************************************/ -- TODO VSTS 280036(rnagpal) : Temporarily Keeping under_utilization to 10 and over_utilization to 70 for now -- since we might reintroduce them in near future which will not require any interface change for the -- Utility object model / UI. Presently, we are not exposing under and over utilization thresholds in UI -- so they are not exposed. If that remains the same till KJ CTP2, i'll remove them. IF (OBJECT_ID(N'[dbo].[sysutility_ucp_mi_cpu_utilizations]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_mi_cpu_utilizations]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_mi_cpu_utilizations]; END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_mi_cpu_utilizations]', 0, 1) WITH NOWAIT; GO CREATE VIEW [dbo].[sysutility_ucp_mi_cpu_utilizations] AS SELECT svr.Name AS server_instance_name, 10 AS under_utilization, CAST(svr.ProcessorUsage AS INT) AS current_utilization, 70 AS over_utilization FROM msdb.dbo.sysutility_ucp_instances AS svr; GO /****************************************************************************/ /* */ /* Creating a utilization view on top of several object views in CMR which */ /* collects the computer related data for all computers in the utility */ /* */ /****************************************************************************/ -- TODO VSTS 280036(rnagpal) : Temporarily Keeping under_utilization to 10 and over_utilization to 70 for now -- since we might reintroduce them in near future which will not require any interface change for the -- Utility object model / UI. Presently, we are not exposing under and over utilization thresholds in UI -- so they are not exposed. If that remains the same till KJ CTP2, i'll remove them. IF (OBJECT_ID(N'[dbo].[sysutility_ucp_computer_cpu_utilizations]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_computer_cpu_utilizations]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_computer_cpu_utilizations]; END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_computer_cpu_utilizations]', 0, 1) WITH NOWAIT; GO CREATE VIEW [dbo].[sysutility_ucp_computer_cpu_utilizations] AS SELECT comp.physical_server_name AS physical_server_name, 10 AS under_utilization, comp.processor_utilization AS current_utilization, 70 AS over_utilization FROM msdb.dbo.sysutility_ucp_computers AS comp GO /****************************************************************************/ /* */ /* Creating a utilization view on top of several object views in CMR which */ /* collects the DAC related data for all DACs in the utility */ /* */ /****************************************************************************/ -- TODO VSTS 280036(rnagpal) : Temporarily Keeping under_utilization to 10 and over_utilization to 70 for now -- since we might reintroduce them in near future which will not require any interface change for the -- Utility object model / UI. Presently, we are not exposing under and over utilization thresholds in UI -- so they are not exposed. If that remains the same till KJ CTP2, i'll remove them. IF (OBJECT_ID(N'[dbo].[sysutility_ucp_dac_cpu_utilizations]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_dac_cpu_utilizations]', 0, 1) WITH NOWAIT; DROP VIEW [dbo].[sysutility_ucp_dac_cpu_utilizations]; END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_dac_cpu_utilizations]', 0, 1) WITH NOWAIT; GO CREATE VIEW [dbo].[sysutility_ucp_dac_cpu_utilizations] AS SELECT dac.dac_name AS dac_name, dac.dac_server_instance_name AS server_instance_name, 10 AS under_utilization, dac.dac_percent_total_cpu_utilization AS current_utilization, 70 AS over_utilization FROM msdb.dbo.sysutility_ucp_deployed_dacs AS dac GO /******************************************************************** Procedure sp_sysutility_ucp_provision_utility_object_internal Helper proc that grants permissions (based on object type) to a role. ********************************************************************/ IF OBJECT_ID(N'dbo.sp_sysutility_ucp_provision_utility_object_internal') IS NOT NULL BEGIN RAISERROR('Dropping procedure dbo.sp_sysutility_ucp_provision_utility_object_internal', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_provision_utility_object_internal; END GO RAISERROR('Creating procedure dbo.sp_sysutility_ucp_provision_utility_object_internal', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE dbo.sp_sysutility_ucp_provision_utility_object_internal @object_name sysname, @role_name sysname WITH EXECUTE AS CALLER AS BEGIN DECLARE @sql_stmt nvarchar(max); DECLARE @grant_type NVARCHAR(20); DECLARE @object_type char(2); DECLARE @database_name sysname; DECLARE @quoted_object_name_with_dbo nvarchar(max); SET @database_name = DB_NAME(); SET @quoted_object_name_with_dbo = 'dbo.' + QUOTENAME(@object_name); SELECT @object_type = [type] FROM sys.objects WHERE object_id = OBJECT_ID(@quoted_object_name_with_dbo); -- TSQL or CLR procs and non-inline functions -- P - stored proc (TSQL) -- PC - stored proc (SQLCLR) -- FN - scalar function (TSQL) -- FS - scalar function (SQLCLR) IF (@object_type IN ('P', 'PC', 'FN', 'FS')) BEGIN SET @grant_type = 'EXECUTE'; END -- Views, inline functions, tables -- V - view -- IF - inline function (TSQL) -- U - user table -- S - system table -- TF - table-valued function (TSQL) -- FT - table-valued function (SQLCLR) ELSE IF (@object_type IN ('V', 'IF', 'U', 'S', 'FT', 'TF')) BEGIN SET @grant_type = 'SELECT'; END; ELSE BEGIN -- The object '%s' does not exist in database '%s' or is invalid for this operation. RAISERROR (15009, 16, 1, @quoted_object_name_with_dbo, @database_name); RETURN; END; SELECT @sql_stmt = N'GRANT '+ @grant_type +' ON ' + @quoted_object_name_with_dbo + ' TO ' + QUOTENAME(@role_name); RAISERROR ('Executing: %s', 0, 1, @sql_stmt) WITH NOWAIT EXEC sp_executesql @sql_stmt; END; GO /**********************************************************************/ /* Create function fn_sysutility_ucp_get_aggregated_health This function determines the aggregated health state based on the input arguments over and under utilization policy violations. The aggregated health state computation algorithm is as follows: If both over and under utililization policy violations are 0 then its steady state (green) If there is over utilization policy violation, then its over utilized state (red) If there is under utilization policy violation (with no over utilization) then its under utilized state (orange)*/ /**********************************************************************/ IF(OBJECT_ID(N'dbo.fn_sysutility_ucp_get_aggregated_health') IS NOT NULL) BEGIN RAISERROR ('Dropping function fn_sysutility_ucp_get_aggregated_health', 0, 1) WITH NOWAIT; DROP FUNCTION dbo.fn_sysutility_ucp_get_aggregated_health END GO RAISERROR ('Creating function fn_sysutility_ucp_get_aggregated_health', 0, 1) WITH NOWAIT; GO CREATE FUNCTION [dbo].[fn_sysutility_ucp_get_aggregated_health] ( @is_over_utilized INT, @is_under_utilized INT ) RETURNS TABLE AS RETURN ((SELECT CASE WHEN 0 = @is_over_utilized AND 0 = @is_under_utilized THEN 1 ELSE CASE WHEN @is_over_utilized > 0 THEN 3 ELSE 2 END END AS val)) GO --********************************************************************** -- Create view sysutility_ucp_volume_powershell_path -- View Description: -- Returns the powershell path of the volumes -- Powershell path - SQLSERVER:\Utility\\Computers\\Volumes\ --Attribute description: -- physical_server_name - the name of the computer in the utility -- volume_name - volume mounted on the computer -- powershell_path - the path representing the volume hiearchy in powershell --********************************************************************* IF (OBJECT_ID(N'[dbo].[sysutility_ucp_volume_powershell_path]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_volume_powershell_path]', 0, 1) WITH NOWAIT; DROP VIEW dbo.sysutility_ucp_volume_powershell_path END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_volume_powershell_path]...', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_volume_powershell_path AS ( SELECT vo.physical_server_name , vo.volume_name , vo.volume_device_id , N'SQLSERVER:\Utility\'+CASE WHEN 0 = CHARINDEX(N'\', CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')), 1) THEN CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')) + N'\DEFAULT' ELSE CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')) END +N'\Computers\'+msdb.dbo.fn_encode_sqlname_for_powershell(vo.physical_server_name) +N'\Volumes\'+msdb.dbo.fn_encode_sqlname_for_powershell(vo.volume_name) AS powershell_path FROM msdb.dbo.sysutility_ucp_volumes vo ) GO --********************************************************************** -- Create view sysutility_ucp_instance_policies -- View Description: -- Returns the resource health policy details for the managed instances -- Powershell path - SQLSERVER:\SQL\ -- 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 , t.is_policy_overridden , t.processing_time FROM msdb.dbo.sysutility_ucp_dac_health_internal AS t WHERE t.set_number = (SELECT latest_health_state_id FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal]) GO /**********************************************************************/ /* Create the sysutility_ucp_mi_health */ /* This view returns the latest resource health states for MI's */ /**********************************************************************/ IF object_id(N'dbo.sysutility_ucp_mi_health', 'V') IS NOT NULL BEGIN RAISERROR ('Dropping view dbo.sysutility_ucp_mi_health', 0, 1) WITH NOWAIT; DROP VIEW dbo.sysutility_ucp_mi_health END; GO RAISERROR ('Creating view dbo.sysutility_ucp_mi_health', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_mi_health AS SELECT t.mi_name , (SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.is_volume_space_over_utilized, t.is_volume_space_under_utilized)) volume_space_health_state , (SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.is_computer_processor_over_utilized, t.is_computer_processor_under_utilized)) computer_processor_health_state , (SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.is_file_space_over_utilized, t.is_file_space_under_utilized)) file_space_health_state , (SELECT val FROM dbo.fn_sysutility_ucp_get_aggregated_health(t.is_mi_processor_over_utilized, t.is_mi_processor_under_utilized)) mi_processor_health_state , t.is_policy_overridden , t.processing_time FROM msdb.dbo.sysutility_ucp_mi_health_internal AS t WHERE t.set_number = (SELECT latest_health_state_id FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal]) GO /**********************************************************************/ -- Table sysutility_ucp_filegroups_with_policy_violations_internal */ -- This table stores an entry for each filegroup that has at least one file -- that violates the specified policy. -- LogFiles are rolled up into an entry (with filegroup_name = N'') /**********************************************************************/ IF(OBJECT_ID(N'dbo.sysutility_ucp_filegroups_with_policy_violations_internal', 'U') IS NULL) BEGIN RAISERROR ('Creating table dbo.sysutility_ucp_filegroups_with_policy_violations_internal', 0, 1) WITH NOWAIT; CREATE TABLE sysutility_ucp_filegroups_with_policy_violations_internal ( server_instance_name SYSNAME, database_name SYSNAME, [filegroup_name] SYSNAME, -- N'' for log files policy_id INT, -- id of the offending policy set_number INT NOT NULL, CONSTRAINT [PK_sysutility_ucp_filegroups_with_policy_violations_internal] PRIMARY KEY CLUSTERED (set_number, policy_id, server_instance_name, database_name, [filegroup_name]) ) END GO --********************************************************************* -- Procedure sp_sysutility_ucp_calculate_filegroups_with_policy_violations -- Description: This function identifies filegroups where *every* single file -- violates a policy (the same policy for all files in the filegroups). -- Logfiles are also captured here under a special entry (with filegroup_name = N'') --********************************************************************** IF OBJECT_ID ('dbo.sp_sysutility_ucp_calculate_filegroups_with_policy_violations') IS NOT NULL BEGIN RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_filegroups_with_policy_violations', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_filegroups_with_policy_violations END; GO RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_filegroups_with_policy_violations', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_filegroups_with_policy_violations @new_set_number INT WITH EXECUTE AS OWNER AS BEGIN INSERT INTO sysutility_ucp_filegroups_with_policy_violations_internal( server_instance_name, database_name, [filegroup_name], policy_id, set_number) SELECT fg1.server_instance_name, fg1.database_name, fg1.[filegroup_name], fg1.policy_id, @new_set_number FROM (SELECT pv.policy_id, f.server_instance_name, f.database_name, f.[filegroup_name], COUNT(*) as policy_violations FROM dbo.sysutility_ucp_database_files AS f, dbo.sysutility_ucp_policy_violations AS pv WHERE f.powershell_path = pv.target_query_expression GROUP BY pv.policy_id, f.server_instance_name, f.database_name, f.[filegroup_name]) as fg1, (SELECT f.server_instance_name, f.database_name, f.[filegroup_name], COUNT(*) as file_count FROM dbo.sysutility_ucp_database_files AS f GROUP BY f.server_instance_name, f.database_name, f.[filegroup_name]) AS fg2 WHERE fg1.server_instance_name = fg2.server_instance_name AND fg1.database_name = fg2.database_name AND fg1.[filegroup_name] = fg2.[filegroup_name] AND fg1.policy_violations = fg2.file_count END GO --********************************************************************* -- Create view sysutility_ucp_dac_cpu_utilization -- Description: Gets the dac cpu utilization based on processor violating the health policy -- Marks the dac as unhealthy if processor violate the policy -- Attributes: -- dac_name - The name of data-tier application -- dac_server_instance_name - The server\instance on which the DAC is hosted -- under_utilized_count - Flag indicating whether the processor is under utilized -- over_utilized_count - Flag indicating whether the processor is over utilized --********************************************************************** IF (OBJECT_ID(N'[dbo].[sysutility_ucp_dac_cpu_utilization]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_dac_cpu_utilization]', 0, 1) WITH NOWAIT; DROP VIEW dbo.sysutility_ucp_dac_cpu_utilization END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_dac_cpu_utilization]...', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_dac_cpu_utilization AS ( -- dac_object_type = 1 -- processor_resource_type = 3 -- target_type = 5 SELECT dp.dac_name , dp.dac_server_instance_name , SUM(CASE WHEN dp.utilization_type = 1 THEN 1 ELSE 0 END) AS under_utilized_count , SUM(CASE WHEN dp.utilization_type = 2 THEN 1 ELSE 0 END) AS over_utilized_count FROM msdb.dbo.sysutility_ucp_dac_policies AS dp INNER JOIN msdb.dbo.sysutility_ucp_policy_violations pv ON dp.policy_id = pv.policy_id AND dp.powershell_path = pv.target_query_expression WHERE dp.resource_type = 3 AND dp.target_type = 5 GROUP BY dp.dac_name, dp.dac_server_instance_name ) GO --********************************************************************* -- Create view sysutility_ucp_instance_cpu_utilization -- Description: Gets the MI cpu utilization based on processor violating the health policy -- Marks the instance as unhealthy if processor violate the policy -- Attributes: -- server_instance_name - The name of the server in the format server_name\instance_name -- under_utilized_count - Flag indicating whether the processor is under utilized -- over_utilized_count - Flag indicating whether the processor is over utilized --********************************************************************** IF (OBJECT_ID(N'[dbo].[sysutility_ucp_instance_cpu_utilization]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_instance_cpu_utilization]', 0, 1) WITH NOWAIT; DROP VIEW dbo.sysutility_ucp_instance_cpu_utilization END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_instance_cpu_utilization]...', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_instance_cpu_utilization AS ( -- server_object_type = 2 -- processor_resource_type = 3 -- target_type = 4 SELECT ip.server_instance_name AS server_instance_name , SUM(CASE WHEN ip.utilization_type = 1 THEN 1 ELSE 0 END) AS under_utilized_count , SUM(CASE WHEN ip.utilization_type = 2 THEN 1 ELSE 0 END) AS over_utilized_count FROM msdb.dbo.sysutility_ucp_instance_policies ip INNER JOIN msdb.dbo.sysutility_ucp_policy_violations pv ON ip.policy_id = pv.policy_id AND ip.powershell_path = pv.target_query_expression WHERE ip.resource_type = 3 AND ip.target_type = 4 GROUP BY ip.server_instance_name ) GO --********************************************************************* -- Create view sysutility_ucp_computer_cpu_utilization -- Description: Gets the computer cpu utilization based on processor violating the health policy -- Marks the computer as unhealthy if processor violate the policy -- Attributes: -- physical_server_name - The name of the computer on which MI/DAC is hosted -- under_utilized_count - Flag indicating whether the processor is under utilized -- over_utilized_count - Flag indicating whether the processor is over utilized --********************************************************************** IF (OBJECT_ID(N'[dbo].[sysutility_ucp_computer_cpu_utilization]', 'V') IS NOT NULL) BEGIN RAISERROR ('Dropping view [dbo].[sysutility_ucp_computer_cpu_utilization]', 0, 1) WITH NOWAIT; DROP VIEW dbo.sysutility_ucp_computer_cpu_utilization END GO RAISERROR ('Creating view [dbo].[sysutility_ucp_computer_cpu_utilization]...', 0, 1) WITH NOWAIT; GO CREATE VIEW dbo.sysutility_ucp_computer_cpu_utilization AS ( -- computer_object_type = 3 -- processor_resource_type = 3 -- target_type = 1 SELECT cp.physical_server_name as physical_server_name , SUM(CASE WHEN cp.utilization_type = 1 THEN 1 ELSE 0 END) AS under_utilized_count , SUM(CASE WHEN cp.utilization_type = 2 THEN 1 ELSE 0 END) AS over_utilized_count FROM msdb.dbo.sysutility_ucp_computer_policies cp INNER JOIN msdb.dbo.sysutility_ucp_policy_violations pv ON cp.policy_id = pv.policy_id AND cp.powershell_path = pv.target_query_expression WHERE cp.resource_type = 3 AND cp.target_type = 1 GROUP BY cp.physical_server_name ) GO --********************************************************************* -- Create procedure sp_sysutility_ucp_calculate_dac_file_space_health -- Description: Computes the file space (data/log file group)health state for the DAC's -- The computed result is consumed by the UI for health state drill down and further -- compute the rollup health state for the DAC's --********************************************************************** IF(OBJECT_ID(N'dbo.sp_sysutility_ucp_calculate_dac_file_space_health', 'P') IS NOT NULL) BEGIN RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_dac_file_space_health', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_dac_file_space_health END GO RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_dac_file_space_health', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_dac_file_space_health @new_set_number INT WITH EXECUTE AS OWNER AS BEGIN DECLARE @under_utilize_type INT = 1 DECLARE @over_utilize_type INT = 2; -- space_resource_type = 1 -- datafile_target_type = 2 -- logfile_target_type = 3 INSERT INTO msdb.dbo.sysutility_ucp_dac_file_space_health_internal( dac_name, dac_server_instance_name, fg_name, set_number, processing_time , over_utilized_count , under_utilized_count , file_type) -- Insert the dac filegroup utilization details SELECT dd.dac_name , dd.dac_server_instance_name , fg.Name AS file_group_name , @new_set_number , dd.dac_processing_time , SUM(CASE WHEN df.policy_id IS NOT NULL AND dp.utilization_type = 2 THEN 1 ELSE 0 END) AS over_utilized_count , SUM(CASE WHEN df.policy_id IS NOT NULL AND dp.utilization_type = 1 THEN 1 ELSE 0 END) AS under_utilized_count , fg.file_type FROM msdb.dbo.sysutility_ucp_deployed_dacs AS dd INNER JOIN (SELECT 1 AS file_type, server_instance_name, database_name, [Name], processing_time FROM msdb.dbo.sysutility_ucp_filegroups UNION ALL SELECT 2 AS file_type, server_instance_name, Name as database_name, N'' AS [Name], processing_time FROM msdb.dbo.sysutility_ucp_databases) AS fg ON dd.dac_server_instance_name = fg.server_instance_name AND dd.dac_name = fg.database_name INNER JOIN msdb.dbo.sysutility_ucp_dac_policies AS dp ON dp.dac_name = dd.dac_name AND dp.dac_server_instance_name = dd.dac_server_instance_name LEFT JOIN msdb.dbo.sysutility_ucp_filegroups_with_policy_violations_internal AS df ON df.server_instance_name = dd.dac_server_instance_name AND df.database_name = dd.dac_name AND fg.Name = df.filegroup_name AND dp.policy_id = df.policy_id AND df.set_number = @new_set_number WHERE dp.resource_type = 1 AND dp.target_type = fg.file_type + 1 -- target_type = 2 (datafile); 3 (logfile) GROUP BY dd.dac_name, dd.dac_server_instance_name, fg.Name , fg.file_type, dd.dac_processing_time END GO --********************************************************************* -- Create procedure sp_sysutility_ucp_calculate_mi_file_space_health -- Description: Computes the file space (data/log file group)health state for the MI's -- The computed result is consumed by the UI for health state drill down and further -- compute the rollup health state for the MI's --********************************************************************** IF(OBJECT_ID(N'dbo.sp_sysutility_ucp_calculate_mi_file_space_health', 'P') IS NOT NULL) BEGIN RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_mi_file_space_health', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_mi_file_space_health END GO RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_mi_file_space_health', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_mi_file_space_health @new_set_number INT WITH EXECUTE AS OWNER AS BEGIN DECLARE @under_utilize_type INT = 1 DECLARE @over_utilize_type INT = 2; -- space_resource_type = 1 -- datafile_target_type = 2 -- logfile_target_type = 3 INSERT INTO msdb.dbo.sysutility_ucp_mi_file_space_health_internal( server_instance_name , database_name , fg_name , set_number , processing_time , over_utilized_count , under_utilized_count , file_type) -- Insert the server filegroup utilization details SELECT fg.server_instance_name , fg.database_name , fg.Name AS file_group_name , @new_set_number , fg.processing_time , SUM(CASE WHEN df.policy_id IS NOT NULL AND ip.utilization_type = 2 THEN 1 ELSE 0 END) AS over_utilized_count , SUM(CASE WHEN df.policy_id IS NOT NULL AND ip.utilization_type = 1 THEN 1 ELSE 0 END) AS under_utilized_count , fg.file_type FROM (SELECT 1 AS file_type, fg.server_instance_name, fg.database_name, fg.Name, fg.processing_time FROM msdb.dbo.sysutility_ucp_filegroups AS fg UNION ALL SELECT 2 AS file_type, db.server_instance_name, db.Name AS database_name, N'' AS Name, db.processing_time FROM msdb.dbo.sysutility_ucp_databases AS db) AS fg INNER JOIN msdb.dbo.sysutility_ucp_instance_policies AS ip ON fg.server_instance_name = ip.server_instance_name LEFT JOIN msdb.dbo.sysutility_ucp_filegroups_with_policy_violations_internal AS df ON fg.server_instance_name = df.server_instance_name AND fg.database_name = df.database_name AND fg.Name = df.[filegroup_name] AND df.set_number = @new_set_number AND ip.policy_id = df.policy_id WHERE ip.resource_type = 1 AND ip.target_type = file_type + 1 -- target_type = 2 (datafile), 3 (logfile) GROUP BY fg.server_instance_name, fg.database_name, fg.Name, fg.file_type, fg.processing_time -- Compute the database health state for the MI's based on the file-space computation. -- Insert the server database utilization details INSERT INTO msdb.dbo.sysutility_ucp_mi_database_health_internal(server_instance_name, database_name, set_number, processing_time , over_utilized_count , under_utilized_count) SELECT fs.server_instance_name , fs.database_name AS database_name , @new_set_number , svr.processing_time , SUM(fs.over_utilized_count) AS over_utilized_count , SUM(fs.under_utilized_count) AS under_utilized_count FROM msdb.dbo.sysutility_ucp_mi_file_space_health_internal AS fs , msdb.dbo.sysutility_ucp_instances AS svr WHERE svr.Name = fs.server_instance_name AND fs.set_number = @new_set_number GROUP BY fs.server_instance_name, fs.database_name, svr.processing_time END GO --********************************************************************* -- Create procedure sp_sysutility_ucp_calculate_computer_health -- Description: Computes the volume space health state for the computer. -- The computed result is consumed by the DAC / MI to determine rollup health state --********************************************************************** IF(OBJECT_ID(N'dbo.sp_sysutility_ucp_calculate_computer_health', 'P') IS NOT NULL) BEGIN RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_computer_health', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_computer_health END GO RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_computer_health', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_computer_health @new_set_number INT WITH EXECUTE AS OWNER AS BEGIN DECLARE @under_utilize_type INT = 1 DECLARE @over_utilize_type INT = 2 DECLARE @computer_object_type INT = 3 DECLARE @target_type INT = 6 DECLARE @space_resource_type INT = 1; -- Compute the volume space health state for the computer. -- CTE to identify the computer volumes violating the under / over utilization policy WITH volume_utilization (physical_server_name, volume_device_id, utilization_type) AS ( SELECT vo.physical_server_name, vo.volume_device_id, cp.utilization_type FROM msdb.dbo.sysutility_ucp_computer_policies cp , msdb.dbo.sysutility_ucp_volume_powershell_path vo , msdb.dbo.sysutility_ucp_policy_violations pv WHERE cp.physical_server_name = vo.physical_server_name AND cp.resource_type = @space_resource_type AND cp.target_type = @target_type AND pv.policy_id = cp.policy_id AND pv.target_query_expression = vo.powershell_path ) -- Insert new record INSERT INTO msdb.dbo.sysutility_ucp_mi_volume_space_health_internal(physical_server_name, server_instance_name, volume_device_id, set_number, processing_time ,health_state) SELECT CAST(svr.ComputerNamePhysicalNetBIOS AS SYSNAME), CAST(svr.Name AS SYSNAME), vol.volume_device_id, @new_set_number, svr.processing_time, CASE WHEN (@over_utilize_type = ISNULL(vu.utilization_type, 0)) THEN 3 -- over utilized WHEN (@under_utilize_type = ISNULL(vu.utilization_type, 0)) THEN 2 -- under utilized ELSE 1 -- healthy END FROM msdb.dbo.sysutility_ucp_instances AS svr INNER JOIN msdb.dbo.sysutility_ucp_volumes AS vol ON vol.physical_server_name = svr.ComputerNamePhysicalNetBIOS LEFT JOIN volume_utilization vu ON vol.physical_server_name = vu.physical_server_name AND vol.volume_device_id = vu.volume_device_id -- Computes the processor health state for the computer. -- Insert new record INSERT INTO msdb.dbo.sysutility_ucp_computer_cpu_health_internal(physical_server_name, set_number, processing_time, health_state) SELECT c.physical_server_name , @new_set_number , c.processing_time, CASE WHEN 0 < ISNULL(cu.over_utilized_count, 0) THEN 3 -- over utilized WHEN 0 < ISNULL(cu.under_utilized_count, 0) THEN 2 -- under utilized ELSE 1 -- healthy END AS health_state FROM msdb.dbo.sysutility_ucp_computers AS c LEFT JOIN msdb.dbo.sysutility_ucp_computer_cpu_utilization cu ON c.physical_server_name = cu.physical_server_name END GO --********************************************************************* -- Create procedure sp_sysutility_ucp_calculate_dac_health -- Description: This is the top level procedure that computes all -- the resource health states for the DAC. --********************************************************************** IF OBJECT_ID ('dbo.sp_sysutility_ucp_calculate_dac_health') IS NOT NULL BEGIN RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_dac_health', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_dac_health END; GO RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_dac_health', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_dac_health @new_set_number INT WITH EXECUTE AS OWNER AS BEGIN -- Compute dac filegroup/log files health state EXEC msdb.dbo.sp_sysutility_ucp_calculate_dac_file_space_health @new_set_number; -- Compute dac health state -- Insert new records SELECT dd.dac_server_instance_name , dd.dac_name , SUM(CASE WHEN hs.health_state = 2 THEN 1 ELSE 0 END) AS under_utilized_count , SUM(CASE WHEN hs.health_state = 3 THEN 1 ELSE 0 END) AS over_utilized_count INTO #dac_volume_file_space_utilization FROM msdb.dbo.sysutility_ucp_deployed_dacs AS dd INNER JOIN msdb.dbo.sysutility_ucp_mi_volume_space_health_internal AS hs ON hs.server_instance_name = dd.dac_server_instance_name INNER JOIN ( SELECT server_instance_name, database_name, volume_device_id FROM sysutility_ucp_datafiles UNION ALL SELECT server_instance_name, database_name, volume_device_id FROM sysutility_ucp_logfiles ) AS df ON df.volume_device_id = hs.volume_device_id AND dd.dac_server_instance_name = df.server_instance_name AND dd.dac_name = df.database_name WHERE hs.set_number = @new_set_number GROUP BY dd.dac_server_instance_name, dd.dac_name; SELECT dd.dac_server_instance_name , dd.dac_name , SUM(CASE WHEN hs.health_state = 2 THEN 1 ELSE 0 END) AS under_utilized_count , SUM(CASE WHEN hs.health_state = 3 THEN 1 ELSE 0 END) AS over_utilized_count INTO #dac_computer_cpu_utilization FROM msdb.dbo.sysutility_ucp_computer_cpu_health_internal AS hs INNER JOIN msdb.dbo.sysutility_ucp_deployed_dacs AS dd ON hs.physical_server_name = dd.dac_physical_server_name WHERE hs.set_number = @new_set_number GROUP BY dd.dac_server_instance_name, dd.dac_name; SELECT hs.dac_server_instance_name , hs.dac_name , SUM(CASE WHEN health_state.val = 2 THEN 1 ELSE 0 END) AS under_utilized_count , SUM(CASE WHEN health_state.val = 3 THEN 1 ELSE 0 END) AS over_utilized_count INTO #dac_file_space_utilization FROM msdb.dbo.sysutility_ucp_dac_file_space_health_internal hs CROSS APPLY dbo.fn_sysutility_ucp_get_aggregated_health(hs.over_utilized_count, hs.under_utilized_count) health_state WHERE hs.set_number = @new_set_number GROUP BY hs.dac_server_instance_name, hs.dac_name; INSERT INTO msdb.dbo.sysutility_ucp_dac_health_internal(dac_name, dac_server_instance_name, set_number , processing_time , is_volume_space_over_utilized , is_volume_space_under_utilized , is_computer_processor_over_utilized , is_computer_processor_under_utilized , is_file_space_over_utilized , is_file_space_under_utilized , is_dac_processor_over_utilized , is_dac_processor_under_utilized , is_policy_overridden) SELECT dd.dac_name , dd.dac_server_instance_name , @new_set_number , dd.dac_processing_time , vu.over_utilized_count AS dac_volume_space_over_utilized_count , vu.under_utilized_count AS dac_volume_space_under_utilized_count , cu.over_utilized_count AS dac_computer_cpu_over_utilized_count , cu.under_utilized_count AS dac_computer_cpu_under_utilized_count , su.over_utilized_count AS dac_file_space_over_utilized_count , su.under_utilized_count AS dac_file_space_under_utilized_count , ISNULL(du.over_utilized_count ,0) AS dac_cpu_over_utilized_count , ISNULL(du.under_utilized_count ,0) AS dac_cpu_under_utilized_count , pt.is_policy_overridden FROM msdb.dbo.sysutility_ucp_deployed_dacs dd LEFT JOIN msdb.dbo.sysutility_ucp_dac_cpu_utilization du ON dd.dac_name = du.dac_name AND dd.dac_server_instance_name = du.dac_server_instance_name INNER JOIN #dac_volume_file_space_utilization AS vu ON dd.dac_name = vu.dac_name AND dd.dac_server_instance_name = vu.dac_server_instance_name INNER JOIN #dac_computer_cpu_utilization AS cu ON dd.dac_name = cu.dac_name AND dd.dac_server_instance_name = cu.dac_server_instance_name INNER JOIN #dac_file_space_utilization AS su ON dd.dac_name = su.dac_name AND dd.dac_server_instance_name = su.dac_server_instance_name INNER JOIN msdb.dbo.sysutility_ucp_dac_policy_type pt ON dd.dac_name = pt.dac_name AND dd.dac_server_instance_name = pt.dac_server_instance_name; END GO --********************************************************************* -- Create procedure sp_sysutility_ucp_calculate_aggregated_dac_health -- Description: This is the top level procedure that computes the health -- statistics for the DAC. This uses the pre-computed health states for DAC's -- The resulting health statistics are used in dashboard display --********************************************************************** IF OBJECT_ID ('dbo.sp_sysutility_ucp_calculate_aggregated_dac_health') IS NOT NULL BEGIN RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_aggregated_dac_health', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_aggregated_dac_health END; GO RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_aggregated_dac_health', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_aggregated_dac_health @new_set_number INT WITH EXECUTE AS OWNER AS BEGIN -- DacCount DECLARE @dac_count INT = 0 SELECT @dac_count = COUNT(*) FROM msdb.dbo.sysutility_ucp_dac_health_internal hs WHERE hs.set_number = @new_set_number -- DacOverUtilizeCount DECLARE @dac_over_utilize_count INT = 0 SELECT @dac_over_utilize_count = COUNT(*) FROM msdb.dbo.sysutility_ucp_dac_health_internal hs WHERE hs.set_number = @new_set_number AND (0 != hs.is_dac_processor_over_utilized OR 0 != hs.is_computer_processor_over_utilized OR 0 != hs.is_file_space_over_utilized OR 0 != hs.is_volume_space_over_utilized) -- DacUnderUtilizeCount DECLARE @dac_under_utilize_count INT = 0 SELECT @dac_under_utilize_count = COUNT(*) FROM msdb.dbo.sysutility_ucp_dac_health_internal hs WHERE hs.set_number = @new_set_number AND (0 != hs.is_dac_processor_under_utilized OR 0 != hs.is_computer_processor_under_utilized OR 0 != hs.is_file_space_under_utilized OR 0 != hs.is_volume_space_under_utilized) AND 0 = hs.is_dac_processor_over_utilized AND 0 = hs.is_computer_processor_over_utilized AND 0 = hs.is_file_space_over_utilized AND 0 = hs.is_volume_space_over_utilized -- DacUnhealthyCount DECLARE @dac_unhealthy_count INT = 0 SELECT @dac_unhealthy_count = @dac_over_utilize_count + @dac_under_utilize_count; -- DacHealthyCount DECLARE @dac_healthy_count INT = 0 SELECT @dac_healthy_count = COUNT(*) FROM msdb.dbo.sysutility_ucp_dac_health_internal hs WHERE hs.set_number = @new_set_number AND 0 = hs.is_dac_processor_under_utilized AND 0 = hs.is_computer_processor_under_utilized AND 0 = hs.is_file_space_under_utilized AND 0 = hs.is_volume_space_under_utilized AND 0 = hs.is_dac_processor_over_utilized AND 0 = hs.is_computer_processor_over_utilized AND 0 = hs.is_file_space_over_utilized AND 0 = hs.is_volume_space_over_utilized -- Insert new record INSERT INTO msdb.dbo.sysutility_ucp_aggregated_dac_health_internal(set_number , dac_count , dac_healthy_count , dac_unhealthy_count , dac_over_utilize_count , dac_under_utilize_count , dac_on_over_utilized_computer_count , dac_on_under_utilized_computer_count , dac_with_files_on_over_utilized_volume_count , dac_with_files_on_under_utilized_volume_count , dac_with_over_utilized_file_count , dac_with_under_utilized_file_count , dac_with_over_utilized_processor_count , dac_with_under_utilized_processor_count) SELECT @new_set_number , @dac_count , @dac_healthy_count , @dac_unhealthy_count , @dac_over_utilize_count , @dac_under_utilize_count , ISNULL(SUM(CASE WHEN 0 < hs.is_computer_processor_over_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_computer_processor_under_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_volume_space_over_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_volume_space_under_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_file_space_over_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_file_space_under_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_dac_processor_over_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_dac_processor_under_utilized THEN 1 ELSE 0 END), 0) FROM msdb.dbo.sysutility_ucp_dac_health_internal hs WHERE hs.set_number = @new_set_number END GO --********************************************************************* -- Create procedure sp_sysutility_ucp_calculate_mi_health -- Description: This is the top level procedure that computes all -- the resource health states for the MI. --********************************************************************** IF OBJECT_ID ('dbo.sp_sysutility_ucp_calculate_mi_health') IS NOT NULL BEGIN RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_mi_health', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_mi_health END; GO RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_mi_health', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_mi_health @new_set_number INT WITH EXECUTE AS OWNER AS BEGIN -- Compute managed instance database health state EXEC msdb.dbo.sp_sysutility_ucp_calculate_mi_file_space_health @new_set_number; -- Compute managed instance health state -- Insert new record SELECT hs.server_instance_name AS server_instance_name, SUM(CASE WHEN health_state.val = 2 THEN 1 ELSE 0 END) AS under_utilized_count, SUM(CASE WHEN health_state.val = 3 THEN 1 ELSE 0 END) AS over_utilized_count INTO #instance_file_space_utilization FROM msdb.dbo.sysutility_ucp_mi_file_space_health_internal as hs CROSS APPLY msdb.dbo.fn_sysutility_ucp_get_aggregated_health(hs.over_utilized_count, hs.under_utilized_count) as health_state WHERE hs.set_number = @new_set_number GROUP BY hs.server_instance_name; SELECT sv.Name AS server_instance_name, SUM(CASE WHEN hs.health_state = 2 THEN 1 ELSE 0 END) AS under_utilized_count, SUM(CASE WHEN hs.health_state = 3 THEN 1 ELSE 0 END) AS over_utilized_count INTO #instance_computer_cpu_utilization FROM msdb.dbo.sysutility_ucp_computer_cpu_health_internal AS hs INNER JOIN msdb.dbo.sysutility_ucp_instances AS sv ON hs.physical_server_name = sv.ComputerNamePhysicalNetBIOS WHERE hs.set_number = @new_set_number GROUP BY sv.Name; SELECT hs.server_instance_name AS server_instance_name, SUM(CASE WHEN hs.health_state = 2 THEN 1 ELSE 0 END) AS under_utilized_count, SUM(CASE WHEN hs.health_state = 3 THEN 1 ELSE 0 END) AS over_utilized_count INTO #instance_volume_file_space_utilization FROM msdb.dbo.sysutility_ucp_mi_volume_space_health_internal AS hs INNER JOIN ( SELECT server_instance_name, database_name, volume_device_id FROM dbo.sysutility_ucp_datafiles UNION ALL SELECT server_instance_name, database_name, volume_device_id FROM dbo.sysutility_ucp_logfiles ) AS df ON hs.volume_device_id = df.volume_device_id AND hs.server_instance_name = df.server_instance_name WHERE hs.set_number = @new_set_number GROUP BY hs.server_instance_name; INSERT INTO msdb.dbo.sysutility_ucp_mi_health_internal(mi_name, set_number , processing_time , is_volume_space_over_utilized , is_volume_space_under_utilized , is_computer_processor_over_utilized , is_computer_processor_under_utilized , is_file_space_over_utilized , is_file_space_under_utilized , is_mi_processor_over_utilized , is_mi_processor_under_utilized , is_policy_overridden) SELECT CAST(sv.Name AS SYSNAME) mi_name , @new_set_number , sv.processing_time , vu.over_utilized_count AS mi_volume_space_over_utilized_count , vu.under_utilized_count AS mi_volume_space_under_utilized_count , cu.over_utilized_count AS mi_computer_cpu_over_utilized_count , cu.under_utilized_count AS mi_computer_cpu_under_utilized_count , su.over_utilized_count AS mi_file_space_over_utilized_count , su.under_utilized_count AS mi_file_space_under_utilized_count , ISNULL(iu.over_utilized_count ,0) AS mi_cpu_over_utilized_count , ISNULL(iu.under_utilized_count ,0) AS mi_cpu_under_utilized_count , pt.is_policy_overridden FROM msdb.dbo.sysutility_ucp_managed_instances AS mi INNER JOIN msdb.dbo.sysutility_ucp_instances AS sv ON sv.Name = mi.instance_name LEFT OUTER JOIN msdb.dbo.sysutility_ucp_instance_cpu_utilization AS iu ON sv.Name = iu.server_instance_name INNER JOIN #instance_volume_file_space_utilization AS vu ON sv.Name = vu.server_instance_name INNER JOIN #instance_computer_cpu_utilization AS cu ON sv.Name = cu.server_instance_name INNER JOIN #instance_file_space_utilization AS su ON sv.Name = su.server_instance_name INNER JOIN msdb.dbo.sysutility_ucp_instance_policy_type AS pt ON sv.Name = pt.server_instance_name; END GO --********************************************************************* -- Create procedure sp_sysutility_ucp_calculate_aggregated_mi_health -- Description: This is the top level procedure that computes the health -- statistics for the MI. This uses the pre-computed health states for MI's -- The resulting health statistics are used in dashboard display --********************************************************************** IF OBJECT_ID ('dbo.sp_sysutility_ucp_calculate_aggregated_mi_health') IS NOT NULL BEGIN RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_aggregated_mi_health', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_aggregated_mi_health END; GO RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_aggregated_mi_health', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_aggregated_mi_health @new_set_number INT WITH EXECUTE AS OWNER AS BEGIN -- ManagedInstanceCount DECLARE @mi_count INT = 0 SELECT @mi_count = COUNT(*) FROM msdb.dbo.sysutility_ucp_mi_health_internal hs WHERE hs.set_number = @new_set_number -- ManagedInstanceOverUtilizeCount DECLARE @mi_over_utilize_count INT = 0 SELECT @mi_over_utilize_count = COUNT(*) FROM msdb.dbo.sysutility_ucp_mi_health_internal hs WHERE hs.set_number = @new_set_number AND (0 != hs.is_volume_space_over_utilized OR 0 != hs.is_computer_processor_over_utilized OR 0 != hs.is_file_space_over_utilized OR 0 != hs.is_mi_processor_over_utilized) -- ManagedInstanceUnderUtilizeCount DECLARE @mi_under_utilize_count INT = 0 SELECT @mi_under_utilize_count = COUNT(*) FROM msdb.dbo.sysutility_ucp_mi_health_internal hs WHERE hs.set_number = @new_set_number AND (0 != hs.is_volume_space_under_utilized OR 0 != hs.is_computer_processor_under_utilized OR 0 != hs.is_file_space_under_utilized OR 0 != hs.is_mi_processor_under_utilized) AND 0 = hs.is_volume_space_over_utilized AND 0 = hs.is_computer_processor_over_utilized AND 0 = hs.is_file_space_over_utilized AND 0 = hs.is_mi_processor_over_utilized -- ManagedInstanceUnhealthyCount DECLARE @mi_unhealthy_count INT = 0 SELECT @mi_unhealthy_count = @mi_over_utilize_count + @mi_under_utilize_count -- ManagedInstanceHealthyCount DECLARE @mi_healthy_count INT = 0 SELECT @mi_healthy_count = COUNT(*) FROM msdb.dbo.sysutility_ucp_mi_health_internal hs WHERE hs.set_number = @new_set_number AND 0 = hs.is_volume_space_under_utilized AND 0 = hs.is_computer_processor_under_utilized AND 0 = hs.is_file_space_under_utilized AND 0 = hs.is_mi_processor_under_utilized AND 0 = hs.is_volume_space_over_utilized AND 0 = hs.is_computer_processor_over_utilized AND 0 = hs.is_file_space_over_utilized AND 0 = hs.is_mi_processor_over_utilized -- Insert new record INSERT INTO msdb.dbo.sysutility_ucp_aggregated_mi_health_internal(set_number , mi_count , mi_healthy_count , mi_unhealthy_count , mi_over_utilize_count , mi_under_utilize_count , mi_on_over_utilized_computer_count , mi_on_under_utilized_computer_count , mi_with_files_on_over_utilized_volume_count , mi_with_files_on_under_utilized_volume_count , mi_with_over_utilized_file_count , mi_with_under_utilized_file_count , mi_with_over_utilized_processor_count , mi_with_under_utilized_processor_count) SELECT @new_set_number , @mi_count , @mi_healthy_count , @mi_unhealthy_count , @mi_over_utilize_count , @mi_under_utilize_count , ISNULL(SUM(CASE WHEN 0 < hs.is_computer_processor_over_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_computer_processor_under_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_volume_space_over_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_volume_space_under_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_file_space_over_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_file_space_under_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_mi_processor_over_utilized THEN 1 ELSE 0 END), 0) , ISNULL(SUM(CASE WHEN 0 < hs.is_mi_processor_under_utilized THEN 1 ELSE 0 END), 0) FROM msdb.dbo.sysutility_ucp_mi_health_internal hs WHERE hs.set_number = @new_set_number END GO --********************************************************************** -- Create procedure sp_sysutility_ucp_calculate_health -- Procedure description: -- This SP computes the health state of the utility resources based on the -- policy evaluation results. The scheduled job runs the policies which evaluate -- the resource for under and over utilization. The health state on the rollup object -- are then determined by aggregating the health state of the underlying objects. -- Following are the resource on which health states determined -- 1. DAC / Server processor -- 2. File storage space -- 3. Volume storage space -- 4. Computer processor --********************************************************************** IF OBJECT_ID ('dbo.sp_sysutility_ucp_calculate_health') IS NOT NULL BEGIN RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_calculate_health', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_calculate_health END; GO RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_calculate_health', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE dbo.sp_sysutility_ucp_calculate_health WITH EXECUTE AS OWNER AS BEGIN SET NOCOUNT ON; -- Snapshot isolation prevents the nightly purge jobs that delete much older data from blocking us. SET TRANSACTION ISOLATION LEVEL SNAPSHOT; DECLARE @new_set_number INT DECLARE @myTableVar table(next_health_state_id INT); DECLARE @task_start_time DATETIME; DECLARE @task_elapsed_ms INT; -- get the "latest" set-number. We want all the health_state tables to -- reflect the same point in time, and we achieve this by using a single -- set_number column in each of the tables. At any point of time, we should -- be using the entries from the table which correspond to the latest_health_state_id -- value in the sysutility_ucp_processing_state_internal table UPDATE msdb.dbo.sysutility_ucp_processing_state_internal SET next_health_state_id = next_health_state_id + 1 OUTPUT INSERTED.next_health_state_id INTO @myTableVar; SELECT @new_set_number = next_health_state_id FROM @myTableVar; -- Fetch the violations for health polices from latest policy evaluation -- and cache them in the intermediate table. All the health state queries -- reference this table to optimize performance SET @task_start_time = GETUTCDATE(); EXEc dbo.sp_sysutility_ucp_get_policy_violations SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('sp_sysutility_ucp_get_policy_violations completed in %d ms', 0, 1, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); -- Identify filegroups that have a policy violation. (i.e.) all files in the filegroup -- should have violated the same policy. Logfiles are considered to belong to a -- fake filegroup with name=N'' -- We will use this information in subsequent calls EXEC dbo.sp_sysutility_ucp_calculate_filegroups_with_policy_violations @new_set_number SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('sp_sysutility_ucp_calculate_filegroups_with_policy_violations completed in %d ms', 0, 1, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); -- Compute computer health state EXEC sp_sysutility_ucp_calculate_computer_health @new_set_number SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('sp_sysutility_ucp_calculate_computer_health completed in %d ms', 0, 1, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); -- Compute dac health state EXEC msdb.dbo.sp_sysutility_ucp_calculate_dac_health @new_set_number SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('sp_sysutility_ucp_calculate_dac_health completed in %d ms', 0, 1, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); -- Compute dac dashboard health stats EXEC msdb.dbo.sp_sysutility_ucp_calculate_aggregated_dac_health @new_set_number SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('sp_sysutility_ucp_calculate_aggregated_dac_health completed in %d ms', 0, 1, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); -- Compute managed instance health state EXEC msdb.dbo.sp_sysutility_ucp_calculate_mi_health @new_set_number SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('sp_sysutility_ucp_calculate_mi_health completed in %d ms', 0, 1, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); -- Compute managed instance dashboard health stats EXEC msdb.dbo.sp_sysutility_ucp_calculate_aggregated_mi_health @new_set_number SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('sp_sysutility_ucp_calculate_aggregated_mi_health completed in %d ms', 0, 1, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); -- Update the config table with the new set_number UPDATE msdb.dbo.sysutility_ucp_processing_state_internal SET latest_health_state_id = @new_set_number -- Delete the old sets SET @task_start_time = GETUTCDATE(); DELETE FROM msdb.dbo.sysutility_ucp_aggregated_mi_health_internal WHERE set_number < @new_set_number DELETE FROM msdb.dbo.sysutility_ucp_mi_health_internal WHERE set_number < @new_set_number DELETE FROM msdb.dbo.sysutility_ucp_aggregated_dac_health_internal WHERE set_number < @new_set_number DELETE FROM msdb.dbo.sysutility_ucp_dac_health_internal WHERE set_number < @new_set_number DELETE FROM msdb.dbo.sysutility_ucp_computer_cpu_health_internal WHERE set_number < @new_set_number DELETE FROM msdb.dbo.sysutility_ucp_mi_volume_space_health_internal WHERE set_number < @new_set_number DELETE FROM msdb.dbo.sysutility_ucp_mi_database_health_internal WHERE set_number < @new_set_number DELETE FROM msdb.dbo.sysutility_ucp_mi_file_space_health_internal WHERE set_number < @new_set_number DELETE FROM msdb.dbo.sysutility_ucp_dac_file_space_health_internal WHERE set_number < @new_set_number DELETE FROM msdb.dbo.sysutility_ucp_filegroups_with_policy_violations_internal WHERE set_number < @new_set_number SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('Deleted older sets in %d ms', 0, 1, @task_elapsed_ms); END GO IF OBJECT_ID ('dbo.sp_sysutility_ucp_configure_policies') IS NOT NULL BEGIN RAISERROR ('Dropping procedure dbo.sp_sysutility_ucp_configure_policies', 0, 1) WITH NOWAIT; DROP PROCEDURE dbo.sp_sysutility_ucp_configure_policies END; GO RAISERROR ('Creating procedure dbo.sp_sysutility_ucp_configure_policies', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [dbo].[sp_sysutility_ucp_configure_policies] WITH EXECUTE AS OWNER AS BEGIN DECLARE @condition_id INT DECLARE @object_set_id INT DECLARE @target_set_id INT DECLARE @policy_id INT DECLARE @computer_urn NVARCHAR(4000) DECLARE @dac_urn NVARCHAR(4000) DECLARE @server_urn NVARCHAR(4000) DECLARE @start DATETIME SELECT @start = getdate() ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Delete existing Policies, Conditions, objectSets and Schedule ------------------------------------------------------------------------------------------------------------------------------------------------------------- IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityComputerProcessorOverUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityComputerProcessorOverUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityComputerProcessorOverUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityComputerProcessorOverUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityComputerProcessorOverUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityComputerProcessorOverUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityComputerProcessorOverUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityComputerProcessorOverUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityComputerProcessorOverUtilizationCondition' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityComputerProcessorOverUtilizationTargetCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityComputerProcessorOverUtilizationTargetCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityComputerProcessorOverUtilizationTargetCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityComputerProcessorUnderUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityComputerProcessorUnderUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityComputerProcessorUnderUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityComputerProcessorUnderUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityComputerProcessorUnderUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityComputerProcessorUnderUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityComputerProcessorUnderUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityComputerProcessorUnderUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityComputerProcessorUnderUtilizationCondition' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityComputerProcessorUnderUtilizationTargetCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityComputerProcessorUnderUtilizationTargetCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityComputerProcessorUnderUtilizationTargetCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityDacDataFileSpaceOverUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacDataFileSpaceOverUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityDacDataFileSpaceOverUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityDacDataFileSpaceOverUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacDataFileSpaceOverUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityDacDataFileSpaceOverUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacDataFileSpaceOverUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacDataFileSpaceOverUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacDataFileSpaceOverUtilizationCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityDacDataFileSpaceUnderUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacDataFileSpaceUnderUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacDataFileSpaceUnderUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacDataFileSpaceUnderUtilizationCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityDacLogFileSpaceOverUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacLogFileSpaceOverUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityDacLogFileSpaceOverUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityDacLogFileSpaceOverUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacLogFileSpaceOverUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityDacLogFileSpaceOverUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacLogFileSpaceOverUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacLogFileSpaceOverUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacLogFileSpaceOverUtilizationCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityDacLogFileSpaceUnderUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacLogFileSpaceUnderUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacLogFileSpaceUnderUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacLogFileSpaceUnderUtilizationCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityServerDataFileSpaceOverUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerDataFileSpaceOverUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityServerDataFileSpaceOverUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityServerDataFileSpaceOverUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerDataFileSpaceOverUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityServerDataFileSpaceOverUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerDataFileSpaceOverUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerDataFileSpaceOverUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerDataFileSpaceOverUtilizationCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityServerDataFileSpaceUnderUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerDataFileSpaceUnderUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerDataFileSpaceUnderUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerDataFileSpaceUnderUtilizationCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityServerLogFileSpaceOverUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerLogFileSpaceOverUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityServerLogFileSpaceOverUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityServerLogFileSpaceOverUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerLogFileSpaceOverUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityServerLogFileSpaceOverUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerLogFileSpaceOverUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerLogFileSpaceOverUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerLogFileSpaceOverUtilizationCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityServerLogFileSpaceUnderUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerLogFileSpaceUnderUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerLogFileSpaceUnderUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerLogFileSpaceUnderUtilizationCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityServerProcessorOverUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerProcessorOverUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityServerProcessorOverUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityServerProcessorOverUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerProcessorOverUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityServerProcessorOverUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerProcessorOverUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerProcessorOverUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerProcessorOverUtilizationCondition' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerProcessorOverUtilizationTargetCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerProcessorOverUtilizationTargetCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerProcessorOverUtilizationTargetCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityServerProcessorUnderUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityServerProcessorUnderUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityServerProcessorUnderUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityServerProcessorUnderUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityServerProcessorUnderUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityServerProcessorUnderUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerProcessorUnderUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerProcessorUnderUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerProcessorUnderUtilizationCondition' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityServerProcessorUnderUtilizationTargetCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityServerProcessorUnderUtilizationTargetCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityServerProcessorUnderUtilizationTargetCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityDacProcessorOverUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacProcessorOverUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityDacProcessorOverUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityDacProcessorOverUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacProcessorOverUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityDacProcessorOverUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacProcessorOverUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacProcessorOverUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacProcessorOverUtilizationCondition' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacProcessorOverUtilizationTargetCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacProcessorOverUtilizationTargetCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacProcessorOverUtilizationTargetCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityDacProcessorUnderUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityDacProcessorUnderUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityDacProcessorUnderUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityDacProcessorUnderUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityDacProcessorUnderUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityDacProcessorUnderUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacProcessorUnderUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacProcessorUnderUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacProcessorUnderUtilizationCondition' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityDacProcessorUnderUtilizationTargetCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityDacProcessorUnderUtilizationTargetCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityDacProcessorUnderUtilizationTargetCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityVolumeSpaceOverUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityVolumeSpaceOverUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityVolumeSpaceOverUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityVolumeSpaceOverUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityVolumeSpaceOverUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityVolumeSpaceOverUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityVolumeSpaceOverUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityVolumeSpaceOverUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityVolumeSpaceOverUtilizationCondition' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityVolumeSpaceOverUtilizationTargetCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityVolumeSpaceOverUtilizationTargetCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityVolumeSpaceOverUtilizationTargetCondition' END IF EXISTS(SELECT policy_id FROM msdb.dbo.syspolicy_policies WHERE name=N'UtilityVolumeSpaceUnderUtilizationPolicy') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'POLICY', @name=N'UtilityVolumeSpaceUnderUtilizationPolicy', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_policy @name=N'UtilityVolumeSpaceUnderUtilizationPolicy' END IF EXISTS(SELECT object_set_id FROM msdb.dbo.syspolicy_object_sets WHERE object_set_name=N'UtilityVolumeSpaceUnderUtilizationPolicy_ObjectSet') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'OBJECTSET', @name=N'UtilityVolumeSpaceUnderUtilizationPolicy_ObjectSet', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_object_set @object_set_name=N'UtilityVolumeSpaceUnderUtilizationPolicy_ObjectSet' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityVolumeSpaceUnderUtilizationCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityVolumeSpaceUnderUtilizationCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityVolumeSpaceUnderUtilizationCondition' END IF EXISTS(SELECT condition_id FROM msdb.dbo.syspolicy_conditions WHERE name=N'UtilityVolumeSpaceUnderUtilizationTargetCondition') BEGIN EXEC msdb.dbo.sp_syspolicy_mark_system @type=N'CONDITION', @name=N'UtilityVolumeSpaceUnderUtilizationTargetCondition', @marker=0 EXEC msdb.dbo.sp_syspolicy_delete_condition @name=N'UtilityVolumeSpaceUnderUtilizationTargetCondition' END IF EXISTS(SELECT COUNT(*) FROM msdb.dbo.sysutility_ucp_health_policies_internal) BEGIN DELETE FROM msdb.dbo.sysutility_ucp_health_policies_internal END IF EXISTS(SELECT schedule_id FROM msdb.dbo.sysschedules WHERE name=N'UtilityResourceHealthStateSchedule') BEGIN EXEC msdb.dbo.sp_delete_schedule @schedule_name=N'UtilityResourceHealthStateSchedule', @force_delete=1 END IF EXISTS (SELECT job_id FROM msdb.dbo.sysjobs_view WHERE name = N'Utility Resource Health State') BEGIN EXEC msdb.dbo.sp_delete_job @job_name=N'Utility Resource Health State', @delete_unused_schedule=1 END ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- UtilityComputerProcessorOverUtilizationCondition ------------------------------------------------------------------------------------------------------------------------------------------------------------- EXEC msdb.dbo.sp_syspolicy_add_condition @name=N'UtilityComputerProcessorOverUtilizationCondition', @description=N'The SQL Server Utility condition that expresses when the CPU overutilization policy is satisfied for a computer that hosts a managed instance of SQL Server. The value that is used in the condition expression is set in SQL Server Utility Explorer.', @facet=N'Computer', @expression=N' 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 END DELETE [dbo].[sysutility_ucp_managed_instances_internal] WHERE instance_id = @instance_id SELECT @retval = @@error RETURN(@retval) END GO /**********************************************************************/ /* */ /* Remove a managed instance from its UCP */ /* */ /**********************************************************************/ IF OBJECT_ID ('dbo.sp_sysutility_mi_remove') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [dbo].[sp_sysutility_mi_remove]', 0, 1) WITH NOWAIT; DROP PROCEDURE [dbo].[sp_sysutility_mi_remove] END; GO CREATE PROCEDURE [dbo].[sp_sysutility_mi_remove] WITH EXECUTE AS OWNER AS BEGIN SET NOCOUNT ON; EXEC msdb.dbo.sp_sysutility_mi_disable_collection; --###FP 1 EXEC msdb.dbo.sp_syscollector_disable_collector; --###FP 2 DECLARE @collection_set_id int; DECLARE @proxy_id int; DECLARE @utility_collection_set_uid uniqueidentifier = N'ABA37A22-8039-48C6-8F8F-39BFE0A195DF'; -- find our collection set and determine if its proxy is set SELECT @collection_set_id = collection_set.collection_set_id ,@proxy_id = collection_set.proxy_id FROM msdb.dbo.syscollector_collection_sets AS collection_set WHERE collection_set.collection_set_uid = @utility_collection_set_uid; -- determine if DC is running -- if agent is not running, is_running won't be changed -- so default it to false DECLARE @is_running int = 0 EXEC msdb.dbo.sp_syscollector_get_collection_set_execution_status @collection_set_id, @is_running OUTPUT; --###FP 3 IF (@is_running = 1) BEGIN EXEC msdb.dbo.sp_syscollector_stop_collection_set @collection_set_id; END --###FP 4 IF (@proxy_id IS NOT NULL ) BEGIN -- retrieve the current cache directory setting -- if the setting can't be found, assume it is not set DECLARE @cache_directory_is_set bit = 0 SELECT @cache_directory_is_set = CASE WHEN config.parameter_value IS NULL THEN 0 ELSE 1 END FROM msdb.dbo.syscollector_config_store AS config WHERE config.parameter_name = N'CacheDirectory'; IF(@cache_directory_is_set = 1) BEGIN EXEC msdb.dbo.sp_syscollector_set_cache_directory @cache_directory = NULL; END --###FP 5 -- clear the proxy -- because we only enter this block if proxy is set, -- postpone clearing proxy until the end of the block -- to ensure that if clearing the cache directory fails -- we will re-enter this block the next time this proc is called EXEC msdb.dbo.sp_syscollector_update_collection_set @collection_set_id = @collection_set_id, @proxy_name = N''; --###FP 6 END EXEC msdb.dbo.sp_syscollector_enable_collector; --###FP 7 EXEC msdb.dbo.sp_sysutility_mi_remove_ucp_registration; END; GO /**********************************************************************/ /* Provision security for utility objects */ /**********************************************************************/ EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_file_space_health_internal', N'UtilityCMRReader' EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_database_health_internal', N'UtilityCMRReader' EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_file_space_health_internal', N'UtilityCMRReader' EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_volume_space_health_internal', N'UtilityCMRReader' EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_file_space_health', N'UtilityCMRReader' EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_database_health', N'UtilityCMRReader' EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_database_file_space_health', N'UtilityCMRReader' EXEC sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_volume_space_health', N'UtilityCMRReader' /**********************************************************************/ /* Add an extended database property to identify database as a utility */ /* store. */ /* */ /**********************************************************************/ DECLARE @prop_name sysname DECLARE @new_value sql_variant DECLARE @old_value sql_variant DECLARE @new_value_str varchar(256); SET @prop_name = 'Microsoft_Management_Utility_Version' SET @new_value = '___SQLVERSION___NEW___' -- This should be replaced at build time with the sql build number. See DC's code for how to do this when needed. SELECT @old_value = value FROM fn_listextendedproperty(@prop_name, NULL, NULL, NULL, NULL, NULL, NULL) IF (@old_value IS NOT NULL) BEGIN -- At some point we might want to do version checking here, but for now, just drop the property RAISERROR ('Dropping extended database property - Microsoft_Management_Utility_Version', 0, 1) WITH NOWAIT; EXEC sp_dropextendedproperty @name = @prop_name END SET @new_value_str = CONVERT (varchar(256), @new_value); RAISERROR ('Creating extended database property - Microsoft_Management_Utility_Version (version ''%s'')', 0, 1, @new_value_str) WITH NOWAIT; EXEC sp_addextendedproperty @name = @prop_name, @value = @new_value GO ----------------------------------------------------------- -- Security for Utility objects ----------------------------------------------------------- -- Provision the Utility tables: Grant the tables, Select permissions to public EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_configuration_internal', N'UtilityIMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_session_statistics_internal', N'UtilityIMRReader' GRANT INSERT, DELETE ON [dbo].[sysutility_mi_session_statistics_internal] TO [UtilityIMRWriter] EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_dac_execution_statistics_internal', N'UtilityIMRReader' GRANT INSERT, DELETE ON [dbo].[sysutility_mi_dac_execution_statistics_internal] TO [UtilityIMRWriter] EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_volumes_stage_internal', N'UtilityIMRReader' GRANT INSERT, DELETE ON [dbo].[sysutility_mi_volumes_stage_internal] TO [UtilityIMRWriter] EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_cpu_stage_internal', N'UtilityIMRReader' GRANT INSERT, DELETE ON [dbo].[sysutility_mi_cpu_stage_internal] TO [UtilityIMRWriter] EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_smo_stage_internal', N'UtilityIMRReader' GRANT INSERT, DELETE ON [dbo].[sysutility_mi_smo_stage_internal] TO [UtilityIMRWriter] EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_smo_properties_to_collect_internal', N'UtilityIMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_mi_smo_objects_to_collect_internal', N'UtilityIMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_managed_instances_internal', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_configuration_internal', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_processing_state_internal', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_health_policies_internal', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_aggregated_dac_health_internal', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_aggregated_mi_health_internal', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_health_internal', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_health_internal', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_policy_check_conditions_internal', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_policy_target_conditions_internal', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_deployed_dacs', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_computers', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_volumes', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_utility_space_utilization', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_instances', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_databases', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_filegroups', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_datafiles', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_logfiles', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_database_file_space_utilizations', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_database_file_space_utilizations', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_volume_space_utilizations', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_volume_space_utilizations', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_cpu_utilizations', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_computer_cpu_utilizations', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_cpu_utilizations', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_file_space_utilization_history', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_cpu_utilization_history', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_get_is_instance_ucp', N'UtilityCMRReader' GO -- Provision the Utility views: Keep the views UtilityCMRReader EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_managed_instances', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_configuration', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_policies', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_policy_violations', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_aggregated_dac_health', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_aggregated_mi_health', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_health', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_mi_health', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_policy_check_conditions', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_policy_target_conditions', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_policy_type', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_instance_policy_type', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_volume_powershell_path', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_instance_policies', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_dac_policies', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sysutility_ucp_computer_policies', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_encode_sqlname_for_powershell', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_applicable_policy', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_aggregated_failure_count', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_global_health_policy', N'UtilityCMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_aggregated_health', N'UtilityCMRReader' GO -- Grant Utility SPs, execute permissions to the role EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sp_sysutility_mi_collect_dac_execution_statistics_internal', N'UtilityIMRWriter' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'sp_sysutility_mi_get_dac_execution_statistics_internal', N'UtilityIMRWriter' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_mi_get_batch_manifest', N'UtilityIMRWriter' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_mi_get_cpu_architecture_name', N'UtilityIMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_mi_get_cpu_family_name', N'UtilityIMRReader' EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_get_culture_invariant_conversion_style_internal', N'UtilityIMRReader' GO -- Provision the table valued functions EXEC dbo.sp_sysutility_ucp_provision_utility_object_internal N'fn_sysutility_ucp_get_policy_violations', N'UtilityCMRReader' GO /**************************************************************/ /* END UTILITY SCRIPTS */ /**************************************************************/ /**************************************************************/ /* Sign agent sps and add them to Off By Default component */ /* */ /* Also sign SPs for other components located in MSDB */ /**************************************************************/ -- List all of the stored procedures we need to sign with the Agent -- signing certicate. Optionally if your SP belongs in the 'Agent XPs' -- Off-by-default component, then specify 1 for the 'obdComponent' -- column; If it belongs in 'DBMail XPs', specify 2. create table #sp_table (name sysname, sign int, obdComponent int, bNewProc int) go insert into #sp_table values(N'sp_sqlagent_is_srvrolemember', 1, 0, 0) insert into #sp_table values(N'sp_verify_category_identifiers', 1, 0, 0) insert into #sp_table values(N'sp_verify_proxy_identifiers', 1, 0, 0) insert into #sp_table values(N'sp_verify_credential_identifiers', 1, 0, 0) insert into #sp_table values(N'sp_verify_subsystem_identifiers', 1, 0, 0) insert into #sp_table values(N'sp_verify_login_identifiers', 1, 0, 0) insert into #sp_table values(N'sp_verify_proxy', 1, 0, 0) insert into #sp_table values(N'sp_add_proxy', 1, 0, 0) insert into #sp_table values(N'sp_delete_proxy', 1, 0, 0) insert into #sp_table values(N'sp_update_proxy', 1, 0, 0) insert into #sp_table values(N'sp_sqlagent_is_member', 1, 0, 0) insert into #sp_table values(N'sp_verify_proxy_permissions', 1, 0, 0) insert into #sp_table values(N'sp_help_proxy', 1, 0, 0) insert into #sp_table values(N'sp_grant_proxy_to_subsystem', 1, 0, 0) insert into #sp_table values(N'sp_grant_login_to_proxy', 1, 0, 0) insert into #sp_table values(N'sp_revoke_login_from_proxy', 1, 0, 0) insert into #sp_table values(N'sp_revoke_proxy_from_subsystem', 1, 0, 0) insert into #sp_table values(N'sp_enum_proxy_for_subsystem', 1, 0, 0) insert into #sp_table values(N'sp_enum_login_for_proxy', 1, 0, 0) insert into #sp_table values(N'sp_sqlagent_get_startup_info', 1, 1, 0) insert into #sp_table values(N'sp_sqlagent_has_server_access', 1, 1, 0) insert into #sp_table values(N'sp_sem_add_message', 1, 0, 0) insert into #sp_table values(N'sp_sem_drop_message', 1, 0, 0) insert into #sp_table values(N'sp_get_message_description', 1, 0, 0) insert into #sp_table values(N'sp_sqlagent_get_perf_counters', 1, 0, 0) insert into #sp_table values(N'sp_sqlagent_notify', 1, 1, 0) insert into #sp_table values(N'sp_is_sqlagent_starting', 1, 1, 0) insert into #sp_table values(N'sp_verify_job_identifiers', 1, 0, 0) insert into #sp_table values(N'sp_verify_schedule_identifiers', 1, 0, 0) insert into #sp_table values(N'sp_verify_jobproc_caller', 1, 0, 0) insert into #sp_table values(N'sp_downloaded_row_limiter', 1, 1, 0) insert into #sp_table values(N'sp_post_msx_operation', 1, 1, 0) insert into #sp_table values(N'sp_verify_performance_condition', 1, 0, 0) insert into #sp_table values(N'sp_verify_job_date', 1, 0, 0) insert into #sp_table values(N'sp_verify_job_time', 1, 0, 0) insert into #sp_table values(N'sp_verify_alert', 1, 1, 0) insert into #sp_table values(N'sp_update_alert', 1, 0, 0) insert into #sp_table values(N'sp_delete_job_references', 1, 0, 0) insert into #sp_table values(N'sp_delete_all_msx_jobs', 1, 0, 0) insert into #sp_table values(N'sp_generate_target_server_job_assignment_sql', 1, 0, 0) insert into #sp_table values(N'sp_generate_server_description', 1, 1, 0) insert into #sp_table values(N'sp_msx_set_account', 1, 1, 0) insert into #sp_table values(N'sp_msx_get_account', 1, 1, 0) insert into #sp_table values(N'sp_delete_operator', 1, 0, 0) insert into #sp_table values(N'sp_msx_defect', 1, 1, 0) insert into #sp_table values(N'sp_msx_enlist', 1, 1, 0) insert into #sp_table values(N'sp_delete_targetserver', 1, 0, 0) insert into #sp_table values(N'sp_get_sqlagent_properties', 1, 1, 0) insert into #sp_table values(N'sp_set_sqlagent_properties', 1, 1, 0) insert into #sp_table values(N'sp_add_targetservergroup', 1, 0, 0) insert into #sp_table values(N'sp_update_targetservergroup', 1, 0, 0) insert into #sp_table values(N'sp_delete_targetservergroup', 1, 0, 0) insert into #sp_table values(N'sp_help_targetservergroup', 1, 0, 0) insert into #sp_table values(N'sp_add_targetsvrgrp_member', 1, 0, 0) insert into #sp_table values(N'sp_delete_targetsvrgrp_member', 1, 0, 0) insert into #sp_table values(N'sp_verify_category', 1, 0, 0) insert into #sp_table values(N'sp_add_category', 1, 0, 0) insert into #sp_table values(N'sp_update_category', 1, 0, 0) insert into #sp_table values(N'sp_delete_category', 1, 0, 0) insert into #sp_table values(N'sp_help_category', 1, 0, 0) insert into #sp_table values(N'sp_help_targetserver', 1, 0, 0) insert into #sp_table values(N'sp_resync_targetserver', 1, 0, 0) insert into #sp_table values(N'sp_purge_jobhistory', 1, 0, 0) insert into #sp_table values(N'sp_help_jobhistory', 1, 0, 0) insert into #sp_table values(N'sp_help_jobhistory_full', 1, 0, 0) insert into #sp_table values(N'sp_help_jobhistory_summary', 1, 0, 0) insert into #sp_table values(N'sp_help_jobhistory_sem', 1, 0, 0) insert into #sp_table values(N'sp_add_jobserver', 1, 0, 0) insert into #sp_table values(N'sp_delete_jobserver', 1, 0, 0) insert into #sp_table values(N'sp_help_jobserver', 1, 0, 0) insert into #sp_table values(N'sp_help_downloadlist', 1, 0, 0) insert into #sp_table values(N'sp_enum_sqlagent_subsystems', 1, 0, 0) insert into #sp_table values(N'sp_enum_sqlagent_subsystems_internal', 1, 0, 0) insert into #sp_table values(N'sp_verify_subsystem', 1, 1, 0) insert into #sp_table values(N'sp_verify_subsystems', 1, 0, 0) insert into #sp_table values(N'sp_verify_schedule', 1, 0, 0) insert into #sp_table values(N'sp_add_schedule', 1, 0, 0) insert into #sp_table values(N'sp_attach_schedule', 1, 0, 0) insert into #sp_table values(N'sp_detach_schedule', 1, 0, 0) insert into #sp_table values(N'sp_update_schedule', 1, 0, 0) insert into #sp_table values(N'sp_delete_schedule', 1, 0, 0) insert into #sp_table values(N'sp_get_jobstep_db_username', 1, 0, 0) insert into #sp_table values(N'sp_verify_jobstep', 1, 0, 0) insert into #sp_table values(N'sp_add_jobstep_internal', 1, 0, 0) insert into #sp_table values(N'sp_add_jobstep', 1, 0, 0) insert into #sp_table values(N'sp_update_jobstep', 1, 0, 0) insert into #sp_table values(N'sp_delete_jobstep', 1, 0, 0) insert into #sp_table values(N'sp_help_jobstep', 1, 0, 0) insert into #sp_table values(N'sp_write_sysjobstep_log', 1, 0, 0) insert into #sp_table values(N'sp_help_jobsteplog', 1, 0, 0) insert into #sp_table values(N'sp_delete_jobsteplog', 1, 0, 0) insert into #sp_table values(N'sp_get_schedule_description', 1, 1, 0) insert into #sp_table values(N'sp_add_jobschedule', 1, 0, 0) insert into #sp_table values(N'sp_update_replication_job_parameter', 1, 0, 0) insert into #sp_table values(N'sp_update_jobschedule', 1, 0, 0) insert into #sp_table values(N'sp_delete_jobschedule', 1, 0, 0) insert into #sp_table values(N'sp_help_schedule', 1, 0, 0) insert into #sp_table values(N'sp_help_jobschedule', 1, 0, 0) insert into #sp_table values(N'sp_verify_job', 1, 1, 0) insert into #sp_table values(N'sp_add_job', 1, 0, 0) insert into #sp_table values(N'sp_update_job', 1, 0, 0) insert into #sp_table values(N'sp_delete_job', 1, 0, 0) insert into #sp_table values(N'sp_get_composite_job_info', 1, 1, 0) insert into #sp_table values(N'sp_help_job', 1, 0, 0) insert into #sp_table values(N'sp_help_jobcount ', 1, 0, 0) insert into #sp_table values(N'sp_help_jobs_in_schedule', 1, 0, 0) insert into #sp_table values(N'sp_manage_jobs_by_login', 1, 0, 0) insert into #sp_table values(N'sp_apply_job_to_targets', 1, 0, 0) insert into #sp_table values(N'sp_remove_job_from_targets', 1, 0, 0) insert into #sp_table values(N'sp_get_job_alerts', 1, 0, 0) insert into #sp_table values(N'sp_convert_jobid_to_char', 1, 0, 0) insert into #sp_table values(N'sp_start_job', 1, 0, 0) insert into #sp_table values(N'sp_stop_job', 1, 0, 0) insert into #sp_table values(N'sp_cycle_agent_errorlog', 1, 0, 0) insert into #sp_table values(N'sp_get_chunked_jobstep_params', 1, 0, 0) insert into #sp_table values(N'sp_check_for_owned_jobs', 1, 0, 0) insert into #sp_table values(N'sp_check_for_owned_jobsteps', 1, 0, 0) insert into #sp_table values(N'sp_sqlagent_refresh_job', 1, 0, 0) insert into #sp_table values(N'sp_jobhistory_row_limiter', 1, 1, 0) insert into #sp_table values(N'sp_sqlagent_log_jobhistory', 1, 0, 0) insert into #sp_table values(N'sp_sqlagent_check_msx_version', 1, 0, 0) insert into #sp_table values(N'sp_sqlagent_probe_msx', 1, 0, 0) insert into #sp_table values(N'sp_set_local_time', 1, 1, 0) insert into #sp_table values(N'sp_multi_server_job_summary', 1, 0, 0) insert into #sp_table values(N'sp_target_server_summary', 1, 0, 0) insert into #sp_table values(N'sp_uniquetaskname', 1, 0, 0) insert into #sp_table values(N'sp_addtask', 1, 0, 0) insert into #sp_table values(N'sp_droptask', 1, 0, 0) insert into #sp_table values(N'sp_add_alert_internal', 1, 0, 0) insert into #sp_table values(N'sp_add_alert', 1, 0, 0) insert into #sp_table values(N'sp_delete_alert', 1, 0, 0) insert into #sp_table values(N'sp_help_alert', 1, 0, 0) insert into #sp_table values(N'sp_verify_operator', 1, 0, 0) insert into #sp_table values(N'sp_add_operator', 1, 0, 0) insert into #sp_table values(N'sp_update_operator', 1, 1, 0) insert into #sp_table values(N'sp_help_operator', 1, 0, 0) insert into #sp_table values(N'sp_help_operator_jobs', 1, 0, 0) insert into #sp_table values(N'sp_verify_operator_identifiers', 1, 0, 0) insert into #sp_table values(N'sp_notify_operator', 1, 0, 0) insert into #sp_table values(N'sp_verify_notification', 1, 0, 0) insert into #sp_table values(N'sp_add_notification', 1, 0, 0) insert into #sp_table values(N'sp_update_notification', 1, 0, 0) insert into #sp_table values(N'sp_delete_notification', 1, 0, 0) insert into #sp_table values(N'sp_help_notification', 1, 0, 0) insert into #sp_table values(N'sp_help_jobactivity', 1, 0, 0) insert into #sp_table values(N'sp_enlist_tsx', 1, 1, 0) insert into #sp_table values(N'trig_targetserver_insert', 1, 0, 0) -- Database Mail configuration procs insert into #sp_table values(N'sysmail_verify_accountparams_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_verify_principal_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_verify_profile_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_verify_account_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_add_profile_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_update_profile_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_delete_profile_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_help_profile_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_create_user_credential_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_alter_user_credential_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_drop_user_credential_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_add_account_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_update_account_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_delete_account_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_help_account_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_help_admin_account_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_add_profileaccount_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_update_profileaccount_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_delete_profileaccount_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_help_profileaccount_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_configure_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_help_configure_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_help_configure_value_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_add_principalprofile_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_update_principalprofile_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_delete_principalprofile_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_help_principalprofile_sp', 1, 0, 0) -- Database Mail: mail host database specific procs insert into #sp_table values(N'sysmail_start_sp', 1, 2, 0) insert into #sp_table values(N'sysmail_stop_sp', 1, 2, 0) insert into #sp_table values(N'sysmail_logmailevent_sp', 1, 0, 0) insert into #sp_table values(N'sp_SendMailMessage', 1, 0, 0) insert into #sp_table values(N'sp_isprohibited', 1, 0, 0) insert into #sp_table values(N'sp_SendMailQueues', 1, 0, 0) insert into #sp_table values(N'sp_ProcessResponse', 1, 0, 0) insert into #sp_table values(N'sp_MailItemResultSets', 1, 0, 0) insert into #sp_table values(N'sp_process_DialogTimer', 1, 0, 0) insert into #sp_table values(N'sp_readrequest', 1, 0, 0) insert into #sp_table values(N'sp_GetAttachmentData', 1, 0, 0) insert into #sp_table values(N'sp_RunMailQuery', 1, 0, 0) insert into #sp_table values(N'sysmail_help_queue_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_help_status_sp', 1, 2, 0) insert into #sp_table values(N'sysmail_delete_mailitems_sp', 1, 0, 0) insert into #sp_table values(N'sysmail_delete_log_sp', 1, 0, 0) insert into #sp_table values(N'sp_validate_user', 1, 2, 0) insert into #sp_table values(N'sp_send_dbmail', 1, 2, 0) insert into #sp_table values(N'sp_ExternalMailQueueListener', 1, 0, 0) insert into #sp_table values(N'sp_sysmail_activate', 1, 0, 0) insert into #sp_table values(N'sp_get_script', 1, 0, 0) -- Maintenance Plans insert into #sp_table values(N'sp_maintplan_delete_log', 1, 0, 0) insert into #sp_table values(N'sp_maintplan_delete_subplan', 1, 0, 0) insert into #sp_table values(N'sp_maintplan_open_logentry', 1, 0, 0) insert into #sp_table values(N'sp_maintplan_close_logentry', 1, 0, 0) insert into #sp_table values(N'sp_maintplan_update_log', 1, 0, 0) insert into #sp_table values(N'sp_maintplan_update_subplan', 1, 0, 0) insert into #sp_table values(N'sp_maintplan_delete_plan', 1, 0, 0) insert into #sp_table values(N'sp_maintplan_start', 1, 0, 0) insert into #sp_table values(N'sp_clear_dbmaintplan_by_db', 1, 0, 0) insert into #sp_table values(N'sp_add_maintenance_plan', 1, 0, 0) insert into #sp_table values(N'sp_delete_maintenance_plan', 1, 0, 0) insert into #sp_table values(N'sp_add_maintenance_plan_db', 1, 0, 0) insert into #sp_table values(N'sp_delete_maintenance_plan_db', 1, 0, 0) insert into #sp_table values(N'sp_add_maintenance_plan_job', 1, 1, 0) insert into #sp_table values(N'sp_delete_maintenance_plan_job', 1, 0, 0) insert into #sp_table values(N'sp_help_maintenance_plan', 1, 0, 0) insert into #sp_table values(N'sp_maintplan_update_subplan_tsx', 1, 0, 0) insert into #sp_table values(N'sp_maintplan_subplans_by_job', 1, 0, 0) -- Log Shipping insert into #sp_table values(N'sp_add_log_shipping_monitor_jobs', 1, 0, 0) insert into #sp_table values(N'sp_add_log_shipping_primary', 1, 0, 0) insert into #sp_table values(N'sp_add_log_shipping_secondary', 1, 0, 0) insert into #sp_table values(N'sp_delete_log_shipping_monitor_jobs', 1, 0, 0) insert into #sp_table values(N'sp_delete_log_shipping_primary', 1, 0, 0) insert into #sp_table values(N'sp_delete_log_shipping_secondary ', 1, 0, 0) insert into #sp_table values(N'sp_log_shipping_in_sync', 1, 0, 0) insert into #sp_table values(N'sp_log_shipping_get_date_from_file ', 1, 0, 0) insert into #sp_table values(N'sp_get_log_shipping_monitor_info', 1, 0, 0) insert into #sp_table values(N'sp_update_log_shipping_monitor_info', 1, 0, 0) insert into #sp_table values(N'sp_delete_log_shipping_monitor_info', 1, 0, 0) insert into #sp_table values(N'sp_remove_log_shipping_monitor_account', 1, 0, 0) insert into #sp_table values(N'sp_log_shipping_monitor_backup', 1, 0, 0) insert into #sp_table values(N'sp_log_shipping_monitor_restore', 1, 0, 0) insert into #sp_table values(N'sp_change_monitor_role', 1, 0, 0) insert into #sp_table values(N'sp_create_log_shipping_monitor_account', 1, 0, 0) -- DTS insert into #sp_table values(N'sp_get_dtsversion', 1, 0, 0) insert into #sp_table values(N'sp_make_dtspackagename', 1, 0, 0) insert into #sp_table values(N'sp_add_dtspackage', 1, 0, 0) insert into #sp_table values(N'sp_drop_dtspackage', 1, 0, 0) insert into #sp_table values(N'sp_reassign_dtspackageowner', 1, 0, 0) insert into #sp_table values(N'sp_get_dtspackage', 1, 0, 0) insert into #sp_table values(N'sp_reassign_dtspackagecategory', 1, 0, 0) insert into #sp_table values(N'sp_enum_dtspackages', 1, 0, 0) insert into #sp_table values(N'sp_add_dtscategory', 1, 0, 0) insert into #sp_table values(N'sp_drop_dtscategory', 1, 0, 0) insert into #sp_table values(N'sp_modify_dtscategory', 1, 0, 0) insert into #sp_table values(N'sp_enum_dtscategories', 1, 0, 0) insert into #sp_table values(N'sp_log_dtspackage_begin', 1, 0, 0) insert into #sp_table values(N'sp_log_dtspackage_end', 1, 0, 0) insert into #sp_table values(N'sp_log_dtsstep_begin', 1, 0, 0) insert into #sp_table values(N'sp_log_dtsstep_end', 1, 0, 0) insert into #sp_table values(N'sp_log_dtstask', 1, 0, 0) insert into #sp_table values(N'sp_enum_dtspackagelog', 1, 0, 0) insert into #sp_table values(N'sp_enum_dtssteplog', 1, 0, 0) insert into #sp_table values(N'sp_enum_dtstasklog', 1, 0, 0) insert into #sp_table values(N'sp_dump_dtslog_all', 1, 0, 0) insert into #sp_table values(N'sp_dump_dtspackagelog', 1, 0, 0) insert into #sp_table values(N'sp_dump_dtssteplog', 1, 0, 0) insert into #sp_table values(N'sp_dump_dtstasklog', 1, 0, 0) insert into #sp_table values(N'sp_dts_secure', 1, 0, 0) insert into #sp_table values(N'sp_ssis_addlogentry', 1, 0, 0) insert into #sp_table values(N'sp_ssis_listpackages', 1, 0, 0) insert into #sp_table values(N'sp_ssis_listfolders', 1, 0, 0) insert into #sp_table values(N'sp_ssis_deletepackage', 1, 0, 0) insert into #sp_table values(N'sp_ssis_deletefolder', 1, 0, 0) insert into #sp_table values(N'sp_ssis_getpackage', 1, 0, 0) insert into #sp_table values(N'sp_ssis_getfolder', 1, 0, 0) insert into #sp_table values(N'sp_ssis_putpackage', 1, 0, 0) insert into #sp_table values(N'sp_ssis_addfolder', 1, 0, 0) insert into #sp_table values(N'sp_ssis_renamefolder', 1, 0, 0) insert into #sp_table values(N'sp_ssis_setpackageroles', 1, 0, 0) insert into #sp_table values(N'sp_ssis_getpackageroles', 1, 0, 0) go /**************************************************************/ /* Mark system objects */ /**************************************************************/ declare @start datetime ,@name sysname select @start = start from #InstMsdb -- As a temporary solution, exclude the syscollector_collection_sets table from being a system table declare newsysobjs cursor for select name from sys.objects where schema_id = 1 and create_date >= @start open newsysobjs fetch next from newsysobjs into @name while @@fetch_status = 0 begin Exec sp_MS_marksystemobject @name update #sp_table set bNewProc = 1 where name = @name fetch next from newsysobjs into @name end deallocate newsysobjs drop table #InstMsdb go PRINT 'Signing sps ...' -- Create certificate to sign Agent sps -- declare @certError int if exists (select * from sys.certificates where name = '##MS_AgentSigningCertificate##') begin PRINT 'Dropping existing Agent certificate ...' drop certificate [##MS_AgentSigningCertificate##] select @certError = @@error IF (@certError <> 0) BEGIN select 'Cannot drop existing certificate. Objects still signed by ##MS_AgentSigningCertificate##' = object_name(crypts.major_id) from sys.crypt_properties crypts, sys.certificates certs where crypts.thumbprint = certs.thumbprint and crypts.class = 1 and certs.name = '##MS_AgentSigningCertificate##' RAISERROR('Cannot drop existing ##MS_AgentSigningCertificate## in msdb. INSTMSDB.SQL terminating.', 20, 127) WITH LOG END end -- If the temp table #SqlAgentSignatures exists, then this script is running as part -- of an upgrade from a previous build of sql server. In this case we want to sign -- the Agent procedures using the same signatures that exist in a clean install -- of this same build. Those signatures are in #SqlAgentSignatures. To do this -- we create the certificate from the .cer file. -- -- In the non-upgrade case, meaning that this script is being run during the build -- of the SQL Server product itself, we simply create the certificate from scratch. CREATE TABLE #InstmsdbAgentCertPath (AgentCert NVARCHAR(1100)) DECLARE @certificate_name NVARCHAR(1100) -- space for path + MS_AgentSigningCertificate.cer IF (NOT OBJECT_ID(N'tempdb.dbo.#SqlAgentSignatures', 'U') IS NULL) BEGIN -- We need agent XP's to be on in order to lookup the path to our instance install folder. DECLARE @advopt_old_value int DECLARE @comp_old_value int EXEC #sp_enable_component 'Agent XPs', @advopt_old_value out, @comp_old_value out DECLARE @InstallRootPath nvarchar(1024) -- Note: This is an _instance_ registry lookup, so it will -- automatically insert the right instance name after the "Microsoft -- SQL Server" part of the path, thus looking in a path like -- 'SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL10.INST1\Setup' EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\Microsoft SQL Server\Setup', N'SQLPath', @InstallRootPath OUTPUT -- Restore Agent XPs to previous state EXECUTE #sp_restore_component_state 'Agent XPs', @advopt_old_value, @comp_old_value IF @InstallRootPath IS NULL BEGIN RAISERROR('Cannot find instance-specific registry path for SOFTWARE\Microsoft\Microsoft SQL Server\Setup\SqlPath. INSTMSDB.SQL terminating.', 20, 127) WITH LOG END -- Now find our certificate in the Install folder, placed there by setup. SELECT @InstallRootPath = @InstallRootPath + N'\Install' select @certificate_name = @InstallRootPath + N'\MS_AgentSigningCertificate.cer' -- Remember this file path so we can import it into master later insert #InstmsdbAgentCertPath(AgentCert) VALUES (@certificate_name) PRINT 'Updated #InstmsdbAgentCertPath with value ' + @certificate_name -- Handle single quotes in the directory name since this string is going to be passed to EXECUTE DECLARE @certificate_nameQuoted NVARCHAR(2200) SELECT @certificate_nameQuoted = '''' + REPLACE(@certificate_name, '''', '''''') + '''' PRINT 'Creating ##MS_AgentSigningCertificate## from ' + @certificate_name EXECUTE(N'create certificate [##MS_AgentSigningCertificate##] from file = ' + @certificate_nameQuoted) select @certError = @@error IF (@certError <> 0) BEGIN RAISERROR('Cannot create ##MS_AgentSigningCertificate## from file. INSTMSDB.SQL terminating.', 20, 127) WITH LOG END END ELSE BEGIN dbcc traceon(4606,-1) -- Ignore domain policy about weak password create certificate [##MS_AgentSigningCertificate##] encryption by password = 'Yukon90_' with subject = 'MS_AgentSigningCertificate' select @certError = @@error dbcc traceoff(4606,-1) IF (@certError <> 0) BEGIN RAISERROR('Cannot create ##MS_AgentSigningCertificate## in msdb. INSTMSDB.SQL terminating.', 20, 127) WITH LOG END --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 INSTMSDB.SQL complete' PRINT '----------------------------------' go CHECKPOINT go use msdb go DECLARE @CMDExec sysname EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent\SubSystems', N'CmdExec', @CMDExec OUTPUT, N'no_output' IF @CMDExec IS NOT NULL BEGIN PRINT '' PRINT 'Remove subsystem definition from registry...' --remove subsystem definition from registry and populate subsystem table DECLARE @ret INT EXEC @ret = master..xp_instance_regdeletekey N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent\SubSystems' IF @ret <> 0 RAISERROR('Failed to remove subsystems definition from registry', 10, 127) END PRINT '' PRINT 'Populate syssubsystem table...' EXEC @ret = sp_enum_sqlagent_subsystems IF @ret <> 0 RAISERROR('Failed to add subsystems definition to syssubsystem table. Upgrade script terminating', 20, 127) WITH LOG go /**********************************************************************/ /* Loading the syssubsystems related user info */ /**********************************************************************/ IF (OBJECT_ID('tempdb..#syssubsystems_temp', 'U') IS NOT NULL) BEGIN PRINT 'Loading saved syssubsystems related user info...' UPDATE syssubsystems SET syssubsystems.max_worker_threads = #syssubsystems_temp.max_worker_threads FROM #syssubsystems_temp WHERE #syssubsystems_temp.subsystem = syssubsystems.subsystem END GO /**********************************************************************/ /* DONE - Loading the syssubsystems related user info */ /**********************************************************************/ --restore Shiloh object permission DECLARE @state_desc nvarchar(60) DECLARE @permission_name sysname DECLARE @object_name sysname DECLARE @grantee_name sysname DECLARE perms_set_cursor CURSOR LOCAL FOR SELECT state_desc, permission_name, object_name, grantee_name from msdb.dbo.upgrade_perms OPEN perms_set_cursor FETCH NEXT FROM perms_set_cursor INTO @state_desc, @permission_name, @object_name, @grantee_name WHILE (@@fetch_status = 0) BEGIN --PRINT @state_desc + N' ' + @permission_name + N' ON ' + @object_name + N' TO ' + @grantee_name BEGIN TRY EXEC (@state_desc + N' ' + @permission_name + N' ON ' + @object_name + N' TO ' + @grantee_name) END TRY BEGIN CATCH END CATCH FETCH NEXT FROM perms_set_cursor INTO @state_desc, @permission_name, @object_name, @grantee_name END DEALLOCATE perms_set_cursor --remove public from SQLAgent Shiloh procedures PRINT '' PRINT 'Revoke any permission to public role...' BEGIN TRY REVOKE ALL ON sp_add_alert FROM PUBLIC REVOKE ALL ON sp_add_category FROM PUBLIC REVOKE ALL ON sp_add_job FROM PUBLIC REVOKE ALL ON sp_add_jobschedule FROM PUBLIC REVOKE ALL ON sp_add_jobserver FROM PUBLIC REVOKE ALL ON sp_add_jobstep FROM PUBLIC REVOKE ALL ON sp_add_notification FROM PUBLIC REVOKE ALL ON sp_add_operator FROM PUBLIC REVOKE ALL ON sp_add_targetservergroup FROM PUBLIC REVOKE ALL ON sp_add_targetsvrgrp_member FROM PUBLIC REVOKE ALL ON sp_apply_job_to_targets FROM PUBLIC REVOKE ALL ON sp_convert_jobid_to_char FROM PUBLIC REVOKE ALL ON sp_delete_alert FROM PUBLIC REVOKE ALL ON sp_delete_all_msx_jobs FROM PUBLIC REVOKE ALL ON sp_delete_category FROM PUBLIC REVOKE ALL ON sp_delete_job FROM PUBLIC REVOKE ALL ON sp_delete_job_references FROM PUBLIC REVOKE ALL ON sp_delete_jobschedule FROM PUBLIC REVOKE ALL ON sp_delete_jobserver FROM PUBLIC REVOKE ALL ON sp_delete_jobstep FROM PUBLIC REVOKE ALL ON sp_delete_notification FROM PUBLIC REVOKE ALL ON sp_delete_operator FROM PUBLIC REVOKE ALL ON sp_delete_targetserver FROM PUBLIC REVOKE ALL ON sp_delete_targetservergroup FROM PUBLIC REVOKE ALL ON sp_delete_targetsvrgrp_member FROM PUBLIC REVOKE ALL ON sp_downloaded_row_limiter FROM PUBLIC REVOKE ALL ON sp_generate_server_description FROM PUBLIC REVOKE ALL ON sp_generate_target_server_job_assignment_sql FROM PUBLIC REVOKE ALL ON sp_get_chunked_jobstep_params FROM PUBLIC REVOKE ALL ON sp_get_composite_job_info FROM PUBLIC REVOKE ALL ON sp_get_job_alerts FROM PUBLIC REVOKE ALL ON sp_get_jobstep_db_username FROM PUBLIC REVOKE ALL ON sp_get_message_description FROM PUBLIC REVOKE ALL ON sp_get_schedule_description FROM PUBLIC REVOKE ALL ON sp_get_sqlagent_properties FROM PUBLIC REVOKE ALL ON sp_help_alert FROM PUBLIC REVOKE ALL ON sp_help_category FROM PUBLIC REVOKE ALL ON sp_help_downloadlist FROM PUBLIC REVOKE ALL ON sp_help_job FROM PUBLIC REVOKE ALL ON sp_help_jobhistory FROM PUBLIC REVOKE ALL ON sp_help_jobschedule FROM PUBLIC REVOKE ALL ON sp_help_jobserver FROM PUBLIC REVOKE ALL ON sp_help_jobstep FROM PUBLIC REVOKE ALL ON sp_help_notification FROM PUBLIC REVOKE ALL ON sp_help_operator FROM PUBLIC REVOKE ALL ON sp_help_operator_jobs FROM PUBLIC REVOKE ALL ON sp_help_targetserver FROM PUBLIC REVOKE ALL ON sp_help_targetservergroup FROM PUBLIC REVOKE ALL ON sp_is_sqlagent_starting FROM PUBLIC REVOKE ALL ON sp_jobhistory_row_limiter FROM PUBLIC REVOKE ALL ON sp_manage_jobs_by_login FROM PUBLIC REVOKE ALL ON sp_msx_defect FROM PUBLIC REVOKE ALL ON sp_msx_enlist FROM PUBLIC REVOKE ALL ON sp_multi_server_job_summary FROM PUBLIC REVOKE ALL ON sp_post_msx_operation FROM PUBLIC REVOKE ALL ON sp_purge_jobhistory FROM PUBLIC REVOKE ALL ON sp_remove_job_from_targets FROM PUBLIC REVOKE ALL ON sp_resync_targetserver FROM PUBLIC REVOKE ALL ON sp_sem_add_message FROM PUBLIC REVOKE ALL ON sp_sem_drop_message FROM PUBLIC REVOKE ALL ON sp_set_local_time FROM PUBLIC REVOKE ALL ON sp_set_sqlagent_properties FROM PUBLIC REVOKE ALL ON sp_sqlagent_check_msx_version FROM PUBLIC REVOKE ALL ON sp_sqlagent_get_perf_counters FROM PUBLIC REVOKE ALL ON sp_sqlagent_get_startup_info FROM PUBLIC REVOKE ALL ON sp_sqlagent_has_server_access FROM PUBLIC REVOKE ALL ON sp_sqlagent_log_jobhistory FROM PUBLIC REVOKE ALL ON sp_sqlagent_notify FROM PUBLIC REVOKE ALL ON sp_sqlagent_probe_msx FROM PUBLIC REVOKE ALL ON sp_sqlagent_refresh_job FROM PUBLIC REVOKE ALL ON sp_start_job FROM PUBLIC REVOKE ALL ON sp_stop_job FROM PUBLIC REVOKE ALL ON sp_target_server_summary FROM PUBLIC REVOKE ALL ON sp_uniquetaskname FROM PUBLIC REVOKE ALL ON sp_update_alert FROM PUBLIC REVOKE ALL ON sp_update_category FROM PUBLIC REVOKE ALL ON sp_update_job FROM PUBLIC REVOKE ALL ON sp_update_jobschedule FROM PUBLIC REVOKE ALL ON sp_update_jobstep FROM PUBLIC REVOKE ALL ON sp_update_notification FROM PUBLIC REVOKE ALL ON sp_update_operator FROM PUBLIC REVOKE ALL ON sp_update_targetservergroup FROM PUBLIC REVOKE ALL ON sp_verify_alert FROM PUBLIC REVOKE ALL ON sp_verify_category FROM PUBLIC REVOKE ALL ON sp_verify_job FROM PUBLIC REVOKE ALL ON sp_verify_job_date FROM PUBLIC REVOKE ALL ON sp_verify_job_identifiers FROM PUBLIC REVOKE ALL ON sp_verify_job_time FROM PUBLIC REVOKE ALL ON sp_verify_jobproc_caller FROM PUBLIC REVOKE ALL ON sp_verify_jobstep FROM PUBLIC REVOKE ALL ON sp_verify_notification FROM PUBLIC REVOKE ALL ON sp_verify_operator FROM PUBLIC REVOKE ALL ON sp_verify_performance_condition FROM PUBLIC REVOKE ALL ON sp_verify_subsystem FROM PUBLIC END TRY BEGIN CATCH END CATCH go --remove public permission from Shiloh objects that have been secured in Shiloh and overwritten during instmsdb.sql --create a temporary table with objects granted to public during execution of instmsdb.sql CREATE TABLE #instmsdb_public_objects(object_name sysname) INSERT INTO #instmsdb_public_objects VALUES (N'backupfile') INSERT INTO #instmsdb_public_objects VALUES (N'backupmediafamily') INSERT INTO #instmsdb_public_objects VALUES (N'backupmediaset') INSERT INTO #instmsdb_public_objects VALUES (N'backupset') INSERT INTO #instmsdb_public_objects VALUES (N'restorehistory') INSERT INTO #instmsdb_public_objects VALUES (N'restorefile') INSERT INTO #instmsdb_public_objects VALUES (N'restorefilegroup') INSERT INTO #instmsdb_public_objects VALUES (N'logmarkhistory') INSERT INTO #instmsdb_public_objects VALUES (N'suspect_pages') INSERT INTO #instmsdb_public_objects VALUES (N'sp_get_dtsversion') INSERT INTO #instmsdb_public_objects VALUES (N'sp_make_dtspackagename') INSERT INTO #instmsdb_public_objects VALUES (N'sp_add_dtspackage') INSERT INTO #instmsdb_public_objects VALUES (N'sp_drop_dtspackage') INSERT INTO #instmsdb_public_objects VALUES (N'sp_reassign_dtspackageowner') INSERT INTO #instmsdb_public_objects VALUES (N'sp_get_dtspackage') INSERT INTO #instmsdb_public_objects VALUES (N'sp_enum_dtspackages') INSERT INTO #instmsdb_public_objects VALUES (N'sp_log_dtspackage_begin') INSERT INTO #instmsdb_public_objects VALUES (N'sp_log_dtspackage_end') INSERT INTO #instmsdb_public_objects VALUES (N'sp_log_dtsstep_begin') INSERT INTO #instmsdb_public_objects VALUES (N'sp_log_dtsstep_end') INSERT INTO #instmsdb_public_objects VALUES (N'sp_log_dtstask') INSERT INTO #instmsdb_public_objects VALUES (N'sp_enum_dtspackagelog') INSERT INTO #instmsdb_public_objects VALUES (N'sp_enum_dtssteplog') INSERT INTO #instmsdb_public_objects VALUES (N'sp_enum_dtstasklog') INSERT INTO #instmsdb_public_objects VALUES (N'sp_dump_dtslog_all') INSERT INTO #instmsdb_public_objects VALUES (N'sp_dump_dtspackagelog') INSERT INTO #instmsdb_public_objects VALUES (N'sp_dump_dtssteplog') INSERT INTO #instmsdb_public_objects VALUES (N'sp_dump_dtstasklog') go DECLARE @object_name sysname DECLARE @tsql nvarchar(300) DECLARE public_remove_cursor CURSOR LOCAL FOR SELECT object_name FROM #instmsdb_public_objects OPEN public_remove_cursor FETCH NEXT FROM public_remove_cursor INTO @object_name WHILE (@@fetch_status = 0) BEGIN --PRINT @state_desc + N' ' + @permission_name + N' ON ' + @object_name + N' TO ' + @grantee_name BEGIN TRY --if this object exists in 8.0 msdb and is granted to public no op otherwise remove permission to public on it IF (EXISTS (SELECT * FROM msdb.dbo.upgrade_perms WHERE UPPER(object_name collate SQL_Latin1_General_CP1_CS_AS ) = UPPER(@object_name collate SQL_Latin1_General_CP1_CS_AS ))) AND (NOT EXISTS (SELECT * FROM msdb.dbo.upgrade_perms WHERE object_name = @object_name AND UPPER(grantee_name collate SQL_Latin1_General_CP1_CS_AS ) = N'PUBLIC')) BEGIN SELECT @tsql = N'REVOKE ALL ON ' + QUOTENAME(@object_name) + N' FROM PUBLIC' PRINT @tsql EXEC (@tsql) END END TRY BEGIN CATCH END CATCH FETCH NEXT FROM public_remove_cursor INTO @object_name END DEALLOCATE public_remove_cursor go DROP TABLE #instmsdb_public_objects DROP TABLE msdb.dbo.upgrade_perms go -------------------------------------------------------------------------------- --update proxy account --proxy update batch --read sysadminonly flag DECLARE @ret INT DECLARE @proxy_id INT DECLARE @job_id UNIQUEIDENTIFIER DECLARE @step_id INT DECLARE @subsystem sysname DECLARE @owner_name NVARCHAR(256) DECLARE @full_name sysname DECLARE @owner_sid VARBINARY(85) DECLARE @is_sysadmin INT --walk throu all job steps excluding TSQL jobsteps DECLARE jobsteps_cursor CURSOR LOCAL FOR SELECT js.job_id, js.step_id, js.subsystem, SUSER_SNAME(j.owner_sid) FROM sysjobs j JOIN sysjobsteps js ON j.job_id = js.job_id WHERE UPPER(js.subsystem collate SQL_Latin1_General_CP1_CS_AS) <> N'TSQL' AND SUSER_SNAME(j.owner_sid) IS NOT NULL FOR READ ONLY --wals thru all non sysadmin job owners DECLARE job_nonsysadmin_owners_cursor CURSOR LOCAL FOR SELECT DISTINCT j.owner_sid FROM sysjobs j FOR READ ONLY OPEN job_nonsysadmin_owners_cursor FETCH NEXT FROM job_nonsysadmin_owners_cursor INTO @owner_sid WHILE (@@fetch_status = 0) BEGIN SELECT @owner_name = SUSER_SNAME(@owner_sid) IF @owner_name IS NOT NULL BEGIN --is job owner member of sysadmin role? BEGIN TRY EXECUTE AS LOGIN=@owner_name -- impersonate SELECT @is_sysadmin = ISNULL(IS_SRVROLEMEMBER('sysadmin'),0) -- check role membership REVERT -- revert back END TRY BEGIN CATCH SET @is_sysadmin = 0 END CATCH IF @is_sysadmin = 0 BEGIN --add job_owner to the SQLAgentUserRole msdb role in order to permit the job owner to handle his jobs --has this login a user in msdb? IF NOT EXISTS(SELECT * FROM sys.database_principals WHERE sid = @owner_sid) BEGIN PRINT '' PRINT 'Granting login access''' + @owner_name + ''' to msdb database...' BEGIN TRY EXEC sp_grantdbaccess @loginame = @owner_name END TRY BEGIN CATCH RAISERROR('A problem was encountered granting access to MSDB database for login ''%s''. Make sure this login is provisioned with SQLServer and rerun sqlagent_msdb_upgrade.sql ', 10, 127) WITH LOG END CATCH END PRINT '' PRINT 'Adding user ''' + @owner_name + ''' to SQLAgentUserRole msdb role...' BEGIN TRY EXEC sp_addrolemember @rolename = 'SQLAgentUserRole', @membername = @owner_name END TRY BEGIN CATCH RAISERROR('A problem was encountered adding user ''%s'' to SQLAgentUserRole. Make sure this is a valid user in MSDB database and rerun sqlagent_msdb_upgrade.sql ', 10, 127) WITH LOG END CATCH END END FETCH NEXT FROM job_nonsysadmin_owners_cursor INTO @owner_sid END DEALLOCATE job_nonsysadmin_owners_cursor --credential created from LSA values in the file src\upgrade_scripts\sqlagent100_upgrade.sql IF EXISTS (SELECT credential_id FROM master.sys.credentials WHERE name = N'UpgradedCredential') BEGIN IF (NOT EXISTS(SELECT * FROM sysproxies WHERE name = N'UpgradedProxyAccount')) BEGIN PRINT 'Update proxy account' SELECT @ret = 0 --create the proxy first PRINT '' PRINT 'Adding Proxy...' BEGIN TRY --prevent sp_add_proxy raising severity 16 error that will terminate upgrade scritp when proxy account has bad info EXEC @ret = sp_add_proxy @proxy_name = N'UpgradedProxyAccount', @credential_name = N'UpgradedCredential', @proxy_id = @proxy_id OUTPUT END TRY BEGIN CATCH SET @ret = 1 END CATCH IF @ret <> 0 BEGIN RAISERROR('A problem was encountered updating proxy account. Verify that user name and secret of credential [UpgradedCredential] are correct and rerun sqlagent_msdb_upgrade.sql ', 10, 127) WITH LOG END ELSE BEGIN OPEN jobsteps_cursor FETCH NEXT FROM jobsteps_cursor INTO @job_id, @step_id, @subsystem, @owner_name WHILE (@@fetch_status = 0) BEGIN --is job owner member of sysadmin role? BEGIN TRY EXECUTE AS LOGIN = @owner_name -- impersonate SELECT @is_sysadmin = ISNULL(IS_SRVROLEMEMBER('sysadmin'),0) -- check role membership REVERT -- revert back END TRY BEGIN CATCH SET @is_sysadmin = 0 END CATCH IF @is_sysadmin = 0 BEGIN IF NOT EXISTS(SELECT * FROM sysproxysubsystem ps JOIN syssubsystems s ON ps.subsystem_id = s.subsystem_id WHERE s.subsystem = @subsystem AND ps.proxy_id = @proxy_id) BEGIN PRINT 'Grant permission to proxy ''UpgradedProxyAccount'' for subsystem ''' + @subsystem + '''...' BEGIN TRY EXEC @ret = sp_grant_proxy_to_subsystem @subsystem_name = @subsystem, @proxy_id = @proxy_id END TRY BEGIN CATCH SET @ret = 1 END CATCH IF @ret <> 0 BEGIN RAISERROR('FAILED TO GRANT PERMISSION TO PROXY (%d) FOR SUBSYSTEM ''%s''.', 10, 127, @proxy_id, @subsystem ) WITH LOG END END IF NOT EXISTS(SELECT * FROM sysproxylogin WHERE proxy_id = @proxy_id AND sid = SUSER_SID(@owner_name)) BEGIN PRINT 'Grant login ''' + @owner_name + ''' permission to use proxy ''UpgradedProxyAccount'' for subsystem ''' + @subsystem + '''...' BEGIN TRY EXEC @ret = sp_grant_login_to_proxy @proxy_id = @proxy_id, @login_name = @owner_name END TRY BEGIN CATCH SET @ret = 1 END CATCH IF @ret <> 0 BEGIN RAISERROR('FAILED TO GRANT PERMISSION TO LOGIN ''%s'' FOR PROXY (%d).', 10, 127, @owner_name, @proxy_id) WITH LOG END END --PRINT 'Update sysjobsteps...' UPDATE sysjobsteps SET proxy_id = @proxy_id WHERE job_id = @job_id and step_id = @step_id END --is_sysadmin = 0 FETCH NEXT FROM jobsteps_cursor INTO @job_id, @step_id, @subsystem, @owner_name END --WHILE (@@fetch_status = 0) CLOSE jobsteps_cursor END END -- NOT EXISTS(SELECT * FROM sysproxies WHERE name = N'UpgradedProxyAccount') END -- EXISTS (SELECT credential_id FROM master.sys.credentials WHERE name = N'AgentCred80') DEALLOCATE jobsteps_cursor go --msx proxy upgrade DECLARE @credential_id INT --credential created from 80 lSA values in the file src\upgrade_scripts\sqlagent100_upgrade.sql IF EXISTS (SELECT credential_id FROM master.sys.credentials WHERE name = N'UpgradedMSXCredential') BEGIN SELECT @credential_id = credential_id FROM master.sys.credentials WHERE name = N'UpgradedMSXCredential' --set credential_id to agent registry EXECUTE master.dbo.xp_instance_regwrite 'HKEY_LOCAL_MACHINE', 'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', 'MSXCredentialID', 'REG_DWORD', @credential_id END go -- Complete replication job security meta-data upgrades IF OBJECT_ID('sys.sp_vupgrade_replsecurity_metadata', 'P') IS NOT NULL BEGIN PRINT 'Performing replication job security meta-data upgrades...' EXECUTE master.sys.sp_vupgrade_replsecurity_metadata END ELSE BEGIN -- "The replication security meta-data could not be upgraded for all database(s). Please ensure that entire server is upgraded and re-execute sp_vupgrade_replsecurity_metadata." RAISERROR(21450, 10, -1, 'security meta-data', 'all', 'entire server', 'master.sys.sp_vupgrade_replsecurity_metadata') WITH LOG END GO -- Log Shipping Upgrade declare @retcode int PRINT '' PRINT 'Upgrading SQL Server Log Shipping...' exec @retcode = master.sys.sp_upgrade_log_shipping if @retcode != 0 or @@error != 0 begin RAISERROR('Upgrade of Log Shipping for SQL Server did not complete successfully. After upgrade, re-execute sp_upgrade_log_shipping from master database.', 10, 1) WITH LOG end PRINT 'Upgraded SQL Server Log Shipping successfully.' go ----------------------------------------------------------------------------- --Drop legacy objects CREATE TABLE #instmsdb_legacy_objects(object_name sysname, type_name sysname, type_id nvarchar(2)) --tables INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.mswebtasks', N'TABLE', N'T' ) --procedures INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_helphistory', N'PROCEDURE', N'P' ) INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_helptask', N'PROCEDURE', N'P' ) INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_insmswebtask', N'PROCEDURE', N'P' ) INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_purgehistory', N'PROCEDURE', N'P' ) INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_sem_get_perf_counter_help', N'PROCEDURE', N'P' ) INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_updatetask', N'PROCEDURE', N'P' ) INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_updmswebtask', N'PROCEDURE', N'P' ) INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_verify_jobschedule', N'PROCEDURE', N'P' ) INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_verifytaskid', N'PROCEDURE', N'P' ) INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sp_check_localserver_name', N'PROCEDURE', N'P' ) --views INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.sysalternates', N'VIEW', N'V' ) INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.systasks', N'VIEW', N'V' ) INSERT INTO #instmsdb_legacy_objects VALUES (N'dbo.systasks_view', N'VIEW', N'V' ) go DECLARE @object_name sysname DECLARE @type_name sysname DECLARE @type_id nvarchar(2) DECLARE @tsql nvarchar(300) DECLARE legacy_remove_cursor CURSOR LOCAL FOR SELECT object_name, type_name, type_id FROM #instmsdb_legacy_objects OPEN legacy_remove_cursor FETCH NEXT FROM legacy_remove_cursor INTO @object_name, @type_name, @type_id WHILE (@@fetch_status = 0) BEGIN BEGIN TRY IF (OBJECT_ID(@object_name, @type_id) IS NOT NULL) BEGIN SELECT @tsql = N'DROP ' + @type_name + N' ' + @object_name PRINT @tsql EXEC (@tsql) END END TRY BEGIN CATCH END CATCH FETCH NEXT FROM legacy_remove_cursor INTO @object_name, @type_name, @type_id END CLOSE legacy_remove_cursor DEALLOCATE legacy_remove_cursor go DROP TABLE #instmsdb_legacy_objects go --other legacy objects CTP15 to RTM only BEGIN TRY if exists (select * from sys.database_principals where name = 'MS_AgentSigningCertificateUser') drop user MS_AgentSigningCertificateUser if exists (select * from sys.server_principals where name = 'MS_AgentSigningCertificateLogin') drop login MS_AgentSigningCertificateLogin -- remove the MaintenanceUserRole role CTP15 to RTM only IF (EXISTS (SELECT * FROM msdb.dbo.sysusers WHERE (name = N'MaintenanceUserRole') AND (issqlrole = 1))) BEGIN BEGIN EXECUTE msdb.dbo.sp_droprole @rolename = N'MaintenanceUserRole' END END END TRY BEGIN CATCH END CATCH go ------------------------------------------------------------- PRINT '------------------------------------' PRINT 'Moving 2005 SSIS Data to 2008 tables' PRINT '------------------------------------' -- -- Add the old roles to the new SSIS role membership -- PRINT 'Mapping SSIS yukon roles to katmai roles...' GO if exists (select * from dbo.sysusers where [name] = N'db_dtsadmin' and [issqlrole] = 1) BEGIN EXEC sp_addrolemember N'db_ssisadmin', N'db_dtsadmin' END GO if exists (select * from dbo.sysusers where [name] = N'db_dtsltduser' and [issqlrole] = 1) BEGIN EXEC sp_addrolemember N'db_ssisltduser', N'db_dtsltduser' END GO if exists (select * from dbo.sysusers where [name] = N'db_dtsoperator' and [issqlrole] = 1) BEGIN EXEC sp_addrolemember N'db_ssisoperator', N'db_dtsoperator' END GO -- -- Move folders from sysdtspackagefolders90 to sysssispackagefolders -- PRINT 'Moving package folders...' GO IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtspackagefolders90]') AND type = N'U') INSERT INTO [msdb].[dbo].[sysssispackagefolders] ([folderid] ,[parentfolderid] ,[foldername]) SELECT [folderid] ,[parentfolderid] ,[foldername] FROM [msdb].[dbo].[sysdtspackagefolders90] WHERE [folderid] != '00000000-0000-0000-0000-000000000000' -- Root folder AND [folderid] != '08aa12d5-8f98-4dab-a4fc-980b150a5dc8' -- Maintenance Plans GO -- -- Move packages from sysdtspackages90 to sysssispackages -- PRINT 'Moving packages...' GO IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtspackages90]') AND type = N'U') INSERT INTO [msdb].[dbo].[sysssispackages] ([name] ,[id] ,[description] ,[createdate] ,[folderid] ,[ownersid] ,[packagedata] ,[packageformat] ,[packagetype] ,[vermajor] ,[verminor] ,[verbuild] ,[vercomments] ,[verid] ,[isencrypted] ,[readrolesid] ,[writerolesid]) SELECT [name] ,[id] ,[description] ,[createdate] ,[folderid] ,[ownersid] ,[packagedata] ,[packageformat] ,[packagetype] ,[vermajor] ,[verminor] ,[verbuild] ,[vercomments] ,[verid] ,[isencrypted] ,[readrolesid] ,[writerolesid] FROM [msdb].[dbo].[sysdtspackages90] GO -- -- Move log data from sysdtslog90 to sysssislog -- PRINT 'Moving logs...' GO IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtslog90]') AND type = N'U') INSERT INTO [msdb].[dbo].[sysssislog] ([event] ,[computer] ,[operator] ,[source] ,[sourceid] ,[executionid] ,[starttime] ,[endtime] ,[datacode] ,[databytes] ,[message]) SELECT [event] ,[computer] ,[operator] ,[source] ,[sourceid] ,[executionid] ,[starttime] ,[endtime] ,[datacode] ,[databytes] ,[message] FROM [msdb].[dbo].[sysdtslog90] GO -- -- Drop yukon objects -- PRINT 'Dropping yukon stored procedures...' GO IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_setpackageroles]') AND type = N'P') DROP PROCEDURE [dbo].[sp_dts_setpackageroles] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_getpackageroles]') AND type = N'P') DROP PROCEDURE [dbo].[sp_dts_getpackageroles] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_deletepackage]') AND type = N'P') DROP PROCEDURE [dbo].[sp_dts_deletepackage] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_getpackage]') AND type = N'P') DROP PROCEDURE [dbo].[sp_dts_getpackage] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_putpackage]') AND type = N'P') DROP PROCEDURE [dbo].[sp_dts_putpackage] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_checkexists]') AND type = N'P') DROP PROCEDURE [dbo].[sp_dts_checkexists] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_listpackages]') AND type = N'P') DROP PROCEDURE [dbo].[sp_dts_listpackages] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_addlogentry]') AND type = N'P') DROP PROCEDURE [dbo].[sp_dts_addlogentry] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_deletefolder]') AND type = N'P') DROP PROCEDURE [dbo].[sp_dts_deletefolder] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_addfolder]') AND type = N'P') DROP PROCEDURE [dbo].[sp_dts_addfolder] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_renamefolder]') AND type = N'P') DROP PROCEDURE [dbo].[sp_dts_renamefolder] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_getfolder]') AND type = N'P') DROP PROCEDURE [dbo].[sp_dts_getfolder] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sp_dts_listfolders]') AND type = N'P') DROP PROCEDURE [dbo].[sp_dts_listfolders] GO PRINT 'Dropping yukon tables...' GO IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtslog90]') AND type = N'U') DROP TABLE [dbo].[sysdtslog90] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtspackages90]') AND type = N'U') DROP TABLE [dbo].[sysdtspackages90] IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtspackagefolders90]') AND type = N'U') DROP TABLE [dbo].[sysdtspackagefolders90] GO -- -- Create a view for the logs table for backwards compatibility -- PRINT 'Creating sysdtslog90 view...' GO IF EXISTS (select * from sys.objects where object_id = object_id(N'[dbo].[sysdtslog90]') and type = N'V') BEGIN DROP VIEW [dbo].[sysdtslog90] END GO CREATE VIEW [dbo].[sysdtslog90] AS SELECT [id] ,[event] ,[computer] ,[operator] ,[source] ,[sourceid] ,[executionid] ,[starttime] ,[endtime] ,[datacode] ,[databytes] ,[message] FROM [msdb].[dbo].[sysssislog] GO ------------------------------------------------------------- /*********************************************************************/ /* Create auxilary procedure to enable OBD (Off By Default component */ /*********************************************************************/ IF (OBJECT_ID(N'tempdb..#sp_enable_component', 'P') IS NOT NULL) BEGIN DROP PROCEDURE [tempdb]..[#sp_enable_component] END GO CREATE PROCEDURE #sp_enable_component @comp_name sysname, @advopt_old_value INT OUT, @comp_old_value INT OUT AS BEGIN SELECT @advopt_old_value=cast(value_in_use as int) from sys.configurations where name = 'show advanced options'; SELECT @comp_old_value=cast(value_in_use as int) from sys.configurations where name = @comp_name; EXEC sp_configure 'show advanced options',1; RECONFIGURE WITH OVERRIDE; EXEC sp_configure @comp_name, 1; RECONFIGURE WITH OVERRIDE; END go IF (OBJECT_ID(N'tempdb..#sp_restore_component_state ', 'P') IS NOT NULL) BEGIN DROP PROCEDURE [tempdb]..[#sp_restore_component_state ] END GO CREATE PROCEDURE #sp_restore_component_state @comp_name sysname, @advopt_old_value INT, @comp_old_value INT AS BEGIN EXEC sp_configure @comp_name, @comp_old_value; RECONFIGURE WITH OVERRIDE; EXEC sp_configure 'show advanced options',@advopt_old_value; RECONFIGURE WITH OVERRIDE; END GO /**************************************************************/ /* DMF Post-upgrade steps */ /**************************************************************/ -- -- >>> CTP5 -> CTP6 Upgrade -- -- Restoring previously saved data and converting TargetSets to ObjectSets IF (OBJECT_ID('dbo.dmf_upgrade', 'U') IS NOT NULL) BEGIN BEGIN TRANSACTION -- Restore policies SET IDENTITY_INSERT dbo.syspolicy_policies_internal ON INSERT dbo.syspolicy_policies_internal (policy_id,name,condition_id,root_condition_id,date_created,execution_mode,policy_category_id,schedule_uid,description,help_text,help_link,is_enabled,job_id,created_by,modified_by,date_modified) SELECT policy_id,name,condition_id,root_condition_id,date_created,execution_mode,policy_category_id,schedule_uid,description,help_text,help_link,is_enabled,job_id,created_by,modified_by,date_modified FROM msdb.dbo.tmp_syspolicy_policies_internal SET IDENTITY_INSERT dbo.syspolicy_policies_internal OFF -- Restore Health state SET IDENTITY_INSERT dbo.syspolicy_system_health_state_internal ON INSERT dbo.syspolicy_system_health_state_internal (health_state_id,policy_id,last_run_date,target_query_expression_with_id,target_query_expression,result) SELECT * FROM msdb.dbo.tmp_syspolicy_system_health_state_internal SET IDENTITY_INSERT dbo.syspolicy_system_health_state_internal OFF -- Restore Execution history SET IDENTITY_INSERT dbo.syspolicy_policy_execution_history_internal ON INSERT dbo.syspolicy_policy_execution_history_internal (history_id,policy_id,start_date,end_date,result,is_full_run,exception_message,exception) SELECT * FROM msdb.dbo.tmp_syspolicy_policy_execution_history_internal SET IDENTITY_INSERT dbo.syspolicy_policy_execution_history_internal OFF SET IDENTITY_INSERT dbo.syspolicy_policy_execution_history_details_internal ON INSERT dbo.syspolicy_policy_execution_history_details_internal (detail_id,history_id,target_query_expression,target_query_expression_with_id,execution_date,result,result_detail,exception_message,exception) SELECT * FROM msdb.dbo.tmp_syspolicy_policy_execution_history_details_internal SET IDENTITY_INSERT dbo.syspolicy_policy_execution_history_details_internal OFF -- Convert TargetSets to ObjectSets SET IDENTITY_INSERT dbo.syspolicy_target_sets_internal ON DECLARE @policy_id int, @policy_name sysname, @facet nvarchar(max), @os_name sysname, @os_id int, @ts_id int DECLARE ts_cur CURSOR FOR SELECT p.policy_id, p.name, c.facet, ts.target_set_id FROM msdb.dbo.tmp_syspolicy_target_sets_internal ts JOIN dbo.syspolicy_policies p ON (ts.policy_id = p.policy_id) JOIN dbo.syspolicy_conditions c ON (p.condition_id = c.condition_id) ORDER BY p.policy_id, ts.target_set_id OPEN ts_cur FETCH ts_cur INTO @policy_id , @policy_name , @facet, @ts_id WHILE @@FETCH_STATUS = 0 BEGIN -- make sure ObjectSet name is unique SET @os_name = LEFT(@policy_name,118) + '_ObjectSet' IF EXISTS (SELECT * FROM dbo.syspolicy_object_sets WHERE object_set_name = @os_name) BEGIN SET @os_name = LEFT(@policy_name,82) + CAST(NEWID() AS nchar(36)) + '_ObjectSet' END -- if we go through multi-TS ObjectSet we only need to add it once -- cursor sort order guarantees @os_id remains correct IF 0 = (SELECT ISNULL(object_set_id, 0) FROM syspolicy_policies WHERE policy_id = @policy_id) BEGIN Exec sp_syspolicy_add_object_set @os_name, @facet, @os_id OUTPUT UPDATE syspolicy_policies_internal SET object_set_id = @os_id WHERE policy_id = @policy_id END INSERT syspolicy_target_sets_internal (target_set_id,object_set_id,type_skeleton,type,enabled) SELECT target_set_id,@os_id,type_skeleton,type,1 FROM msdb.dbo.tmp_syspolicy_target_sets_internal WHERE target_set_id = @ts_id FETCH ts_cur INTO @policy_id , @policy_name , @facet, @ts_id END CLOSE ts_cur DEALLOCATE ts_cur SET IDENTITY_INSERT dbo.syspolicy_target_sets_internal OFF SET IDENTITY_INSERT dbo.syspolicy_target_set_levels_internal ON INSERT syspolicy_target_set_levels_internal (target_set_level_id,target_set_id,type_skeleton,condition_id,level_name) SELECT target_set_level_id,target_set_id,type_skeleton,condition_id,level_name FROM msdb.dbo.tmp_syspolicy_target_set_levels_internal SET IDENTITY_INSERT dbo.syspolicy_target_set_levels_internal OFF -- Add missing levels for MultipartName sets DECLARE @tsl_id int SET @ts_id = 0 SET @tsl_id = 0 DECLARE @mpn_facet_id int SELECT @mpn_facet_id = management_facet_id FROM dbo.syspolicy_management_facets WHERE name = 'IMultipartNameFacet' DECLARE os_cur CURSOR FOR SELECT object_set_id FROM dbo.syspolicy_object_sets_internal WHERE facet_id = @mpn_facet_id OPEN os_cur FETCH os_cur INTO @os_id WHILE @@FETCH_STATUS = 0 BEGIN IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'PROCEDURE') BEGIN Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/StoredProcedure', @type='PROCEDURE', @enabled=0, @target_set_id = @ts_id OUTPUT Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/StoredProcedure', @condition_id=NULL, @level_name='StoredProcedure', @target_set_level_id=@tsl_id END IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'SYNONYM') BEGIN Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/Synonym', @type='SYNONYM', @enabled=0, @target_set_id = @ts_id OUTPUT Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/Synonym', @condition_id=NULL, @level_name='Synonym', @target_set_level_id=@tsl_id END IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'TABLE') BEGIN Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/Table', @type='TABLE', @enabled=0, @target_set_id = @ts_id OUTPUT Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/Table', @condition_id=NULL, @level_name='Table', @target_set_level_id=@tsl_id END IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'FUNCTION') BEGIN Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/UserDefinedFunction', @type='FUNCTION', @enabled=0, @target_set_id = @ts_id OUTPUT Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/UserDefinedFunction', @condition_id=NULL, @level_name='UserDefinedFunction', @target_set_level_id=@tsl_id END IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'TYPE') BEGIN Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/UserDefinedType', @type='TYPE', @enabled=0, @target_set_id = @ts_id OUTPUT Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/UserDefinedType', @condition_id=NULL, @level_name='UserDefinedType', @target_set_level_id=@tsl_id END IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'VIEW') BEGIN Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/View', @type='VIEW', @enabled=0, @target_set_id = @ts_id OUTPUT Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/View', @condition_id=NULL, @level_name='View', @target_set_level_id=@tsl_id END IF NOT EXISTS (SELECT * FROM syspolicy_target_sets_internal WHERE object_set_id = @os_id AND type = 'XMLSCHEMACOLLECTION') BEGIN Exec sp_syspolicy_add_target_set @object_set_id=@os_id, @type_skeleton='Server/Database/XmlSchemaCollection', @type='XMLSCHEMACOLLECTION', @enabled=0, @target_set_id = @ts_id OUTPUT Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database', @condition_id=NULL, @level_name='Database', @target_set_level_id=@tsl_id Exec sp_syspolicy_add_target_set_level @target_set_id = @ts_id, @type_skeleton='Server/Database/XmlSchemaCollection', @condition_id=NULL, @level_name='XmlSchemaCollection', @target_set_level_id=@tsl_id END FETCH os_cur INTO @os_id END CLOSE os_cur DEALLOCATE os_cur -- Clean up upgrade marker DROP TABLE dmf_upgrade -- And temp tables DROP TABLE msdb.dbo.tmp_syspolicy_target_sets_internal DROP TABLE msdb.dbo.tmp_syspolicy_target_set_levels_internal DROP TABLE msdb.dbo.tmp_syspolicy_policies_internal DROP TABLE msdb.dbo.tmp_syspolicy_system_health_state_internal DROP TABLE msdb.dbo.tmp_syspolicy_policy_execution_history_internal DROP TABLE msdb.dbo.tmp_syspolicy_policy_execution_history_details_internal COMMIT TRANSACTION PRINT 'DMF successfully upgraded' END -- -- <<< CTP5 -> CTP6 Upgrade -- -- Make sure Agent XPs are enabled regardless of the state of the server -- We will restove the state afterwards DECLARE @advopt_old_value int DECLARE @comp_old_value int EXECUTE #sp_enable_component 'Agent XPs', @advopt_old_value out, @comp_old_value out -- sproc that creates a job for PBM. This covers the creation of job for all upgrade scenarios. PRINT 'Executing msdb.dbo.sp_syspolicy_create_purge_job' EXEC msdb.dbo.sp_syspolicy_create_purge_job -- Now restore the Agent XPs to their old state EXECUTE #sp_restore_component_state 'Agent XPs', @advopt_old_value, @comp_old_value -- Check if temp table that holds data collector's old status before upgrade exists -- and enable data collector if DC was enabled before running upgrade scripts PRINT 'Checking if Data collector was enabled before upgrade...' IF (OBJECT_ID('tempdb..#data_collector_status', 'U') IS NOT NULL) BEGIN DECLARE @collector_enabled int; SELECT @collector_enabled = data_collector_old_status FROM #data_collector_status IF (@collector_enabled <> 0) BEGIN PRINT 'Data collector was enabled before upgrade, Enabling Data Collector ...' EXEC sp_syscollector_enable_collector END END GO -- Final upgrade step does not depend on any schema changes, and thus can be run for any upgrade -- Make sure that the policies are using a valid evaluation mode -- If a policy is using an evaluation mode that is not supported for a facet, then the evaluation mode is None and it is not enabled UPDATE p SET p.execution_mode = 0, p.is_enabled = 0 FROM [msdb].[dbo].[syspolicy_policies_internal] p INNER JOIN [msdb].[dbo].[syspolicy_conditions] c ON (p.condition_id = c.condition_id) INNER JOIN [msdb].[dbo].[syspolicy_management_facets] f ON (c.facet = f.name) WHERE p.execution_mode <> (p.execution_mode & f.execution_mode) -- Upgrade from CTP5 & CTP6 to RC0 - correct typo in the management facet name UPDATE [msdb].[dbo].[syspolicy_management_facets] SET name = 'MessageType' WHERE name = 'Messagetype' GO /**************************************************************/ /* End of DMF Post-upgrade steps */ /**************************************************************/ IF (NOT OBJECT_ID(N'tempdb.dbo.#SqlAgentSignatures', 'U') IS NULL) BEGIN DROP TABLE #SqlAgentSignatures END ------------------------------------------------------------------------- -- /**************************************************************/ /* Data Collector Post-upgrade steps */ /**************************************************************/ DECLARE @advopt_old_value int DECLARE @comp_old_value int EXECUTE #sp_enable_component 'Agent XPs', @advopt_old_value out, @comp_old_value out -- Load the instmdw.sql script into the blob EXECUTE [dbo].[sp_syscollector_upload_instmdw] -- Now restore the Agent XPs to their old state EXECUTE #sp_restore_component_state 'Agent XPs', @advopt_old_value, @comp_old_value GO /**************************************************************/ /* End of Data Collector Post-upgrade steps */ /**************************************************************/ DROP PROCEDURE #sp_enable_component DROP PROCEDURE #sp_restore_component_state GO /**************************************************************/ /* Utility Post-upgrade steps */ /**************************************************************/ ----------------------------------------------- -- SQL 2008 R2 CTP2 --> CTP3 or later ----------------------------------------------- -- Drop the CTP2 MI job "sysutility_performance_information_collection" if it exists. -- This job was renamed in CTP3. IF EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = 'sysutility_performance_information_collection') BEGIN EXEC msdb.dbo.sp_delete_job @job_name = 'sysutility_performance_information_collection'; END; GO /**************************************************************/ /* End of Utility Post-upgrade steps */ /**************************************************************/ GO /**************************************************************/ /* DAC Post-upgrade steps */ /**************************************************************/ ----------------------------------------------- -- SQL 2008 R2 CTP3 --> RC0 ----------------------------------------------- DECLARE @dac_constraint_name SYSNAME DECLARE @sql nvarchar(1000) --Find the Default constraint system name on the DAC tables for created_by column. If it's suser_sname() (CTP3), --drop this constraint and add the new default pointing to the new function 'fn_sysdac_get_currentusername' --sysdac_instances_internal SELECT @dac_constraint_name=dc.name FROM sys.default_constraints dc JOIN sys.columns c ON dc.parent_object_id = c.object_id AND dc.parent_column_id = c.column_id WHERE dc.parent_object_id = object_id('[dbo].[sysdac_instances_internal]', 'U') AND dc.definition ='(suser_sname())' AND c.name='created_by' IF @dac_constraint_name IS NOT NULL BEGIN SELECT @sql = 'ALTER TABLE [dbo].[sysdac_instances_internal] DROP CONSTRAINT ' + QUOTENAME(@dac_constraint_name) EXEC (@sql) ALTER TABLE [dbo].[sysdac_instances_internal] ADD DEFAULT dbo.fn_sysdac_get_currentusername() FOR created_by END --sysdac_history_internal SELECT @dac_constraint_name=dc.name FROM sys.default_constraints dc JOIN sys.columns c ON dc.parent_object_id = c.object_id AND dc.parent_column_id = c.column_id WHERE dc.parent_object_id = object_id('[dbo].[sysdac_history_internal]', 'U') AND dc.definition ='(suser_sname())' AND c.name='created_by' IF @dac_constraint_name IS NOT NULL BEGIN SELECT @sql = 'ALTER TABLE [dbo].[sysdac_history_internal] DROP CONSTRAINT ' + QUOTENAME(@dac_constraint_name) EXEC (@sql) ALTER TABLE [dbo].[sysdac_history_internal] ADD DEFAULT dbo.fn_sysdac_get_currentusername() FOR created_by END /**************************************************************/ /* End of DAC Post-upgrade steps */ /**************************************************************/ GO PRINT '' PRINT '-----------------------------------------' PRINT 'Execution of POSTINSTMSDB100.SQL complete' PRINT '-----------------------------------------' go Print 'Upgrading Database Mail related objects...' /* One of the main functions of this script is to handle migration of multiple Mail Host database to a single Mail Host Database in MSDB. Pre Beta 3 (CPT 15) versions of SQL Server 2005 allowed Databae Mail object to exist in any database. This script will migrate previously created profile, SentItems and log information to MSDB. Finally the script will remove Database Mail objects in these other databases. Another goal of this script is to handle all metadata changes between different releases of the product starting with B-2 through subsequent CTPs */ USE msdb SET NOCOUNT ON -- remove obsolete configuration parameter IF EXISTS(SELECT * FROM msdb.dbo.sysmail_configuration WHERE paramname = N'MaxNumberOfMailsPerDay') DELETE FROM msdb.dbo.sysmail_configuration WHERE paramname=N'MaxNumberOfMailsPerDay' /* Upgrade objects in MSDB if it was previously used as mailhost database */ BEGIN TRY IF EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='profile_name' and id = (SELECT OBJECT_ID(N'dbo.sysmail_mailitems', 'U'))) BEGIN -- convert data from profile_name column to profile_id column exec sp_executesql N' DECLARE @profile_name sysname DECLARE @profile_id int DECLARE profile_name_cursor CURSOR LOCAL FOR SELECT sp.profile_id, sp.name FROM dbo.sysmail_profile sp, dbo.sysmail_mailitems mi WHERE sp.name = mi.profile_name AND mi.profile_name IS NOT NULL OPEN profile_name_cursor FETCH NEXT FROM profile_name_cursor INTO @profile_id, @profile_name WHILE (@@fetch_status = 0) BEGIN UPDATE dbo.sysmail_mailitems SET profile_id = @profile_id WHERE profile_name = @profile_name FETCH NEXT FROM profile_name_cursor INTO @profile_id, @profile_name END Close profile_name_cursor DEALLOCATE profile_name_cursor' -- remove obsolete column ALTER TABLE dbo.sysmail_mailitems DROP COLUMN profile_name END END TRY BEGIN CATCH print 'There was a problem upgrading MSDB mail host database.' print 'Unable to map existing profile name values to profile_id column' print 'Error State: ' + convert(varchar(10),ERROR_STATE() ) print 'Error Message: ' + ERROR_MESSAGE() print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY()) END CATCH GO if EXISTS(Select * from msdb.dbo.sysmail_principalprofile) BEGIN BEGIN TRY -- add existing mail users defined in MSDB to new role DECLARE @DBMailUser sysname DECLARE principal_profile_cursor CURSOR LOCAL FOR SELECT dp.name FROM dbo.sysmail_principalprofile pp INNER JOIN sys.database_principals dp ON pp.principal_sid = dp.sid and dp.principal_id > 4 OPEN principal_profile_cursor FETCH NEXT FROM principal_profile_cursor INTO @DBMailUser WHILE (@@fetch_status = 0) BEGIN EXEC msdb..sp_addrolemember @rolename = 'DatabaseMailUserRole',@membername = @DBMailUser FETCH NEXT FROM principal_profile_cursor INTO @DBMailUser END CLOSE principal_profile_cursor DEALLOCATE principal_profile_cursor END TRY BEGIN CATCH print 'There was a problem upgrading MSDB mail host database.' print 'Unable to add existing mail user to DatabaseMailUserRole' print 'Error State: ' + convert(varchar(10),ERROR_STATE() ) print 'Error Message: ' + ERROR_MESSAGE() print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY()) END CATCH END GO if EXISTS(Select * from msdb.dbo.sysmail_principalprofile) BEGIN BEGIN TRY -- cleaning up database mail related broker conversations if( SELECT is_broker_enabled from msdb.sys.databases WHERE name = 'msdb' ) = 0 BEGIN PRINT 'NEED TO RE-ENABLE SSB' WHILE(1=1) BEGIN DECLARE @handle UNIQUEIDENTIFIER SET @handle = NULL SELECT TOP(1) @handle=conversation_handle FROM msdb.sys.conversation_endpoints WHERE (msdb.sys.conversation_endpoints.far_service IN ('SQL/Notifications/SysMailNotification/v1.0', 'ExternalMailService', 'InternalMailService', 'SqlQueryNotificationService', 'iMailRequestorService', 'iMailResponderService', 'http://schemas.microsoft.com/SQL/Notifications/EventNotificationService', 'http://schemas.microsoft.com/SQL/Notifications/PostEventNotification' ) ) IF @handle IS NULL BREAK PRINT 'ENDING CONVERSATION ' + convert(varchar(256),@handle) END CONVERSATION @handle WITH CLEANUP END END -- We cannot turn the broker on, because we don't know whether it was disabled by the user -- ALTER DATABASE msdb SET ENABLE_BROKER END TRY BEGIN CATCH print 'There was a problem upgrading Mail Host databases.' print 'Unable to re-enable server broker. You might need to manually' print 'end any pending secure conversations.' print 'Error State: ' + convert(varchar(10),ERROR_STATE() ) print 'Error Message: ' + ERROR_MESSAGE() print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY()) END CATCH END GO if EXISTS(SELECT * FROM msdb.dbo.sysmail_principalprofile) AND EXISTS(SELECT * FROM msdb.dbo.syscolumns WHERE name='database_id' and id =(SELECT OBJECT_ID(N'dbo.sysmail_principalprofile', 'U'))) BEGIN /* Handle migration of multiple mail host databases into MSDB */ BEGIN TRY DECLARE @DBName sysname DECLARE @DBNameQuote sysname DECLARE @sql nvarchar(max) DECLARE @new_mailitem_id int DECLARE @old_mailitem_id int DECLARE @profile_name_exists bit DECLARE @Error bit DECLARE @db_id int DECLARE DBName_Cursor CURSOR FOR select name from sys.databases WHERE name NOT IN ('msdb', 'tempdb', 'master') and HAS_DBACCESS([name]) <> 0 OPEN DBName_Cursor FETCH NEXT FROM DBName_Cursor INTO @DBName WHILE @@FETCH_STATUS = 0 BEGIN Print 'Checking database: ' + @DBName SET @DBNameQuote = QUOTENAME(@DBName) SELECT @db_id = db_id(@DBName) IF ( OBJECT_ID(@DBNameQuote + '.dbo.sysmail_log') IS NOT NULL) --Determine if Database Mail objects exist BEGIN Print @DBName + ' is a MailHost database.' --Going to migrate profiles from database to MSDB DECLARE @DBMailUser sysname DECLARE @sid_MSDB varbinary(85) DECLARE @sid_principal varbinary(85) DECLARE @old_profile_id int DECLARE @new_principal_id int DECLARE @old_principal_id int DECLARE @LoginName sysname SET @sql = N'DECLARE DBMail_Cursor CURSOR FOR SELECT p.name , pp.profile_id , msdb_p.sid , p.sid , pp.principal_id FROM msdb..sysmail_principalprofile pp JOIN '+ @DBNameQuote +'.sys.database_principals p ON pp.principal_id = p.principal_id LEFT JOIN msdb.sys.database_principals msdb_p ON p.sid = msdb_p.sid where pp.database_id = ' + convert(nvarchar(10),@db_id) --print @sql EXEC(@sql) OPEN DBMail_Cursor FETCH NEXT FROM DBMail_Cursor INTO @DBMailUser, @old_profile_id, @sid_MSDB, @sid_principal, @old_principal_id WHILE @@FETCH_STATUS = 0 BEGIN Print 'Going to process user: ' + @DBMailUser IF (@sid_MSDB IS NULL) -- does not already exist in MSDB BEGIN IF (NOT EXISTS(Select * from sysusers where name = @DBMailUser)) BEGIN IF (EXISTS(Select * from master.dbo.syslogins where sid = @sid_principal)) BEGIN /* Determine the Login Name from the SID */ SELECT @LoginName = name FROM master.dbo.syslogins WHERE sid = @sid_principal PRINT 'Add USER to MSDB: ' + @DBMailUser SET @sql = N'CREATE USER ' + QUOTENAME(@DBMailUser) + ' FOR LOGIN ' + QUOTENAME(@LoginName) EXEC (@sql) IF (@old_principal_id > 4) -- do not add special accounts such as dbo, public, sys BEGIN Print 'Grant USER permission to send mail.' exec msdb..sp_addrolemember @rolename = 'DatabaseMailUserRole',@membername = @DBMailUser END END ELSE BEGIN PRINT 'Can not add user as the login does not exist.' END END ELSE BEGIN Print 'User has already been added to MSDB: ' + @DBMailUser IF (@old_principal_id > 4) -- do not add special accounts such as dbo, public, sys BEGIN PRINT 'Ensure user has the right to send mail.' exec msdb..sp_addrolemember @rolename = 'DatabaseMailUserRole',@membername = @DBMailUser END END END ELSE BEGIN Print 'Login already mapped to a user in MSDB' END IF (EXISTS(Select * from master.dbo.syslogins where sid = @sid_principal)) BEGIN --Get principle_id SELECT @new_principal_id = principal_id FROM msdb.sys.database_principals Where sid = @sid_principal IF (@new_principal_id is not Null) BEGIN print 'New Principal_id: ' + Convert(varchar(10),@new_principal_id) + ' Old profile_id: ' + convert(varchar(10),@old_profile_id) + ' Old principal id: ' + convert(varchar(10),@old_principal_id) SET @sql = N' IF NOT EXISTS(select * from msdb..sysmail_principalprofile Where profile_id = ' + Convert(varchar(10),@old_profile_id) + ' AND database_id = 4 AND principal_id = ' + Convert(varchar(10),@new_principal_id) + ') BEGIN --Update the Profile UPDATE msdb..sysmail_principalprofile SET database_id = 4 , principal_id = ' + Convert(varchar(10),@new_principal_id) + ' Where profile_id = ' + Convert(varchar(10),@old_profile_id) + ' AND database_id = ' + Convert(varchar(10),@db_id) + ' AND principal_id = ' + Convert(varchar(10),@old_principal_id) + ' IF (@@rowcount = 1) BEGIN Print ''sysmail_principalprofile updated based on moving user to MSDB.'' END END ELSE BEGIN Print ''This user already has already been configured with this profile in MSDB.'' END' EXEC(@sql) END ELSE BEGIN Print 'sysmail_principalprofile table can not be updated for sid: ' + convert(varchar(100),@sid_principal) END END FETCH NEXT FROM DBMail_Cursor INTO @DBMailUser, @old_profile_id, @sid_MSDB, @sid_principal, @old_principal_id END CLOSE DBMail_Cursor DEALLOCATE DBMail_Cursor --/* Move Data from user Mail Host database to MSDB */ --Print 'Move Data from user Mail Host database to MSDB.' SET @sql = N'DECLARE mailitem_id_Cursor CURSOR FOR SELECT mailitem_id FROM ' + @DBNameQuote + '.dbo.sysmail_mailitems' EXEC(@sql) OPEN mailitem_id_Cursor Set @Error = 0 --Assume no errors BEGIN TRANSACTION /* Disable Trigger so the "last_mod_date" and "last_mod_user colums" are not updated during transfer. */ EXEC ('DISABLE TRIGGER [dbo].[trig_sysmail_attachments] ON [dbo].[sysmail_attachments]') EXEC ('DISABLE TRIGGER [dbo].[trig_sysmail_log] ON [dbo].[sysmail_log]') EXEC ('DISABLE TRIGGER [dbo].[trig_sysmail_mailitems]ON [dbo].[sysmail_mailitems]') Print 'Going to move ALL sysmail_log items not associated with a mailitem' SET @sql = N'INSERT INTO msdb.dbo.sysmail_log (event_type, log_date, description, process_id, mailitem_id, account_id, last_mod_date, last_mod_user) SELECT event_type, log_date, description, process_id, NULL, account_id, last_mod_date, last_mod_user FROM '+ @DBNameQuote +'.dbo.sysmail_log WHERE mailitem_id IS NULL ' exec(@sql) FETCH NEXT FROM mailitem_id_Cursor INTO @old_mailitem_id WHILE @@FETCH_STATUS = 0 BEGIN /****************************************/ /* MOVE dbo.sysmail_mailitems DATA */ /****************************************/ /* Need to check the schema defination of the table */ SET @sql = N'USE ' + @DBNameQuote + ' DECLARE @sql nvarchar(max) IF (EXISTS(select * from syscolumns where id = object_id(''[dbo].[sysmail_mailitems]'') AND name = ''profile_name'')) BEGIN SET @sql = N''INSERT INTO msdb.dbo.sysmail_mailitems (profile_id, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_date, send_request_user, sent_account_id, sent_status, sent_date, last_mod_date, last_mod_user) SELECT CASE WHEN p.profile_id IS NULL THEN -1 ELSE p.profile_id END, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_date, send_request_user, sent_account_id, sent_status, sent_date, last_mod_date, mi.last_mod_user FROM '+ @DBNameQuote +'.dbo.sysmail_mailitems mi LEFT JOIN msdb..sysmail_profile p ON mi.profile_name = p.name WHERE mailitem_id = ' + CONVERT(VARCHAR(20),@old_mailitem_id) + ''' END ELSE BEGIN SET @sql = N''INSERT INTO msdb.dbo.sysmail_mailitems (profile_id, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_date, send_request_user, sent_account_id, sent_status, sent_date, last_mod_date, last_mod_user) SELECT profile_id, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_date, send_request_user, sent_account_id, sent_status, sent_date, last_mod_date, last_mod_user FROM '+ @DBNameQuote +'.dbo.sysmail_mailitems WHERE mailitem_id = ' + CONVERT(VARCHAR(20),@old_mailitem_id) + ''' END EXEC(@sql)' EXEC(@sql) IF (@@error <> 0) --Check for error BEGIN Set @Error = 1 END SELECT @new_mailitem_id = @@identity /****************************************/ /* MOVE dbo.sysmail_log DATA */ /****************************************/ SET @sql = N'INSERT INTO msdb.dbo.sysmail_log (event_type, log_date, description, process_id, mailitem_id, account_id, last_mod_date, last_mod_user) SELECT event_type, log_date, description, process_id, ' + convert(varchar(5),@new_mailitem_id) + ', account_id, last_mod_date, last_mod_user FROM '+ @DBNameQuote +'.dbo.sysmail_log WHERE mailitem_id = ' + CONVERT(VARCHAR(20),@old_mailitem_id) EXEC(@sql) IF @@error <> 0 --Check for error BEGIN Set @Error = 1 END /****************************************/ /* MOVE dbo.sysmail_attachments DATA */ /****************************************/ SET @sql = N'USE ' + @DBNameQuote + ' IF (object_id(''dbo.sysmail_attachments'') IS NOT NULL) begin INSERT INTO msdb.dbo.sysmail_attachments (mailitem_id, filename, filesize, attachment, last_mod_date, last_mod_user) SELECT ' + convert(varchar(5),@new_mailitem_id) + ', filename, filesize, attachment, last_mod_date, last_mod_user FROM '+ @DBNameQuote +'.dbo.sysmail_attachments WHERE mailitem_id = ' + CONVERT(VARCHAR(20),@old_mailitem_id) + ' end' EXEC(@sql) IF @@error <> 0 --Check for error BEGIN Set @Error = 1 END FETCH NEXT FROM mailitem_id_Cursor INTO @old_mailitem_id END IF @Error = 0 BEGIN Print 'Completed data transfer to MSDB.' COMMIT TRANSACTION END ELSE BEGIN Print 'Not able to complete data transfer to MSDB.' ROLLBACK TRANSACTION END /* ENABLE Triggers as they were previously DISABLE */ EXEC ('ENABLE TRIGGER [dbo].[trig_sysmail_attachments] ON [dbo].[sysmail_attachments]') EXEC ('ENABLE TRIGGER [dbo].[trig_sysmail_log] ON [dbo].[sysmail_log]') EXEC ('ENABLE TRIGGER [dbo].[trig_sysmail_mailitems]ON [dbo].[sysmail_mailitems]') CLOSE mailitem_id_Cursor DEALLOCATE mailitem_id_Cursor IF @Error = 0 BEGIN /**********************************************************************/ /* */ /* Uninstalls the tables, triggers and stored procedures necessary for*/ /* sqlimail operations */ /* */ /* ** Copyright Microsoft, Inc. 2004 ** All Rights Reserved. */ /**********************************************************************/ /**************************************************************/ -- Drop ALL Database Mail objects (i.e Functions/Procedures ) /**************************************************************/ PRINT '' PRINT 'Dropping old Database Mail FUNCTIONS and PROCEDURES ...' PRINT '' ----- PRINT 'Dropping function ConvertToInt' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.ConvertToInt'', ''FN'') IS NULL DROP FUNCTION ConvertToInt') ----- PRINT 'Dropping procedure sysmail_start_sp' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_start_sp'', ''P'') IS NULL DROP PROCEDURE dbo.sysmail_start_sp') ----- PRINT 'Dropping procedure sysmail_stop_sp' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_stop_sp'', ''P'') IS NULL DROP PROCEDURE dbo.sysmail_stop_sp') ----- PRINT 'Dropping procedure sysmail_logmailevent_sp' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_logmailevent_sp'', ''P'') IS NULL DROP PROCEDURE dbo.sysmail_logmailevent_sp') ----- PRINT 'Dropping procedure sp_has_changedbuser_permission' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_has_changedbuser_permission'', ''P'') IS NULL DROP PROCEDURE dbo.sp_has_changedbuser_permission') ----- PRINT 'Dropping procedure sp_getprofilerequestxml' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_getprofilerequestxml'', ''P'') IS NULL DROP PROCEDURE dbo.sp_getprofilerequestxml') ----- PRINT 'Dropping procedure sp_sendandreturn' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendandreturn'', ''P'') IS NULL DROP PROCEDURE dbo.sp_sendandreturn') ----- PRINT 'Dropping procedure sp_sendandblock' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendandblock'', ''P'') IS NULL DROP PROCEDURE dbo.sp_sendandblock') ----- PRINT 'Dropping procedure sp_isprohibited' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_isprohibited'', ''P'') IS NULL DROP PROCEDURE dbo.sp_isprohibited') ----- PRINT 'Dropping procedure sp_sendimailqueues' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendimailqueues'', ''P'') IS NULL DROP PROCEDURE dbo.sp_sendimailqueues') ----- PRINT 'Dropping procedure sp_gettestprofilexml' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_gettestprofilexml'', ''P'') IS NULL DROP PROCEDURE dbo.sp_gettestprofilexml') ----- PRINT 'Dropping procedure sp_testprofileimailqueues' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_testprofileimailqueues'', ''P'') IS NULL DROP PROCEDURE dbo.sp_testprofileimailqueues') ----- PRINT 'Dropping procedure sp_endconversation' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_endconversation'', ''P'') IS NULL DROP PROCEDURE dbo.sp_endconversation') ----- PRINT 'Dropping procedure sp_endconversation' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_endconversation'', ''P'') IS NULL DROP PROCEDURE dbo.sp_endconversation') ----- PRINT 'Dropping procedure sp_readrequest' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_readrequest'', ''P'') IS NULL DROP PROCEDURE dbo.sp_readrequest') ----- PRINT 'Dropping procedure sp_sendresponse' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendresponse'', ''P'') IS NULL DROP PROCEDURE dbo.sp_sendresponse') ----- PRINT 'Dropping procedure sp_sendtestprofileresponse' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendtestprofileresponse'', ''P'') IS NULL DROP PROCEDURE dbo.sp_sendtestprofileresponse') ----- PRINT 'Dropping procedure sp_getsendmailxml' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_getsendmailxml'', ''P'') IS NULL DROP PROCEDURE dbo.sp_getsendmailxml') ----- PRINT 'Dropping procedure sendimail_sp' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sendimail_sp'', ''P'') IS NULL DROP PROCEDURE dbo.sendimail_sp') ----- PRINT 'Dropping procedure sp_testimailprofile' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_testimailprofile'', ''P'') IS NULL DROP PROCEDURE dbo.sp_testimailprofile') ----- PRINT 'Dropping procedure dbo.sp_add_quota_information' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_add_quota_information'', ''P'') IS NULL DROP PROCEDURE dbo.sp_add_quota_information') ----- PRINT 'Dropping procedure dbo.sp_current_principal_mails' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_current_principal_mails'', ''P'') IS NULL DROP PROCEDURE dbo.sp_current_principal_mails') ----- PRINT 'Dropping procedure dbo.sp_delete_quota_information' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_delete_quota_information'', ''P'') IS NULL DROP PROCEDURE dbo.sp_delete_quota_information') ----- PRINT 'Dropping procedure dbo.sp_ExernalMailQueueListener' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_ExernalMailQueueListener'', ''P'') IS NULL DROP PROCEDURE dbo.sp_ExernalMailQueueListener') ----- PRINT 'Dropping procedure dbo.sp_GetAttachmentData' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_GetAttachmentData'', ''P'') IS NULL DROP PROCEDURE dbo.sp_GetAttachmentData') ----- PRINT 'Dropping procedure dbo.sp_RunMailQuery' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_RunMailQuery'', ''P'') IS NULL DROP PROCEDURE dbo.sp_RunMailQuery') ----- PRINT 'Dropping procedure dbo.sp_SendMailMessage' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_SendMailMessage'', ''P'') IS NULL DROP PROCEDURE dbo.sp_SendMailMessage') ----- PRINT 'Dropping procedure dbo.sp_SendMailQueues' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_SendMailQueues'', ''P'') IS NULL DROP PROCEDURE dbo.sp_SendMailQueues') ----- PRINT 'Dropping procedure dbo.sp_verify_quota_mail_count' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_verify_quota_mail_count'', ''P'') IS NULL DROP PROCEDURE dbo.sp_verify_quota_mail_count') ----- PRINT 'Dropping procedure dbo.sp_activate_sqlimail' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_activate_sqlimail'', ''P'') IS NULL DROP PROCEDURE dbo.sp_activate_sqlimail') /**************************************************************/ -- Drop all Database Mail TABLES /**************************************************************/ PRINT '' PRINT 'Dropping TABLES...' PRINT '' ----- PRINT 'Dropping table sysmail_log' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_log'', ''U'') IS NULL DROP TABLE sysmail_log') ----- PRINT 'Dropping table sysmail_send_retries' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_send_retries'', ''U'') IS NULL DROP TABLE dbo.sysmail_send_retries') ----- PRINT 'Dropping table sqlimail_data_transfer' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sqlimail_data_transfer'', ''U'') IS NULL DROP TABLE sqlimail_data_transfer') ----- PRINT 'Dropping table sysmail_attachments' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_attachments'', ''U'') IS NULL DROP TABLE sysmail_attachments') ----- PRINT 'Dropping table sysmail_query_transfer' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_query_transfer'', ''U'') IS NULL DROP TABLE sysmail_query_transfer') ----- PRINT 'Dropping table sysmail_attachments_transfer' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_attachments_transfer'', ''U'') IS NULL DROP TABLE sysmail_attachments_transfer') ----- PRINT 'Dropping table sysmail_quota_information' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_quota_information'', ''U'') IS NULL DROP TABLE sysmail_quota_information') ----- PRINT 'Dropping table sysmail_mailitems' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_mailitems'', ''U'') IS NULL DROP TABLE sysmail_mailitems') /**************************************************************/ -- Drop MESSAGES, CONTRACTS, QUEUES AND SERVICES /**************************************************************/ PRINT '' PRINT 'Dropping MESSAGES, CONTRACTS, QUEUES AND SERVICES...' PRINT '' ----- PRINT 'Dropping service iMailRequestorService' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''iMailRequestorService'') DROP SERVICE iMailRequestorService') ----- PRINT 'Dropping service iMailResponderService' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''iMailResponderService'') DROP SERVICE iMailResponderService') ----- PRINT 'Dropping queue iMailRequestor' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.objects WHERE name = ''iMailRequestor'' AND type = ''SQ'') DROP QUEUE iMailRequestor') ----- PRINT 'Dropping queue iMailResponder' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.objects WHERE name = ''iMailResponder'' AND type = ''SQ'') DROP QUEUE iMailResponder') ----- PRINT 'Dropping service [SQL/Notifications/IMailNotification/v1.0]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''SQL/Notifications/IMailNotification/v1.0'') DROP SERVICE [SQL/Notifications/IMailNotification/v1.0]') ----- PRINT 'Dropping service [ExternalMailService]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''ExternalMailService'') DROP SERVICE [ExternalMailService]') ----- PRINT 'Dropping service [InternalMailService]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''InternalMailService'') DROP SERVICE [InternalMailService]') ----- PRINT 'Dropping queue iMailNotificationQueue' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.objects WHERE name = ''iMailNotificationQueue'' AND type = ''SQ'') DROP QUEUE iMailNotificationQueue') ----- PRINT 'Dropping contract [//www.microsoft.com/imail/contracts/SendMail/v1.0]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_contracts WHERE name = ''//www.microsoft.com/imail/contracts/SendMail/v1.0'') DROP CONTRACT [//www.microsoft.com/imail/contracts/SendMail/v1.0]') ----- PRINT 'Dropping message type [{//www.microsoft.com/imail/messages}SendMail]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_message_types WHERE name = ''{//www.microsoft.com/imail/messages}SendMail'') DROP MESSAGE TYPE [{//www.microsoft.com/imail/messages}SendMail]') ----- PRINT 'Dropping contract [//www.microsoft.com/imail/contracts/TestProfile/v1.0]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_contracts WHERE name = ''//www.microsoft.com/imail/contracts/TestProfile/v1.0'') DROP CONTRACT [//www.microsoft.com/imail/contracts/TestProfile/v1.0]') ----- PRINT 'Dropping message type [{//www.microsoft.com/imail/messages}TestProfile]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_message_types WHERE name = ''{//www.microsoft.com/imail/messages}TestProfile'') DROP MESSAGE TYPE [{//www.microsoft.com/imail/messages}TestProfile]') ----- PRINT 'Dropping message type [{//www.microsoft.com/imail/messages}TestProfileResponse]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_message_types WHERE name = ''{//www.microsoft.com/imail/messages}TestProfileResponse'') DROP MESSAGE TYPE [{//www.microsoft.com/imail/messages}TestProfileResponse]') ----- PRINT 'Dropping certificates and related users' ----- DECLARE @sqlcmd nvarchar(max), @CertName sysname, @CertNameQuoted sysname, @MailLogin sysname, @MailLoginQuoted sysname SELECT @CertName = N'SQLiMail-Certificate-' + @DBName, @CertNameQuoted = QUOTENAME( @CertName,''''), @MailLogin = N'SQLiMail-' + @DBName + '-Certificate-Login', @MailLoginQuoted= QUOTENAME( @MailLogin,'''') ----- PRINT 'Dropping user in msdb' ----- SET @sqlcmd = N'USE msdb IF(EXISTS (SELECT * FROM sys.database_principals WHERE name = N' + @MailLoginQuoted + ')) DROP USER ' + QUOTENAME( @MailLogin) EXEC sp_executesql @sqlcmd ----- PRINT 'Dropping user and certificate login in master' ----- SET @sqlcmd = N'USE master IF(EXISTS (SELECT * FROM sys.database_principals WHERE name = N' + @MailLoginQuoted + ')) DROP USER ' + QUOTENAME( @MailLogin) + ' IF(EXISTS(select * from sys.server_principals where name = N' + @MailLoginQuoted + ')) DROP LOGIN ' + QUOTENAME( @MailLogin) EXEC sp_executesql @sqlcmd ----- PRINT 'Dropping user in this database' ----- SET @sqlcmd = N'USE ' + @DBNameQuote + ' IF(EXISTS (SELECT * FROM sys.database_principals WHERE name = N' + @MailLoginQuoted + ')) DROP USER ' + QUOTENAME( @MailLogin) EXEC sp_executesql @sqlcmd ----- PRINT 'Dropping the certificate in this db' ----- SET @sqlcmd = N'USE ' + @DBNameQuote + ' IF(EXISTS(SELECT * FROM sys.certificates WHERE name = N' + @CertNameQuoted + ')) BEGIN DROP CERTIFICATE ' + QUOTENAME(@CertName) + ' END' EXEC sp_executesql @sqlcmd ----- PRINT 'Dropping certificate from master' ----- SET @sqlcmd = N'USE master IF(EXISTS(SELECT * FROM sys.certificates WHERE name = N' + @CertNameQuoted + ')) DROP CERTIFICATE ' + QUOTENAME(@CertName) EXEC sp_executesql @sqlcmd END END FETCH NEXT FROM DBName_Cursor INTO @DBName END -- Loop through all databases. CLOSE DBName_Cursor DEALLOCATE DBName_Cursor -- deleting all principal associations that are not in MSDB or public exec sp_executesql N' delete from msdb.dbo.sysmail_principalprofile where database_id <> 4 and database_id <> 0' END TRY BEGIN CATCH print 'There was a problem upgrading Mail Host databases.' print 'Error State: ' + convert(varchar(10),ERROR_STATE() ) print 'Error Message: ' + ERROR_MESSAGE() print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY()) END CATCH END BEGIN TRY IF EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='principal_id' and id = (SELECT OBJECT_ID(N'dbo.sysmail_principalprofile', 'U'))) BEGIN -- convert data from principal_id to principal_sid exec sp_executesql N' DECLARE @principal_sid varbinary(85) DECLARE @principal_id int DECLARE principal_sid_cursor CURSOR LOCAL FOR SELECT distinct principal_id FROM dbo.sysmail_principalprofile OPEN principal_sid_cursor FETCH NEXT FROM principal_sid_cursor INTO @principal_id WHILE (@@fetch_status = 0) BEGIN IF @principal_id = 0 SET @principal_sid = 0x00 ELSE SELECT @principal_sid = dbo.get_principal_sid(@principal_id) IF @principal_sid IS NOT NULL -- principal_id is valid BEGIN UPDATE dbo.sysmail_principalprofile SET principal_sid = @principal_sid WHERE principal_id = @principal_id END ELSE BEGIN DELETE FROM dbo.sysmail_principalprofile WHERE principal_id = @principal_id END FETCH NEXT FROM principal_sid_cursor INTO @principal_id END CLOSE principal_sid_cursor DEALLOCATE principal_sid_cursor' -- safety clean-up DELETE FROM dbo.sysmail_principalprofile WHERE principal_sid = 0xFFFF -- remove obsolete column ALTER TABLE dbo.sysmail_principalprofile DROP CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique] ALTER TABLE dbo.sysmail_principalprofile DROP COLUMN principal_id ALTER TABLE dbo.sysmail_principalprofile ADD CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique] PRIMARY KEY CLUSTERED ([profile_id] ASC,[principal_sid] ASC) END END TRY BEGIN CATCH print 'There was a problem upgrading MSDB mail host database.' print 'Unable to map existing profile id values to profile_sid column' print 'Error State: ' + convert(varchar(10),ERROR_STATE() ) print 'Error Message: ' + ERROR_MESSAGE() print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY()) END CATCH GO BEGIN TRY -- remove database_id column IF EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='database_id' and id = (SELECT OBJECT_ID(N'dbo.sysmail_principalprofile', 'U'))) BEGIN ALTER TABLE dbo.sysmail_principalprofile DROP CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique] ALTER TABLE dbo.sysmail_principalprofile DROP COLUMN database_id ALTER TABLE dbo.sysmail_principalprofile ADD CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique] PRIMARY KEY CLUSTERED ([profile_id] ASC,[principal_sid] ASC) END END TRY BEGIN CATCH print 'There was a problem upgrading MSDB mail host database.' print 'Unable to create a primary key constraint on sysmail_principalprofile table' print 'Error State: ' + convert(varchar(10),ERROR_STATE() ) print 'Error Message: ' + ERROR_MESSAGE() print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY()) END CATCH Print 'Completed upgrade of Database Mail related objects...' GO -- *************************************************************************** -- Copyright (c) 1997 - 2003 Microsoft Corporation. -- All Rights Reserved -- -- @File: repl_master.tpt -- -- Purpose: -- Procedures/XPs/functions that are owned by replication and are created on master database -- -- Notes: Created 2001/02/07 10:28 (RMak) -- -- History: -- -- @Version: Yukon -- -- @EndHeader@ -- EXEC dbo.sp_configure 'allow updates', 1 GO reconfigure with override GO set ANSI_NULLS off use master go -- Make sure that we remove procedures that got accidentally installed in -- master by an 80 sp2 QFE if object_id('dbo.sp_MSreplremoveuncdir', 'P') > 0 drop procedure dbo.sp_MSreplremoveuncdir if object_id('dbo.sp_MSdeletefoldercontents', 'P') > 0 drop procedure dbo.sp_MSdeletefoldercontents -- drop extended procedures that were created in master if object_id('xp_mergexpusage', 'local') is not null execute sys.sp_dropextendedproc 'xp_mergexpusage' if object_id('xp_mergelineages', 'local') is not null execute sys.sp_dropextendedproc 'xp_mergelineages' if object_id('xp_mapdown_bitmap', 'local') is not null execute sys.sp_dropextendedproc 'xp_mapdown_bitmap' if object_id('xp_ORbitmap', 'local') is not null execute sys.sp_dropextendedproc 'xp_ORbitmap' if object_id('xp_firstonly_bitmap', 'local') is not null execute sys.sp_dropextendedproc 'xp_firstonly_bitmap' if object_id('xp_varbintohexstr', 'local') is not null execute sys.sp_dropextendedproc 'xp_varbintohexstr' if object_id('xp_intersectbitmaps', 'local') is not null execute sys.sp_dropextendedproc 'xp_intersectbitmaps' if object_id('xp_displayparamstmt', 'local') is not null execute sys.sp_dropextendedproc 'xp_displayparamstmt' if object_id('xp_printstatements', 'local') is not null execute sys.sp_dropextendedproc 'xp_printstatements' if object_id('xp_makecab', 'local') is not null exec sys.sp_dropextendedproc 'xp_makecab' if object_id('xp_unpackcab', 'local') is not null exec sys.sp_dropextendedproc 'xp_unpackcab' if object_id('sp_repldone', 'local') is not null exec sys.sp_dropextendedproc 'sp_repldone' if object_id('sp_repltrans', 'local') is not null exec sys.sp_dropextendedproc 'sp_repltrans' if object_id('sp_replcounters', 'local') is not null exec sys.sp_dropextendedproc 'sp_replcounters' if object_id('sp_replhelp', 'local') is not null exec sys.sp_dropextendedproc 'sp_replhelp' if object_id('sp_replddlparser', 'local') is not null exec sys.sp_dropextendedproc 'sp_replddlparser' if object_id('sp_replcmds', 'local') is not null exec sys.sp_dropextendedproc 'sp_replcmds' if object_id('sp_replflush', 'local') is not null exec sys.sp_dropextendedproc 'sp_replflush' if object_id('sp_replpostcmd', 'local') is not null exec sys.sp_dropextendedproc 'sp_replpostcmd' if object_id('sp_replincrementlsn_internal', 'local') is not null exec sys.sp_dropextendedproc 'sp_replincrementlsn_internal' if object_id('sp_replupdateschema', 'local') is not null exec sys.sp_dropextendedproc 'sp_replupdateschema' if object_id('sp_replsetoriginator_internal', 'local') is not null exec sys.sp_dropextendedproc 'sp_replsetoriginator_internal' if object_id('sp_replsetsyncstatus', 'local') is not null exec sys.sp_dropextendedproc 'sp_replsetsyncstatus' if object_id('sp_replpostsyncstatus_int', 'local') is not null exec sys.sp_dropextendedproc 'sp_replpostsyncstatus_int' if object_id('xp_dsninfo', 'local') is not null exec sys.sp_dropextendedproc 'xp_dsninfo' if object_id('xp_enumdsn', 'local') is not null exec sys.sp_dropextendedproc 'xp_enumdsn' if object_id('xp_oledbinfo', 'local') is not null exec sys.sp_dropextendedproc 'xp_oledbinfo' if object_id('xp_repl_encrypt', 'local') is not null exec sys.sp_dropextendedproc 'xp_repl_encrypt' if object_id('xp_repl_convert_encrypt', 'local') is not null exec sys.sp_dropextendedproc 'xp_repl_convert_encrypt' if object_id('xp_repl_help_connect', 'local') is not null exec sys.sp_dropextendedproc 'xp_repl_help_connect' if object_id('xp_replproberemsrv', 'local') is not null exec sys.sp_dropextendedproc 'xp_replproberemsrv' if object_id('xp_showlineage', 'local') is not null exec sys.sp_dropextendedproc 'xp_showlineage' if object_id('xp_showcolv', 'local') is not null exec sys.sp_dropextendedproc 'xp_showcolv' if object_id('sp_replpostschema', 'local') is not null exec sys.sp_dropextendedproc 'sp_replpostschema' go -- -- Create table dbo.MSreplication_options in master if needed -- if object_id(N'dbo.MSreplication_options', 'local') is null BEGIN -- table does not exist raiserror('Creating table MSreplication_options',0,1) CREATE TABLE dbo.MSreplication_options ( optname sysname NOT NULL, value bit NOT NULL, major_version int NOT NULL, minor_version int NOT NULL, revision int NOT NULL, install_failures int NOT NULL ) exec dbo.sp_MS_marksystemobject N'dbo.MSreplication_options' END ELSE BEGIN -- table exists -- drop index if needed (this index was used in Sphinx) if exists (select * from sys.indexes where object_id = object_id(N'dbo.MSreplication_options') and name = N'ucMSreplication_options') begin drop index dbo.MSreplication_options.ucMSreplication_options end END GO IF NOT EXISTS (SELECT * FROM MSreplication_options WHERE optname = 'transactional') INSERT INTO MSreplication_options VALUES ('transactional',0,0,0,0,0) IF NOT EXISTS (SELECT * FROM MSreplication_options WHERE optname = 'merge') INSERT INTO MSreplication_options VALUES ('merge',0,0,0,0,0) IF NOT EXISTS (SELECT * FROM MSreplication_options WHERE optname = 'security_model') BEGIN DECLARE @major_version int, @minor_version int, @revision int -- @@microsoftversion is set as 0xMMmmRR[RR] wher M=Major, m=minor and R=revision -- SELECT @major_version = CONVERT(int, SUBSTRING(CONVERT(varbinary(4), @@microsoftversion), 1, 1)), -- @minor_version = CONVERT(int, SUBSTRING(CONVERT(varbinary(4), @@microsoftversion), 2, 1)), -- @revision = CONVERT(int, SUBSTRING(CONVERT(varbinary(4), @@microsoftversion), 3, 2)) SELECT @major_version = 90, @minor_version = 0, @revision = 0 INSERT INTO MSreplication_options (optname, value, major_version, minor_version, revision, install_failures) VALUES ('security_model', 1, @major_version, @minor_version, @revision, 0) END UPDATE MSreplication_options SET major_version = 90 GO -- Startup procs have to be created in master if object_id('sp_MSrepl_startup', 'local') is not null drop procedure sp_MSrepl_startup raiserror('Creating procedure sp_MSrepl_startup', 0,1) go create procedure dbo.sp_MSrepl_startup as exec sys.sp_MSrepl_startup_internal go exec master.dbo.sp_MS_marksystemobject sp_MSrepl_startup -- If a distributor is installed, mark the sp as a startup sp. if exists (select * FROM master..sysservers WHERE srvstatus & 8 <> 0) exec dbo.sp_procoption 'sp_MSrepl_startup', 'startup', 'true' go if object_id('sp_MScleanupmergepublisher', 'local') is not null drop procedure sp_MScleanupmergepublisher go SET ANSI_NULLS ON SET ANSI_WARNINGS ON raiserror('Creating procedure sp_MScleanupmergepublisher', 0,1) go create procedure dbo.sp_MScleanupmergepublisher as exec sys.sp_MScleanupmergepublisher_internal go exec master.dbo.sp_MS_marksystemobject sp_MScleanupmergepublisher -- If there are any merge published databases installed on this server, -- mark sp_MScleanupmergepublisher as a startup proc if exists (select * from master..sysdatabases where (category & 4) <> 0) exec dbo.sp_procoption 'sp_MScleanupmergepublisher', 'startup', 'true' -- -- Functions that used to be created in master and have now moved to resource -- use exec to drop, otherwise "drop function" gives syntax error on SQL 7, which did not have UDFs yet -- if object_id('fn_varbintohexstr', 'local') is not null exec('drop function dbo.fn_varbintohexstr') if object_id('fn_varbintohexsubstring', 'local') is not null exec('drop function dbo.fn_varbintohexsubstring') go -- -- procedures that used to be created in master and are obsolete now -- drop the local procedures in master -- if object_id(N'dbo.sp_addpublisher', 'local') is not null drop procedure dbo.sp_addpublisher if object_id(N'dbo.sp_fetchshowcmdsinput', 'local') is not null drop procedure dbo.sp_fetchshowcmdsinput if object_id(N'dbo.sp_getagentoffloadinfo', 'local') is not null drop procedure dbo.sp_getagentoffloadinfo if object_id(N'dbo.sp_gettypestring', 'local') is not null drop procedure dbo.sp_gettypestring if object_id(N'dbo.sp_helpmergecleanupwait', 'local') is not null drop procedure dbo.sp_helpmergecleanupwait if object_id(N'dbo.sp_helpsubscriptionjobname', 'local') is not null drop procedure dbo.sp_helpsubscriptionjobname if object_id(N'dbo.sp_mergecompletecleanup', 'local') is not null drop procedure dbo.sp_mergecompletecleanup if object_id(N'dbo.sp_mergepreparecleanup', 'local') is not null drop procedure dbo.sp_mergepreparecleanup if object_id(N'dbo.sp_MSaddpubtocontents', 'local') is not null drop procedure dbo.sp_MSaddpubtocontents if object_id(N'dbo.sp_MSareallcolumnscomputed', 'local') is not null drop procedure dbo.sp_MSareallcolumnscomputed if object_id(N'dbo.sp_MSchunkgeneration', 'local') is not null drop procedure dbo.sp_MSchunkgeneration if object_id(N'dbo.sp_MScleanup_metadata', 'local') is not null drop procedure dbo.sp_MScleanup_metadata if object_id(N'dbo.sp_MScleanuptask', 'local') is not null drop procedure dbo.sp_MScleanuptask if object_id(N'dbo.sp_MScompletecleanup', 'local') is not null drop procedure dbo.sp_MScompletecleanup if object_id(N'dbo.sp_MScomputearticlescreationorder', 'local') is not null drop procedure dbo.sp_MScomputearticlescreationorder if object_id(N'dbo.sp_MScomputeunresolvedrefs', 'local') is not null drop procedure dbo.sp_MScomputeunresolvedrefs if object_id(N'dbo.sp_MSdelete_specifiedcontents', 'local') is not null drop procedure dbo.sp_MSdelete_specifiedcontents if object_id(N'dbo.sp_MSdeletecontents', 'local') is not null drop procedure dbo.sp_MSdeletecontents if object_id(N'dbo.sp_MSdeletepushagent', 'local') is not null drop procedure dbo.sp_MSdeletepushagent if object_id(N'dbo.sp_MSenumchanges_direct', 'local') is not null drop procedure dbo.sp_MSenumchanges_direct if object_id(N'dbo.sp_MSenumchanges_pal', 'local') is not null drop procedure dbo.sp_MSenumchanges_pal if object_id(N'dbo.sp_MSenumpartialchanges_direct', 'local') is not null drop procedure dbo.sp_MSenumpartialchanges_direct if object_id(N'dbo.sp_MSenumpartialchanges_pal', 'local') is not null drop procedure dbo.sp_MSenumpartialchanges_pal if object_id(N'dbo.sp_MSexternalfkreferences', 'local') is not null drop procedure dbo.sp_MSexternalfkreferences if object_id(N'dbo.sp_MSget_subtypedatasrc', 'local') is not null drop procedure dbo.sp_MSget_subtypedatasrc if object_id(N'dbo.sp_MSgettypestringudt', 'local') is not null drop procedure dbo.sp_MSgettypestringudt if object_id(N'dbo.sp_MShelpsubscriptionjobname', 'local') is not null drop procedure dbo.sp_MShelpsubscriptionjobname if object_id(N'dbo.sp_MSinsertcontents', 'local') is not null drop procedure dbo.sp_MSinsertcontents if object_id(N'dbo.sp_MSis_col_replicated', 'local') is not null drop procedure dbo.sp_MSis_col_replicated if object_id(N'dbo.sp_MSload_replication_status', 'local') is not null drop procedure dbo.sp_MSload_replication_status if object_id(N'dbo.sp_MSmakedynsnapshotvws_longdef', 'local') is not null drop procedure dbo.sp_MSmakedynsnapshotvws_longdef if object_id(N'dbo.sp_MSpreparecleanup', 'local') is not null drop procedure dbo.sp_MSpreparecleanup if object_id(N'dbo.sp_MSquiescecheck', 'local') is not null drop procedure dbo.sp_MSquiescecheck if object_id(N'dbo.sp_MSquiesceforcleanup', 'local') is not null drop procedure dbo.sp_MSquiesceforcleanup if object_id(N'dbo.sp_MSquiescetriggersoff', 'local') is not null drop procedure dbo.sp_MSquiescetriggersoff if object_id(N'dbo.sp_MSquiescetriggerson', 'local') is not null drop procedure dbo.sp_MSquiescetriggerson if object_id(N'dbo.sp_MSreplcheck_connection', 'local') is not null drop procedure dbo.sp_MSreplcheck_connection if object_id(N'dbo.sp_MSscript_security', 'local') is not null drop procedure dbo.sp_MSscript_security if object_id(N'dbo.sp_MSscript_validate_subscription', 'local') is not null drop procedure dbo.sp_MSscript_validate_subscription if object_id(N'dbo.sp_MSscriptmvastable', 'local') is not null drop procedure dbo.sp_MSscriptmvastable if object_id(N'dbo.sp_MSscriptmvastableidx', 'local') is not null drop procedure dbo.sp_MSscriptmvastableidx if object_id(N'dbo.sp_MSscriptmvastablenci', 'local') is not null drop procedure dbo.sp_MSscriptmvastablenci if object_id(N'dbo.sp_MSscriptmvastablepkc', 'local') is not null drop procedure dbo.sp_MSscriptmvastablepkc if object_id(N'dbo.sp_MSsubst_filter_name', 'local') is not null drop procedure dbo.sp_MSsubst_filter_name if object_id(N'dbo.sp_MSupdate_replication_status', 'local') is not null drop procedure dbo.sp_MSupdate_replication_status if object_id(N'dbo.sp_MSupdatecontents', 'local') is not null drop procedure dbo.sp_MSupdatecontents if object_id(N'dbo.sp_replicationoption', 'local') is not null drop procedure dbo.sp_replicationoption if object_id(N'dbo.sp_replproberemoteserver', 'local') is not null drop procedure dbo.sp_replproberemoteserver if object_id(N'dbo.sp_replsetoriginator_pal', 'local') is not null drop procedure dbo.sp_replsetoriginator_pal if object_id(N'dbo.sp_verify_publication', 'local') is not null drop procedure dbo.sp_verify_publication if object_id(N'dbo.sp_MSarticletextcol', 'local') is not null drop procedure dbo.sp_MSarticletextcol if object_id(N'dbo.sp_MSexists_file', 'local') is not null drop procedure dbo.sp_MSexists_file if object_id(N'dbo.sp_MSfixlineagemismatch', 'local') is not null drop procedure dbo.sp_MSfixlineagemismatch if object_id(N'dbo.sp_MStextcolstatus', 'local') is not null drop procedure dbo.sp_MStextcolstatus if object_id(N'dbo.sp_MSread_resolver_clsid', 'local') is not null drop procedure dbo.sp_MSread_resolver_clsid if object_id(N'dbo.sp_MSsubscriptions', 'local') is not null drop procedure dbo.sp_MSsubscriptions go EXEC dbo.sp_configure 'allow updates', 0 GO reconfigure with override GO PAD-- -- this script is run by engine during engine startup after upgrade -- raiserror(N'Executing replication upgrade scripts.', 10, 1) go use master go set nocount on declare @script_status int ,@repl_script_key nvarchar(277) ,@repl_script_value nvarchar(8) ,@retcode int ,@repl_installed int -- this script will be run regardless of whether replication is installed -- check to see if replication is installed before proceeding execute master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Replication', N'IsInstalled', @repl_installed OUTPUT, N'no_output' select @repl_installed = isnull(@repl_installed, 0) if @repl_installed = 0 begin raiserror(N'Replication feature is not installed. Upgrade scripts will not be run.', 10, 1) return end select @repl_script_key = N'SOFTWARE\Microsoft\MSSQLServer\Replication\Setup' ,@repl_script_value = N'Upgraded' -- Set script upgrade status to 0 set @script_status = 0 execute @retcode = master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', @repl_script_key, @repl_script_value, N'REG_DWORD', @script_status begin try raiserror(N'Executing sp_vupgrade_replication.', 10, 1) exec @retcode = sys.sp_vupgrade_replication if @retcode != 0 or @@error != 0 begin set @script_status = 0 end else begin set @script_status = 1 raiserror(N'sp_vupgrade_replication executed successfully', 10, 1) end end try begin catch -- re-raise the error non-fatally declare @error_msg nvarchar(2048) select @error_msg = ERROR_MESSAGE() raiserror(@error_msg, 10, 1) set @script_status = 0 end catch if @script_status != 1 begin raiserror(N'Error executing sp_vupgrade_replication.', 10, 1) end -- write the status to the registry raiserror(N'Saving upgrade script status to ''%s''.', 10, 1, @repl_script_key) execute @retcode = master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', @repl_script_key, @repl_script_value, N'REG_DWORD', @script_status if @retcode != 0 or @@error != 0 begin raiserror(N'Error saving upgrade script status.', 10, 1) end else begin raiserror(N'Saved upgrade script status successfully.', 10, 1) end goPA/**************************************************************************** // Copyright (c) Microsoft Corporation. // // @File: provisionsystemaccounts.sql // @Owner: kaloianm // // Purpose: // Script to grant login and SA privileges to NT AUTHORITY\SYSTEM and // the SQL Engine group. It also removes the FT group accounts no // longer required in Katmai // // Notes: // run by the SQL script upgrade framework. // // // @EndHeader@ *****************************************************************************/ use master go PRINT '----------------------------------------' PRINT 'Starting provisionsystemaccounts.sql ...'; PRINT '----------------------------------------' go -- Obtain the name of NT AUTHORITY\SYSTEM from a well-known SID -- (as it might be on an non-ENU installation) -- DECLARE @systemGroupSID AS NVARCHAR(85) DECLARE @systemGroupSIDBinary AS VARBINARY(85) DECLARE @systemGroupName AS NVARCHAR(85); SET @systemGroupSID = N'S-1-5-18'; -- convert textual SID to name -- SELECT @systemGroupSIDBinary = sid_binary(@systemGroupSID); SELECT @systemGroupName = suser_sname(@systemGroupSIDBinary); IF (@systemGroupName IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM sys.server_principals WHERE NAME = @systemGroupName)) BEGIN PRINT 'Adding login for ' + @systemGroupName DECLARE @strExec NVARCHAR (MAX) SET @strExec = 'CREATE LOGIN ' + QUOTENAME(@systemGroupName) + ' FROM WINDOWS' EXEC (@strExec) END EXEC sys.sp_addsrvrolemember @systemGroupName, N'sysadmin' END go ---- remove all SQL 2005 provision for msfteuser group account --------------- ---- those accounts are no longer needed for katmai and we need to remove them during upgrade ---------- declare @accountname nvarchar(256) declare @principalid int declare ftgroup_cursor cursor static local for select name, principal_id from sys.server_principals where name like '%SQLServer%MSFTEUser%' and type = 'G' open ftgroup_cursor fetch ftgroup_cursor into @accountname, @principalid while @@fetch_status >=0 begin if ((@accountname is not null) and (@principalid is not null)) begin declare @sqlstr nvarchar(1024) exec sp_dropsrvrolemember @accountname, 'sysadmin' select @sqlstr = N'revoke exec on sys.sp_fulltext_getdata from '+quotename(@accountname) exec sp_executesql @sqlstr select @sqlstr = N'drop login '+quotename(@accountname) exec sp_executesql @sqlstr end fetch ftgroup_cursor into @accountname, @principalid end deallocate ftgroup_cursor go PA/*------------------------------------------------------------------------------ msdb_upgrade_discovery.sql This script detects the MSDB format era as Shiloh, Yukon or Katmai, and selectively enables scripts in the upgrade table neede to properly upgrade MSDB to the current build format based on its findings. This discovery script selects which of the 3 "era upgrade" scripts following it in the upgrade table will run. These "era upgrade" scripts are disabled by default in the table. The possible outcomes of running it are: - Shiloh MSDB detected, so enable all 3 - Yukon MSDB detected, so enable the last 2 - Katmai MSDB detected, so enable the last 1 (this one is always enabled here, so it runs once per SQL version upgrade as does this script) It uses sys.sp_dbscriptlevel to enable the other upgrade scripts to run. ** Copyright (c) Microsoft Corporation. All rights reserved. ------------------------------------------------------------------------------*/ -- These must match the definitions used in sqlscriptupgrade.cpp DECLARE @EMPTY_SCRIPT_LEVEL INT DECLARE @KATMAI_SCRIPT_LEVEL INT DECLARE @ID_SQLAGENT90_MSDB_UPGRADE INT DECLARE @ID_SQLAGENT90_SYSDBUPG INT DECLARE @ID_SQLAGENT100_MSDB_UPGRADE INT SELECT @EMPTY_SCRIPT_LEVEL = 0 SELECT @KATMAI_SCRIPT_LEVEL = 2 SELECT @ID_SQLAGENT90_MSDB_UPGRADE = 4 SELECT @ID_SQLAGENT90_SYSDBUPG = 5 SELECT @ID_SQLAGENT100_MSDB_UPGRADE = 6 PRINT ' ' PRINT '----------------------------------------------------------------' PRINT 'msdb_upgrade_discovery starting' DECLARE @msdb_version NVARCHAR(20) DECLARE @msdb_version_desc NVARCHAR(100) SELECT @msdb_version = N'Katmai' SELECT @msdb_version_desc = N'SQL Server 2008' -- no need to check if earlier than Shiloh since we do not support that and you would not encounter that scenario in here. -- check if Shiloh -- this MaintPlan view does not exist before Yukon RTM IF (OBJECT_ID(N'msdb.dbo.sysmaintplan_plans', 'V') IS NULL) BEGIN SELECT @msdb_version = N'Shiloh' SELECT @msdb_version_desc = N'SQL Server 2000' END -- check if Yukon -- this MaintPlan view always exists from Yukon RTM onward, and -- DMF did not exist before Katmai, the policies table has been present since Katmai CTP3 -- we are not trying to support Katmai upgrading prior to CTP3, so this is sufficient to "tell the difference" for our purposes ELSE IF (OBJECT_ID(N'msdb.dbo.sysmaintplan_plans', 'V') IS NOT NULL AND OBJECT_ID(N'msdb.dbo.syspolicy_policies_internal', 'U') IS NULL) BEGIN SELECT @msdb_version = N'Yukon' SELECT @msdb_version_desc = N'SQL Server 2005' END -- summarize to log PRINT N'MSDB format is: ' + @msdb_version_desc -- process the script states -- set all initial values to disabled -- since they could be 0's (CTP6) or 2's (RC0+), don't assume they are initially either value exec sys.sp_dbscriptlevel 'master', @ID_SQLAGENT90_MSDB_UPGRADE, @KATMAI_SCRIPT_LEVEL exec sys.sp_dbscriptlevel 'master', @ID_SQLAGENT90_SYSDBUPG, @KATMAI_SCRIPT_LEVEL exec sys.sp_dbscriptlevel 'master', @ID_SQLAGENT100_MSDB_UPGRADE, @KATMAI_SCRIPT_LEVEL if (@msdb_version = N'Shiloh') BEGIN -- enable Shiloh-Yukon upgrade script PRINT 'Running SQL Server 2000 SP4 to SQL Server 2005 upgrade script' exec sys.sp_dbscriptlevel 'master', @ID_SQLAGENT90_MSDB_UPGRADE, @EMPTY_SCRIPT_LEVEL END if (@msdb_version = N'Shiloh' OR @msdb_version = N'Yukon') BEGIN -- enable intra-Yukon upgrade script PRINT 'Running SQL Server 2005 to SQL Server 2005 SP2 upgrade script' exec sys.sp_dbscriptlevel 'master', @ID_SQLAGENT90_SYSDBUPG, @EMPTY_SCRIPT_LEVEL END if (@msdb_version = N'Shiloh' OR @msdb_version = N'Yukon' OR @msdb_version = N'Katmai') BEGIN -- enable Yukon SP2-Katmai RTM upgrade script -- as of Katmai RTM this one is intended to always run -- (if this discovery script is even called means we need it) exec sys.sp_dbscriptlevel 'master', @ID_SQLAGENT100_MSDB_UPGRADE, @EMPTY_SCRIPT_LEVEL PRINT 'Running SQL Server 2005 SP2 to SQL Server 2008 upgrade script' END PRINT '----------------------------------------------------------------' PRINT ' ' GO P/********************************************************************************************/ /* XPSTAR.SQL - Extended Stored Procedures for the SQL Server Enterprise Components */ /* ** Copyright Microsoft, Inc. 1994 - 2000 ** All Rights Reserved. */ /********************************************************************************************/ use master ---------------------------------------------------------------------------------------- -- drop legacy procedures that are not used anymore ---------------------------------------------------------------------------------------- create table #procs (name sysname NOT NULL) go insert into #procs values (N'sp_sqlregister') insert into #procs values (N'xp_getfiledetails') insert into #procs values (N'xp_eventlog') insert into #procs values (N'sp_eventlog') insert into #procs values (N'sp_IsMBCSLeadByte') insert into #procs values (N'sp_GetMBCSCharLen') insert into #procs values (N'xp_enum_activescriptengines') insert into #procs values (N'xp_MSPlatform') insert into #procs values (N'xp_IsNTAdmin') insert into #procs values (N'xp_SetSQLSecurity') insert into #procs values (N'xp_GetAdminGroupName') insert into #procs values (N'xp_MSnt2000') insert into #procs values (N'xp_MSLocalSystem') declare proc_cursor cursor for select name from #procs open proc_cursor declare @name as sysname fetch next from proc_cursor into @name while @@fetch_status = 0 begin if object_id(@name, 'X') is not null begin --print N'DEBUG: dropping ' + @name + '...' exec sp_dropextendedproc @functname = @name end if object_id('dbo.' + @name, 'P') is not null begin --print N'DEBUG: dropping ' + @name + '...' execute ('drop procedure dbo.' + @name) end fetch next from proc_cursor into @name end close proc_cursor deallocate proc_cursor go drop table #procs go /********************************************************************************************/ /* EOF XPSTAR.SQL */ /********************************************************************************************/ -- This script will handle moving credentials from LSA to SQL Server secrets store use master go DECLARE @sql nvarchar(max) --------------------------------------------------------------------------- -- Check configuration and possibly turn on OBD component for calling XP --------------------------------------------------------------------------- DECLARE @show_advanced bit SELECT @show_advanced = cast(value_in_use as bit) from sys.configurations where name = N'show advanced options' DECLARE @component_enabled bit SELECT @component_enabled = cast(value_in_use as bit) from sys.configurations where name = N'Agent XPs' IF 1 <> @component_enabled BEGIN IF 1 <> @show_advanced BEGIN exec sys.sp_configure @configname = N'show advanced options', @configvalue = 1 reconfigure with override END -- turn it on exec sys.sp_configure @configname = N'Agent XPs', @configvalue = 1 reconfigure with override END ------------------------------------- -- Upgrade proxy account ------------------------------------- BEGIN TRY IF (NOT EXISTS( SELECT * FROM master.sys.credentials WHERE name = 'UpgradedCredential')) BEGIN DECLARE @sysadmin_only int SELECT @sysadmin_only = 0 --if this flag is not present we default to allow proxy in Shiloh EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'SysAdminOnly', @sysadmin_only OUTPUT, N'no_output' IF @sysadmin_only = 0 BEGIN DECLARE @user_domain sysname DECLARE @user_name sysname DECLARE @user_password sysname --read LSA string for 7.0 server. If not there, try 8.0 --7.0 stores only the proxy password, they used a fixed account, SQLAgentCmdExec EXECUTE master.dbo.xp_sqlagent_param 4, N'MicrosoftSQLServerAgentProxyPasswordKey', @user_password OUTPUT IF @user_password IS NOT NULL BEGIN SELECT @user_domain = CONVERT(sysname, SERVERPROPERTY('MachineName')) SELECT @user_name = N'SQLAgentCmdExec' END --read LSA 8.0 proxy account ELSE BEGIN EXECUTE master.dbo.xp_sqlagent_param 0, N'SQLAgentProxyDomain', @user_domain OUTPUT EXECUTE master.dbo.xp_sqlagent_param 0, N'SQLAgentProxyUsername', @user_name OUTPUT EXECUTE master.dbo.xp_sqlagent_param 4, N'SQLAgentProxyPassword', @user_password OUTPUT END -- create the credential IF @user_domain IS NOT NULL AND @user_name IS NOT NULL AND @user_password IS NOT NULL BEGIN PRINT '' PRINT 'Adding Upgraded Credential...' DECLARE @full_name sysname SELECT @full_name = @user_domain + N'\' + @user_name SELECT @sql = N'CREATE CREDENTIAL UpgradedCredential WITH IDENTITY = ''' + @full_name + N''' , SECRET = ''' + @user_password + N'''' EXEC master.dbo.sp_executesql @sql PRINT '' PRINT 'Adding SQL Server Proxy Account...' EXEC master.dbo.sp_xp_cmdshell_proxy_account @full_name, @user_password END END -- IF @sysadmin_only = 0 END --NOT EXISTS( SELECT * FROM master.sys.credentials WHERE name = 'UpgradedCredential') END TRY BEGIN CATCH print 'There was a problem upgrading MSDB database.' print 'Could not upgrade proxy account information' print 'Error Number: ' + convert(varchar(20), ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(20), ERROR_SEVERITY()) print 'Error State: ' + convert(varchar(20), ERROR_STATE()) print 'Error Message: ' + ERROR_MESSAGE() END CATCH ------------------------------------ -- Upgrade msx account ------------------------------------ BEGIN TRY IF (NOT EXISTS( SELECT * FROM master.sys.credentials WHERE name = 'UpgradedMSXCredential')) BEGIN DECLARE @msx_regular_connection INT SELECT @msx_regular_connection = 0 --integrated connections EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'RegularMSXConnections', @msx_regular_connection OUTPUT, N'no_output' IF @msx_regular_connection = 1 BEGIN DECLARE @user sysname EXECUTE master.dbo.xp_sqlagent_param 0, N'SQLAgentMSXUsername', @user OUTPUT DECLARE @password sysname EXECUTE master.dbo.xp_sqlagent_param 4, N'SQLAgentMSXPassword', @password OUTPUT IF @user IS NOT NULL AND @password IS NOT NULL BEGIN PRINT '' PRINT 'Adding Upgraded MSX Credential...' SELECT @sql = N'CREATE CREDENTIAL UpgradedMSXCredential WITH IDENTITY = ''' + @user + N''' , SECRET = ''' + @password + N'''' EXEC master.dbo.sp_executesql @sql END END END END TRY BEGIN CATCH print 'There was a problem upgrading MSDB database.' print 'Could not upgrade msx account information' print 'Error Number: ' + convert(varchar(20), ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(20), ERROR_SEVERITY()) print 'Error State: ' + convert(varchar(20), ERROR_STATE()) print 'Error Message: ' + ERROR_MESSAGE() END CATCH ---------------------------------------------------------------------- -- Restore previous state of the component if we had to turn it on ---------------------------------------------------------------------- IF 1 <> @component_enabled BEGIN -- turn it back off exec sys.sp_configure @configname = N'Agent XPs', @configvalue = 0 reconfigure with override IF 1 <> @show_advanced BEGIN exec sys.sp_configure @configname = N'show advanced options', @configvalue = 0 reconfigure with override END END /**********************************************************************/ /* MSDB8TO9.SQL */ /* */ /* Upgrades 7.x and 8.x msdb to 9.0 and drops all obsolete 8.x */ /* */ /* ** Copyright Microsoft, Inc. 1994 - 2003 ** All Rights Reserved. */ /**********************************************************************/ PRINT '----------------------------------' PRINT 'Starting execution of PREINSTMSDB90.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 90 sp_dbcmptlevel @dbname = 'msdb', @new_cmptlevel = '90' 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 --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 PREINSTMSDB90.SQL complete' PRINT '----------------------------------' go /**********************************************************************/ /* INSTMSDB.SQL */ /* */ /* Installs the tables, triggers and stored procedures necessary for */ /* supporting local (and multi-server) jobs, alerts, operators, and */ /* backup history. These objects are used by SQLDMO, SQL Enterprise */ /* Manager, and SQLServerAgent. */ /* */ /* Also contains SQL DTS (Data Transformation Services) tables and */ /* stored procedures for local SQL Server storage of DTS Packages. */ /* */ /* ** Copyright Microsoft, Inc. 1994 - 2005 ** All Rights Reserved. */ /**********************************************************************/ PRINT '---------------------------------------------' PRINT 'Starting execution of Shiloh-era INSTMSDB.SQL' PRINT '---------------------------------------------' go --this version of instmsdb should be executed only against 9.0 or later servers -- to perform the Shiloh-to-Yukon-era upgrading. IF (@@microsoftversion / 0x01000000) < 9 BEGIN RAISERROR('This version of instmsdb.sql should only be executed against 9.0 or later servers.', 20, 127) WITH LOG END go /**************************************************************/ /* Create auxilary procedure to enable OBD 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 = 256KB) LOG ON (NAME = N''MSDBLog'', FILENAME = N''' + @device_directory + N'MSDBLog.ldf'', SIZE = 512KB, MAXSIZE = UNLIMITED, FILEGROWTH = 256KB)') EXECUTE (N'ALTER DATABASE msdb SET DB_CHAINING ON') EXECUTE (N'ALTER DATABASE msdb SET TRUSTWORTHY 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') PRINT 'Attempting to expand the msdb database...' EXECUTE(N'ALTER DATABASE msdb MODIFY FILE (NAME = N''' + @logical_file_name + N''', FILENAME = N''' + @os_file_name + N''', SIZE =' + @size_as_char + N'KB)') IF (@@error <> 0) RAISERROR('Unable to expand the msdb database. INSTMSDB.SQL terminating.', 20, 127) WITH LOG EXECUTE (N'ALTER DATABASE msdb SET TRUSTWORTHY ON') END PRINT '' END go -- truncate log on checkpoint ALTER DATABASE msdb SET RECOVERY SIMPLE go USE msdb go -- Check that we're in msdb IF (DB_NAME() <> N'msdb') RAISERROR('A problem was encountered accessing msdb. INSTMSDB.SQL terminating.', 20, 127) WITH LOG go -- Add the guest user IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysusers WHERE (name = N'guest') AND (hasdbaccess = 1))) BEGIN PRINT '' EXECUTE sys.sp_adduser N'guest' END go CHECKPOINT go /**************************************************************/ /* Record time of start of creates */ /**************************************************************/ SELECT start = getdate() INTO #InstMsdb go /**************************************************************/ /* */ /* T A B L E D R O P S */ /* */ /**************************************************************/ SET NOCOUNT ON DECLARE @build_number INT DECLARE @rebuild_needed TINYINT SELECT @build_number = @@microsoftversion & 0xffff IF (@build_number <= 1050) -- The last build that we changed the schema in SELECT @rebuild_needed = 1 ELSE SELECT @rebuild_needed = 0 IF ((NOT OBJECT_ID(N'dbo.sysjobactivity', 'U') IS NULL) AND (@rebuild_needed = 1)) BEGIN PRINT '' PRINT 'Dropping table sysjobactivity...' DROP TABLE dbo.sysjobactivity END IF ((NOT OBJECT_ID(N'dbo.syssessions', 'U') IS NULL) AND (@rebuild_needed = 1)) BEGIN PRINT '' PRINT 'Dropping table syssessions...' DROP TABLE dbo.syssessions END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sqlagent_info') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sqlagent_info...' DROP TABLE dbo.sqlagent_info END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmaintplan_logdetail') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysmaintplan_logdetail...' DROP TABLE msdb.dbo.sysmaintplan_logdetail END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmaintplan_log') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysmaintplan_log...' DROP TABLE msdb.dbo.sysmaintplan_log END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmaintplan_subplans') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysmaintplan_subplans...' DROP TABLE msdb.dbo.sysmaintplan_subplans END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdownloadlist') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysdownloadlist...' IF (EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE (id = OBJECT_ID(N'sysdownloadlist')) AND (name = N'error_message') AND (cdefault <> 0))) EXECUTE sys.sp_unbindefault N'sysdownloadlist.error_message' IF (EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE (id = OBJECT_ID(N'sysdownloadlist')) AND (name = N'date_posted') AND (cdefault <> 0))) EXECUTE sys.sp_unbindefault N'sysdownloadlist.date_posted' IF (EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE (id = OBJECT_ID(N'sysdownloadlist')) AND (name = N'status') AND (cdefault <> 0))) EXECUTE sys.sp_unbindefault N'sysdownloadlist.status' DROP TABLE dbo.sysdownloadlist END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobhistory') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysjobhistory...' DROP TABLE dbo.sysjobhistory END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobservers') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysjobservers...' DROP TABLE dbo.sysjobservers END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobschedules') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysjobschedules...' DROP TABLE dbo.sysjobschedules END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobs') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysjobs...' DROP TABLE dbo.sysjobs END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobsteps') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysjobsteps...' DROP TABLE dbo.sysjobsteps END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobstepslogs') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysjobstepslogs...' DROP TABLE dbo.sysjobstepslogs END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysschedules') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysschedules...' DROP TABLE dbo.sysschedules END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'syscategories') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table syscategories...' DROP TABLE dbo.syscategories END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'systargetservers') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table systargetservers...' IF (EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE (id = OBJECT_ID(N'systargetservers')) AND (name = N'enlist_date') AND (cdefault <> 0))) EXECUTE sys.sp_unbindefault N'systargetservers.enlist_date' IF (EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE (id = OBJECT_ID(N'systargetservers')) AND (name = N'last_poll_date') AND (cdefault <> 0))) EXECUTE sys.sp_unbindefault N'systargetservers.last_poll_date' IF (EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE (id = OBJECT_ID(N'systargetservers')) AND (name = N'status') AND (cdefault <> 0))) EXECUTE sys.sp_unbindefault N'systargetservers.status' DROP TABLE dbo.systargetservers END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'systargetservergroups') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table systargetservergroups...' DROP TABLE dbo.systargetservergroups END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'systargetservergroupmembers') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table systargetservergroupmembers...' DROP TABLE dbo.systargetservergroupmembers END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'systaskids') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table systaskids...' DROP TABLE dbo.systaskids END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysalerts') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysalerts...' DROP TABLE dbo.sysalerts END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysoperators') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysoperators...' DROP TABLE dbo.sysoperators END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysnotifications') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysnotifications...' DROP TABLE dbo.sysnotifications END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdbmaintplan_jobs') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysdbmaintplan_jobs...' DROP TABLE sysdbmaintplan_jobs END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdbmaintplan_databases') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysdbmaintplan_databases...' DROP TABLE sysdbmaintplan_databases END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdbmaintplan_history') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysdbmaintplan_history...' DROP TABLE sysdbmaintplan_history END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdbmaintplans') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysdbmaintplans...' DROP TABLE sysdbmaintplans END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'syscachedcredentials') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table syscachedcredentials...' DROP TABLE syscachedcredentials END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'default_sdl_error_message') AND (type = 'D') AND (@rebuild_needed = 1))) DROP DEFAULT default_sdl_error_message IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'default_current_date') AND (type = 'D') AND (@rebuild_needed = 1))) DROP DEFAULT default_current_date IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'default_zero') AND (type = 'D') AND (@rebuild_needed = 1))) DROP DEFAULT default_zero IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'default_one') AND (type = 'D') AND (@rebuild_needed = 1))) DROP DEFAULT default_one IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysoriginatingservers') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysoriginatingservers...' DROP TABLE dbo.sysoriginatingservers END IF ((NOT OBJECT_ID(N'dbo.sysproxies', 'U') IS NULL) AND (@rebuild_needed = 1)) BEGIN PRINT '' PRINT 'Dropping table sysproxies...' DROP TABLE dbo.sysproxies END IF ((NOT OBJECT_ID(N'dbo.sysproxylogin', 'U') IS NULL) AND (@rebuild_needed = 1)) BEGIN PRINT '' PRINT 'Dropping table sysproxylogin...' DROP TABLE dbo.sysproxylogin END IF ((NOT OBJECT_ID(N'dbo.sysproxysubsystem', 'U') IS NULL) AND (@rebuild_needed = 1)) BEGIN PRINT '' PRINT 'Dropping table sysproxysubsystem...' DROP TABLE dbo.sysproxysubsystem END --------------------------------------------------------------- -- Database Mail: general configuration tables --------------------------------------------------------------- IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_server') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysmail_server...' DROP TABLE msdb.dbo.sysmail_server END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_servertype') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysmail_servertype...' DROP TABLE msdb.dbo.sysmail_servertype END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_profileaccount') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysmail_profileaccount...' DROP TABLE msdb.dbo.sysmail_profileaccount END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_account') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysmail_account...' DROP TABLE msdb.dbo.sysmail_account END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_principalprofile') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysmail_principalprofile...' DROP TABLE msdb.dbo.sysmail_principalprofile END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_profile') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysmail_profile...' DROP TABLE msdb.dbo.sysmail_profile END IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmail_configuration') AND (type = 'U') AND (@rebuild_needed = 1))) BEGIN PRINT '' PRINT 'Dropping table sysmail_configuration...' DROP TABLE msdb.dbo.sysmail_configuration END --------------------------------------------------------- -- Database Mail: mail host database specific objects --------------------------------------------------------- IF (@rebuild_needed = 1 AND OBJECT_ID('dbo.sysmail_log', 'U') IS NOT NULL) BEGIN PRINT 'Dropping TABLE sysmail_log' DROP TABLE sysmail_log END IF (@rebuild_needed = 1 AND OBJECT_ID('dbo.sysmail_attachments', 'U') IS NOT NULL) BEGIN PRINT 'Dropping TABLE sysmail_attachments' DROP TABLE sysmail_attachments END IF (@rebuild_needed = 1 AND OBJECT_ID('dbo.sysmail_mailitems', 'U') IS NOT NULL) BEGIN PRINT 'Dropping TABLE sysmail_mailitems' DROP TABLE sysmail_mailitems END GO -- Always drop sysmail_query_transfer. It has no user data IF (OBJECT_ID('dbo.sysmail_query_transfer', 'U') IS NOT NULL) DROP TABLE sysmail_query_transfer -- Always drop sysmail_attachments_transfer. It has no user data IF (OBJECT_ID('dbo.sysmail_attachments_transfer', 'U') IS NOT NULL) DROP TABLE sysmail_attachments_transfer ------------------------------------------------------------------------- -- Database Mail SSB objects (messages, contracts, queues, servrices) ------------------------------------------------------------------------- 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 --------------------------------------------------------------- -- Replication Datatype mapping tables --------------------------------------------------------------- exec sys.sp_MSrepl_dropdatatypemappings go CHECKPOINT go /**************************************************************/ /* */ /* D E F A U L T S */ /* */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'default_sdl_error_message') AND (type = 'D'))) EXECUTE('CREATE DEFAULT default_sdl_error_message AS NULL') go IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'default_current_date') AND (type = 'D'))) EXECUTE('CREATE DEFAULT default_current_date AS GETDATE()') go IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'default_zero') AND (type = 'D'))) EXECUTE('CREATE DEFAULT default_zero AS 0') go IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'default_one') AND (type = 'D'))) EXECUTE('CREATE DEFAULT default_one AS 1') go /**************************************************************/ /* */ /* T A B L E S */ /* */ /**************************************************************/ /**************************************************************/ /* SYSPROXIES */ /**************************************************************/ IF (OBJECT_ID(N'dbo.sysproxies', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysproxies...' CREATE TABLE dbo.sysproxies ( proxy_id INT IDENTITY, --used to identify a proxy name sysname NOT NULL, --friendly name of a proxy credential_id INT NOT NULL, enabled TINYINT NOT NULL, description NVARCHAR(512) NULL, --nvarchar(512) user_sid VARBINARY(85) NOT NULL, --sid of proxy NT user credential_date_created DATETIME NOT NULL --the date the associated credential has been created ) CREATE UNIQUE CLUSTERED INDEX clust ON sysproxies(proxy_id) CREATE UNIQUE NONCLUSTERED INDEX nc1 ON sysproxies(name) END go IF (OBJECT_ID(N'dbo.syssubsystems', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table syssubsystems...' CREATE TABLE dbo.syssubsystems ( subsystem_id INT NOT NULL, -- used to identify the subsystem subsystem NVARCHAR(40) COLLATE database_default NOT NULL, -- Name of subsystem description_id INT NULL, -- Message number of description string. These messages are stored in sysmessages table for localization purposes. Same story as for Shiloh subsystem_dll NVARCHAR(255) COLLATE database_default NULL, -- Store full path of the name of subsystem dll subsystem are always installed in the binn folder of an instance agent_exe NVARCHAR(255) COLLATE database_default NULL, -- Full path to the executable that use the subsystem start_entry_point NVARCHAR(30) COLLATE database_default NULL, -- Function called when initializing subsystem event_entry_point NVARCHAR(30) COLLATE database_default NULL, -- Function called when executing a subsystem step stop_entry_point NVARCHAR(30) COLLATE database_default NULL, -- Function called when unloading a subsystem max_worker_threads INT NULL -- Number of maximum concurrent steps for a subsystem ) CREATE UNIQUE CLUSTERED INDEX clust ON syssubsystems(subsystem_id) CREATE UNIQUE NONCLUSTERED INDEX nc1 ON syssubsystems(subsystem) END go IF (OBJECT_ID(N'dbo.sysproxysubsystem', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysproxysubsystem...' CREATE TABLE dbo.sysproxysubsystem ( subsystem_id INT NOT NULL, -- used to identify the subsystem proxy_id INT NOT NULL, -- used to identify the proxy ) CREATE UNIQUE CLUSTERED INDEX clust ON sysproxysubsystem(subsystem_id, proxy_id) END go IF (OBJECT_ID(N'dbo.sysproxylogin', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysproxylogin...' CREATE TABLE dbo.sysproxylogin ( proxy_id INT NOT NULL, --used to identify the proxy sid VARBINARY(85) NULL, --keep logins, fixed server roles or msdb database roles flags INT DEFAULT 0 NOT NULL -- tells is member_id is login = 0, server fixed role, msdb role. ) CREATE UNIQUE CLUSTERED INDEX clust ON sysproxylogin(proxy_id, sid, flags) END go PRINT '' PRINT 'Creating view sysproxyloginsubsystem_view...' go IF (NOT OBJECT_ID(N'dbo.sysproxyloginsubsystem_view', 'V') IS NULL) DROP VIEW sysproxyloginsubsystem_view go CREATE VIEW sysproxyloginsubsystem_view AS SELECT ps.subsystem_id AS subsystem_id, pl.proxy_id AS proxy_id, pl.sid AS sid, pl.flags AS flags FROM sysproxylogin pl JOIN sysproxysubsystem ps ON pl.proxy_id = ps.proxy_id go IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sqlagent_info') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sqlagent_info...' CREATE TABLE sqlagent_info ( attribute sysname NOT NULL, value NVARCHAR(512) NOT NULL ) END go /**************************************************************/ /* SYSDOWNLOADLIST */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdownloadlist') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysdownloadlist...' CREATE TABLE sysdownloadlist ( instance_id INT IDENTITY NOT NULL, source_server sysname NOT NULL, operation_code TINYINT NOT NULL, object_type TINYINT NOT NULL, object_id UNIQUEIDENTIFIER NOT NULL, target_server sysname NOT NULL, error_message NVARCHAR(1024) NULL, date_posted DATETIME NOT NULL, date_downloaded DATETIME NULL, status TINYINT NOT NULL, deleted_object_name sysname NULL ) EXECUTE sys.sp_bindefault default_sdl_error_message, N'sysdownloadlist.error_message' EXECUTE sys.sp_bindefault default_current_date, N'sysdownloadlist.date_posted' EXECUTE sys.sp_bindefault default_zero, N'sysdownloadlist.status' CREATE UNIQUE CLUSTERED INDEX clust ON sysdownloadlist(instance_id) CREATE NONCLUSTERED INDEX nc1 ON sysdownloadlist(target_server) CREATE NONCLUSTERED INDEX nc2 ON sysdownloadlist(object_id) END go /**************************************************************/ /* SYSJOBHISTORY */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobhistory') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysjobhistory...' CREATE TABLE sysjobhistory ( instance_id INT IDENTITY NOT NULL, job_id UNIQUEIDENTIFIER NOT NULL, step_id INT NOT NULL, step_name sysname NOT NULL, sql_message_id INT NOT NULL, sql_severity INT NOT NULL, message NVARCHAR(1024) 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 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(1024) 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 go /**************************************************************/ /* SYSJOBS_VIEW */ /**************************************************************/ PRINT '' PRINT 'Creating view sysjobs_view...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobs_view') AND (type = 'V'))) DROP VIEW sysjobs_view go CREATE VIEW sysjobs_view AS SELECT jobs.job_id, svr.originating_server, jobs.name, jobs.enabled, jobs.description, jobs.start_step_id, jobs.category_id, jobs.owner_sid, jobs.notify_level_eventlog, jobs.notify_level_email, jobs.notify_level_netsend, jobs.notify_level_page, jobs.notify_email_operator_id, jobs.notify_netsend_operator_id, jobs.notify_page_operator_id, jobs.delete_level, jobs.date_created, jobs.date_modified, jobs.version_number, jobs.originating_server_id, svr.master_server FROM msdb.dbo.sysjobs as jobs JOIN msdb.dbo.sysoriginatingservers_view as svr ON jobs.originating_server_id = svr.originating_server_id --LEFT JOIN msdb.dbo.sysjobservers js ON jobs.job_id = js.job_id WHERE (owner_sid = SUSER_SID()) OR (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'SQLAgentReaderRole'), 0) = 1) OR ( (ISNULL(IS_MEMBER(N'TargetServersRole'), 0) = 1) AND (EXISTS(SELECT * FROM msdb.dbo.sysjobservers js WHERE js.server_id <> 0 AND js.job_id = jobs.job_id))) -- filter out local jobs go IF (OBJECT_ID(N'dbo.syssessions', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table syssessions...' CREATE TABLE dbo.syssessions ( session_id INT IDENTITY PRIMARY KEY, agent_start_date DATETIME NOT NULL ) CREATE UNIQUE NONCLUSTERED INDEX nonclust ON syssessions(agent_start_date) END go IF (OBJECT_ID(N'dbo.sysjobactivity', 'U') IS NULL) BEGIN PRINT '' PRINT 'Creating table sysjobactivity...' CREATE TABLE dbo.sysjobactivity ( session_id INT NOT NULL REFERENCES syssessions(session_id), job_id UNIQUEIDENTIFIER NOT NULL REFERENCES sysjobs(job_id) ON DELETE CASCADE, run_requested_date DATETIME NULL, run_requested_source sysname NULL, queued_date DATETIME NULL, start_execution_date DATETIME NULL, last_executed_step_id INT NULL, last_executed_step_date DATETIME NULL, stop_execution_date DATETIME NULL, job_history_id INT NULL, --keeps a reference to the record in sysjobhistory for detailed job information next_scheduled_run_date DATETIME NULL ) CREATE UNIQUE CLUSTERED INDEX clust ON sysjobactivity(session_id, job_id) END go /**************************************************************/ /* SYSJOBSTEPS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobsteps') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysjobsteps...' CREATE TABLE sysjobsteps ( job_id UNIQUEIDENTIFIER NOT NULL, step_id INT NOT NULL, step_name sysname NOT NULL, subsystem NVARCHAR(40) NOT NULL, command NVARCHAR(max) NULL, flags INT NOT NULL, additional_parameters NTEXT NULL, cmdexec_success_code INT NOT NULL, on_success_action TINYINT NOT NULL, on_success_step_id INT NOT NULL, on_fail_action TINYINT NOT NULL, on_fail_step_id INT NOT NULL, server sysname NULL, -- Used only by replication and OLAP database_name sysname NULL, database_user_name sysname NULL, retry_attempts INT NOT NULL, retry_interval INT NOT NULL, os_run_priority INT NOT NULL, -- NOTE: Cannot use TINYINT because we need a signed number output_file_name NVARCHAR(200) NULL, last_run_outcome INT NOT NULL, last_run_duration INT NOT NULL, last_run_retries INT NOT NULL, last_run_date INT NOT NULL, last_run_time INT NOT NULL, proxy_id INT NULL, step_uid UNIQUEIDENTIFIER NULL ) CREATE UNIQUE CLUSTERED INDEX clust ON sysjobsteps(job_id, step_id) CREATE UNIQUE NONCLUSTERED INDEX nc1 ON sysjobsteps(job_id, step_name) CREATE UNIQUE NONCLUSTERED INDEX nc2 ON sysjobsteps(step_uid) END go /**************************************************************/ /* SYSJOBSTEPSLOGS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobstepslogs') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysjobstepslogs...' CREATE TABLE sysjobstepslogs ( log_id INT IDENTITY (1,1) PRIMARY KEY NOT NULL, log NVARCHAR(max) NOT NULL, date_created DATETIME NOT NULL DEFAULT getdate(), date_modified DATETIME NOT NULL DEFAULT getdate(), log_size bigint NOT NULL , step_uid UNIQUEIDENTIFIER NOT NULL REFERENCES sysjobsteps(step_uid) ON DELETE CASCADE ) END go /**************************************************************/ /* SYSSCHEDULES */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysschedules') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysschedules...' CREATE TABLE sysschedules ( schedule_id INT IDENTITY PRIMARY KEY CLUSTERED, schedule_uid UNIQUEIDENTIFIER NOT NULL, originating_server_id INT NOT NULL, -- REFERENCE enforced by trig_sysschedules_insert_update name sysname NOT NULL, owner_sid varbinary(85) NOT NULL, enabled INT NOT NULL, freq_type INT NOT NULL, freq_interval INT NOT NULL, freq_subday_type INT NOT NULL, freq_subday_interval INT NOT NULL, freq_relative_interval INT NOT NULL, freq_recurrence_factor INT NOT NULL, active_start_date INT NOT NULL, active_end_date INT NOT NULL, active_start_time INT NOT NULL, active_end_time INT NOT NULL, date_created DATETIME NOT NULL DEFAULT (GETDATE()), date_modified DATETIME NOT NULL DEFAULT (GETDATE()), version_number INT NOT NULL DEFAULT (1) ) -- CREATE UNIQUE CLUSTERED INDEX clust ON sysschedules(job_id, name) END go /**************************************************************/ /* trig_sysschedules_insert_update */ /**************************************************************/ PRINT '' PRINT 'Creating trigger trig_sysschedules_insert_update...' IF NOT OBJECT_ID('dbo.trig_sysschedules_insert_update', 'TR') IS NULL DROP TRIGGER dbo.trig_sysschedules_insert_update GO CREATE TRIGGER dbo.trig_sysschedules_insert_update ON dbo.sysschedules FOR INSERT, UPDATE AS BEGIN SET NOCOUNT ON -- Disallow the insert or update if the originating_server_id isn't in sysoriginatingservers_view. IF (EXISTS (SELECT * FROM inserted WHERE inserted.originating_server_id NOT IN (SELECT v.originating_server_id FROM sysoriginatingservers_view AS v))) BEGIN RAISERROR(14379, -1, -1, 'dbo.sysschedules') ROLLBACK TRANSACTION RETURN END END go /**************************************************************/ /* SYSSCHEDULES_LOCALSERVER_VIEW */ /**************************************************************/ PRINT '' PRINT 'Creating view sysschedules_localserver_view...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysschedules_localserver_view') AND (type = 'V'))) DROP VIEW sysschedules_localserver_view go CREATE VIEW sysschedules_localserver_view AS SELECT sched.schedule_id, sched.schedule_uid, sched.originating_server_id, sched.name, sched.owner_sid, sched.enabled, sched.freq_type, sched.freq_interval, sched.freq_subday_type, sched.freq_subday_interval, sched.freq_relative_interval, sched.freq_recurrence_factor, sched.active_start_date, sched.active_end_date, sched.active_start_time, sched.active_end_time, sched.date_created, sched.date_modified, sched.version_number, svr.originating_server, svr.master_server FROM msdb.dbo.sysschedules as sched JOIN msdb.dbo.sysoriginatingservers_view as svr ON sched.originating_server_id = svr.originating_server_id WHERE (svr.master_server = 0) AND ( (sched.owner_sid = SUSER_SID()) OR (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'SQLAgentReaderRole'), 0) = 1) ) go /**************************************************************/ /* SYSJOBSCHEDULES */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobschedules') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysjobschedules...' CREATE TABLE sysjobschedules ( schedule_id INT REFERENCES sysschedules(schedule_id), job_id UNIQUEIDENTIFIER REFERENCES sysjobs(job_id), next_run_date INT NOT NULL DEFAULT 0, next_run_time INT NOT NULL DEFAULT 0 ) CREATE UNIQUE CLUSTERED INDEX clust ON sysjobschedules(job_id, schedule_id) -- CREATE UNIQUE NONCLUSTERED INDEX nc1 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 ( 4, 1, 1, N'Web Assistant') -- Default for all jobs created by the Web Assistant 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 (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 /**************************************************************/ /* SYSOPERATORS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysoperators') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysoperators...' CREATE TABLE sysoperators ( id INT IDENTITY NOT NULL, name sysname NOT NULL, -- Was length 50 in 6.x enabled TINYINT NOT NULL, email_address NVARCHAR(100) NULL, last_email_date INT NOT NULL, -- Was NULL in 6.x last_email_time INT NOT NULL, -- Was NULL in 6.x pager_address NVARCHAR(100) NULL, last_pager_date INT NOT NULL, -- Was NULL in 6.x last_pager_time INT NOT NULL, -- Was NULL in 6.x weekday_pager_start_time INT NOT NULL, weekday_pager_end_time INT NOT NULL, saturday_pager_start_time INT NOT NULL, saturday_pager_end_time INT NOT NULL, sunday_pager_start_time INT NOT NULL, sunday_pager_end_time INT NOT NULL, pager_days TINYINT NOT NULL, netsend_address NVARCHAR(100) NULL, -- New for 7.0 last_netsend_date INT NOT NULL, -- New for 7.0 last_netsend_time INT NOT NULL, -- New for 7.0 category_id INT NOT NULL -- New for 7.0 ) CREATE UNIQUE CLUSTERED INDEX ByName ON sysoperators(name) CREATE UNIQUE INDEX ByID ON sysoperators(id) END go /**************************************************************/ /* SYSNOTIFICATIONS */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysnotifications') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysnotifications...' CREATE TABLE sysnotifications ( alert_id INT NOT NULL, operator_id INT NOT NULL, notification_method TINYINT NOT NULL ) CREATE UNIQUE CLUSTERED INDEX ByAlertIDAndOperatorID ON sysnotifications(alert_id, operator_id) END go /**************************************************************/ /* */ /* M A I N T E N A N C E P L A N S */ /* */ /**************************************************************/ /**************************************************************/ /* sysmaintplan_subplans */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmaintplan_subplans') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysmaintplan_subplans...' -- This table stores the DTS package associated with the maintenance plan -- It also stored metadata about the maintenance plan such as its name, description etc CREATE TABLE sysmaintplan_subplans ( subplan_id UNIQUEIDENTIFIER NOT NULL CONSTRAINT [PK_sysmaintplan_subplan] PRIMARY KEY CLUSTERED, subplan_name sysname NOT NULL, subplan_description NVARCHAR(512) NULL, plan_id UNIQUEIDENTIFIER NOT NULL, job_id UNIQUEIDENTIFIER NOT NULL CONSTRAINT FK_subplan_job_id FOREIGN KEY (job_id) REFERENCES sysjobs(job_id), schedule_id INT NULL CONSTRAINT FK_subplan_schedule_id FOREIGN KEY (schedule_id) REFERENCES sysschedules(schedule_id) ) 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 NOT NULL, subplan_id UNIQUEIDENTIFIER NOT 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 ) END go /**************************************************************/ /* sysmaintplan_logdetail */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysmaintplan_logdetail') AND (type = 'U'))) BEGIN PRINT '' PRINT 'Creating table sysmaintplan_logdetail...' -- This table stores the maintenance plan log details CREATE TABLE sysmaintplan_logdetail ( task_detail_id UNIQUEIDENTIFIER NOT NULL CONSTRAINT [FK_sysmaintplan_log_detail_task_id] FOREIGN KEY (task_detail_id) REFERENCES sysmaintplan_log(task_detail_id) ON DELETE CASCADE, line1 NVARCHAR(256) NOT NULL, line2 NVARCHAR(256) NULL, line3 NVARCHAR(256) NULL, line4 NVARCHAR(256) NULL, line5 NVARCHAR(256) NULL, server_name sysname NOT NULL, start_time DATETIME NULL, end_time DATETIME NULL, error_number INT NULL, error_message NVARCHAR(max) NULL, command NVARCHAR(max) NULL, succeeded BIT NULL ) END go /**************************************************************/ /* SYSTASKIDS */ /* */ /* This table provides a mapping between new GUID job ID's */ /* and 6.x INT task ID's. */ /* Entries are made in this table for all existing 6.x tasks */ /* and for all new tasks added using the 7.0 version of */ /* sp_addtask. */ /* Callers of the 7.0 version of sp_helptask will ONLY see */ /* tasks [jobs] that have a corresponding entry in this table */ /* [IE. Jobs created with sp_add_job will not be returned]. */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'systaskids') AND (type = 'U'))) BEGIN CREATE TABLE systaskids ( task_id INT IDENTITY NOT NULL, job_id UNIQUEIDENTIFIER NOT NULL ) CREATE CLUSTERED INDEX clust ON systaskids(job_id) END go /**************************************************************/ /* SYSCACHEDCREDENTIALS */ /* */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'syscachedcredentials') AND (type = 'U'))) BEGIN CREATE TABLE syscachedcredentials ( login_name sysname COLLATE database_default NOT NULL PRIMARY KEY, has_server_access BIT NOT NULL DEFAULT 0, is_sysadmin_member BIT NOT NULL DEFAULT 0, cachedate DATETIME NOT NULL DEFAULT getdate() ) END go --------------------------------------------------------------- -- Replication Datatype mapping tables --------------------------------------------------------------- exec sys.sp_MSrepl_createdatatypemappings go CHECKPOINT go /**************************************************************/ /* */ /* C O R E P R O C E D U R E S */ /* */ /**************************************************************/ PRINT '' PRINT 'Creating function SQLAGENT_SUSER_SNAME ...' IF (NOT OBJECT_ID(N'dbo.SQLAGENT_SUSER_SNAME', 'FN') IS NULL) DROP FUNCTION dbo.SQLAGENT_SUSER_SNAME go CREATE FUNCTION dbo.SQLAGENT_SUSER_SNAME(@user_sid VARBINARY(85)) RETURNS sysname AS BEGIN DECLARE @ret sysname IF @user_sid = 0xFFFFFFFF SELECT @ret = N'$(SQLAgentAccount)' ELSE SELECT @ret = SUSER_SNAME(@user_sid) RETURN @ret END go PRINT '' PRINT 'Creating function SQLAGENT_SUSER_SID ...' IF (NOT OBJECT_ID(N'dbo.SQLAGENT_SUSER_SID', 'FN') IS NULL) DROP FUNCTION dbo.SQLAGENT_SUSER_SID go CREATE FUNCTION dbo.SQLAGENT_SUSER_SID(@user_name sysname) RETURNS VARBINARY(85) AS BEGIN DECLARE @ret VARBINARY(85) IF @user_name = N'$(SQLAgentAccount)' SELECT @ret = 0xFFFFFFFF ELSE SELECT @ret = SUSER_SID(@user_name, 0) RETURN @ret END go ----------------------------------------------------------- -- get_principal_id : retrieves principal_id for a given sid -- ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.get_principal_id', 'FN') IS NULL DROP FUNCTION dbo.get_principal_id GO CREATE FUNCTION dbo.get_principal_id(@principal_sid varbinary(85)) RETURNS int AS BEGIN DECLARE @principal_id int SELECT @principal_id=principal_id FROM msdb.sys.database_principals WHERE sid=@principal_sid RETURN @principal_id END GO ----------------------------------------------------------- -- get_principal_sid : retrieves principal sid from principal_id -- ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.get_principal_sid', 'FN') IS NULL DROP FUNCTION dbo.get_principal_sid GO CREATE FUNCTION dbo.get_principal_sid(@principal_id int) RETURNS varbinary(85) AS BEGIN DECLARE @principal_sid varbinary(85) SELECT @principal_sid=sid FROM msdb.sys.database_principals WHERE principal_id=@principal_id RETURN @principal_sid END GO /**************************************************************/ /* SP_SQLAGENT_IS_SRVROLEMEMBER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure SP_SQLAGENT_IS_SRVROLEMEMBER...' IF (NOT OBJECT_ID(N'dbo.sp_sqlagent_is_srvrolemember', 'P') IS NULL) DROP PROCEDURE dbo.sp_sqlagent_is_srvrolemember go CREATE PROCEDURE sp_sqlagent_is_srvrolemember @role_name sysname, @login_name sysname AS BEGIN DECLARE @is_member INT SET NOCOUNT ON IF @role_name IS NULL OR @login_name IS NULL RETURN(0) SELECT @is_member = 0 --IS_SRVROLEMEMBER works only if the login to be tested is provisioned with sqlserver 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 EXECUTE AS LOGIN = @login_name -- impersonate SELECT @is_member = ISNULL(IS_SRVROLEMEMBER(@role_name), 0) -- check role membership REVERT -- revert back END TRY BEGIN CATCH SELECT @is_member = 0 END CATCH END RETURN @is_member END go /**************************************************************/ /* SP_VERIFY_CATEGORY_IDENTIFIERS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_category_identifiers...' IF (NOT OBJECT_ID(N'dbo.sp_verify_category_identifiers', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_category_identifiers go CREATE PROCEDURE sp_verify_category_identifiers @name_of_name_parameter [varchar](60), @name_of_id_parameter [varchar](60), @category_name [sysname] OUTPUT, @category_id [INT] OUTPUT AS BEGIN DECLARE @retval INT DECLARE @category_id_as_char NVARCHAR(36) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter)) SELECT @name_of_id_parameter = LTRIM(RTRIM(@name_of_id_parameter)) SELECT @category_name = LTRIM(RTRIM(@category_name)) IF (@category_name = N'') SELECT @category_name = NULL IF ((@category_name IS NOT NULL) AND (@category_id IS NOT NULL)) BEGIN RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- Check category id IF (@category_id IS NOT NULL) BEGIN SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = @category_id) IF (@category_name IS NULL) BEGIN SELECT @category_id_as_char = CONVERT(nvarchar(36), @category_id) RAISERROR(14262, -1, -1, '@category_id', @category_id_as_char) RETURN(1) -- Failure END END ELSE -- Check category name IF (@category_name IS NOT NULL) BEGIN -- The name is not ambiguous, so get the corresponding category_id (if the job exists) SELECT @category_id = category_id FROM msdb.dbo.syscategories WHERE (name = @category_name) IF (@category_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@category_name', @category_name) RETURN(1) -- Failure END END RETURN(0) -- Success END go PRINT '' PRINT 'Creating function agent_datetime...' IF (NOT OBJECT_ID(N'dbo.agent_datetime', 'FN') IS NULL) DROP FUNCTION dbo.agent_datetime go CREATE FUNCTION agent_datetime(@date int, @time int) RETURNS DATETIME AS BEGIN RETURN ( CONVERT(DATETIME, CONVERT(NVARCHAR(4),@date / 10000) + N'-' + CONVERT(NVARCHAR(2),(@date % 10000)/100) + N'-' + CONVERT(NVARCHAR(2),@date % 100) + N' ' + CONVERT(NVARCHAR(2),@time / 10000) + N':' + CONVERT(NVARCHAR(2),(@time % 10000)/100) + N':' + CONVERT(NVARCHAR(2),@time % 100), 120) ) END go /**************************************************************/ /* SP_VERIFY_PROXY_IDENTIFIERS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_proxy_identifiers...' IF (NOT OBJECT_ID(N'dbo.sp_verify_proxy_identifiers', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_proxy_identifiers go CREATE PROCEDURE sp_verify_proxy_identifiers @name_of_name_parameter [varchar](60), @name_of_id_parameter [varchar](60), @proxy_name [sysname] OUTPUT, @proxy_id [INT] OUTPUT AS BEGIN DECLARE @retval INT DECLARE @proxy_id_as_char NVARCHAR(36) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter)) SELECT @name_of_id_parameter = LTRIM(RTRIM(@name_of_id_parameter)) SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) IF (@proxy_name = N'') SELECT @proxy_name = NULL IF ((@proxy_name IS NULL) AND (@proxy_id IS NULL)) OR ((@proxy_name IS NOT NULL) AND (@proxy_id IS NOT NULL)) BEGIN RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- Check proxy id IF (@proxy_id IS NOT NULL) BEGIN SELECT @proxy_name = name FROM msdb.dbo.sysproxies WHERE (proxy_id = @proxy_id) IF (@proxy_name IS NULL) BEGIN SELECT @proxy_id_as_char = CONVERT(nvarchar(36), @proxy_id) RAISERROR(14262, -1, -1, '@proxy_id', @proxy_id_as_char) RETURN(1) -- Failure END END ELSE -- Check proxy name IF (@proxy_name IS NOT NULL) BEGIN -- The name is not ambiguous, so get the corresponding proxy_id (if the job exists) SELECT @proxy_id = proxy_id FROM msdb.dbo.sysproxies WHERE (name = @proxy_name) IF (@proxy_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@proxy_name', @proxy_name) RETURN(1) -- Failure END END RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_CREDENTIAL_IDENTIFIERS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_credential_identifiers...' IF (NOT OBJECT_ID(N'dbo.sp_verify_credential_identifiers', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_credential_identifiers go CREATE PROCEDURE sp_verify_credential_identifiers @name_of_name_parameter [varchar](60), @name_of_id_parameter [varchar](60), @credential_name [sysname] OUTPUT, @credential_id [INT] OUTPUT AS BEGIN DECLARE @retval INT DECLARE @credential_id_as_char NVARCHAR(36) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter)) SELECT @name_of_id_parameter = LTRIM(RTRIM(@name_of_id_parameter)) SELECT @credential_name = LTRIM(RTRIM(@credential_name)) IF (@credential_name = N'') SELECT @credential_name = NULL IF ((@credential_name IS NULL) AND (@credential_id IS NULL)) OR ((@credential_name IS NOT NULL) AND (@credential_id IS NOT NULL)) BEGIN RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- Check credential_id IF (@credential_id IS NOT NULL) BEGIN SELECT @credential_name = name FROM master.sys.credentials WHERE (credential_id = @credential_id) IF (@credential_name IS NULL) BEGIN SELECT @credential_id_as_char = CONVERT(nvarchar(36), @credential_id) RAISERROR(14262, -1, -1, '@credential_id', @credential_id_as_char) RETURN(1) -- Failure END END ELSE -- Check proxy name IF (@credential_name IS NOT NULL) BEGIN -- The name is not ambiguous, so get the corresponding credential_id (if the job exists) SELECT @credential_id = credential_id FROM master.sys.credentials WHERE (name = @credential_name) IF (@credential_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@credential_name', @credential_name) RETURN(1) -- Failure END END RETURN(0) -- Success END go /**************************************************************/ /* sp_verify_subsystems */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_subsystems...' IF (NOT OBJECT_ID(N'dbo.sp_verify_subsystems', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_subsystems go CREATE PROCEDURE dbo.sp_verify_subsystems @syssubsytems_refresh_needed BIT = 0 AS BEGIN SET NOCOUNT ON DECLARE @retval INT DECLARE @InstRootPath nvarchar(512) DECLARE @ComRootPath nvarchar(512) DECLARE @DtsInstalled INT DECLARE @DtsRootPath nvarchar(512) IF ( (@syssubsytems_refresh_needed=1) OR (NOT EXISTS(select * from syssubsystems)) ) BEGIN EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @InstRootPath OUTPUT IF @InstRootPath IS NULL BEGIN RAISERROR(14658, -1, -1) WITH LOG RETURN (1) END SELECT @InstRootPath = @InstRootPath + N'\binn\' EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\Microsoft Sql Server\90', N'VerSpecificRootDir', @ComRootPath OUTPUT IF @ComRootPath IS NULL BEGIN RAISERROR(14659, -1, -1) WITH LOG RETURN(1) END EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSDTS\Setup\DTSInstall', N'', @DtsInstalled OUTPUT, N'no_output' IF( (@DtsInstalled IS NOT NULL) AND (@DtsInstalled = 1) ) BEGIN EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSDTS\Setup\DTSPath', N'', @DtsRootPath OUTPUT, N'no_output' IF (@DtsRootPath IS NOT NULL) SELECT @DtsRootPath = @DtsRootPath + N'Binn\' END ELSE SET @DtsRootPath = NULL SELECT @ComRootPath = @ComRootPath + N'COM\' DECLARE @TranCounter INT; SET @TranCounter = @@TRANCOUNT; IF @TranCounter > 0 BEGIN -- Procedure called when there is -- an active transaction. -- Create a savepoint to be able -- to roll back only the work done -- in the procedure if there is an -- error. SAVE TRANSACTION tran_sp_verify_subsystems_i; END ELSE BEGIN -- Procedure must start its own -- transaction. BEGIN TRANSACTION; END -- Obtain processor count to determine maximum number of threads per subsystem DECLARE @xp_results TABLE ( id INT NOT NULL, name NVARCHAR(30) COLLATE database_default NOT NULL, internal_value INT NULL, character_value NVARCHAR(212) COLLATE database_default NULL ) INSERT INTO @xp_results EXECUTE master.dbo.xp_msver DECLARE @processor_count INT SELECT @processor_count = internal_value from @xp_results where id=16 -- ProcessorCount -- Modify database. BEGIN TRY --create subsystems --TSQL subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'TSQL') INSERT syssubsystems VALUES ( 1, N'TSQL',14556, FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), 20 * @processor_count ) --ActiveScripting subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'ActiveScripting') INSERT syssubsystems VALUES ( 2, N'ActiveScripting', 14555, @InstRootPath + N'SQLATXSS90.DLL',NULL,N'ActiveScriptStart',N'ActiveScriptEvent',N'ActiveScriptStop', 10 * @processor_count ) --CmdExec subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'CmdExec') INSERT syssubsystems VALUES ( 3, N'CmdExec', 14550, @InstRootPath + N'SQLCMDSS90.DLL',NULL,N'CmdExecStart',N'CmdEvent',N'CmdExecStop', 10 * @processor_count ) --Snapshot subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'Snapshot') INSERT syssubsystems VALUES ( 4, N'Snapshot', 14551, @InstRootPath + N'SQLREPSS90.DLL', @ComRootPath + N'SNAPSHOT.EXE', N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count ) --LogReader subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'LogReader') INSERT syssubsystems VALUES ( 5, N'LogReader', 14552, @InstRootPath + N'SQLREPSS90.DLL', @ComRootPath + N'logread.exe',N'ReplStart',N'ReplEvent',N'ReplStop',25 * @processor_count ) --Distribution subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'Distribution') INSERT syssubsystems VALUES ( 6, N'Distribution', 14553, @InstRootPath + N'SQLREPSS90.DLL', @ComRootPath + N'DISTRIB.EXE',N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count ) --Merge subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'Merge') INSERT syssubsystems VALUES ( 7, N'Merge', 14554, @InstRootPath + N'SQLREPSS90.DLL',@ComRootPath + N'REPLMERG.EXE',N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count ) --QueueReader subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'QueueReader') INSERT syssubsystems VALUES ( 8, N'QueueReader', 14581, @InstRootPath + N'sqlrepss90.dll',@ComRootPath + N'qrdrsvc.exe',N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count ) --ANALYSISQUERY subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'ANALYSISQUERY') INSERT syssubsystems VALUES ( 9, N'ANALYSISQUERY', 14513, @InstRootPath + N'SQLOLAPSS90.DLL',NULL,N'OlapStart',N'OlapQueryEvent',N'OlapStop',100 * @processor_count ) --ANALYSISQUERY subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'ANALYSISCOMMAND') INSERT syssubsystems VALUES ( 10, N'ANALYSISCOMMAND', 14514, @InstRootPath + N'SQLOLAPSS90.DLL',NULL,N'OlapStart',N'OlapCommandEvent',N'OlapStop',100 * @processor_count ) IF(@DtsRootPath IS NOT NULL) BEGIN --DTS subsystem IF (NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'SSIS') ) INSERT syssubsystems VALUES ( 11, N'SSIS', 14538, @InstRootPath + N'SQLDTSSS90.DLL',@DtsRootPath + N'DTExec.exe',N'DtsStart',N'DtsEvent',N'DtsStop',100 * @processor_count ) ELSE UPDATE syssubsystems SET agent_exe = @DtsRootPath + N'DTExec.exe' WHERE subsystem = N'SSIS' END ELSE BEGIN IF EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'SSIS') DELETE FROM syssubsystems WHERE subsystem = N'SSIS' END IF @TranCounter = 0 BEGIN -- @TranCounter = 0 means no transaction was -- started before the procedure was called. -- The procedure must commit the transaction -- it started. COMMIT TRANSACTION; END END TRY BEGIN CATCH -- An error occurred, must determine -- which type of rollback will roll -- back only the work done in the -- procedure. IF @TranCounter = 0 BEGIN -- Transaction started in procedure. -- Roll back complete transaction. ROLLBACK TRANSACTION; END ELSE -- Transaction started before procedure -- called, do not roll back modifications -- made before the procedure was called. IF (XACT_STATE()) <> - 1 BEGIN -- If the transaction is still valid, just -- roll back to the savepoint set at the -- start of the stored procedure. ROLLBACK TRANSACTION tran_sp_verify_subsystems_i; END -- If the transaction is uncommitable, a rollback -- to the save point is not allowed because -- the savepoint rollback writes to the log. -- Just return to the caller, which should -- roll back the outer transaction. -- After the appropriate rollback, 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() RAISERROR (@ErrorMessage, -- Message text. @ErrorSeverity, -- Severity. @ErrorState -- State. ) RETURN (1) END CATCH END --(NOT EXISTS(select * from syssubsystems)) 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 CREATE PROCEDURE dbo.sp_sqlagent_is_member ( @group_sid VARBINARY(85) = NULL, @role_principal_id INT = NULL, --only should be NOT 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 --a sysadmin can check for every user group membership IF 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) SELECT @ret_success = IS_MEMBER(@group_name) --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) IF (@name_sid IS NULL) BEGIN RAISERROR(14516, -1, -1, @proxy_id, @subsystem_name, @name) RETURN(1) -- Failure END --check first if name has been granted explicit permissions 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 RETURN(0) 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 -- 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 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 -- FIXED SERVER Roles BEGIN -- we have to use impersonation to check for role membership SELECT @role_from_sid = SUSER_SNAME(@granted_sid) SELECT @name_from_sid = SUSER_SNAME(@name_sid) EXEC @is_member = sp_sqlagent_is_srvrolemember @role_from_sid, @name_from_sid -- check role membership IF @is_member = 1 SELECT @retval = 0 END IF @retval = 1 BEGIN SELECT @granted_sid = NULL FETCH NEXT FROM proxy_subsystem INTO @granted_sid, @flags END END DEALLOCATE proxy_subsystem IF (@retval = 1 AND @raise_error <> 0) BEGIN RAISERROR(14516, -1, -1, @proxy_id, @subsystem_name, @name) RETURN(1) -- Failure END --0 is for success RETURN @retval END go /**************************************************************/ /* SP_HELP_PROXY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_proxy...' IF (NOT OBJECT_ID(N'dbo.sp_help_proxy', 'P') IS NULL) DROP PROCEDURE dbo.sp_help_proxy go CREATE PROCEDURE dbo.sp_help_proxy @proxy_id int = NULL, @proxy_name sysname = NULL, @subsystem_name sysname = NULL, @name nvarchar(256) = NULL AS BEGIN DECLARE @retval INT DECLARE @subsystem_id INT DECLARE @cur_subsystem_name NVARCHAR(40) DECLARE @current_proxy_id INT DECLARE @not_have_permission INT DECLARE cur_proxy CURSOR LOCAL FOR SELECT p.proxy_id, s.subsystem FROM sysproxies p, syssubsystems s WHERE ISNULL(UPPER(@subsystem_name collate SQL_Latin1_General_CP1_CS_AS), UPPER(s.subsystem collate SQL_Latin1_General_CP1_CS_AS) ) = UPPER(s.subsystem collate SQL_Latin1_General_CP1_CS_AS) AND s.subsystem_id <> 1 --last is TSQL subsystem -- this call will populate subsystems table if necessary EXEC @retval = msdb.dbo.sp_verify_subsystems IF @retval <> 0 RETURN(1) --failure --create temp table with returned rows DECLARE @temp_proxy TABLE ( proxy_id INT --used to identify a proxy ) SET NOCOUNT ON SELECT @subsystem_id = NULL -- Remove any leading/trailing spaces from parameters SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) IF @proxy_name = '' SELECT @proxy_name = NULL SELECT @subsystem_name = LTRIM(RTRIM(@subsystem_name)) IF @proxy_name = '' SELECT @proxy_name = NULL SELECT @name = LTRIM(RTRIM(@name)) IF @name = '' SELECT @name = NULL IF (@proxy_id IS NOT NULL OR @proxy_name IS NOT NULL) BEGIN EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END IF @subsystem_name IS NOT NULL BEGIN EXECUTE @retval = sp_verify_subsystem_identifiers '@subsystem_name', '@subsystem_id', @subsystem_name OUTPUT, @subsystem_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END IF (@subsystem_name IS NOT NULL AND @name IS NULL) OR (@subsystem_name IS NULL AND @name IS NOT NULL) BEGIN RAISERROR(14532, -1, -1, '@subsystem_name', '@name') RETURN(1) -- Failure END --only member of sysadmin and SQLAgentOperatorRole roles can see proxies granted to somebody else IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0)) BEGIN SELECT @name = SUSER_SNAME() END IF @name IS NOT NULL BEGIN OPEN cur_proxy FETCH NEXT FROM cur_proxy INTO @current_proxy_id, @cur_subsystem_name WHILE (@@fetch_status = 0) BEGIN --verify if supplied user have permission to use the current proxy for specified subsystem --disabled proxy should be shown as well IF NOT EXISTS(SELECT * FROM @temp_proxy WHERE proxy_id = @current_proxy_id) BEGIN EXECUTE @not_have_permission = sp_verify_proxy_permissions @subsystem_name = @cur_subsystem_name, @proxy_id = @current_proxy_id, @name = @name, @raise_error = 0, @allow_disable_proxy = 1, @verify_special_account = 0, @check_only_read_perm = 1 IF (@not_have_permission = 0) -- have permissions INSERT @temp_proxy VALUES(@current_proxy_id) END FETCH NEXT FROM cur_proxy INTO @current_proxy_id, @cur_subsystem_name END CLOSE cur_proxy DEALLOCATE cur_proxy END ELSE INSERT @temp_proxy SELECT proxy_id from sysproxies -- returns different result sets if caller is admin or not IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 1)) BEGIN SELECT p.proxy_id, p.name, c.credential_identity, p.enabled, p.description, p.user_sid, p.credential_id, CASE WHEN c.credential_id IS NULL THEN 0 ELSE 1 END as credential_identity_exists FROM sysproxies p LEFT JOIN master.sys.credentials c ON p.credential_id = c.credential_id JOIN @temp_proxy t ON p.proxy_id = t.proxy_id WHERE ISNULL(@proxy_id, p.proxy_id) = p.proxy_id END ELSE BEGIN SELECT p.proxy_id, p.name, null as credential_identity, p.enabled, p.description, null as user_sid, p.credential_id, null as credential_identity_exists FROM sysproxies p, @temp_proxy t WHERE ISNULL(@proxy_id, p.proxy_id) = p.proxy_id AND p.proxy_id = t.proxy_id END END go /**************************************************************/ /* sp_grant_proxy_to_subsystem */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_grant_proxy_to_subsystem...' IF (NOT OBJECT_ID(N'dbo.sp_grant_proxy_to_subsystem', 'P') IS NULL) DROP PROCEDURE dbo.sp_grant_proxy_to_subsystem go CREATE PROCEDURE dbo.sp_grant_proxy_to_subsystem @proxy_id int = NULL, @proxy_name sysname = NULL, -- must specify only one of above parameter to identify the proxy @subsystem_id int = NULL, @subsystem_name sysname = NULL -- must specify only one of above parameter to identify the subsystem AS BEGIN DECLARE @retval INT DECLARE @proxy_account sysname SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @subsystem_name = LTRIM(RTRIM(@subsystem_name)) SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) -- Turn [nullable] empty string parameters into NULLs IF @subsystem_name = '' SELECT @subsystem_name = NULL IF @proxy_name = '' SELECT @proxy_name = NULL EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure EXECUTE @retval = sp_verify_subsystem_identifiers '@subsystem_name', '@subsystem_id', @subsystem_name OUTPUT, @subsystem_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure --TSQL subsystem is prohibited IF @subsystem_id = 1 BEGIN RAISERROR(14530, -1, -1) RETURN(1) -- Failure END --check if we already added an user for the pair subsystem-proxy IF (EXISTS(SELECT * FROM sysproxysubsystem WHERE subsystem_id = @subsystem_id AND proxy_id = @proxy_id)) BEGIN RAISERROR(14531, -1, -1) RETURN(1) -- Failure END INSERT INTO sysproxysubsystem ( subsystem_id, proxy_id ) VALUES ( @subsystem_id, @proxy_id ) END go /**************************************************************/ /* sp_grant_login_to_proxy */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_grant_login_to_proxy...' IF (NOT OBJECT_ID(N'dbo.sp_grant_login_to_proxy', 'P') IS NULL) DROP PROCEDURE dbo.sp_grant_login_to_proxy go CREATE PROCEDURE dbo.sp_grant_login_to_proxy @login_name NVARCHAR(256) = NULL, @fixed_server_role NVARCHAR(256) = NULL, @msdb_role NVARCHAR(256) = NULL, -- must specify only one of above parameter to identify the type of login @proxy_id int = NULL, @proxy_name sysname = NULL -- must specify only one of above parameter to identify the proxy AS BEGIN DECLARE @retval INT DECLARE @name nvarchar(256) DECLARE @flags INT DECLARE @sid VARBINARY(85) DECLARE @is_sysadmin BIT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) SELECT @fixed_server_role = LTRIM(RTRIM(@fixed_server_role)) SELECT @msdb_role = LTRIM(RTRIM(@msdb_role)) -- Turn [nullable] empty string parameters into NULLs IF @proxy_name = '' SELECT @proxy_name = NULL IF @login_name = '' SELECT @login_name = NULL IF @fixed_server_role = '' SELECT @fixed_server_role = NULL IF @msdb_role = '' SELECT @msdb_role = NULL EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure EXECUTE @retval = sp_verify_login_identifiers @login_name, @fixed_server_role, @msdb_role, @name OUTPUT, @sid OUTPUT, @flags OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- is login member of sysadmin role? SELECT @is_sysadmin = 0 IF (@login_name IS NOT NULL) BEGIN EXEC @is_sysadmin = sp_sqlagent_is_srvrolemember N'sysadmin', @login_name -- check role membership END IF (@is_sysadmin = 1) BEGIN -- @name is sysadmin, it cannot granted to proxy -- issue a message and do nothing RAISERROR(14395, 10, 1, @name) END ELSE BEGIN --check if we already added an user for the pair subsystem-proxy IF (EXISTS(SELECT * FROM sysproxylogin WHERE proxy_id = @proxy_id AND ISNULL(sid, 0) = ISNULL(@sid,0) AND flags = @flags)) BEGIN RAISERROR(14531, -1, -1) RETURN(1) -- Failure END INSERT INTO sysproxylogin ( proxy_id, sid, flags ) VALUES ( @proxy_id, @sid, @flags) END END go /**************************************************************/ /* sp_revoke_login_from_proxy */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_revoke_login_from_proxy...' IF (NOT OBJECT_ID(N'dbo.sp_revoke_login_from_proxy', 'P') IS NULL) DROP PROCEDURE dbo.sp_revoke_login_from_proxy go CREATE PROCEDURE dbo.sp_revoke_login_from_proxy @name NVARCHAR(256), @proxy_id INT = NULL, @proxy_name sysname = NULL -- must specify only one of above parameter to identify the proxy AS BEGIN DECLARE @retval INT DECLARE @sid VARBINARY(85) DECLARE @is_sysadmin BIT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) SELECT @name = LTRIM(RTRIM(@name)) -- Turn [nullable] empty string parameters into NULLs IF @proxy_name = '' SELECT @proxy_name = NULL IF @name = '' SELECT @name = NULL EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- check if login is a member of sysadmin group SELECT @is_sysadmin = 0 IF (EXISTS(SELECT [type] FROM sys.server_principals WHERE name = @name AND [type] IN (N'U', N'S'))) BEGIN EXEC @is_sysadmin = sp_sqlagent_is_srvrolemember N'sysadmin', @name -- check role membership END IF (@is_sysadmin = 1) BEGIN -- @name is sysadmin, it cannot be revoked from proxy -- issue a message and do nothing RAISERROR(14395, 10, -1, @name) END ELSE BEGIN --force case insensitive comparation for NT users SELECT @sid = SUSER_SID(@name, 0) IF @sid IS NULL -- then @name is a MSDB role SELECT @sid = sid FROM sys.database_principals WHERE name = @name --check parametrs validity IF (EXISTS(SELECT * FROM sysproxylogin WHERE proxy_id = @proxy_id AND ISNULL(sid, 0) = ISNULL(@sid, 0))) BEGIN DELETE FROM sysproxylogin WHERE proxy_id = @proxy_id AND ISNULL(sid, 0)= ISNULL(@sid, 0) END ELSE BEGIN RAISERROR(14523, -1, -1, @name, @proxy_name) RETURN(1) -- Failure END END RETURN(0) END go /**************************************************************/ /* sp_revoke_proxy_from_subsystem */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_revoke_proxy_from_subsystem...' IF (NOT OBJECT_ID(N'dbo.sp_revoke_proxy_from_subsystem', 'P') IS NULL) DROP PROCEDURE dbo.sp_revoke_proxy_from_subsystem go CREATE PROCEDURE dbo.sp_revoke_proxy_from_subsystem @proxy_id INT = NULL, @proxy_name sysname = NULL, -- must specify only one of above parameter to identify the proxyAS @subsystem_id INT = NULL, @subsystem_name sysname = NULL -- must specify only one of above parameter to identify the subsystem AS BEGIN DECLARE @retval INT DECLARE @proxy_account sysname SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @subsystem_name = LTRIM(RTRIM(@subsystem_name)) SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) -- Turn [nullable] empty string parameters into NULLs IF @subsystem_name = '' SELECT @subsystem_name = NULL IF @proxy_name = '' SELECT @proxy_name = NULL EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure EXECUTE @retval = sp_verify_subsystem_identifiers '@subsystem_name', '@subsystem_id', @subsystem_name OUTPUT, @subsystem_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure --check parametrs validity IF (EXISTS(SELECT * FROM sysproxysubsystem WHERE subsystem_id = @subsystem_id AND proxy_id = @proxy_id )) BEGIN DELETE FROM sysproxysubsystem WHERE subsystem_id = @subsystem_id AND proxy_id = @proxy_id END ELSE BEGIN RAISERROR(14600, -1, -1, @proxy_name, @subsystem_name) RETURN(1) -- Failure END RETURN(0) END go /**************************************************************/ /* SP_ENUM_PROXY_FOR_SUBSYSTEM */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_proxy_for_subsystem...' IF (NOT OBJECT_ID(N'dbo.sp_enum_proxy_for_subsystem', 'P') IS NULL) DROP PROCEDURE dbo.sp_enum_proxy_for_subsystem go CREATE PROCEDURE sp_enum_proxy_for_subsystem @proxy_id int = NULL, @proxy_name sysname = NULL, -- must specify only one of above parameter to identify the proxy or none @subsystem_id int = NULL, @subsystem_name sysname = NULL -- must specify only one of above parameter to identify the subsystem or none AS BEGIN DECLARE @retval INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @subsystem_name = LTRIM(RTRIM(@subsystem_name)) SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) -- Turn [nullable] empty string parameters into NULLs IF @subsystem_name = '' SELECT @subsystem_name = NULL IF @proxy_name = '' SELECT @proxy_name = NULL IF @proxy_name IS NOT NULL OR @proxy_id IS NOT NULL BEGIN EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END IF @subsystem_name IS NOT NULL OR @subsystem_id IS NOT NULL BEGIN EXECUTE @retval = sp_verify_subsystem_identifiers '@subsystem_name', '@subsystem_id', @subsystem_name OUTPUT, @subsystem_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END SELECT ps.subsystem_id AS subsystem_id, s.subsystem AS subsystem_name, ps.proxy_id AS proxy_id, p.name AS proxy_name FROM sysproxysubsystem ps JOIN sysproxies p ON ps.proxy_id = p.proxy_id JOIN syssubsystems s ON ps.subsystem_id = s.subsystem_id WHERE ISNULL(@subsystem_id, ps.subsystem_id) = ps.subsystem_id AND ISNULL(@proxy_id, ps.proxy_id ) = ps.proxy_id END go /**************************************************************/ /* sp_enum_login_for_proxy */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_login_for_proxy...' IF (NOT OBJECT_ID(N'dbo.sp_enum_login_for_proxy', 'P') IS NULL) DROP PROCEDURE dbo.sp_enum_login_for_proxy go CREATE PROCEDURE sp_enum_login_for_proxy @name NVARCHAR(256) = NULL, @proxy_id INT = NULL, @proxy_name sysname = NULL -- must specify only one of above parameter to identify the proxy or none AS BEGIN DECLARE @retval INT DECLARE @sid VARBINARY(85) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) SELECT @name = LTRIM(RTRIM(@name)) -- Turn [nullable] empty string parameters into NULLs IF @proxy_name = '' SELECT @proxy_name = NULL IF @name = '' SELECT @name = NULL IF @proxy_name IS NOT NULL OR @proxy_id IS NOT NULL BEGIN EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END IF (@name IS NOT NULL) AND --force case insensitive comparation for NT users (ISNULL(SUSER_SID(@name, 0), 0) = 0) AND (ISNULL(IS_SRVROLEMEMBER(@name), -1) = -1) AND (ISNULL(IS_MEMBER(@name), -1) = -1) BEGIN RAISERROR(14520, -1, -1, @name) RETURN(1) -- Failure END --force case insensitive comparation for NT users SELECT @sid = SUSER_SID(@name, 0) IF @sid IS NULL -- then @name is a MSDB role SELECT @sid = sid FROM sys.database_principals WHERE name = @name SELECT pl.proxy_id AS proxy_id, p.name AS proxy_name, pl.flags as flags, CASE pl.flags WHEN 0 THEN SUSER_SNAME(pl.sid) -- SQLLOGIN, NT USER/GROUP WHEN 1 THEN SUSER_SNAME(pl.sid) -- SQL fixed server role WHEN 2 THEN USER_NAME(msdb.dbo.get_principal_id(pl.sid)) -- MSDB role ELSE NULL -- should never be the case END AS name, pl.sid AS sid, msdb.dbo.get_principal_id(pl.sid) AS principal_id FROM sysproxylogin pl JOIN sysproxies p ON pl.proxy_id = p.proxy_id WHERE COALESCE(@proxy_id, pl.proxy_id, 0 ) = ISNULL(pl.proxy_id, 0) AND COALESCE(@sid, pl.sid, 0 ) = ISNULL(pl.sid, 0) END go /**************************************************************/ /* SP_SQLAGENT_GET_STARTUP_INFO */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_get_startup_info...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_sqlagent_get_startup_info') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_get_startup_info go CREATE PROCEDURE sp_sqlagent_get_startup_info AS BEGIN DECLARE @tbu INT SET NOCOUNT ON IF (ServerProperty('InstanceName') IS NULL) BEGIN EXECUTE @tbu = master.dbo.xp_qv '1338198028' END ELSE BEGIN DECLARE @instancename NVARCHAR(128) SELECT @instancename = CONVERT(NVARCHAR(128), ServerProperty('InstanceName')) EXECUTE @tbu = master.dbo.xp_qv '1338198028', @instancename END IF (@tbu < 0) SELECT @tbu = 0 SELECT 'msdb_70_compatible' = (SELECT CASE WHEN cmptlevel >= 70 THEN 1 ELSE 0 END FROM master.dbo.sysdatabases WHERE (name = 'msdb')), 'msdb_read_only' = DATABASEPROPERTY('msdb', 'IsReadOnly'), 'msdb_available' = CASE ISNULL(DATABASEPROPERTY('msdb', 'IsSingleUser'), 0) WHEN 0 THEN 1 ELSE 0 END & CASE ISNULL(DATABASEPROPERTY('msdb', 'IsDboOnly'), 0) WHEN 0 THEN 1 ELSE 0 END & CASE ISNULL(DATABASEPROPERTY('msdb', 'IsNotRecovered'), 0) WHEN 0 THEN 1 ELSE 0 END & CASE ISNULL(DATABASEPROPERTY('msdb', 'IsSuspect'), 0) WHEN 0 THEN 1 ELSE 0 END, 'case_sensitive_server' = CASE ISNULL((SELECT 1 WHERE 'a' = 'A'), 0) WHEN 1 THEN 0 ELSE 1 END, 'max_user_connection' = (SELECT value FROM master.dbo.syscurconfigs WHERE (config = 103)), 'sql_server_name' = CONVERT(sysname, SERVERPROPERTY('SERVERNAME')), 'tbu' = ISNULL(@tbu, 0), 'platform' = PLATFORM(), 'instance_name' = ISNULL(CONVERT(sysname, SERVERPROPERTY('INSTANCENAME')), 'MSSQLSERVER'), 'is_clustered' = CONVERT(INT, SERVERPROPERTY('ISCLUSTERED')) RETURN(0) -- Success END go /**************************************************************/ /* SP_SQLAGENT_HAS_SERVER_ACCESS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_has_server_access...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_sqlagent_has_server_access') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_has_server_access go CREATE PROCEDURE sp_sqlagent_has_server_access @login_name sysname = NULL, @is_sysadmin_member INT = NULL OUTPUT AS BEGIN DECLARE @has_server_access BIT DECLARE @is_sysadmin BIT DECLARE @actual_login_name sysname DECLARE @cachedate DATETIME SET NOCOUNT ON SELECT @cachedate = NULL -- remove expired entries from the cache DELETE msdb.dbo.syscachedcredentials WHERE DATEDIFF(MINUTE, cachedate, GETDATE()) >= 29 -- query the cache SELECT @is_sysadmin = is_sysadmin_member, @has_server_access = has_server_access, @cachedate = cachedate FROM msdb.dbo.syscachedcredentials WHERE login_name = @login_name AND DATEDIFF(MINUTE, cachedate, GETDATE()) < 29 IF (@cachedate IS NOT NULL) BEGIN -- no output variable IF (@is_sysadmin_member IS NULL) BEGIN -- Return result row SELECT has_server_access = @has_server_access, is_sysadmin = @is_sysadmin, actual_login_name = @login_name RETURN END ELSE BEGIN SELECT @is_sysadmin_member = @is_sysadmin RETURN END END -- select from cache -- Set defaults SELECT @has_server_access = 0 SELECT @is_sysadmin = 0 SELECT @actual_login_name = FORMATMESSAGE(14205) IF (@login_name IS NULL) BEGIN SELECT has_server_access = 1, is_sysadmin = IS_SRVROLEMEMBER(N'sysadmin'), actual_login_name = SUSER_SNAME() RETURN END IF (@login_name LIKE '%\%') BEGIN -- Handle the LocalSystem account ('NT AUTHORITY\SYSTEM') as a special case IF (UPPER(@login_name collate SQL_Latin1_General_CP1_CS_AS) = N'NT AUTHORITY\SYSTEM') BEGIN IF (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (UPPER(loginname collate SQL_Latin1_General_CP1_CS_AS) = N'BUILTIN\ADMINISTRATORS'))) BEGIN SELECT @has_server_access = hasaccess, @is_sysadmin = sysadmin, @actual_login_name = loginname FROM master.dbo.syslogins WHERE (UPPER(loginname collate SQL_Latin1_General_CP1_CS_AS) = N'BUILTIN\ADMINISTRATORS') END END ELSE BEGIN -- Check if the NT login has been explicitly denied access IF (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (loginname = @login_name) AND (denylogin = 1))) BEGIN SELECT @has_server_access = 0, @is_sysadmin = sysadmin, @actual_login_name = loginname FROM master.dbo.syslogins WHERE (loginname = @login_name) END ELSE BEGIN -- declare table variable for storing results DECLARE @xp_results TABLE ( account_name sysname COLLATE database_default NOT NULL PRIMARY KEY, type NVARCHAR(10) COLLATE database_default NOT NULL, privilege NVARCHAR(10) COLLATE database_default NOT NULL, mapped_login_name sysname COLLATE database_default NOT NULL, permission_path sysname COLLATE database_default NULL ) -- Call xp_logininfo to determine server access INSERT INTO @xp_results EXECUTE master.dbo.xp_logininfo @login_name SELECT @has_server_access = CASE COUNT(*) WHEN 0 THEN 0 ELSE 1 END FROM @xp_results SELECT @actual_login_name = mapped_login_name, @is_sysadmin = CASE UPPER(privilege collate SQL_Latin1_General_CP1_CS_AS) WHEN 'ADMIN' THEN 1 ELSE 0 END FROM @xp_results END END END ELSE BEGIN -- Standard login IF (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (loginname = @login_name))) BEGIN SELECT @has_server_access = hasaccess, @is_sysadmin = sysadmin, @actual_login_name = loginname FROM master.dbo.syslogins WHERE (loginname = @login_name) END END -- update the cache only if something is found IF (UPPER(@actual_login_name collate SQL_Latin1_General_CP1_CS_AS) <> '(UNKNOWN)') BEGIN DECLARE @TranCounter INT; SET @TranCounter = @@TRANCOUNT; IF @TranCounter > 0 BEGIN -- Procedure called when there is -- an active transaction. -- Create a savepoint to be able -- to roll back only the work done -- in the procedure if there is an -- error. SAVE TRANSACTION tran_sp_has_server_access; END ELSE BEGIN -- Procedure must start its own -- transaction. BEGIN TRANSACTION; END -- 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 IF @TranCounter = 0 BEGIN -- @TranCounter = 0 means no transaction was -- started before the procedure was called. -- The procedure must commit the transaction -- it started. COMMIT TRANSACTION; END END TRY BEGIN CATCH -- An error occurred, must determine -- which type of rollback will roll -- back only the work done in the -- procedure. IF @TranCounter = 0 BEGIN -- Transaction started in procedure. -- Roll back complete transaction. ROLLBACK TRANSACTION; END ELSE -- Transaction started before procedure -- called, do not roll back modifications -- made before the procedure was called. IF (XACT_STATE()) <> - 1 BEGIN -- If the transaction is still valid, just -- roll back to the savepoint set at the -- start of the stored procedure. ROLLBACK TRANSACTION tran_sp_has_server_access; END -- If the transaction is uncommitable, a rollback -- to the save point is not allowed because -- the savepoint rollback writes to the log. -- Just do nothing since it is not critical to update syscredential table END CATCH END IF (@is_sysadmin_member IS NULL) -- Return result row SELECT has_server_access = @has_server_access, is_sysadmin = @is_sysadmin, actual_login_name = @actual_login_name ELSE -- output variable only SELECT @is_sysadmin_member = @is_sysadmin END go /**************************************************************/ /* SP_SEM_ADD_MESSAGE [used by SEM only] */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sem_add_message...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_sem_add_message') AND (type = 'P'))) DROP PROCEDURE sp_sem_add_message go CREATE PROCEDURE sp_sem_add_message @msgnum INT = NULL, @severity SMALLINT = NULL, @msgtext NVARCHAR(255) = NULL, @lang sysname = NULL, -- Message language name @with_log VARCHAR(5) = 'FALSE', @replace VARCHAR(7) = NULL AS BEGIN DECLARE @retval INT DECLARE @language_name sysname SET NOCOUNT ON SET ROWCOUNT 1 SELECT @language_name = name FROM sys.syslanguages WHERE msglangid = (SELECT number FROM master.dbo.spt_values WHERE (type = 'LNG') AND (name = @lang)) SET ROWCOUNT 0 SELECT @language_name = ISNULL(@language_name, 'us_english') EXECUTE @retval = master.dbo.sp_addmessage @msgnum, @severity, @msgtext, @language_name, @with_log, @replace RETURN(@retval) END go /**************************************************************/ /* SP_SEM_DROP_MESSAGE [used by SEM only] */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sem_drop_message...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_sem_drop_message') AND (type = 'P'))) DROP PROCEDURE sp_sem_drop_message go CREATE PROCEDURE sp_sem_drop_message @msgnum int = NULL, @lang sysname = NULL -- Message language name AS BEGIN DECLARE @retval INT DECLARE @language_name sysname SET NOCOUNT ON SET ROWCOUNT 1 SELECT @language_name = name FROM sys.syslanguages WHERE msglangid = (SELECT number FROM master.dbo.spt_values WHERE (type = 'LNG') AND (name = @lang)) SET ROWCOUNT 0 SELECT @language_name = ISNULL(@language_name, 'us_english') EXECUTE @retval = master.dbo.sp_dropmessage @msgnum, @language_name RETURN(@retval) END go /**************************************************************/ /* SP_GET_MESSAGE_DESCRIPTION [used by SEM only] */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_message_description...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_get_message_description') AND (type = 'P'))) DROP PROCEDURE sp_get_message_description go CREATE PROCEDURE sp_get_message_description @error INT AS BEGIN IF EXISTS (SELECT * FROM master.dbo.sysmessages WHERE (error = @error) AND (msglangid = (SELECT msglangid FROM sys.syslanguages WHERE (langid = @@langid)))) SELECT description FROM master.dbo.sysmessages WHERE (error = @error) AND (msglangid = (SELECT msglangid FROM sys.syslanguages WHERE (langid = @@langid))) ELSE SELECT description FROM master.dbo.sysmessages WHERE (error = @error) AND (msglangid = 1033) END go /**************************************************************/ /* SP_SQLAGENT_GET_PERF_COUNTERS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_get_perf_counters...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_sqlagent_get_perf_counters') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_get_perf_counters go CREATE PROCEDURE sp_sqlagent_get_perf_counters @all_counters BIT = 0 AS BEGIN SET NOCOUNT ON IF (@all_counters = 0) BEGIN SELECT 'object_name' = RTRIM(SUBSTRING(spi1.object_name, 1, 50)), 'counter_name' = RTRIM(SUBSTRING(spi1.counter_name, 1, 50)), 'instance_name' = CASE spi1.instance_name WHEN N'' THEN NULL ELSE RTRIM(spi1.instance_name) END, 'value' = CASE spi1.cntr_type WHEN 537003008 -- A ratio THEN CONVERT(FLOAT, spi1.cntr_value) / (SELECT CASE spi2.cntr_value WHEN 0 THEN 1 ELSE spi2.cntr_value END FROM sys.dm_os_performance_counters spi2 WHERE (spi1.counter_name + ' ' = SUBSTRING(spi2.counter_name, 1, PATINDEX('% Base%', spi2.counter_name))) AND (spi1.instance_name = spi2.instance_name) AND (spi2.cntr_type = 1073939459)) ELSE spi1.cntr_value END, 'type' = spi1.cntr_type FROM sys.dm_os_performance_counters spi1, ( SELECT DISTINCT SUBSTRING(performance_condition, 1, CHARINDEX('|', performance_condition, PATINDEX('%_|_%', performance_condition) + 2) - 1) as performance_condition_s FROM msdb.dbo.sysalerts WHERE (performance_condition IS NOT NULL) AND ISNULL(event_id, 0) <> 8 -- exclude WMI events that reuse performance_condition field AND (enabled = 1) ) tmp WHERE (spi1.cntr_type <> 1073939459) -- Divisors AND (tmp.performance_condition_s = RTRIM(spi1.object_name) + '|' + RTRIM(spi1.counter_name)) END ELSE BEGIN SELECT 'object_name' = RTRIM(SUBSTRING(spi1.object_name, 1, 50)), 'counter_name' = RTRIM(SUBSTRING(spi1.counter_name, 1, 50)), 'instance_name' = CASE spi1.instance_name WHEN N'' THEN NULL ELSE RTRIM(spi1.instance_name) END, 'value' = CASE spi1.cntr_type WHEN 537003008 -- A ratio THEN CONVERT(FLOAT, spi1.cntr_value) / (SELECT CASE spi2.cntr_value WHEN 0 THEN 1 ELSE spi2.cntr_value END FROM sys.dm_os_performance_counters spi2 WHERE (spi1.counter_name + ' ' = SUBSTRING(spi2.counter_name, 1, PATINDEX('% Base%', spi2.counter_name))) AND (spi1.instance_name = spi2.instance_name) AND (spi2.cntr_type = 1073939459)) ELSE spi1.cntr_value END, 'type' = spi1.cntr_type FROM sys.dm_os_performance_counters spi1 WHERE (spi1.cntr_type <> 1073939459) -- Divisors END END GO /**************************************************************/ /* SP_SQLAGENT_NOTIFY */ /* */ /* NOTE: We define this procedure here instead of in the */ /* 'Support procedures' section because of the many */ /* other procedures that reference it. */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_notify...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_sqlagent_notify') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_notify go CREATE PROCEDURE sp_sqlagent_notify @op_type NCHAR(1), -- One of: J (Job action [refresh or start/stop]), -- S (Schedule action [refresh only]) -- A (Alert action [refresh only]), -- G (Re-cache all registry settings), -- D (Dump job [or job schedule] cache to errorlog) -- P (Force an immediate poll of the MSX) -- L (Cycle log file) -- T (Test WMI parameters (namespace and query)) @job_id UNIQUEIDENTIFIER = NULL, -- JobID (for OpTypes 'J', 'S' and 'D') @schedule_id INT = NULL, -- ScheduleID (for OpType 'S') @alert_id INT = NULL, -- AlertID (for OpType 'A') @action_type NCHAR(1) = NULL, -- For 'J' one of: R (Run - no service check), -- S (Start - with service check), -- I (Insert), -- U (Update), -- D (Delete), -- C (Stop [Cancel]) -- For 'S' or 'A' one of: I (Insert), -- U (Update), -- D (Delete) @error_flag INT = 1, -- Set to 0 to suppress the error from xp_sqlagent_notify if SQLServer agent is not running @wmi_namespace nvarchar(128) = NULL, @wmi_query nvarchar(512) = NULL AS BEGIN DECLARE @retval INT DECLARE @id_as_char VARCHAR(10) DECLARE @job_id_as_char VARCHAR(36) DECLARE @nt_user_name NVARCHAR(100) SET NOCOUNT ON SELECT @retval = 0 -- Success -- Make sure that we're dealing only with uppercase characters SELECT @op_type = UPPER(@op_type collate SQL_Latin1_General_CP1_CS_AS) SELECT @action_type = UPPER(@action_type collate SQL_Latin1_General_CP1_CS_AS) -- Verify operation code IF (CHARINDEX(@op_type, N'JSAGDPLT') = 0) BEGIN RAISERROR(14266, -1, -1, '@op_type', 'J, S, A, G, D, P, L, T') RETURN(1) -- Failure END -- Check the job id for those who use it IF (CHARINDEX(@op_type, N'JSD') <> 0) BEGIN IF (NOT ((@op_type = N'D' OR @op_type = N'S') AND (@job_id IS NULL))) -- For 'D' and 'S' job_id is optional BEGIN IF ((@job_id IS NULL) OR ((@action_type <> N'D') AND NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id)))) BEGIN SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) RAISERROR(14262, -1, -1, '@job_id', @job_id_as_char) RETURN(1) -- Failure END END END -- Verify 'job' action parameters IF (@op_type = N'J') BEGIN SELECT @alert_id = 0 IF (@schedule_id IS NULL) SELECT @schedule_id = 0 -- The schedule_id (if specified) is the start step IF ((CHARINDEX(@action_type, N'RS') <> 0) AND (@schedule_id <> 0)) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @schedule_id))) BEGIN SELECT @id_as_char = ISNULL(CONVERT(VARCHAR, @schedule_id), '(null)') RAISERROR(14262, -1, -1, '@schedule_id', @id_as_char) RETURN(1) -- Failure END END ELSE SELECT @schedule_id = 0 IF (CHARINDEX(@action_type, N'RSIUDC') = 0) BEGIN RAISERROR(14266, -1, -1, '@action_type', 'R, S, I, U, D, C') RETURN(1) -- Failure END END -- Verify 'schedule' action parameters IF (@op_type = N'S') BEGIN SELECT @alert_id = 0 IF (CHARINDEX(@action_type, N'IUD') = 0) BEGIN RAISERROR(14266, -1, -1, '@action_type', 'I, U, D') RETURN(1) -- Failure END IF ((@schedule_id IS NULL) OR ((@action_type <> N'D') AND NOT EXISTS (SELECT * FROM msdb.dbo.sysschedules WHERE (schedule_id = @schedule_id)))) BEGIN SELECT @id_as_char = ISNULL(CONVERT(VARCHAR, @schedule_id), '(null)') RAISERROR(14262, -1, -1, '@schedule_id', @id_as_char) RETURN(1) -- Failure END END -- Verify 'alert' action parameters IF (@op_type = N'A') BEGIN SELECT @job_id = 0x00 SELECT @schedule_id = 0 IF (CHARINDEX(@action_type, N'IUD') = 0) BEGIN RAISERROR(14266, -1, -1, '@action_type', 'I, U, D') RETURN(1) -- Failure END IF ((@alert_id IS NULL) OR ((@action_type <> N'D') AND NOT EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (id = @alert_id)))) BEGIN SELECT @id_as_char = ISNULL(CONVERT(VARCHAR, @alert_id), '(null)') RAISERROR(14262, -1, -1, '@alert_id', @id_as_char) RETURN(1) -- Failure END END -- Verify 'registry', 'job dump' and 'force MSX poll' and 'cycle log' action parameters IF (CHARINDEX(@op_type, N'GDPL') <> 0) BEGIN IF (@op_type <> N'D') SELECT @job_id = 0x00 SELECT @alert_id = 0 SELECT @schedule_id = 0 SELECT @action_type = NULL END -- Parameters are valid, so now check execution permissions... -- For anything except a job (or schedule) action the caller must be SysAdmin, DBO, or DB_Owner IF (@op_type NOT IN (N'J', N'S')) BEGIN IF NOT ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1) OR (UPPER(USER_NAME() collate SQL_Latin1_General_CP1_CS_AS) = N'DBO')) BEGIN RAISERROR(14260, -1, -1) RETURN(1) -- Failure END END -- For a Job Action the caller must be SysAdmin, DBO, DB_Owner, or the job owner IF (@op_type = N'J') BEGIN IF NOT ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1) OR (UPPER(USER_NAME() collate SQL_Latin1_General_CP1_CS_AS) = N'DBO') OR (EXISTS (SELECT * FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id)))) BEGIN RAISERROR(14252, -1, -1) RETURN(1) -- Failure END END --verify WMI parameters IF (@op_type = N'T') BEGIN SELECT @wmi_namespace = LTRIM(RTRIM(@wmi_namespace)) SELECT @wmi_query = LTRIM(RTRIM(@wmi_query)) IF (@wmi_namespace IS NULL) or (@wmi_query IS NULL) BEGIN RAISERROR(14508, 16, 1) RETURN(1) -- Failure END END -- Ok, let's do it... SELECT @nt_user_name = ISNULL(NT_CLIENT(), ISNULL(SUSER_SNAME(), FORMATMESSAGE(14205))) EXECUTE @retval = master.dbo.xp_sqlagent_notify @op_type, @job_id, @schedule_id, @alert_id, @action_type, @nt_user_name, @error_flag, @@trancount, @wmi_namespace, @wmi_query RETURN(@retval) END go /**************************************************************/ /* SP_IS_SQLAGENT_STARTING */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_is_sqlagent_starting...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_is_sqlagent_starting') AND (type = 'P'))) DROP PROCEDURE sp_is_sqlagent_starting go CREATE PROCEDURE sp_is_sqlagent_starting AS BEGIN DECLARE @retval INT SELECT @retval = 0 EXECUTE master.dbo.xp_sqlagent_is_starting @retval OUTPUT IF (@retval = 1) RAISERROR(14258, -1, -1) RETURN(@retval) END go /**************************************************************/ /* SP_VERIFY_JOB_IDENTIFIERS */ /* */ /* NOTE: We define this procedure here instead of in the */ /* 'Support procedures' section because of the many */ /* other procedures that reference it. */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_job_identifiers...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_job_identifiers') AND (type = 'P'))) DROP PROCEDURE sp_verify_job_identifiers go CREATE PROCEDURE sp_verify_job_identifiers @name_of_name_parameter VARCHAR(60), -- Eg. '@job_name' @name_of_id_parameter VARCHAR(60), -- Eg. '@job_id' @job_name sysname OUTPUT, -- Eg. 'My Job' @job_id UNIQUEIDENTIFIER OUTPUT, @sqlagent_starting_test VARCHAR(7) = 'TEST', -- By default we DO want to test if SQLServerAgent is running (caller should specify 'NO_TEST' if not desired) @owner_sid VARBINARY(85) = NULL OUTPUT AS BEGIN DECLARE @retval INT DECLARE @job_id_as_char VARCHAR(36) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter)) SELECT @name_of_id_parameter = LTRIM(RTRIM(@name_of_id_parameter)) SELECT @job_name = LTRIM(RTRIM(@job_name)) IF (@job_name = N'') SELECT @job_name = NULL IF ((@job_name IS NULL) AND (@job_id IS NULL)) OR ((@job_name IS NOT NULL) AND (@job_id IS NOT NULL)) BEGIN RAISERROR(14294, -1, -1, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- Check job id IF (@job_id IS NOT NULL) BEGIN SELECT @job_name = name, @owner_sid = owner_sid FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id) -- the view would take care of all the permissions issues. IF (@job_name IS NULL) BEGIN SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) RAISERROR(14262, -1, -1, '@job_id', @job_id_as_char) RETURN(1) -- Failure END END ELSE -- Check job name IF (@job_name IS NOT NULL) BEGIN -- Check if the job name is ambiguous IF ((SELECT COUNT(*) FROM msdb.dbo.sysjobs_view WHERE (name = @job_name)) > 1) BEGIN RAISERROR(14293, -1, -1, @job_name, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- The name is not ambiguous, so get the corresponding job_id (if the job exists) SELECT @job_id = job_id, @owner_sid = owner_sid FROM msdb.dbo.sysjobs_view WHERE (name = @job_name) -- the view would take care of all the permissions issues. IF (@job_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@job_name', @job_name) RETURN(1) -- Failure END END IF (@sqlagent_starting_test = 'TEST') BEGIN -- Finally, check if SQLServerAgent is in the process of starting and if so prevent the -- calling SP from running EXECUTE @retval = msdb.dbo.sp_is_sqlagent_starting IF (@retval <> 0) RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_SCHEDULE_IDENTIFIERS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_schedule_identifiers...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_schedule_identifiers') AND (type = 'P'))) DROP PROCEDURE sp_verify_schedule_identifiers go CREATE PROCEDURE sp_verify_schedule_identifiers @name_of_name_parameter VARCHAR(60), -- Eg. '@schedule_name' @name_of_id_parameter VARCHAR(60), -- Eg. '@schedule_id' @schedule_name sysname OUTPUT, @schedule_id INT OUTPUT, @owner_sid VARBINARY(85) OUTPUT, @orig_server_id INT OUTPUT, @job_id_filter UNIQUEIDENTIFIER = NULL AS BEGIN DECLARE @retval INT DECLARE @schedule_id_as_char VARCHAR(36) DECLARE @sch_name_count INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter)) SELECT @name_of_id_parameter = LTRIM(RTRIM(@name_of_id_parameter)) SELECT @schedule_name = LTRIM(RTRIM(@schedule_name)) SELECT @sch_name_count = 0 IF (@schedule_name = N'') SELECT @schedule_name = NULL IF ((@schedule_name IS NULL) AND (@schedule_id IS NULL)) OR ((@schedule_name IS NOT NULL) AND (@schedule_id IS NOT NULL)) BEGIN RAISERROR(14373, -1, -1, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- Check schedule id IF (@schedule_id IS NOT NULL) BEGIN -- if Agent is calling look in all schedules not just the local server schedules if(PROGRAM_NAME() LIKE N'SQLAgent%') BEGIN -- Look at all schedules SELECT @schedule_name = name, @owner_sid = owner_sid, @orig_server_id = originating_server_id FROM msdb.dbo.sysschedules WHERE (schedule_id = @schedule_id) END ELSE BEGIN --Look at local schedules only SELECT @schedule_name = name, @owner_sid = owner_sid, @orig_server_id = originating_server_id FROM msdb.dbo.sysschedules_localserver_view WHERE (schedule_id = @schedule_id) END IF (@schedule_name IS NULL) BEGIN --If the schedule is from an MSX and a sysadmin is calling report a specific 'MSX' message IF(PROGRAM_NAME() NOT LIKE N'SQLAgent%' AND ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 AND EXISTS(SELECT * FROM msdb.dbo.sysschedules as sched JOIN msdb.dbo.sysoriginatingservers_view as svr ON sched.originating_server_id = svr.originating_server_id WHERE (schedule_id = @schedule_id) AND (svr.master_server = 1))) BEGIN RAISERROR(14274, -1, -1) END ELSE BEGIN SELECT @schedule_id_as_char = CONVERT(VARCHAR(36), @schedule_id) RAISERROR(14262, -1, -1, '@schedule_id', @schedule_id_as_char) END RETURN(1) -- Failure END END ELSE -- Check job name IF (@schedule_name IS NOT NULL) BEGIN -- if a job_id is supplied use it as a filter. This helps with V8 legacy support IF(@job_id_filter IS NOT NULL) BEGIN -- Check if the job name is ambiguous and also get the schedule_id optimistically. -- If the name is not ambiguous this gets the corresponding schedule_id (if the schedule exists) SELECT @sch_name_count = COUNT(*), @schedule_id = MIN(s.schedule_id), @owner_sid = MIN(owner_sid), @orig_server_id = MIN(originating_server_id) FROM msdb.dbo.sysschedules_localserver_view as s JOIN msdb.dbo.sysjobschedules as js ON s.schedule_id = js.schedule_id WHERE (name = @schedule_name) AND (js.job_id = @job_id_filter) END ELSE BEGIN -- Check if the job name is ambiguous from the count(*) result -- If the name is not ambiguous it is safe use the fields returned by the MIN() function SELECT @sch_name_count = COUNT(*), @schedule_id = MIN(schedule_id), @owner_sid = MIN(owner_sid), @orig_server_id = MIN(originating_server_id) FROM msdb.dbo.sysschedules_localserver_view WHERE (name = @schedule_name) END IF(@sch_name_count > 1) BEGIN -- ambiguous, user needs to use a schedule_id instead of a schedule_name RAISERROR(14371, -1, -1, @schedule_name, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END --schedule_id isn't visible to this user or doesn't exist IF (@schedule_id IS NULL) BEGIN --If the schedule is from an MSX and a sysadmin is calling report a specific 'MSX' message IF(PROGRAM_NAME() NOT LIKE N'SQLAgent%' AND ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 AND EXISTS(SELECT * FROM msdb.dbo.sysschedules as sched JOIN msdb.dbo.sysoriginatingservers_view as svr ON sched.originating_server_id = svr.originating_server_id JOIN msdb.dbo.sysjobschedules as js ON sched.schedule_id = js.schedule_id WHERE (svr.master_server = 1) AND (name = @schedule_name) AND ((@job_id_filter IS NULL) OR (js.job_id = @job_id_filter)))) BEGIN RAISERROR(14274, -1, -1) END ELSE BEGIN --If not a MSX schedule raise local error RAISERROR(14262, -1, -1, '@schedule_name', @schedule_name) END RETURN(1) -- Failure END END RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_JOBPROC_CALLER */ /* */ /* NOTE: We define this procedure here instead of in the */ /* 'Support procedures' section because of the many */ /* other procedures that reference it. */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_jobproc_caller...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_jobproc_caller') AND (type = 'P'))) DROP PROCEDURE sp_verify_jobproc_caller go CREATE PROCEDURE sp_verify_jobproc_caller @job_id UNIQUEIDENTIFIER, @program_name sysname AS BEGIN SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @program_name = LTRIM(RTRIM(@program_name)) IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id) AND (master_server = 1) )) -- master_server = 1 filters on MSX jobs in this TSX server AND (PROGRAM_NAME() NOT LIKE @program_name) BEGIN RAISERROR(14274, -1, -1) RETURN(1) -- Failure END RETURN(0) END go /**************************************************************/ /* SP_DOWNLOADED_ROW_LIMITER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_downloaded_row_limiter...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_downloaded_row_limiter') AND (type = 'P'))) DROP PROCEDURE dbo.sp_downloaded_row_limiter go CREATE PROCEDURE sp_downloaded_row_limiter @server_name sysname -- Target server name AS BEGIN -- This trigger controls how many downloaded (status = 1) sysdownloadlist rows exist -- for any given server. It does NOT control the absolute number of rows in the table. DECLARE @current_rows_per_server INT DECLARE @max_rows_per_server INT -- This value comes from the resgistry (DownloadedMaxRows) DECLARE @rows_to_delete INT DECLARE @rows_to_delete_as_char VARCHAR(10) 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 SELECT @rows_to_delete_as_char = CONVERT(VARCHAR, @rows_to_delete) IF (@rows_to_delete > 0) BEGIN SELECT @quoted_server_name = QUOTENAME(@server_name, '''') EXECUTE ('DECLARE @new_oldest_id INT SET NOCOUNT ON SET ROWCOUNT ' + @rows_to_delete_as_char + 'SELECT @new_oldest_id = instance_id FROM msdb.dbo.sysdownloadlist WHERE (target_server = N' + @quoted_server_name + ')' + ' AND (status = 1) ORDER BY instance_id SET ROWCOUNT 0 DELETE FROM msdb.dbo.sysdownloadlist WHERE (target_server = N' + @quoted_server_name + ')' + ' AND (instance_id <= @new_oldest_id) AND (status = 1)') END END go /**************************************************************/ /* SP_POST_MSX_OPERATION */ /* */ /* NOTE: We define this procedure here instead of in the */ /* 'Support procedures' section because of the many */ /* other procedures that reference it. */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_post_msx_operation...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_post_msx_operation') AND (type = 'P'))) DROP PROCEDURE sp_post_msx_operation go CREATE PROCEDURE sp_post_msx_operation @operation VARCHAR(64), @object_type VARCHAR(64) = 'JOB',-- Can be JOB, SERVER or SCHEDULE @job_id UNIQUEIDENTIFIER = NULL, -- NOTE: 0x00 means 'ALL' jobs @specific_target_server sysname = NULL, @value INT = NULL, -- For polling interval value @schedule_uid UNIQUEIDENTIFIER = NULL -- schedule_uid if the @object_type = 'SCHEDULE' AS BEGIN DECLARE @operation_code INT DECLARE @specific_target_server_id INT DECLARE @instructions_posted INT DECLARE @job_id_as_char VARCHAR(36) DECLARE @schedule_uid_as_char VARCHAR(36) DECLARE @msx_time_zone_adjustment INT DECLARE @local_machine_name sysname DECLARE @retval INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @operation = LTRIM(RTRIM(@operation)) SELECT @object_type = LTRIM(RTRIM(@object_type)) SELECT @specific_target_server = LTRIM(RTRIM(@specific_target_server)) -- Turn [nullable] empty string parameters into NULLs IF (@specific_target_server = N'') SELECT @specific_target_server = NULL -- Only a sysadmin can do this, but fail silently for a non-sysadmin IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) RETURN(0) -- Success (or more accurately a no-op) -- Check operation SELECT @operation = UPPER(@operation collate SQL_Latin1_General_CP1_CS_AS) SELECT @operation_code = CASE @operation WHEN 'INSERT' THEN 1 WHEN 'UPDATE' THEN 2 WHEN 'DELETE' THEN 3 WHEN 'START' THEN 4 WHEN 'STOP' THEN 5 WHEN 'RE-ENLIST' THEN 6 WHEN 'DEFECT' THEN 7 WHEN 'SYNC-TIME' THEN 8 WHEN 'SET-POLL' THEN 9 ELSE 0 END IF (@operation_code = 0) BEGIN RAISERROR(14266, -1, -1, '@operation_code', 'INSERT, UPDATE, DELETE, START, STOP, RE-ENLIST, DEFECT, SYNC-TIME, SET-POLL') RETURN(1) -- Failure END -- Check object type (in 9.0 only 'JOB', 'SERVER' or 'SCHEDULE'are valid) IF ((@object_type <> 'JOB') AND (@object_type <> 'SERVER') AND (@object_type <> 'SCHEDULE')) BEGIN RAISERROR(14266, -1, -1, '@object_type', 'JOB, SERVER, SCHEDULE') RETURN(1) -- Failure END -- Check that for a object type of JOB a job_id has been supplied IF ((@object_type = 'JOB') AND (@job_id IS NULL)) BEGIN RAISERROR(14233, -1, -1) RETURN(1) -- Failure END -- Check that for a object type of JOB a job_id has been supplied IF ((@object_type = 'SCHEDULE') AND (@schedule_uid IS NULL)) BEGIN RAISERROR(14365, -1, -1) RETURN(1) -- Failure END -- Check polling interval value IF (@operation_code = 9) AND ((ISNULL(@value, 0) < 10) OR (ISNULL(@value, 0) > 28800)) BEGIN RAISERROR(14266, -1, -1, '@value', '10..28800') RETURN(1) -- Failure END -- Check specific target server IF (@specific_target_server IS NOT NULL) BEGIN SELECT @specific_target_server = UPPER(@specific_target_server) -- Check if the local server is being targeted IF (@specific_target_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) BEGIN RETURN(0) END ELSE BEGIN SELECT @specific_target_server_id = server_id FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @specific_target_server) IF (@specific_target_server_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@specific_target_server', @specific_target_server) RETURN(1) -- Failure END END END -- Check that this server is an MSX server IF ((SELECT COUNT(*) FROM msdb.dbo.systargetservers) = 0) BEGIN RETURN(0) END -- Get local machine name EXECUTE @retval = master.dbo.xp_getnetname @local_machine_name OUTPUT IF (@retval <> 0) OR (@local_machine_name IS NULL) BEGIN RAISERROR(14225, -1, -1) RETURN(1) END -- Job-specific processing... IF (@object_type = 'JOB') BEGIN -- Validate the job (if supplied) IF (@job_id <> CONVERT(UNIQUEIDENTIFIER, 0x00)) BEGIN SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) -- Check if the job exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id))) BEGIN RAISERROR(14262, -1, -1, '@job_id', @job_id_as_char) RETURN(1) -- Failure END -- If this is a local job then there's nothing for us to do IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) -- 0 means local server OR (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id))) BEGIN RETURN(0) END END -- Generate the sysdownloadlist row(s)... IF (@operation_code = 1) OR -- Insert (@operation_code = 2) OR -- Update (@operation_code = 3) OR -- Delete (@operation_code = 4) OR -- Start (@operation_code = 5) -- Stop BEGIN IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) -- IE. 'ALL' BEGIN -- All jobs -- Handle DELETE as a special case (rather than posting 1 instruction per job we just -- post a single instruction that means 'delete all jobs from the MSX') IF (@operation_code = 3) BEGIN INSERT INTO msdb.dbo.sysdownloadlist (source_server, operation_code, object_type, object_id, target_server) SELECT @local_machine_name, @operation_code, 1, -- 1 means 'JOB' CONVERT(UNIQUEIDENTIFIER, 0x00), sts.server_name FROM systargetservers sts WHERE ((@specific_target_server_id IS NULL) OR (sts.server_id = @specific_target_server_id)) AND ((SELECT COUNT(*) FROM msdb.dbo.sysjobservers WHERE (server_id = sts.server_id)) > 0) SELECT @instructions_posted = @@rowcount END ELSE BEGIN INSERT INTO msdb.dbo.sysdownloadlist (source_server, operation_code, object_type, object_id, target_server) SELECT @local_machine_name, @operation_code, 1, -- 1 means 'JOB' sjv.job_id, sts.server_name FROM sysjobs_view sjv, sysjobservers sjs, systargetservers sts WHERE (sjv.job_id = sjs.job_id) AND (sjs.server_id = sts.server_id) AND (sjs.server_id <> 0) -- We want to exclude local jobs AND ((@specific_target_server_id IS NULL) OR (sjs.server_id = @specific_target_server_id)) SELECT @instructions_posted = @@rowcount END END ELSE BEGIN -- Specific job (ie. @job_id is not 0x00) INSERT INTO msdb.dbo.sysdownloadlist (source_server, operation_code, object_type, object_id, target_server, deleted_object_name) SELECT @local_machine_name, @operation_code, 1, -- 1 means 'JOB' sjv.job_id, sts.server_name, CASE @operation_code WHEN 3 -- Delete THEN sjv.name ELSE NULL END FROM sysjobs_view sjv, sysjobservers sjs, systargetservers sts WHERE (sjv.job_id = @job_id) AND (sjv.job_id = sjs.job_id) AND (sjs.server_id = sts.server_id) AND (sjs.server_id <> 0) -- We want to exclude local jobs AND ((@specific_target_server_id IS NULL) OR (sjs.server_id = @specific_target_server_id)) SELECT @instructions_posted = @@rowcount END END ELSE BEGIN RAISERROR(14266, -1, -1, '@operation_code', 'INSERT, UPDATE, DELETE, START, STOP') RETURN(1) -- Failure END END -- SCHEDULE specific processing for INSERT, UPDATE or DELETE schedule operations -- All msx jobs that use the specified @schedule_uid will be notified with an Insert operation. -- This will cause agent to reload all schedules for each job. -- This is compatible with the legacy shiloh servers that don't know about reusable schedules IF (@object_type = 'SCHEDULE') BEGIN -- Validate the schedule -- Check if the schedule exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysschedules_localserver_view WHERE (schedule_uid = @schedule_uid))) BEGIN SELECT @schedule_uid_as_char = CONVERT(VARCHAR(36), @schedule_uid) RAISERROR(14262, -1, -1, '@schedule_uid', @schedule_uid_as_char) RETURN(1) -- Failure END -- If this schedule is only used locally (no target servers) then there's nothing to do IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysschedules s, msdb.dbo.sysjobschedules js, msdb.dbo.sysjobs_view sjv, msdb.dbo.sysjobservers sjs, msdb.dbo.systargetservers sts WHERE (s.schedule_uid = @schedule_uid) AND (s.schedule_id = js.schedule_id) AND (sjv.job_id = js.job_id) AND (sjv.job_id = sjs.job_id) AND (sjs.server_id = sts.server_id) AND (sjs.server_id <> 0))) BEGIN RETURN(0) END -- Generate the sysdownloadlist row(s)... IF (@operation_code = 1) OR -- Insert (@operation_code = 2) OR -- Update (@operation_code = 3) -- Delete BEGIN -- Insert specific schedule into sysdownloadlist -- We need to create a sysdownloadlist JOB INSERT record for each job that runs the schedule INSERT INTO msdb.dbo.sysdownloadlist (source_server, operation_code, object_type, object_id, target_server) SELECT @local_machine_name, 1, -- 1 means 'Insert' 1, -- 1 means 'JOB' sjv.job_id, sts.server_name FROM msdb.dbo.sysschedules s, msdb.dbo.sysjobschedules js, msdb.dbo.sysjobs_view sjv, msdb.dbo.sysjobservers sjs, systargetservers sts WHERE (s.schedule_id = js.schedule_id) AND (js.job_id = sjv.job_id) AND (sjv.job_id = sjs.job_id) AND (sjs.server_id = sts.server_id) AND (s.schedule_uid = @schedule_uid) AND (sjs.server_id <> 0) -- We want to exclude local jobs AND ((@specific_target_server_id IS NULL) OR (sjs.server_id = @specific_target_server_id)) SELECT @instructions_posted = @@rowcount END ELSE BEGIN RAISERROR(14266, -1, -1, '@operation_code', 'UPDATE, DELETE') RETURN(1) -- Failure END END -- Server-specific processing... IF (@object_type = 'SERVER') BEGIN -- Generate the sysdownloadlist row(s)... IF (@operation_code = 6) OR -- ReEnlist (@operation_code = 7) OR -- Defect (@operation_code = 8) OR -- Synchronize time (with MSX) (@operation_code = 9) -- Set MSX polling interval (in seconds) BEGIN IF (@operation_code = 8) BEGIN EXECUTE master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SYSTEM\CurrentControlSet\Control\TimeZoneInformation', N'Bias', @msx_time_zone_adjustment OUTPUT, N'no_output' SELECT @msx_time_zone_adjustment = -ISNULL(@msx_time_zone_adjustment, 0) END INSERT INTO msdb.dbo.sysdownloadlist (source_server, operation_code, object_type, object_id, target_server) SELECT @local_machine_name, @operation_code, 2, -- 2 means 'SERVER' CASE @operation_code WHEN 8 THEN CONVERT(UNIQUEIDENTIFIER, CONVERT(BINARY(16), -(@msx_time_zone_adjustment - sts.time_zone_adjustment))) WHEN 9 THEN CONVERT(UNIQUEIDENTIFIER, CONVERT(BINARY(16), @value)) ELSE CONVERT(UNIQUEIDENTIFIER, 0x00) END, sts.server_name FROM systargetservers sts WHERE ((@specific_target_server_id IS NULL) OR (sts.server_id = @specific_target_server_id)) SELECT @instructions_posted = @@rowcount END ELSE BEGIN RAISERROR(14266, -1, -1, '@operation_code', 'RE-ENLIST, DEFECT, SYNC-TIME, SET-POLL') RETURN(1) -- Failure END END -- Report number of rows inserted IF (@object_type = 'JOB') AND (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) AND (@instructions_posted = 0) AND (@specific_target_server_id IS NOT NULL) RAISERROR(14231, 0, 1, '@specific_target_server', @specific_target_server) ELSE RAISERROR(14230, 0, 1, @instructions_posted, @operation) -- Delete any [downloaded] instructions that are over the registry-defined limit IF (@specific_target_server IS NOT NULL) EXECUTE msdb.dbo.sp_downloaded_row_limiter @specific_target_server RETURN(0) -- 0 means success END go /**************************************************************/ /* SP_VERIFY_PERFORMANCE_CONDITION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_performance_condition...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_performance_condition') AND (type = 'P'))) DROP PROCEDURE sp_verify_performance_condition go CREATE PROCEDURE sp_verify_performance_condition @performance_condition NVARCHAR(512) AS BEGIN DECLARE @delimiter_count INT DECLARE @temp_str NVARCHAR(512) DECLARE @object_name sysname DECLARE @counter_name sysname DECLARE @instance_name sysname DECLARE @pos INT SET NOCOUNT ON -- The performance condition must have the format 'object|counter|instance|comparator|value' -- NOTE: 'instance' may be empty. IF (PATINDEX(N'%_|%_|%|[><=]|[0-9]%', @performance_condition) = 0) BEGIN RAISERROR(14507, 16, 1) RETURN(1) -- Failure END -- Parse the performance_condition SELECT @delimiter_count = 0 SELECT @temp_str = @performance_condition SELECT @pos = CHARINDEX(N'|', @temp_str) WHILE (@pos <> 0) BEGIN SELECT @delimiter_count = @delimiter_count + 1 IF (@delimiter_count = 1) SELECT @object_name = SUBSTRING(@temp_str, 1, @pos - 1) IF (@delimiter_count = 2) SELECT @counter_name = SUBSTRING(@temp_str, 1, @pos - 1) IF (@delimiter_count = 3) SELECT @instance_name = SUBSTRING(@temp_str, 1, @pos - 1) SELECT @temp_str = SUBSTRING(@temp_str, @pos + 1, (DATALENGTH(@temp_str) / 2) - @pos) SELECT @pos = CHARINDEX(N'|', @temp_str) END IF (@delimiter_count <> 4) BEGIN RAISERROR(14507, 16, 1) RETURN(1) -- Failure END -- Check the object_name IF (NOT EXISTS (SELECT * FROM master.dbo.sysperfinfo WHERE (object_name = @object_name))) BEGIN RAISERROR(14262, 16, 1, 'object_name', @object_name) RETURN(1) -- Failure END -- Check the counter_name IF (NOT EXISTS (SELECT * FROM master.dbo.sysperfinfo WHERE (object_name = @object_name) AND (counter_name = @counter_name))) BEGIN RAISERROR(14262, 16, 1, 'counter_name', @counter_name) RETURN(1) -- Failure END -- Check the instance_name IF (@instance_name IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM master.dbo.sysperfinfo WHERE (object_name = @object_name) AND (counter_name = @counter_name) AND (instance_name = @instance_name))) BEGIN RAISERROR(14262, 16, 1, 'instance_name', @instance_name) RETURN(1) -- Failure END END RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_JOB_DATE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_job_date...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_job_date') AND (type = 'P'))) DROP PROCEDURE sp_verify_job_date go CREATE PROCEDURE sp_verify_job_date @date INT, @date_name VARCHAR(60) = 'date', @error_severity INT = -1 AS BEGIN SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @date_name = LTRIM(RTRIM(@date_name)) IF ((ISDATE(CONVERT(VARCHAR, @date)) = 0) OR (@date < 19900101) OR (@date > 99991231)) BEGIN RAISERROR(14266, @error_severity, -1, @date_name, '19900101..99991231') RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_JOB_TIME */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_job_time...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_job_time') AND (type = 'P'))) DROP PROCEDURE sp_verify_job_time go CREATE PROCEDURE sp_verify_job_time @time INT, @time_name VARCHAR(60) = 'time', @error_severity INT = -1 AS BEGIN DECLARE @hour INT DECLARE @minute INT DECLARE @second INT DECLARE @part_name NVARCHAR(50) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @time_name = LTRIM(RTRIM(@time_name)) IF ((@time < 0) OR (@time > 235959)) BEGIN RAISERROR(14266, @error_severity, -1, @time_name, '000000..235959') RETURN(1) -- Failure END SELECT @hour = (@time / 10000) SELECT @minute = (@time % 10000) / 100 SELECT @second = (@time % 100) -- Check hour range IF (@hour > 23) BEGIN SELECT @part_name = FORMATMESSAGE(14218) RAISERROR(14287, @error_severity, -1, @time_name, @part_name) RETURN(1) -- Failure END -- Check minute range IF (@minute > 59) BEGIN SELECT @part_name = FORMATMESSAGE(14219) RAISERROR(14287, @error_severity, -1, @time_name, @part_name) RETURN(1) -- Failure END -- Check second range IF (@second > 59) BEGIN SELECT @part_name = FORMATMESSAGE(14220) RAISERROR(14287, @error_severity, -1, @time_name, @part_name) RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_ALERT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_alert...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_alert') AND (type = 'P'))) DROP PROCEDURE sp_verify_alert go CREATE PROCEDURE sp_verify_alert @name sysname, @message_id INT, @severity INT, @enabled TINYINT, @delay_between_responses INT, @notification_message NVARCHAR(512), @include_event_description_in TINYINT, @database_name sysname, @event_description_keyword NVARCHAR(100), @job_id UNIQUEIDENTIFIER OUTPUT, @job_name sysname OUTPUT, @occurrence_count INT, @raise_snmp_trap TINYINT, @performance_condition NVARCHAR(512), @category_name sysname, @category_id INT OUTPUT, @count_reset_date INT, @count_reset_time INT, @wmi_namespace NVARCHAR(512), -- New for 9.0 @wmi_query NVARCHAR(512), -- New for 9.0 @event_id INT OUTPUT -- New for 9.0 AS BEGIN DECLARE @retval INT DECLARE @non_alertable_errors VARCHAR(512) DECLARE @message_id_as_string VARCHAR(10) DECLARE @res_valid_range NVARCHAR(100) DECLARE @alert_no_wmi_check INT DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @notification_message = LTRIM(RTRIM(@notification_message)) SELECT @database_name = LTRIM(RTRIM(@database_name)) SELECT @event_description_keyword = LTRIM(RTRIM(@event_description_keyword)) SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @performance_condition = LTRIM(RTRIM(@performance_condition)) SELECT @category_name = LTRIM(RTRIM(@category_name)) SELECT @alert_no_wmi_check = 0 -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check if the NewName is unique IF (EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (name = @name))) BEGIN RAISERROR(14261, 16, 1, '@name', @name) RETURN(1) -- Failure END -- Check if the user has supplied MessageID OR Severity OR Performance-Condition OR WMI namespace/query IF ((@performance_condition IS NULL) AND (@message_id = 0) AND (@severity = 0) AND ((@wmi_namespace IS NULL) OR (@wmi_query IS NULL))) OR ((@performance_condition IS NOT NULL) AND ((@message_id <> 0) OR (@severity <> 0) OR (@wmi_namespace IS NOT NULL) OR (@wmi_query IS NOT NULL))) OR ((@message_id <> 0) AND ((@performance_condition IS NOT NULL) OR (@severity <> 0) OR (@wmi_namespace IS NOT NULL) OR (@wmi_query IS NOT NULL))) OR ((@severity <> 0) AND ((@performance_condition IS NOT NULL) OR (@message_id <> 0) OR (@wmi_namespace IS NOT NULL) OR (@wmi_query IS NOT NULL))) BEGIN RAISERROR(14500, 16, 1) RETURN(1) -- Failure END -- Check the Severity IF ((@severity < 0) OR (@severity > 25)) BEGIN RAISERROR(14266, 16, 1, '@severity', '0..25') RETURN(1) -- Failure END -- Check the MessageID IF (@message_id <> 0) AND (NOT EXISTS (SELECT error FROM master.dbo.sysmessages WHERE (error = @message_id))) BEGIN SELECT @message_id_as_string = CONVERT(VARCHAR, @message_id) RAISERROR(14262, 16, 1, '@message_id', @message_id_as_string) RETURN(1) -- Failure END -- Check if it is legal to set an alert on this MessageID DECLARE @TempRetVal TABLE (RetVal INT) EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'NonAlertableErrors', @non_alertable_errors OUTPUT, N'no_output' IF (ISNULL(@non_alertable_errors, N'NULL') <> N'NULL') BEGIN DECLARE @message_id_as_char VARCHAR(10) SELECT @message_id_as_char = CONVERT(VARCHAR(10), @message_id) INSERT INTO @TempRetVal EXECUTE ('IF (' + @message_id_as_char + ' IN (' + @non_alertable_errors + ')) SELECT 1') END IF (EXISTS (SELECT * FROM @TempRetVal)) BEGIN RAISERROR(14506, 16, 1, @message_id) RETURN(1) -- Failure END -- Enabled must be 0 or 1 IF (@enabled NOT IN (0, 1)) BEGIN RAISERROR(14266, 16, 1, '@enabled', '0, 1') RETURN(1) -- Failure END -- DelayBetweenResponses must be > 0 IF (@delay_between_responses < 0) BEGIN SELECT @res_valid_range = FORMATMESSAGE(14206) RAISERROR(14266, 16, 1, '@delay_between_responses', @res_valid_range) RETURN(1) -- Failure END -- NOTE: We don't check the notification message -- Check IncludeEventDescriptionIn IF ((@include_event_description_in < 0) OR (@include_event_description_in > 7)) BEGIN SELECT @res_valid_range = FORMATMESSAGE(14208) RAISERROR(14266, 16, 1, '@include_event_description_in', @res_valid_range) RETURN(1) -- Failure END -- Check the database name IF (@database_name IS NOT NULL) AND (DB_ID(@database_name) IS NULL) BEGIN RAISERROR(15010, 16, 1, @database_name) RETURN(1) -- Failure END -- NOTE: We don't check the event description keyword -- Check JobName/ID IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL)) BEGIN -- We use '' as a special value which means 'no job' (we cannot use NULL since this forces -- sp_update_alert to use the existing value) IF (@job_name = N'') SELECT @job_id = 0x00 ELSE BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReaderRole and SQLAgentOperatorRole can see all jobs but -- cannot modify them IF (@job_owner_sid <> SUSER_SID() -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) -- is not sysadmin BEGIN RAISERROR(14525, -1, -1); RETURN(1) -- Failure END -- Check that the job is a local job IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN RAISERROR(14527, -1, -1, @job_name) RETURN(1) -- Failure END END END -- OccurrenceCount must be > 0 IF (@occurrence_count < 0) BEGIN RAISERROR(14266, 16, 1, '@occurrence_count', '0..n') RETURN(1) -- Failure END -- RaiseSNMPTrap must be 0 or 1 IF (@raise_snmp_trap NOT IN (0, 1)) BEGIN RAISERROR(14266, 16, 1, '@raise_snmp_trap', '0, 1') RETURN(1) -- Failure END -- Check the performance condition (including invalid parameter combinations) IF (@performance_condition IS NOT NULL) BEGIN IF (@database_name IS NOT NULL) BEGIN RAISERROR(14505, 16, 1, '@database_name') RETURN(1) -- Failure END IF (@event_description_keyword IS NOT NULL) BEGIN RAISERROR(14505, 16, 1, '@event_description_keyword') RETURN(1) -- Failure END IF (@wmi_namespace IS NOT NULL) BEGIN RAISERROR(14505, 16, 1, '@wmi_namespace') RETURN(1) -- Failure END IF (@wmi_query IS NOT NULL) BEGIN RAISERROR(14505, 16, 1, '@wmi_query') RETURN(1) -- Failure END -- Verify the performance condition EXECUTE @retval = msdb.dbo.sp_verify_performance_condition @performance_condition IF (@retval <> 0) RETURN(1) -- Failure END -- Check category name IF (@category_name = N'[DEFAULT]') SELECT @category_id = 98 ELSE BEGIN SELECT @category_id = category_id FROM msdb.dbo.syscategories WHERE (category_class = 2) -- Alerts AND (category_type = 3) -- None AND (name = @category_name) END IF (@category_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@category_name', @category_name) RETURN(1) -- Failure END -- Check count reset date IF (@count_reset_date <> 0) BEGIN EXECUTE @retval = msdb.dbo.sp_verify_job_date @count_reset_date, '@count_reset_date' IF (@retval <> 0) RETURN(1) -- Failure END -- Check count reset time IF (@count_reset_time <> 0) BEGIN EXECUTE @retval = msdb.dbo.sp_verify_job_time @count_reset_time, '@count_reset_time' IF (@retval <> 0) RETURN(1) -- Failure END -- Check WMI parameters. Both must exist IF (@wmi_namespace IS NOT NULL) BEGIN IF (@wmi_query IS NULL) BEGIN RAISERROR(14509, 16, 1, '@wmi_query') RETURN(1) -- Failure END IF (@database_name IS NOT NULL) BEGIN RAISERROR(14510, 16, 1, '@database_name') RETURN(1) -- Failure END IF (@event_description_keyword IS NOT NULL) BEGIN RAISERROR(14510, 16, 1, '@event_description_keyword') RETURN(1) -- Failure END --do not check WMI properties if a registry setting is present EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertNoWmiCheck', @alert_no_wmi_check OUTPUT, 'no_output' if (@alert_no_wmi_check <> 1) BEGIN EXECUTE @retval = msdb.dbo.sp_sqlagent_notify @op_type = N'T', @wmi_namespace = @wmi_namespace, @wmi_query = @wmi_query, @error_flag = 0 IF (@retval <> 0) BEGIN RAISERROR(14511, 16, 1) RETURN(1) -- Failure END END -- Set event_id to indicate WMI alert SELECT @event_id = 8 END ELSE IF (@wmi_query IS NOT NULL) BEGIN RAISERROR(14512, 16, 1, '@wmi_namespace') RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_UPDATE_ALERT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_alert...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_alert') AND (type = 'P'))) DROP PROCEDURE sp_update_alert go CREATE PROCEDURE sp_update_alert @name sysname, @new_name sysname = NULL, @enabled TINYINT = NULL, @message_id INT = NULL, @severity INT = NULL, @delay_between_responses INT = NULL, @notification_message NVARCHAR(512) = NULL, @include_event_description_in TINYINT = NULL, -- 0 = None, 1 = Email, 2 = Pager. 4 = NetSend, 7 = All @database_name sysname = NULL, @event_description_keyword NVARCHAR(100) = NULL, @job_id UNIQUEIDENTIFIER = NULL, -- If provided must NOT also provide job_name @job_name sysname = NULL, -- If provided must NOT also provide job_id @occurrence_count INT = NULL, -- Can only be set to 0 @count_reset_date INT = NULL, @count_reset_time INT = NULL, @last_occurrence_date INT = NULL, -- Can only be set to 0 @last_occurrence_time INT = NULL, -- Can only be set to 0 @last_response_date INT = NULL, -- Can only be set to 0 @last_response_time INT = NULL, -- Can only be set to 0 @raise_snmp_trap TINYINT = NULL, @performance_condition NVARCHAR(512) = NULL, -- New for 7.0 @category_name sysname = NULL, -- New for 7.0 @wmi_namespace sysname = NULL, -- New for 9.0 @wmi_query NVARCHAR(512) = NULL -- New for 9.0 AS BEGIN DECLARE @x_enabled TINYINT DECLARE @x_message_id INT DECLARE @x_severity INT DECLARE @x_delay_between_responses INT DECLARE @x_notification_message NVARCHAR(512) DECLARE @x_include_event_description TINYINT DECLARE @x_database_name sysname DECLARE @x_event_description_keyword NVARCHAR(100) DECLARE @x_occurrence_count INT DECLARE @x_count_reset_date INT DECLARE @x_count_reset_time INT DECLARE @x_last_occurrence_date INT DECLARE @x_last_occurrence_time INT DECLARE @x_last_response_date INT DECLARE @x_last_response_time INT DECLARE @x_flags INT DECLARE @x_performance_condition NVARCHAR(512) DECLARE @x_job_id UNIQUEIDENTIFIER DECLARE @x_category_id INT DECLARE @x_event_id INT DECLARE @x_wmi_namespace sysname DECLARE @x_wmi_query NVARCHAR(512) DECLARE @include_event_desc_code TINYINT DECLARE @return_code INT DECLARE @duplicate_name sysname DECLARE @category_id INT DECLARE @alert_id INT DECLARE @cached_attribute_modified INT DECLARE @event_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @new_name = LTRIM(RTRIM(@new_name)) SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @notification_message = LTRIM(RTRIM(@notification_message)) SELECT @database_name = LTRIM(RTRIM(@database_name)) SELECT @event_description_keyword = LTRIM(RTRIM(@event_description_keyword)) SELECT @performance_condition = LTRIM(RTRIM(@performance_condition)) SELECT @category_name = LTRIM(RTRIM(@category_name)) -- Are we modifying an attribute which SQLServerAgent caches? IF ((@new_name IS NOT NULL) OR (@enabled IS NOT NULL) OR (@message_id IS NOT NULL) OR (@severity IS NOT NULL) OR (@delay_between_responses IS NOT NULL) OR (@notification_message IS NOT NULL) OR (@include_event_description_in IS NOT NULL) OR (@database_name IS NOT NULL) OR (@event_description_keyword IS NOT NULL) OR (@job_id IS NOT NULL) OR (@job_name IS NOT NULL) OR (@last_response_date IS NOT NULL) OR (@last_response_time IS NOT NULL) OR (@raise_snmp_trap IS NOT NULL) OR (@performance_condition IS NOT NULL) OR (@wmi_namespace IS NOT NULL) OR (@wmi_query IS NOT NULL)) SELECT @cached_attribute_modified = 1 ELSE SELECT @cached_attribute_modified = 0 -- Map a job_id of 0 to the real value we use to mean 'no job' IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) AND (@job_name IS NULL) SELECT @job_name = N'' -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) END -- Check if SQLServerAgent is in the process of starting EXECUTE @return_code = msdb.dbo.sp_is_sqlagent_starting IF (@return_code <> 0) RETURN(1) -- Failure -- Check if this Alert exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (name = @name))) BEGIN RAISERROR(14262, 16, 1, '@name', @name) RETURN(1) END -- Certain values (if supplied) may only be updated to 0 IF (@occurrence_count <> 0) BEGIN RAISERROR(14266, -1, -1, '@occurrence_count', '0') RETURN(1) -- Failure END IF (@last_occurrence_date <> 0) BEGIN RAISERROR(14266, -1, -1, '@last_occurrence_date', '0') RETURN(1) -- Failure END IF (@last_occurrence_time <> 0) BEGIN RAISERROR(14266, -1, -1, '@last_occurrence_time', '0') RETURN(1) -- Failure END IF (@last_response_date <> 0) BEGIN RAISERROR(14266, -1, -1, '@last_response_date', '0') RETURN(1) -- Failure END IF (@last_response_time <> 0) BEGIN RAISERROR(14266, -1, -1, '@last_response_time', '0') RETURN(1) -- Failure END -- Get existing (@x_) values SELECT @alert_id = id, @x_enabled = enabled, @x_message_id = message_id, @x_severity = severity, @x_delay_between_responses = delay_between_responses, @x_notification_message = notification_message, @x_include_event_description = include_event_description, @x_database_name = database_name, @x_event_description_keyword = event_description_keyword, @x_occurrence_count = occurrence_count, @x_count_reset_date = count_reset_date, @x_count_reset_time = count_reset_time, @x_job_id = job_id, @x_last_occurrence_date = last_occurrence_date, @x_last_occurrence_time = last_occurrence_time, @x_last_response_date = last_response_date, @x_last_response_time = last_response_time, @x_flags = flags, @x_performance_condition = performance_condition, @x_category_id = category_id, @x_event_id = event_id FROM msdb.dbo.sysalerts WHERE (name = @name) SELECT @x_job_id = sjv.job_id FROM msdb.dbo.sysalerts sa, msdb.dbo.sysjobs_view sjv WHERE (sa.job_id = sjv.job_id) AND (sa.name = @name) -- Fill out the values for all non-supplied parameters from the existsing values IF (@x_event_id = 8) BEGIN -- WMI alert type IF (@wmi_namespace IS NULL) SELECT @wmi_namespace = @x_database_name IF (@wmi_query IS NULL) SELECT @wmi_query = @x_performance_condition END ELSE BEGIN -- Non-WMI alert type IF (@database_name IS NULL) SELECT @database_name = @x_database_name IF (@performance_condition IS NULL) SELECT @performance_condition = @x_performance_condition END IF (@enabled IS NULL) SELECT @enabled = @x_enabled IF (@message_id IS NULL) SELECT @message_id = @x_message_id IF (@severity IS NULL) SELECT @severity = @x_severity IF (@delay_between_responses IS NULL) SELECT @delay_between_responses = @x_delay_between_responses IF (@notification_message IS NULL) SELECT @notification_message = @x_notification_message IF (@include_event_description_in IS NULL) SELECT @include_event_description_in = @x_include_event_description IF (@event_description_keyword IS NULL) SELECT @event_description_keyword = @x_event_description_keyword IF (@job_id IS NULL) AND (@job_name IS NULL) SELECT @job_id = @x_job_id IF (@occurrence_count IS NULL) SELECT @occurrence_count = @x_occurrence_count IF (@count_reset_date IS NULL) SELECT @count_reset_date = @x_count_reset_date IF (@count_reset_time IS NULL) SELECT @count_reset_time = @x_count_reset_time IF (@last_occurrence_date IS NULL) SELECT @last_occurrence_date = @x_last_occurrence_date IF (@last_occurrence_time IS NULL) SELECT @last_occurrence_time = @x_last_occurrence_time IF (@last_response_date IS NULL) SELECT @last_response_date = @x_last_response_date IF (@last_response_time IS NULL) SELECT @last_response_time = @x_last_response_time IF (@raise_snmp_trap IS NULL) SELECT @raise_snmp_trap = @x_flags & 0x1 IF (@category_name IS NULL) SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = @x_category_id) IF (@category_name IS NULL) BEGIN SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = 98) END -- Turn [nullable] empty string parameters into NULLs IF (@new_name = N'') SELECT @new_name = NULL IF (@notification_message = N'') SELECT @notification_message = NULL IF (@database_name = N'') SELECT @database_name = NULL IF (@event_description_keyword = N'') SELECT @event_description_keyword = NULL IF (@performance_condition = N'') SELECT @performance_condition = NULL IF (@wmi_namespace = N'') SELECT @wmi_namespace = NULL IF (@wmi_query = N'') SELECT @wmi_query = NULL -- Verify the Alert IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) SELECT @job_id = NULL EXECUTE @return_code = sp_verify_alert @new_name, @message_id, @severity, @enabled, @delay_between_responses, @notification_message, @include_event_description_in, @database_name, @event_description_keyword, @job_id OUTPUT, @job_name OUTPUT, @occurrence_count, @raise_snmp_trap, @performance_condition, @category_name, @category_id OUTPUT, @count_reset_date, @count_reset_time, @wmi_namespace, @wmi_query, @event_id OUTPUT IF (@return_code <> 0) RETURN(1) -- Failure -- If the user didn't supply a NewName, use the old one. -- NOTE: This must be done AFTER sp_verify_alert. IF (@new_name IS NULL) SELECT @new_name = @name -- Turn the 1st 'flags' bit on or off accordingly IF (@raise_snmp_trap = 0) SELECT @x_flags = @x_flags & 0xFFFE ELSE SELECT @x_flags = @x_flags | 0x0001 -- For WMI alerts replace -- database_name with wmi_namespace and -- performance_conditon with wmi_query -- so we can store them in those columns in sysalerts table IF (@event_id = 8) BEGIN SELECT @database_name = @wmi_namespace SELECT @performance_condition = @wmi_query END -- Check if this Alert already exists SELECT @duplicate_name = FORMATMESSAGE(14205) SELECT @duplicate_name = name FROM msdb.dbo.sysalerts WHERE ((event_id = 8) AND (ISNULL(performance_condition, N'') = ISNULL(@performance_condition, N'')) AND (ISNULL(database_name, N'') = ISNULL(@database_name, N''))) OR ((ISNULL(event_id,1) <> 8) AND (ISNULL(performance_condition, N'apples') = ISNULL(@performance_condition, N'oranges'))) OR ((performance_condition IS NULL) AND (message_id = @message_id) AND (severity = @severity) AND (ISNULL(database_name, N'') = ISNULL(@database_name, N'')) AND (ISNULL(event_description_keyword, N'') = ISNULL(@event_description_keyword, N''))) IF (@duplicate_name <> FORMATMESSAGE(14205) AND @duplicate_name <> @name) BEGIN RAISERROR(14501, 16, 1, @duplicate_name) RETURN(1) -- Failure END -- Finally, do the actual UPDATE UPDATE msdb.dbo.sysalerts SET name = @new_name, message_id = @message_id, severity = @severity, enabled = @enabled, delay_between_responses = @delay_between_responses, notification_message = @notification_message, include_event_description = @include_event_description_in, database_name = @database_name, event_description_keyword = @event_description_keyword, job_id = ISNULL(@job_id, CONVERT(UNIQUEIDENTIFIER, 0x00)), occurrence_count = @occurrence_count, count_reset_date = @count_reset_date, count_reset_time = @count_reset_time, last_occurrence_date = @last_occurrence_date, last_occurrence_time = @last_occurrence_time, last_response_date = @last_response_date, last_response_time = @last_response_time, flags = @x_flags, performance_condition = @performance_condition, category_id = @category_id, event_id = @event_id WHERE (name = @name) -- Notify SQLServerAgent of the change IF (@cached_attribute_modified = 1) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'U' RETURN(0) -- Success END go /**************************************************************/ /* SP_DELETE_JOB_REFERENCES */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_job_references...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_job_references') AND (type = 'P'))) DROP PROCEDURE sp_delete_job_references go CREATE PROCEDURE sp_delete_job_references @notify_sqlagent BIT = 1 AS BEGIN DECLARE @deleted_job_id UNIQUEIDENTIFIER DECLARE @task_id_as_char VARCHAR(10) DECLARE @job_is_cached INT DECLARE @alert_name sysname -- 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 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 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(512) DECLARE @current_msx_server sysname 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) SELECT @reassign_to_operator = 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_operator + 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_operator + 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_operator + N''', ' ELSE SELECT @cmd = @cmd + N'@notify_page_operator_name = N'''', @notify_level_page = 0, ' SELECT @cmd = SUBSTRING(@cmd, 1, (DATALENGTH(@cmd) / 2) - 2) EXECUTE (N'EXECUTE ' + @cmd) FETCH NEXT FROM jobs_referencing_this_operator INTO @job_id, @notify_email_operator_id, @notify_netsend_operator_id, @notify_page_operator_id END DEALLOCATE jobs_referencing_this_operator -- Finally, do the actual DELETE DELETE FROM msdb.dbo.sysoperators WHERE (id = @id) COMMIT TRANSACTION RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_MSX_DEFECT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_msx_defect...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_msx_defect') AND (type = 'P'))) DROP PROCEDURE sp_msx_defect go CREATE PROCEDURE sp_msx_defect @forced_defection BIT = 0 AS BEGIN DECLARE @current_msx_server sysname DECLARE @retval INT DECLARE @jobs_deleted INT DECLARE @polling_interval INT DECLARE @nt_user NVARCHAR(100) SET NOCOUNT ON -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END SELECT @retval = 0 SELECT @jobs_deleted = 0 -- Get the current MSX server name from the registry EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @current_msx_server OUTPUT, N'no_output' SELECT @current_msx_server = UPPER(LTRIM(RTRIM(@current_msx_server))) IF ((@current_msx_server IS NULL) OR (@current_msx_server = N'')) BEGIN RAISERROR(14298, -1, -1) RETURN(1) -- Failure END SELECT @nt_user = ISNULL(NT_CLIENT(), ISNULL(SUSER_SNAME(), FORMATMESSAGE(14205))) EXECUTE @retval = master.dbo.xp_msx_enlist 1, @current_msx_server, @nt_user IF (@retval <> 0) AND (@forced_defection = 0) RETURN(1) -- Failure -- Clear the MSXServerName registry entry EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', N'REG_SZ', N'' -- Delete the MSXPollingInterval registry entry EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXPollInterval', @polling_interval OUTPUT, N'no_output' IF (@polling_interval IS NOT NULL) EXECUTE master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXPollInterval' -- Remove the entry from sqlagent_info DELETE FROM msdb.dbo.sqlagent_info WHERE (attribute = N'DateEnlisted') -- Delete all the jobs that originated from the MSX -- NOTE: We can't use sp_delete_job here since sp_delete_job checks if the caller is -- SQLServerAgent (only SQLServerAgent can delete non-local jobs). EXECUTE msdb.dbo.sp_delete_all_msx_jobs @current_msx_server, @jobs_deleted OUTPUT RAISERROR(14227, 0, 1, @current_msx_server, @jobs_deleted) -- Now delete the old msx server record DELETE msdb.dbo.sysoriginatingservers WHERE (originating_server = @current_msx_server) AND (master_server = 1) -- If a forced defection was performed, attempt to notify the MSXOperator IF (@forced_defection = 1) BEGIN DECLARE @network_address NVARCHAR(100) DECLARE @command NVARCHAR(512) DECLARE @local_machine_name sysname DECLARE @res_warning NVARCHAR(300) SELECT @network_address = netsend_address FROM msdb.dbo.sysoperators WHERE (name = N'MSXOperator') IF (@network_address IS NOT NULL) BEGIN EXECUTE @retval = master.dbo.xp_getnetname @local_machine_name OUTPUT IF (@retval <> 0) RETURN(1) -- Failure SELECT @res_warning = FORMATMESSAGE(14217) SELECT @command = N'NET SEND ' + @network_address + N' ' + @res_warning SELECT @command = STUFF(@command, PATINDEX(N'%[%%]s%', @command), 2, NT_CLIENT()) SELECT @command = STUFF(@command, PATINDEX(N'%[%%]s%', @command), 2, @local_machine_name) EXECUTE master.dbo.xp_cmdshell @command, no_output END END -- Delete the 'MSXOperator' (must do this last) IF (EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (name = N'MSXOperator'))) EXECUTE msdb.dbo.sp_delete_operator @name = N'MSXOperator' RETURN(0) -- 0 means success END go /**************************************************************/ /* SP_MSX_ENLIST */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_msx_enlist...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_msx_enlist') AND (type = 'P'))) DROP PROCEDURE sp_msx_enlist go CREATE PROCEDURE sp_msx_enlist @msx_server_name sysname, @location NVARCHAR(100) = NULL -- The procedure will supply a default AS BEGIN DECLARE @current_msx_server sysname DECLARE @local_machine_name sysname DECLARE @msx_originating_server sysname DECLARE @retval INT DECLARE @time_zone_adjustment INT DECLARE @local_time NVARCHAR(100) DECLARE @nt_user NVARCHAR(100) DECLARE @poll_interval INT SET NOCOUNT ON -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Only an NT server can be enlisted IF ((PLATFORM() & 0x1) <> 0x1) -- NT BEGIN RAISERROR(14540, -1, 1) RETURN(1) -- Failure END -- Only SBS, Standard, or Enterprise editions of SQL Server can be enlisted IF ((PLATFORM() & 0x100) = 0x100) -- Desktop package BEGIN RAISERROR(14539, -1, -1) RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SELECT @msx_server_name = UPPER(LTRIM(RTRIM(@msx_server_name))) SELECT @location = LTRIM(RTRIM(@location)) SELECT @local_machine_name = UPPER(CONVERT(NVARCHAR(30), SERVERPROPERTY('ServerName'))) -- Turn [nullable] empty string parameters into NULLs IF (@location = N'') SELECT @location = NULL SELECT @retval = 0 -- Get the values that we'll need for the [re]enlistment operation (except the local time -- which we get right before we call xp_msx_enlist to that it's as accurate as possible) SELECT @nt_user = ISNULL(NT_CLIENT(), ISNULL(SUSER_SNAME(), FORMATMESSAGE(14205))) EXECUTE master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SYSTEM\CurrentControlSet\Control\TimeZoneInformation', N'Bias', @time_zone_adjustment OUTPUT, N'no_output' IF ((PLATFORM() & 0x1) = 0x1) -- NT SELECT @time_zone_adjustment = -ISNULL(@time_zone_adjustment, 0) ELSE SELECT @time_zone_adjustment = -CONVERT(INT, CONVERT(BINARY(2), ISNULL(@time_zone_adjustment, 0))) EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXPollInterval', @poll_interval OUTPUT, N'no_output' SELECT @poll_interval = ISNULL(@poll_interval, 60) -- This should be the same as DEF_REG_MSX_POLL_INTERVAL EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @current_msx_server OUTPUT, N'no_output' SELECT @current_msx_server = LTRIM(RTRIM(@current_msx_server)) -- Check if this machine is an MSX (and therefore cannot be enlisted into another MSX) IF (EXISTS (SELECT * FROM msdb.dbo.systargetservers)) BEGIN --Get local server/instance name RAISERROR(14299, -1, -1, @local_machine_name) RETURN(1) -- Failure END -- Check if the MSX supplied is the same as the local machine (this is not allowed) IF (UPPER(@local_machine_name) = @msx_server_name) BEGIN RAISERROR(14297, -1, -1) RETURN(1) -- Failure END -- Check if MSDB has be re-installed since we enlisted IF (@current_msx_server IS NOT NULL) AND (NOT EXISTS (SELECT * FROM msdb.dbo.sqlagent_info WHERE (attribute = 'DateEnlisted'))) BEGIN -- User is tring to [re]enlist after a re-install, so we have to forcefully defect before -- we can fully enlist again EXECUTE msdb.dbo.sp_msx_defect @forced_defection = 1 SELECT @current_msx_server = NULL END -- Check if we are already enlisted, in which case we re-enlist IF ((@current_msx_server IS NOT NULL) AND (@current_msx_server <> N'')) BEGIN IF (UPPER(@current_msx_server) = @msx_server_name) BEGIN -- Update the [existing] enlistment SELECT @local_time = CONVERT(NVARCHAR, GETDATE(), 112) + N' ' + CONVERT(NVARCHAR, GETDATE(), 108) EXECUTE @retval = master.dbo.xp_msx_enlist 2, @msx_server_name, @nt_user, @location, @time_zone_adjustment, @local_time, @poll_interval RETURN(@retval) -- 0 means success END ELSE BEGIN RAISERROR(14296, -1, -1, @current_msx_server) RETURN(1) -- Failure END END -- If we get this far then we're dealing with a new enlistment... -- If no location is supplied, generate one (such as we can) IF (@location IS NULL) EXECUTE msdb.dbo.sp_generate_server_description @location OUTPUT SELECT @local_time = CONVERT(NVARCHAR, GETDATE(), 112) + ' ' + CONVERT(NVARCHAR, GETDATE(), 108) EXECUTE @retval = master.dbo.xp_msx_enlist 0, @msx_server_name, @nt_user, @location, @time_zone_adjustment, @local_time, @poll_interval IF (@retval = 0) BEGIN EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', N'REG_SZ', @msx_server_name IF (@current_msx_server IS NOT NULL) RAISERROR(14228, 0, 1, @current_msx_server, @msx_server_name) ELSE RAISERROR(14229, 0, 1, @msx_server_name) -- Update the sysoriginatingservers table with the msx server name. May need to clean up if it already has an msx entry SELECT @msx_originating_server = NULL -- Get the msx server name SELECT @msx_originating_server = originating_server FROM msdb.dbo.sysoriginatingservers WHERE (master_server = 1) IF(@msx_originating_server IS NULL) BEGIN -- Good. No msx server found so just add the new one INSERT INTO msdb.dbo.sysoriginatingservers(originating_server, master_server) VALUES (@msx_server_name, 1) END ELSE BEGIN -- Found a previous entry. If it isn't the same server we need to clean up any existing msx jobs IF(@msx_originating_server != @msx_server_name) BEGIN INSERT INTO msdb.dbo.sysoriginatingservers(originating_server, master_server) VALUES (@msx_server_name, 1) -- Optimistically try and remove any msx jobs left over from the previous msx enlistment. EXECUTE msdb.dbo.sp_delete_all_msx_jobs @msx_originating_server -- And finally delete the old msx server record DELETE msdb.dbo.sysoriginatingservers WHERE (originating_server = @msx_originating_server) AND (master_server = 1) END END -- Add entry to sqlagent_info INSERT INTO msdb.dbo.sqlagent_info (attribute, value) VALUES ('DateEnlisted', CONVERT(VARCHAR(10), GETDATE(), 112)) END RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_DELETE_TARGETSERVER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_targetserver...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_targetserver') AND (type = 'P'))) DROP PROCEDURE sp_delete_targetserver go CREATE PROCEDURE sp_delete_targetserver @server_name sysname, @clear_downloadlist BIT = 1, @post_defection BIT = 1 AS BEGIN DECLARE @server_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name))) -- Check server name SELECT @server_id = server_id FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name) IF (@server_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END BEGIN TRANSACTION IF (@clear_downloadlist = 1) BEGIN DELETE FROM msdb.dbo.sysdownloadlist WHERE (target_server = @server_name) END IF (@post_defection = 1) BEGIN -- Post a defect instruction to the server -- NOTE: We must do this BEFORE deleting the systargetservers row EXECUTE msdb.dbo.sp_post_msx_operation 'DEFECT', 'SERVER', 0x00, @server_name END DELETE FROM msdb.dbo.systargetservers WHERE (server_id = @server_id) DELETE FROM msdb.dbo.systargetservergroupmembers WHERE (server_id = @server_id) DELETE FROM msdb.dbo.sysjobservers WHERE (server_id = @server_id) COMMIT TRANSACTION RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_ENLIST_TSX */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enlist_tsx' go IF EXISTS (SELECT name FROM sysobjects WHERE name = 'sp_enlist_tsx' AND type = 'P') DROP PROCEDURE sp_enlist_tsx GO create proc sp_enlist_tsx @Action int, -- 0 - enlist; 1 - defect; 2 - update @ServerName sysname, -- tsx server name @Location nvarchar(200), -- tsx server location @TimeZoneAdjustment int, -- tsx server time zone adjustment @LocalTime datetime, -- tsx server local time @NTUserName nvarchar(100), -- name of the user performing the enlistment @PollInterval int, -- polling interval @TSX_Version int = 0 -- VersionMajor: ((@TSX_Version / 0x1000000) & 0xff) -- VersionMinor: ((@TSX_Version / 0x10000) & 0xff) -- Build no: (@TSX_Version & 0xFFFF) as begin SET NOCOUNT ON /* check permissions */ IF (ISNULL(IS_MEMBER(N'TargetServersRole'), 0) = 0) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) begin raiserror(15003,-1,-1, N'TargetServersRole') return 1 end --9.0 and above servers set this version param if(@TSX_Version is null) set @TSX_Version = 0 --Only check version during enlistment if(@Action = 0 AND ((@TSX_Version / 0x1000000) & 0xff) < 9) begin DECLARE @majorVer int, @minorVer int, @buildNo int SELECT @majorVer = ((@@microsoftversion / 0x1000000) & 0xff), @minorVer = ((@@microsoftversion / 0x10000) & 0xff), @buildNo = (@@microsoftversion & 0xfff) raiserror(14306, -1, -1, @majorVer, @minorVer, @buildNo ) return 12 end /* check input parameters */ if @ServerName is null begin raiserror(14043, -1, -1, '@ServerName') return 2 end select @ServerName = LTRIM(@ServerName) select @ServerName = RTRIM(@ServerName) if @ServerName = '' begin raiserror(21263, -1, -1, '@ServerName') return 3 end select @ServerName = UPPER(@ServerName) if @Action <> 1 And @Action <> 2 begin /* default action is to enlist */ select @Action = 0 end if @Action = 0 /* enlisting */ begin /* check input parameters */ if @NTUserName is null begin raiserror(14043, -1, -1, '@NTUserName') return 4 end select @NTUserName = LTRIM(@NTUserName) select @NTUserName = RTRIM(@NTUserName) if @NTUserName = '' begin raiserror(21263, -1, -1, '@NTUserName') return 5 end /* check if local server is already configured as TSX machine */ declare @msx_server_name sysname select @msx_server_name = N'' execute master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @msx_server_name OUTPUT select @msx_server_name = LTRIM(@msx_server_name) select @msx_server_name = RTRIM(@msx_server_name) if @msx_server_name <> N'' begin raiserror(14360, -1, -1, @@SERVERNAME) return 6 end /* * check that local server is not running a desktop SKU, * i.e. Win9x, Office, or MSDE */ if( PLATFORM() & 0x100 = 0x100 ) begin raiserror(14362, -1, -1) return 8 end /* check if we have any MSXOperators defined */ if not exists (SELECT * FROM msdb.dbo.sysoperators WHERE name = N'MSXOperator') begin raiserror(14363, -1, -1) return 9 end /* all checks have passed, insert new row into systargetservers table */ INSERT INTO msdb.dbo.systargetservers ( server_name, location, time_zone_adjustment, enlist_date, last_poll_date, status, local_time_at_last_poll, enlisted_by_nt_user, poll_interval ) VALUES ( @ServerName, @Location, @TimeZoneAdjustment, GETDATE(), GETDATE(), 1, @LocalTime, @NTUserName, @PollInterval ) /* delete hanging rows from sysdownloadlist */ DELETE FROM msdb.dbo.sysdownloadlist WHERE target_server = @ServerName end if @Action = 2 /* updating existing enlistment */ begin /* check if we have any MSXOperators defined */ if not exists (SELECT * FROM msdb.dbo.sysoperators WHERE name = N'MSXOperator') begin raiserror(14363, -1, -1) return 10 end /* check if TSX machine is already enlisted */ If not exists (SELECT * FROM msdb.dbo.systargetservers WHERE UPPER(server_name) = @ServerName) begin raiserror(14364, -1, -1) return 11 end if @Location is null /* don't update the location if it is not supplied */ begin UPDATE msdb.dbo.systargetservers SET time_zone_adjustment = @TimeZoneAdjustment, poll_interval = @PollInterval WHERE (UPPER(server_name) = @ServerName) end else begin UPDATE msdb.dbo.systargetservers SET location = @Location, time_zone_adjustment = @TimeZoneAdjustment, poll_interval = @PollInterval WHERE (UPPER(server_name) = @ServerName) end end if @Action = 1 /* defecting */ begin if (exists (SELECT * FROM msdb.dbo.systargetservers WHERE UPPER(server_name) = @ServerName)) begin execute msdb.dbo.sp_delete_targetserver @server_name = @ServerName, @post_defection = 0 end else begin DELETE FROM msdb.dbo.sysdownloadlist WHERE (target_server = @ServerName) end end if @Action = 0 Or @Action = 2 /* enlisting or updating existing enlistment */ begin /* select resultset to return to the caller */ SELECT id, name, enabled, email_address, pager_address, netsend_address, weekday_pager_start_time, weekday_pager_end_time, saturday_pager_start_time, saturday_pager_end_time, sunday_pager_start_time, sunday_pager_end_time, pager_days FROM msdb.dbo.sysoperators WHERE (name = N'MSXOperator') end end go /**************************************************************/ /* SP_GET_SQLAGENT_PROPERTIES */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_sqlagent_properties...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_get_sqlagent_properties') AND (type = 'P'))) DROP PROCEDURE sp_get_sqlagent_properties go CREATE PROCEDURE sp_get_sqlagent_properties AS BEGIN DECLARE @auto_start INT DECLARE @startup_account NVARCHAR(100) DECLARE @msx_server_name sysname -- Non-SQLDMO exposed properties DECLARE @sqlserver_restart INT DECLARE @jobhistory_max_rows INT DECLARE @jobhistory_max_rows_per_job INT DECLARE @errorlog_file NVARCHAR(255) DECLARE @errorlogging_level INT DECLARE @error_recipient NVARCHAR(30) DECLARE @monitor_autostart INT DECLARE @local_host_server sysname DECLARE @job_shutdown_timeout INT DECLARE @cmdexec_account VARBINARY(64) DECLARE @regular_connections INT DECLARE @host_login_name sysname DECLARE @host_login_password VARBINARY(512) DECLARE @login_timeout INT DECLARE @idle_cpu_percent INT DECLARE @idle_cpu_duration INT DECLARE @oem_errorlog INT DECLARE @email_profile NVARCHAR(64) DECLARE @email_save_in_sent_folder INT DECLARE @cpu_poller_enabled INT DECLARE @alert_replace_runtime_tokens INT SET NOCOUNT ON -- NOTE: We return all SQLServerAgent properties at one go for performance reasons -- Read the values from the registry IF ((PLATFORM() & 0x1) = 0x1) -- NT BEGIN DECLARE @key NVARCHAR(200) SELECT @key = N'SYSTEM\CurrentControlSet\Services\' IF (SERVERPROPERTY('INSTANCENAME') IS NOT NULL) SELECT @key = @key + N'SQLAgent$' + CONVERT (sysname, SERVERPROPERTY('INSTANCENAME')) ELSE SELECT @key = @key + N'SQLServerAgent' EXECUTE master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', @key, N'Start', @auto_start OUTPUT, N'no_output' EXECUTE master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', @key, N'ObjectName', @startup_account OUTPUT, N'no_output' END ELSE BEGIN SELECT @auto_start = 3 -- Manual start SELECT @startup_account = NULL END EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @msx_server_name OUTPUT, N'no_output' -- Non-SQLDMO exposed properties EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'RestartSQLServer', @sqlserver_restart OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRows', @jobhistory_max_rows OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRowsPerJob', @jobhistory_max_rows_per_job OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ErrorLogFile', @errorlog_file OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ErrorLoggingLevel', @errorlogging_level OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ErrorMonitor', @error_recipient OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MonitorAutoStart', @monitor_autostart OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ServerHost', @local_host_server OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobShutdownTimeout', @job_shutdown_timeout OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'CmdExecAccount', @cmdexec_account OUTPUT, N'no_output' SET @regular_connections = 0 SET @host_login_name = NULL SET @host_login_password = NULL EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'LoginTimeout', @login_timeout OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'IdleCPUPercent', @idle_cpu_percent OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'IdleCPUDuration', @idle_cpu_duration OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'OemErrorLog', @oem_errorlog OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'EmailProfile', @email_profile OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'EmailSaveSent', @email_save_in_sent_folder OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertReplaceRuntimeTokens', @alert_replace_runtime_tokens OUTPUT, N'no_output' EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'CoreEngineMask', @cpu_poller_enabled OUTPUT, N'no_output' IF (@cpu_poller_enabled IS NOT NULL) SELECT @cpu_poller_enabled = CASE WHEN (@cpu_poller_enabled & 32) = 32 THEN 0 ELSE 1 END -- Return the values to the client SELECT auto_start = CASE @auto_start WHEN 2 THEN 1 -- 2 means auto-start WHEN 3 THEN 0 -- 3 means don't auto-start ELSE 0 -- Safety net END, msx_server_name = @msx_server_name, sqlagent_type = (SELECT CASE WHEN (COUNT(*) = 0) AND (ISNULL(DATALENGTH(@msx_server_name), 0) = 0) THEN 1 -- Standalone WHEN (COUNT(*) = 0) AND (ISNULL(DATALENGTH(@msx_server_name), 0) > 0) THEN 2 -- TSX WHEN (COUNT(*) > 0) AND (ISNULL(DATALENGTH(@msx_server_name), 0) = 0) THEN 3 -- MSX WHEN (COUNT(*) > 0) AND (ISNULL(DATALENGTH(@msx_server_name), 0) > 0) THEN 0 -- Multi-Level MSX (currently invalid) ELSE 0 -- Invalid END FROM msdb.dbo.systargetservers), startup_account = @startup_account, -- Non-SQLDMO exposed properties sqlserver_restart = ISNULL(@sqlserver_restart, 1), jobhistory_max_rows = @jobhistory_max_rows, jobhistory_max_rows_per_job = @jobhistory_max_rows_per_job, errorlog_file = @errorlog_file, errorlogging_level = ISNULL(@errorlogging_level, 7), error_recipient = @error_recipient, monitor_autostart = ISNULL(@monitor_autostart, 0), local_host_server = @local_host_server, job_shutdown_timeout = ISNULL(@job_shutdown_timeout, 15), cmdexec_account = @cmdexec_account, regular_connections = ISNULL(@regular_connections, 0), host_login_name = @host_login_name, host_login_password = @host_login_password, login_timeout = ISNULL(@login_timeout, 30), idle_cpu_percent = ISNULL(@idle_cpu_percent, 10), idle_cpu_duration = ISNULL(@idle_cpu_duration, 600), oem_errorlog = ISNULL(@oem_errorlog, 0), sysadmin_only = NULL, email_profile = @email_profile, email_save_in_sent_folder = ISNULL(@email_save_in_sent_folder, 0), cpu_poller_enabled = ISNULL(@cpu_poller_enabled, 0), alert_replace_runtime_tokens = ISNULL(@alert_replace_runtime_tokens, 0) END go /**************************************************************/ /* SP_SET_SQLAGENT_PROPERTIES */ /**************************************************************/ IF EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE name = N'sp_set_sqlagent_properties' AND type = 'P') BEGIN DROP PROCEDURE dbo.sp_set_sqlagent_properties END go PRINT '' PRINT 'Create procedure sp_set_sqlagent_properties...' go CREATE PROCEDURE dbo.sp_set_sqlagent_properties @auto_start INT = NULL, -- 1 or 0 -- Non-SQLDMO exposed properties @sqlserver_restart INT = NULL, -- 1 or 0 @jobhistory_max_rows INT = NULL, -- No maximum = -1, otherwise must be > 1 @jobhistory_max_rows_per_job INT = NULL, -- 1 to @jobhistory_max_rows @errorlog_file NVARCHAR(255) = NULL, -- Full drive\path\name of errorlog file @errorlogging_level INT = NULL, -- 1 = error, 2 = warning, 4 = information @error_recipient NVARCHAR(30) = NULL, -- Network address of error popup recipient @monitor_autostart INT = NULL, -- 1 or 0 @local_host_server sysname = NULL, -- Alias of local host server @job_shutdown_timeout INT = NULL, -- 5 to 600 seconds @cmdexec_account VARBINARY(64) = NULL, -- CmdExec account information @regular_connections INT = NULL, -- obsolete @host_login_name sysname = NULL, -- obsolete @host_login_password VARBINARY(512) = NULL, -- obsolete @login_timeout INT = NULL, -- 5 to 45 (seconds) @idle_cpu_percent INT = NULL, -- 1 to 100 @idle_cpu_duration INT = NULL, -- 20 to 86400 seconds @oem_errorlog INT = NULL, -- 1 or 0 @sysadmin_only INT = NULL, -- not applicable to Yukon server, for backwards compatibility only @email_profile NVARCHAR(64) = NULL, -- Email profile name @email_save_in_sent_folder INT = NULL, -- 1 or 0 @cpu_poller_enabled INT = NULL, -- 1 or 0 @alert_replace_runtime_tokens INT = NULL -- 1 or 0 AS BEGIN -- NOTE: We set all SQLServerAgent properties at one go for performance reasons. -- NOTE: You cannot set the value of the properties msx_server_name, is_msx or -- startup_account - they are all read only. DECLARE @res_valid_range NVARCHAR(100) DECLARE @existing_core_engine_mask INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @errorlog_file = LTRIM(RTRIM(@errorlog_file)) SELECT @error_recipient = LTRIM(RTRIM(@error_recipient)) SELECT @local_host_server = LTRIM(RTRIM(@local_host_server)) SELECT @host_login_name = LTRIM(RTRIM(@host_login_name)) SELECT @email_profile = LTRIM(RTRIM(@email_profile)) -- Make sure values (if supplied) are good IF (@auto_start IS NOT NULL) BEGIN -- NOTE: When setting the the services start value, 2 == auto-start, 3 == Don't auto-start SELECT @auto_start = CASE @auto_start WHEN 0 THEN 3 WHEN 1 THEN 2 ELSE 3 -- Assume non auto-start if passed a junk value END END -- Non-SQLDMO exposed properties IF ((@sqlserver_restart IS NOT NULL) AND (@sqlserver_restart <> 0)) SELECT @sqlserver_restart = 1 IF (@jobhistory_max_rows IS NOT NULL) BEGIN SELECT @res_valid_range = FORMATMESSAGE(14207) IF ((@jobhistory_max_rows < -1) OR (@jobhistory_max_rows = 0)) BEGIN RAISERROR(14266, -1, -1, '@jobhistory_max_rows', @res_valid_range) RETURN(1) -- Failure END END ELSE BEGIN EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRows', @jobhistory_max_rows OUTPUT, N'no_output' SELECT @jobhistory_max_rows = ISNULL(@jobhistory_max_rows, -1) END IF (@jobhistory_max_rows_per_job IS NOT NULL) BEGIN IF (@jobhistory_max_rows = -1) SELECT @jobhistory_max_rows_per_job = 0 ELSE BEGIN IF ((@jobhistory_max_rows_per_job < 1) OR (@jobhistory_max_rows_per_job > @jobhistory_max_rows)) BEGIN SELECT @res_valid_range = N'1..' + CONVERT(NVARCHAR, @jobhistory_max_rows) RAISERROR(14266, -1, -1, '@jobhistory_max_rows', @res_valid_range) RETURN(1) -- Failure END END END IF (@errorlogging_level IS NOT NULL) AND ((@errorlogging_level < 1) OR (@errorlogging_level > 7)) BEGIN RAISERROR(14266, -1, -1, '@errorlogging_level', '1..7') RETURN(1) -- Failure END IF (@monitor_autostart IS NOT NULL) AND ((@monitor_autostart < 0) OR (@monitor_autostart > 1)) BEGIN RAISERROR(14266, -1, -1, '@monitor_autostart', '0, 1') RETURN(1) -- Failure END IF (@job_shutdown_timeout IS NOT NULL) AND ((@job_shutdown_timeout < 5) OR (@job_shutdown_timeout > 600)) BEGIN RAISERROR(14266, -1, -1, '@job_shutdown_timeout', '5..600') RETURN(1) -- Failure END IF (@login_timeout IS NOT NULL) AND ((@login_timeout < 5) OR (@login_timeout > 45)) BEGIN RAISERROR(14266, -1, -1, '@login_timeout', '5..45') RETURN(1) -- Failure END IF ((@idle_cpu_percent IS NOT NULL) AND ((@idle_cpu_percent < 1) OR (@idle_cpu_percent > 100))) BEGIN RAISERROR(14266, -1, -1, '@idle_cpu_percent', '10..100') RETURN(1) -- Failure END IF ((@idle_cpu_duration IS NOT NULL) AND ((@idle_cpu_duration < 20) OR (@idle_cpu_duration > 86400))) BEGIN RAISERROR(14266, -1, -1, '@idle_cpu_duration', '20..86400') RETURN(1) -- Failure END IF (@oem_errorlog IS NOT NULL) AND ((@oem_errorlog < 0) OR (@oem_errorlog > 1)) BEGIN RAISERROR(14266, -1, -1, '@oem_errorlog', '0, 1') RETURN(1) -- Failure END IF (@sysadmin_only IS NOT NULL) BEGIN RAISERROR(14378, -1, -1) RETURN(1) -- Failure END IF (@email_save_in_sent_folder IS NOT NULL) AND ((@email_save_in_sent_folder < 0) OR (@email_save_in_sent_folder > 1)) BEGIN RAISERROR(14266, -1, -1, 'email_save_in_sent_folder', '0, 1') RETURN(1) -- Failure END IF (@cpu_poller_enabled IS NOT NULL) AND ((@cpu_poller_enabled < 0) OR (@cpu_poller_enabled > 1)) BEGIN RAISERROR(14266, -1, -1, 'cpu_poller_enabled', '0, 1') RETURN(1) -- Failure END IF (@alert_replace_runtime_tokens IS NOT NULL) AND ((@alert_replace_runtime_tokens < 0) OR (@alert_replace_runtime_tokens > 1)) BEGIN RAISERROR(14266, -1, -1, 'alert_replace_runtime_tokens', '0, 1') RETURN(1) -- Failure END -- Write out the values IF (@auto_start IS NOT NULL) BEGIN IF ((PLATFORM() & 0x1) = 0x1) -- NT BEGIN DECLARE @key NVARCHAR(200) SELECT @key = N'SYSTEM\CurrentControlSet\Services\' IF (SERVERPROPERTY('INSTANCENAME') IS NOT NULL) SELECT @key = @key + N'SQLAgent$' + CONVERT (sysname, SERVERPROPERTY('INSTANCENAME')) ELSE SELECT @key = @key + N'SQLServerAgent' EXECUTE master.dbo.xp_regwrite N'HKEY_LOCAL_MACHINE', @key, N'Start', N'REG_DWORD', @auto_start END ELSE RAISERROR(14546, 16, 1, '@auto_start') END -- Non-SQLDMO exposed properties IF (@sqlserver_restart IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'RestartSQLServer', N'REG_DWORD', @sqlserver_restart IF (@jobhistory_max_rows IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRows', N'REG_DWORD', @jobhistory_max_rows IF (@jobhistory_max_rows_per_job IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobHistoryMaxRowsPerJob', N'REG_DWORD', @jobhistory_max_rows_per_job IF (@errorlog_file IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ErrorLogFile', N'REG_SZ', @errorlog_file IF (@errorlogging_level IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ErrorLoggingLevel', N'REG_DWORD', @errorlogging_level IF (@error_recipient IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ErrorMonitor', N'REG_SZ', @error_recipient IF (@monitor_autostart IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MonitorAutoStart', N'REG_DWORD', @monitor_autostart IF (@local_host_server IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'ServerHost', N'REG_SZ', @local_host_server IF (@job_shutdown_timeout IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'JobShutdownTimeout', N'REG_DWORD', @job_shutdown_timeout IF (@cmdexec_account IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'CmdExecAccount', N'REG_BINARY', @cmdexec_account IF (@login_timeout IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'LoginTimeout', N'REG_DWORD', @login_timeout IF (@idle_cpu_percent IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'IdleCPUPercent', N'REG_DWORD', @idle_cpu_percent IF (@idle_cpu_duration IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'IdleCPUDuration', N'REG_DWORD', @idle_cpu_duration IF (@oem_errorlog IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'OemErrorLog', N'REG_DWORD', @oem_errorlog IF (@email_profile IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'EmailProfile', N'REG_SZ', @email_profile IF (@email_save_in_sent_folder IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'EmailSaveSent', N'REG_DWORD', @email_save_in_sent_folder IF (@alert_replace_runtime_tokens IS NOT NULL) EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertReplaceRuntimeTokens', N'REG_DWORD', @alert_replace_runtime_tokens IF (@cpu_poller_enabled IS NOT NULL) BEGIN EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'CoreEngineMask', @existing_core_engine_mask OUTPUT, N'no_output' IF ((@existing_core_engine_mask IS NOT NULL) OR (@cpu_poller_enabled = 1)) BEGIN IF (@cpu_poller_enabled = 1) SELECT @cpu_poller_enabled = (ISNULL(@existing_core_engine_mask, 0) & ~32) ELSE SELECT @cpu_poller_enabled = (ISNULL(@existing_core_engine_mask, 0) | 32) IF ((@existing_core_engine_mask IS NOT NULL) AND (@cpu_poller_enabled = 32)) EXECUTE master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'CoreEngineMask' ELSE EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'CoreEngineMask', N'REG_DWORD', @cpu_poller_enabled END END RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_TARGETSERVERGROUP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_targetservergroup...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_targetservergroup') AND (type = 'P'))) DROP PROCEDURE sp_add_targetservergroup go CREATE PROCEDURE sp_add_targetservergroup @name sysname AS BEGIN SET NOCOUNT ON -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) -- Check if the group already exists IF (EXISTS (SELECT * FROM msdb.dbo.systargetservergroups WHERE name = @name)) BEGIN RAISERROR(14261, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Disallow names with commas in them (since sp_apply_job_to_targets parses a comma-separated list of group names) IF (@name LIKE N'%,%') BEGIN RAISERROR(14289, -1, -1, '@name', ',') RETURN(1) -- Failure END INSERT INTO msdb.dbo.systargetservergroups (name) VALUES (@name) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_UPDATE_TARGETSERVERGROUP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_targetservergroup...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_targetservergroup') AND (type = 'P'))) DROP PROCEDURE sp_update_targetservergroup go CREATE PROCEDURE sp_update_targetservergroup @name sysname, @new_name sysname AS BEGIN SET NOCOUNT ON -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @new_name = LTRIM(RTRIM(@new_name)) -- Check if the group exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservergroups WHERE (name = @name))) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Check if a group with the new name already exists IF (EXISTS (SELECT * FROM msdb.dbo.systargetservergroups WHERE (name = @new_name))) BEGIN RAISERROR(14261, -1, -1, '@new_name', @new_name) RETURN(1) -- Failure END -- Disallow names with commas in them (since sp_apply_job_to_targets parses a comma-separated list of group names) IF (@new_name LIKE N'%,%') BEGIN RAISERROR(14289, -1, -1, '@new_name', ',') RETURN(1) -- Failure END -- Update the group's name UPDATE msdb.dbo.systargetservergroups SET name = @new_name WHERE (name = @name) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_DELETE_TARGETSERVERGROUP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_targetservergroup...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_targetservergroup') AND (type = 'P'))) DROP PROCEDURE sp_delete_targetservergroup go CREATE PROCEDURE sp_delete_targetservergroup @name sysname AS BEGIN DECLARE @servergroup_id INT SET NOCOUNT ON -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) -- Check if the group exists SELECT @servergroup_id = servergroup_id FROM msdb.dbo.systargetservergroups WHERE (name = @name) IF (@servergroup_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Remove the group members DELETE FROM msdb.dbo.systargetservergroupmembers WHERE (servergroup_id = @servergroup_id) -- Remove the group DELETE FROM msdb.dbo.systargetservergroups WHERE (name = @name) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_HELP_TARGETSERVERGROUP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_targetservergroup...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_targetservergroup') AND (type = 'P'))) DROP PROCEDURE sp_help_targetservergroup go CREATE PROCEDURE sp_help_targetservergroup @name sysname = NULL AS BEGIN DECLARE @servergroup_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) IF (@name IS NULL) BEGIN -- Show all groups SELECT servergroup_id, name FROM msdb.dbo.systargetservergroups RETURN(@@error) -- 0 means success END ELSE BEGIN -- Check if the group exists SELECT @servergroup_id = servergroup_id FROM msdb.dbo.systargetservergroups WHERE (name = @name) IF (@servergroup_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Return the members of the group SELECT sts.server_id, sts.server_name FROM msdb.dbo.systargetservers sts, msdb.dbo.systargetservergroupmembers stsgm WHERE (stsgm.servergroup_id = @servergroup_id) AND (stsgm.server_id = sts.server_id) RETURN(@@error) -- 0 means success END END go /**************************************************************/ /* SP_ADD_TARGETSVRGRP_MEMBER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_targetsvgrp_member...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_targetsvrgrp_member') AND (type = 'P'))) DROP PROCEDURE sp_add_targetsvrgrp_member go CREATE PROCEDURE sp_add_targetsvrgrp_member @group_name sysname, @server_name sysname AS BEGIN DECLARE @servergroup_id INT DECLARE @server_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @group_name = LTRIM(RTRIM(@group_name)) SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name))) -- Check if the group exists SELECT @servergroup_id = servergroup_id FROM msdb.dbo.systargetservergroups WHERE (name = @group_name) IF (@servergroup_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@group_name', @group_name) RETURN(1) -- Failure END -- Check if the server exists SELECT @server_id = server_id FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name) IF (@server_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END -- Check if the server is already in this group IF (EXISTS (SELECT * FROM msdb.dbo.systargetservergroupmembers WHERE (servergroup_id = @servergroup_id) AND (server_id = @server_id))) BEGIN RAISERROR(14263, -1, -1, @server_name, @group_name) RETURN(1) -- Failure END -- Add the row to systargetservergroupmembers INSERT INTO msdb.dbo.systargetservergroupmembers VALUES (@servergroup_id, @server_id) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_DELETE_TARGETSVRGRP_MEMBER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_targetsvrgrp_member...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_targetsvrgrp_member') AND (type = 'P'))) DROP PROCEDURE sp_delete_targetsvrgrp_member go CREATE PROCEDURE sp_delete_targetsvrgrp_member @group_name sysname, @server_name sysname AS BEGIN DECLARE @servergroup_id INT DECLARE @server_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @group_name = LTRIM(RTRIM(@group_name)) SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name))) -- Check if the group exists SELECT @servergroup_id = servergroup_id FROM msdb.dbo.systargetservergroups WHERE (name = @group_name) IF (@servergroup_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@group_name', @group_name) RETURN(1) -- Failure END -- Check if the server exists SELECT @server_id = server_id FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name) IF (@server_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END -- Check if the server is in the group IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservergroupmembers WHERE (servergroup_id = @servergroup_id) AND (server_id = @server_id))) BEGIN RAISERROR(14264, -1, -1, @server_name, @group_name) RETURN(1) -- Failure END -- Delete the row from systargetservergroupmembers DELETE FROM msdb.dbo.systargetservergroupmembers WHERE (servergroup_id = @servergroup_id) AND (server_id = @server_id) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_VERIFY_CATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_category...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_category') AND (type = 'P'))) DROP PROCEDURE sp_verify_category go CREATE PROCEDURE sp_verify_category @class VARCHAR(8), @type VARCHAR(12) = NULL, -- Supply NULL only if you don't want it checked @name sysname = NULL, -- Supply NULL only if you don't want it checked @category_class INT OUTPUT, @category_type INT OUTPUT -- Supply NULL only if you don't want the return value AS BEGIN SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @class = LTRIM(RTRIM(@class)) SELECT @type = LTRIM(RTRIM(@type)) SELECT @name = LTRIM(RTRIM(@name)) -- Turn [nullable] empty string parameters into NULLs IF (@type = '') SELECT @type = NULL IF (@name = N'') SELECT @name = NULL -- Check class SELECT @class = UPPER(@class collate SQL_Latin1_General_CP1_CS_AS) SELECT @category_class = CASE @class WHEN 'JOB' THEN 1 WHEN 'ALERT' THEN 2 WHEN 'OPERATOR' THEN 3 ELSE 0 END IF (@category_class = 0) BEGIN RAISERROR(14266, -1, -1, '@class', 'JOB, ALERT, OPERATOR') RETURN(1) -- Failure END -- Check name IF ((@name IS NOT NULL) AND (@name = N'[DEFAULT]')) BEGIN RAISERROR(14200, -1, -1, '@name') RETURN(1) -- Failure END -- Check type [optionally] IF (@type IS NOT NULL) BEGIN IF (@class = 'JOB') BEGIN SELECT @type = UPPER(@type collate SQL_Latin1_General_CP1_CS_AS) SELECT @category_type = CASE @type WHEN 'LOCAL' THEN 1 WHEN 'MULTI-SERVER' THEN 2 ELSE 0 END IF (@category_type = 0) BEGIN RAISERROR(14266, -1, -1, '@type', 'LOCAL, MULTI-SERVER') RETURN(1) -- Failure END END ELSE BEGIN IF (@type <> 'NONE') BEGIN RAISERROR(14266, -1, -1, '@type', 'NONE') RETURN(1) -- Failure END ELSE SELECT @category_type = 3 END END RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_CATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_category...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_category') AND (type = 'P'))) DROP PROCEDURE sp_add_category go CREATE PROCEDURE sp_add_category @class VARCHAR(8) = 'JOB', -- JOB or ALERT or OPERATOR @type VARCHAR(12) = 'LOCAL', -- LOCAL or MULTI-SERVER (for JOB) or NONE otherwise @name sysname AS BEGIN DECLARE @retval INT DECLARE @category_type INT DECLARE @category_class INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @class = LTRIM(RTRIM(@class)) SELECT @type = LTRIM(RTRIM(@type)) SELECT @name = LTRIM(RTRIM(@name)) EXECUTE @retval = sp_verify_category @class, @type, @name, @category_class OUTPUT, @category_type OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check name IF (EXISTS (SELECT * FROM msdb.dbo.syscategories WHERE (category_class = @category_class) AND (name = @name))) BEGIN RAISERROR(14261, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Add the row INSERT INTO msdb.dbo.syscategories (category_class, category_type, name) VALUES (@category_class, @category_type, @name) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_UPDATE_CATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_category...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_category') AND (type = 'P'))) DROP PROCEDURE sp_update_category go CREATE PROCEDURE sp_update_category @class VARCHAR(8), -- JOB or ALERT or OPERATOR @name sysname, @new_name sysname AS BEGIN DECLARE @retval INT DECLARE @category_id INT DECLARE @category_class INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @class = LTRIM(RTRIM(@class)) SELECT @name = LTRIM(RTRIM(@name)) SELECT @new_name = LTRIM(RTRIM(@new_name)) --turn empy parametrs tu null parameters IF @name = '' SELECT @name = NULL EXECUTE @retval = sp_verify_category @class, NULL, @new_name, @category_class OUTPUT, NULL IF (@retval <> 0) RETURN(1) -- Failure --ID @name not null check id such a category exists --check name - it should exist if not null IF @name IS NOT NULL AND NOT EXISTS(SELECT * FROM msdb.dbo.syscategories WHERE name = @name AND category_class = @category_class) BEGIN RAISERROR(14526, -1, -1, @name, @category_class) RETURN(1) -- Failure END -- Check name SELECT @category_id = category_id FROM msdb.dbo.syscategories WHERE (category_class = @category_class) AND (name = @new_name) IF (@category_id IS NOT NULL) BEGIN RAISERROR(14261, -1, -1, '@new_name', @new_name) RETURN(1) -- Failure END -- Make sure that we're not updating one of the permanent categories (id's 0 - 99) IF (@category_id < 100) BEGIN RAISERROR(14276, -1, -1, @name, @class) RETURN(1) -- Failure END -- Update the category name UPDATE msdb.dbo.syscategories SET name = @new_name WHERE (category_class = @category_class) AND (name = @name) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_DELETE_CATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_category...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_category') AND (type = 'P'))) DROP PROCEDURE sp_delete_category go CREATE PROCEDURE sp_delete_category @class VARCHAR(8), -- JOB or ALERT or OPERATOR @name sysname AS BEGIN DECLARE @retval INT DECLARE @category_id INT DECLARE @category_class INT DECLARE @category_type INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @class = LTRIM(RTRIM(@class)) SELECT @name = LTRIM(RTRIM(@name)) EXECUTE @retval = sp_verify_category @class, NULL, NULL, @category_class OUTPUT, NULL IF (@retval <> 0) RETURN(1) -- Failure -- Check name SELECT @category_id = category_id, @category_type = category_type FROM msdb.dbo.syscategories WHERE (category_class = @category_class) AND (name = @name) IF (@category_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Make sure that we're not deleting one of the permanent categories (id's 0 - 99) IF (@category_id < 100) BEGIN RAISERROR(14276, -1, -1, @name, @class) RETURN(1) -- Failure END BEGIN TRANSACTION -- Clean-up any Jobs that reference the deleted category UPDATE msdb.dbo.sysjobs SET category_id = CASE @category_type WHEN 1 THEN 0 -- [Uncategorized (Local)] WHEN 2 THEN 2 -- [Uncategorized (Multi-Server)] END WHERE (category_id = @category_id) -- Clean-up any Alerts that reference the deleted category UPDATE msdb.dbo.sysalerts SET category_id = 98 WHERE (category_id = @category_id) -- Clean-up any Operators that reference the deleted category UPDATE msdb.dbo.sysoperators SET category_id = 99 WHERE (category_id = @category_id) -- Finally, delete the category itself DELETE FROM msdb.dbo.syscategories WHERE (category_id = @category_id) COMMIT TRANSACTION RETURN(0) -- Success END go /**************************************************************/ /* SP_HELP_CATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_category...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_category') AND (type = 'P'))) DROP PROCEDURE sp_help_category go CREATE PROCEDURE sp_help_category @class VARCHAR(8) = 'JOB', -- JOB, ALERT or OPERATOR @type VARCHAR(12) = NULL, -- LOCAL, MULTI-SERVER, or NONE @name sysname = NULL, @suffix BIT = 0 -- 0 = no suffix, 1 = add suffix AS BEGIN DECLARE @retval INT DECLARE @type_in VARCHAR(12) DECLARE @category_type INT DECLARE @category_class INT DECLARE @where_clause NVARCHAR(255) DECLARE @cmd NVARCHAR(255) 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 */ /**************************************************************/ 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 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. -- NOTE: SQLDMO relies on the 'FULL' format; ** DO NOT CHANGE IT ** IF (@mode = 'FULL') BEGIN IF(@distributed_job_history = 1) BEGIN 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)) END ELSE BEGIN 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) END END ELSE IF (@mode = 'SUMMARY') BEGIN -- Summary format: same WHERE clause just a different SELECT list IF(@distributed_job_history = 1) BEGIN 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)) END ELSE BEGIN 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) END END ELSE IF (@mode = 'SEM') BEGIN -- SQL Enterprise Manager format IF(@distributed_job_history = 1) BEGIN 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) END ELSE BEGIN 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) END 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) -- If we deleted the last jobserver then re-categorize the job to the sp_add_job default 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', '0x1, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80') 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()) 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 IF (@freq_type = 0x1) -- FREQTYPE_ONETIME BEGIN DECLARE @current_date INT DECLARE @current_time INT 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) RAISERROR(14266, -1, -1, '@active_start_date'' / ''@active_start_time', @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)) OR ((@freq_subday_type = 0x2) AND (@freq_subday_interval < 10)) 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 ) AS BEGIN DECLARE @retval INT DECLARE @sched_owner_sid VARBINARY(85) DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Check that we can uniquely identify the job EXECUTE @retval = msdb.dbo.sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check authority (only SQLServerAgent can add a schedule to a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- Check that we can uniquely identify the schedule EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = @sched_owner_sid OUTPUT, @orig_server_id = NULL IF (@retval <> 0) RETURN(1) -- Failure --Schedules can only be attached to a job if the job and schedule have the --same owner or the caller is a sysadmin IF ((@sched_owner_sid <> @job_owner_sid) AND ((@sched_owner_sid <> SUSER_SID()) OR (@job_owner_sid <> SUSER_SID())) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(14377, -1, -1) RETURN(1) -- Failure END -- If the record doesn't already exist create it IF( NOT EXISTS(SELECT * FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) AND (job_id = @job_id)) ) BEGIN INSERT INTO msdb.dbo.sysjobschedules (schedule_id, job_id) SELECT @schedule_id, @job_id SELECT @retval = @@ERROR -- Notify SQLServerAgent of the change, but only if we know the job has been cached IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'S', @job_id = @job_id, @schedule_id = @schedule_id, @action_type = N'I' END -- For a multi-server job, remind the user that they need to call sp_post_msx_operation IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation') 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 ) AS BEGIN DECLARE @retval INT DECLARE @sched_owner_sid VARBINARY(85) DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Check that we can uniquely identify the job EXECUTE @retval = msdb.dbo.sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check authority (only SQLServerAgent can add a schedule to a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- Check that we can uniquely identify the schedule EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = @sched_owner_sid OUTPUT, @orig_server_id = NULL, @job_id_filter = @job_id IF (@retval <> 0) RETURN(1) -- Failure -- If the record doesn't exist raise an error IF( NOT EXISTS(SELECT * FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) AND (job_id = @job_id)) ) BEGIN RAISERROR(14374, 0, 1, @schedule_name, @job_name) RETURN(1) -- Failure END ELSE BEGIN -- Only sysadmin can detach schedules from jobs they do not own IF (((@sched_owner_sid <> SUSER_SID()) OR (@job_owner_sid <> SUSER_SID())) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(14391, -1, -1) RETURN(1) -- Failure END DELETE FROM msdb.dbo.sysjobschedules WHERE (job_id = @job_id) AND (schedule_id = @schedule_id) SELECT @retval = @@ERROR --delete the schedule if requested and it isn't referenced IF(@retval = 0 AND @delete_unused_schedule = 1) BEGIN IF(NOT EXISTS(SELECT * FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id))) BEGIN DELETE FROM msdb.dbo.sysschedules WHERE (schedule_id = @schedule_id) END END -- Update the job's version/last-modified information UPDATE msdb.dbo.sysjobs SET version_number = version_number + 1, date_modified = GETDATE() WHERE (job_id = @job_id) -- Notify SQLServerAgent of the change, but only if we know the job has been cached IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'S', @job_id = @job_id, @schedule_id = @schedule_id, @action_type = N'D' END -- For a multi-server job, remind the user that they need to call sp_post_msx_operation IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation') 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 noticiations 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 ) 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 -- For a multi-server job, remind the user that they need to call sp_post_msx_operation 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(256) 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 sysname DECLARE @database_user_name_temp sysname 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 SET NOCOUNT ON -- 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 SELECT @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 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 Qith Success (@on_fail_action <> 2) AND -- Quit Qith 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 > 45)) BEGIN RAISERROR(14266, -1, -1, '@flags', '0..45') 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' )) BEGIN RAISERROR(14545, -1, -1, '@output_file_name', @subsystem) RETURN(1) -- Failure END -- Check writing to table flags IF (@flags IS NOT NULL) AND (((@flags & 8) <> 0) OR ((@flags & 16) <> 0)) AND (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('TSQL', 'CMDEXEC', 'ANALYSISQUERY', 'ANALYSISCOMMAND' )) 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 = REPLACE(@database_name, N'''', N'''''') EXECUTE(N'DECLARE @ret INT SELECT @ret = COUNT(*) FROM ' + @database_name_temp + N'.dbo.sysusers WHERE (name = N''' + @database_user_name_temp + N''') HAVING (COUNT(*) > 0)') IF (@@ROWCOUNT = 0) BEGIN RAISERROR(14262, -1, -1, '@database_user_name', @database_user_name) RETURN(1) -- Failure END END END ELSE SELECT @database_user_name = NULL END -- End of TSQL property verification RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_JOBSTEP_INTERNAL */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_jobstep_internal...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_jobstep_internal') AND (type = 'P'))) DROP PROCEDURE dbo.sp_add_jobstep_internal go CREATE PROCEDURE dbo.sp_add_jobstep_internal @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @step_id INT = NULL, -- The proc assigns a default @step_name sysname, @subsystem NVARCHAR(40) = N'TSQL', @command NVARCHAR(max) = NULL, @additional_parameters NTEXT = NULL, @cmdexec_success_code INT = 0, @on_success_action TINYINT = 1, -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step @on_success_step_id INT = 0, @on_fail_action TINYINT = 2, -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step @on_fail_step_id INT = 0, @server sysname = NULL, @database_name sysname = NULL, @database_user_name sysname = NULL, @retry_attempts INT = 0, -- No retries @retry_interval INT = 0, -- 0 minute interval @os_run_priority INT = 0, -- -15 = Idle, -1 = Below Normal, 0 = Normal, 1 = Above Normal, 15 = Time Critical) @output_file_name NVARCHAR(200) = NULL, @flags INT = 0, -- 0 = Normal, -- 1 = Encrypted command (read only), -- 2 = Append output files (if any), -- 4 = Write TSQL step output to step history -- 8 = Write log to table (overwrite existing history) -- 16 = Write log to table (append to existing history) @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 -- Procedure called when there is -- an active transaction. -- Create a savepoint to be able -- to roll back only the work done -- in the procedure if there is an -- error. SAVE TRANSACTION tran_sp_add_jobstep_internal; END ELSE BEGIN -- Procedure must start its own -- 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 -- @TranCounter = 0 means no transaction was -- started before the procedure was called. -- The procedure must commit the transaction -- it started. COMMIT TRANSACTION; END END TRY BEGIN CATCH -- An error occurred, must determine -- which type of rollback will roll -- back only the work done in the -- procedure. IF @TranCounter = 0 BEGIN -- Transaction started in procedure. -- Roll back complete transaction. ROLLBACK TRANSACTION; END ELSE -- Transaction started before procedure -- called, do not roll back modifications -- made before the procedure was called. IF (XACT_STATE()) <> - 1 BEGIN -- If the transaction is still valid, just -- roll back to the savepoint set at the -- start of the stored procedure. ROLLBACK TRANSACTION tran_sp_add_jobstep_internal; END -- If the transaction is uncommitable, a rollback -- to the save point is not allowed because -- the savepoint rollback writes to the log. -- Just return to the caller, which should -- roll back the outer transaction. -- After the appropriate rollback, 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() RAISERROR (@ErrorMessage, -- Message text. @ErrorSeverity, -- Severity. @ErrorState -- State. ) RETURN (1) END CATCH -- Make sure that SQLServerAgent refreshes the job if the 'Has Steps' property has changed IF ((SELECT COUNT(*) FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id)) = 1) BEGIN -- NOTE: We only notify SQLServerAgent if we know the job has been cached IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @action_type = N'U' END -- For a multi-server job, push changes to the target servers IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) BEGIN EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id END RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_JOBSTEP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_jobstep...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_jobstep') AND (type = 'P'))) DROP PROCEDURE dbo.sp_add_jobstep go CREATE PROCEDURE dbo.sp_add_jobstep @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @step_id INT = NULL, -- The proc assigns a default @step_name sysname, @subsystem NVARCHAR(40) = N'TSQL', @command NVARCHAR(max) = NULL, @additional_parameters NTEXT = NULL, @cmdexec_success_code INT = 0, @on_success_action TINYINT = 1, -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step @on_success_step_id INT = 0, @on_fail_action TINYINT = 2, -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step @on_fail_step_id INT = 0, @server sysname = NULL, @database_name sysname = NULL, @database_user_name sysname = NULL, @retry_attempts INT = 0, -- No retries @retry_interval INT = 0, -- 0 minute interval @os_run_priority INT = 0, -- -15 = Idle, -1 = Below Normal, 0 = Normal, 1 = Above Normal, 15 = Time Critical) @output_file_name NVARCHAR(200) = NULL, @flags INT = 0, -- 0 = Normal, 1 = Encrypted command (read only), 2 = Append output files (if any), -- 4 = Write TSQL step output to step history, -- 8 = Write log to table (overwrite existing history), 16 = Write log to table (append to existing history) @proxy_id INT = NULL, @proxy_name sysname = NULL, -- mutual exclusive; must specify only one of above 2 parameters to -- identify the proxy. @step_uid UNIQUEIDENTIFIER = NULL OUTPUT AS BEGIN DECLARE @retval INT SET NOCOUNT ON -- Only sysadmin's or db_owner's of msdb can add replication job steps directly IF (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) IN (N'DISTRIBUTION', N'SNAPSHOT', N'LOGREADER', N'MERGE', N'QUEUEREADER')) BEGIN IF NOT ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1) OR (UPPER(USER_NAME() collate SQL_Latin1_General_CP1_CS_AS) = N'DBO')) BEGIN RAISERROR(14260, -1, -1) RETURN(1) -- Failure END END --Only sysadmin can specify @database_user_name IF (@database_user_name IS NOT NULL) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(14583, -1, -1) RETURN(1) -- Failure END -- Make sure Dts is translated into new subsystem's name SSIS IF UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DTS' BEGIN SET @subsystem = N'SSIS' END EXECUTE @retval = dbo.sp_add_jobstep_internal @job_id = @job_id, @job_name = @job_name, @step_id = @step_id, @step_name = @step_name, @subsystem = @subsystem, @command = @command, @additional_parameters = @additional_parameters, @cmdexec_success_code = @cmdexec_success_code, @on_success_action = @on_success_action, @on_success_step_id = @on_success_step_id, @on_fail_action = @on_fail_action, @on_fail_step_id = @on_fail_step_id, @server = @server, @database_name = @database_name, @database_user_name = @database_user_name, @retry_attempts = @retry_attempts, @retry_interval = @retry_interval, @os_run_priority = @os_run_priority, @output_file_name = @output_file_name, @flags = @flags, @proxy_id = @proxy_id, @proxy_name = @proxy_name, @step_uid = @step_uid OUTPUT RETURN(@retval) END GO /**************************************************************/ /* SP_UPDATE_JOBSTEP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_jobstep...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_jobstep') AND (type = 'P'))) DROP PROCEDURE sp_update_jobstep go CREATE PROCEDURE sp_update_jobstep @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Not updatable (provided for identification purposes only) @step_id INT, -- Not updatable (provided for identification purposes only) @step_name sysname = NULL, @subsystem NVARCHAR(40) = NULL, @command NVARCHAR(max) = NULL, @additional_parameters NTEXT = NULL, @cmdexec_success_code INT = NULL, @on_success_action TINYINT = NULL, @on_success_step_id INT = NULL, @on_fail_action TINYINT = NULL, @on_fail_step_id INT = NULL, @server sysname = NULL, @database_name sysname = NULL, @database_user_name sysname = NULL, @retry_attempts INT = NULL, @retry_interval INT = NULL, @os_run_priority INT = NULL, @output_file_name NVARCHAR(200) = NULL, @flags INT = NULL, @proxy_id int = NULL, @proxy_name sysname = NULL -- mutual exclusive; must specify only one of above 2 parameters to -- identify the proxy. AS BEGIN DECLARE @retval INT DECLARE @os_run_priority_code INT DECLARE @step_id_as_char VARCHAR(10) DECLARE @new_step_name sysname DECLARE @x_step_name sysname DECLARE @x_subsystem NVARCHAR(40) DECLARE @x_command NVARCHAR(max) DECLARE @x_flags INT DECLARE @x_cmdexec_success_code INT DECLARE @x_on_success_action TINYINT DECLARE @x_on_success_step_id INT DECLARE @x_on_fail_action TINYINT DECLARE @x_on_fail_step_id INT DECLARE @x_server sysname DECLARE @x_database_name sysname DECLARE @x_database_user_name sysname DECLARE @x_retry_attempts INT DECLARE @x_retry_interval INT DECLARE @x_os_run_priority INT DECLARE @x_output_file_name NVARCHAR(200) DECLARE @x_proxy_id INT DECLARE @x_last_run_outcome TINYINT -- Not updatable (but may be in future) DECLARE @x_last_run_duration INT -- Not updatable (but may be in future) DECLARE @x_last_run_retries INT -- Not updatable (but may be in future) DECLARE @x_last_run_date INT -- Not updatable (but may be in future) DECLARE @x_last_run_time INT -- Not updatable (but may be in future) DECLARE @new_proxy_id INT DECLARE @subsystem_id INT DECLARE @auto_proxy_name sysname DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON SELECT @new_proxy_id = NULL -- Remove any leading/trailing spaces from parameters SELECT @step_name = LTRIM(RTRIM(@step_name)) SELECT @subsystem = LTRIM(RTRIM(@subsystem)) SELECT @command = LTRIM(RTRIM(@command)) SELECT @server = LTRIM(RTRIM(@server)) SELECT @database_name = LTRIM(RTRIM(@database_name)) SELECT @database_user_name = LTRIM(RTRIM(@database_user_name)) SELECT @output_file_name = LTRIM(RTRIM(@output_file_name)) SELECT @proxy_name = LTRIM(RTRIM(@proxy_name)) -- Make sure Dts is translated into new subsystem's name SSIS IF (@subsystem IS NOT NULL AND UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DTS') BEGIN SET @subsystem = N'SSIS' END -- Only sysadmin's or db_owner's of msdb can directly change -- an existing job step to use one of the replication -- subsystems IF (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) IN (N'DISTRIBUTION', N'SNAPSHOT', N'LOGREADER', N'MERGE', N'QUEUEREADER')) BEGIN IF NOT ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (ISNULL(IS_MEMBER(N'db_owner'), 0) = 1) OR (UPPER(USER_NAME() collate SQL_Latin1_General_CP1_CS_AS) = N'DBO')) BEGIN RAISERROR(14260, -1, -1) RETURN(1) -- Failure END END EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReader and SQLAgentOperator roles that can see all jobs -- cannot modify jobs they do not own IF (@job_owner_sid <> SUSER_SID() -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) -- is not sysadmin BEGIN RAISERROR(14525, -1, -1); RETURN(1) -- Failure END -- Check that the step exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @step_id))) BEGIN SELECT @step_id_as_char = CONVERT(VARCHAR(10), @step_id) RAISERROR(14262, -1, -1, '@step_id', @step_id_as_char) RETURN(1) -- Failure END -- check proxy identifiers only if a proxy has been provided -- @proxy_name = N'' is a special case to allow change of an existing proxy with NULL IF (@proxy_id IS NOT NULL) OR (@proxy_name IS NOT NULL AND @proxy_name <> N'') BEGIN EXECUTE @retval = sp_verify_proxy_identifiers '@proxy_name', '@proxy_id', @proxy_name OUTPUT, @proxy_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure SELECT @new_proxy_id = @proxy_id END -- Check authority (only SQLServerAgent can modify a step of a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- Set the x_ (existing) variables SELECT @x_step_name = step_name, @x_subsystem = subsystem, @x_command = command, @x_flags = flags, @x_cmdexec_success_code = cmdexec_success_code, @x_on_success_action = on_success_action, @x_on_success_step_id = on_success_step_id, @x_on_fail_action = on_fail_action, @x_on_fail_step_id = on_fail_step_id, @x_server = server, @x_database_name = database_name, @x_database_user_name = database_user_name, @x_retry_attempts = retry_attempts, @x_retry_interval = retry_interval, @x_os_run_priority = os_run_priority, @x_output_file_name = output_file_name, @x_proxy_id = proxy_id, @x_last_run_outcome = last_run_outcome, @x_last_run_duration = last_run_duration, @x_last_run_retries = last_run_retries, @x_last_run_date = last_run_date, @x_last_run_time = last_run_time FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @step_id) IF ((@step_name IS NOT NULL) AND (@step_name <> @x_step_name)) SELECT @new_step_name = @step_name -- Fill out the values for all non-supplied parameters from the existing values IF (@step_name IS NULL) SELECT @step_name = @x_step_name IF (@subsystem IS NULL) SELECT @subsystem = @x_subsystem IF (@command IS NULL) SELECT @command = @x_command IF (@flags IS NULL) SELECT @flags = @x_flags IF (@cmdexec_success_code IS NULL) SELECT @cmdexec_success_code = @x_cmdexec_success_code IF (@on_success_action IS NULL) SELECT @on_success_action = @x_on_success_action IF (@on_success_step_id IS NULL) SELECT @on_success_step_id = @x_on_success_step_id IF (@on_fail_action IS NULL) SELECT @on_fail_action = @x_on_fail_action IF (@on_fail_step_id IS NULL) SELECT @on_fail_step_id = @x_on_fail_step_id IF (@server IS NULL) SELECT @server = @x_server IF (@database_name IS NULL) SELECT @database_name = @x_database_name IF (@database_user_name IS NULL) SELECT @database_user_name = @x_database_user_name IF (@retry_attempts IS NULL) SELECT @retry_attempts = @x_retry_attempts IF (@retry_interval IS NULL) SELECT @retry_interval = @x_retry_interval IF (@os_run_priority IS NULL) SELECT @os_run_priority = @x_os_run_priority IF (@output_file_name IS NULL) SELECT @output_file_name = @x_output_file_name IF (@proxy_id IS NULL) SELECT @new_proxy_id = @x_proxy_id --if an empty proxy_name is supplied the proxy is removed IF @proxy_name = N'' SELECT @new_proxy_id = NULL -- Turn [nullable] empty string parameters into NULLs IF (@command = N'') SELECT @command = NULL IF (@server = N'') SELECT @server = NULL IF (@database_name = N'') SELECT @database_name = NULL IF (@database_user_name = N'') SELECT @database_user_name = NULL IF (@output_file_name = N'') SELECT @output_file_name = NULL -- Check new values EXECUTE @retval = sp_verify_jobstep @job_id, @step_id, @new_step_name, @subsystem, @command, @server, @on_success_action, @on_success_step_id, @on_fail_action, @on_fail_step_id, @os_run_priority, @database_name OUTPUT, @database_user_name OUTPUT, @flags, @output_file_name, @new_proxy_id IF (@retval <> 0) RETURN(1) -- Failure BEGIN TRANSACTION -- Update the job's version/last-modified information UPDATE msdb.dbo.sysjobs SET version_number = version_number + 1, date_modified = GETDATE() WHERE (job_id = @job_id) -- Update the step UPDATE msdb.dbo.sysjobsteps SET step_name = @step_name, subsystem = @subsystem, command = @command, flags = @flags, cmdexec_success_code = @cmdexec_success_code, on_success_action = @on_success_action, on_success_step_id = @on_success_step_id, on_fail_action = @on_fail_action, on_fail_step_id = @on_fail_step_id, server = @server, database_name = @database_name, database_user_name = @database_user_name, retry_attempts = @retry_attempts, retry_interval = @retry_interval, os_run_priority = @os_run_priority, output_file_name = @output_file_name, last_run_outcome = @x_last_run_outcome, last_run_duration = @x_last_run_duration, last_run_retries = @x_last_run_retries, last_run_date = @x_last_run_date, last_run_time = @x_last_run_time, proxy_id = @new_proxy_id WHERE (job_id = @job_id) AND (step_id = @step_id) -- Since we can't declare TEXT parameters (and therefore use the @x_ technique) we handle -- @additional_parameters as a special case... IF (@additional_parameters IS NOT NULL) BEGIN UPDATE msdb.dbo.sysjobsteps SET additional_parameters = @additional_parameters WHERE (job_id = @job_id) AND (step_id = @step_id) END COMMIT TRANSACTION -- For a multi-server job, push changes to the target servers IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) BEGIN EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id END RETURN(0) -- Success END go /**************************************************************/ /* SP_DELETE_JOBSTEP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_jobstep...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_jobstep') AND (type = 'P'))) DROP PROCEDURE sp_delete_jobstep go CREATE PROCEDURE sp_delete_jobstep @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @step_id INT AS BEGIN DECLARE @retval INT DECLARE @max_step_id INT DECLARE @valid_range VARCHAR(50) DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check authority (only SQLServerAgent can delete a step of a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = 'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- SQLAgentReader and SQLAgentOperator roles that can see all jobs -- cannot modify jobs they do not own IF (@job_owner_sid <> SUSER_SID() -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) -- is not sysadmin BEGIN RAISERROR(14525, -1, -1); RETURN(1) -- Failure END -- Get current maximum step id SELECT @max_step_id = ISNULL(MAX(step_id), 0) FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) -- Check step id IF (@step_id < 0) OR (@step_id > @max_step_id) BEGIN SELECT @valid_range = FORMATMESSAGE(14201) + CONVERT(VARCHAR, @max_step_id) RAISERROR(14266, -1, -1, '@step_id', @valid_range) RETURN(1) -- Failure END BEGIN TRANSACTION -- Delete either the specified step or ALL the steps (if step id is 0) IF (@step_id = 0) BEGIN DELETE FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) END ELSE BEGIN DELETE FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @step_id) END IF (@step_id <> 0) BEGIN -- Adjust step id's UPDATE msdb.dbo.sysjobsteps SET step_id = step_id - 1 WHERE (step_id > @step_id) AND (job_id = @job_id) -- Clean up OnSuccess/OnFail references UPDATE msdb.dbo.sysjobsteps SET on_success_step_id = on_success_step_id - 1 WHERE (on_success_step_id > @step_id) AND (job_id = @job_id) UPDATE msdb.dbo.sysjobsteps SET on_fail_step_id = on_fail_step_id - 1 WHERE (on_fail_step_id > @step_id) AND (job_id = @job_id) UPDATE msdb.dbo.sysjobsteps SET on_success_step_id = 0, on_success_action = 1 -- Quit With Success WHERE (on_success_step_id = @step_id) AND (job_id = @job_id) UPDATE msdb.dbo.sysjobsteps SET on_fail_step_id = 0, on_fail_action = 2 -- Quit With Failure WHERE (on_fail_step_id = @step_id) AND (job_id = @job_id) END -- Update the job's version/last-modified information UPDATE msdb.dbo.sysjobs SET version_number = version_number + 1, date_modified = GETDATE() WHERE (job_id = @job_id) COMMIT TRANSACTION -- Make sure that SQLServerAgent refreshes the job if the 'Has Steps' property has changed IF ((SELECT COUNT(*) FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id)) = 0) BEGIN -- NOTE: We only notify SQLServerAgent if we know the job has been cached IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @action_type = N'U' END -- For a multi-server job, push changes to the target servers IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) BEGIN EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id END RETURN(0) -- Success END go /**************************************************************/ /* SP_HELP_JOBSTEP */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_jobstep...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_jobstep') AND (type = 'P'))) DROP PROCEDURE sp_help_jobstep go CREATE PROCEDURE sp_help_jobstep @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @step_id INT = NULL, @step_name sysname = NULL, @suffix BIT = 0 -- A flag to control how the result set is formatted AS BEGIN DECLARE @retval INT DECLARE @max_step_id INT DECLARE @valid_range VARCHAR(50) SET NOCOUNT ON EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, 'NO_TEST' IF (@retval <> 0) RETURN(1) -- Failure -- The suffix flag must be either 0 (ie. no suffix) or 1 (ie. add suffix). 0 is the default. IF (@suffix <> 0) SELECT @suffix = 1 -- Check step id (if supplied) IF (@step_id IS NOT NULL) BEGIN -- Get current maximum step id SELECT @max_step_id = ISNULL(MAX(step_id), 0) FROM msdb.dbo.sysjobsteps WHERE job_id = @job_id IF @max_step_id = 0 BEGIN RAISERROR(14528, -1, -1, @job_name) RETURN(1) -- Failure END ELSE IF (@step_id < 1) OR (@step_id > @max_step_id) BEGIN SELECT @valid_range = '1..' + CONVERT(VARCHAR, @max_step_id) RAISERROR(14266, -1, -1, '@step_id', @valid_range) RETURN(1) -- Failure END END -- Check step name (if supplied) -- NOTE: A supplied step id overrides a supplied step name IF ((@step_id IS NULL) AND (@step_name IS NOT NULL)) BEGIN SELECT @step_id = step_id FROM msdb.dbo.sysjobsteps WHERE (step_name = @step_name) AND (job_id = @job_id) IF (@step_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@step_name', @step_name) RETURN(1) -- Failure END END -- Return the job steps for this job (or just return the specific step) IF (@suffix = 0) BEGIN SELECT step_id, step_name, subsystem, command, flags, cmdexec_success_code, on_success_action, on_success_step_id, on_fail_action, on_fail_step_id, server, database_name, database_user_name, retry_attempts, retry_interval, os_run_priority, output_file_name, last_run_outcome, last_run_duration, last_run_retries, last_run_date, last_run_time, proxy_id FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND ((@step_id IS NULL) OR (step_id = @step_id)) 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)) 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 ovrerwrite and log exists, delete it IF (@append_to_last = 0 AND @log_already_exists = 1) BEGIN -- remove pervious logs entires 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 ) 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 depricated. Use sp_add_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 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_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 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 depricated 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 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))) 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 @job_id UNIQUEIDENTIFIER = NULL, @job_name sysname = NULL, @name sysname, @keep_schedule int = 0 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))) 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) -- next_run_date, -- next_run_time INTO #temp_jobschedule FROM msdb.dbo.sysschedules_localserver_view as sch WHERE ( (@attached_schedules_only = 0) OR (EXISTS(SELECT * FROM sysjobschedules as jobsch WHERE sch.schedule_id = jobsch.schedule_id)) ) AND((@schedule_id IS NULL) OR (schedule_id = @schedule_id)) IF (@include_description = 1) BEGIN -- For each schedule, generate the textual schedule description and update the temporary -- table with it IF (EXISTS (SELECT * FROM #temp_jobschedule)) BEGIN WHILE (EXISTS (SELECT * FROM #temp_jobschedule WHERE schedule_description = FORMATMESSAGE(14549))) BEGIN SET ROWCOUNT 1 SELECT @name = schedule_name, @freq_type = freq_type, @freq_interval = freq_interval, @freq_subday_type = freq_subday_type, @freq_subday_interval = freq_subday_interval, @freq_relative_interval = freq_relative_interval, @freq_recurrence_factor = freq_recurrence_factor, @active_start_date = active_start_date, @active_end_date = active_end_date, @active_start_time = active_start_time, @active_end_time = active_end_time FROM #temp_jobschedule WHERE (schedule_description = FORMATMESSAGE(14549)) SET ROWCOUNT 0 EXECUTE sp_get_schedule_description @freq_type, @freq_interval, @freq_subday_type, @freq_subday_interval, @freq_relative_interval, @freq_recurrence_factor, @active_start_date, @active_end_date, @active_start_time, @active_end_time, @schedule_description OUTPUT UPDATE #temp_jobschedule SET schedule_description = ISNULL(LTRIM(RTRIM(@schedule_description)), FORMATMESSAGE(14205)) WHERE (schedule_name = @name) END -- While END END -- Return the result set, adding job count SELECT *, (SELECT COUNT(*) FROM sysjobschedules WHERE sysjobschedules.schedule_id = #temp_jobschedule.schedule_id) as 'job_count' FROM #temp_jobschedule ORDER BY schedule_id RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_HELP_JOBSCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_jobschedule...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_jobschedule') AND (type = 'P'))) DROP PROCEDURE sp_help_jobschedule go CREATE PROCEDURE sp_help_jobschedule @job_id UNIQUEIDENTIFIER = NULL, @job_name sysname = NULL, @schedule_name sysname = NULL, @schedule_id INT = NULL, @include_description BIT = 0 -- 1 if a schedule description is required (NOTE: It's expensive to generate the description) AS BEGIN DECLARE @retval INT DECLARE @schedule_description NVARCHAR(255) DECLARE @name sysname DECLARE @freq_type INT DECLARE @freq_interval INT DECLARE @freq_subday_type INT DECLARE @freq_subday_interval INT DECLARE @freq_relative_interval INT DECLARE @freq_recurrence_factor INT DECLARE @active_start_date INT DECLARE @active_end_date INT DECLARE @active_start_time INT DECLARE @active_end_time INT DECLARE @schedule_id_as_char VARCHAR(10) DECLARE @job_count INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @schedule_name = LTRIM(RTRIM(@schedule_name)) SELECT @job_count = 0 -- Turn [nullable] empty string parameters into NULLs IF (@schedule_name = N'') SELECT @schedule_name = NULL -- The user must provide either: -- 1) job_id (or job_name) and (optionally) a schedule name -- or... -- 2) just schedule_id IF (@schedule_id IS NULL) AND (@job_id IS NULL) AND (@job_name IS NULL) BEGIN RAISERROR(14273, -1, -1) RETURN(1) -- Failure END IF (@schedule_id IS NOT NULL) AND ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL) OR (@schedule_name IS NOT NULL)) BEGIN RAISERROR(14273, -1, -1) RETURN(1) -- Failure END -- Check that the schedule (by ID) exists and it is only used by one job. -- Allowing this for backward compatibility with versions prior to V9 IF (@schedule_id IS NOT NULL) AND (@job_id IS NULL) AND (@job_name IS NULL) BEGIN SELECT @job_count = COUNT(*) FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) if(@job_count > 1) BEGIN SELECT @schedule_id_as_char = CONVERT(VARCHAR, @schedule_id) RAISERROR(14369, -1, -1, @schedule_id_as_char) RETURN(1) -- Failure END SELECT @job_id = job_id FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) IF (@job_id IS NULL) BEGIN SELECT @schedule_id_as_char = CONVERT(VARCHAR, @schedule_id) RAISERROR(14262, -1, -1, '@schedule_id', @schedule_id_as_char) RETURN(1) -- Failure END END -- Check that we can uniquely identify the job IF (@job_id IS NOT NULL) OR (@job_name IS NOT NULL) BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, 'NO_TEST' IF (@retval <> 0) RETURN(1) -- Failure END IF (@schedule_id IS NOT NULL OR @schedule_name IS NOT NULL) BEGIN -- Check that we can uniquely identify the schedule EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = NULL, @orig_server_id = NULL, @job_id_filter = @job_id IF (@retval <> 0) RETURN(1) -- Failure END -- Check that the schedule (by name) exists IF (@schedule_name IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobschedules AS js JOIN msdb.dbo.sysschedules AS s ON js.schedule_id = s.schedule_id WHERE (js.job_id = @job_id) AND (s.name = @schedule_name))) BEGIN RAISERROR(14262, -1, -1, '@schedule_name', @schedule_name) RETURN(1) -- Failure END END -- Get the schedule(s) into a temporary table SELECT s.schedule_id, 'schedule_name' = name, enabled, freq_type, freq_interval, freq_subday_type, freq_subday_interval, freq_relative_interval, freq_recurrence_factor, active_start_date, active_end_date, active_start_time, active_end_time, date_created, 'schedule_description' = FORMATMESSAGE(14549), js.next_run_date, js.next_run_time, s.schedule_uid INTO #temp_jobschedule FROM msdb.dbo.sysjobschedules AS js JOIN msdb.dbo.sysschedules AS s ON js.schedule_id = s.schedule_id WHERE ((@job_id IS NULL) OR (js.job_id = @job_id)) AND ((@schedule_name IS NULL) OR (s.name = @schedule_name)) AND ((@schedule_id IS NULL) OR (s.schedule_id = @schedule_id)) IF (@include_description = 1) BEGIN -- For each schedule, generate the textual schedule description and update the temporary -- table with it IF (EXISTS (SELECT * FROM #temp_jobschedule)) BEGIN WHILE (EXISTS (SELECT * FROM #temp_jobschedule WHERE schedule_description = FORMATMESSAGE(14549))) BEGIN SET ROWCOUNT 1 SELECT @name = schedule_name, @freq_type = freq_type, @freq_interval = freq_interval, @freq_subday_type = freq_subday_type, @freq_subday_interval = freq_subday_interval, @freq_relative_interval = freq_relative_interval, @freq_recurrence_factor = freq_recurrence_factor, @active_start_date = active_start_date, @active_end_date = active_end_date, @active_start_time = active_start_time, @active_end_time = active_end_time FROM #temp_jobschedule WHERE (schedule_description = FORMATMESSAGE(14549)) SET ROWCOUNT 0 EXECUTE sp_get_schedule_description @freq_type, @freq_interval, @freq_subday_type, @freq_subday_interval, @freq_relative_interval, @freq_recurrence_factor, @active_start_date, @active_end_date, @active_start_time, @active_end_time, @schedule_description OUTPUT UPDATE #temp_jobschedule SET schedule_description = ISNULL(LTRIM(RTRIM(@schedule_description)), FORMATMESSAGE(14205)) WHERE (schedule_name = @name) END -- While END END -- Return the result set, adding job count to it SELECT *, (SELECT COUNT(*) FROM sysjobschedules WHERE sysjobschedules.schedule_id = #temp_jobschedule.schedule_id) as 'job_count' FROM #temp_jobschedule ORDER BY schedule_id RETURN(@@error) -- 0 means success END go CHECKPOINT go /**************************************************************/ /* SP_VERIFY_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_job') AND (type = 'P'))) DROP PROCEDURE sp_verify_job go CREATE PROCEDURE sp_verify_job @job_id UNIQUEIDENTIFIER, @name sysname, @enabled TINYINT, @start_step_id INT, @category_name sysname, @owner_sid VARBINARY(85) OUTPUT, -- Output since we may modify it @notify_level_eventlog INT, @notify_level_email INT OUTPUT, -- Output since we may reset it to 0 @notify_level_netsend INT OUTPUT, -- Output since we may reset it to 0 @notify_level_page INT OUTPUT, -- Output since we may reset it to 0 @notify_email_operator_name sysname, @notify_netsend_operator_name sysname, @notify_page_operator_name sysname, @delete_level INT, @category_id INT OUTPUT, -- The ID corresponding to the name @notify_email_operator_id INT OUTPUT, -- The ID corresponding to the name @notify_netsend_operator_id INT OUTPUT, -- The ID corresponding to the name @notify_page_operator_id INT OUTPUT, -- The ID corresponding to the name @originating_server sysname OUTPUT -- Output since we may modify it AS BEGIN DECLARE @job_type INT DECLARE @retval INT DECLARE @current_date INT DECLARE @res_valid_range NVARCHAR(200) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @category_name = LTRIM(RTRIM(@category_name)) SELECT @originating_server = UPPER(LTRIM(RTRIM(@originating_server))) SELECT @originating_server = ISNULL(@originating_server, UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) -- Check originating server (only the SQLServerAgent can add jobs that originate from a remote server) IF (@originating_server <> UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) AND (PROGRAM_NAME() NOT LIKE N'SQLAgent%') BEGIN RAISERROR(14275, -1, -1) RETURN(1) -- Failure END -- NOTE: We allow jobs with the same name (since job_id is always unique) but only if -- they originate from different servers. Thus jobs can flow from an MSX to a TSX -- without having to worry about naming conflicts. IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs as job JOIN msdb.dbo.sysoriginatingservers_view as svr ON (svr.originating_server_id = job.originating_server_id) WHERE (name = @name) AND (svr.originating_server = @originating_server) AND (job_id <> ISNULL(@job_id, 0x911)))) -- When adding a new job @job_id is NULL BEGIN RAISERROR(14261, -1, -1, '@name', @name) RETURN(1) -- Failure END -- Check enabled state IF (@enabled <> 0) AND (@enabled <> 1) BEGIN RAISERROR(14266, -1, -1, '@enabled', '0, 1') RETURN(1) -- Failure END -- Check start step IF (@job_id IS NULL) BEGIN -- New job -- NOTE: For [new] MSX jobs we allow the start step to be other than 1 since -- the start step was validated when the job was created at the MSX IF (@start_step_id <> 1) AND (@originating_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) BEGIN RAISERROR(14266, -1, -1, '@start_step_id', '1') RETURN(1) -- Failure END END ELSE BEGIN -- Existing job DECLARE @max_step_id INT DECLARE @valid_range VARCHAR(50) -- Get current maximum step id SELECT @max_step_id = ISNULL(MAX(step_id), 0) FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) IF (@start_step_id < 1) OR (@start_step_id > @max_step_id + 1) BEGIN SELECT @valid_range = '1..' + CONVERT(VARCHAR, @max_step_id + 1) RAISERROR(14266, -1, -1, '@start_step_id', @valid_range) RETURN(1) -- Failure END END -- Check category SELECT @job_type = NULL IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) SELECT @job_type = 1 -- LOCAL IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) SELECT @job_type = 2 -- MULTI-SERVER -- A local job cannot be added to a multi-server job_category IF (@job_type = 1) AND (EXISTS (SELECT * FROM msdb.dbo.syscategories WHERE (category_class = 1) -- Job AND (category_type = 2) -- Multi-Server AND (name = @category_name))) BEGIN RAISERROR(14285, -1, -1) RETURN(1) -- Failure END -- A multi-server job cannot be added to a local job_category IF (@job_type = 2) AND (EXISTS (SELECT * FROM msdb.dbo.syscategories WHERE (category_class = 1) -- Job AND (category_type = 1) -- Local AND (name = @category_name))) BEGIN RAISERROR(14286, -1, -1) RETURN(1) -- Failure END -- Get the category_id, handling any special-cases as appropriate SELECT @category_id = NULL IF (@category_name = N'[DEFAULT]') -- User wants to revert to the default job category BEGIN SELECT @category_id = CASE ISNULL(@job_type, 1) WHEN 1 THEN 0 -- [Uncategorized (Local)] WHEN 2 THEN 2 -- [Uncategorized (Multi-Server)] END END ELSE IF (@category_name IS NULL) -- The sp_add_job default BEGIN SELECT @category_id = 0 END ELSE BEGIN SELECT @category_id = category_id FROM msdb.dbo.syscategories WHERE (category_class = 1) -- Job AND (name = @category_name) END IF (@category_id IS NULL) BEGIN RAISERROR(14234, -1, -1, '@category_name', 'sp_help_category') RETURN(1) -- Failure END -- Only SQLServerAgent may add jobs to the 'Jobs From MSX' category IF (@category_id = 1) AND (PROGRAM_NAME() NOT LIKE N'SQLAgent%') BEGIN RAISERROR(14267, -1, -1, @category_name) RETURN(1) -- Failure END -- Check owner -- Default the owner to be the calling user if: -- caller is not a sysadmin -- caller is not SQLAgentOperator and job_id is NULL, meaning new job IF (((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) AND ((ISNULL(IS_MEMBER('SQLAgentOperatorRole'), 0) = 0) AND @job_id IS NULL)) AND (@owner_sid <> SUSER_SID())) BEGIN SELECT @owner_sid = SUSER_SID() END -- Now just check that the login id is valid (ie. it exists and isn't an NT group) IF (@owner_sid <> 0x010100000000000512000000) AND -- NT AUTHORITY\SYSTEM sid (@owner_sid <> 0x010100000000000514000000) -- NT AUTHORITY\NETWORK SERVICE sid BEGIN IF (@owner_sid IS NULL) OR (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (sid = @owner_sid) AND (isntgroup <> 0))) BEGIN -- NOTE: In the following message we quote @owner_login_name instead of @owner_sid -- since this is the parameter the user passed to the calling SP (ie. either -- sp_add_job or sp_update_job) SELECT @res_valid_range = FORMATMESSAGE(14203) RAISERROR(14234, -1, -1, '@owner_login_name', @res_valid_range) RETURN(1) -- Failure END END -- Check notification levels (must be 0, 1, 2 or 3) IF (@notify_level_eventlog & 0x3 <> @notify_level_eventlog) BEGIN RAISERROR(14266, -1, -1, '@notify_level_eventlog', '0, 1, 2, 3') RETURN(1) -- Failure END IF (@notify_level_email & 0x3 <> @notify_level_email) BEGIN RAISERROR(14266, -1, -1, '@notify_level_email', '0, 1, 2, 3') RETURN(1) -- Failure END IF (@notify_level_netsend & 0x3 <> @notify_level_netsend) BEGIN RAISERROR(14266, -1, -1, '@notify_level_netsend', '0, 1, 2, 3') RETURN(1) -- Failure END IF (@notify_level_page & 0x3 <> @notify_level_page) BEGIN RAISERROR(14266, -1, -1, '@notify_level_page', '0, 1, 2, 3') RETURN(1) -- Failure END -- If we're at a TSX, only SQLServerAgent may add jobs that notify 'MSXOperator' IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservers)) AND ((@notify_email_operator_name = N'MSXOperator') OR (@notify_page_operator_name = N'MSXOperator') OR (@notify_netsend_operator_name = N'MSXOperator')) AND (PROGRAM_NAME() NOT LIKE N'SQLAgent%') BEGIN RAISERROR(14251, -1, -1, 'MSXOperator') RETURN(1) -- Failure END -- Check operator to notify (via email) IF (@notify_email_operator_name IS NOT NULL) BEGIN SELECT @notify_email_operator_id = id FROM msdb.dbo.sysoperators WHERE (name = @notify_email_operator_name) IF (@notify_email_operator_id IS NULL) BEGIN RAISERROR(14234, -1, -1, '@notify_email_operator_name', 'sp_help_operator') RETURN(1) -- Failure END -- If a valid operator is specified the level must be non-zero IF (@notify_level_email = 0) BEGIN RAISERROR(14266, -1, -1, '@notify_level_email', '1, 2, 3') RETURN(1) -- Failure END END ELSE BEGIN SELECT @notify_email_operator_id = 0 SELECT @notify_level_email = 0 END -- Check operator to notify (via netsend) IF (@notify_netsend_operator_name IS NOT NULL) BEGIN SELECT @notify_netsend_operator_id = id FROM msdb.dbo.sysoperators WHERE (name = @notify_netsend_operator_name) IF (@notify_netsend_operator_id IS NULL) BEGIN RAISERROR(14234, -1, -1, '@notify_netsend_operator_name', 'sp_help_operator') RETURN(1) -- Failure END -- If a valid operator is specified the level must be non-zero IF (@notify_level_netsend = 0) BEGIN RAISERROR(14266, -1, -1, '@notify_level_netsend', '1, 2, 3') RETURN(1) -- Failure END END ELSE BEGIN SELECT @notify_netsend_operator_id = 0 SELECT @notify_level_netsend = 0 END -- Check operator to notify (via page) IF (@notify_page_operator_name IS NOT NULL) BEGIN SELECT @notify_page_operator_id = id FROM msdb.dbo.sysoperators WHERE (name = @notify_page_operator_name) IF (@notify_page_operator_id IS NULL) BEGIN RAISERROR(14234, -1, -1, '@notify_page_operator_name', 'sp_help_operator') RETURN(1) -- Failure END -- If a valid operator is specified the level must be non-zero IF (@notify_level_page = 0) BEGIN RAISERROR(14266, -1, -1, '@notify_level_page', '1, 2, 3') RETURN(1) -- Failure END END ELSE BEGIN SELECT @notify_page_operator_id = 0 SELECT @notify_level_page = 0 END -- Check delete level (must be 0, 1, 2 or 3) IF (@delete_level & 0x3 <> @delete_level) BEGIN RAISERROR(14266, -1, -1, '@delete_level', '0, 1, 2, 3') RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_job') AND (type = 'P'))) DROP PROCEDURE sp_add_job go CREATE PROCEDURE sp_add_job @job_name sysname, @enabled TINYINT = 1, -- 0 = Disabled, 1 = Enabled @description NVARCHAR(512) = NULL, @start_step_id INT = 1, @category_name sysname = NULL, @category_id INT = NULL, -- A language-independent way to specify which category to use @owner_login_name sysname = NULL, -- The procedure assigns a default @notify_level_eventlog INT = 2, -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always @notify_level_email INT = 0, -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always @notify_level_netsend INT = 0, -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always @notify_level_page INT = 0, -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always @notify_email_operator_name sysname = NULL, @notify_netsend_operator_name sysname = NULL, @notify_page_operator_name sysname = NULL, @delete_level INT = 0, -- 0 = Never, 1 = On Success, 2 = On Failure, 3 = Always @job_id UNIQUEIDENTIFIER = NULL OUTPUT, @originating_server sysname = NULL -- For SQLAgent use only AS BEGIN DECLARE @retval INT DECLARE @notify_email_operator_id INT DECLARE @notify_netsend_operator_id INT DECLARE @notify_page_operator_id INT DECLARE @owner_sid VARBINARY(85) DECLARE @originating_server_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters (except @owner_login_name) SELECT @originating_server = UPPER(LTRIM(RTRIM(@originating_server))) SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @description = LTRIM(RTRIM(@description)) SELECT @category_name = LTRIM(RTRIM(@category_name)) SELECT @notify_email_operator_name = LTRIM(RTRIM(@notify_email_operator_name)) SELECT @notify_netsend_operator_name = LTRIM(RTRIM(@notify_netsend_operator_name)) SELECT @notify_page_operator_name = LTRIM(RTRIM(@notify_page_operator_name)) SELECT @originating_server_id = NULL -- Turn [nullable] empty string parameters into NULLs IF (@originating_server = N'') SELECT @originating_server = NULL IF (@description = N'') SELECT @description = NULL IF (@category_name = N'') SELECT @category_name = NULL IF (@notify_email_operator_name = N'') SELECT @notify_email_operator_name = NULL IF (@notify_netsend_operator_name = N'') SELECT @notify_netsend_operator_name = NULL IF (@notify_page_operator_name = N'') SELECT @notify_page_operator_name = NULL IF (@originating_server IS NULL) OR (@originating_server = '(LOCAL)') SELECT @originating_server= UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) --only members of sysadmins role can set the owner IF (@owner_login_name IS NOT NULL AND ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) AND (@owner_login_name <> SUSER_SNAME()) BEGIN RAISERROR(14515, -1, -1) RETURN(1) -- Failure END -- Default the owner (if not supplied or if a non-sa is [illegally] trying to create a job for another user) -- allow special account only when caller is sysadmin IF (@owner_login_name = N'$(SQLAgentAccount)') AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) BEGIN SELECT @owner_sid = 0xFFFFFFFF END ELSE IF (@owner_login_name IS NULL) OR ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) AND (@owner_login_name <> SUSER_SNAME())) BEGIN SELECT @owner_sid = SUSER_SID() END ELSE BEGIN --force case insensitive comparation for NT users SELECT @owner_sid = SUSER_SID(@owner_login_name, 0) -- If @owner_login_name is invalid then SUSER_SID() will return NULL END -- Default the description (if not supplied) IF (@description IS NULL) SELECT @description = FORMATMESSAGE(14571) -- If a category ID is provided this overrides any supplied category name EXECUTE @retval = sp_verify_category_identifiers '@category_name', '@category_id', @category_name OUTPUT, @category_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check parameters EXECUTE @retval = sp_verify_job NULL, -- The job id is null since this is a new job @job_name, @enabled, @start_step_id, @category_name, @owner_sid OUTPUT, @notify_level_eventlog, @notify_level_email OUTPUT, @notify_level_netsend OUTPUT, @notify_level_page OUTPUT, @notify_email_operator_name, @notify_netsend_operator_name, @notify_page_operator_name, @delete_level, @category_id OUTPUT, @notify_email_operator_id OUTPUT, @notify_netsend_operator_id OUTPUT, @notify_page_operator_id OUTPUT, @originating_server OUTPUT IF (@retval <> 0) RETURN(1) -- Failure SELECT @originating_server_id = originating_server_id FROM msdb.dbo.sysoriginatingservers_view WHERE (originating_server = @originating_server) IF (@originating_server_id IS NULL) BEGIN RAISERROR(14370, -1, -1) RETURN(1) -- Failure END IF (@job_id IS NULL) BEGIN -- Assign the GUID SELECT @job_id = NEWID() END ELSE BEGIN -- A job ID has been provided, so check that the caller is SQLServerAgent (inserting an MSX job) IF (PROGRAM_NAME() NOT LIKE N'SQLAgent%') BEGIN RAISERROR(14274, -1, -1) RETURN(1) -- Failure END END INSERT INTO msdb.dbo.sysjobs (job_id, originating_server_id, name, enabled, description, start_step_id, category_id, owner_sid, notify_level_eventlog, notify_level_email, notify_level_netsend, notify_level_page, notify_email_operator_id, notify_netsend_operator_id, notify_page_operator_id, delete_level, date_created, date_modified, version_number) VALUES (@job_id, @originating_server_id, @job_name, @enabled, @description, @start_step_id, @category_id, @owner_sid, @notify_level_eventlog, @notify_level_email, @notify_level_netsend, @notify_level_page, @notify_email_operator_id, @notify_netsend_operator_id, @notify_page_operator_id, @delete_level, GETDATE(), GETDATE(), 1) -- Version number 1 SELECT @retval = @@error -- NOTE: We don't notify SQLServerAgent to update it's cache (we'll do this in sp_add_jobserver) RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_UPDATE_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_job') AND (type = 'P'))) DROP PROCEDURE sp_update_job go CREATE PROCEDURE sp_update_job @job_id UNIQUEIDENTIFIER = NULL, -- Must provide this or current_name @job_name sysname = NULL, -- Must provide this or job_id @new_name sysname = NULL, @enabled TINYINT = NULL, @description NVARCHAR(512) = NULL, @start_step_id INT = NULL, @category_name sysname = NULL, @owner_login_name sysname = NULL, @notify_level_eventlog INT = NULL, @notify_level_email INT = NULL, @notify_level_netsend INT = NULL, @notify_level_page INT = NULL, @notify_email_operator_name sysname = NULL, @notify_netsend_operator_name sysname = NULL, @notify_page_operator_name sysname = NULL, @delete_level INT = NULL, @automatic_post BIT = 1 -- Flag for SEM use only AS BEGIN DECLARE @retval INT DECLARE @category_id INT DECLARE @notify_email_operator_id INT DECLARE @notify_netsend_operator_id INT DECLARE @notify_page_operator_id INT DECLARE @owner_sid VARBINARY(85) DECLARE @alert_id INT DECLARE @cached_attribute_modified INT DECLARE @is_sysadmin INT DECLARE @current_owner sysname DECLARE @enable_only_used INT DECLARE @x_new_name sysname DECLARE @x_enabled TINYINT DECLARE @x_description NVARCHAR(512) DECLARE @x_start_step_id INT DECLARE @x_category_name sysname DECLARE @x_category_id INT DECLARE @x_owner_sid VARBINARY(85) DECLARE @x_notify_level_eventlog INT DECLARE @x_notify_level_email INT DECLARE @x_notify_level_netsend INT DECLARE @x_notify_level_page INT DECLARE @x_notify_email_operator_name sysname DECLARE @x_notify_netsnd_operator_name sysname DECLARE @x_notify_page_operator_name sysname DECLARE @x_delete_level INT DECLARE @x_originating_server_id INT -- Not updatable DECLARE @x_master_server BIT -- Remove any leading/trailing spaces from parameters (except @owner_login_name) SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @new_name = LTRIM(RTRIM(@new_name)) SELECT @description = LTRIM(RTRIM(@description)) SELECT @category_name = LTRIM(RTRIM(@category_name)) SELECT @notify_email_operator_name = LTRIM(RTRIM(@notify_email_operator_name)) SELECT @notify_netsend_operator_name = LTRIM(RTRIM(@notify_netsend_operator_name)) SELECT @notify_page_operator_name = LTRIM(RTRIM(@notify_page_operator_name)) SET NOCOUNT ON EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Are we modifying an attribute which SQLServerAgent caches? IF ((@new_name IS NOT NULL) OR (@enabled IS NOT NULL) OR (@start_step_id IS NOT NULL) OR (@owner_login_name IS NOT NULL) OR (@notify_level_eventlog IS NOT NULL) OR (@notify_level_email IS NOT NULL) OR (@notify_level_netsend IS NOT NULL) OR (@notify_level_page IS NOT NULL) OR (@notify_email_operator_name IS NOT NULL) OR (@notify_netsend_operator_name IS NOT NULL) OR (@notify_page_operator_name IS NOT NULL) OR (@delete_level IS NOT NULL)) SELECT @cached_attribute_modified = 1 ELSE SELECT @cached_attribute_modified = 0 -- Is @enable the only parameter used beside jobname and jobid? IF ((@enabled IS NOT NULL) AND (@new_name IS NULL) AND (@description IS NULL) AND (@start_step_id IS NULL) AND (@category_name IS NULL) AND (@owner_login_name IS NULL) AND (@notify_level_eventlog IS NULL) AND (@notify_level_email IS NULL) AND (@notify_level_netsend IS NULL) AND (@notify_level_page IS NULL) AND (@notify_email_operator_name IS NULL) AND (@notify_netsend_operator_name IS NULL) AND (@notify_page_operator_name IS NULL) AND (@delete_level IS NULL)) SELECT @enable_only_used = 1 ELSE SELECT @enable_only_used = 0 -- Set the x_ (existing) variables SELECT @x_new_name = sjv.name, @x_enabled = sjv.enabled, @x_description = sjv.description, @x_start_step_id = sjv.start_step_id, @x_category_name = sc.name, -- From syscategories @x_category_id = sc.category_id, -- From syscategories @x_owner_sid = sjv.owner_sid, @x_notify_level_eventlog = sjv.notify_level_eventlog, @x_notify_level_email = sjv.notify_level_email, @x_notify_level_netsend = sjv.notify_level_netsend, @x_notify_level_page = sjv.notify_level_page, @x_notify_email_operator_name = so1.name, -- From sysoperators @x_notify_netsnd_operator_name = so2.name, -- From sysoperators @x_notify_page_operator_name = so3.name, -- From sysoperators @x_delete_level = sjv.delete_level, @x_originating_server_id = sjv.originating_server_id, @x_master_server = master_server FROM msdb.dbo.sysjobs_view sjv LEFT OUTER JOIN msdb.dbo.sysoperators so1 ON (sjv.notify_email_operator_id = so1.id) LEFT OUTER JOIN msdb.dbo.sysoperators so2 ON (sjv.notify_netsend_operator_id = so2.id) LEFT OUTER JOIN msdb.dbo.sysoperators so3 ON (sjv.notify_page_operator_id = so3.id), msdb.dbo.syscategories sc WHERE (sjv.job_id = @job_id) AND (sjv.category_id = sc.category_id) -- Check authority (only SQLServerAgent can modify a non-local job) IF ((@x_master_server = 1) AND (PROGRAM_NAME() NOT LIKE N'SQLAgent%') ) BEGIN RAISERROR(14274, -1, -1) RETURN(1) -- Failure END -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReader and SQLAgentOperator roles that can see all jobs -- cannot modify jobs they do not own IF ( (@x_owner_sid <> SUSER_SID()) -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) -- is not sysadmin AND (@enable_only_used <> 1 OR ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) <> 1)) BEGIN RAISERROR(14525, -1, -1); RETURN(1) -- Failure END -- Check job category, only sysadmin can modify mutli-server jobs IF (EXISTS (SELECT * FROM msdb.dbo.syscategories WHERE (category_class = 1) -- Job AND (category_type = 2) -- Multi-Server AND (category_id = @x_category_id) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1))) -- is not sysadmin BEGIN RAISERROR(14396, -1, -1); RETURN(1) -- Failure END IF (@new_name = N'') SELECT @new_name = NULL -- Fill out the values for all non-supplied parameters from the existing values IF (@new_name IS NULL) SELECT @new_name = @x_new_name IF (@enabled IS NULL) SELECT @enabled = @x_enabled IF (@description IS NULL) SELECT @description = @x_description IF (@start_step_id IS NULL) SELECT @start_step_id = @x_start_step_id IF (@category_name IS NULL) SELECT @category_name = @x_category_name IF (@owner_sid IS NULL) SELECT @owner_sid = @x_owner_sid IF (@notify_level_eventlog IS NULL) SELECT @notify_level_eventlog = @x_notify_level_eventlog IF (@notify_level_email IS NULL) SELECT @notify_level_email = @x_notify_level_email IF (@notify_level_netsend IS NULL) SELECT @notify_level_netsend = @x_notify_level_netsend IF (@notify_level_page IS NULL) SELECT @notify_level_page = @x_notify_level_page IF (@notify_email_operator_name IS NULL) SELECT @notify_email_operator_name = @x_notify_email_operator_name IF (@notify_netsend_operator_name IS NULL) SELECT @notify_netsend_operator_name = @x_notify_netsnd_operator_name IF (@notify_page_operator_name IS NULL) SELECT @notify_page_operator_name = @x_notify_page_operator_name IF (@delete_level IS NULL) SELECT @delete_level = @x_delete_level -- If the SA is attempting to assign ownership of the job to someone else, then convert -- the login name to an ID IF (@owner_login_name = N'$(SQLAgentAccount)') AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) BEGIN SELECT @owner_sid = 0xFFFFFFFF END ELSE IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) AND (@owner_login_name IS NOT NULL)) BEGIN --force case insensitive comparation for NT users SELECT @owner_sid = SUSER_SID(@owner_login_name, 0) -- If @owner_login_name is invalid then SUSER_SID() will return NULL END -- Only the SA can re-assign jobs IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) AND (@owner_login_name IS NOT NULL)) RAISERROR(14242, -1, -1) -- Ownership of a multi-server job cannot be assigned to a non-sysadmin IF (@owner_login_name IS NOT NULL) AND (EXISTS (SELECT * FROM msdb.dbo.sysjobs sj, msdb.dbo.sysjobservers sjs WHERE (sj.job_id = sjs.job_id) AND (sj.job_id = @job_id) AND (sjs.server_id <> 0))) BEGIN IF (@owner_login_name = N'$(SQLAgentAccount)') -- allow distributed jobs to be assigned to special account BEGIN SELECT @is_sysadmin = 1 END ELSE BEGIN SELECT @is_sysadmin = 0 EXECUTE msdb.dbo.sp_sqlagent_has_server_access @login_name = @owner_login_name, @is_sysadmin_member = @is_sysadmin OUTPUT END IF (@is_sysadmin = 0) BEGIN SELECT @current_owner = dbo.SQLAGENT_SUSER_SNAME(@x_owner_sid) RAISERROR(14543, -1, -1, @current_owner, N'sysadmin') RETURN(1) -- Failure END END -- Turn [nullable] empty string parameters into NULLs IF (@description = N'') SELECT @description = NULL IF (@category_name = N'') SELECT @category_name = NULL IF (@notify_email_operator_name = N'') SELECT @notify_email_operator_name = NULL IF (@notify_netsend_operator_name = N'') SELECT @notify_netsend_operator_name = NULL IF (@notify_page_operator_name = N'') SELECT @notify_page_operator_name = NULL -- Check new values EXECUTE @retval = sp_verify_job @job_id, @new_name, @enabled, @start_step_id, @category_name, @owner_sid OUTPUT, @notify_level_eventlog, @notify_level_email OUTPUT, @notify_level_netsend OUTPUT, @notify_level_page OUTPUT, @notify_email_operator_name, @notify_netsend_operator_name, @notify_page_operator_name, @delete_level, @category_id OUTPUT, @notify_email_operator_id OUTPUT, @notify_netsend_operator_id OUTPUT, @notify_page_operator_id OUTPUT, NULL IF (@retval <> 0) RETURN(1) -- Failure BEGIN TRANSACTION -- If the job is being re-assigned, modify sysjobsteps.database_user_name as necessary IF (@owner_login_name IS NOT NULL) BEGIN IF (EXISTS (SELECT * FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (subsystem = N'TSQL'))) BEGIN IF (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (sid = @owner_sid) AND (sysadmin <> 1))) BEGIN -- The job is being re-assigned to an non-SA UPDATE msdb.dbo.sysjobsteps SET database_user_name = NULL WHERE (job_id = @job_id) AND (subsystem = N'TSQL') END END END UPDATE msdb.dbo.sysjobs SET name = @new_name, enabled = @enabled, description = @description, start_step_id = @start_step_id, category_id = @category_id, -- Returned from sp_verify_job owner_sid = @owner_sid, notify_level_eventlog = @notify_level_eventlog, notify_level_email = @notify_level_email, notify_level_netsend = @notify_level_netsend, notify_level_page = @notify_level_page, notify_email_operator_id = @notify_email_operator_id, -- Returned from sp_verify_job notify_netsend_operator_id = @notify_netsend_operator_id, -- Returned from sp_verify_job notify_page_operator_id = @notify_page_operator_id, -- Returned from sp_verify_job delete_level = @delete_level, version_number = version_number + 1, -- Update the job's version date_modified = GETDATE() -- Update the job's last-modified information WHERE (job_id = @job_id) SELECT @retval = @@error COMMIT TRANSACTION -- Always re-post the job if it's an auto-delete job (or if we're updating an auto-delete job -- to be non-auto-delete) IF (((SELECT delete_level FROM msdb.dbo.sysjobs WHERE (job_id = @job_id)) <> 0) OR ((@x_delete_level = 1) AND (@delete_level = 0))) EXECUTE msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id ELSE BEGIN -- Post the update to target servers IF (@automatic_post = 1) EXECUTE msdb.dbo.sp_post_msx_operation 'UPDATE', 'JOB', @job_id END -- Keep SQLServerAgent's cache in-sync -- NOTE: We only notify SQLServerAgent if we know the job has been cached and if -- attributes other than description or category have been changed (since -- SQLServerAgent doesn't cache these two) IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0) AND (@cached_attribute_modified = 1))) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @action_type = N'U' -- If the name was changed, make SQLServerAgent re-cache any alerts that reference the job -- since the alert cache contains the job name IF ((@job_name <> @new_name) AND (EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (job_id = @job_id)))) BEGIN DECLARE sysalerts_cache_update CURSOR LOCAL FOR SELECT id FROM msdb.dbo.sysalerts WHERE (job_id = @job_id) OPEN sysalerts_cache_update FETCH NEXT FROM sysalerts_cache_update INTO @alert_id WHILE (@@fetch_status = 0) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'U' FETCH NEXT FROM sysalerts_cache_update INTO @alert_id END DEALLOCATE sysalerts_cache_update END RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_DELETE_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_job') AND (type = 'P'))) DROP PROCEDURE sp_delete_job go CREATE PROCEDURE sp_delete_job @job_id UNIQUEIDENTIFIER = NULL, -- If provided should NOT also provide job_name @job_name sysname = NULL, -- If provided should NOT also provide job_id @originating_server sysname = NULL, -- Reserved (used by SQLAgent) @delete_history BIT = 1, -- Reserved (used by SQLAgent) @delete_unused_schedule BIT = 1 -- For backward compatibility schedules are deleted by default if they are not -- being used by another job. With the introduction of reusable schedules in V9 -- callers should set this to 0 so the schedule will be preserved for reuse. AS BEGIN DECLARE @current_msx_server sysname DECLARE @bMSX_job BIT DECLARE @retval INT DECLARE @local_machine_name sysname DECLARE @category_id INT DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @originating_server = UPPER(LTRIM(RTRIM(@originating_server))) -- Turn [nullable] empty string parameters into NULLs IF (@originating_server = N'') SELECT @originating_server = NULL -- Change server name to always reflect real servername or servername\instancename IF (@originating_server IS NOT NULL AND @originating_server = '(LOCAL)') SELECT @originating_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL)) BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END -- We need either a job name or a server name, not both IF ((@job_name IS NULL) AND (@originating_server IS NULL)) OR ((@job_name IS NOT NULL) AND (@originating_server IS NOT NULL)) BEGIN RAISERROR(14279, -1, -1) RETURN(1) -- Failure END -- Get category to see if it is a misc. replication agent. @category_id will be -- NULL if there is no @job_id. select @category_id = category_id from msdb.dbo.sysjobs where job_id = @job_id -- If job name was given, determine if the job is from an MSX IF (@job_id IS NOT NULL) BEGIN SELECT @bMSX_job = CASE UPPER(originating_server) WHEN UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) THEN 0 ELSE 1 END FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id) END -- If server name was given, warn user if different from current MSX IF (@originating_server IS NOT NULL) BEGIN EXECUTE @retval = master.dbo.xp_getnetname @local_machine_name OUTPUT IF (@retval <> 0) RETURN(1) -- Failure IF ((@originating_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName')))) OR (@originating_server = UPPER(@local_machine_name))) SELECT @originating_server = UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))) EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @current_msx_server OUTPUT, N'no_output' SELECT @current_msx_server = UPPER(@current_msx_server) -- If server name was given but it's not the current MSX, print a warning SELECT @current_msx_server = LTRIM(RTRIM(@current_msx_server)) IF ((@current_msx_server IS NOT NULL) AND (@current_msx_server <> N'') AND (@originating_server <> @current_msx_server)) RAISERROR(14224, 0, 1, @current_msx_server) END -- Check authority (only SQLServerAgent can delete a non-local job) IF (((@originating_server IS NOT NULL) AND (@originating_server <> UPPER(CONVERT(sysname, SERVERPROPERTY('ServerName'))))) OR (@bMSX_job = 1)) AND (PROGRAM_NAME() NOT LIKE N'SQLAgent%') BEGIN RAISERROR(14274, -1, -1) RETURN(1) -- Failure END -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReader and SQLAgentOperator roles that can see all jobs -- cannot delete jobs they do not own IF (@job_id IS NOT NULL) BEGIN IF (@job_owner_sid <> SUSER_SID() -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) -- is not sysadmin BEGIN RAISERROR(14525, -1, -1); RETURN(1) -- Failure END END -- Do the delete (for a specific job) IF (@job_id IS NOT NULL) BEGIN -- Note: This temp table is referenced by msdb.dbo.sp_delete_job_references CREATE TABLE #temp_jobs_to_delete (job_id UNIQUEIDENTIFIER NOT NULL, 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. DELETE FROM msdb.dbo.sysdownloadlist WHERE (object_id = @job_id) 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 --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) --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 @is_sysadmin 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 @is_sysadmin = ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) 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 @is_sysadmin, @job_owner, @job_id ELSE INSERT INTO @xp_results EXECUTE master.dbo.xp_sqlagent_enum_jobs @is_sysadmin, @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, SCEDULES, 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 < '1 Jan 1990 12:00:00am') OR (@date_created > '31 Dec 9999 11:59:59pm')) BEGIN RAISERROR(14266, -1, -1, '@date_created', '1/1/1990 12:00am .. 12/31/9999 11:59pm') RETURN(1) -- Failure END IF (@date_last_modified IS NOT NULL) AND ((@date_last_modified < '1 Jan 1990 12:00am') OR (@date_last_modified > 'Dec 31 9999 11:59:59pm')) BEGIN RAISERROR(14266, -1, -1, '@date_last_modified', '1/1/1990 12:00am .. 12/31/9999 11:59pm') RETURN(1) -- Failure END -- Generate results set... EXECUTE sp_get_composite_job_info @job_id, @job_type, @owner_login_name, @subsystem, @category_id, @enabled, @execution_status, @date_comparator, @date_created, @date_last_modified, @description END RETURN(0) -- Success END go CHECKPOINT go /**************************************************************/ /* sp_help_jobcount */ /* At least one parameter must be specified */ /* returns a one row/one column recordset with a integer */ /* representing the number of jobs assigned to the */ /* specified schedule */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_jobcount...' GO IF (NOT OBJECT_ID(N'dbo.sp_help_jobcount', 'P') IS NULL) DROP PROCEDURE dbo.sp_help_jobcount GO CREATE PROCEDURE sp_help_jobcount @schedule_name sysname = NULL, -- Specify if @schedule_id is null @schedule_id INT = NULL -- Specify if @schedule_name is null AS BEGIN SET NOCOUNT ON DECLARE @retval INT -- Check that we can uniquely identify the schedule. This only returns a schedule that is visible to this user EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = NULL, @orig_server_id = NULL IF (@retval <> 0) RETURN(1) -- Failure SELECT COUNT(*) AS JobCount FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) RETURN (0) -- 0 means success END go /**************************************************************/ /* sp_help_jobs_in_schedule */ /* At least one parameter must be specified to identify */ /* the schedule. Returns the same information as */ /* sp_help_job. Only jobs in the specified schedule are */ /* in the recordset */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_jobs_in_schedule...' GO IF (NOT OBJECT_ID(N'dbo.sp_help_jobs_in_schedule', 'P') IS NULL) DROP PROCEDURE dbo.sp_help_jobs_in_schedule GO CREATE PROCEDURE sp_help_jobs_in_schedule @schedule_name sysname = NULL, -- Specify if @schedule_id is null @schedule_id INT = NULL -- Specify if @schedule_name is null AS BEGIN SET NOCOUNT ON DECLARE @retval INT -- Check that we can uniquely identify the schedule. This only returns a schedule that is visible to this user EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = NULL, @orig_server_id = NULL IF (@retval <> 0) RETURN(1) -- Failure EXECUTE @retval = msdb.dbo.sp_get_composite_job_info @schedule_id = @schedule_id IF (@retval <> 0) RETURN(1) -- Failure RETURN (0) -- 0 means success END go /**************************************************************/ /* SP_MANAGE_JOBS_BY_LOGIN */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_manage_jobs_by_login...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_manage_jobs_by_login') AND (type = 'P'))) DROP PROCEDURE sp_manage_jobs_by_login go CREATE PROCEDURE sp_manage_jobs_by_login @action VARCHAR(10), -- DELETE or REASSIGN @current_owner_login_name sysname, @new_owner_login_name sysname = NULL AS BEGIN DECLARE @current_sid VARBINARY(85) DECLARE @new_sid VARBINARY(85) DECLARE @job_id UNIQUEIDENTIFIER DECLARE @rows_affected INT DECLARE @is_sysadmin INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @action = LTRIM(RTRIM(@action)) SELECT @current_owner_login_name = LTRIM(RTRIM(@current_owner_login_name)) SELECT @new_owner_login_name = LTRIM(RTRIM(@new_owner_login_name)) -- Turn [nullable] empty string parameters into NULLs IF (@new_owner_login_name = N'') SELECT @new_owner_login_name = NULL -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check action IF (@action NOT IN ('DELETE', 'REASSIGN')) BEGIN RAISERROR(14266, -1, -1, '@action', 'DELETE, REASSIGN') RETURN(1) -- Failure END -- Check parameter combinations IF ((@action = 'DELETE') AND (@new_owner_login_name IS NOT NULL)) RAISERROR(14281, 0, 1) IF ((@action = 'REASSIGN') AND (@new_owner_login_name IS NULL)) BEGIN RAISERROR(14237, -1, -1) RETURN(1) -- Failure END -- Check current login SELECT @current_sid = dbo.SQLAGENT_SUSER_SID(@current_owner_login_name) IF (@current_sid IS NULL) BEGIN RAISERROR(14262, -1, -1, '@current_owner_login_name', @current_owner_login_name) RETURN(1) -- Failure END -- Check new login (if supplied) IF (@new_owner_login_name IS NOT NULL) BEGIN SELECT @new_sid = dbo.SQLAGENT_SUSER_SID(@new_owner_login_name) IF (@new_sid IS NULL) BEGIN RAISERROR(14262, -1, -1, '@new_owner_login_name', @new_owner_login_name) RETURN(1) -- Failure END END IF (@action = 'DELETE') BEGIN DECLARE jobs_to_delete CURSOR LOCAL FOR SELECT job_id FROM msdb.dbo.sysjobs WHERE (owner_sid = @current_sid) OPEN jobs_to_delete FETCH NEXT FROM jobs_to_delete INTO @job_id SELECT @rows_affected = 0 WHILE (@@fetch_status = 0) BEGIN EXECUTE sp_delete_job @job_id = @job_id SELECT @rows_affected = @rows_affected + 1 FETCH NEXT FROM jobs_to_delete INTO @job_id END DEALLOCATE jobs_to_delete RAISERROR(14238, 0, 1, @rows_affected) END ELSE IF (@action = 'REASSIGN') BEGIN -- Check if the current owner owns any multi-server jobs. -- If they do, then the new owner must be member of the sysadmin role. IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs sj, msdb.dbo.sysjobservers sjs WHERE (sj.job_id = sjs.job_id) AND (sj.owner_sid = @current_sid) AND (sjs.server_id <> 0)) AND @new_sid <> 0xFFFFFFFF) -- speical account allowed for MSX jobs BEGIN SELECT @is_sysadmin = 0 EXECUTE msdb.dbo.sp_sqlagent_has_server_access @login_name = @new_owner_login_name, @is_sysadmin_member = @is_sysadmin OUTPUT IF (@is_sysadmin = 0) BEGIN RAISERROR(14543, -1, -1, @current_owner_login_name, N'sysadmin') RETURN(1) -- Failure END END UPDATE msdb.dbo.sysjobs SET owner_sid = @new_sid WHERE (owner_sid = @current_sid) RAISERROR(14239, 0, 1, @@rowcount, @new_owner_login_name) END RETURN(0) -- Success END go /**************************************************************/ /* SP_APPLY_JOB_TO_TARGETS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_apply_job_to_targets...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_apply_job_to_targets') AND (type = 'P'))) DROP PROCEDURE sp_apply_job_to_targets go CREATE PROCEDURE sp_apply_job_to_targets @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @target_server_groups NVARCHAR(2048) = NULL, -- A comma-separated list of target server groups @target_servers NVARCHAR(2048) = NULL, -- An comma-separated list of target servers @operation VARCHAR(7) = 'APPLY' -- Or 'REMOVE' AS BEGIN DECLARE @retval INT DECLARE @rows_affected INT DECLARE @server_name sysname DECLARE @groups NVARCHAR(2048) DECLARE @group sysname DECLARE @servers NVARCHAR(2048) DECLARE @server sysname DECLARE @pos_of_comma INT SET NOCOUNT ON -- Only a sysadmin can do this IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Remove any leading/trailing spaces from parameters SELECT @target_server_groups = LTRIM(RTRIM(@target_server_groups)) SELECT @target_servers = UPPER(LTRIM(RTRIM(@target_servers))) SELECT @operation = LTRIM(RTRIM(@operation)) -- Turn [nullable] empty string parameters into NULLs IF (@target_server_groups = NULL) SELECT @target_server_groups = NULL IF (@target_servers = NULL) SELECT @target_servers = NULL IF (@operation = NULL) SELECT @operation = NULL EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check operation type IF ((@operation <> 'APPLY') AND (@operation <> 'REMOVE')) BEGIN RAISERROR(14266, -1, -1, '@operation', 'APPLY, REMOVE') RETURN(1) -- Failure END -- Check that we have a target server group list and/or a target server list IF ((@target_server_groups IS NULL) AND (@target_servers IS NULL)) BEGIN RAISERROR(14283, -1, -1) RETURN(1) -- Failure END DECLARE @temp_groups TABLE (group_name sysname COLLATE database_default NOT NULL) DECLARE @temp_server_name TABLE (server_name sysname COLLATE database_default NOT NULL) -- Parse the Target Server comma-separated list (if supplied) IF (@target_servers IS NOT NULL) BEGIN SELECT @servers = @target_servers SELECT @pos_of_comma = CHARINDEX(N',', @servers) WHILE (@pos_of_comma <> 0) BEGIN SELECT @server = SUBSTRING(@servers, 1, @pos_of_comma - 1) INSERT INTO @temp_server_name (server_name) VALUES (LTRIM(RTRIM(@server))) SELECT @servers = RIGHT(@servers, (DATALENGTH(@servers) / 2) - @pos_of_comma) SELECT @pos_of_comma = CHARINDEX(N',', @servers) END INSERT INTO @temp_server_name (server_name) VALUES (LTRIM(RTRIM(@servers))) END -- Parse the Target Server Groups comma-separated list IF (@target_server_groups IS NOT NULL) BEGIN SELECT @groups = @target_server_groups SELECT @pos_of_comma = CHARINDEX(N',', @groups) WHILE (@pos_of_comma <> 0) BEGIN SELECT @group = SUBSTRING(@groups, 1, @pos_of_comma - 1) INSERT INTO @temp_groups (group_name) VALUES (LTRIM(RTRIM(@group))) SELECT @groups = RIGHT(@groups, (DATALENGTH(@groups) / 2) - @pos_of_comma) SELECT @pos_of_comma = CHARINDEX(N',', @groups) END INSERT INTO @temp_groups (group_name) VALUES (LTRIM(RTRIM(@groups))) END -- Check server groups SET ROWCOUNT 1 -- We do this so that we catch the FIRST invalid group SELECT @group = NULL SELECT @group = group_name FROM @temp_groups WHERE group_name NOT IN (SELECT name FROM msdb.dbo.systargetservergroups) IF (@group IS NOT NULL) BEGIN RAISERROR(14262, -1, -1, '@target_server_groups', @group) RETURN(1) -- Failure END SET ROWCOUNT 0 -- Find the distinct list of servers being targeted INSERT INTO @temp_server_name (server_name) SELECT DISTINCT sts.server_name FROM msdb.dbo.systargetservergroups stsg, msdb.dbo.systargetservergroupmembers stsgm, msdb.dbo.systargetservers sts WHERE (stsg.name IN (SELECT group_name FROM @temp_groups)) AND (stsg.servergroup_id = stsgm.servergroup_id) AND (stsgm.server_id = sts.server_id) AND (UPPER(sts.server_name) NOT IN (SELECT server_name FROM @temp_server_name)) IF (@operation = 'APPLY') BEGIN -- Remove those servers to which the job has already been applied DELETE FROM @temp_server_name WHERE server_name IN (SELECT sts.server_name FROM msdb.dbo.sysjobservers sjs, msdb.dbo.systargetservers sts WHERE (sjs.job_id = @job_id) AND (sjs.server_id = sts.server_id)) END IF (@operation = 'REMOVE') BEGIN -- Remove those servers to which the job is not currently applied DELETE FROM @temp_server_name WHERE server_name NOT IN (SELECT sts.server_name FROM msdb.dbo.sysjobservers sjs, msdb.dbo.systargetservers sts WHERE (sjs.job_id = @job_id) AND (sjs.server_id = sts.server_id)) END SELECT @rows_affected = COUNT(*) FROM @temp_server_name SET ROWCOUNT 1 WHILE (EXISTS (SELECT * FROM @temp_server_name)) BEGIN SELECT @server_name = server_name FROM @temp_server_name IF (@operation = 'APPLY') EXECUTE sp_add_jobserver @job_id = @job_id, @server_name = @server_name ELSE IF (@operation = 'REMOVE') EXECUTE sp_delete_jobserver @job_id = @job_id, @server_name = @server_name DELETE FROM @temp_server_name WHERE (server_name = @server_name) END SET ROWCOUNT 0 IF (@operation = 'APPLY') RAISERROR(14240, 0, 1, @rows_affected) IF (@operation = 'REMOVE') RAISERROR(14241, 0, 1, @rows_affected) RETURN(0) -- Success END go /**************************************************************/ /* SP_REMOVE_JOB_FROM_TARGETS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_remove_job_from_targets...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_remove_job_from_targets') AND (type = 'P'))) DROP PROCEDURE sp_remove_job_from_targets go CREATE PROCEDURE sp_remove_job_from_targets @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @target_server_groups NVARCHAR(1024) = NULL, -- A comma-separated list of target server groups @target_servers NVARCHAR(1024) = NULL -- A comma-separated list of target servers AS BEGIN DECLARE @retval INT SET NOCOUNT ON EXECUTE @retval = sp_apply_job_to_targets @job_id, @job_name, @target_server_groups, @target_servers, 'REMOVE' RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_GET_JOB_ALERTS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_job_alerts...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_get_job_alerts') AND (type = 'P'))) DROP PROCEDURE sp_get_job_alerts go CREATE PROCEDURE sp_get_job_alerts @job_id UNIQUEIDENTIFIER = NULL, @job_name sysname = NULL AS BEGIN DECLARE @retval INT EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure SELECT id, name, enabled, type = CASE ISNULL(performance_condition, '!') WHEN '!' THEN 1 -- SQL Server event alert ELSE CASE event_id WHEN 8 THEN 3 -- WMI event alert ELSE 2 -- SQL Server performance condition alert END END FROM msdb.dbo.sysalerts WHERE (job_id = @job_id) RETURN(0) -- Success END go /**************************************************************/ /* */ /* S U P P O R T P R O C E D U R E S */ /* */ /**************************************************************/ /**************************************************************/ /* SP_CONVERT_JOBID_TO_CHAR [used by SEM only] */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_convert_jobid_to_char...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_convert_jobid_to_char') AND (type = 'P'))) DROP PROCEDURE sp_convert_jobid_to_char go CREATE PROCEDURE sp_convert_jobid_to_char @job_id UNIQUEIDENTIFIER, @job_id_as_char NVARCHAR(34) OUTPUT -- 34 because of the leading '0x' AS BEGIN DECLARE @job_id_as_binary BINARY(16) DECLARE @temp NCHAR(8) DECLARE @counter INT DECLARE @byte_value INT DECLARE @high_word INT DECLARE @low_word INT DECLARE @high_high_nybble INT DECLARE @high_low_nybble INT DECLARE @low_high_nybble INT DECLARE @low_low_nybble INT SET NOCOUNT ON SELECT @job_id_as_binary = CONVERT(BINARY(16), @job_id) SELECT @temp = CONVERT(NCHAR(8), @job_id_as_binary) SELECT @job_id_as_char = N'' SELECT @counter = 1 WHILE (@counter <= (DATALENGTH(@temp) / 2)) BEGIN SELECT @byte_value = CONVERT(INT, CONVERT(BINARY(2), SUBSTRING(@temp, @counter, 1))) SELECT @high_word = (@byte_value & 0xff00) / 0x100 SELECT @low_word = (@byte_value & 0x00ff) SELECT @high_high_nybble = (@high_word & 0xff) / 16 SELECT @high_low_nybble = (@high_word & 0xff) % 16 SELECT @low_high_nybble = (@low_word & 0xff) / 16 SELECT @low_low_nybble = (@low_word & 0xff) % 16 IF (@high_high_nybble < 10) SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('0') + @high_high_nybble) ELSE SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('A') + (@high_high_nybble - 10)) IF (@high_low_nybble < 10) SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('0') + @high_low_nybble) ELSE SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('A') + (@high_low_nybble - 10)) IF (@low_high_nybble < 10) SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('0') + @low_high_nybble) ELSE SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('A') + (@low_high_nybble - 10)) IF (@low_low_nybble < 10) SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('0') + @low_low_nybble) ELSE SELECT @job_id_as_char = @job_id_as_char + NCHAR(ASCII('A') + (@low_low_nybble - 10)) SELECT @counter = @counter + 1 END SELECT @job_id_as_char = N'0x' + LOWER(@job_id_as_char) END go /**************************************************************/ /* SP_START_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_start_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_start_job') AND (type = 'P'))) DROP PROCEDURE sp_start_job go CREATE PROCEDURE sp_start_job @job_name sysname = NULL, @job_id UNIQUEIDENTIFIER = NULL, @error_flag INT = 1, -- Set to 0 to suppress the error from sp_sqlagent_notify if SQLServerAgent is not running @server_name sysname = NULL, -- The specific target server to start the [multi-server] job on @step_name sysname = NULL, -- The name of the job step to start execution with [for use with a local job only] @output_flag INT = 1 -- Set to 0 to suppress the success message AS BEGIN DECLARE @job_id_as_char VARCHAR(36) DECLARE @retval INT DECLARE @step_id INT DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name))) SELECT @step_name = LTRIM(RTRIM(@step_name)) -- Turn [nullable] empty string parameters into NULLs IF (@job_name = N'') SELECT @job_name = NULL IF (@server_name = N'') SELECT @server_name = NULL IF (@step_name = N'') SELECT @step_name = NULL EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReader role can see all jobs but -- cannot start/stop jobs they do not own IF (@job_owner_sid <> SUSER_SID() -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) -- is not sysadmin AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0)) -- is not SQLAgentOperatorRole BEGIN RAISERROR(14393, -1, -1); RETURN(1) -- Failure END IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id))) BEGIN SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) RAISERROR(14256, -1, -1, @job_name, @job_id_as_char) RETURN(1) -- Failure END IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN -- The job is local, so start (run) the job locally -- Check the step name (if supplied) IF (@step_name IS NOT NULL) BEGIN SELECT @step_id = step_id FROM msdb.dbo.sysjobsteps WHERE (step_name = @step_name) AND (job_id = @job_id) IF (@step_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@step_name', @step_name) RETURN(1) -- Failure END END EXECUTE @retval = msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @schedule_id = @step_id, -- This is the start step @action_type = N'S', @error_flag = @error_flag IF ((@retval = 0) AND (@output_flag = 1)) RAISERROR(14243, 0, 1, @job_name) END ELSE BEGIN -- The job is a multi-server job -- Only sysadmin can start multi-server job IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(14397, -1, -1); RETURN(1) -- Failure END -- Check target server name (if any) IF (@server_name IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name))) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END END -- Re-post the job if it's an auto-delete job IF ((SELECT delete_level FROM msdb.dbo.sysjobs WHERE (job_id = @job_id)) <> 0) EXECUTE @retval = msdb.dbo.sp_post_msx_operation 'INSERT', 'JOB', @job_id, @server_name -- Post start instruction(s) EXECUTE @retval = msdb.dbo.sp_post_msx_operation 'START', 'JOB', @job_id, @server_name END RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_STOP_JOB */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_stop_job...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_stop_job') AND (type = 'P'))) DROP PROCEDURE sp_stop_job go CREATE PROCEDURE sp_stop_job @job_name sysname = NULL, @job_id UNIQUEIDENTIFIER = NULL, @originating_server sysname = NULL, -- So that we can stop ALL jobs that came from the given server @server_name sysname = NULL -- The specific target server to stop the [multi-server] job on AS BEGIN DECLARE @job_id_as_char VARCHAR(36) DECLARE @retval INT DECLARE @num_parameters INT DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @originating_server = UPPER(LTRIM(RTRIM(@originating_server))) SELECT @server_name = UPPER(LTRIM(RTRIM(@server_name))) -- Turn [nullable] empty string parameters into NULLs IF (@job_name = N'') SELECT @job_name = NULL IF (@originating_server = N'') SELECT @originating_server = NULL IF (@server_name = N'') SELECT @server_name = NULL -- We must have EITHER a job id OR a job name OR an originating server SELECT @num_parameters = 0 IF (@job_id IS NOT NULL) SELECT @num_parameters = @num_parameters + 1 IF (@job_name IS NOT NULL) SELECT @num_parameters = @num_parameters + 1 IF (@originating_server IS NOT NULL) SELECT @num_parameters = @num_parameters + 1 IF (@num_parameters <> 1) BEGIN RAISERROR(14232, -1, -1) RETURN(1) -- Failure END IF (@originating_server IS NOT NULL) BEGIN -- Stop (cancel) ALL local jobs that originated from the specified server IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs_view WHERE (originating_server = @originating_server))) BEGIN RAISERROR(14268, -1, -1, @originating_server) RETURN(1) -- Failure END -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReader role that can see all jobs but -- cannot start/stop jobs they do not own IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) -- is not sysadmin AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0)) -- is not SQLAgentOperatorRole BEGIN RAISERROR(14393, -1, -1); RETURN(1) -- Failure END DECLARE @total_counter INT DECLARE @success_counter INT DECLARE stop_jobs CURSOR LOCAL FOR SELECT job_id FROM msdb.dbo.sysjobs_view WHERE (originating_server = @originating_server) SELECT @total_counter = 0, @success_counter = 0 OPEN stop_jobs FETCH NEXT FROM stop_jobs INTO @job_id WHILE (@@fetch_status = 0) BEGIN SELECT @total_counter + @total_counter + 1 EXECUTE @retval = msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @action_type = N'C' IF (@retval = 0) SELECT @success_counter = @success_counter + 1 FETCH NEXT FROM stop_jobs INTO @job_id END RAISERROR(14253, 0, 1, @success_counter, @total_counter) DEALLOCATE stop_jobs RETURN(0) -- 0 means success END ELSE BEGIN -- Stop ONLY the specified job EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id))) BEGIN SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) RAISERROR(14257, -1, -1, @job_name, @job_id_as_char) RETURN(1) -- Failure END -- Check permissions beyond what's checked by the sysjobs_view -- SQLAgentReader role that can see all jobs but -- cannot start/stop jobs they do not own IF (@job_owner_sid <> SUSER_SID() -- does not own the job AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) -- is not sysadmin AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0)) -- is not SQLAgentOperatorRole BEGIN RAISERROR(14393, -1, -1); RETURN(1) -- Failure END IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN -- The job is local, so stop (cancel) the job locally EXECUTE @retval = msdb.dbo.sp_sqlagent_notify @op_type = N'J', @job_id = @job_id, @action_type = N'C' IF (@retval = 0) RAISERROR(14254, 0, 1, @job_name) END ELSE BEGIN -- The job is a multi-server job -- Only sysadmin can stop multi-server job IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(14397, -1, -1); RETURN(1) -- Failure END -- Check target server name (if any) IF (@server_name IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name))) BEGIN RAISERROR(14262, -1, -1, '@server_name', @server_name) RETURN(1) -- Failure END END -- Post the stop instruction(s) EXECUTE @retval = sp_post_msx_operation 'STOP', 'JOB', @job_id, @server_name END RETURN(@retval) -- 0 means success END END go /**************************************************************/ /* SP_CYCLE_AGENT_ERRORLOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_cycle_agent_errorlog...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_cycle_agent_errorlog') AND (type = 'P'))) DROP PROCEDURE sp_cycle_agent_errorlog go CREATE PROCEDURE sp_cycle_agent_errorlog AS BEGIN exec sp_sqlagent_notify N'L' END go /**************************************************************/ /* SP_GET_CHUNKED_JOBSTEP_PARAMS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_chunked_jobstep_params...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_get_chunked_jobstep_params') AND (type = 'P'))) DROP PROCEDURE sp_get_chunked_jobstep_params go CREATE PROCEDURE sp_get_chunked_jobstep_params @job_name sysname, @step_id INT = 1 AS BEGIN DECLARE @job_id UNIQUEIDENTIFIER DECLARE @step_id_as_char VARCHAR(10) DECLARE @text_pointer VARBINARY(16) DECLARE @remaining_length INT DECLARE @offset INT DECLARE @chunk INT DECLARE @retval INT SET NOCOUNT ON -- Check that the job exists EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check that the step exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @step_id))) BEGIN SELECT @step_id_as_char = CONVERT(VARCHAR(10), @step_id) RAISERROR(14262, -1, -1, '@step_id', @step_id_as_char) RETURN(1) -- Failure END -- Return the sysjobsteps.additional_parameters TEXT column as multiple readtexts of -- length 2048 SELECT @text_pointer = TEXTPTR(additional_parameters), @remaining_length = (DATALENGTH(additional_parameters) / 2) FROM msdb.dbo.sysjobsteps WHERE (job_id = @job_id) AND (step_id = @step_id) SELECT @offset = 0, @chunk = 100 -- Get all the chunks of @chunk size WHILE (@remaining_length > @chunk) BEGIN READTEXT msdb.dbo.sysjobsteps.additional_parameters @text_pointer @offset @chunk SELECT @offset = @offset + @chunk SELECT @remaining_length = @remaining_length - @chunk END -- Get the last chunk IF (@remaining_length > 0) READTEXT msdb.dbo.sysjobsteps.additional_parameters @text_pointer @offset @remaining_length RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_CHECK_FOR_OWNED_JOBS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_check_for_owned_jobs...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_check_for_owned_jobs') AND (type = 'P'))) DROP PROCEDURE sp_check_for_owned_jobs go CREATE PROCEDURE sp_check_for_owned_jobs @login_name sysname, @table_name sysname AS BEGIN SET NOCOUNT ON -- This procedure is called by sp_droplogin to check if the login being dropped -- still owns jobs. The return value (the number of jobs owned) is passed back -- via the supplied table name [this cumbersome approach is necessary because -- sp_check_for_owned_jobs is invoked via an EXEC() and because we always want -- sp_droplogin to work, even if msdb and/or sysjobs does not exist]. IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysjobs') AND (type = 'U'))) BEGIN DECLARE @sql NVARCHAR(1024) SET @sql = N'INSERT INTO ' + QUOTENAME(@table_name, N'[') + N' SELECT COUNT(*) FROM msdb.dbo.sysjobs WHERE (owner_sid = SUSER_SID(N' + QUOTENAME(@login_name, '''') + ', 0))' --force case insensitive comparation for NT users EXEC sp_executesql @statement = @sql END END go /**************************************************************/ /* SP_CHECK_FOR_OWNED_JOBSTEPS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_check_for_owned_jobsteps...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_check_for_owned_jobsteps') AND (type = 'P'))) DROP PROCEDURE sp_check_for_owned_jobsteps go CREATE PROCEDURE sp_check_for_owned_jobsteps @login_name sysname = NULL, -- Supply this OR the database_X parameters, but not both @database_name sysname = NULL, @database_user_name sysname = NULL AS BEGIN DECLARE @db_name NVARCHAR(255) DECLARE @escaped_db_name NVARCHAR(255) 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 @login_name = REPLACE(@login_name, N'''', N'''''') WHILE (@@fetch_status = 0) BEGIN SELECT @escaped_db_name = QUOTENAME(@db_name, N'[') SELECT @db_name = REPLACE(@db_name, '''', '''''') EXECUTE(N'INSERT INTO #work_table SELECT N''' + @db_name + N''', name FROM ' + @escaped_db_name + N'.dbo.sysusers WHERE (sid = SUSER_SID(N''' + @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.sysjobs_view sjv, msdb.dbo.sysjobservers sjs 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 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 @rows_to_delete_as_char VARCHAR(10) DECLARE @current_rows INT DECLARE @current_rows_per_job INT DECLARE @job_id_as_char VARCHAR(36) SET NOCOUNT ON SELECT @job_id_as_char = CONVERT(VARCHAR(36), @job_id) -- 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 SELECT @rows_to_delete_as_char = CONVERT(VARCHAR, @rows_to_delete) IF (@rows_to_delete > 0) BEGIN EXECUTE ('DECLARE @new_oldest_id INT SET NOCOUNT ON SET ROWCOUNT ' + @rows_to_delete_as_char + 'SELECT @new_oldest_id = instance_id FROM msdb.dbo.sysjobhistory WHERE (job_id = ''' + @job_id_as_char + ''') ' + 'ORDER BY instance_id SET ROWCOUNT 0 DELETE FROM msdb.dbo.sysjobhistory WHERE (job_id = ''' + @job_id_as_char + ''')' + ' AND (instance_id <= @new_oldest_id)') 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 SELECT @rows_to_delete_as_char = CONVERT(VARCHAR, @rows_to_delete) IF (@rows_to_delete > 0) BEGIN EXECUTE ('DECLARE @new_oldest_id INT SET NOCOUNT ON SET ROWCOUNT ' + @rows_to_delete_as_char + 'SELECT @new_oldest_id = instance_id FROM msdb.dbo.sysjobhistory ORDER BY instance_id SET ROWCOUNT 0 DELETE FROM msdb.dbo.sysjobhistory WHERE (instance_id <= @new_oldest_id)') 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(1024) = 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 @job_id_as_char VARCHAR(36) DECLARE @step_id_as_char VARCHAR(10) 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 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 SELECT @step_id_as_char = CONVERT(VARCHAR, @step_id) RAISERROR(14262, @error_severity, -1, '@step_id', @step_id_as_char) RETURN(1) -- Failure END END ELSE SELECT @step_name = FORMATMESSAGE(14570) -- Check run_status IF (@run_status NOT IN (0, 1, 2, 3, 4, 5)) -- SQLAGENT_EXEC_X code BEGIN RAISERROR(14266, @error_severity, -1, '@run_status', '0, 1, 2, 3, 4, 5') RETURN(1) -- Failure END -- Check run_date EXECUTE @retval = sp_verify_job_date @run_date, '@run_date', 10 IF (@retval <> 0) RETURN(1) -- Failure -- Check run_time EXECUTE @retval = sp_verify_job_time @run_time, '@run_time', 10 IF (@retval <> 0) RETURN(1) -- Failure -- Check operator_id_emailed IF (@operator_id_emailed <> 0) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (id = @operator_id_emailed))) BEGIN SELECT @operator_id_as_char = CONVERT(VARCHAR, @operator_id_emailed) RAISERROR(14262, @error_severity, -1, '@operator_id_emailed', @operator_id_as_char) RETURN(1) -- Failure END END -- Check operator_id_netsent IF (@operator_id_netsent <> 0) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (id = @operator_id_netsent))) BEGIN SELECT @operator_id_as_char = CONVERT(VARCHAR, @operator_id_netsent) RAISERROR(14262, @error_severity, -1, '@operator_id_netsent', @operator_id_as_char) RETURN(1) -- Failure END END -- Check operator_id_paged IF (@operator_id_paged <> 0) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (id = @operator_id_paged))) BEGIN SELECT @operator_id_as_char = CONVERT(VARCHAR, @operator_id_paged) RAISERROR(14262, @error_severity, -1, '@operator_id_paged', @operator_id_as_char) RETURN(1) -- Failure END END -- Insert the history row INSERT INTO msdb.dbo.sysjobhistory (job_id, step_id, step_name, sql_message_id, sql_severity, message, run_status, run_date, run_time, run_duration, operator_id_emailed, operator_id_netsent, operator_id_paged, retries_attempted, server) VALUES (@job_id, @step_id, @step_name, @sql_message_id, @sql_severity, @message, @run_status, @run_date, @run_time, @run_duration, @operator_id_emailed, @operator_id_netsent, @operator_id_paged, @retries_attempted, @server) -- Update sysjobactivity table IF (@step_id = 0) --only update for job, not for each step BEGIN UPDATE msdb.dbo.sysjobactivity SET stop_execution_date = DATEADD(ms, -DATEPART(ms, GetDate()), GetDate()), job_history_id = SCOPE_IDENTITY() WHERE session_id = @session_id AND job_id = @job_id END -- Special handling of replication jobs DECLARE @job_name sysname DECLARE @category_id int SELECT @job_name = name, @category_id = category_id from msdb.dbo.sysjobs WHERE job_id = @job_id -- If replicatio agents (snapshot, logreader, distribution, merge, and queuereader -- and the step has been canceled and if we are at the distributor. IF @category_id in (10,13,14,15,19) and @run_status = 3 and object_id('MSdistributiondbs') is not null BEGIN -- Get the database DECLARE @database sysname SELECT @database = database_name from sysjobsteps where job_id = @job_id and lower(subsystem) in (N'distribution', N'logreader','snapshot',N'merge', N'queuereader') -- If the database is a distribution database IF EXISTS (select * from MSdistributiondbs where name = @database) BEGIN DECLARE @proc nvarchar(500) SELECT @proc = quotename(@database) + N'.dbo.sp_MSlog_agent_cancel' EXEC @proc @job_id = @job_id, @category_id = @category_id, @message = @message END END -- Delete any history rows that are over the registry-defined limits EXECUTE msdb.dbo.sp_jobhistory_row_limiter @job_id RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_SQLAGENT_CHECK_MSX_VERSION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_check_msx_version...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_sqlagent_check_msx_version') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_check_msx_version go CREATE PROCEDURE sp_sqlagent_check_msx_version @required_microsoft_version INT = NULL AS BEGIN SET NOCOUNT ON DECLARE @msx_version NVARCHAR(16) DECLARE @required_msx_version NVARCHAR(16) IF (@required_microsoft_version IS NULL) SELECT @required_microsoft_version = 0x07000252 -- 7.0.594 IF (@@microsoftversion < @required_microsoft_version) BEGIN SELECT @msx_version = CONVERT( NVARCHAR(2), CONVERT( INT, CONVERT( BINARY(1), @@microsoftversion / 0x1000000 ) ) ) + N'.' + CONVERT( NVARCHAR(2), CONVERT( INT, CONVERT( BINARY(1), CONVERT( BINARY(2), ((@@microsoftversion / 0x10000) % 0x100) ) ) ) ) + N'.' + CONVERT( NVARCHAR(4), @@microsoftversion % 0x10000 ) SELECT @required_msx_version = CONVERT( NVARCHAR(2), CONVERT( INT, CONVERT( BINARY(1), @required_microsoft_version / 0x1000000 ) ) ) + N'.' + CONVERT( NVARCHAR(2), CONVERT( INT, CONVERT( BINARY(1), CONVERT( BINARY(2), ((@required_microsoft_version / 0x10000) % 0x100) ) ) ) ) + N'.' + CONVERT( NVARCHAR(4), @required_microsoft_version % 0x10000 ) RAISERROR(14541, -1, -1, @msx_version, @required_msx_version) RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_SQLAGENT_PROBE_MSX */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_sqlagent_probe_msx...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_sqlagent_probe_msx') AND (type = 'P'))) DROP PROCEDURE sp_sqlagent_probe_msx go CREATE PROCEDURE sp_sqlagent_probe_msx @server_name sysname, -- The name of the target server probing the MSX @local_time NVARCHAR(100), -- The local time at the target server in the format YYYY/MM/DD HH:MM:SS @poll_interval INT, -- The frequency (in seconds) with which the target polls the MSX @time_zone_adjustment INT = NULL -- The offset from GMT in minutes (may be NULL if unknown) AS BEGIN DECLARE @bad_enlistment BIT DECLARE @blocking_instructions INT DECLARE @pending_instructions INT SET NOCOUNT ON SELECT @server_name = UPPER(@server_name) SELECT @bad_enlistment = 0, @blocking_instructions = 0, @pending_instructions = 0 UPDATE msdb.dbo.systargetservers SET last_poll_date = GETDATE(), local_time_at_last_poll = CONVERT(DATETIME, @local_time, 111), poll_interval = @poll_interval, time_zone_adjustment = ISNULL(@time_zone_adjustment, time_zone_adjustment) WHERE (UPPER(server_name) = @server_name) -- If the systargetservers entry is missing (and no DEFECT instruction has been posted) -- then the enlistment is bad IF (NOT EXISTS (SELECT 1 FROM msdb.dbo.systargetservers WHERE (UPPER(server_name) = @server_name))) AND (NOT EXISTS (SELECT 1 FROM msdb.dbo.sysdownloadlist WHERE (target_server = @server_name) AND (operation_code = 7) AND (object_type = 2))) SELECT @bad_enlistment = 1 SELECT @blocking_instructions = COUNT(*) FROM msdb.dbo.sysdownloadlist WHERE (target_server = @server_name) AND (error_message IS NOT NULL) SELECT @pending_instructions = COUNT(*) FROM msdb.dbo.sysdownloadlist WHERE (target_server = @server_name) AND (error_message IS NULL) AND (status = 0) SELECT @bad_enlistment, @blocking_instructions, @pending_instructions END go /**************************************************************/ /* SP_SET_LOCAL_TIME */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_set_local_time...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_set_local_time') AND (type = 'P'))) DROP PROCEDURE sp_set_local_time go CREATE PROCEDURE sp_set_local_time @server_name sysname = NULL, @adjustment_in_minutes INT = 0 -- Only needed for Win9x AS BEGIN DECLARE @ret INT DECLARE @local_time INT DECLARE @local_date INT DECLARE @current_datetime DATETIME DECLARE @local_time_sz VARCHAR(30) DECLARE @cmd NVARCHAR(200) DECLARE @date_format NVARCHAR(64) DECLARE @year_sz NVARCHAR(16) DECLARE @month_sz NVARCHAR(16) DECLARE @day_sz NVARCHAR(16) -- Synchronize the clock with the remote server (if supplied) -- NOTE: NT takes timezones into account, whereas Win9x does not IF (@server_name IS NOT NULL) BEGIN SELECT @cmd = N'net time \\' + @server_name + N' /set /y' EXECUTE @ret = master.dbo.xp_cmdshell @cmd, no_output IF (@ret <> 0) RETURN(1) -- Failure END -- Since NET TIME on Win9x does not take time zones into account we need to manually adjust -- for this using @adjustment_in_minutes which will be the difference between the MSX GMT -- offset and the target server GMT offset IF ((PLATFORM() & 0x2) = 0x2) -- Win9x BEGIN -- Get the date format from the registry (so that we can construct our DATE command-line command) EXECUTE master.dbo.xp_regread N'HKEY_CURRENT_USER', N'Control Panel\International', N'sShortDate', @date_format OUTPUT, N'no_output' SELECT @date_format = LOWER(@date_format) IF (@adjustment_in_minutes <> 0) BEGIN -- Wait for SQLServer to re-cache the OS time WAITFOR DELAY '00:01:00' SELECT @current_datetime = DATEADD(mi, @adjustment_in_minutes, GETDATE()) SELECT @local_time_sz = SUBSTRING(CONVERT(VARCHAR, @current_datetime, 8), 1, 5) SELECT @local_time = CONVERT(INT, LTRIM(SUBSTRING(@local_time_sz, 1, PATINDEX('%:%', @local_time_sz) - 1) + SUBSTRING(@local_time_sz, PATINDEX('%:%', @local_time_sz) + 1, 2))) SELECT @local_date = CONVERT(INT, CONVERT(VARCHAR, @current_datetime, 112)) -- Set the date SELECT @year_sz = CONVERT(NVARCHAR, @local_date / 10000) SELECT @month_sz = CONVERT(NVARCHAR, (@local_date % 10000) / 100) SELECT @day_sz = CONVERT(NVARCHAR, @local_date % 100) IF (@date_format LIKE N'y%m%d') SELECT @cmd = N'DATE ' + @year_sz + N'-' + @month_sz + N'-' + @day_sz IF (@date_format LIKE N'y%d%m') SELECT @cmd = N'DATE ' + @year_sz + N'-' + @day_sz + N'-' + @month_sz IF (@date_format LIKE N'm%d%y') SELECT @cmd = N'DATE ' + @month_sz + N'-' + @day_sz + N'-' + @year_sz IF (@date_format LIKE N'd%m%y') SELECT @cmd = N'DATE ' + @day_sz + N'-' + @month_sz + N'-' + @year_sz EXECUTE @ret = master.dbo.xp_cmdshell @cmd, no_output IF (@ret <> 0) RETURN 1 -- Failure -- Set the time (NOTE: We can't set the millisecond part of the time, so we may be up to .999 sec off) SELECT @cmd = N'TIME ' + CONVERT(NVARCHAR, @local_time / 100) + N':' + CONVERT(NVARCHAR, @local_time % 100) + ':' + CONVERT(NVARCHAR(2), DATEPART(SS, GETDATE())) EXECUTE @ret = master.dbo.xp_cmdshell @cmd, no_output IF (@ret <> 0) RETURN 1 -- Failure END END RETURN(0) -- Success END go /**************************************************************/ /* SP_MULTI_SERVER_JOB_SUMMARY [used by SEM only] */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_multi_server_job_summary...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_multi_server_job_summary') AND (type = 'P'))) DROP PROCEDURE sp_multi_server_job_summary go CREATE PROCEDURE sp_multi_server_job_summary @job_id UNIQUEIDENTIFIER = NULL, @job_name sysname = NULL AS BEGIN DECLARE @retval INT SET NOCOUNT ON IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL)) BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END -- NOTE: We join with syscategories - not sysjobservers - since we want to include jobs -- which are of type multi-server but which don't currently have any servers SELECT 'job_id' = sj.job_id, 'job_name' = sj.name, 'enabled' = sj.enabled, 'category_name' = sc.name, 'target_servers' = (SELECT COUNT(*) FROM msdb.dbo.sysjobservers sjs WHERE (sjs.job_id = sj.job_id)), 'pending_download_instructions' = (SELECT COUNT(*) FROM msdb.dbo.sysdownloadlist sdl WHERE (sdl.object_id = sj.job_id) AND (status = 0)), 'download_errors' = (SELECT COUNT(*) FROM msdb.dbo.sysdownloadlist sdl WHERE (sdl.object_id = sj.job_id) AND (sdl.error_message IS NOT NULL)), 'execution_failures' = (SELECT COUNT(*) FROM msdb.dbo.sysjobservers sjs WHERE (sjs.job_id = sj.job_id) AND (sjs.last_run_date <> 0) AND (sjs.last_run_outcome <> 1)) -- 1 is success FROM msdb.dbo.sysjobs sj, msdb.dbo.syscategories sc WHERE (sj.category_id = sc.category_id) AND (sc.category_class = 1) -- JOB AND (sc.category_type = 2) -- Multi-Server AND ((@job_id IS NULL) OR (sj.job_id = @job_id)) AND ((@job_name IS NULL) OR (sj.name = @job_name)) RETURN(0) -- Success END go /**************************************************************/ /* SP_TARGET_SERVER_SUMMARY [used by SEM only] */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_target_server_summary...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_target_server_summary') AND (type = 'P'))) DROP PROCEDURE sp_target_server_summary go CREATE PROCEDURE sp_target_server_summary @target_server sysname = NULL AS BEGIN SET NOCOUNT ON SELECT server_id, server_name, 'local_time' = DATEADD(SS, DATEDIFF(SS, last_poll_date, GETDATE()), local_time_at_last_poll), last_poll_date, 'unread_instructions' = (SELECT COUNT(*) FROM msdb.dbo.sysdownloadlist sdl WHERE (UPPER(sdl.target_server) = UPPER(sts.server_name)) AND (sdl.status = 0)), 'blocked' = (SELECT COUNT(*) FROM msdb.dbo.sysdownloadlist sdl WHERE (UPPER(sdl.target_server) = UPPER(sts.server_name)) AND (sdl.error_message IS NOT NULL)), poll_interval FROM msdb.dbo.systargetservers sts WHERE ((@target_server IS NULL) OR (UPPER(@target_server) = UPPER(sts.server_name))) END go CHECKPOINT go /**************************************************************/ /* */ /* 6 . X P R O C E D U R E S */ /* */ /* These procedures are provided for backwards compatability */ /* with 6.x scripts and 6.x replication. The re-implemented */ /* procedures are as follows: */ /* */ /* - sp_uniquetaskname (SQLDMO) */ /* - systasks_view (INSTDIST.SQL) */ /* - sp_addtask (INSTREPL.SQL, INSTDIST.SQL, SQLDMO) */ /* - sp_droptask (INSTREPL.SQL, INSTDIST.SQL, SQLDMO) */ /* - systasks (For completeness only) */ /**************************************************************/ /**************************************************************/ /* SP_UNIQUETASKNAME */ /**************************************************************/ PRINT '' PRINT 'Creating [legacy] procedure sp_uniquetaskname...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_uniquetaskname') AND (type = 'P'))) DROP PROCEDURE sp_uniquetaskname go CREATE PROCEDURE sp_uniquetaskname @seed NVARCHAR(92) AS BEGIN DECLARE @newest_suffix INT SET NOCOUNT ON -- We're going to add a suffix of 8 characters so make sure the seed is at most 84 characters SELECT @seed = LTRIM(RTRIM(@seed)) IF (DATALENGTH(@seed) > 0) SELECT @seed = SUBSTRING(@seed, 1, 84) -- Find the newest (highest) suffix so far SELECT @newest_suffix = MAX(CONVERT(INT, RIGHT(name, 8))) FROM msdb.dbo.sysjobs -- DON'T use sysjobs_view here! WHERE (name LIKE N'%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]') -- Generate the task name by appending the 'newest suffix' value (plus one) to the seed IF (@newest_suffix IS NOT NULL) BEGIN SELECT @newest_suffix = @newest_suffix + 1 SELECT 'TaskName' = CONVERT(NVARCHAR(92), @seed + REPLICATE(N'0', 8 - (DATALENGTH(CONVERT(NVARCHAR, @newest_suffix)) / 2)) + CONVERT(NVARCHAR, @newest_suffix)) END ELSE SELECT 'TaskName' = CONVERT(NVARCHAR(92), @seed + N'00000001') END go /**************************************************************/ /* SP_ADDTASK */ /**************************************************************/ PRINT '' PRINT 'Creating [legacy] procedure sp_addtask...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_addtask') AND (type = 'P'))) DROP PROCEDURE sp_addtask go CREATE PROCEDURE sp_addtask @name sysname, -- Was VARCHAR(100) in 6.x @subsystem NVARCHAR(40) = N'TSQL', -- Was VARCHAR(30) in 6.x @server sysname = NULL, @username sysname = NULL, -- Was VARCHAR(30) in 6.x @databasename sysname = NULL, -- Was VARCHAR(30) in 6.x @enabled TINYINT = 0, @freqtype INT = 2, -- 2 means OnDemand @freqinterval INT = 1, @freqsubtype INT = 1, @freqsubinterval INT = 1, @freqrelativeinterval INT = 1, @freqrecurrencefactor INT = 1, @activestartdate INT = 0, @activeenddate INT = 0, @activestarttimeofday INT = 0, @activeendtimeofday INT = 0, @nextrundate INT = 0, @nextruntime INT = 0, @runpriority INT = 0, @emailoperatorname sysname = NULL, -- Was VARCHAR(50) in 6.x @retryattempts INT = 0, @retrydelay INT = 10, @command NVARCHAR(max) = NULL, @loghistcompletionlevel INT = 2, @emailcompletionlevel INT = 0, @description NVARCHAR(512) = NULL, -- Was VARCHAR(255) in 6.x @tagadditionalinfo VARCHAR(96) = NULL, -- Obsolete in 7.0 @tagobjectid INT = NULL, -- Obsolete in 7.0 @tagobjecttype INT = NULL, -- Obsolete in 7.0 @newid INT = NULL OUTPUT, @parameters NTEXT = NULL, -- Was TEXT in 6.x @cmdexecsuccesscode INT = 0, @category_name sysname = NULL, -- New for 7.0 @category_id INT = NULL -- New for 7.0 AS BEGIN DECLARE @retval INT DECLARE @job_id UNIQUEIDENTIFIER DECLARE @id INT DECLARE @distdb sysname DECLARE @proc nvarchar(255) SET NOCOUNT ON SELECT @retval = 1 -- 0 means success, 1 means failure -- Set 7.0 category names for 6.5 replication tasks IF (LOWER(@subsystem) = N'sync') SELECT @category_id = 15 ELSE IF (LOWER(@subsystem) = N'logreader') SELECT @category_id = 13 ELSE IF (LOWER(@subsystem) = N'distribution') SELECT @category_id = 10 -- Convert old replication synchronization subsystem name to the 7.0 name IF (LOWER(@subsystem) = N'sync') SELECT @subsystem = N'Snapshot' -- If a category ID is provided this overrides any supplied category name IF (@category_id IS NOT NULL) BEGIN SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = @category_id) SELECT @category_name = ISNULL(@category_name, FORMATMESSAGE(14205)) END -- In 6.x active start date was not restricted, but it is in 7.0; so to avoid a "noisey" -- failure in sp_add_jobschedule we modify the value accordingly IF ((@activestartdate <> 0) AND (@activestartdate < 19900101)) SELECT @activestartdate = 19900101 BEGIN TRANSACTION -- Add the job EXECUTE @retval = sp_add_job @job_name = @name, @enabled = @enabled, @start_step_id = 1, @description = @description, @category_name = @category_name, @notify_level_eventlog = @loghistcompletionlevel, @notify_level_email = @emailcompletionlevel, @notify_email_operator_name = @emailoperatorname, @job_id = @job_id OUTPUT IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END -- Add an entry to systaskids for the new job (created by a 6.x client) INSERT INTO msdb.dbo.systaskids (job_id) VALUES (@job_id) -- Get the assigned task id SELECT @id = task_id, @newid = task_id FROM msdb.dbo.systaskids WHERE (job_id = @job_id) -- Add the job step EXECUTE @retval = sp_add_jobstep @job_id = @job_id, @step_id = 1, @step_name = N'Step 1', @subsystem = @subsystem, @command = @command, @additional_parameters = @parameters, @cmdexec_success_code = @cmdexecsuccesscode, @server = @server, @database_name = @databasename, @database_user_name = @username, @retry_attempts = @retryattempts, @retry_interval = @retrydelay, @os_run_priority = @runpriority IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END -- Add the job schedule IF (@activestartdate = 0) SELECT @activestartdate = NULL IF (@activeenddate = 0) SELECT @activeenddate = NULL IF (@activestarttimeofday = 0) SELECT @activestarttimeofday = NULL IF (@activeendtimeofday = 0) SELECT @activeendtimeofday = NULL IF (@freqtype <> 0x2) -- OnDemand tasks simply have no schedule in 7.0 BEGIN EXECUTE @retval = sp_add_jobschedule @job_id = @job_id, @name = N'6.x schedule', @enabled = 1, @freq_type = @freqtype, @freq_interval = @freqinterval, @freq_subday_type = @freqsubtype, @freq_subday_interval = @freqsubinterval, @freq_relative_interval = @freqrelativeinterval, @freq_recurrence_factor = @freqrecurrencefactor, @active_start_date = @activestartdate, @active_end_date = @activeenddate, @active_start_time = @activestarttimeofday, @active_end_time = @activeendtimeofday IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END END -- And finally, add the job server EXECUTE @retval = sp_add_jobserver @job_id = @job_id, @server_name = NULL IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END -- Add the replication agent for monitoring IF (@category_id = 13) -- Logreader BEGIN SELECT @distdb = distribution_db from MSdistpublishers where name = @server SELECT @proc = @distdb + '.dbo.sp_MSadd_logreader_agent' EXECUTE @retval = @proc @name = @name, @publisher = @server, @publisher_db = @databasename, @publication = '', @local_job = 1, @job_existing = 1, @job_id = @job_id IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END END ELSE IF (@category_id = 15) -- Snapshot BEGIN DECLARE @publication sysname EXECUTE @retval = master.dbo.sp_MSget_publication_from_taskname @taskname = @name, @publisher = @server, @publisherdb = @databasename, @publication = @publication OUTPUT IF (@publication IS NOT NULL) BEGIN SELECT @distdb = distribution_db from MSdistpublishers where name = @server SELECT @proc = @distdb + '.dbo.sp_MSadd_snapshot_agent' EXECUTE @retval = @proc @name = @name, @publisher = @server, @publisher_db = @databasename, @publication = @publication, @local_job = 1, @job_existing = 1, @snapshot_jobid = @job_id IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END SELECT @proc = @distdb + '.dbo.sp_MSadd_publication' EXECUTE @retval = @proc @publisher = @server, @publisher_db = @databasename, @publication = @publication, @publication_type = 0 -- Transactional IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END END END COMMIT TRANSACTION -- If this is an autostart LogReader or Distribution job, add the [new] '-Continuous' paramter to the command IF (@freqtype = 0x40) AND ((UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'LOGREADER') OR (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DISTRIBUTION')) BEGIN UPDATE msdb.dbo.sysjobsteps SET command = command + N' -Continuous' WHERE (job_id = @job_id) AND (step_id = 1) END -- If this is an autostart job, start it now (for backwards compatibility with 6.x SQLExecutive behaviour) IF (@freqtype = 0x40) EXECUTE msdb.dbo.sp_start_job @job_id = @job_id, @error_flag = 0, @output_flag = 0 Quit: RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_DROPTASK */ /**************************************************************/ PRINT '' PRINT 'Creating [legacy] procedure sp_droptask...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_droptask') AND (type = 'P'))) DROP PROCEDURE sp_droptask go CREATE PROCEDURE sp_droptask @name sysname = NULL, -- Was VARCHAR(100) in 6.x @loginname sysname = NULL, -- Was VARCHAR(30) in 6.x @id INT = NULL AS BEGIN DECLARE @retval INT DECLARE @job_id UNIQUEIDENTIFIER DECLARE @category_id int SET NOCOUNT ON IF ((@name IS NULL) AND (@id IS NULL) AND (@loginname IS NULL)) OR ((@name IS NOT NULL) AND ((@id IS NOT NULL) OR (@loginname IS NOT NULL))) OR ((@id IS NOT NULL) AND ((@name IS NOT NULL) OR (@loginname IS NOT NULL))) OR ((@loginname IS NOT NULL) AND ((@name IS NOT NULL) OR (@id IS NOT NULL))) BEGIN RAISERROR(14245, -1, -1) RETURN(1) -- Failure END -- If the name is supplied, get the job_id directly from sysjobs IF (@name IS NOT NULL) BEGIN -- Check if the name is ambiguous IF ((SELECT COUNT(*) FROM msdb.dbo.sysjobs_view WHERE (name = @name)) > 1) BEGIN RAISERROR(14292, -1, -1, @name, '@id', '@name') RETURN(1) -- Failure END SELECT @job_id = job_id, @category_id = category_id FROM msdb.dbo.sysjobs_view WHERE (name = @name) SELECT @id = task_id FROM msdb.dbo.systaskids WHERE (job_id = @job_id) IF (@job_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@name', @name) RETURN(1) -- Failure END END -- If the id is supplied lookup the corresponding job_id from systaskids IF (@id IS NOT NULL) BEGIN SELECT @job_id = job_id FROM msdb.dbo.systaskids WHERE (task_id = @id) -- Check that the job still exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id))) BEGIN SELECT @name = CONVERT(NVARCHAR, @id) RAISERROR(14262, -1, -1, '@id', @name) RETURN(1) -- Failure END -- Get the name of this job SELECT @name = name, @category_id = category_id FROM msdb.dbo.sysjobs_view WHERE (job_id = @job_id) END -- Delete the specific job IF (@name IS NOT NULL) BEGIN BEGIN TRANSACTION DELETE FROM msdb.dbo.systaskids WHERE (job_id = @job_id) EXECUTE @retval = sp_delete_job @job_id = @job_id IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END -- If a Logreader or Snapshot task, delete corresponding replication agent information IF @category_id = 13 or @category_id = 15 BEGIN EXECUTE @retval = sp_MSdrop_6x_replication_agent @job_id, @category_id IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END END COMMIT TRANSACTION END -- Delete all jobs belonging to the specified login IF (@loginname IS NOT NULL) BEGIN BEGIN TRANSACTION DELETE FROM msdb.dbo.systaskids WHERE job_id IN (SELECT job_id FROM msdb.dbo.sysjobs_view WHERE (owner_sid = SUSER_SID(@loginname))) EXECUTE @retval = sp_manage_jobs_by_login @action = 'DELETE', @current_owner_login_name = @loginname IF (@retval <> 0) BEGIN ROLLBACK TRANSACTION GOTO Quit END COMMIT TRANSACTION END Quit: RETURN(@retval) -- 0 means success END go /**************************************************************/ /* */ /* E R R O R M E S S A G E S */ /* */ /* These are now created by MESSAGES.SQL. */ /* */ /* NOTE: 14255 and 14265 are called by dynamic SQL generated */ /* by SQLServerAgent. */ /**************************************************************/ /**************************************************************/ /* */ /* T R I G G E R S */ /* */ /**************************************************************/ /**************************************************************/ /* TRIG_TARGETSERVER_INSERT */ /**************************************************************/ PRINT '' PRINT 'Creating trigger trig_targetserver_insert...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_targetserver_insert') AND (type = 'TR'))) DROP TRIGGER dbo.trig_targetserver_insert go CREATE TRIGGER trig_targetserver_insert ON msdb.dbo.systargetservers FOR INSERT, DELETE AS BEGIN SET NOCOUNT ON -- Disallow the insert if the server is called 'ALL' -- NOTE: We have to do this check here in the trigger since there is no sp_add_targetserver -- (target servers insert a row for themselves when they 'enlist' in an MSX) IF (EXISTS (SELECT * FROM inserted WHERE (server_name = N'ALL'))) BEGIN DELETE FROM msdb.dbo.systargetservers WHERE (server_name = N'ALL') RAISERROR(14271, -1, -1, 'ALL') RETURN END -- Set (or delete) the registy flag (so that SETUP can detect if we're an MSX) IF ((SELECT COUNT(*) FROM msdb.dbo.systargetservers) = 0) BEGIN DECLARE @val INT EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServer', @val OUTPUT, N'no_output' IF (@val IS NOT NULL) EXECUTE master.dbo.xp_instance_regdeletevalue N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServer' END ELSE EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServer', N'REG_DWORD', 1 END go CHECKPOINT go /**************************************************************/ /** **/ /** A L E R T S A N D O P E R A T O R S **/ /** **/ /**************************************************************/ /**************************************************************/ /* */ /* C O R E P R O C E D U R E S */ /* */ /**************************************************************/ /**************************************************************/ /* SP_ADD_ALERT_INTERNAL */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_alert_internal...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_alert_internal') AND (type = 'P'))) DROP PROCEDURE sp_add_alert_internal go CREATE PROCEDURE sp_add_alert_internal @name sysname, @message_id INT = 0, @severity INT = 0, @enabled TINYINT = 1, @delay_between_responses INT = 0, @notification_message NVARCHAR(512) = NULL, @include_event_description_in TINYINT = 5, -- 0 = None, 1 = Email, 2 = Pager, 4 = NetSend, 7 = All @database_name sysname = NULL, @event_description_keyword NVARCHAR(100) = NULL, @job_id UNIQUEIDENTIFIER = NULL, -- If provided must NOT also provide job_name @job_name sysname = NULL, -- If provided must NOT also provide job_id @raise_snmp_trap TINYINT = 0, @performance_condition NVARCHAR(512) = NULL, -- New for 7.0 @category_name sysname = NULL, -- New for 7.0 @wmi_namespace NVARCHAR(512) = NULL, -- New for 9.0 @wmi_query NVARCHAR(512) = NULL, -- New for 9.0 @verify_alert TINYINT = 1 -- 0 = do not verify alert, 1(or anything else) = verify alert before adding AS BEGIN DECLARE @event_source NVARCHAR(100) DECLARE @event_category_id INT DECLARE @event_id INT DECLARE @last_occurrence_date INT DECLARE @last_occurrence_time INT DECLARE @last_notification_date INT DECLARE @last_notification_time INT DECLARE @occurrence_count INT DECLARE @count_reset_date INT DECLARE @count_reset_time INT DECLARE @has_notification INT DECLARE @return_code INT DECLARE @duplicate_name sysname DECLARE @category_id INT DECLARE @alert_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @notification_message = LTRIM(RTRIM(@notification_message)) SELECT @database_name = LTRIM(RTRIM(@database_name)) SELECT @event_description_keyword = LTRIM(RTRIM(@event_description_keyword)) SELECT @job_name = LTRIM(RTRIM(@job_name)) SELECT @performance_condition = LTRIM(RTRIM(@performance_condition)) SELECT @category_name = LTRIM(RTRIM(@category_name)) -- Turn [nullable] empty string parameters into NULLs IF (@notification_message = N'') SELECT @notification_message = NULL IF (@database_name = N'') SELECT @database_name = NULL IF (@event_description_keyword = N'') SELECT @event_description_keyword = NULL IF (@job_name = N'') SELECT @job_name = NULL IF (@performance_condition = N'') SELECT @performance_condition = NULL IF (@category_name = N'') SELECT @category_name = NULL SELECT @message_id = ISNULL(@message_id, 0) SELECT @severity = ISNULL(@severity, 0) -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check if SQLServerAgent is in the process of starting EXECUTE @return_code = msdb.dbo.sp_is_sqlagent_starting IF (@return_code <> 0) RETURN(1) -- Failure -- Hard-code the new Alert defaults -- event source needs to be instance aware DECLARE @instance_name sysname SELECT @instance_name = CONVERT (sysname, SERVERPROPERTY ('InstanceName')) IF (@instance_name IS NULL OR @instance_name = N'MSSQLSERVER') SELECT @event_source = N'MSSQLSERVER' ELSE SELECT @event_source = N'MSSQL$' + @instance_name SELECT @event_category_id = NULL SELECT @event_id = NULL SELECT @last_occurrence_date = 0 SELECT @last_occurrence_time = 0 SELECT @last_notification_date = 0 SELECT @last_notification_time = 0 SELECT @occurrence_count = 0 SELECT @count_reset_date = 0 SELECT @count_reset_time = 0 SELECT @has_notification = 0 IF (@category_name IS NULL) BEGIN --Default category_id for alerts SELECT @category_id = 98 SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = 98) END -- Map a job_id of 0 to the real value we use to mean 'no job' IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) AND (@job_name IS NULL) SELECT @job_name = N'' -- Verify the Alert if @verify_alert <> 0 IF (@verify_alert <> 0) BEGIN IF (@job_id = CONVERT(UNIQUEIDENTIFIER, 0x00)) SELECT @job_id = NULL EXECUTE @return_code = sp_verify_alert @name, @message_id, @severity, @enabled, @delay_between_responses, @notification_message, @include_event_description_in, @database_name, @event_description_keyword, @job_id OUTPUT, @job_name OUTPUT, @occurrence_count, @raise_snmp_trap, @performance_condition, @category_name, @category_id OUTPUT, @count_reset_date, @count_reset_time, @wmi_namespace, @wmi_query, @event_id OUTPUT IF (@return_code <> 0) BEGIN RETURN(1) -- Failure END END -- For WMI alerts replace -- database_name with wmi_namespace and -- performance_conditon with wmi_query -- so we can store them in those columns in sysalerts table IF (@event_id = 8) BEGIN SELECT @database_name = @wmi_namespace SELECT @performance_condition = @wmi_query END -- Check if this Alert already exists SELECT @duplicate_name = FORMATMESSAGE(14205) SELECT @duplicate_name = name FROM msdb.dbo.sysalerts WHERE ((event_id = 8) AND (ISNULL(performance_condition, N'') = ISNULL(@performance_condition, N'')) AND (ISNULL(database_name, N'') = ISNULL(@database_name, N''))) OR ((ISNULL(event_id,1) <> 8) AND (ISNULL(performance_condition, N'apples') = ISNULL(@performance_condition, N'oranges'))) OR ((performance_condition IS NULL) AND (message_id = @message_id) AND (severity = @severity) AND (ISNULL(database_name, N'') = ISNULL(@database_name, N'')) AND (ISNULL(event_description_keyword, N'') = ISNULL(@event_description_keyword, N''))) IF (@duplicate_name <> FORMATMESSAGE(14205)) BEGIN RAISERROR(14501, 16, 1, @duplicate_name) RETURN(1) -- Failure END -- Finally, do the actual INSERT INSERT INTO msdb.dbo.sysalerts (name, event_source, event_category_id, event_id, message_id, severity, enabled, delay_between_responses, last_occurrence_date, last_occurrence_time, last_response_date, last_response_time, notification_message, include_event_description, database_name, event_description_keyword, occurrence_count, count_reset_date, count_reset_time, job_id, has_notification, flags, performance_condition, category_id) VALUES (@name, @event_source, @event_category_id, @event_id, @message_id, @severity, @enabled, @delay_between_responses, @last_occurrence_date, @last_occurrence_time, @last_notification_date, @last_notification_time, @notification_message, @include_event_description_in, @database_name, @event_description_keyword, @occurrence_count, @count_reset_date, @count_reset_time, ISNULL(@job_id, CONVERT(UNIQUEIDENTIFIER, 0x00)), @has_notification, @raise_snmp_trap, @performance_condition, @category_id) -- Notify SQLServerAgent of the change SELECT @alert_id = id FROM msdb.dbo.sysalerts WHERE (name = @name) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'I' RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_ALERT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_alert...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_alert') AND (type = 'P'))) DROP PROCEDURE sp_add_alert go CREATE PROCEDURE sp_add_alert @name sysname, @message_id INT = 0, @severity INT = 0, @enabled TINYINT = 1, @delay_between_responses INT = 0, @notification_message NVARCHAR(512) = NULL, @include_event_description_in TINYINT = 5, -- 0 = None, 1 = Email, 2 = Pager, 4 = NetSend, 7 = All @database_name sysname = NULL, @event_description_keyword NVARCHAR(100) = NULL, @job_id UNIQUEIDENTIFIER = NULL, -- If provided must NOT also provide job_name @job_name sysname = NULL, -- If provided must NOT also provide job_id @raise_snmp_trap TINYINT = 0, @performance_condition NVARCHAR(512) = NULL, -- New for 7.0 @category_name sysname = NULL, -- New for 7.0 @wmi_namespace sysname = NULL, -- New for 9.0 @wmi_query NVARCHAR(512) = NULL -- New for 9.0 AS BEGIN DECLARE @verify_alert INT --Always verify alerts before adding SELECT @verify_alert = 1 EXECUTE msdb.dbo.sp_add_alert_internal @name, @message_id, @severity, @enabled, @delay_between_responses, @notification_message, @include_event_description_in, @database_name, @event_description_keyword, @job_id, @job_name, @raise_snmp_trap, @performance_condition, @category_name, @wmi_namespace, @wmi_query, @verify_alert END GO /**************************************************************/ /* SP_DELETE_ALERT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_alert...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_alert') AND (type = 'P'))) DROP PROCEDURE sp_delete_alert go CREATE PROCEDURE sp_delete_alert @name sysname AS BEGIN DECLARE @alert_id INT DECLARE @return_code INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check if SQLServerAgent is in the process of starting EXECUTE @return_code = msdb.dbo.sp_is_sqlagent_starting IF (@return_code <> 0) RETURN(1) -- Failure -- Check if this Alert exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (name = @name))) BEGIN RAISERROR(14262, 16, 1, '@name', @name) RETURN(1) -- Failure END -- Convert the Name to it's ID SELECT @alert_id = id FROM msdb.dbo.sysalerts WHERE (name = @name) BEGIN TRANSACTION -- Delete sysnotifications entries DELETE FROM msdb.dbo.sysnotifications WHERE (alert_id = @alert_id) -- Finally, do the actual DELETE DELETE FROM msdb.dbo.sysalerts WHERE (id = @alert_id) COMMIT TRANSACTION -- Notify SQLServerAgent of the change EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'D' RETURN(0) -- Success END go /**************************************************************/ /* SP_HELP_ALERT */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_alert...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_alert') AND (type = 'P'))) DROP PROCEDURE sp_help_alert go CREATE PROCEDURE sp_help_alert @alert_name sysname = NULL, @order_by sysname = N'name', @alert_id INT = NULL, @category_name sysname = NULL, @legacy_format BIT = 0 AS BEGIN DECLARE @alert_id_as_char NVARCHAR(10) 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 @alert_name = REPLACE(@alert_name, N'''', N'''''') -- Double up any single quotes in @category_name IF (@category_name IS NOT NULL) SELECT @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''' + @alert_name + N''' = N'''') OR (sa.name = N''' + @alert_name + N''')) AND ((' + @alert_id_as_char + N' IS NULL) OR (sa.id = ' + @alert_id_as_char + N')) AND ((N''' + @category_name + N''' = N'''') OR (sc.name = N''' + @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''' + @alert_name + N''' = N'''') OR (sa.name = N''' + @alert_name + N''')) AND ((' + @alert_id_as_char + N' IS NULL) OR (sa.id = ' + @alert_id_as_char + N')) AND ((N''' + @category_name + N''' = N'''') OR (sc.name = N''' + @category_name + N''')) ORDER BY ' + @order_by) END RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_VERIFY_OPERATOR */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_operator...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_operator') AND (type = 'P'))) DROP PROCEDURE sp_verify_operator go CREATE PROCEDURE sp_verify_operator @name sysname, @enabled TINYINT, @pager_days TINYINT, @weekday_pager_start_time INT, @weekday_pager_end_time INT, @saturday_pager_start_time INT, @saturday_pager_end_time INT, @sunday_pager_start_time INT, @sunday_pager_end_time INT, @category_name sysname, @category_id INT OUTPUT AS BEGIN DECLARE @return_code TINYINT DECLARE @res_valid_range NVARCHAR(100) SET NOCOUNT ON SELECT @res_valid_range = FORMATMESSAGE(14209) -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @category_name = LTRIM(RTRIM(@category_name)) -- The name must be unique IF (EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (name = @name))) BEGIN RAISERROR(14261, 16, 1, '@name', @name) RETURN(1) -- Failure END -- Enabled must be 0 or 1 IF (@enabled NOT IN (0, 1)) BEGIN RAISERROR(14266, 16, 1, '@enabled', '0, 1') RETURN(1) -- Failure END -- Check PagerDays IF (@pager_days < 0) OR (@pager_days > 127) BEGIN RAISERROR(14266, 16, 1, '@pager_days', @res_valid_range) RETURN(1) -- Failure END -- Check Start/End Times EXECUTE @return_code = sp_verify_job_time @weekday_pager_start_time, '@weekday_pager_start_time' IF (@return_code <> 0) RETURN(1) EXECUTE @return_code = sp_verify_job_time @weekday_pager_end_time, '@weekday_pager_end_time' IF (@return_code <> 0) RETURN(1) EXECUTE @return_code = sp_verify_job_time @saturday_pager_start_time, '@saturday_pager_start_time' IF (@return_code <> 0) RETURN(1) EXECUTE @return_code = sp_verify_job_time @saturday_pager_end_time, '@saturday_pager_end_time' IF (@return_code <> 0) RETURN(1) EXECUTE @return_code = sp_verify_job_time @sunday_pager_start_time, '@sunday_pager_start_time' IF (@return_code <> 0) RETURN(1) EXECUTE @return_code = sp_verify_job_time @sunday_pager_end_time, '@sunday_pager_end_time' IF (@return_code <> 0) RETURN(1) -- Check category name IF (@category_name = N'[DEFAULT]') SELECT @category_id = 99 ELSE BEGIN SELECT @category_id = category_id FROM msdb.dbo.syscategories WHERE (category_class = 3) -- Operators AND (category_type = 3) -- None AND (name = @category_name) END IF (@category_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@category_name', @category_name) RETURN(1) -- Failure END RETURN(0) END go /**************************************************************/ /* SP_ADD_OPERATOR */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_operator...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_operator') AND (type = 'P'))) DROP PROCEDURE sp_add_operator go CREATE PROCEDURE sp_add_operator @name sysname, @enabled TINYINT = 1, @email_address NVARCHAR(100) = NULL, @pager_address NVARCHAR(100) = NULL, @weekday_pager_start_time INT = 090000, -- HHMMSS using 24 hour clock @weekday_pager_end_time INT = 180000, -- As above @saturday_pager_start_time INT = 090000, -- As above @saturday_pager_end_time INT = 180000, -- As above @sunday_pager_start_time INT = 090000, -- As above @sunday_pager_end_time INT = 180000, -- As above @pager_days TINYINT = 0, -- 1 = Sunday .. 64 = Saturday @netsend_address NVARCHAR(100) = NULL, -- New for 7.0 @category_name sysname = NULL -- New for 7.0 AS BEGIN DECLARE @return_code TINYINT DECLARE @category_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @email_address = LTRIM(RTRIM(@email_address)) SELECT @pager_address = LTRIM(RTRIM(@pager_address)) SELECT @netsend_address = LTRIM(RTRIM(@netsend_address)) SELECT @category_name = LTRIM(RTRIM(@category_name)) -- Turn [nullable] empty string parameters into NULLs IF (@email_address = N'') SELECT @email_address = NULL IF (@pager_address = N'') SELECT @pager_address = NULL IF (@netsend_address = N'') SELECT @netsend_address = NULL IF (@category_name = N'') SELECT @category_name = NULL -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END IF (@category_name IS NULL) BEGIN SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = 99) END -- Verify the operator EXECUTE @return_code = sp_verify_operator @name, @enabled, @pager_days, @weekday_pager_start_time, @weekday_pager_end_time, @saturday_pager_start_time, @saturday_pager_end_time, @sunday_pager_start_time, @sunday_pager_end_time, @category_name, @category_id OUTPUT IF (@return_code <> 0) RETURN(1) -- Failure -- Finally, do the INSERT INSERT INTO msdb.dbo.sysoperators (name, enabled, email_address, last_email_date, last_email_time, pager_address, last_pager_date, last_pager_time, weekday_pager_start_time, weekday_pager_end_time, saturday_pager_start_time, saturday_pager_end_time, sunday_pager_start_time, sunday_pager_end_time, pager_days, netsend_address, last_netsend_date, last_netsend_time, category_id) VALUES (@name, @enabled, @email_address, 0, 0, @pager_address, 0, 0, @weekday_pager_start_time, @weekday_pager_end_time, @saturday_pager_start_time, @saturday_pager_end_time, @sunday_pager_start_time, @sunday_pager_end_time, @pager_days, @netsend_address, 0, 0, @category_id) RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_UPDATE_OPERATOR */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_operator...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_operator') AND (type = 'P'))) DROP PROCEDURE sp_update_operator go CREATE PROCEDURE sp_update_operator @name sysname, @new_name sysname = NULL, @enabled TINYINT = NULL, @email_address NVARCHAR(100) = NULL, @pager_address NVARCHAR(100) = NULL, @weekday_pager_start_time INT = NULL, -- HHMMSS using 24 hour clock @weekday_pager_end_time INT = NULL, -- As above @saturday_pager_start_time INT = NULL, -- As above @saturday_pager_end_time INT = NULL, -- As above @sunday_pager_start_time INT = NULL, -- As above @sunday_pager_end_time INT = NULL, -- As above @pager_days TINYINT = NULL, @netsend_address NVARCHAR(100) = NULL, -- New for 7.0 @category_name sysname = NULL -- New for 7.0 AS BEGIN DECLARE @x_enabled TINYINT DECLARE @x_email_address NVARCHAR(100) DECLARE @x_pager_address NVARCHAR(100) DECLARE @x_weekday_pager_start_time INT DECLARE @x_weekday_pager_end_time INT DECLARE @x_saturday_pager_start_time INT DECLARE @x_saturday_pager_end_time INT DECLARE @x_sunday_pager_start_time INT DECLARE @x_sunday_pager_end_time INT DECLARE @x_pager_days TINYINT DECLARE @x_netsend_address NVARCHAR(100) DECLARE @x_category_id INT DECLARE @return_code INT DECLARE @notification_method INT DECLARE @alert_fail_safe_operator sysname DECLARE @current_msx_server sysname DECLARE @category_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name = LTRIM(RTRIM(@name)) SELECT @new_name = LTRIM(RTRIM(@new_name)) SELECT @email_address = LTRIM(RTRIM(@email_address)) SELECT @pager_address = LTRIM(RTRIM(@pager_address)) SELECT @netsend_address = LTRIM(RTRIM(@netsend_address)) SELECT @category_name = LTRIM(RTRIM(@category_name)) -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check if this Operator exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (name = @name))) BEGIN RAISERROR(14262, 16, 1, '@name', @name) RETURN(1) -- Failure END -- Check if this operator is 'MSXOperator' IF (@name = N'MSXOperator') BEGIN -- Disallow the update operation if we're at a TSX for all callers other than xp_msx_enlist EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'MSXServerName', @current_msx_server OUTPUT, N'no_output' IF ((@current_msx_server IS NOT NULL) AND (PROGRAM_NAME() <> N'xp_msx_enlist')) BEGIN RAISERROR(14223, 16, 1, 'MSXOperator', 'TSX') RETURN(1) -- Failure END END -- Get existing (@x_) operator property values SELECT @x_enabled = enabled, @x_email_address = email_address, @x_pager_address = pager_address, @x_weekday_pager_start_time = weekday_pager_start_time, @x_weekday_pager_end_time = weekday_pager_end_time, @x_saturday_pager_start_time = saturday_pager_start_time, @x_saturday_pager_end_time = saturday_pager_end_time, @x_sunday_pager_start_time = sunday_pager_start_time, @x_sunday_pager_end_time = sunday_pager_end_time, @x_pager_days = pager_days, @x_netsend_address = netsend_address, @x_category_id = category_id FROM msdb.dbo.sysoperators WHERE (name = @name) -- Fill out the values for all non-supplied parameters from the existsing values IF (@enabled IS NULL) SELECT @enabled = @x_enabled IF (@email_address IS NULL) SELECT @email_address = @x_email_address IF (@pager_address IS NULL) SELECT @pager_address = @x_pager_address IF (@weekday_pager_start_time IS NULL) SELECT @weekday_pager_start_time = @x_weekday_pager_start_time IF (@weekday_pager_end_time IS NULL) SELECT @weekday_pager_end_time = @x_weekday_pager_end_time IF (@saturday_pager_start_time IS NULL) SELECT @saturday_pager_start_time = @x_saturday_pager_start_time IF (@saturday_pager_end_time IS NULL) SELECT @saturday_pager_end_time = @x_saturday_pager_end_time IF (@sunday_pager_start_time IS NULL) SELECT @sunday_pager_start_time = @x_sunday_pager_start_time IF (@sunday_pager_end_time IS NULL) SELECT @sunday_pager_end_time = @x_sunday_pager_end_time IF (@pager_days IS NULL) SELECT @pager_days = @x_pager_days IF (@netsend_address IS NULL) SELECT @netsend_address = @x_netsend_address IF (@category_name IS NULL) SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = @x_category_id) IF (@category_name IS NULL) BEGIN SELECT @category_name = name FROM msdb.dbo.syscategories WHERE (category_id = 99) END -- Turn [nullable] empty string parameters into NULLs IF (@email_address = N'') SELECT @email_address = NULL IF (@pager_address = N'') SELECT @pager_address = NULL IF (@netsend_address = N'') SELECT @netsend_address = NULL IF (@category_name = N'') SELECT @category_name = NULL -- Verify the operator EXECUTE @return_code = sp_verify_operator @new_name, @enabled, @pager_days, @weekday_pager_start_time, @weekday_pager_end_time, @saturday_pager_start_time, @saturday_pager_end_time, @sunday_pager_start_time, @sunday_pager_end_time, @category_name, @category_id OUTPUT IF (@return_code <> 0) RETURN(1) -- Failure -- If no new name is supplied, use the old one -- NOTE: We must do this AFTER calling sp_verify_operator. IF (@new_name IS NULL) SELECT @new_name = @name ELSE BEGIN -- You can't rename the MSXOperator IF (@name = N'MSXOperator') BEGIN RAISERROR(14222, 16, 1, 'MSXOperator') RETURN(1) -- Failure END END -- Do the UPDATE UPDATE msdb.dbo.sysoperators SET name = @new_name, enabled = @enabled, email_address = @email_address, pager_address = @pager_address, weekday_pager_start_time = @weekday_pager_start_time, weekday_pager_end_time = @weekday_pager_end_time, saturday_pager_start_time = @saturday_pager_start_time, saturday_pager_end_time = @saturday_pager_end_time, sunday_pager_start_time = @sunday_pager_start_time, sunday_pager_end_time = @sunday_pager_end_time, pager_days = @pager_days, netsend_address = @netsend_address, category_id = @category_id WHERE (name = @name) -- Check if the operator is 'MSXOperator', in which case we need to re-enlist all the targets -- so that they will download the new MSXOperator details IF ((@name = N'MSXOperator') AND ((SELECT COUNT(*) FROM msdb.dbo.systargetservers) > 0)) EXECUTE msdb.dbo.sp_post_msx_operation 'RE-ENLIST', 'SERVER', 0x00 -- Check if this operator is the FailSafe Operator EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertFailSafeOperator', @alert_fail_safe_operator OUTPUT, N'no_output' -- If it is, we update the 4 'AlertFailSafe...' registry entries and AlertNotificationMethod IF (LTRIM(RTRIM(@alert_fail_safe_operator)) = @name) BEGIN -- Update AlertFailSafeX values EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertFailSafeOperator', N'REG_SZ', @new_name EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertFailSafeEmailAddress', N'REG_SZ', @email_address EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertFailSafePagerAddress', N'REG_SZ', @pager_address EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertFailSafeNetSendAddress', N'REG_SZ', @netsend_address -- Update AlertNotificationMethod values EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertNotificationMethod', @notification_method OUTPUT, N'no_output' IF (LTRIM(RTRIM(@email_address)) IS NULL) SELECT @notification_method = @notification_method & ~1 IF (LTRIM(RTRIM(@pager_address)) IS NULL) SELECT @notification_method = @notification_method & ~2 IF (LTRIM(RTRIM(@netsend_address)) IS NULL) SELECT @notification_method = @notification_method & ~4 EXECUTE master.dbo.xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent', N'AlertNotificationMethod', N'REG_DWORD', @notification_method -- And finally, let SQLServerAgent know of the changes EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'G' END RETURN(0) -- Success END go /**************************************************************/ /* SP_HELP_OPERATOR */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_operator...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_operator') AND (type = 'P'))) DROP PROCEDURE sp_help_operator go CREATE PROCEDURE sp_help_operator @operator_name sysname = NULL, @operator_id INT = NULL AS BEGIN DECLARE @operator_id_as_char VARCHAR(10) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @operator_name = LTRIM(RTRIM(@operator_name)) IF (@operator_name = '') SELECT @operator_name = NULL -- Check operator name IF (@operator_name IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (name = @operator_name))) BEGIN RAISERROR(14262, -1, -1, '@operator_name', @operator_name) RETURN(1) -- Failure END END -- Check operator id IF (@operator_id IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (id = @operator_id))) BEGIN SELECT @operator_id_as_char = CONVERT(VARCHAR, @operator_id) RAISERROR(14262, -1, -1, '@operator_id', @operator_id_as_char) RETURN(1) -- Failure END END SELECT so.id, so.name, so.enabled, so.email_address, so.last_email_date, so.last_email_time, so.pager_address, so.last_pager_date, so.last_pager_time, so.weekday_pager_start_time, so.weekday_pager_end_time, so.saturday_pager_start_time, so.saturday_pager_end_time, so.sunday_pager_start_time, so.sunday_pager_end_time, so.pager_days, so.netsend_address, so.last_netsend_date, so.last_netsend_time, category_name = sc.name FROM msdb.dbo.sysoperators so LEFT OUTER JOIN msdb.dbo.syscategories sc ON (so.category_id = sc.category_id) WHERE ((@operator_name IS NULL) OR (so.name = @operator_name)) AND ((@operator_id IS NULL) OR (so.id = @operator_id)) ORDER BY so.name RETURN(@@error) -- 0 means success END go /**************************************************************/ /* SP_HELP_OPERATOR_JOBS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_operator_jobs...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_help_operator_jobs') AND (type = 'P'))) DROP PROCEDURE sp_help_operator_jobs go CREATE PROCEDURE sp_help_operator_jobs @operator_name sysname = NULL AS BEGIN DECLARE @operator_id INT SET NOCOUNT ON -- Check operator name SELECT @operator_id = id FROM msdb.dbo.sysoperators WHERE (name = @operator_name) IF (@operator_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@operator_name', @operator_name) RETURN(1) -- Failure END -- Get the job info SELECT job_id, name, notify_level_email, notify_level_netsend, notify_level_page FROM msdb.dbo.sysjobs_view WHERE ((notify_email_operator_id = @operator_id) AND (notify_level_email <> 0)) OR ((notify_netsend_operator_id = @operator_id) AND (notify_level_netsend <> 0)) OR ((notify_page_operator_id = @operator_id) AND (notify_level_page <> 0)) RETURN(0) -- Success END go /**************************************************************/ /* SP_VERIFY_OPERATOR_IDENTIFIERS */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_operator_identifiers...' IF (NOT OBJECT_ID(N'dbo.sp_verify_operator_identifiers', 'P') IS NULL) DROP PROCEDURE dbo.sp_verify_operator_identifiers go CREATE PROCEDURE sp_verify_operator_identifiers @name_of_name_parameter [varchar](60), @name_of_id_parameter [varchar](60), @operator_name [sysname] OUTPUT, @operator_id [INT] OUTPUT AS BEGIN DECLARE @retval INT DECLARE @operator_id_as_char NVARCHAR(36) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @name_of_name_parameter = LTRIM(RTRIM(@name_of_name_parameter)) SELECT @name_of_id_parameter = LTRIM(RTRIM(@name_of_id_parameter)) SELECT @operator_name = LTRIM(RTRIM(@operator_name)) IF (@operator_name = N'') SELECT @operator_name = NULL IF ((@operator_name IS NULL) AND (@operator_id IS NULL)) OR ((@operator_name IS NOT NULL) AND (@operator_id IS NOT NULL)) BEGIN RAISERROR(14524, -1, -1, @name_of_id_parameter, @name_of_name_parameter) RETURN(1) -- Failure END -- Check job id IF (@operator_id IS NOT NULL) BEGIN SELECT @operator_name = name FROM msdb.dbo.sysoperators WHERE (id = @operator_id) IF (@operator_name IS NULL) BEGIN SELECT @operator_id_as_char = CONVERT(nvarchar(36), @operator_id) RAISERROR(14262, -1, -1, '@operator_id', @operator_id_as_char) RETURN(1) -- Failure END END ELSE -- Check proxy name IF (@operator_name IS NOT NULL) BEGIN -- The name is not ambiguous, so get the corresponding operator_id (if the job exists) SELECT @operator_id = id FROM msdb.dbo.sysoperators WHERE (name = @operator_name) IF (@operator_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@operator_name', @operator_name) RETURN(1) -- Failure END END RETURN(0) -- Success END go /**************************************************************/ /* SP_NOTIFY_OPERATOR */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_notify_operator...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_notify_operator') AND (type = 'P'))) DROP PROCEDURE sp_notify_operator go CREATE PROCEDURE sp_notify_operator @profile_name sysname = NULL, --name of Database Mail profile to be used for sending email, cannot be null @id INT = NULL, @name sysname = NULL, --mutual exclusive, one and only one should be non null. Specify the operator whom mail adress will be used to send this email @subject NVARCHAR(256) = NULL, @body NVARCHAR(MAX) = NULL, -- This is the body of the email message @file_attachments NVARCHAR(512) = NULL, @mail_database sysname = N'msdb' -- Have infrastructure in place to support this but disabled by default -- For first implementation we will have this parameters but using it will generate an error - not implemented yet. AS BEGIN DECLARE @retval INT DECLARE @email_address NVARCHAR(100) DECLARE @enabled TINYINT DECLARE @qualified_sp_sendmail sysname DECLARE @db_id INT SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters SELECT @profile_name = LTRIM(RTRIM(@profile_name)) SELECT @name = LTRIM(RTRIM(@name)) SELECT @file_attachments = LTRIM(RTRIM(@file_attachments)) SELECT @mail_database = LTRIM(RTRIM(@mail_database)) IF @profile_name = '' SELECT @profile_name = NULL IF @name = '' SELECT @name = NULL IF @file_attachments = '' SELECT @file_attachments = NULL IF @mail_database = '' SELECT @mail_database = NULL EXECUTE @retval = sp_verify_operator_identifiers '@name', '@id', @name OUTPUT, @id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure --is operator enabled? SELECT @enabled = enabled, @email_address = email_address FROM sysoperators WHERE id = @id IF @enabled = 0 BEGIN RAISERROR(14601, 16, 1, @name) RETURN 1 END IF @email_address IS NULL BEGIN RAISERROR(14602, 16, 1, @name) RETURN 1 END SELECT @qualified_sp_sendmail = @mail_database + '.dbo.sp_send_dbmail' EXEC @retval = @qualified_sp_sendmail @profile_name = @profile_name, @recipients = @email_address, @subject = @subject, @body = @body, @file_attachments = @file_attachments RETURN @retval END go /**************************************************************/ /* SP_VERIFY_NOTIFICATION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_verify_notification...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_verify_notification') AND (type = 'P'))) DROP PROCEDURE sp_verify_notification go CREATE PROCEDURE sp_verify_notification @alert_name sysname, @operator_name sysname, @notification_method TINYINT, @alert_id INT OUTPUT, @operator_id INT OUTPUT AS BEGIN DECLARE @res_valid_range NVARCHAR(100) SET NOCOUNT ON SELECT @res_valid_range = FORMATMESSAGE(14208) -- Remove any leading/trailing spaces from parameters SELECT @alert_name = LTRIM(RTRIM(@alert_name)) SELECT @operator_name = LTRIM(RTRIM(@operator_name)) -- Check if the AlertName is valid SELECT @alert_id = id FROM msdb.dbo.sysalerts WHERE (name = @alert_name) IF (@alert_id IS NULL) BEGIN RAISERROR(14262, 16, 1, '@alert_name', @alert_name) RETURN(1) -- Failure END -- Check if the OperatorName is valid SELECT @operator_id = id FROM msdb.dbo.sysoperators WHERE (name = @operator_name) IF (@operator_id IS NULL) BEGIN RAISERROR(14262, 16, 1, '@operator_name', @operator_name) RETURN(1) -- Failure END -- If we're at a TSX, we disallow using operator 'MSXOperator' IF (NOT EXISTS (SELECT * FROM msdb.dbo.systargetservers)) AND (@operator_name = N'MSXOperator') BEGIN RAISERROR(14251, -1, -1, @operator_name) RETURN(1) -- Failure END -- Check if the NotificationMethod is valid IF ((@notification_method < 1) OR (@notification_method > 7)) BEGIN RAISERROR(14266, 16, 1, '@notification_method', @res_valid_range) RETURN(1) -- Failure END RETURN(0) -- Success END go /**************************************************************/ /* SP_ADD_NOTIFICATION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_notification...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_notification') AND (type = 'P'))) DROP PROCEDURE sp_add_notification go CREATE PROCEDURE sp_add_notification @alert_name sysname, @operator_name sysname, @notification_method TINYINT -- 1 = Email, 2 = Pager, 4 = NetSend, 7 = All AS BEGIN DECLARE @alert_id INT DECLARE @operator_id INT DECLARE @notification NVARCHAR(512) DECLARE @retval INT DECLARE @old_has_notification INT DECLARE @new_has_notification INT DECLARE @res_notification NVARCHAR(100) SET NOCOUNT ON SELECT @res_notification = FORMATMESSAGE(14210) -- Remove any leading/trailing spaces from parameters SELECT @alert_name = LTRIM(RTRIM(@alert_name)) SELECT @operator_name = LTRIM(RTRIM(@operator_name)) -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check if the Notification is valid EXECUTE @retval = msdb.dbo.sp_verify_notification @alert_name, @operator_name, @notification_method, @alert_id OUTPUT, @operator_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check if this notification already exists -- NOTE: The unique index would catch this, but testing for the problem here lets us -- control the message. IF (EXISTS (SELECT * FROM msdb.dbo.sysnotifications WHERE (alert_id = @alert_id) AND (operator_id = @operator_id))) BEGIN SELECT @notification = @alert_name + N' / ' + @operator_name RAISERROR(14261, 16, 1, @res_notification, @notification) RETURN(1) -- Failure END SELECT @old_has_notification = has_notification FROM msdb.dbo.sysalerts WHERE (id = @alert_id) -- Do the INSERT INSERT INTO msdb.dbo.sysnotifications (alert_id, operator_id, notification_method) VALUES (@alert_id, @operator_id, @notification_method) SELECT @retval = @@error SELECT @new_has_notification = has_notification FROM msdb.dbo.sysalerts WHERE (id = @alert_id) -- Notify SQLServerAgent of the change - if any - to has_notifications IF (@old_has_notification <> @new_has_notification) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'U' RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_UPDATE_NOTIFICATION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_notification...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_update_notification') AND (type = 'P'))) DROP PROCEDURE sp_update_notification go CREATE PROCEDURE sp_update_notification @alert_name sysname, @operator_name sysname, @notification_method TINYINT -- 1 = Email, 2 = Pager, 4 = NetSend, 7 = All AS BEGIN DECLARE @alert_id INT DECLARE @operator_id INT DECLARE @notification NVARCHAR(512) DECLARE @retval INT DECLARE @old_has_notification INT DECLARE @new_has_notification INT DECLARE @res_notification NVARCHAR(100) SET NOCOUNT ON SELECT @res_notification = FORMATMESSAGE(14210) -- Remove any leading/trailing spaces from parameters SELECT @alert_name = LTRIM(RTRIM(@alert_name)) SELECT @operator_name = LTRIM(RTRIM(@operator_name)) -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Check if the Notification is valid EXECUTE sp_verify_notification @alert_name, @operator_name, @notification_method, @alert_id OUTPUT, @operator_id OUTPUT -- Check if this notification exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysnotifications WHERE (alert_id = @alert_id) AND (operator_id = @operator_id))) BEGIN SELECT @notification = @alert_name + N' / ' + @operator_name RAISERROR(14262, 16, 1, @res_notification, @notification) RETURN(1) -- Failure END SELECT @old_has_notification = has_notification FROM msdb.dbo.sysalerts WHERE (id = @alert_id) -- Do the UPDATE UPDATE msdb.dbo.sysnotifications SET notification_method = @notification_method WHERE (alert_id = @alert_id) AND (operator_id = @operator_id) SELECT @retval = @@error SELECT @new_has_notification = has_notification FROM msdb.dbo.sysalerts WHERE (id = @alert_id) -- Notify SQLServerAgent of the change - if any - to has_notifications IF (@old_has_notification <> @new_has_notification) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'U' RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_DELETE_NOTIFICATION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_notification...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_notification') AND (type = 'P'))) DROP PROCEDURE sp_delete_notification go CREATE PROCEDURE sp_delete_notification @alert_name sysname, @operator_name sysname AS BEGIN DECLARE @alert_id INT DECLARE @operator_id INT DECLARE @ignored TINYINT DECLARE @notification NVARCHAR(512) DECLARE @retval INT DECLARE @old_has_notification INT DECLARE @new_has_notification INT DECLARE @res_notification NVARCHAR(100) SET NOCOUNT ON SELECT @res_notification = FORMATMESSAGE(14210) -- Remove any leading/trailing spaces from parameters SELECT @alert_name = LTRIM(RTRIM(@alert_name)) SELECT @operator_name = LTRIM(RTRIM(@operator_name)) -- Only a sysadmin can do this IF ((ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END -- Get the alert and operator ID's EXECUTE sp_verify_notification @alert_name, @operator_name, 7, -- A dummy (but valid) value @alert_id OUTPUT, @operator_id OUTPUT -- Check if this notification exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysnotifications WHERE (alert_id = @alert_id) AND (operator_id = @operator_id))) BEGIN SELECT @notification = @alert_name + N' / ' + @operator_name RAISERROR(14262, 16, 1, @res_notification, @notification) RETURN(1) -- Failure END SELECT @old_has_notification = has_notification FROM msdb.dbo.sysalerts WHERE (id = @alert_id) -- Do the Delete DELETE FROM msdb.dbo.sysnotifications WHERE (alert_id = @alert_id) AND (operator_id = @operator_id) SELECT @retval = @@error SELECT @new_has_notification = has_notification FROM msdb.dbo.sysalerts WHERE (id = @alert_id) -- Notify SQLServerAgent of the change - if any - to has_notifications IF (@old_has_notification <> @new_has_notification) EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'A', @alert_id = @alert_id, @action_type = N'U' RETURN(@retval) -- 0 means success END go /**************************************************************/ /* SP_HELP_NOTIFICATION */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_notification...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_notification') AND (type = 'P'))) DROP PROCEDURE sp_help_notification go CREATE PROCEDURE sp_help_notification @object_type CHAR(9), -- Either 'ALERTS' (enumerates Alerts for given Operator) -- or 'OPERATORS' (enumerates Operators for given Alert) @name sysname, -- Either an Operator Name (if @object_type is 'ALERTS') -- or an Alert Name (if @object_type is 'OPERATORS') @enum_type CHAR(10), -- Either 'ALL' (enumerate all objects [eg. all alerts irrespective of whether 'Fred' receives a notification for them]) -- or 'ACTUAL' (enumerate only the associated objects [eg. only the alerts which 'Fred' receives a notification for]) -- or 'TARGET' (enumerate only the objects matching @target_name [eg. a single row showing how 'Fred' is notfied for alert 'Test']) @notification_method TINYINT, -- Either 1 (Email) - Modifies the result set to only show use_email column -- or 2 (Pager) - Modifies the result set to only show use_pager column -- or 4 (NetSend) - Modifies the result set to only show use_netsend column -- or 7 (All) - Modifies the result set to show all the use_xxx columns @target_name sysname = NULL -- Either an Alert Name (if @object_type is 'ALERTS') -- or an Operator Name (if @object_type is 'OPERATORS') -- NOTE: This parameter is only required if @enum_type is 'TARGET') AS BEGIN DECLARE @id INT -- We use this to store the decode of @name DECLARE @target_id INT -- We use this to store the decode of @target_name DECLARE @select_clause NVARCHAR(1024) DECLARE @from_clause NVARCHAR(512) DECLARE @where_clause NVARCHAR(512) DECLARE @res_valid_range NVARCHAR(100) SET NOCOUNT ON SELECT @res_valid_range = FORMATMESSAGE(14208) -- Remove any leading/trailing spaces from parameters SELECT @object_type = UPPER(LTRIM(RTRIM(@object_type)) collate SQL_Latin1_General_CP1_CS_AS) SELECT @name = LTRIM(RTRIM(@name)) SELECT @enum_type = UPPER(LTRIM(RTRIM(@enum_type)) collate SQL_Latin1_General_CP1_CS_AS) SELECT @target_name = LTRIM(RTRIM(@target_name)) -- Turn [nullable] empty string parameters into NULLs IF (@target_name = N'') SELECT @target_name = NULL -- Check ObjectType IF (@object_type NOT IN ('ALERTS', 'OPERATORS')) BEGIN RAISERROR(14266, 16, 1, '@object_type', 'ALERTS, OPERATORS') RETURN(1) -- Failure END -- Check AlertName IF (@object_type = 'OPERATORS') AND (NOT EXISTS (SELECT * FROM msdb.dbo.sysalerts WHERE (name = @name))) BEGIN RAISERROR(14262, 16, 1, '@name', @name) RETURN(1) -- Failure END -- Check OperatorName IF (@object_type = 'ALERTS') AND (NOT EXISTS (SELECT * FROM msdb.dbo.sysoperators WHERE (name = @name))) BEGIN RAISERROR(14262, 16, 1, '@name', @name) RETURN(1) -- Failure END -- Check EnumType IF (@enum_type NOT IN ('ALL', 'ACTUAL', 'TARGET')) BEGIN RAISERROR(14266, 16, 1, '@enum_type', 'ALL, ACTUAL, TARGET') RETURN(1) -- Failure END -- Check Notification Method IF ((@notification_method < 1) OR (@notification_method > 7)) BEGIN RAISERROR(14266, 16, 1, '@notification_method', @res_valid_range) RETURN(1) -- Failure END -- If EnumType is 'TARGET', check if we have a @TargetName parameter IF (@enum_type = 'TARGET') AND (@target_name IS NULL) BEGIN RAISERROR(14502, 16, 1) RETURN(1) -- Failure END -- If EnumType isn't 'TARGET', we shouldn't have an @target_name parameter IF (@enum_type <> 'TARGET') AND (@target_name IS NOT NULL) BEGIN RAISERROR(14503, 16, 1) RETURN(1) -- Failure END -- Translate the Name into an ID IF (@object_type = 'ALERTS') BEGIN SELECT @id = id FROM msdb.dbo.sysoperators WHERE (name = @name) END IF (@object_type = 'OPERATORS') BEGIN SELECT @id = id FROM msdb.dbo.sysalerts WHERE (name = @name) END -- Translate the TargetName into a TargetID IF (@target_name IS NOT NULL) BEGIN IF (@object_type = 'OPERATORS') BEGIN SELECT @target_id = id FROM msdb.dbo.sysoperators WHERE (name = @target_name ) END IF (@object_type = 'ALERTS') BEGIN SELECT @target_id = id FROM msdb.dbo.sysalerts WHERE (name = @target_name) END IF (@target_id IS NULL) -- IE. the Target Name is invalid BEGIN RAISERROR(14262, 16, 1, @object_type, @target_name) RETURN(1) -- Failure END END -- Ok, the parameters look good so generate the SQL then EXECUTE() it... -- Generate the 'stub' SELECT clause and the FROM clause IF (@object_type = 'OPERATORS') -- So we want a list of Operators for the supplied AlertID BEGIN SELECT @select_clause = N'SELECT operator_id = o.id, operator_name = o.name, ' IF (@enum_type = 'ALL') SELECT @from_clause = N'FROM msdb.dbo.sysoperators o LEFT OUTER JOIN msdb.dbo.sysnotifications sn ON (o.id = sn.operator_id) ' ELSE SELECT @from_clause = N'FROM msdb.dbo.sysoperators o, msdb.dbo.sysnotifications sn ' END IF (@object_type = 'ALERTS') -- So we want a list of Alerts for the supplied OperatorID BEGIN SELECT @select_clause = N'SELECT alert_id = a.id, alert_name = a.name, ' IF (@enum_type = 'ALL') SELECT @from_clause = N'FROM msdb.dbo.sysalerts a LEFT OUTER JOIN msdb.dbo.sysnotifications sn ON (a.id = sn.alert_id) ' ELSE SELECT @from_clause = N'FROM msdb.dbo.sysalerts a, msdb.dbo.sysnotifications sn ' END -- Add the required use_xxx columns to the SELECT clause IF (@notification_method & 1 = 1) SELECT @select_clause = @select_clause + N'use_email = ISNULL((sn.notification_method & 1) / POWER(2, 0), 0), ' IF (@notification_method & 2 = 2) SELECT @select_clause = @select_clause + N'use_pager = ISNULL((sn.notification_method & 2) / POWER(2, 1), 0), ' IF (@notification_method & 4 = 4) SELECT @select_clause = @select_clause + N'use_netsend = ISNULL((sn.notification_method & 4) / POWER(2, 2), 0), ' -- Remove the trailing comma SELECT @select_clause = SUBSTRING(@select_clause, 1, (DATALENGTH(@select_clause) / 2) - 2) + N' ' -- Generate the WHERE clause IF (@object_type = 'OPERATORS') BEGIN IF (@enum_type = 'ALL') SELECT @from_clause = @from_clause + N' AND (sn.alert_id = ' + CONVERT(VARCHAR(10), @id) + N')' IF (@enum_type = 'ACTUAL') SELECT @where_clause = N'WHERE (o.id = sn.operator_id) AND (sn.alert_id = ' + CONVERT(VARCHAR(10), @id) + N') AND (sn.notification_method & ' + CONVERT(VARCHAR, @notification_method) + N' <> 0)' IF (@enum_type = 'TARGET') SELECT @where_clause = N'WHERE (o.id = sn.operator_id) AND (sn.operator_id = ' + CONVERT(VARCHAR(10), @target_id) + N') AND (sn.alert_id = ' + CONVERT(VARCHAR(10), @id) + N')' END IF (@object_type = 'ALERTS') BEGIN IF (@enum_type = 'ALL') SELECT @from_clause = @from_clause + N' AND (sn.operator_id = ' + CONVERT(VARCHAR(10), @id) + N')' IF (@enum_type = 'ACTUAL') SELECT @where_clause = N'WHERE (a.id = sn.alert_id) AND (sn.operator_id = ' + CONVERT(VARCHAR(10), @id) + N') AND (sn.notification_method & ' + CONVERT(VARCHAR, @notification_method) + N' <> 0)' IF (@enum_type = 'TARGET') SELECT @where_clause = N'WHERE (a.id = sn.alert_id) AND (sn.alert_id = ' + CONVERT(VARCHAR(10), @target_id) + N') AND (sn.operator_id = ' + CONVERT(VARCHAR(10), @id) + N')' END -- Add the has_email and has_pager columns to the SELECT clause IF (@object_type = 'OPERATORS') BEGIN SELECT @select_clause = @select_clause + N', has_email = PATINDEX(N''%[^ ]%'', ISNULL(o.email_address, N''''))' SELECT @select_clause = @select_clause + N', has_pager = PATINDEX(N''%[^ ]%'', ISNULL(o.pager_address, N''''))' SELECT @select_clause = @select_clause + N', has_netsend = PATINDEX(N''%[^ ]%'', ISNULL(o.netsend_address, N''''))' END IF (@object_type = 'ALERTS') BEGIN -- NOTE: We return counts so that the UI can detect 'unchecking' the last notification SELECT @select_clause = @select_clause + N', has_email = (SELECT COUNT(*) FROM sysnotifications WHERE (alert_id = a.id) AND ((notification_method & 1) = 1)) ' SELECT @select_clause = @select_clause + N', has_pager = (SELECT COUNT(*) FROM sysnotifications WHERE (alert_id = a.id) AND ((notification_method & 2) = 2)) ' SELECT @select_clause = @select_clause + N', has_netsend = (SELECT COUNT(*) FROM sysnotifications WHERE (alert_id = a.id) AND ((notification_method & 4) = 4)) ' END EXECUTE (@select_clause + @from_clause + @where_clause) RETURN(@@error) -- 0 means success END go PRINT '' PRINT 'Creating procedure sp_help_jobactivity...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'sp_help_jobactivity') AND (type = 'P'))) DROP PROCEDURE sp_help_jobactivity go CREATE PROCEDURE sp_help_jobactivity @job_id UNIQUEIDENTIFIER = NULL, -- If provided should NOT also provide job_name @job_name sysname = NULL, -- If provided should NOT also provide job_id @session_id INT = NULL AS BEGIN DECLARE @retval INT DECLARE @session_id_as_char NVARCHAR(16) SET NOCOUNT ON -- Remove any leading/trailing spaces from parameters (except @owner_login_name) SELECT @job_name = LTRIM(RTRIM(@job_name)) -- Turn [nullable] empty string parameters into NULLs IF (@job_name = N'') SELECT @job_name = NULL IF ((@job_id IS NOT NULL) OR (@job_name IS NOT NULL)) BEGIN EXECUTE @retval = sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT IF (@retval <> 0) RETURN(1) -- Failure END IF @session_id IS NULL SELECT TOP(1) @session_id = session_id FROM syssessions ORDER by agent_start_date DESC ELSE IF NOT EXISTS( SELECT * FROM syssessions WHERE session_id = @session_id) BEGIN SELECT @session_id_as_char = CONVERT(NVARCHAR(16), @session_id) RAISERROR(14262, -1, -1, '@session_id', @session_id_as_char) RETURN(1) --failure END SELECT ja.session_id, ja.job_id, j.name AS job_name, ja.run_requested_date, ja.run_requested_source, ja.queued_date, ja.start_execution_date, ja.last_executed_step_id, ja.last_executed_step_date, ja.stop_execution_date, ja.next_scheduled_run_date, ja.job_history_id, jh.message, jh.run_status, jh.operator_id_emailed, jh.operator_id_netsent, jh.operator_id_paged FROM (msdb.dbo.sysjobactivity ja LEFT JOIN msdb.dbo.sysjobhistory jh ON ja.job_history_id = jh.instance_id) join msdb.dbo.sysjobs_view j on ja.job_id = j.job_id WHERE (@job_id IS NULL OR ja.job_id = @job_id) AND ja.session_id = @session_id RETURN(0) END go /**************************************************************/ /* */ /* T R I G G E R S */ /* */ /**************************************************************/ /**************************************************************/ /* DROP THE OLD 6.x TRIGGERS */ /* [multiple triggers of the same type are allowed in 7.0] */ /**************************************************************/ IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'NewOrChangedNotification') AND (type = 'TR'))) DROP TRIGGER NewOrChangedNotification IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'RemovedNotification') AND (type = 'TR'))) DROP TRIGGER RemovedNotification go /**************************************************************/ /* TRIG_NOTIFICATION_INS_OR_UPD */ /**************************************************************/ PRINT '' PRINT 'Creating trigger trig_notification_ins_or_upd...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_notification_ins_or_upd') AND (type = 'TR'))) DROP TRIGGER trig_notification_ins_or_upd go CREATE TRIGGER trig_notification_ins_or_upd ON msdb.dbo.sysnotifications FOR INSERT, UPDATE AS BEGIN SET NOCOUNT ON -- First, throw out 'non-notification' rows DELETE FROM msdb.dbo.sysnotifications WHERE (notification_method = 0) -- Reset the has_notification flag for the affected alerts UPDATE msdb.dbo.sysalerts SET has_notification = 0 FROM inserted i, msdb.dbo.sysalerts sa WHERE (i.alert_id = sa.id) -- Update sysalerts.has_notification (for email) UPDATE msdb.dbo.sysalerts SET has_notification = has_notification | 1 FROM msdb.dbo.sysalerts sa, msdb.dbo.sysnotifications sn, inserted i WHERE (sa.id = sn.alert_id) AND (sa.id = i.alert_id) AND ((sn.notification_method & 1) = 1) -- Update sysalerts.has_notification (for pager) UPDATE msdb.dbo.sysalerts SET has_notification = has_notification | 2 FROM msdb.dbo.sysalerts sa, msdb.dbo.sysnotifications sn, inserted i WHERE (sa.id = sn.alert_id) AND (sa.id = i.alert_id) AND ((sn.notification_method & 2) = 2) -- Update sysalerts.has_notification (for netsend) UPDATE msdb.dbo.sysalerts SET has_notification = has_notification | 4 FROM msdb.dbo.sysalerts sa, msdb.dbo.sysnotifications sn, inserted i WHERE (sa.id = sn.alert_id) AND (sa.id = i.alert_id) AND ((sn.notification_method & 4) = 4) END go /**************************************************************/ /* TRIG_NOTIFICATION_DELETE */ /**************************************************************/ PRINT '' PRINT 'Creating trigger trig_notification_delete...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'trig_notification_delete') AND (type = 'TR'))) DROP TRIGGER trig_notification_delete go CREATE TRIGGER trig_notification_delete ON msdb.dbo.sysnotifications FOR DELETE AS BEGIN SET NOCOUNT ON -- Reset the has_notification flag for the affected alerts UPDATE msdb.dbo.sysalerts SET has_notification = 0 FROM deleted d, msdb.dbo.sysalerts sa WHERE (d.alert_id = sa.id) -- Update sysalerts.has_notification (for email) UPDATE msdb.dbo.sysalerts SET has_notification = has_notification | 1 FROM msdb.dbo.sysalerts sa, msdb.dbo.sysnotifications sn, deleted d WHERE (sa.id = sn.alert_id) AND (sa.id = d.alert_id) AND ((sn.notification_method & 1) = 1) -- Update sysalerts.has_notification (for pager) UPDATE msdb.dbo.sysalerts SET has_notification = has_notification | 2 FROM msdb.dbo.sysalerts sa, msdb.dbo.sysnotifications sn, deleted d WHERE (sa.id = sn.alert_id) AND (sa.id = d.alert_id) AND ((sn.notification_method & 2) = 2) -- Update sysalerts.has_notification (for netsend) UPDATE msdb.dbo.sysalerts SET has_notification = has_notification | 4 FROM msdb.dbo.sysalerts sa, msdb.dbo.sysnotifications sn, deleted d WHERE (sa.id = sn.alert_id) AND (sa.id = d.alert_id) AND ((sn.notification_method & 4) = 4) END go /**************************************************************/ /** **/ /** B A C K U P H I S T O R Y S U P P O R T **/ /** **/ /**************************************************************/ /**************************************************************/ /* T A B L E S */ /**************************************************************/ /**************************************************************/ /* BACKUPMEDIASET */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'backupmediaset'))) BEGIN PRINT '' PRINT 'Creating table backupmediaset...' CREATE TABLE backupmediaset ( media_set_id INT IDENTITY NOT NULL PRIMARY KEY, media_uuid UNIQUEIDENTIFIER NULL, -- Null if this media set only one media family media_family_count TINYINT NULL, -- Number of media families in the media set name NVARCHAR(128) NULL, description NVARCHAR(255) NULL, software_name NVARCHAR(128) NULL, software_vendor_id INT NULL, MTF_major_version TINYINT NULL, mirror_count TINYINT NULL, -- number of mirror plexes is_password_protected BIT NULL ) 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='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 ' + @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 ) CREATE INDEX backupsetuuid ON backupset (backup_set_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='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='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 END go /**************************************************************/ -- BACKUPFILE/FILEGROUP -- One row per file/filegroup backed up /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'backupfilegroup'))) BEGIN PRINT '' PRINT 'Creating table backupfilegroup...' CREATE TABLE backupfilegroup ( backup_set_id INT NOT NULL REFERENCES backupset(backup_set_id), name NVARCHAR(128) NOT NULL, filegroup_id INT NOT NULL, filegroup_guid UNIQUEIDENTIFIER NULL, type CHAR(2) NOT NULL, type_desc NVARCHAR(60) NOT NULL, is_default BIT NOT NULL, is_readonly BIT NOT NULL, log_filegroup_guid UNIQUEIDENTIFIER NULL PRIMARY KEY (backup_set_id, filegroup_id) ) END go IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'backupfile'))) BEGIN PRINT '' PRINT 'Creating table backupfile...' CREATE TABLE backupfile ( backup_set_id INT NOT NULL REFERENCES backupset(backup_set_id), first_family_number TINYINT NULL, -- Family number & media number of he first media first_media_number SMALLINT NULL, -- containing this file filegroup_name NVARCHAR(128) NULL, page_size INT NULL, file_number NUMERIC(10,0) NOT NULL, backed_up_page_count NUMERIC(10,0) NULL, file_type CHAR(1) NULL, -- database or log source_file_block_size NUMERIC(10,0) NULL, file_size NUMERIC(20,0) NULL, logical_name NVARCHAR(128) NULL, physical_drive NVARCHAR(260) NULL, -- Drive or partition name physical_name NVARCHAR(260) NULL, -- Remainder of physical (OS) filename state TINYINT NULL, state_desc NVARCHAR(64) NULL, create_lsn NUMERIC(25,0) NULL, drop_lsn NUMERIC(25,0) NULL, file_guid UNIQUEIDENTIFIER NULL, read_only_lsn NUMERIC(25,0) NULL, read_write_lsn NUMERIC(25,0) NULL, differential_base_lsn NUMERIC(25,0) NULL, differential_base_guid UNIQUEIDENTIFIER NULL, backup_size NUMERIC(20,0) NULL, filegroup_guid UNIQUEIDENTIFIER NULL, is_readonly BIT NULL, is_present BIT NULL PRIMARY KEY (backup_set_id, file_number) ) END ELSE BEGIN IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='state' and id = (select id from msdb.dbo.sysobjects where name='backupfile')) BEGIN -- we want NVARCHAR instead of VARCHAR ALTER TABLE backupfile ALTER COLUMN physical_drive NVARCHAR(260) NULL ALTER TABLE backupfile ALTER COLUMN physical_name NVARCHAR(260) NULL ALTER TABLE backupfile ADD state TINYINT NULL, state_desc NVARCHAR(64) NULL, create_lsn NUMERIC(25,0) NULL, drop_lsn NUMERIC(25,0) NULL, file_guid UNIQUEIDENTIFIER NULL, read_only_lsn NUMERIC(25,0) NULL, read_write_lsn NUMERIC(25,0) NULL, differential_base_lsn NUMERIC(25,0) NULL, differential_base_guid UNIQUEIDENTIFIER NULL, backup_size NUMERIC(20,0) NULL, filegroup_guid UNIQUEIDENTIFIER NULL, is_readonly BIT NULL, is_present BIT NULL END END go /**************************************************************/ /* RESTOREHISTORY - One row per restore operation. */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'restorehistory'))) BEGIN PRINT '' PRINT 'Creating table restorehistory...' CREATE TABLE restorehistory ( restore_history_id INT NOT NULL IDENTITY PRIMARY KEY, restore_date DATETIME NULL, destination_database_name NVARCHAR(128) NULL, user_name NVARCHAR(128) NULL, backup_set_id INT NOT NULL REFERENCES backupset(backup_set_id), -- The backup set restored restore_type CHAR(1) NULL, -- Database, file, filegroup, log, verifyonly, ... -- Various options... replace BIT NULL, -- Replace(1), Noreplace(0) recovery BIT NULL, -- Recovery(1), Norecovery(0) restart BIT NULL, -- Restart(1), Norestart(0) stop_at DATETIME NULL, device_count TINYINT NULL, -- Can be less than number of media families stop_at_mark_name NVARCHAR(128) NULL, stop_before BIT NULL ) CREATE INDEX restorehistorybackupset ON restorehistory (backup_set_id) END IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE (name in ('stop_at_mark_name', 'stop_before')) AND (id = (SELECT id FROM msdb.dbo.sysobjects WHERE (name = 'restorehistory'))))) BEGIN IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='stop_before' and id = (select id from msdb.dbo.sysobjects where name='restorehistory')) BEGIN PRINT '' PRINT 'Adding columns to table restorehistory...' ALTER TABLE restorehistory ADD stop_at_mark_name NVARCHAR(128) NULL, stop_before BIT NULL END END go /**************************************************************/ /* RESTOREFILE - One row per file restored. */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'restorefile'))) BEGIN PRINT '' PRINT 'Creating table restorefile...' CREATE TABLE restorefile ( restore_history_id INT NOT NULL REFERENCES restorehistory(restore_history_id), file_number NUMERIC(10,0) NULL, -- Note: requires database to make unique destination_phys_drive NVARCHAR(260) NULL, destination_phys_name NVARCHAR(260) NULL ) END ELSE BEGIN ALTER TABLE restorefile ALTER COLUMN destination_phys_drive NVARCHAR(260) NULL ALTER TABLE restorefile ALTER COLUMN destination_phys_name NVARCHAR(260) NULL END go /**************************************************************/ /* RESTOREFILEGROUP - One row per filegroup restored. */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'restorefilegroup'))) BEGIN PRINT '' PRINT 'Creating table restorefilegroup...' CREATE TABLE restorefilegroup ( restore_history_id INT NOT NULL REFERENCES restorehistory(restore_history_id), filegroup_name NVARCHAR(128) NULL ) END go /**************************************************************/ /* LOGMARKHISTORY - One row per log mark generated */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'logmarkhistory'))) BEGIN PRINT '' PRINT 'Creating table logmarkhistory...' CREATE TABLE logmarkhistory ( database_name NVARCHAR(128) NOT NULL, mark_name NVARCHAR(128) NOT NULL, description NVARCHAR(255) NULL, user_name NVARCHAR(128) NOT NULL, lsn NUMERIC(25,0) NOT NULL, mark_time DATETIME NOT NULL ) CREATE INDEX logmarkhistory1 ON logmarkhistory (database_name, mark_name) CREATE INDEX logmarkhistory2 ON logmarkhistory (database_name, lsn) END go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'trig_backupset_delete') AND (OBJECTPROPERTY(id, 'IsTrigger') != 0))) BEGIN DROP TRIGGER trig_backupset_delete END go CREATE TRIGGER trig_backupset_delete ON msdb.dbo.backupset FOR DELETE AS BEGIN DELETE FROM msdb.dbo.logmarkhistory from deleted WHERE (msdb.dbo.logmarkhistory.database_name = deleted.database_name) AND (msdb.dbo.logmarkhistory.lsn >= deleted.first_lsn) AND (msdb.dbo.logmarkhistory.lsn < deleted.last_lsn) END go /**************************************************************/ /* suspect_pages */ /**************************************************************/ IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'badpagehistory'))) DROP TABLE badpagehistory go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'suspect_page_table'))) DROP TABLE suspect_page_table go IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = 'suspect_pages'))) BEGIN PRINT '' PRINT 'Creating table suspect_pages...' CREATE TABLE suspect_pages ( database_id INT NOT NULL, file_id INT NOT NULL, page_id bigint NOT NULL, -- we only use unsigned 32bits event_type INT NOT NULL, error_count INT NOT NULL, last_update_date DATETIME NOT NULL DEFAULT GETDATE() ) END go /**************************************************************/ /** **/ /** O B J E C T P E R M I S S I O N S **/ /** **/ /**************************************************************/ -------------------------------------------------------------- -- SQL Agent roles and procs -------------------------------------------------------------- PRINT '' PRINT 'Setting object permissions...' go -- Create the TargetServers role (for use by target servers when downloading jobs / uploading status) IF (EXISTS (SELECT * FROM msdb.dbo.sysusers WHERE (name = N'TargetServersRole') AND (issqlrole = 1))) BEGIN -- If there are no members in the role, then drop and re-create it IF ((SELECT COUNT(*) FROM msdb.dbo.sysusers su, msdb.dbo.sysmembers sm WHERE (su.uid = sm.groupuid) AND (su.name = N'TargetServersRole') AND (su.issqlrole = 1)) = 0) BEGIN EXECUTE msdb.dbo.sp_droprole @rolename = N'TargetServersRole' EXECUTE msdb.dbo.sp_addrole @rolename = N'TargetServersRole' END END ELSE EXECUTE msdb.dbo.sp_addrole @rolename = N'TargetServersRole' -- Create the SQLAgentUserRole role IF (EXISTS (SELECT * FROM msdb.dbo.sysusers WHERE (name = N'SQLAgentUserRole') AND (issqlrole = 1))) BEGIN -- If there are no members in the role, then drop and re-create it IF ((SELECT COUNT(*) FROM msdb.dbo.sysusers su, msdb.dbo.sysmembers sm WHERE (su.uid = sm.groupuid) AND (su.name = N'SQLAgentUserRole') AND (su.issqlrole = 1)) = 0) BEGIN EXECUTE msdb.dbo.sp_droprole @rolename = N'SQLAgentUserRole' EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentUserRole' END END ELSE EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentUserRole' -- Create the SQLAgentReaderRole role IF (EXISTS (SELECT * FROM msdb.dbo.sysusers WHERE (name = N'SQLAgentReaderRole') AND (issqlrole = 1))) BEGIN -- If there are no members in the role, then drop and re-create it IF ((SELECT COUNT(*) FROM msdb.dbo.sysusers su, msdb.dbo.sysmembers sm WHERE (su.uid = sm.groupuid) AND (su.name = N'SQLAgentReaderRole') AND (su.issqlrole = 1)) = 0) BEGIN EXECUTE msdb.dbo.sp_droprole @rolename = N'SQLAgentReaderRole' EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentReaderRole' END END ELSE EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentReaderRole' -- Create the SQLAgentOperatorRole role IF (EXISTS (SELECT * FROM msdb.dbo.sysusers WHERE (name = N'SQLAgentOperatorRole') AND (issqlrole = 1))) BEGIN -- If there are no members in the role, then drop and re-create it IF ((SELECT COUNT(*) FROM msdb.dbo.sysusers su, msdb.dbo.sysmembers sm WHERE (su.uid = sm.groupuid) AND (su.name = N'SQLAgentOperatorRole') AND (su.issqlrole = 1)) = 0) BEGIN EXECUTE msdb.dbo.sp_droprole @rolename = N'SQLAgentOperatorRole' EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentOperatorRole' END END ELSE EXECUTE msdb.dbo.sp_addrole @rolename = N'SQLAgentOperatorRole' -- Add roles to each other. -- SQLAgentReaderRole is also SQLAgentUserRole -- SQLAgentOperatorRole is also SQLAgentReaderRole and SQLAgentUserRole EXECUTE sp_addrolemember @rolename = 'SQLAgentUserRole' , @membername = 'SQLAgentReaderRole' EXECUTE sp_addrolemember @rolename = 'SQLAgentReaderRole' , @membername = 'SQLAgentOperatorRole' go GRANT EXECUTE ON sp_notify_operator TO SQLAgentUserRole -- Permissions a non-SA needs to create/update/delete a job GRANT EXECUTE ON sp_get_sqlagent_properties TO SQLAgentUserRole GRANT EXECUTE ON sp_help_category TO SQLAgentUserRole GRANT EXECUTE ON sp_enum_sqlagent_subsystems TO SQLAgentUserRole GRANT EXECUTE ON sp_add_jobserver TO SQLAgentUserRole GRANT EXECUTE ON sp_delete_jobserver TO SQLAgentUserRole GRANT SELECT ON syscategories TO SQLAgentUserRole GRANT EXECUTE ON sp_help_jobhistory TO SQLAgentUserRole GRANT EXECUTE ON sp_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 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 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 sysoperators FROM SQLAgentUserRole REVOKE ALL ON sysnotifications FROM SQLAgentUserRole --DENY TargetServerRole permission that would allow modifying of jobs DENY ALL ON sp_add_jobserver TO TargetServersRole DENY ALL ON sp_delete_jobserver TO TargetServersRole DENY ALL ON sp_add_jobstep TO TargetServersRole DENY ALL ON sp_update_jobstep TO TargetServersRole DENY ALL ON sp_delete_jobstep TO TargetServersRole DENY ALL ON sp_add_jobschedule TO TargetServersRole DENY ALL ON sp_update_jobschedule TO TargetServersRole DENY ALL ON sp_delete_jobschedule TO TargetServersRole DENY ALL ON sp_add_job TO TargetServersRole DENY ALL ON sp_update_job TO TargetServersRole DENY ALL ON sp_delete_job TO TargetServersRole DENY ALL ON sp_start_job TO TargetServersRole DENY ALL ON sp_stop_job TO TargetServersRole DENY ALL ON sp_post_msx_operation TO TargetServersRole DENY ALL ON sp_addtask TO TargetServersRole DENY ALL ON sp_droptask TO TargetServersRole GRANT SELECT ON backupfile TO PUBLIC GRANT SELECT ON backupmediafamily TO PUBLIC GRANT SELECT ON backupmediaset TO PUBLIC GRANT SELECT ON backupset TO PUBLIC GRANT SELECT ON restorehistory TO PUBLIC GRANT SELECT ON restorefile TO PUBLIC GRANT SELECT ON restorefilegroup TO PUBLIC GRANT SELECT ON logmarkhistory TO PUBLIC GRANT SELECT ON suspect_pages TO PUBLIC GRANT SELECT, UPDATE, DELETE ON sysdownloadlist TO TargetServersRole GRANT SELECT, UPDATE ON sysjobservers TO TargetServersRole GRANT SELECT, UPDATE ON systargetservers TO TargetServersRole GRANT EXECUTE ON sp_downloaded_row_limiter TO TargetServersRole GRANT SELECT ON sysjobs TO TargetServersRole GRANT EXECUTE ON sp_help_jobstep TO TargetServersRole GRANT EXECUTE ON sp_help_jobschedule TO TargetServersRole GRANT EXECUTE ON sp_sqlagent_refresh_job TO TargetServersRole GRANT EXECUTE ON sp_sqlagent_probe_msx TO TargetServersRole GRANT EXECUTE ON sp_sqlagent_check_msx_version TO TargetServersRole GRANT EXECUTE ON sp_enlist_tsx TO TargetServersRole GRANT SELECT ON syssubsystems TO TargetServersRole GRANT EXECUTE ON sp_help_jobactivity TO SQLAgentUserRole GRANT EXECUTE ON sp_help_operator TO SQLAgentUserRole go USE msdb go /**************************************************************/ /**************************************************************/ /* BEGIN DTS */ /**************************************************************/ /**************************************************************/ /**************************************************************/ /* DTS TABLES */ /* These are never dropped since we dropped MSDB itself if */ /* this was an upgrade from pre-beta3, and we preserve beta3 */ /* packages. However, we need to add the owner_sid column */ /* if it's not there already, defaulting to sa ownership. */ /**************************************************************/ /**************************************************************/ /* SYSDTSCATEGORIES */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdtscategories'))) BEGIN PRINT '' PRINT 'Creating table sysdtscategories...' CREATE TABLE sysdtscategories ( name sysname NOT NULL, description NVARCHAR(1024) NULL, id UNIQUEIDENTIFIER NOT NULL, parentid UNIQUEIDENTIFIER NOT NULL, --// IID_NULL if a predefined root category CONSTRAINT pk_dtscategories PRIMARY KEY (id), CONSTRAINT uq_dtscategories_name_parent UNIQUE (name, parentid) ) /**************************************************************/ /* PREDEFINED DTS CATEGORIES */ /**************************************************************/ PRINT '' PRINT 'Adding predefined dts categories...' --// MUST BE IN SYNC with DTSPkg.h! --// These must be INSERTed explicitly as the IID_NULL parent does not exist. INSERT sysdtscategories VALUES (N'Local', 'DTS Packages stored on local SQL Server', 'B8C30000-A282-11D1-B7D9-00C04FB6EFD5', '00000000-0000-0000-0000-000000000000') INSERT sysdtscategories VALUES (N'Repository', 'DTS Packages stored on Repository', 'B8C30001-A282-11D1-B7D9-00C04FB6EFD5', '00000000-0000-0000-0000-000000000000') --// Default location for DTSPackage.SaveToSQLServer INSERT sysdtscategories VALUES (N'LocalDefault', 'Default local subcategory for DTS Packages', 'B8C30002-A282-11D1-B7D9-00C04FB6EFD5', 'B8C30000-A282-11D1-B7D9-00C04FB6EFD5') END GO /**************************************************************/ /* SYSDTSPACKAGES */ /**************************************************************/ IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sysdtspackages'))) BEGIN PRINT '' PRINT 'Creating table sysdtspackages...' CREATE TABLE sysdtspackages ( name sysname NOT NULL, --// May have multiple ids id UNIQUEIDENTIFIER NOT NULL, --// May have multiple versionids versionid UNIQUEIDENTIFIER NOT NULL UNIQUE, description NVARCHAR(1024) NULL, categoryid UNIQUEIDENTIFIER NOT NULL REFERENCES sysdtscategories (id), createdate DATETIME, owner sysname, packagedata IMAGE, owner_sid VARBINARY(85) NOT NULL DEFAULT SUSER_SID(N'sa'), packagetype int NOT NULL DEFAULT 0 --// DTSPkgType_Default CONSTRAINT pk_dtspackages PRIMARY KEY (id, versionid) ) END ELSE BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'owner_sid' AND id = OBJECT_ID(N'sysdtspackages'))) BEGIN PRINT '' PRINT 'Altering table sysdtspackages for owner_sid and packagetype...' ALTER TABLE sysdtspackages ADD owner_sid VARBINARY(85) NOT NULL DEFAULT SUSER_SID(N'sa'), packagetype int NOT NULL DEFAULT 0 --// DTSPkgType_Default END IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'packagetype' AND id = OBJECT_ID(N'sysdtspackages'))) BEGIN PRINT '' PRINT 'Altering table sysdtspackages for packagetype...' ALTER TABLE sysdtspackages ADD packagetype int NOT NULL DEFAULT 0 --// DTSPkgType_Default END END GO /**************************************************************/ /* SP_MAKE_DTSPACKAGENAME */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_dtsversion...' go IF OBJECT_ID(N'sp_get_dtsversion') IS NOT NULL DROP PROCEDURE sp_get_dtsversion go CREATE PROCEDURE sp_get_dtsversion AS /* Values for this are same as @@microsoftversion */ /* @@microsoftversion format is 0xaaiibbbb (aa = major, ii = minor, bb[bb] = build #) */ DECLARE @i INT select @i = 0x08000000 /* Must be in hex! */ /* Select the numeric value, and a conversion to make it readable */ select N'Microsoft SQLDTS Scripts' = @i, N'Version' = convert(binary(4), @i) GO GRANT EXECUTE ON sp_get_dtsversion TO PUBLIC GO /**************************************************************/ /* SP_MAKE_DTSPACKAGENAME */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_make_dtspackagename...' go IF OBJECT_ID(N'sp_make_dtspackagename') IS NOT NULL DROP PROCEDURE sp_make_dtspackagename go CREATE PROCEDURE sp_make_dtspackagename @categoryid UNIQUEIDENTIFIER, @name sysname OUTPUT, @flags int = 0 AS SET NOCOUNT ON --// If NULL catid, default to the LocalDefault category. IF (@categoryid IS NULL) SELECT @categoryid = 'B8C30002-A282-11d1-B7D9-00C04FB6EFD5' --// Validate category. We'll generate a unique name within category. DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @categoryid) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @categoryid) RAISERROR(14262, 16, 1, '@categoryid', @stringfromclsid) RETURN(1) -- Failure END --// Autogenerate the next name in our format. DECLARE @max sysname, @i INT, @spidchar NVARCHAR(20) --// Any logic we use may have collisions so let's get the max and wrap if we have to. --// @@spid is necessary for guaranteed uniqueness but makes it ugly so for now, don't use it. --// Note: use only 9 characters as it makes the pattern match easier without overflowing. SELECT @i = 0, @spidchar = '_' -- + LTRIM(STR(@@spid)) + '_' SELECT @max = MAX(name) FROM sysdtspackages WHERE name like 'DTS_' + @spidchar + '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' IF @max IS NOT NULL SELECT @i = CONVERT(INT, SUBSTRING(@max, (DATALENGTH(N'DTS_' + @spidchar) / 2) + 1, 9)) --// Wrap if needed. Find a gap in the names. IF @i < 999999999 BEGIN SELECT @i = @i + 1 END ELSE BEGIN SELECT @i = 1 DECLARE @existingname sysname DECLARE hC CURSOR LOCAL FOR SELECT name FROM sysdtspackages WHERE categoryid = @categoryid ORDER BY name FOR READ ONLY OPEN hC FETCH NEXT FROM hC INTO @existingname WHILE @@FETCH_STATUS = 0 AND @i < 999999999 BEGIN SELECT @name = 'DTS_' + @spidchar + REPLICATE('0', 9 - DATALENGTH(LTRIM(STR(@i)))) + LTRIM(STR(@i)) IF @existingname > @name BREAK SELECT @i = @i + 1 FETCH NEXT FROM hC INTO @existingname END CLOSE hC DEALLOCATE hC END --// Set the name. SELECT @name = 'DTS_' + @spidchar + REPLICATE('0', 9 - DATALENGTH(LTRIM(STR(@i)))) + LTRIM(STR(@i)) IF (@flags & 1) <> 0 SELECT @name GO GRANT EXECUTE ON sp_make_dtspackagename TO PUBLIC GO /**************************************************************/ /* SP_ADD_DTSPACKAGE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_dtspackage...' GO IF OBJECT_ID(N'sp_add_dtspackage') IS NOT NULL DROP PROCEDURE sp_add_dtspackage GO CREATE PROCEDURE sp_add_dtspackage @name sysname, @id UNIQUEIDENTIFIER, @versionid UNIQUEIDENTIFIER, @description NVARCHAR(255), @categoryid UNIQUEIDENTIFIER, @owner sysname, @packagedata IMAGE, @packagetype int = 0 --// DTSPkgType_Default AS SET NOCOUNT ON --// If NULL catid, default to the LocalDefault category. IF (@categoryid IS NULL) SELECT @categoryid = 'B8C30002-A282-11d1-B7D9-00C04FB6EFD5' --// Autogenerate name if it came in NULL. If it didn't, the below will validate uniqueness. IF DATALENGTH(@name) = 0 SELECT @name = NULL IF @name IS NULL BEGIN --// First see if they specified a new version based on id instead of name. if @id IS NOT NULL BEGIN SELECT @name = name FROM sysdtspackages WHERE @id = id IF @name IS NOT NULL GOTO AddPackage -- OK, add with the existing name END --// Name not available, autogenerate one. exec sp_make_dtspackagename @categoryid, @name OUTPUT GOTO AddPackage END --// Verify name unique within category. Allow a new versionid of the same name though. IF EXISTS (SELECT * FROM sysdtspackages WHERE name = @name AND categoryid = @categoryid AND id <> @id) BEGIN RAISERROR (14590, -1, -1, @name) RETURN(1) -- Failure END --// Verify that the same id is not getting a different name. IF EXISTS (SELECT * FROM sysdtspackages WHERE id = @id AND name <> @name) BEGIN DECLARE @stringfromclsid NVARCHAR(200) SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @id) RAISERROR (14597, -1, -1, @stringfromclsid) RETURN(1) -- Failure END --// Verify all versions of a package go in the same category. IF EXISTS (SELECT * FROM sysdtspackages WHERE id = @id AND categoryid <> @categoryid) BEGIN RAISERROR (14596, -1, -1, @name) RETURN(1) -- Failure END --// The real information is in the IMAGE; the rest is "documentary". --// Therefore, there is no need to verify anything. --// The REFERENCE in sysdtspackages will validate @categoryid. AddPackage: --// We will use the original owner_sid for all new versions - all must have the same owner. --// New packages will get the current login's SID as owner_sid. DECLARE @owner_sid VARBINARY(85) SELECT @owner_sid = MIN(owner_sid) FROM sysdtspackages WHERE id = @id IF @@rowcount = 0 OR @owner_sid IS NULL BEGIN SELECT @owner_sid = SUSER_SID() END ELSE BEGIN --// Only the owner of DTS Package ''%s'' or a member of the sysadmin role may create new versions of it. IF (@owner_sid <> SUSER_SID() AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR (14586, -1, -1, @name) RETURN(1) -- Failure END END --// Everything checks out, add the package or its new version. INSERT sysdtspackages ( name, id, versionid, description, categoryid, createdate, owner, packagedata, owner_sid, packagetype ) VALUES ( @name, @id, @versionid, @description, @categoryid, GETDATE(), @owner, @packagedata, @owner_sid, @packagetype ) RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_add_dtspackage TO PUBLIC GO /**************************************************************/ /* SP_DROP_DTSPACKAGE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_drop_dtspackage...' go IF OBJECT_ID(N'sp_drop_dtspackage') IS NOT NULL DROP PROCEDURE sp_drop_dtspackage go CREATE PROCEDURE sp_drop_dtspackage @name sysname, @id UNIQUEIDENTIFIER, @versionid UNIQUEIDENTIFIER AS SET NOCOUNT ON --// Does the specified package (uniquely) exist? Referencing by name only may not be unique. --// We do a bit of a hack here as SQL can't handle a DISTINCT clause with UNIQUEIDENTIFIER. --// @id will get the first id returned; if only name specified, see if there are more. DECLARE @findid UNIQUEIDENTIFIER SELECT @findid = id FROM sysdtspackages WHERE (@name IS NOT NULL OR @id IS NOT NULL OR @versionid IS NOT NULL) AND (@name IS NULL OR @name = name) AND (@id IS NULL OR @id = id) AND (@versionid IS NULL or @versionid = versionid) IF @@rowcount = 0 BEGIN DECLARE @pkgnotfound NVARCHAR(200) DECLARE @dts_package_res NVARCHAR(100) SELECT @pkgnotfound = FORMATMESSAGE(14599) + ' = ''' + ISNULL(@name, FORMATMESSAGE(14589)) + '''; ' + FORMATMESSAGE(14588) + ' {' SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @id IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @id) END + '}.{' SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @versionid IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @versionid) END + '}' SELECT @dts_package_res = FORMATMESSAGE(14594) RAISERROR(14262, 16, 1, @dts_package_res, @pkgnotfound) RETURN(1) -- Failure END ELSE IF @name IS NOT NULL AND @id IS NULL AND @versionid IS NULL AND EXISTS (SELECT * FROM sysdtspackages WHERE name = @name AND id <> @findid) BEGIN RAISERROR(14595, -1, -1, @name) RETURN(1) -- Failure END SELECT @id = @findid --// Only the owner of DTS Package ''%s'' or a member of the sysadmin role may drop it or any of its versions. --// sp_add_dtspackage ensures that all versions have the same owner_sid. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN IF (NOT EXISTS (SELECT * FROM sysdtspackages WHERE id = @id AND owner_sid = SUSER_SID())) BEGIN SELECT @name = name FROM sysdtspackages WHERE id = @id RAISERROR (14587, -1, -1, @name) RETURN(1) -- Failure END END --// If @versionid is NULL, drop all versions of name, else only the @versionid version. DELETE sysdtspackages WHERE id = @id AND (@versionid IS NULL OR @versionid = versionid) RETURN 0 -- SUCCESS go GRANT EXECUTE ON sp_drop_dtspackage TO PUBLIC go /**************************************************************/ /* SP_REASSIGN_DTSPACKAGEOWNER */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_reassign_dtspackageowner...' go IF OBJECT_ID(N'sp_reassign_dtspackageowner') IS NOT NULL DROP PROCEDURE sp_reassign_dtspackageowner go CREATE PROCEDURE sp_reassign_dtspackageowner @name sysname, @id UNIQUEIDENTIFIER, @newloginname sysname AS SET NOCOUNT ON --// First, is this a valid login? IF SUSER_SID(@newloginname) IS NULL BEGIN RAISERROR(14262, -1, -1, '@newloginname', @newloginname) RETURN(1) -- Failure END --// Does the specified package (uniquely) exist? Referencing by name only may not be unique. --// We do a bit of a hack here as SQL can't handle a DISTINCT clause with UNIQUEIDENTIFIER. --// @id will get the first id returned; if only name specified, see if there are more. DECLARE @findid UNIQUEIDENTIFIER SELECT @findid = id FROM sysdtspackages WHERE (@name IS NOT NULL OR @id IS NOT NULL) AND (@name IS NULL OR @name = name) AND (@id IS NULL OR @id = id) IF @@rowcount = 0 BEGIN DECLARE @pkgnotfound NVARCHAR(200) DECLARE @dts_package_res NVARCHAR(100) SELECT @pkgnotfound = FORMATMESSAGE(14599) + ' = ''' + ISNULL(@name, FORMATMESSAGE(14589)) + '''; ' + FORMATMESSAGE(14588) + ' {' SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @id IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @id) END + '}.{' SELECT @pkgnotfound = @pkgnotfound + FORMATMESSAGE(14589) + '}' SELECT @dts_package_res = FORMATMESSAGE(14594) RAISERROR(14262, 16, 1, @dts_package_res, @pkgnotfound) RETURN(1) -- Failure END ELSE IF @name IS NOT NULL AND @id IS NULL AND EXISTS (SELECT * FROM sysdtspackages WHERE name = @name AND id <> @findid) BEGIN RAISERROR(14595, -1, -1, @name) RETURN(1) -- Failure END SELECT @id = @findid --// Only the owner of DTS Package ''%s'' or a member of the sysadmin role may reassign its ownership. --// sp_add_dtspackage ensures that all versions have the same owner_sid. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN IF (NOT EXISTS (SELECT * FROM sysdtspackages WHERE id = @id AND owner_sid = SUSER_SID())) BEGIN SELECT @name = name FROM sysdtspackages WHERE id = @id RAISERROR (14585, -1, -1, @name) RETURN(1) -- Failure END END --// Everything checks out, so reassign the owner. --// Note that @newloginname may be a sql server login rather than a network user, --// which is not quite the same as when a package is created. UPDATE sysdtspackages SET owner_sid = SUSER_SID(@newloginname), owner = @newloginname WHERE id = @id RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_reassign_dtspackageowner TO PUBLIC GO /**************************************************************/ /* SP_GET_DTSPACKAGE */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_dtspackage...' go IF OBJECT_ID(N'sp_get_dtspackage') IS NOT NULL DROP PROCEDURE sp_get_dtspackage go CREATE PROCEDURE sp_get_dtspackage @name sysname, @id UNIQUEIDENTIFIER, @versionid UNIQUEIDENTIFIER AS SET NOCOUNT ON --// Does the specified package (uniquely) exist? Dropping by name only may not be unique. --// We do a bit of a hack here as SQL can't handle a DISTINCT clause with UNIQUEIDENTIFIER. --// @id will get the first id returned; if only name specified, see if there are more. DECLARE @findid UNIQUEIDENTIFIER SELECT @findid = id FROM sysdtspackages WHERE (@name IS NOT NULL OR @id IS NOT NULL OR @versionid IS NOT NULL) AND (@name IS NULL OR @name = name) AND (@id IS NULL OR @id = id) AND (@versionid IS NULL or @versionid = versionid) IF @@rowcount = 0 BEGIN DECLARE @pkgnotfound NVARCHAR(200) DECLARE @dts_package_res NVARCHAR(100) SELECT @pkgnotfound = FORMATMESSAGE(14599) + ' = ''' + ISNULL(@name, FORMATMESSAGE(14589)) + '''; ' + FORMATMESSAGE(14588) + ' {' SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @id IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @id) END + '}.{' SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @versionid IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @versionid) END + '}' SELECT @dts_package_res = FORMATMESSAGE(14594) RAISERROR(14262, 16, 1, @dts_package_res, @pkgnotfound) RETURN(1) -- Failure END ELSE IF @name IS NOT NULL AND @id IS NULL AND @versionid IS NULL AND EXISTS (SELECT * FROM sysdtspackages WHERE name = @name AND id <> @findid) BEGIN RAISERROR(14595, -1, -1, @name) RETURN(1) -- Failure END SELECT @id = @findid --// If @versionid is NULL, select all versions of name, else only the @versionid version. --// This must return the IMAGE as the rightmost column. SELECT name, id, versionid, description, createdate, owner, pkgsize = datalength(packagedata), packagedata, isowner = CASE WHEN (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 OR owner_sid = SUSER_SID()) THEN 1 ELSE 0 END, packagetype FROM sysdtspackages WHERE id = @id AND (@versionid IS NULL OR @versionid = versionid) ORDER BY name, createdate DESC RETURN 0 -- SUCCESS go GRANT EXECUTE ON sp_get_dtspackage TO PUBLIC go /**************************************************************/ /* SP_REASSIGN_DTSPACKAGECATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_reassign_dtspackagecategory...' go IF OBJECT_ID(N'sp_reassign_dtspackagecategory') IS NOT NULL DROP PROCEDURE sp_reassign_dtspackagecategory go CREATE PROCEDURE sp_reassign_dtspackagecategory @packageid UNIQUEIDENTIFIER, @categoryid UNIQUEIDENTIFIER AS SET NOCOUNT ON --// Does the package exist? DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * from sysdtspackages WHERE id = @packageid) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @packageid) RAISERROR(14262, 16, 1, '@packageid', @stringfromclsid) RETURN(1) -- Failure END --// Does the category exist? IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @categoryid) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @categoryid) RAISERROR(14262, 16, 1, '@categoryid', @stringfromclsid) RETURN(1) -- Failure END UPDATE sysdtspackages SET categoryid = @categoryid WHERE id = @packageid go /**************************************************************/ /* SP_ENUM_DTSPACKAGES */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_dtspackages...' go IF OBJECT_ID(N'sp_enum_dtspackages') IS NOT NULL DROP PROCEDURE sp_enum_dtspackages go CREATE PROCEDURE sp_enum_dtspackages @name_like sysname = '%', @description_like NVARCHAR(255) = '%', @categoryid UNIQUEIDENTIFIER = NULL, @flags INT = 0, --// Bitmask: 0x01 == return image data --// 0x02 == recursive (packagenames and categorynames only) --// 0x04 == all versions (default == only most-recent-versions) --// 0x08 == all prior versions versions (not most-recent; requires @id) @id UNIQUEIDENTIFIER = NULL, --// If non-NULL, enum versions of this package. @wanttype int = NULL --// If non-NULL, enum only packages of the given type AS IF (@flags & 0x02) <> 0 GOTO DO_RECURSE --// Just return the non-IMAGE stuff - sp_get_dtspackage will return the --// actual dtspackage info. DECLARE @latestversiondate datetime SELECT @latestversiondate = NULL IF (@flags & 0x08 = 0x08) BEGIN SELECT @latestversiondate = MAX(t.createdate) FROM sysdtspackages t WHERE t.id = @id IF @latestversiondate IS NULL BEGIN DECLARE @pkgnotfound NVARCHAR(200) DECLARE @dts_package_res NVARCHAR(100) SELECT @pkgnotfound = FORMATMESSAGE(14599) + ' = ' + FORMATMESSAGE(14589) + '; ' + FORMATMESSAGE(14588) + ' {' SELECT @pkgnotfound = @pkgnotfound + CASE WHEN @id IS NULL THEN FORMATMESSAGE(14589) ELSE CONVERT(NVARCHAR(50), @id) END + '}.{' SELECT @pkgnotfound = @pkgnotfound + FORMATMESSAGE(14589) + '}' SELECT @dts_package_res = FORMATMESSAGE(14594) RAISERROR(14262, 16, 1, @dts_package_res, @pkgnotfound) RETURN(1) -- Failure END END SELECT p.name, p.id, p.versionid, p.description, p.createdate, p.owner, size = datalength(p.packagedata), packagedata = CASE (@flags & 0x01) WHEN 0 THEN NULL ELSE p.packagedata END, isowner = CASE WHEN (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1 OR p.owner_sid = SUSER_SID()) THEN 1 ELSE 0 END, p.packagetype FROM sysdtspackages p WHERE (@name_like IS NULL OR p.name LIKE @name_like) AND (@description_like IS NULL OR p.description LIKE @description_like) AND (@categoryid IS NULL OR p.categoryid = @categoryid) AND (@id is NULL OR p.id = @id) -- These filter by version AND ( (@flags & 0x08 = 0x08 AND p.createdate < @latestversiondate) OR ( (@flags & 0x04 = 0x04) OR (@flags & 0x08 = 0 AND p.createdate = (SELECT MAX(t.createdate) FROM sysdtspackages t WHERE t.id = p.id)) ) ) AND (@wanttype is NULL or p.packagetype = @wanttype) ORDER BY id, createdate DESC RETURN 0 -- SUCCESS DO_RECURSE: DECLARE @packagesfound INT SELECT @packagesfound = 0 --// Starting parent category. If null, start at root. if (@categoryid IS NULL) SELECT @categoryid = '00000000-0000-0000-0000-000000000000' IF EXISTS (SELECT * FROM sysdtspackages p INNER JOIN sysdtscategories c ON p.categoryid = c.id WHERE p.categoryid = @categoryid AND (@name_like IS NULL OR p.name LIKE @name_like) AND (@description_like IS NULL OR p.description LIKE @description_like) ) SELECT @packagesfound = 1 IF (@packagesfound <> 0) BEGIN --// Identify the category and list its Packages. SELECT 'Level' = @@nestlevel, 'PackageName' = p.name, 'CategoryName' = c.name FROM sysdtspackages p INNER JOIN sysdtscategories c ON p.categoryid = c.id WHERE p.categoryid = @categoryid AND (@name_like IS NULL OR p.name LIKE @name_like) AND (@description_like IS NULL OR p.description LIKE @description_like) END --// List its subcategories' packages DECLARE @childid UNIQUEIDENTIFIER DECLARE hC CURSOR LOCAL FOR SELECT id FROM sysdtscategories c WHERE parentid = @categoryid ORDER BY c.name FOR READ ONLY OPEN hC FETCH NEXT FROM hC INTO @childid WHILE @@FETCH_STATUS = 0 BEGIN EXECUTE sp_enum_dtspackages @name_like, @description_like, @childid, @flags FETCH NEXT FROM hC INTO @childid END CLOSE hC DEALLOCATE hC RETURN 0 go GRANT EXECUTE ON sp_enum_dtspackages TO PUBLIC go /**************************************************************/ /* SP_ADD_DTSCATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_dtscategory...' go IF OBJECT_ID(N'sp_add_dtscategory') IS NOT NULL DROP PROCEDURE sp_add_dtscategory go CREATE PROCEDURE sp_add_dtscategory @name sysname, @description NVARCHAR(1024), @id UNIQUEIDENTIFIER, @parentid UNIQUEIDENTIFIER AS SET NOCOUNT ON --// If parentid is NULL, use 'Local' IF @parentid IS NULL SELECT @parentid = 'B8C30000-A282-11d1-B7D9-00C04FB6EFD5' --// First do some simple validation of "non-assert" cases. UI should validate others and the table --// definitions will act as an "assert", but we check here (with a nice message) for user-error stuff --// it would be hard for UI to validate. IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @parentid) BEGIN DECLARE @stringfromclsid NVARCHAR(200) SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @parentid) RAISERROR(14262, 16, 1, '@parentid', @stringfromclsid) RETURN(1) -- Failure END IF EXISTS (SELECT * FROM sysdtscategories WHERE name = @name AND parentid = @parentid) BEGIN RAISERROR(14591, 16, -1, @name) RETURN(1) -- Failure END --// id uniqueness is ensured by the primary key. INSERT sysdtscategories ( name, description, id, parentid ) VALUES ( @name, @description, @id, @parentid ) RETURN 0 -- SUCCESS go /**************************************************************/ /* SP_DROP_DTSCATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_drop_dtscategory...' go IF OBJECT_ID(N'sp_drop_dtscategory') IS NOT NULL DROP PROCEDURE sp_drop_dtscategory go CREATE PROCEDURE sp_drop_dtscategory @name_like sysname, @id UNIQUEIDENTIFIER = NULL, @flags INT = 0 --// Bitmask: 0x01 == recursive (drop all subcategories and packages) AS SET NOCOUNT ON --// Temp table in case recursion is needed. DECLARE @recurse TABLE (id UNIQUEIDENTIFIER, passcount INT DEFAULT(0)) IF (@name_like IS NOT NULL) BEGIN INSERT @recurse (id) SELECT id FROM sysdtscategories WHERE name LIKE @name_like IF @@rowcount = 0 BEGIN RAISERROR(14262, 16, 1, '@name_like', @name_like) RETURN(1) -- Failure END IF @@rowcount > 1 BEGIN RAISERROR(14592, 16, -1, @name_like) RETURN(1) -- Failure END SELECT @name_like = name, @id = id FROM sysdtscategories WHERE name LIKE @name_like END ELSE BEGIN --// Verify the id. @name_like will be NULL if we're here so no need to initialize. SELECT @name_like = name FROM sysdtscategories WHERE id = @id IF @name_like IS NULL BEGIN DECLARE @stringfromclsid NVARCHAR(200) SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @id) RAISERROR(14262, 16, 1, '@id', @stringfromclsid) RETURN(1) -- Failure END INSERT @recurse (id) VALUES (@id) END --// We now have a unique category. --// Cannot drop the predefined categories (or the root, which already failed above as IID_NULL --// is not an id in sysdtscategories). These will be at top level. IF @id IN ( 'B8C30000-A282-11d1-B7D9-00C04FB6EFD5' , 'B8C30001-A282-11d1-B7D9-00C04FB6EFD5' , 'B8C30002-A282-11d1-B7D9-00C04FB6EFD5' ) BEGIN RAISERROR(14598, 16, 1) RETURN(1) -- Failure END --// Check for subcategories or packages. IF EXISTS (SELECT * FROM sysdtspackages WHERE categoryid = @id) OR EXISTS (SELECT * FROM sysdtscategories WHERE parentid = @id) BEGIN --// It does. Make sure recursion was requested. IF (@flags & 0x01 = 0) BEGIN RAISERROR(14593, 16, -1, @name_like) RETURN(1) -- Failure END --// Fill up @recurse. UPDATE @recurse SET passcount = 0 WHILE (1 = 1) BEGIN UPDATE @recurse SET passcount = passcount + 1 INSERT @recurse (id, passcount) SELECT c.id, 0 FROM sysdtscategories c INNER JOIN @recurse r ON c.parentid = r.id WHERE passcount = 1 IF @@rowcount = 0 BREAK END END DELETE sysdtspackages FROM sysdtspackages INNER JOIN @recurse r ON sysdtspackages.categoryid = r.id DELETE sysdtscategories FROM sysdtscategories INNER JOIN @recurse r ON sysdtscategories.id = r.id RETURN(0) -- SUCCESS go /**************************************************************/ /* SP_MODIFY_DTSCATEGORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_modify_dtscategory...' go IF OBJECT_ID(N'sp_modify_dtscategory') IS NOT NULL DROP PROCEDURE sp_modify_dtscategory go CREATE PROCEDURE sp_modify_dtscategory @id UNIQUEIDENTIFIER, @name sysname, @description NVARCHAR(1024), @parentid UNIQUEIDENTIFIER AS SET NOCOUNT ON --// Validate. DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @id) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @id) RAISERROR(14262, 16, 1, '@id', @stringfromclsid) RETURN(1) -- Failure END IF NOT EXISTS (SELECT * FROM sysdtscategories WHERE id = @parentid) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @parentid) RAISERROR(14262, 16, 1, '@parentid', @stringfromclsid) RETURN(1) -- Failure END --// Check the name uniqueness within parent, but make sure the id is different (we may just be renaming --// without reassigning parentage). IF EXISTS (SELECT * FROM sysdtscategories WHERE name = @name AND parentid = @parentid and id <> @id) BEGIN RAISERROR(14591, 16, -1, @name) RETURN(1) -- Failure END UPDATE sysdtscategories SET name = @name, description = @description, parentid = @parentid WHERE id = @id RETURN(0) -- SUCCESS go /**************************************************************/ /* SP_ENUM_DTSCATEGORIES */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_dtscategories...' go IF OBJECT_ID(N'sp_enum_dtscategories') IS NOT NULL DROP PROCEDURE sp_enum_dtscategories go CREATE PROCEDURE sp_enum_dtscategories @parentid UNIQUEIDENTIFIER = NULL, @flags INT = 0 --// Bitmask: 0x01 == recursive (enum all subcategories; names only) AS IF (@flags & 0x01) <> 0 GOTO DO_RECURSE --// Go to the root if no parentid specified IF @parentid IS NULL SELECT @parentid = '00000000-0000-0000-0000-000000000000' --// 'No results' is valid here. SELECT name, description, id FROM sysdtscategories WHERE parentid = @parentid ORDER BY name RETURN 0 DO_RECURSE: --// Identify the category. IF @@nestlevel <> 0 SELECT 'Level' = @@nestlevel, name FROM sysdtscategories WHERE id = @parentid --// List its subcategories DECLARE @childid UNIQUEIDENTIFIER DECLARE hC CURSOR LOCAL FOR SELECT id FROM sysdtscategories c WHERE parentid = @parentid ORDER BY c.name FOR READ ONLY OPEN hC FETCH NEXT FROM hC INTO @childid WHILE @@FETCH_STATUS = 0 BEGIN EXECUTE sp_enum_dtscategories @childid, @flags FETCH NEXT FROM hC INTO @childid END CLOSE hC DEALLOCATE hC RETURN 0 go /**************************************************************/ /* Drop Beta1 DTS Logging objects */ /**************************************************************/ if OBJECT_ID('sysdtspackagestepslog') IS NOT NULL BEGIN PRINT '' PRINT 'Dropping Beta1 logging tables and stored procedures...' DROP TABLE sysdtspackagestepslog IF OBJECT_ID('sysdtspackagelog') IS NOT NULL DROP TABLE sysdtspackagelog IF OBJECT_ID('sp_log_dtspackage') IS NOT NULL DROP PROCEDURE sp_log_dtspackage IF OBJECT_ID('sp_log_dtspackagesteps') IS NOT NULL DROP PROCEDURE sp_log_dtspackagesteps END /**************************************************************/ /* SYSDTSPACKAGELOG */ /**************************************************************/ if OBJECT_ID('sysdtspackagelog') IS NULL BEGIN PRINT '' PRINT 'Creating table sysdtspackagelog...' CREATE TABLE sysdtspackagelog ( name sysname NOT NULL, description NVARCHAR(1000) NULL, id UNIQUEIDENTIFIER NOT NULL, versionid UNIQUEIDENTIFIER NOT NULL, lineagefull UNIQUEIDENTIFIER NOT NULL PRIMARY KEY, lineageshort INT NOT NULL, starttime DATETIME NOT NULL, endtime DATETIME NULL, elapsedtime double precision NULL, computer sysname NOT NULL, operator sysname NOT NULL, logdate datetime NOT NULL DEFAULT GETDATE(), errorcode INT NULL, errordescription NVARCHAR(2000) NULL ) END /**************************************************************/ /* SYSDTSSTEPLOG */ /**************************************************************/ if OBJECT_ID('sysdtssteplog') IS NULL BEGIN PRINT '' PRINT 'Creating table sysdtssteplog...' CREATE TABLE sysdtssteplog ( stepexecutionid BIGINT IDENTITY (1, 1) NOT NULL PRIMARY KEY, lineagefull UNIQUEIDENTIFIER NOT NULL REFERENCES sysdtspackagelog(lineagefull) ON DELETE CASCADE, stepname sysname NOT NULL, stepexecstatus int NULL, stepexecresult int NULL, starttime DATETIME NOT NULL, endtime DATETIME NULL, elapsedtime double precision NULL, errorcode INT NULL, errordescription NVARCHAR(2000) NULL, progresscount BIGINT NULL ) END ELSE BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name = N'stepexecresult' AND id = OBJECT_ID(N'sysdtssteplog'))) BEGIN PRINT '' PRINT 'Altering table sysdtssteplog...' ALTER TABLE sysdtssteplog ADD stepexecresult INT NULL DEFAULT 0 END END /**************************************************************/ /* SYSDTSTASKLOG */ /**************************************************************/ if OBJECT_ID('sysdtstasklog') IS NULL BEGIN PRINT '' PRINT 'Creating table sysdtstasklog...' CREATE TABLE sysdtstasklog ( stepexecutionid BIGINT NOT NULL REFERENCES sysdtssteplog (stepexecutionid) ON DELETE CASCADE, sequenceid INT NOT NULL, errorcode INT NOT NULL, description NVARCHAR(2000) NULL, PRIMARY KEY (stepexecutionid, sequenceid) ) END /**************************************************************/ /* SP_LOG_DTSPACKAGE_BEGIN */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_dtspackage_begin...' GO IF OBJECT_ID(N'sp_log_dtspackage_begin') IS NOT NULL DROP PROCEDURE sp_log_dtspackage_begin GO CREATE PROCEDURE sp_log_dtspackage_begin @name sysname, @description NVARCHAR(1000), @id UNIQUEIDENTIFIER, @versionid UNIQUEIDENTIFIER, @lineagefull UNIQUEIDENTIFIER, @lineageshort INT, @starttime DATETIME, @computer sysname, @operator sysname AS SET NOCOUNT ON INSERT sysdtspackagelog ( name, description, id, versionid, lineagefull, lineageshort, starttime, computer, operator ) VALUES ( @name, @description, @id, @versionid, @lineagefull, @lineageshort, @starttime, @computer, @operator ) RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_log_dtspackage_begin TO PUBLIC GO /**************************************************************/ /* SP_LOG_DTSPACKAGE_END */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_dtspackage_end...' GO IF OBJECT_ID(N'sp_log_dtspackage_end') IS NOT NULL DROP PROCEDURE sp_log_dtspackage_end GO CREATE PROCEDURE sp_log_dtspackage_end @lineagefull UNIQUEIDENTIFIER, @endtime DATETIME, @elapsedtime double precision, @errorcode INT, @errordescription NVARCHAR(2000) AS SET NOCOUNT ON --// Validate lineage. DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * FROM sysdtspackagelog WHERE lineagefull = @lineagefull) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @lineagefull) RAISERROR(14262, 16, 1, '@lineagefull', @stringfromclsid) RETURN(1) -- Failure END UPDATE sysdtspackagelog SET endtime = @endtime, elapsedtime = @elapsedtime, errorcode = @errorcode, errordescription = @errordescription WHERE lineagefull = @lineagefull RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_log_dtspackage_end TO PUBLIC GO /**************************************************************/ /* SP_LOG_DTSSTEP_BEGIN */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_dtsstep_begin...' GO IF OBJECT_ID(N'sp_log_dtsstep_begin') IS NOT NULL DROP PROCEDURE sp_log_dtsstep_begin GO CREATE PROCEDURE sp_log_dtsstep_begin @lineagefull UNIQUEIDENTIFIER, @stepname sysname, @starttime DATETIME AS SET NOCOUNT ON --// Validate lineage. DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * FROM sysdtspackagelog WHERE lineagefull = @lineagefull) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @lineagefull) RAISERROR(14262, 16, 1, '@lineagefull', @stringfromclsid) RETURN(1) -- Failure END INSERT sysdtssteplog ( lineagefull, stepname, starttime ) VALUES ( @lineagefull, @stepname, @starttime ) --// Return the @@identity for sp_log_dtstask and sp_logdtsstep_end SELECT @@IDENTITY RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_log_dtsstep_begin TO PUBLIC GO /**************************************************************/ /* SP_LOG_DTSSTEP_END */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_dtsstep_end...' GO IF OBJECT_ID(N'sp_log_dtsstep_end') IS NOT NULL DROP PROCEDURE sp_log_dtsstep_end GO CREATE PROCEDURE sp_log_dtsstep_end @stepexecutionid BIGINT, @stepexecstatus int, @stepexecresult int, @endtime DATETIME, @elapsedtime double precision, @errorcode INT, @errordescription NVARCHAR(2000), @progresscount BIGINT AS SET NOCOUNT ON --// Validate @stepexecutionid. DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * FROM sysdtssteplog WHERE stepexecutionid = @stepexecutionid) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @stepexecutionid) RAISERROR(14262, 16, 1, '@stepexecutionid', @stringfromclsid) RETURN(1) -- Failure END UPDATE sysdtssteplog SET stepexecstatus = @stepexecstatus, stepexecresult = @stepexecresult, endtime = @endtime, elapsedtime = @elapsedtime, errorcode = @errorcode, errordescription = @errordescription, progresscount = @progresscount WHERE stepexecutionid = @stepexecutionid RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_log_dtsstep_end TO PUBLIC GO /**************************************************************/ /* SP_LOG_DTSTASK */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_dtstask...' GO IF OBJECT_ID(N'sp_log_dtstask') IS NOT NULL DROP PROCEDURE sp_log_dtstask GO CREATE PROCEDURE sp_log_dtstask @stepexecutionid BIGINT, @sequenceid INT, @errorcode INT, @description NVARCHAR(2000) AS SET NOCOUNT ON --// Validate @stepexecutionid. DECLARE @stringfromclsid NVARCHAR(200) IF NOT EXISTS (SELECT * FROM sysdtssteplog WHERE stepexecutionid = @stepexecutionid) BEGIN SELECT @stringfromclsid = CONVERT(NVARCHAR(50), @stepexecutionid) RAISERROR(14262, 16, 1, '@stepexecutionid', @stringfromclsid) RETURN(1) -- Failure END INSERT sysdtstasklog ( stepexecutionid, sequenceid, errorcode, description ) VALUES ( @stepexecutionid, @sequenceid, @errorcode, @description ) RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_log_dtstask TO PUBLIC GO /**************************************************************/ /* SP_ENUM_DTSPACKAGELOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_dtspackagelog...' GO IF OBJECT_ID(N'sp_enum_dtspackagelog') IS NOT NULL DROP PROCEDURE sp_enum_dtspackagelog GO CREATE PROCEDURE sp_enum_dtspackagelog @name sysname, @flags INT = 0, --// Bitmask: 0x01 == return only latest @id UNIQUEIDENTIFIER = NULL, --// If non-NULL, use instead of @name. @versionid UNIQUEIDENTIFIER = NULL, --// If non-NULL, use instead of @id or @name @lineagefull UNIQUEIDENTIFIER = NULL --// If non-NULL, use instead of @versionid or @id or @name AS SET NOCOUNT ON --// This is used for realtime viewing of package logs, so don't error if no entries --// found, simply return an empty result set. SELECT p.name, p.description, p.id, p.versionid, p.lineagefull, p.lineageshort, p.starttime, p.endtime, p.elapsedtime, p.computer, p.operator, p.logdate, p.errorcode, p.errordescription FROM sysdtspackagelog p WHERE ((@lineagefull IS NULL OR p.lineagefull = @lineagefull) AND (@versionid IS NULL OR p.versionid = @versionid) AND (@id IS NULL OR p.id = @id) AND (@name IS NULL OR p.name = @name)) AND ((@flags & 0x01) = 0 OR p.logdate = ( SELECT MAX(logdate) FROM sysdtspackagelog d WHERE (d.id = p.id) ) ) ORDER BY logdate RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_enum_dtspackagelog TO PUBLIC GO /**************************************************************/ /* SP_ENUM_DTSSTEPLOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_dtssteplog...' GO IF OBJECT_ID(N'sp_enum_dtssteplog') IS NOT NULL DROP PROCEDURE sp_enum_dtssteplog GO CREATE PROCEDURE sp_enum_dtssteplog @lineagefull UNIQUEIDENTIFIER = NULL, -- all steps in this package execution @stepexecutionid BIGINT = NULL AS SET NOCOUNT ON --// This is used for realtime viewing of package logs, so don't error if no entries --// found, simply return an empty result set. --// This query must be restricted within a single package execution (lineage); it may --// be further restricted by stepexecutionid to a single step within that package execution. SELECT stepexecutionid, lineagefull, stepname, stepexecstatus, stepexecresult, starttime, endtime, elapsedtime, errorcode, errordescription, progresscount FROM sysdtssteplog WHERE (@lineagefull IS NULL OR lineagefull = @lineagefull) AND (@stepexecutionid IS NULL OR stepexecutionid = @stepexecutionid) ORDER BY stepexecutionid RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_enum_dtssteplog TO PUBLIC GO /**************************************************************/ /* SP_ENUM_DTSTASKLOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_enum_dtstasklog...' GO IF OBJECT_ID(N'sp_enum_dtstasklog') IS NOT NULL DROP PROCEDURE sp_enum_dtstasklog GO CREATE PROCEDURE sp_enum_dtstasklog @stepexecutionid BIGINT, @sequenceid INT = NULL AS SET NOCOUNT ON --// This is used for realtime viewing of package logs, so don't error if no entries --// found, simply return an empty result set. --// This query must be restricted within a single step execution; it may --// be further restricted by stepexecutionid to a single record within that step execution. SELECT -- stepexecutionid, -- this is always passed in so we don't need to return it. sequenceid, errorcode, description FROM sysdtstasklog WHERE (stepexecutionid IS NULL or stepexecutionid = @stepexecutionid) AND (@sequenceid IS NULL OR sequenceid = @sequenceid) ORDER BY sequenceid RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_enum_dtstasklog TO PUBLIC GO /**************************************************************/ /* SP_DUMP_DTSLOG_ALL */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_dump_dtslog_all...' GO IF OBJECT_ID(N'sp_dump_dtslog_all') IS NOT NULL DROP PROCEDURE sp_dump_dtslog_all GO CREATE PROCEDURE sp_dump_dtslog_all AS SET NOCOUNT ON --// sysadmin only. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END DELETE sysdtspackagelog RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_dump_dtslog_all TO PUBLIC GO /**************************************************************/ /* SP_DUMP_DTSPACKAGELOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_dump_dtspackagelog...' GO IF OBJECT_ID(N'sp_dump_dtspackagelog') IS NOT NULL DROP PROCEDURE sp_dump_dtspackagelog GO CREATE PROCEDURE sp_dump_dtspackagelog @name sysname, @flags INT = 0, --// Bitmask: 0x01 == preserve latest @id UNIQUEIDENTIFIER = NULL, --// If non-NULL, use instead of @name. @versionid UNIQUEIDENTIFIER = NULL, --// If non-NULL, use instead of @id or @name @lineagefull UNIQUEIDENTIFIER = NULL --// If non-NULL, use instead of @versionid or @id or @name AS SET NOCOUNT ON --// sysadmin only. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END --// Don't error if no entries found, as the desired result will be met. --// DELETE will CASCADE DELETE sysdtspackagelog FROM sysdtspackagelog p WHERE ((@lineagefull IS NULL OR p.lineagefull = @lineagefull) AND (@versionid IS NULL OR p.versionid = @versionid) AND (@id IS NULL OR p.id = @id) AND (@name IS NULL OR p.name = @name)) AND ((@flags & 0x01) = 0 OR p.logdate < ( SELECT MAX(logdate) FROM sysdtspackagelog d WHERE (d.id = p.id) ) ) RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_dump_dtspackagelog TO PUBLIC GO /**************************************************************/ /* SP_DUMP_DTSSTEPLOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_dump_dtssteplog...' GO IF OBJECT_ID(N'sp_dump_dtssteplog') IS NOT NULL DROP PROCEDURE sp_dump_dtssteplog GO CREATE PROCEDURE sp_dump_dtssteplog @lineagefull UNIQUEIDENTIFIER = NULL, -- all steps in this package execution @stepexecutionid BIGINT = NULL AS SET NOCOUNT ON --// sysadmin only. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END --// Don't error if no entries found, as the desired result will be met. --// DELETE will CASCADE DELETE sysdtssteplog WHERE (@lineagefull IS NULL OR lineagefull = @lineagefull) AND (@stepexecutionid IS NULL OR stepexecutionid = @stepexecutionid) RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_dump_dtssteplog TO PUBLIC GO /**************************************************************/ /* SP_DUMP_DTSTASKLOG */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_dump_dtstasklog...' GO IF OBJECT_ID(N'sp_dump_dtstasklog') IS NOT NULL DROP PROCEDURE sp_dump_dtstasklog GO CREATE PROCEDURE sp_dump_dtstasklog @stepexecutionid BIGINT, @sequenceid INT = NULL AS SET NOCOUNT ON --// sysadmin only. IF (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1) BEGIN RAISERROR(15003, 16, 1, N'sysadmin') RETURN(1) -- Failure END --// Don't error if no entries found, as the desired result will be met. DELETE sysdtstasklog WHERE (stepexecutionid IS NULL or stepexecutionid = @stepexecutionid) AND (@sequenceid IS NULL OR sequenceid = @sequenceid) RETURN 0 -- SUCCESS GO GRANT EXECUTE ON sp_dump_dtstasklog TO PUBLIC 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), 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 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), email_address nvarchar(128) not null, display_name nvarchar(128), replyto_address nvarchar(128), 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 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, 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 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, 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 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), description nvarchar(256), 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 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 ----------------------------------------------------------- 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, 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 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 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 ' + @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 ' + @fkName EXEC (@sql) end END GO /**************************************************************/ /* sysmail_event_log */ /**************************************************************/ 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 [msdb].[dbo].[sysmail_log] sl WHERE (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1) OR (EXISTS ( SELECT mailitem_id FROM [msdb].[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 ----------------------------------------------------------- PRINT 'Creating TABLE sysmail_query_transfer' ----------------------------------------------------------- -- 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 ---------------------------------------------------------------- CREATE TABLE sysmail_query_transfer ( uid uniqueidentifier NOT NULL PRIMARY KEY, text_data NVARCHAR(max) NULL, create_date DATETIME NOT NULL DEFAULT GETDATE() ) GO ----------------------------------------------------------- PRINT 'Creating TABLE sysmail_attachments_transfer' ----------------------------------------------------------- -- 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 ---------------------------------------------------------------- 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() ) 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 superceed any explicit credentials passed in IF((@use_default_credentials = 1) AND (@username IS NOT NULL)) BEGIN RAISERROR(14666, -1, -1) RETURN (1) END --If a password is specified then @username must be a non empty string IF((@password IS NOT NULL) AND (@username IS NULL)) BEGIN RAISERROR(14615, -1, -1) RETURN (1) END RETURN(0) -- SUCCESS go PRINT '' PRINT 'Creating procedure sysmail_verify_principal_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_verify_principal_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_verify_principal_sp go CREATE PROCEDURE dbo.sysmail_verify_principal_sp @principal_id int, @principal_name sysname, @allow_both_nulls bit, @principal_sid varbinary(85) OUTPUT AS IF @allow_both_nulls = 0 BEGIN -- at least one parameter must be supplied IF (@principal_id IS NULL AND @principal_name IS NULL) BEGIN RAISERROR(14604, -1, -1, 'principal') RETURN(1) END END DECLARE @principalid int IF (@principal_id IS NOT NULL AND @principal_name IS NOT NULL) -- both parameters supplied BEGIN SELECT @principalid=principal_id FROM msdb.sys.database_principals WHERE type in ('U','S','G') AND principal_id = @principal_id AND name = @principal_name IF (@principalid IS NULL) BEGIN RAISERROR(14605, -1, -1, 'principal') RETURN(2) END END ELSE IF (@principal_id IS NOT NULL) -- use id BEGIN SELECT @principalid=principal_id FROM msdb.sys.database_principals WHERE type in ('U','S','G') AND principal_id = @principal_id IF (@principalid IS NULL) BEGIN RAISERROR(14606, -1, -1, 'principal') RETURN(3) END END ELSE IF (@principal_name IS NOT NULL) -- use name BEGIN SELECT @principalid=principal_id FROM msdb.sys.database_principals WHERE type in ('U','S','G') AND name = @principal_name IF (@principalid IS NULL) BEGIN RAISERROR(14607, -1, -1, 'principal') RETURN(4) END END -- populate return variable SELECT @principal_sid = dbo.get_principal_sid(@principalid) RETURN(0) -- SUCCESS go PRINT '' PRINT 'Creating procedure sysmail_verify_profile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_verify_profile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_verify_profile_sp go CREATE PROCEDURE dbo.sysmail_verify_profile_sp @profile_id int, @profile_name sysname, @allow_both_nulls bit, @allow_id_name_mismatch bit, @profileid int OUTPUT AS IF @allow_both_nulls = 0 BEGIN -- at least one parameter must be supplied IF (@profile_id IS NULL AND @profile_name IS NULL) BEGIN RAISERROR(14604, -1, -1, 'profile') RETURN(1) END END IF ((@allow_id_name_mismatch = 0) AND (@profile_id IS NOT NULL AND @profile_name IS NOT NULL)) -- use both parameters BEGIN SELECT @profileid = profile_id FROM msdb.dbo.sysmail_profile WHERE profile_id=@profile_id AND name=@profile_name IF (@profileid IS NULL) -- id and name do not match BEGIN RAISERROR(14605, -1, -1, 'profile') RETURN(2) END END ELSE IF (@profile_id IS NOT NULL) -- use id BEGIN SELECT @profileid = profile_id FROM msdb.dbo.sysmail_profile WHERE profile_id=@profile_id IF (@profileid IS NULL) -- id is invalid BEGIN RAISERROR(14606, -1, -1, 'profile') RETURN(3) END END ELSE IF (@profile_name IS NOT NULL) -- use name BEGIN SELECT @profileid = profile_id FROM msdb.dbo.sysmail_profile WHERE name=@profile_name IF (@profileid IS NULL) -- name is invalid BEGIN RAISERROR(14607, -1, -1, 'profile') RETURN(4) END END RETURN(0) -- SUCCESS go PRINT '' PRINT 'Creating procedure sysmail_verify_account_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_verify_account_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_verify_account_sp go CREATE PROCEDURE dbo.sysmail_verify_account_sp @account_id int, @account_name sysname, @allow_both_nulls bit, @allow_id_name_mismatch bit, @accountid int OUTPUT AS IF @allow_both_nulls = 0 BEGIN -- at least one parameter must be supplied IF (@account_id IS NULL AND @account_name IS NULL) BEGIN RAISERROR(14604, -1, -1, 'account') RETURN(1) END END IF ((@allow_id_name_mismatch = 0) AND (@account_id IS NOT NULL AND @account_name IS NOT NULL)) -- use both parameters BEGIN SELECT @accountid = account_id FROM msdb.dbo.sysmail_account WHERE account_id=@account_id AND name=@account_name IF (@accountid IS NULL) -- id and name do not match BEGIN RAISERROR(14605, -1, -1, 'account') RETURN(2) END END ELSE IF (@account_id IS NOT NULL) -- use id BEGIN SELECT @accountid = account_id FROM msdb.dbo.sysmail_account WHERE account_id=@account_id IF (@accountid IS NULL) -- id is invalid BEGIN RAISERROR(14606, -1, -1, 'account') RETURN(3) END END ELSE IF (@account_name IS NOT NULL) -- use name BEGIN SELECT @accountid = account_id FROM msdb.dbo.sysmail_account WHERE name=@account_name IF (@accountid IS NULL) -- name is invalid BEGIN RAISERROR(14607, -1, -1, 'account') RETURN(4) END END RETURN(0) -- SUCCESS go PRINT '' PRINT 'Creating procedure sysmail_add_profile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_add_profile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_add_profile_sp go CREATE PROCEDURE dbo.sysmail_add_profile_sp @profile_name sysname, @description nvarchar(256) = NULL, @profile_id int = NULL OUTPUT AS SET NOCOUNT ON -- insert new profile record, rely on primary key constraint to error out INSERT INTO msdb.dbo.sysmail_profile (name,description) VALUES (@profile_name, @description) -- fetch back profile_id SELECT @profile_id = profile_id FROM msdb.dbo.sysmail_profile WHERE name = @profile_name RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_update_profile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_update_profile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_update_profile_sp go CREATE PROCEDURE dbo.sysmail_update_profile_sp @profile_id int = NULL, -- must provide either id or name @profile_name sysname = NULL, @description nvarchar(256) = NULL AS SET NOCOUNT ON DECLARE @rc int DECLARE @profileid int exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 0, 1, @profileid OUTPUT IF @rc <> 0 RETURN(1) IF (@profile_name IS NOT NULL AND @description IS NOT NULL) UPDATE msdb.dbo.sysmail_profile SET name=@profile_name, description = @description WHERE profile_id = @profileid ELSE IF (@profile_name IS NOT NULL) UPDATE msdb.dbo.sysmail_profile SET name=@profile_name WHERE profile_id = @profileid ELSE IF (@description IS NOT NULL) UPDATE msdb.dbo.sysmail_profile SET description = @description WHERE profile_id = @profileid ELSE BEGIN RAISERROR(14610, -1, -1) RETURN(1) END RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_delete_profile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_delete_profile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_delete_profile_sp go CREATE PROCEDURE dbo.sysmail_delete_profile_sp @profile_id int = NULL, -- must provide either id or name @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, 0, 0, @profileid OUTPUT IF @rc <> 0 RETURN(1) DELETE FROM msdb.dbo.sysmail_profile WHERE profile_id = @profileid RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_help_profile_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_help_profile_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_help_profile_sp go CREATE PROCEDURE dbo.sysmail_help_profile_sp @profile_id int = NULL, @profile_name sysname = NULL AS SET NOCOUNT ON DECLARE @rc int DECLARE @profileid int exec @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id, @profile_name, 1, 0, @profileid OUTPUT IF @rc <> 0 RETURN(1) IF (@profileid IS NOT NULL) SELECT profile_id, name, description FROM msdb.dbo.sysmail_profile WHERE profile_id = @profileid ELSE -- don't filter the output SELECT profile_id, name, description FROM msdb.dbo.sysmail_profile RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_create_user_credential_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_create_user_credential_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_create_user_credential_sp go CREATE PROCEDURE dbo.sysmail_create_user_credential_sp @username nvarchar(128), @password nvarchar(128), @credential_id int OUTPUT AS SET NOCOUNT ON DECLARE @rc int DECLARE @credential_name UNIQUEIDENTIFIER DECLARE @credential_name_as_str varchar(40) DECLARE @sql NVARCHAR(max) -- create a GUID as the name for the credential SET @credential_name = newid() SET @credential_name_as_str = convert(varchar(40), @credential_name) SET @sql = N'CREATE CREDENTIAL [' + @credential_name_as_str + N'] WITH IDENTITY = ' + QUOTENAME(@username, '''') + N', SECRET = ' + QUOTENAME(ISNULL(@password, N''), '''') EXEC @rc = sp_executesql @statement = @sql IF(@rc <> 0) RETURN @rc SELECT @credential_id = credential_id FROM sys.credentials WHERE name = convert(sysname, @credential_name) IF(@credential_id IS NULL) BEGIN RAISERROR(14616, -1, -1, @credential_name_as_str) RETURN 1 END RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_alter_user_credential_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_alter_user_credential_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_alter_user_credential_sp go CREATE PROCEDURE dbo.sysmail_alter_user_credential_sp @credential_name sysname, @username nvarchar(128), @password nvarchar(128) AS SET NOCOUNT ON DECLARE @rc int DECLARE @sql NVARCHAR(max) -- alter credential DDL SET @sql = N'ALTER CREDENTIAL ' + QUOTENAME(@credential_name) + N' WITH IDENTITY = ' + QUOTENAME(@username, '''') + N', SECRET = ' + QUOTENAME(ISNULL(@password, N''), '''') EXEC @rc = sp_executesql @statement = @sql IF(@rc <> 0) RETURN @rc RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_drop_user_credential_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_drop_user_credential_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_drop_user_credential_sp go CREATE PROCEDURE dbo.sysmail_drop_user_credential_sp @credential_name sysname AS SET NOCOUNT ON DECLARE @rc int DECLARE @sql NVARCHAR(max) -- Drop credential DDL SET @sql = N'DROP CREDENTIAL ' + QUOTENAME(@credential_name) EXEC @rc = sp_executesql @statement = @sql IF(@rc <> 0) RETURN @rc RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_add_account_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_add_account_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_add_account_sp go CREATE PROCEDURE dbo.sysmail_add_account_sp @account_name sysname, @email_address nvarchar(128), @display_name nvarchar(128) = NULL, @replyto_address nvarchar(128) = NULL, @description nvarchar(256) = NULL, @mailserver_name sysname = NULL, -- the following fields are part of server definition @mailserver_type sysname = N'SMTP', @port int = 25, @username nvarchar(128) = NULL, @password nvarchar(128) = NULL, @use_default_credentials bit = 0, @enable_ssl bit = 0, @account_id int = NULL OUTPUT AS SET NOCOUNT ON DECLARE @rc int DECLARE @credential_id int EXEC @rc = msdb.dbo.sysmail_verify_accountparams_sp @use_default_credentials = @use_default_credentials, @mailserver_type = @mailserver_type OUTPUT, -- validates and returns trimmed value @username = @username OUTPUT, -- returns trimmed value, NULL if empty @password = @password OUTPUT -- returns trimmed value, NULL if empty IF(@rc <> 0) RETURN (1) --transact this in case sysmail_create_user_credential_sp fails BEGIN TRANSACTION -- insert new account record, rely on primary key constraint to error out INSERT INTO msdb.dbo.sysmail_account (name,description,email_address,display_name,replyto_address) VALUES (@account_name,@description,@email_address,@display_name,@replyto_address) IF (@@ERROR <> 0) BEGIN ROLLBACK TRANSACTION RETURN (2) END -- fetch back account_id SELECT @account_id = account_id FROM msdb.dbo.sysmail_account WHERE name = @account_name IF (@mailserver_name IS NULL) -- use local server as default SELECT @mailserver_name=@@SERVERNAME --create a credential in the credential store if a password needs to be stored IF(@username IS NOT NULL) BEGIN EXEC @rc = msdb.dbo.sysmail_create_user_credential_sp @username, @password, @credential_id OUTPUT IF(@rc <> 0) BEGIN ROLLBACK TRANSACTION RETURN (3) END END INSERT INTO msdb.dbo.sysmail_server (account_id,servertype,servername,port,username,credential_id,use_default_credentials,enable_ssl) VALUES (@account_id,@mailserver_type,@mailserver_name,@port,@username,@credential_id,@use_default_credentials,@enable_ssl) IF (@@ERROR <> 0) BEGIN ROLLBACK TRANSACTION RETURN (4) END COMMIT TRANSACTION RETURN(0) go PRINT '' PRINT 'Creating procedure sysmail_update_account_sp...' IF (NOT OBJECT_ID(N'dbo.sysmail_update_account_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_update_account_sp go 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), @replyto_address nvarchar(128), @description nvarchar(256), @mailserver_name sysname, @mailserver_type sysname, @port int, @username sysname, @password sysname, @use_default_credentials bit, @enable_ssl bit -- 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 exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 0, 1, @accountid OUTPUT IF @rc <> 0 RETURN(1) 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) 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 (@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 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 WHERE account_id=@accountid AND servertype=@mailserver_type 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 databasemail90.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 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 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' 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' ----- GO -- Processes responses from dbmail -- CREATE PROCEDURE sp_ProcessResponse @conv_handle uniqueidentifier, @message_type_name NVARCHAR(256), @xml_message_body VARCHAR(max) AS BEGIN DECLARE @idoc INT, @mailitem_id INT, @sent_status INT, @rc INT, @index INT, @processId INT, @sent_date DATETIME, @localmessage NVARCHAR(max), @LogMessage NVARCHAR(max), @retry_hconv uniqueidentifier, @paramStr NVARCHAR(256), @accRetryDelay INT -------------------------- --Always send the response ;SEND ON CONVERSATION @conv_handle MESSAGE TYPE @message_type_name (@xml_message_body) -- -- Need to handle the case where a sent retry is requested. -- This is done by setting a conversation timer, The timer with go off in the external queue -- Get the handle to the xml document EXEC @rc = sp_xml_preparedocument @idoc OUTPUT, @xml_message_body, N'' IF(@rc <> 0) BEGIN --Log the error. The response has already sent to the Internal queue. -- This will update the mail with the latest staus SET @localmessage = FORMATMESSAGE(14655, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage GOTO ErrorHandler; END -- Execute a SELECT statement that uses the OPENXML rowset provider to get the MailItemId and sent status. SELECT @mailitem_id = MailItemId, @sent_status = SentStatus FROM OPENXML (@idoc, '/responses:SendMail', 1) WITH (MailItemId INT './MailItemId/@Id', SentStatus INT './SentStatus/@Status') --Close the handle to the xml document EXEC sp_xml_removedocument @idoc IF(@mailitem_id IS NULL OR @sent_status IS NULL) BEGIN --Log error and continue. SET @localmessage = FORMATMESSAGE(14652, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage GOTO ErrorHandler; END -- -- A send retry has been requested. Set a conversation timer IF(@sent_status = 3) BEGIN -- Get the associated mail item data for the given @conversation_handle (if it exists) SELECT @retry_hconv = conversation_handle FROM sysmail_send_retries as sr RIGHT JOIN sysmail_mailitems as mi ON sr.mailitem_id = mi.mailitem_id WHERE mi.mailitem_id = @mailitem_id --Must be the first retry attempt. Create a sysmail_send_retries record to track retries IF(@retry_hconv IS NULL) BEGIN INSERT sysmail_send_retries(conversation_handle, mailitem_id) --last_send_attempt_date VALUES(@conv_handle, @mailitem_id) END ELSE BEGIN --Update existing retry record UPDATE sysmail_send_retries SET last_send_attempt_date = GETDATE(), send_attempts = send_attempts + 1 WHERE mailitem_id = @mailitem_id END --Get the global retry delay time EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'AccountRetryDelay', @parameter_value = @paramStr OUTPUT --ConvertToInt will return the default if @paramStr is null SET @accRetryDelay = dbo.ConvertToInt(@paramStr, 0x7fffffff, 300) -- 5 min default --Now set the dialog timer. This triggers the send retry ;BEGIN CONVERSATION TIMER (@conv_handle) TIMEOUT = @accRetryDelay END ELSE BEGIN --Only end theconversation if a retry isn't being attempted END CONVERSATION @conv_handle END -- All done OK goto ExitProc; ----------------- -- Error Handler ----------------- ErrorHandler: ------------------ -- Exit Procedure ------------------ ExitProc: RETURN (@rc); END GO ----------------------------------------------------------- -- procedure sp_MailItemResultSets ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_MailItemResultSets', 'P') IS NULL DROP PROCEDURE dbo.sp_MailItemResultSets GO ----- PRINT 'Creating sp_MailItemResultSets' ----- GO -- sp_MailItemResultSets : -- Sends back multiple rowsets with the mail items data CREATE PROCEDURE sp_MailItemResultSets @mailitem_id INT, @profile_id INT, @conversation_handle uniqueidentifier, @service_contract_name NVARCHAR(256), @message_type_name NVARCHAR(256) AS BEGIN SET NOCOUNT ON -- -- Send back multiple rowsets with the mail items data ---- -- 1) MessageTypeName SELECT @message_type_name as 'message_type_name', @service_contract_name as 'service_contract_name', @conversation_handle as 'conversation_handle', @mailitem_id as 'mailitem_id' ----- -- 2) The mail item record from sysmail_mailitems. SELECT mi.mailitem_id, mi.profile_id, (SELECT name FROM msdb.dbo.sysmail_profile p WHERE p.profile_id = mi.profile_id) as 'profile_name', mi.recipients, mi.copy_recipients, mi.blind_copy_recipients, mi.subject, mi.body, mi.body_format, mi.importance, mi.sensitivity, ISNULL(sr.send_attempts, 0) as retry_attempt FROM sysmail_mailitems as mi LEFT JOIN sysmail_send_retries as sr ON sr.mailitem_id = mi.mailitem_id WHERE mi.mailitem_id = @mailitem_id ----- -- 3) Account information SELECT a.account_id, a.name FROM msdb.dbo.sysmail_profileaccount as pa JOIN msdb.dbo.sysmail_account as a ON pa.account_id = a.account_id WHERE pa.profile_id = @profile_id ORDER BY pa.sequence_number ----- -- 4) Attachments if any SELECT attachment_id, mailitem_id, filename, filesize, attachment FROM sysmail_attachments WHERE mailitem_id = @mailitem_id RETURN 0 END GO ----------------------------------------------------------- -- procedure sp_process_DialogTimer ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_process_DialogTimer', 'P') IS NULL DROP PROCEDURE dbo.sp_process_DialogTimer GO ----- PRINT 'Creating sp_process_DialogTimer' ----- GO -- Processes a DialogTimer message from the the queue. This is used for send mail retries. -- Returns the mail to be send if a retry is required or logs a failure if max retry count has been reached CREATE PROCEDURE sp_process_DialogTimer @conversation_handle uniqueidentifier, @service_contract_name NVARCHAR(256), @message_type_name NVARCHAR(256) AS BEGIN SET NOCOUNT ON -- Declare all variables DECLARE @mailitem_id INT, @profile_id INT, @send_attempts INT, @mail_request_date DATETIME, @localmessage NVARCHAR(255), @paramStr NVARCHAR(256), @accRetryAttempts INT -- Get the associated mail item data for the given @conversation_handle SELECT @mailitem_id = mi.mailitem_id, @profile_id = mi.profile_id, @mail_request_date = mi.send_request_date, @send_attempts = sr.send_attempts FROM sysmail_send_retries as sr JOIN sysmail_mailitems as mi ON sr.mailitem_id = mi.mailitem_id WHERE sr.conversation_handle = @conversation_handle -- If not able to find a mailitem_id return and move to the next message. -- This could happen if the mail items table was cleared before the retry was fired IF(@mailitem_id IS NULL) BEGIN --Log warning and continue -- "mailitem_id on conversation %s was not found in the sysmail_send_retries table. This mail item will not be sent." SET @localmessage = FORMATMESSAGE(14662, convert(NVARCHAR(50), @conversation_handle)) exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@localmessage -- Resource clean-up IF(@conversation_handle IS NOT NULL) BEGIN END CONVERSATION @conversation_handle; END -- return code has special meaning and will be propagated to the calling process RETURN 2; END --Get the retry attempt count from sysmailconfig. EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'AccountRetryAttempts', @parameter_value = @paramStr OUTPUT --ConvertToInt will return the default if @paramStr is null SET @accRetryAttempts = dbo.ConvertToInt(@paramStr, 0x7fffffff, 1) --Check the send attempts and log and error if send_attempts >= retry count. --This shouldn't happen unless the retry configuration was changed IF(@send_attempts > @accRetryAttempts) BEGIN --Log warning and continue -- "Mail Id %d has exceeded the retry count. This mail item will not be sent." SET @localmessage = FORMATMESSAGE(14663, @mailitem_id) exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@localmessage, @mailitem_id=@mailitem_id -- Resource clean-up IF(@conversation_handle IS NOT NULL) BEGIN END CONVERSATION @conversation_handle; END -- return code has special meaning and will be propagated to the calling process RETURN 3; END -- This returns the mail item to the client as multiple result sets EXEC sp_MailItemResultSets @mailitem_id = @mailitem_id, @profile_id = @profile_id, @conversation_handle = @conversation_handle, @service_contract_name = @service_contract_name, @message_type_name = @message_type_name RETURN 0 END GO ----------------------------------------------------------- -- procedure sp_readrequest ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_readrequest', 'P') IS NULL DROP PROCEDURE dbo.sp_readrequest GO ----- PRINT 'Creating sp_readrequest' ----- GO -- sp_readrequest : Reads a request from the the queue and returns its -- contents. CREATE PROCEDURE sp_readrequest @receive_timeout INT -- the max time this read will wait for new message AS BEGIN SET NOCOUNT ON -- Table to store message information. DECLARE @msgs TABLE ( [conversation_handle] uniqueidentifier, [service_contract_name] nvarchar(256), [message_type_name] nvarchar(256), [message_body] varbinary(max) ) -- Declare variables to store row values fetched from the cursor DECLARE @exit INT, @idoc INT, @mailitem_id INT, @profile_id INT, @conversation_handle uniqueidentifier, @service_contract_name NVARCHAR(256), @message_type_name NVARCHAR(256), @xml_message_body VARCHAR(max), @timediff INT, @rec_timeout INT, @start_time DATETIME, @localmessage NVARCHAR(256), @rc INT --Init variables SELECT @start_time = GETDATE(), @timediff = 0, @exit = 0, @rc = 0 WHILE (@timediff <= @receive_timeout) BEGIN -- Delete all messages from @msgs table DELETE FROM @msgs -- Pick all message from queue SET @rec_timeout = @receive_timeout - @timediff WAITFOR(RECEIVE conversation_handle, service_contract_name, message_type_name, message_body FROM ExternalMailQueue INTO @msgs), TIMEOUT @rec_timeout -- Check if there was some error in reading from queue SET @rc = @@ERROR IF (@rc <> 0) BEGIN IF(@rc < 4) -- make sure return code is not in reserved range (1-3) SET @rc = 4 --Note: we will get error no. 9617 if the service queue 'ExternalMailQueue' is currently disabled. BREAK END --If there is no message in the queue return 1 to indicate a timeout IF NOT EXISTS(SELECT * FROM @msgs) BEGIN SET @rc = 1 BREAK END -- Create a cursor to iterate through the messages. DECLARE msgs_cursor CURSOR FOR SELECT conversation_handle, service_contract_name, message_type_name, CONVERT(VARCHAR(MAX), message_body) FROM @msgs; -- Open the cursor OPEN msgs_cursor; -- Perform the first fetch and store the values in the variables. FETCH NEXT FROM msgs_cursor INTO @conversation_handle, @service_contract_name, @message_type_name, @xml_message_body -- Check @@FETCH_STATUS to see if there are any more rows to fetch. WHILE (@@FETCH_STATUS = 0) BEGIN -- Check if the message is a send mail message IF(@message_type_name = N'{//www.microsoft.com/databasemail/messages}SendMail') BEGIN -- Get the handle to the xml document EXEC @rc = sp_xml_preparedocument @idoc OUTPUT, @xml_message_body, N'' IF(@rc <> 0) BEGIN IF(@rc < 4) -- make sure return code is not in reserved rang (1-3) SET @rc = 4 END ELSE -- parse the document and process its contents BEGIN -- Execute a SELECT statement that uses the OPENXML rowset provider to get the MailItemId. SELECT @mailitem_id = MailItemId FROM OPENXML (@idoc, '/requests:SendMail', 1) WITH (MailItemId INT './MailItemId') --Close the handle to the xml document EXEC sp_xml_removedocument @idoc -- get account information SELECT @profile_id = profile_id FROM sysmail_mailitems WHERE mailitem_id = @mailitem_id IF(@profile_id IS NULL) -- mail item has been deleted from the database BEGIN -- log warning SET @localmessage = FORMATMESSAGE(14667, convert(NVARCHAR(50), @mailitem_id)) exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@localmessage -- Resource clean-up IF(@conversation_handle IS NOT NULL) END CONVERSATION @conversation_handle; -- return code has special meaning and will be propagated to the calling process SET @rc = 2 END ELSE BEGIN -- This returns the mail item to the client as multiple result sets EXEC sp_MailItemResultSets @mailitem_id = @mailitem_id, @profile_id = @profile_id, @conversation_handle = @conversation_handle, @service_contract_name = @service_contract_name, @message_type_name = @message_type_name SET @exit = 1 -- make sure we exit outer loop END END -- always break from the loop upon processing SendMail message BREAK END -- Check if the message is a dialog timer. This is used for account retries ELSE IF(@message_type_name = N'http://schemas.microsoft.com/SQL/ServiceBroker/DialogTimer') BEGIN -- Handle the retry case. - DialogTimer is used for send mail reties EXEC @rc = sp_process_DialogTimer @conversation_handle = @conversation_handle, @service_contract_name = @service_contract_name, @message_type_name = N'{//www.microsoft.com/databasemail/messages}SendMail' -- Always break from the loop upon processing DialogTimer message -- In case of failure return code from procedure call will simply be propagated to the calling process SET @exit = 1 -- make sure we exit outer loop BREAK END -- Error case ELSE IF (@message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/Error') -- Error in the conversation, hence ignore all the messages of this conversation. BREAK -- This is executed as long as fetch succeeds. FETCH NEXT FROM msgs_cursor INTO @conversation_handle, @service_contract_name, @message_type_name, @xml_message_body END CLOSE msgs_cursor; DEALLOCATE msgs_cursor; -- Check if we read any request or only SSB generated messages -- If a valid request is read with or without an error break out of loop IF (@exit = 1 OR @rc <> 0) BREAK --Keep track of how long this sp has been running select @timediff = DATEDIFF(ms, @start_time, getdate()) END -- propagate return code to the calling process RETURN @rc END GO ----------------------------------------------------------- -- procedure sp_GetAttachmentData ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_GetAttachmentData', 'P') IS NULL DROP PROCEDURE dbo.sp_GetAttachmentData GO ----- PRINT 'Creating sp_GetAttachmentData' ----- GO CREATE PROCEDURE sp_GetAttachmentData @attachments nvarchar(max), @temp_table_uid uniqueidentifier, @exclude_query_output BIT = 0 AS BEGIN SET NOCOUNT ON SET QUOTED_IDENTIFIER ON DECLARE @rc INT, @prohibitedExts NVARCHAR(1000), @attachFilePath NVARCHAR(260), @scIndex INT, @startLocation INT, @fileSizeStr NVARCHAR(256), @fileSize INT, @mailDbName sysname, @uidStr VARCHAR(36) --Get the maximum file size allowed for attachments from sysmailconfig. EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'MaxFileSize', @parameter_value = @fileSizeStr OUTPUT --ConvertToInt will return the default if @fileSizeStr is null SET @fileSize = dbo.ConvertToInt(@fileSizeStr, 0x7fffffff, 100000) --May need this if attaching files EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'ProhibitedExtensions', @parameter_value = @prohibitedExts OUTPUT SET @mailDbName = DB_NAME() SET @uidStr = CONVERT(VARCHAR(36), @temp_table_uid) SET @attachments = @attachments + ';' SET @startLocation = 0 SET @scIndex = CHARINDEX(';', @attachments, @startLocation) WHILE (@scIndex <> 0) BEGIN SET @attachFilePath = SUBSTRING(@attachments, @startLocation, (@scIndex - @startLocation)) -- Make sure we have an attachment file name to work with, and that it hasn't been truncated IF (@scIndex - @startLocation > 260 ) BEGIN RAISERROR(14628, 16, 1) RETURN 1 END IF ((@attachFilePath IS NULL) OR (LEN(@attachFilePath) = 0)) BEGIN RAISERROR(14628, 16, 1) RETURN 1 END --Check if attachment ext is allowed EXEC @rc = sp_isprohibited @attachFilePath, @prohibitedExts IF (@rc <> 0) BEGIN RAISERROR(14630, 16, 1, @attachFilePath, @prohibitedExts) RETURN 2 END DECLARE @no_output_int INT SET @no_output_int = CONVERT(int, @exclude_query_output) -- return code checked after select and delete calls EXEC @rc = master..xp_sysmail_attachment_load @message = @mailDbName, @attachments = @attachFilePath, @subject = @uidStr, @max_attachment_size = @fileSize, @no_output = @no_output_int IF (@rc <> 0) RETURN (@rc) --Get next substring index SET @startLocation = @scIndex + 1 SET @scIndex = CHARINDEX(';', @attachments, @startLocation) IF (@scIndex = 0) BREAK END RETURN 0 END GO ----------------------------------------------------------- -- procedure sp_RunMailQuery ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_RunMailQuery', 'P') IS NULL DROP PROCEDURE dbo.sp_RunMailQuery GO ----- PRINT 'Creating sp_RunMailQuery' ----- GO CREATE PROCEDURE 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 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, @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) 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 RETURN @rc 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' ----- GO -- sp_sendemail : Sends a mail from Yukon outbox. -- CREATE PROCEDURE 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, @mailitem_id INT = NULL 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 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 -- Initialize SELECT @rc = 0, @QueryResultsExist = 0, @AttachmentsExist = 0, @temp_table_uid = NEWID(), @procName = OBJECT_NAME(@@PROCID), @tranStartedBool = 0, @trancountSave = @@TRANCOUNT 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 RAISERROR(14636, 16, 1) RETURN 1 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 RAISERROR(14607, -1, -1, 'profile') RETURN 1 END END END --Attach results must be specified IF @attach_query_result_as_file IS NULL BEGIN RAISERROR(14618, 16, 1, 'attach_query_result_as_file') RETURN 2 END --No output must be specified IF @exclude_query_output IS NULL BEGIN RAISERROR(14618, 16, 1, 'exclude_query_output') RETURN 3 END --No header must be specified IF @query_result_header IS NULL BEGIN RAISERROR(14618, 16, 1, 'query_result_header') RETURN 4 END -- Check if query_result_separator is specifed IF @query_result_separator IS NULL OR DATALENGTH(@query_result_separator) = 0 BEGIN RAISERROR(14618, 16, 1, 'query_result_separator') RETURN 5 END --Echo error must be specified IF @append_query_error IS NULL BEGIN RAISERROR(14618, 16, 1, 'append_query_error') RETURN 6 END --@body_format can be TEXT (default) or HTML IF (@body_format IS NULL) BEGIN SET @body_format = 'TEXT' END ELSE BEGIN SET @body_format = UPPER(@body_format) IF @body_format NOT IN ('TEXT', 'HTML') BEGIN RAISERROR(14626, 16, 1, @body_format) RETURN 13 END END --Importance must be specified IF @importance IS NULL BEGIN RAISERROR(14618, 16, 1, 'importance') RETURN 15 END SET @importance = UPPER(@importance) --Importance must be one of the predefined values IF @importance NOT IN ('LOW', 'NORMAL', 'HIGH') BEGIN RAISERROR(14622, 16, 1, @importance) RETURN 16 END --Sensitivity must be specified IF @sensitivity IS NULL BEGIN RAISERROR(14618, 16, 1, 'sensitivity') RETURN 17 END SET @sensitivity = UPPER(@sensitivity) --Sensitivity must be one of predefined values IF @sensitivity NOT IN ('NORMAL', 'PERSONAL', 'PRIVATE', 'CONFIDENTIAL') BEGIN RAISERROR(14623, 16, 1, @sensitivity) RETURN 18 END --Message body cannot be null. Atleast one of message, subject, query, --attachments must be specified. IF( (@body IS NULL AND @query IS NULL AND @file_attachments IS NULL AND @subject IS NULL) OR ( (LEN(@body) IS NULL OR LEN(@body) <= 0) AND (LEN(@query) IS NULL OR LEN(@query) <= 0) AND (LEN(@file_attachments) IS NULL OR LEN(@file_attachments) <= 0) AND (LEN(@subject) IS NULL OR LEN(@subject) <= 0) ) ) BEGIN RAISERROR(14624, 16, 1, '@body, @query, @file_attachments, @subject') RETURN 19 END ELSE IF @subject IS NULL OR LEN(@subject) <= 0 SET @subject='SQL Server Message' --Recipients cannot be empty. Atleast one of the To, Cc, Bcc must be specified IF ( (@recipients IS NULL AND @copy_recipients IS NULL AND @blind_copy_recipients IS NULL ) OR ( (LEN(@recipients) IS NULL OR LEN(@recipients) <= 0) AND (LEN(@copy_recipients) IS NULL OR LEN(@copy_recipients) <= 0) AND (LEN(@blind_copy_recipients) IS NULL OR LEN(@blind_copy_recipients) <= 0) ) ) BEGIN RAISERROR(14624, 16, 1, '@recipients, @copy_recipients, @blind_copy_recipients') RETURN 20 END --If query is not specified, attach results and no header cannot be true. IF ( (@query IS NULL OR LEN(@query) <= 0) AND @attach_query_result_as_file = 1) BEGIN RAISERROR(14625, 16, 1) RETURN 21 END -- -- Execute Query if query is specified IF ((@query IS NOT NULL) AND (LEN(@query) > 0)) BEGIN EXECUTE AS CALLER EXEC @rc = sp_RunMailQuery @query = @query, @attach_results = @attach_query_result_as_file, @query_attachment_filename = @query_attachment_filename, @no_output = @exclude_query_output, @query_result_header = @query_result_header, @separator = @query_result_separator, @echo_error = @append_query_error, @dbuse = @execute_query_database, @width = @query_result_width, @temp_table_uid = @temp_table_uid, @query_no_truncate = @query_no_truncate -- 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 ) 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 ) SELECT @rc = @@ERROR, @mailitem_id = @@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 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) 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' ----- GO -- Processes messages from the external mail queue -- CREATE PROCEDURE sp_ExternalMailQueueListener AS BEGIN DECLARE @idoc INT, @mailitem_id INT, @sent_status INT, @sent_account_id INT, @rc INT, @processId INT, @sent_date DATETIME, @localmessage NVARCHAR(max), @conv_handle uniqueidentifier, @message_type_name NVARCHAR(256), @xml_message_body VARCHAR(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 exernal 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 VARCHAR(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: -- -- -- -- -- -- -- -- -- -- -- -- -- Get the handle to the xml document EXEC @rc = sp_xml_preparedocument @idoc OUTPUT, @xml_message_body, N'' IF(@rc <> 0) BEGIN --Log error and continue. Don't want to block the following messages on the queue SET @localmessage = FORMATMESSAGE(14655, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage GOTO ErrorHandler; END -- Execute a SELECT statement that uses the OPENXML rowset provider to get the MailItemId and sent status. SELECT @mailitem_id = MailItemId, @sent_status = SentStatus, @sent_account_id = SentAccountId, @sent_date = SentDate, @processId = CallingProcess, @LogMessage = LogMessage FROM OPENXML (@idoc, '/responses:SendMail', 1) WITH (MailItemId INT './MailItemId/@Id', SentStatus INT './SentStatus/@Status', SentAccountId INT './SentAccountId/@Id', SentDate DATETIME './SentDate/@Date', --The date was formated using ISO8601 CallingProcess INT './CallingProcess/@Id', LogMessage NVARCHAR(max) './Information/Failure/@Message') --Close the handle to the xml document EXEC sp_xml_removedocument @idoc IF(@mailitem_id IS NULL) BEGIN --Log error and continue. Don't want to block the following messages on the queue by rolling back the tran SET @localmessage = FORMATMESSAGE(14652, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage END ELSE BEGIN -- check sent_status is valid : 0(PendingSend), 1(SendSuccessful), 2(SendFailed), 3(AttemptingSendRetry) IF(@sent_status NOT IN (1, 2, 3)) BEGIN SET @localmessage = FORMATMESSAGE(14653, N'SentStatus', CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@localmessage --Set value to SendFailed SET @sent_status = 2 END --Make the @sent_account_id NULL if it is 0. IF(@sent_account_id IS NOT NULL AND @sent_account_id = 0) SET @sent_account_id = NULL -- -- Update the mail status if not a retry. Nothing else needs to be done in this case UPDATE sysmail_mailitems SET sent_status = CAST (@sent_status as TINYINT), sent_account_id = @sent_account_id, sent_date = @sent_date WHERE mailitem_id = @mailitem_id -- Report a failure if no record is found in the sysmail_mailitems table IF (@@ROWCOUNT = 0) BEGIN SET @localmessage = FORMATMESSAGE(14653, N'MailItemId', CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage END IF (@LogMessage IS NOT NULL) BEGIN exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@LogMessage, @process_id=@processId, @mailitem_id=@mailitem_id, @account_id=@sent_account_id END END END ------------------------------------------------------- --Process all other messages by logging to sysmail_log SET @conv_handle = NULL; --Always end the conversion if this message is received SELECT @conv_handle = conversation_handle FROM @msgs WHERE [message_type_name] = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog' IF(@conv_handle IS NOT NULL) BEGIN END CONVERSATION @conv_handle; END DECLARE @queuemessage nvarchar(max) DECLARE queue_messages_cursor CURSOR LOCAL FOR SELECT FORMATMESSAGE(14654, CONVERT(NVARCHAR(50), conversation_handle), message_type_name, message_body) FROM @msgs WHERE [message_type_name] NOT IN (N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog', N'{//www.microsoft.com/databasemail/messages}SendMailStatus') OPEN queue_messages_cursor FETCH NEXT FROM queue_messages_cursor INTO @queuemessage WHILE (@@fetch_status = 0) BEGIN exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@queuemessage FETCH NEXT FROM queue_messages_cursor INTO @queuemessage END CLOSE queue_messages_cursor DEALLOCATE queue_messages_cursor -- All done OK goto ExitProc; ----------------- -- Error Handler ----------------- ErrorHandler: ------------------ -- Exit Procedure ------------------ ExitProc: RETURN (@rc) END GO ---------------------------------------------------------- -- procedure sp_sysmail_activate ----------------------------------------------------------- IF NOT OBJECT_ID('dbo.sp_sysmail_activate', 'P') IS NULL DROP PROCEDURE dbo.sp_sysmail_activate GO ----- PRINT 'Creating sp_sysmail_activate' ----- GO -- sp_sysmail_activate : Starts the DatabaseMail process if it isn't already running -- CREATE PROCEDURE 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 @rc INT 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) --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, @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 /* Drop obsolete objects left over from previous CTPs */ IF (NOT OBJECT_ID(N'dbo.sysmail_verify_database_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_verify_database_sp IF (NOT OBJECT_ID(N'dbo.sysmail_verify_mailhost_database_sp', 'P') IS NULL) DROP PROCEDURE dbo.sysmail_verify_mailhost_database_sp IF (OBJECT_ID('dbo.sysmail_quota_information', 'U') IS NOT NULL) DROP TABLE sysmail_quota_information IF (OBJECT_ID('dbo.sp_delete_quota_information', 'P') IS NOT NULL) DROP PROCEDURE dbo.sp_delete_quota_information IF (OBJECT_ID('dbo.sp_verify_quota_mail_count', 'P') IS NOT NULL) DROP PROCEDURE dbo.sp_verify_quota_mail_count IF (OBJECT_ID('dbo.sp_add_quota_information', 'P') IS NOT NULL) DROP PROCEDURE dbo.sp_add_quota_information IF (OBJECT_ID('dbo.sp_current_principal_mails', 'P') IS NOT NULL) DROP PROCEDURE dbo.sp_current_principal_mails 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) */ /* */ /*************************************************************************/ ------------------------------------------------------------------- -- Create Database Mail MESSAGES, CONTRACTS, QUEUES AND SERVICES ------------------------------------------------------------------- PRINT '' PRINT 'Creating MESSAGES, CONTRACTS, QUEUES AND SERVICES...' PRINT '' -- Create SendMail message type. PRINT 'Creating MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMail]' CREATE MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMail] VALIDATION = NONE CREATE MESSAGE TYPE [{//www.microsoft.com/databasemail/messages}SendMailStatus] VALIDATION = NONE -- Create SendMail contract. PRINT 'Creating CONTRACT [//www.microsoft.com/databasemail/contracts/SendMail/v1.0]' CREATE CONTRACT [//www.microsoft.com/databasemail/contracts/SendMail/v1.0] ( [{//www.microsoft.com/databasemail/messages}SendMail] SENT BY INITIATOR, [{//www.microsoft.com/databasemail/messages}SendMailStatus] SENT BY TARGET ) -- Create InternalMailQueue queue. PRINT 'Creating QUEUE InternalMailQueue' CREATE QUEUE InternalMailQueue WITH ACTIVATION (PROCEDURE_NAME = sp_ExternalMailQueueListener, MAX_QUEUE_READERS = 1, EXECUTE AS SELF); -- Create ExternalMailQueue queue. PRINT 'Creating QUEUE ExternalMailQueue' CREATE QUEUE ExternalMailQueue WITH ACTIVATION (PROCEDURE_NAME = sp_sysmail_activate, MAX_QUEUE_READERS = 1, EXECUTE AS SELF); -- Create InternalMailService service. PRINT 'Creating SERVICE InternalMailService ON QUEUE InternalMailQueue' CREATE SERVICE InternalMailService ON QUEUE InternalMailQueue ( [//www.microsoft.com/databasemail/contracts/SendMail/v1.0] -- ,[//www.microsoft.com/databasemail/contracts/TestProfile/v1.0] ); -- Create ExternalMailService service. PRINT 'Creating SERVICE ExternalMailService ON QUEUE ExternalMailQueue' CREATE SERVICE ExternalMailService ON QUEUE ExternalMailQueue ( [//www.microsoft.com/databasemail/contracts/SendMail/v1.0] -- ,[//www.microsoft.com/databasemail/contracts/TestProfile/v1.0] ); GO /**************************************************************/ /* */ /* M A I N T E N A N C E P L A N S */ /* */ /**************************************************************/ /**************************************************************/ /* sp_maintplan_delete_log */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_maintplan_delete_log...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_maintplan_delete_log') AND (type = 'P'))) DROP PROCEDURE sp_maintplan_delete_log go CREATE PROCEDURE sp_maintplan_delete_log @plan_id UNIQUEIDENTIFIER = NULL, @subplan_id UNIQUEIDENTIFIER = NULL, @oldest_time DATETIME = NULL AS BEGIN -- @plan_id and @subplan_id must be both NULL or only one exclusively set IF (@plan_id IS NOT NULL) AND (@subplan_id IS NOT NULL) BEGIN RAISERROR(12980, -1, -1, '@plan_id', '@subplan_id') RETURN(1) END --Scenario 1: User wants to delete all logs --Scenario 2: User wants to delete all logs older than X date --Scenario 3: User wants to delete all logs for a given plan --Scenario 4: User wants to delete all logs for a specific subplan --Scenario 5: User wants to delete all logs for a given plan older than X date --Scenario 6: User wants to delete all logs for a specific subplan older than X date -- Special case 1: Delete all logs IF (@plan_id IS NULL) AND (@subplan_id IS NULL) AND (@oldest_time IS NULL) BEGIN DELETE msdb.dbo.sysmaintplan_logdetail DELETE msdb.dbo.sysmaintplan_log RETURN (0) END DELETE msdb.dbo.sysmaintplan_log WHERE ( task_detail_id in (SELECT task_detail_id FROM msdb.dbo.sysmaintplan_log WHERE ((@plan_id IS NULL) OR (plan_id = @plan_id)) AND ((@subplan_id IS NULL) OR (subplan_id = @subplan_id)) AND ((@oldest_time IS NULL) OR (start_time < @oldest_time))) ) RETURN (0) END GO /**************************************************************/ /* sp_maintplan_delete_subplan */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_maintplan_delete_subplan...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_maintplan_delete_subplan') AND (type = 'P'))) DROP PROCEDURE sp_maintplan_delete_subplan go CREATE PROCEDURE sp_maintplan_delete_subplan @subplan_id UNIQUEIDENTIFIER AS BEGIN DECLARE @retval INT DECLARE @job UNIQUEIDENTIFIER DECLARE @schedule INT 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 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 msdb.dbo.sysmaintplan_subplans WHERE (subplan_id = @subplan_id) --delete the job associated with this subplan IF (@job IS NOT NULL) BEGIN --Delete Job EXEC @retval = msdb.dbo.sp_delete_job @job_id = @job, @delete_unused_schedule = 1 IF (@retval <> 0) BEGIN ROLLBACK TRAN RETURN @retval END END COMMIT TRAN RETURN (0) 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 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) VALUES( @subplan_id, @plan_id, @description, @name, @job_id, @schedule_id) END ELSE BEGIN --Update the table DECLARE @s_subplan_name sysname DECLARE @s_subplan_description NVARCHAR(512) DECLARE @s_job_id UNIQUEIDENTIFIER DECLARE @s_schedule_id INT SELECT @s_subplan_name = subplan_name, @s_subplan_description = subplan_description, @s_job_id = job_id, @s_schedule_id = schedule_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 (@description IS NOT NULL) SELECT @s_subplan_description = @description IF (@job_id IS NOT NULL) SELECT @s_job_id = @job_id IF (@schedule_id IS NOT NULL) SELECT @s_schedule_id = @schedule_id --UPDATE the record UPDATE msdb.dbo.sysmaintplan_subplans SET subplan_name = @s_subplan_name, subplan_description = @s_subplan_description, job_id = @s_job_id, schedule_id = @s_schedule_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 ' + @constraint_name EXEC (@sql) ALTER TABLE sysdbmaintplan_history ADD CONSTRAINT servername_default DEFAULT (CONVERT(sysname, ServerProperty('ServerName'))) FOR server_name END END go /**************************************************************/ /* SPs for the maintenance plans */ /**************************************************************/ /**************************************************************/ /* sp_clear_dbmaintplan_by_db */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_clear_dbmaintplan_by_db...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_clear_dbmaintplan_by_db') AND (type = 'P'))) DROP PROCEDURE sp_clear_dbmaintplan_by_db GO CREATE PROCEDURE sp_clear_dbmaintplan_by_db @db_name sysname AS BEGIN DECLARE planid_cursor CURSOR FOR select plan_id from msdb.dbo.sysdbmaintplan_databases where database_name=@db_name OPEN planid_cursor declare @planid uniqueidentifier FETCH NEXT FROM planid_cursor INTO @planid WHILE (@@FETCH_STATUS <> -1) BEGIN IF (@@FETCH_STATUS <> -2) BEGIN delete from msdb.dbo.sysdbmaintplan_databases where plan_id=@planid AND database_name=@db_name if (NOT EXISTS(select * from msdb.dbo.sysdbmaintplan_databases where plan_id=@planid)) BEGIN --delete the job DECLARE jobid_cursor CURSOR FOR select job_id from msdb.dbo.sysdbmaintplan_jobs where plan_id=@planid OPEN jobid_cursor DECLARE @jobid uniqueidentifier FETCH NEXT FROM jobid_cursor INTO @jobid WHILE (@@FETCH_STATUS <> -1) BEGIN if (@@FETCH_STATUS <> -2) BEGIN execute msdb.dbo.sp_delete_job @jobid END FETCH NEXT FROM jobid_cursor into @jobid END CLOSE jobid_cursor DEALLOCATE jobid_cursor --delete the history delete from msdb.dbo.sysdbmaintplan_history where plan_id=@planid --delete the plan delete from msdb.dbo.sysdbmaintplans where plan_id=@planid END END FETCH NEXT FROM planid_cursor INTO @planid END CLOSE planid_cursor DEALLOCATE planid_cursor END GO /**************************************************************/ /* sp_add_maintenance_plan */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_maintenance_plan...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_maintenance_plan') AND (type = 'P'))) DROP PROCEDURE sp_add_maintenance_plan GO CREATE PROCEDURE sp_add_maintenance_plan @plan_name varchar(128), @plan_id UNIQUEIDENTIFIER OUTPUT AS BEGIN IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysdbmaintplans WHERE plan_name=@plan_name)) BEGIN SELECT @plan_id=NEWID() INSERT INTO msdb.dbo.sysdbmaintplans (plan_id, plan_name) VALUES (@plan_id, @plan_name) END ELSE BEGIN RAISERROR(14261,-1,-1,'@plan_name',@plan_name) RETURN(1) -- failure END END GO /**************************************************************/ /* sp_delete_maintenance_plan */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_maintenance_plan...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_maintenance_plan') AND (type = 'P'))) DROP PROCEDURE sp_delete_maintenance_plan GO CREATE PROCEDURE sp_delete_maintenance_plan @plan_id UNIQUEIDENTIFIER AS BEGIN /*check if the plan_id is valid*/ IF (NOT EXISTS(SELECT * FROM sysdbmaintplans WHERE plan_id=@plan_id)) BEGIN DECLARE @syserr VARCHAR(100) SELECT @syserr=CONVERT(VARCHAR(100),@plan_id) RAISERROR(14262,-1,-1,'@plan_id',@syserr) RETURN(1) END /* clean the related records in sysdbmaintplan_database */ DELETE FROM msdb.dbo.sysdbmaintplan_databases WHERE plan_id=@plan_id /* clean the related records in sysdbmaintplan_jobs*/ DELETE FROM msdb.dbo.sysdbmaintplan_jobs WHERE plan_id=@plan_id /* clean sysdbmaintplans */ DELETE FROM msdb.dbo.sysdbmaintplans WHERE plan_id= @plan_id END GO /**************************************************************/ /* sp_add_maintenance_plan_db */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_maintenance_plan_db...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_maintenance_plan_db') AND (type = 'P'))) DROP PROCEDURE sp_add_maintenance_plan_db GO CREATE PROCEDURE sp_add_maintenance_plan_db @plan_id UNIQUEIDENTIFIER, @db_name sysname AS BEGIN DECLARE @syserr VARCHAR(100) /*check if the plan_id is valid */ IF (NOT EXISTS (SELECT plan_id FROM msdb.dbo.sysdbmaintplans WHERE plan_id=@plan_id)) BEGIN SELECT @syserr=CONVERT(VARCHAR(100),@plan_id) RAISERROR(14262,-1,-1,'@plan_id',@syserr) RETURN(1) END /*check if the database name is valid */ IF (NOT EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE name=@db_name)) BEGIN RAISERROR(14262,-1,-1,'@db_name',@db_name) RETURN(1) END /*check if the (plan_id, database) pair already exists*/ IF (EXISTS (SELECT * FROM sysdbmaintplan_databases WHERE plan_id=@plan_id AND database_name=@db_name)) BEGIN SELECT @syserr=CONVERT(VARCHAR(100),@plan_id)+' + '+@db_name RAISERROR(14261,-1,-1,'@plan_id+@db_name',@syserr) RETURN(1) END INSERT INTO msdb.dbo.sysdbmaintplan_databases (plan_id,database_name) VALUES (@plan_id, @db_name) END GO /**************************************************************/ /* sp_delete_maintenance_plan_db */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_maintenance_plan_db...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_maintenance_plan_db') AND (type = 'P'))) DROP PROCEDURE sp_delete_maintenance_plan_db go CREATE PROCEDURE sp_delete_maintenance_plan_db @plan_id uniqueidentifier, @db_name sysname AS BEGIN /*check if the (plan_id, db_name) exists in the table*/ IF (NOT EXISTS(SELECT * FROM msdb.dbo.sysdbmaintplan_databases WHERE @plan_id=plan_id AND @db_name=database_name)) BEGIN DECLARE @syserr VARCHAR(300) SELECT @syserr=CONVERT(VARCHAR(100),@plan_id)+' + '+@db_name RAISERROR(14262,-1,-1,'@plan_id+@db_name',@syserr) RETURN(1) END /*delete the pair*/ DELETE FROM msdb.dbo.sysdbmaintplan_databases WHERE plan_id=@plan_id AND database_name=@db_name END GO /**************************************************************/ /* sp_add_maintenance_plan_job */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_maintenance_plan_job...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_add_maintenance_plan_job') AND (type = 'P'))) DROP PROCEDURE sp_add_maintenance_plan_job GO CREATE PROCEDURE sp_add_maintenance_plan_job @plan_id UNIQUEIDENTIFIER, @job_id UNIQUEIDENTIFIER AS BEGIN DECLARE @syserr varchar(100) /*check if the @plan_id is valid*/ IF (NOT EXISTS(SELECT plan_id FROM msdb.dbo.sysdbmaintplans WHERE plan_id=@plan_id)) BEGIN SELECT @syserr=CONVERT(VARCHAR(100),@plan_id) RAISERROR(14262,-1,-1,'@plan_id',@syserr) RETURN(1) END /*check if the @job_id is valid*/ IF (NOT EXISTS(SELECT job_id FROM msdb.dbo.sysjobs WHERE job_id=@job_id)) BEGIN SELECT @syserr=CONVERT(VARCHAR(100),@job_id) RAISERROR(14262,-1,-1,'@job_id',@syserr) RETURN(1) END /*check if the job has at least one step calling xp_sqlmaint*/ DECLARE @maxind INT SELECT @maxind=(SELECT MAX(CHARINDEX('xp_sqlmaint', command)) FROM msdb.dbo.sysjobsteps WHERE @job_id=job_id) IF (@maxind<=0) BEGIN /*print N'Warning: The job is not for maitenance plan.' -- will add the new sysmessage here*/ SELECT @syserr=CONVERT(VARCHAR(100),@job_id) RAISERROR(14199,-1,-1,@syserr) RETURN(1) END INSERT INTO msdb.dbo.sysdbmaintplan_jobs(plan_id,job_id) VALUES (@plan_id, @job_id) --don't have to check duplicate here END GO /**************************************************************/ /* sp_delete_maintenance_plan_job */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_maintenance_plan_job...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_maintenance_plan_job') AND (type = 'P'))) DROP PROCEDURE sp_delete_maintenance_plan_job GO CREATE PROCEDURE sp_delete_maintenance_plan_job @plan_id uniqueidentifier, @job_id uniqueidentifier AS BEGIN /*check if the (plan_id, job_id) exists*/ IF (NOT EXISTS(SELECT * FROM sysdbmaintplan_jobs WHERE @plan_id=plan_id AND @job_id=job_id)) BEGIN DECLARE @syserr VARCHAR(300) SELECT @syserr=CONVERT(VARCHAR(100),@plan_id)+' + '+CONVERT(VARCHAR(100),@job_id) RAISERROR(14262,-1,-1,'@plan_id+@job_id',@syserr) RETURN(1) END DELETE FROM msdb.dbo.sysdbmaintplan_jobs WHERE plan_id=@plan_id AND job_id=@job_id END GO /**************************************************************/ /* sp_help_maintenance_plan */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_help_maintenance_plan...' GO IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_maintenance_plan') AND (type = 'P'))) DROP PROCEDURE sp_help_maintenance_plan GO CREATE PROCEDURE sp_help_maintenance_plan @plan_id UNIQUEIDENTIFIER = NULL AS BEGIN IF (@plan_id IS NOT NULL) BEGIN /*return the information about the plan itself*/ SELECT * FROM msdb.dbo.sysdbmaintplans WHERE plan_id=@plan_id /*return the information about databases this plan defined on*/ SELECT database_name FROM msdb.dbo.sysdbmaintplan_databases WHERE plan_id=@plan_id /*return the information about the jobs that relating to the plan*/ SELECT job_id FROM msdb.dbo.sysdbmaintplan_jobs WHERE plan_id=@plan_id END ELSE BEGIN SELECT * FROM msdb.dbo.sysdbmaintplans END END GO /**************************************************************/ /* */ /* B A C K U P H I S T O R Y */ /* */ /**************************************************************/ /**************************************************************/ /* sp_delete_database_backuphistory */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_database_backuphistory...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_database_backuphistory') AND (type = 'P'))) DROP PROCEDURE sp_delete_database_backuphistory go CREATE PROCEDURE sp_delete_database_backuphistory @database_name sysname AS BEGIN SET NOCOUNT ON DECLARE @backup_set_id TABLE (backup_set_id INT) DECLARE @media_set_id TABLE (media_set_id INT) DECLARE @restore_history_id TABLE (restore_history_id INT) INSERT INTO @backup_set_id (backup_set_id) SELECT DISTINCT backup_set_id FROM msdb.dbo.backupset WHERE database_name = @database_name INSERT INTO @media_set_id (media_set_id) SELECT DISTINCT media_set_id FROM msdb.dbo.backupset WHERE database_name = @database_name INSERT INTO @restore_history_id (restore_history_id) SELECT DISTINCT restore_history_id FROM msdb.dbo.restorehistory WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) BEGIN TRANSACTION DELETE FROM msdb.dbo.backupfile WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.backupfilegroup WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.restorefile WHERE restore_history_id IN (SELECT restore_history_id FROM @restore_history_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.restorefilegroup WHERE restore_history_id IN (SELECT restore_history_id FROM @restore_history_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.restorehistory WHERE restore_history_id IN (SELECT restore_history_id FROM @restore_history_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.backupset WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) IF (@@error > 0) GOTO Quit DELETE msdb.dbo.backupmediafamily FROM msdb.dbo.backupmediafamily bmf WHERE bmf.media_set_id IN (SELECT media_set_id FROM @media_set_id) AND ((SELECT COUNT(*) FROM msdb.dbo.backupset WHERE media_set_id = bmf.media_set_id) = 0) IF (@@error > 0) GOTO Quit DELETE msdb.dbo.backupmediaset FROM msdb.dbo.backupmediaset bms WHERE bms.media_set_id IN (SELECT media_set_id FROM @media_set_id) AND ((SELECT COUNT(*) FROM msdb.dbo.backupset WHERE media_set_id = bms.media_set_id) = 0) IF (@@error > 0) GOTO Quit COMMIT TRANSACTION RETURN Quit: ROLLBACK TRANSACTION END go /**************************************************************/ /* SP_DELETE_BACKUPHISTORY */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_backuphistory...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_delete_backuphistory') AND (type = 'P'))) DROP PROCEDURE sp_delete_backuphistory go CREATE PROCEDURE sp_delete_backuphistory @oldest_date datetime AS BEGIN SET NOCOUNT ON DECLARE @backup_set_id TABLE (backup_set_id INT) DECLARE @media_set_id TABLE (media_set_id INT) DECLARE @restore_history_id TABLE (restore_history_id INT) INSERT INTO @backup_set_id (backup_set_id) SELECT DISTINCT backup_set_id FROM msdb.dbo.backupset WHERE backup_finish_date < @oldest_date INSERT INTO @media_set_id (media_set_id) SELECT DISTINCT media_set_id FROM msdb.dbo.backupset WHERE backup_finish_date < @oldest_date INSERT INTO @restore_history_id (restore_history_id) SELECT DISTINCT restore_history_id FROM msdb.dbo.restorehistory WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) BEGIN TRANSACTION DELETE FROM msdb.dbo.backupfile WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.backupfilegroup WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.restorefile WHERE restore_history_id IN (SELECT restore_history_id FROM @restore_history_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.restorefilegroup WHERE restore_history_id IN (SELECT restore_history_id FROM @restore_history_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.restorehistory WHERE restore_history_id IN (SELECT restore_history_id FROM @restore_history_id) IF (@@error > 0) GOTO Quit DELETE FROM msdb.dbo.backupset WHERE backup_set_id IN (SELECT backup_set_id FROM @backup_set_id) IF (@@error > 0) GOTO Quit DELETE msdb.dbo.backupmediafamily FROM msdb.dbo.backupmediafamily bmf WHERE bmf.media_set_id IN (SELECT media_set_id FROM @media_set_id) AND ((SELECT COUNT(*) FROM msdb.dbo.backupset WHERE media_set_id = bmf.media_set_id) = 0) IF (@@error > 0) GOTO Quit DELETE msdb.dbo.backupmediaset FROM msdb.dbo.backupmediaset bms WHERE bms.media_set_id IN (SELECT media_set_id FROM @media_set_id) AND ((SELECT COUNT(*) FROM msdb.dbo.backupset WHERE media_set_id = bms.media_set_id) = 0) IF (@@error > 0) GOTO Quit COMMIT TRANSACTION RETURN Quit: ROLLBACK TRANSACTION END go /**********************************************************************/ /* TABLE : log_shipping_primaries */ /* Populated on the monitor server */ /* */ /**********************************************************************/ IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE (TABLE_NAME = N'log_shipping_primaries'))) BEGIN PRINT '' PRINT 'Creating table log_shipping_primaries...' CREATE TABLE log_shipping_primaries ( primary_id INT IDENTITY NOT NULL PRIMARY KEY, primary_server_name sysname NOT NULL, primary_database_name sysname NOT NULL, maintenance_plan_id UNIQUEIDENTIFIER NULL, backup_threshold INT NOT NULL, threshold_alert INT NOT NULL, threshold_alert_enabled BIT NOT NULL, /* 1 = enabled, 0 = disabled */ last_backup_filename NVARCHAR(500) NULL, last_updated DATETIME NULL, planned_outage_start_time INT NOT NULL, planned_outage_end_time INT NOT NULL, planned_outage_weekday_mask INT NOT NULL, source_directory NVARCHAR(500) NULL ) END ELSE BEGIN IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE (TABLE_NAME = N'log_shipping_primaries') AND (COLUMN_NAME = N'source_directory'))) BEGIN PRINT '' PRINT 'Adding columns to table log_shipping_primaries...' ALTER TABLE log_shipping_primaries ADD source_directory NVARCHAR(500) NULL END END go /**********************************************************************/ /* TABLE : log_shipping_secondaries */ /* Populated on the monitor server */ /* */ /**********************************************************************/ IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE (TABLE_NAME = N'log_shipping_secondaries'))) BEGIN PRINT '' PRINT 'Creating table log_shipping_secondaries...' CREATE TABLE log_shipping_secondaries ( primary_id INT FOREIGN KEY REFERENCES log_shipping_primaries (primary_id), secondary_server_name sysname, secondary_database_name sysname, last_copied_filename NVARCHAR(500), last_loaded_filename NVARCHAR(500), last_copied_last_updated DATETIME, last_loaded_last_updated DATETIME, secondary_plan_id UNIQUEIDENTIFIER, copy_enabled BIT, load_enabled BIT, /* 1 = load enabled, 0 = load disabled */ out_of_sync_threshold INT, threshold_alert INT, threshold_alert_enabled BIT, /*1 = enabled, 0 = disabled */ planned_outage_start_time INT, planned_outage_end_time INT, planned_outage_weekday_mask INT, allow_role_change BIT DEFAULT (0) ) END ELSE BEGIN IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE (TABLE_NAME = N'log_shipping_secondaries') AND (COLUMN_NAME = N'allow_role_change'))) BEGIN PRINT '' PRINT 'Adding columns to table log_shipping_secondaries...' ALTER TABLE log_shipping_secondaries ADD allow_role_change BIT DEFAULT (0) END END go /**************************************************************/ /* sp_add_log_shipping_monitor_jobs */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_log_shipping_monitor_jobs...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_add_log_shipping_monitor_jobs' AND type = N'P') ) drop procedure sp_add_log_shipping_monitor_jobs go CREATE PROCEDURE sp_add_log_shipping_monitor_jobs AS BEGIN SET NOCOUNT ON BEGIN TRANSACTION DECLARE @rv INT DECLARE @backup_job_name sysname SET @backup_job_name = N'Log Shipping Alert Job - Backup' IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @backup_job_name)) BEGIN EXECUTE @rv = msdb.dbo.sp_add_job @job_name = N'Log Shipping Alert Job - Backup' IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error EXECUTE @rv = msdb.dbo.sp_add_jobstep @job_name = N'Log Shipping Alert Job - Backup', @step_id = 1, @step_name = N'Log Shipping Alert - Backup', @command = N'EXECUTE msdb.dbo.sp_log_shipping_monitor_backup', @on_fail_action = 2, @flags = 4, @subsystem = N'TSQL', @on_success_step_id = 0, @on_success_action = 1, @on_fail_step_id = 0 IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error EXECUTE @rv = msdb.dbo.sp_add_jobschedule @job_name = @backup_job_name, @freq_type = 4, @freq_interval = 1, @freq_subday_type = 0x4, @freq_subday_interval = 1, -- run every minute @freq_relative_interval = 0, @name = @backup_job_name IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error EXECUTE @rv = msdb.dbo.sp_add_jobserver @job_name = @backup_job_name, @server_name = NULL IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error END DECLARE @restore_job_name sysname SET @restore_job_name = 'Log Shipping Alert Job - Restore' IF (NOT EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @restore_job_name)) BEGIN EXECUTE @rv = msdb.dbo.sp_add_job @job_name = @restore_job_name IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error EXECUTE @rv = msdb.dbo.sp_add_jobstep @job_name = @restore_job_name, @step_id = 1, @step_name = @restore_job_name, @command = N'EXECUTE msdb.dbo.sp_log_shipping_monitor_restore', @on_fail_action = 2, @flags = 4, @subsystem = N'TSQL', @on_success_step_id = 0, @on_success_action = 1, @on_fail_step_id = 0 IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error EXECUTE @rv = msdb.dbo.sp_add_jobschedule @job_name = @restore_job_name, @freq_type = 4, @freq_interval = 1, @freq_subday_type = 0x4, @freq_subday_interval = 1, -- run every minute @freq_relative_interval = 0, @name = @restore_job_name IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error EXECUTE @rv = msdb.dbo.sp_add_jobserver @job_name = @restore_job_name, @server_name = NULL IF (@@error <> 0 OR @rv <> 0) GOTO rollback_quit -- error END COMMIT TRANSACTION RETURN rollback_quit: ROLLBACK TRANSACTION END go /**************************************************************/ /* sp_add_log_shipping_primary */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_log_shipping_primary...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_add_log_shipping_primary' AND type = N'P')) drop procedure sp_add_log_shipping_primary go CREATE PROCEDURE sp_add_log_shipping_primary @primary_server_name sysname, @primary_database_name sysname, @maintenance_plan_id UNIQUEIDENTIFIER = NULL, @backup_threshold INT = 60, @threshold_alert INT = 14420, @threshold_alert_enabled BIT = 1, @planned_outage_start_time INT = 0, @planned_outage_end_time INT = 0, @planned_outage_weekday_mask INT = 0, @primary_id INT = NULL OUTPUT AS BEGIN SET NOCOUNT ON IF EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name) BEGIN DECLARE @pair_name NVARCHAR SELECT @pair_name = @primary_server_name + N'.' + @primary_database_name RAISERROR (14261,16,1, N'primary_server_name.primary_database_name', @pair_name) RETURN (1) -- error END INSERT INTO msdb.dbo.log_shipping_primaries ( primary_server_name, primary_database_name, maintenance_plan_id, backup_threshold, threshold_alert, threshold_alert_enabled, last_backup_filename, last_updated, planned_outage_start_time, planned_outage_end_time, planned_outage_weekday_mask, source_directory) VALUES (@primary_server_name, @primary_database_name, @maintenance_plan_id, @backup_threshold, @threshold_alert, @threshold_alert_enabled, N'first_file_000000000000.trn', GETDATE (), @planned_outage_start_time, @planned_outage_end_time, @planned_outage_weekday_mask, NULL) SELECT @primary_id = @@IDENTITY EXECUTE msdb.dbo.sp_add_log_shipping_monitor_jobs END go /**************************************************************/ /* sp_add_log_shipping_secondary */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_add_log_shipping_secondary...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_add_log_shipping_secondary' AND type = N'P')) drop procedure sp_add_log_shipping_secondary go CREATE PROCEDURE sp_add_log_shipping_secondary @primary_id INT, @secondary_server_name sysname, @secondary_database_name sysname, @secondary_plan_id UNIQUEIDENTIFIER, @copy_enabled BIT = 1, @load_enabled BIT = 1, @out_of_sync_threshold INT = 60, @threshold_alert INT = 14421, @threshold_alert_enabled BIT = 1, @planned_outage_start_time INT = 0, @planned_outage_end_time INT = 0, @planned_outage_weekday_mask INT = 0, @allow_role_change BIT = 0 AS BEGIN SET NOCOUNT ON IF NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries where primary_id = @primary_id) BEGIN RAISERROR (14262, 16, 1, N'primary_id', N'msdb.dbo.log_shipping_primaries') RETURN(1) END INSERT INTO msdb.dbo.log_shipping_secondaries ( primary_id, secondary_server_name, secondary_database_name, last_copied_filename, last_loaded_filename, last_copied_last_updated, last_loaded_last_updated, secondary_plan_id, copy_enabled, load_enabled, out_of_sync_threshold, threshold_alert, threshold_alert_enabled, planned_outage_start_time, planned_outage_end_time, planned_outage_weekday_mask, allow_role_change) VALUES (@primary_id, @secondary_server_name, @secondary_database_name, N'first_file_000000000000.trn', N'first_file_000000000000.trn', GETDATE (), GETDATE (), @secondary_plan_id, @copy_enabled, @load_enabled, @out_of_sync_threshold, @threshold_alert, @threshold_alert_enabled, @planned_outage_start_time, @planned_outage_end_time, @planned_outage_weekday_mask, @allow_role_change) END go /**************************************************************/ /* sp_delete_log_shipping_monitor_jobs */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_log_shipping_monitor_jobs...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_delete_log_shipping_monitor_jobs' AND type = N'P') ) drop procedure sp_delete_log_shipping_monitor_jobs go CREATE PROCEDURE sp_delete_log_shipping_monitor_jobs AS BEGIN DECLARE @backup_job_name sysname SET NOCOUNT ON SET @backup_job_name = N'Log Shipping Alert Job - Backup' IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @backup_job_name)) EXECUTE msdb.dbo.sp_delete_job @job_name = N'Log Shipping Alert Job - Backup' DECLARE @restore_job_name sysname SET @restore_job_name = 'Log Shipping Alert Job - Restore' IF (EXISTS (SELECT * FROM msdb.dbo.sysjobs WHERE name = @restore_job_name)) EXECUTE msdb.dbo.sp_delete_job @job_name = N'Log Shipping Alert Job - Restore' END go /**************************************************************/ /* sp_delete_log_shipping_primary */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_log_shipping_primary...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_delete_log_shipping_primary' AND type = N'P') ) drop procedure sp_delete_log_shipping_primary go CREATE PROCEDURE sp_delete_log_shipping_primary @primary_server_name sysname, @primary_database_name sysname, @delete_secondaries BIT = 0 AS BEGIN DECLARE @primary_id INT SET NOCOUNT ON SELECT @primary_id = primary_id FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name IF (@primary_id IS NULL) RETURN (0) BEGIN TRANSACTION IF (EXISTS (SELECT * FROM msdb.dbo.log_shipping_secondaries WHERE primary_id = @primary_id)) BEGIN IF (@delete_secondaries = 0) BEGIN RAISERROR (14429,-1,-1) goto rollback_quit END DELETE FROM msdb.dbo.log_shipping_secondaries WHERE primary_id = @primary_id IF (@@ERROR <> 0) GOTO rollback_quit END DELETE FROM msdb.dbo.log_shipping_primaries WHERE primary_id = @primary_id IF (@@ERROR <> 0) GOTO rollback_quit COMMIT TRANSACTION DECLARE @i INT SELECT @i = COUNT(*) FROM msdb.dbo.log_shipping_primaries IF (@i=0) EXECUTE msdb.dbo.sp_delete_log_shipping_monitor_jobs RETURN (0) rollback_quit: ROLLBACK TRANSACTION RETURN(1) -- error END go /**************************************************************/ /* sp_delete_log_shipping_secondary */ /**************************************************************/ PRINT '' PRINT 'Creating sp_delete_log_shipping_secondary...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_delete_log_shipping_secondary' AND type = N'P') ) drop procedure sp_delete_log_shipping_secondary go CREATE PROCEDURE sp_delete_log_shipping_secondary @secondary_server_name sysname, @secondary_database_name sysname AS BEGIN SET NOCOUNT ON DELETE FROM msdb.dbo.log_shipping_secondaries WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name END go /**************************************************************/ /* sp_log_shipping_in_sync */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_shipping_in_sync...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_log_shipping_in_sync' AND type = N'P') ) drop procedure sp_log_shipping_in_sync go CREATE PROCEDURE sp_log_shipping_in_sync @last_updated DATETIME, @compare_with DATETIME, @threshold INT, @outage_start_time INT, @outage_end_time INT, @outage_weekday_mask INT, @enabled BIT = 1, @delta INT = NULL OUTPUT AS BEGIN SET NOCOUNT ON DECLARE @cur_time INT SELECT @delta = DATEDIFF (mi, @last_updated, @compare_with) -- in sync IF (@delta <= @threshold) RETURN (0) -- in sync IF (@enabled = 0) RETURN(0) -- in sync IF (@outage_weekday_mask & DATEPART(dw, GETDATE ()) > 0) -- potentially in outage window BEGIN SELECT @cur_time = DATEPART (hh, GETDATE()) * 10000 + DATEPART (mi, GETDATE()) * 100 + DATEPART (ss, GETDATE()) -- outage doesn't span midnight IF (@outage_start_time < @outage_end_time) BEGIN IF (@cur_time >= @outage_start_time AND @cur_time < @outage_end_time) RETURN(1) -- in outage END -- outage does span midnight ELSE IF (@outage_start_time > @outage_end_time) BEGIN IF (@cur_time >= @outage_start_time OR @cur_time < @outage_end_time) RETURN(1) -- in outage END END RETURN(-1 ) -- not in outage, not in sync END go /**************************************************************/ /* sp_log_shipping_get_date_from_file */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_shipping_get_date_from_file...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_log_shipping_get_date_from_file' AND type = N'P') ) drop procedure sp_log_shipping_get_date_from_file go CREATE PROCEDURE sp_log_shipping_get_date_from_file @db_name sysname, @filename NVARCHAR (500), @file_date DATETIME OUTPUT AS BEGIN SET NOCOUNT ON DECLARE @tempname NVARCHAR (500) IF (LEN (@filename) - (LEN(@db_name) + LEN ('_tlog_')) <= 0) RETURN(1) -- filename string isn't long enough SELECT @tempname = RIGHT (@filename, LEN (@filename) - (LEN(@db_name) + LEN ('_tlog_'))) IF (CHARINDEX ('.',@tempname,0) > 0) SELECT @tempname = LEFT (@tempname, CHARINDEX ('.',@tempname,0) - 1) IF (LEN (@tempname) <> 8 AND LEN (@tempname) <> 12) RETURN (1) -- error must be yyyymmddhhmm or yyyymmdd IF (ISNUMERIC (@tempname) = 0 OR CHARINDEX ('.',@tempname,0) <> 0 OR CONVERT (FLOAT,SUBSTRING (@tempname, 1,8)) < 1 ) RETURN (1) -- must be numeric, can't contain any '.' etc SELECT @file_date = CONVERT (DATETIME,SUBSTRING (@tempname, 1,8),112) IF (LEN (@tempname) = 12) BEGIN SELECT @file_date = DATEADD (hh, CONVERT (INT, SUBSTRING (@tempname,9,2)),@file_date) SELECT @file_date = DATEADD (mi, CONVERT (INT, SUBSTRING (@tempname,11,2)),@file_date) END RETURN (0) -- success END go /**************************************************************/ /* sp_get_log_shipping_monitor_info */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_get_log_shipping_monitor_info...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_get_log_shipping_monitor_info' AND type = N'P') ) drop procedure sp_get_log_shipping_monitor_info go CREATE PROCEDURE sp_get_log_shipping_monitor_info @primary_server_name sysname = N'%', @primary_database_name sysname = N'%', @secondary_server_name sysname = N'%', @secondary_database_name sysname = N'%' AS BEGIN SET NOCOUNT ON DECLARE @lsp TABLE ( primary_server_name sysname COLLATE database_default NOT NULL, primary_database_name sysname COLLATE database_default NOT NULL, secondary_server_name sysname COLLATE database_default NOT NULL, secondary_database_name sysname COLLATE database_default NOT NULL, backup_threshold INT NOT NULL, backup_threshold_alert INT NOT NULL, backup_threshold_alert_enabled BIT NOT NULL, last_backup_filename NVARCHAR(500) COLLATE database_default NOT NULL, last_backup_last_updated DATETIME NOT NULL, backup_outage_start_time INT NOT NULL, backup_outage_end_time INT NOT NULL, backup_outage_weekday_mask INT NOT NULL, backup_in_sync INT NULL, -- 0 = in sync, -1 = out of sync, 1 = in outage window backup_delta INT NULL, last_copied_filename NVARCHAR(500) COLLATE database_default NOT NULL, last_copied_last_updated DATETIME NOT NULL, last_loaded_filename NVARCHAR(500) COLLATE database_default NOT NULL, last_loaded_last_updated DATETIME NOT NULL, copy_delta INT NULL, copy_enabled BIT NOT NULL, load_enabled BIT NOT NULL, out_of_sync_threshold INT NOT NULL, load_threshold_alert INT NOT NULL, load_threshold_alert_enabled BIT NOT NULL, load_outage_start_time INT NOT NULL, load_outage_end_time INT NOT NULL, load_outage_weekday_mask INT NOT NULL, load_in_sync INT NULL, -- 0 = in sync, -1 = out of sync, 1 = in outage window load_delta INT NULL, maintenance_plan_id UNIQUEIDENTIFIER NULL, secondary_plan_id UNIQUEIDENTIFIER NOT NULL) INSERT INTO @lsp SELECT primary_server_name, primary_database_name, secondary_server_name, secondary_database_name, backup_threshold, p.threshold_alert, p.threshold_alert_enabled, last_backup_filename, p.last_updated, p.planned_outage_start_time, p.planned_outage_end_time, p.planned_outage_weekday_mask, NULL, NULL, last_copied_filename, last_copied_last_updated, last_loaded_filename, last_loaded_last_updated, NULL, copy_enabled, load_enabled, out_of_sync_threshold, s.threshold_alert, s.threshold_alert_enabled, s.planned_outage_start_time, s.planned_outage_weekday_mask, s.planned_outage_end_time, NULL, NULL, maintenance_plan_id, secondary_plan_id FROM msdb.dbo.log_shipping_primaries p, msdb.dbo.log_shipping_secondaries s WHERE p.primary_id = s.primary_id AND primary_server_name LIKE @primary_server_name AND primary_database_name LIKE @primary_database_name AND secondary_server_name LIKE @secondary_server_name AND secondary_database_name LIKE @secondary_database_name DECLARE @load_in_sync INT DECLARE @backup_in_sync INT DECLARE @_primary_server_name sysname DECLARE @_primary_database_name sysname DECLARE @_secondary_server_name sysname DECLARE @_secondary_database_name sysname DECLARE @last_loaded_last_updated DATETIME DECLARE @last_loaded_filename NVARCHAR (500) DECLARE @last_copied_filename NVARCHAR (500) DECLARE @last_backup_last_updated DATETIME DECLARE @last_backup_filename NVARCHAR (500) DECLARE @backup_outage_start_time INT DECLARE @backup_outage_end_time INT DECLARE @backup_outage_weekday_mask INT DECLARE @backup_threshold INT DECLARE @backup_threshold_alert_enabled BIT DECLARE @load_outage_start_time INT DECLARE @load_outage_end_time INT DECLARE @load_outage_weekday_mask INT DECLARE @load_threshold INT DECLARE @load_threshold_alert_enabled BIT DECLARE @backupdt DATETIME DECLARE @restoredt DATETIME DECLARE @copydt DATETIME DECLARE @rv INT DECLARE @dt DATETIME DECLARE @copy_delta INT DECLARE @load_delta INT DECLARE @backup_delta INT DECLARE @last_copied_last_updated DATETIME SELECT @dt = GETDATE () DECLARE sync_update CURSOR FOR SELECT primary_server_name, primary_database_name, secondary_server_name, secondary_database_name, last_backup_filename, last_backup_last_updated, last_loaded_filename, last_loaded_last_updated, backup_outage_start_time, backup_outage_end_time, backup_outage_weekday_mask, backup_threshold, backup_threshold_alert_enabled, load_outage_start_time, load_outage_end_time, out_of_sync_threshold, load_outage_weekday_mask, load_threshold_alert_enabled, last_copied_filename, last_copied_last_updated FROM @lsp FOR READ ONLY OPEN sync_update loop: FETCH NEXT FROM sync_update INTO @_primary_server_name, @_primary_database_name, @_secondary_server_name, @_secondary_database_name, @last_backup_filename, @last_backup_last_updated, @last_loaded_filename, @last_loaded_last_updated, @backup_outage_start_time, @backup_outage_end_time, @backup_outage_weekday_mask, @backup_threshold, @backup_threshold_alert_enabled, @load_outage_start_time, @load_outage_end_time, @load_threshold, @load_outage_weekday_mask, @load_threshold_alert_enabled, @last_copied_filename, @last_copied_last_updated IF @@fetch_status <> 0 GOTO _loop EXECUTE @rv = sp_log_shipping_get_date_from_file @_primary_database_name, @last_backup_filename, @backupdt OUTPUT IF (@rv <> 0) SElECT @backupdt = @last_backup_last_updated EXECUTE @rv = sp_log_shipping_get_date_from_file @_primary_database_name, @last_loaded_filename, @restoredt OUTPUT IF (@rv <> 0) SElECT @restoredt = @last_loaded_last_updated EXECUTE @rv = sp_log_shipping_get_date_from_file @_primary_database_name, @last_copied_filename, @copydt OUTPUT IF (@rv <> 0) SElECT @copydt = @last_copied_last_updated EXECUTE @load_in_sync = msdb.dbo.sp_log_shipping_in_sync @restoredt, @backupdt, @load_threshold, @load_outage_start_time, @load_outage_end_time, @load_outage_weekday_mask, @load_threshold_alert_enabled, @load_delta OUTPUT EXECUTE @backup_in_sync = msdb.dbo.sp_log_shipping_in_sync @last_backup_last_updated, @dt, @backup_threshold, @backup_outage_start_time, @backup_outage_end_time, @backup_outage_weekday_mask, @backup_threshold_alert_enabled, @backup_delta OUTPUT EXECUTE msdb.dbo.sp_log_shipping_in_sync @copydt, @backupdt, 1,0,0,0,0, @copy_delta OUTPUT UPDATE @lsp SET backup_in_sync = @backup_in_sync, load_in_sync = @load_in_sync, copy_delta = @copy_delta, load_delta = @load_delta, backup_delta = @backup_delta WHERE primary_server_name = @_primary_server_name AND secondary_server_name = @_secondary_server_name AND primary_database_name = @_primary_database_name AND secondary_database_name = @_secondary_database_name GOTO loop _loop: CLOSE sync_update DEALLOCATE sync_update SELECT * FROM @lsp END go /**************************************************************/ /* sp_update_log_shipping_monitor_info */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_update_log_shipping_monitor_info...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_update_log_shipping_monitor_info' AND type = N'P') ) DROP PROCEDURE sp_update_log_shipping_monitor_info go CREATE PROCEDURE sp_update_log_shipping_monitor_info @primary_server_name sysname, @primary_database_name sysname, @secondary_server_name sysname, @secondary_database_name sysname, @backup_threshold INT = NULL, @backup_threshold_alert INT = NULL, @backup_threshold_alert_enabled BIT = NULL, @backup_outage_start_time INT = NULL, @backup_outage_end_time INT = NULL, @backup_outage_weekday_mask INT = NULL, @copy_enabled BIT = NULL, @load_enabled BIT = NULL, @out_of_sync_threshold INT = NULL, @out_of_sync_threshold_alert INT = NULL, @out_of_sync_threshold_alert_enabled BIT = NULL, @out_of_sync_outage_start_time INT = NULL, @out_of_sync_outage_end_time INT = NULL, @out_of_sync_outage_weekday_mask INT = NULL AS BEGIN SET NOCOUNT ON DECLARE @_backup_threshold INT DECLARE @_backup_threshold_alert INT DECLARE @_backup_threshold_alert_enabled BIT DECLARE @_backup_outage_start_time INT DECLARE @_backup_outage_end_time INT DECLARE @_backup_outage_weekday_mask INT DECLARE @_copy_enabled BIT DECLARE @_load_enabled BIT DECLARE @_out_of_sync_threshold INT DECLARE @_out_of_sync_threshold_alert INT DECLARE @_out_of_sync_threshold_alert_enabled BIT DECLARE @_out_of_sync_outage_start_time INT DECLARE @_out_of_sync_outage_end_time INT DECLARE @_out_of_sync_outage_weekday_mask INT -- check that the primary exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name)) BEGIN DECLARE @pp sysname SELECT @pp = @primary_server_name + N'.' + @primary_database_name RAISERROR (14262, 16, 1, N'primary_server_name.primary_database_name', @pp) RETURN (1) -- error END -- check that the secondary exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_secondaries WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name)) BEGIN DECLARE @sp sysname SELECT @sp = @secondary_server_name + N'.' + @secondary_database_name RAISERROR (14262, 16, 1, N'secondary_server_name.secondary_database_name', @sp) RETURN (1) -- error END -- load the original variables SELECT @_backup_threshold = backup_threshold, @_backup_threshold_alert = p.threshold_alert, @_backup_threshold_alert_enabled = p.threshold_alert_enabled, @_backup_outage_start_time = p.planned_outage_start_time, @_backup_outage_end_time = p.planned_outage_end_time, @_backup_outage_weekday_mask = p.planned_outage_weekday_mask, @_copy_enabled = copy_enabled, @_load_enabled = load_enabled, @_out_of_sync_threshold = out_of_sync_threshold, @_out_of_sync_threshold_alert = s.threshold_alert, @_out_of_sync_threshold_alert_enabled = s.threshold_alert_enabled, @_out_of_sync_outage_start_time = s.planned_outage_start_time, @_out_of_sync_outage_weekday_mask = s.planned_outage_weekday_mask, @_out_of_sync_outage_end_time = s.planned_outage_end_time FROM msdb.dbo.log_shipping_primaries p, msdb.dbo.log_shipping_secondaries s WHERE p.primary_id = s.primary_id AND primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name AND secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name SELECT @_backup_threshold = ISNULL (@backup_threshold, @_backup_threshold) SELECT @_backup_threshold_alert = ISNULL (@backup_threshold_alert, @_backup_threshold_alert) SELECT @_backup_threshold_alert_enabled = ISNULL (@backup_threshold_alert_enabled, @_backup_threshold_alert_enabled) SELECT @_backup_outage_start_time = ISNULL (@backup_outage_start_time, @_backup_outage_start_time) SELECT @_backup_outage_end_time = ISNULL (@backup_outage_end_time, @_backup_outage_end_time) SELECT @_backup_outage_weekday_mask = ISNULL (@backup_outage_weekday_mask, @_backup_outage_weekday_mask) SELECT @_copy_enabled = ISNULL (@copy_enabled, @_copy_enabled) SELECT @_load_enabled = ISNULL (@load_enabled, @_load_enabled) SELECT @_out_of_sync_threshold = ISNULL (@out_of_sync_threshold, @_out_of_sync_threshold) SELECT @_out_of_sync_threshold_alert = ISNULL (@out_of_sync_threshold_alert, @_out_of_sync_threshold_alert) SELECT @_out_of_sync_threshold_alert_enabled = ISNULL (@out_of_sync_threshold_alert_enabled, @_out_of_sync_threshold_alert_enabled) SELECT @_out_of_sync_outage_start_time = ISNULL (@out_of_sync_outage_start_time, @_out_of_sync_outage_start_time) SELECT @_out_of_sync_outage_end_time = ISNULL (@out_of_sync_outage_end_time, @_out_of_sync_outage_end_time) SELECT @_out_of_sync_outage_weekday_mask = ISNULL (@out_of_sync_outage_weekday_mask, @_out_of_sync_outage_weekday_mask) -- updates UPDATE msdb.dbo.log_shipping_primaries SET backup_threshold = @_backup_threshold, threshold_alert = @_backup_threshold_alert, threshold_alert_enabled = @_backup_threshold_alert_enabled, planned_outage_start_time = @_backup_outage_start_time, planned_outage_end_time = @_backup_outage_end_time, planned_outage_weekday_mask = @_backup_outage_weekday_mask WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name UPDATE msdb.dbo.log_shipping_secondaries SET copy_enabled = @_copy_enabled, load_enabled = @_load_enabled, out_of_sync_threshold = @_out_of_sync_threshold, threshold_alert = @_out_of_sync_threshold_alert, threshold_alert_enabled = @_out_of_sync_threshold_alert_enabled, planned_outage_start_time = @_out_of_sync_outage_start_time, planned_outage_end_time = @_out_of_sync_outage_weekday_mask, planned_outage_weekday_mask = @_out_of_sync_outage_end_time WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name RETURN(0) END go /**************************************************************/ /* sp_delete_log_shipping_monitor_info */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_delete_log_shipping_monitor_info...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_delete_log_shipping_monitor_info' AND type = N'P') ) DROP PROCEDURE sp_delete_log_shipping_monitor_info go CREATE PROCEDURE sp_delete_log_shipping_monitor_info @primary_server_name sysname, @primary_database_name sysname, @secondary_server_name sysname, @secondary_database_name sysname AS BEGIN -- check that the primary exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name)) BEGIN DECLARE @pp sysname SELECT @pp = @primary_server_name + N'.' + @primary_database_name RAISERROR (14262, 16, 1, N'primary_server_name.primary_database_name', @pp) RETURN (1) -- error END -- check that the secondary exists IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_secondaries WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name)) BEGIN DECLARE @sp sysname SELECT @sp = @secondary_server_name + N'.' + @secondary_database_name RAISERROR (14262, 16, 1, N'secondary_server_name.secondary_database_name', @sp) RETURN (1) -- error END BEGIN TRANSACTION -- delete the secondary DELETE FROM msdb.dbo.log_shipping_secondaries WHERE secondary_server_name = @secondary_server_name AND secondary_database_name = @secondary_database_name IF (@@error <> 0) goto rollback_quit -- if there are no more secondaries for this primary then delete it IF (NOT EXISTS (SELECT * FROM msdb.dbo.log_shipping_primaries p, msdb.dbo.log_shipping_secondaries s WHERE p.primary_id = s.primary_id AND primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name)) BEGIN DELETE FROM msdb.dbo.log_shipping_primaries WHERE primary_server_name = @primary_server_name AND primary_database_name = @primary_database_name IF (@@error <> 0) goto rollback_quit END COMMIT TRANSACTION RETURN (0) rollback_quit: ROLLBACK TRANSACTION RETURN(1) -- Failure END go /**************************************************************/ /* sp_remove_log_shipping_monitor_account */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_remove_log_shipping_monitor_account...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_remove_log_shipping_monitor_account' AND type = N'P') ) DROP PROCEDURE sp_remove_log_shipping_monitor_account go CREATE PROCEDURE sp_remove_log_shipping_monitor_account AS BEGIN SET NOCOUNT ON EXECUTE sp_dropuser N'log_shipping_monitor_probe' EXECUTE sp_droplogin N'log_shipping_monitor_probe' END go /**************************************************************/ /* sp_log_shipping_monitor_backup */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_shipping_monitor_backup...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_log_shipping_monitor_backup' AND type = N'P') ) drop procedure sp_log_shipping_monitor_backup go CREATE PROCEDURE sp_log_shipping_monitor_backup AS BEGIN DECLARE @primary_id sysname DECLARE @primary_server_name sysname DECLARE @primary_database_name sysname DECLARE @maintenance_plan_id UNIQUEIDENTIFIER DECLARE @backup_threshold INT DECLARE @threshold_alert INT DECLARE @threshold_alert_enabled BIT DECLARE @last_backup_filename sysname DECLARE @last_updated DATETIME DECLARE @planned_outage_start_time INT DECLARE @planned_outage_end_time INT DECLARE @planned_outage_weekday_mask INT DECLARE @sync_status INT DECLARE @backup_delta INT DECLARE @delta_string NVARCHAR (10) DECLARE @dt DATETIME SELECT @dt = GETDATE () SET NOCOUNT ON DECLARE bmlsp_cur CURSOR FOR SELECT primary_id, primary_server_name, primary_database_name, maintenance_plan_id, backup_threshold, threshold_alert, threshold_alert_enabled, last_backup_filename, last_updated, planned_outage_start_time, planned_outage_end_time, planned_outage_weekday_mask FROM msdb.dbo.log_shipping_primaries FOR READ ONLY OPEN bmlsp_cur loop: FETCH NEXT FROM bmlsp_cur INTO @primary_id, @primary_server_name, @primary_database_name, @maintenance_plan_id, @backup_threshold, @threshold_alert, @threshold_alert_enabled, @last_backup_filename, @last_updated, @planned_outage_start_time, @planned_outage_end_time, @planned_outage_weekday_mask IF @@FETCH_STATUS <> 0 -- nothing more to fetch, finish the loop GOTO _loop EXECUTE @sync_status = sp_log_shipping_in_sync @last_updated, @dt, @backup_threshold, @planned_outage_start_time, @planned_outage_end_time, @planned_outage_weekday_mask, @threshold_alert_enabled, @backup_delta OUTPUT IF (@sync_status < 0) BEGIN SELECT @delta_string = CONVERT (NVARCHAR(10), @backup_delta) RAISERROR (@threshold_alert, 16, 1, @primary_server_name, @primary_database_name, @delta_string) END GOTO loop _loop: CLOSE bmlsp_cur DEALLOCATE bmlsp_cur END go /**************************************************************/ /* sp_log_shipping_monitor_restore */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_log_shipping_monitor_restore...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_log_shipping_monitor_restore' AND type = N'P') ) drop procedure sp_log_shipping_monitor_restore go CREATE PROCEDURE sp_log_shipping_monitor_restore AS BEGIN SET NOCOUNT ON DECLARE @primary_id INT DECLARE @secondary_server_name sysname DECLARE @secondary_database_name sysname DECLARE @secondary_plan_id UNIQUEIDENTIFIER DECLARE @out_of_sync_threshold INT DECLARE @threshold_alert INT DECLARE @threshold_alert_enabled BIT DECLARE @last_loaded_filename NVARCHAR (500) DECLARE @last_backup_filename NVARCHAR (500) DECLARE @primary_database_name sysname DECLARE @last_loaded_last_updated DATETIME DECLARE @last_backup_last_updated DATETIME DECLARE @planned_outage_start_time INT DECLARE @planned_outage_end_time INT DECLARE @planned_outage_weekday_mask INT DECLARE @sync_status INT DECLARE @sync_delta INT DECLARE @delta_string NVARCHAR(10) SET NOCOUNT ON DECLARE @backupdt DATETIME DECLARE @restoredt DATETIME DECLARE @rv INT DECLARE rmlsp_cur CURSOR FOR SELECT s.primary_id, s.secondary_server_name, s.secondary_database_name, s.secondary_plan_id, s.out_of_sync_threshold, s.threshold_alert, s.threshold_alert_enabled, s.last_loaded_filename, s.last_loaded_last_updated, p.last_backup_filename, p.last_updated, p.primary_database_name, s.planned_outage_start_time, s.planned_outage_end_time, s.planned_outage_weekday_mask FROM msdb.dbo.log_shipping_secondaries s INNER JOIN msdb.dbo.log_shipping_primaries p ON s.primary_id = p.primary_id FOR READ ONLY OPEN rmlsp_cur loop: FETCH NEXT FROM rmlsp_cur INTO @primary_id, @secondary_server_name, @secondary_database_name, @secondary_plan_id, @out_of_sync_threshold, @threshold_alert, @threshold_alert_enabled, @last_loaded_filename, @last_loaded_last_updated, @last_backup_filename, @last_backup_last_updated, @primary_database_name, @planned_outage_start_time, @planned_outage_end_time, @planned_outage_weekday_mask IF @@FETCH_STATUS <> 0 -- nothing more to fetch, finish the loop GOTO _loop EXECUTE @rv = sp_log_shipping_get_date_from_file @primary_database_name, @last_backup_filename, @backupdt OUTPUT IF (@rv <> 0) SELECT @backupdt = @last_backup_last_updated EXECUTE @rv = sp_log_shipping_get_date_from_file @primary_database_name, @last_loaded_filename, @restoredt OUTPUT IF (@rv <> 0) SELECT @restoredt = @last_loaded_last_updated EXECUTE @sync_status = sp_log_shipping_in_sync @restoredt, @backupdt, @out_of_sync_threshold, @planned_outage_start_time, @planned_outage_end_time, @planned_outage_weekday_mask, @threshold_alert_enabled, @sync_delta OUTPUT IF (@sync_status < 0) BEGIN SELECT @delta_string = CONVERT (NVARCHAR(10), @sync_delta) RAISERROR (@threshold_alert, 16, 1, @secondary_server_name, @secondary_database_name, @delta_string) END GOTO loop _loop: CLOSE rmlsp_cur DEALLOCATE rmlsp_cur END go /**************************************************************/ /* sp_change_monitor_role */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_change_monitor_role...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_change_monitor_role' AND type = N'P') ) DROP PROCEDURE sp_change_monitor_role go CREATE PROCEDURE sp_change_monitor_role @primary_server sysname, @secondary_server sysname, @database sysname, @new_source NVARCHAR (128) AS BEGIN SET NOCOUNT ON BEGIN TRANSACTION -- drop the secondary DELETE FROM msdb.dbo.log_shipping_secondaries WHERE secondary_server_name = @secondary_server AND secondary_database_name = @database IF (@@ROWCOUNT <> 1) BEGIN ROLLBACK TRANSACTION RAISERROR (14442,-1,-1) return(1) END -- let everyone know that we are the new primary UPDATE msdb.dbo.log_shipping_primaries SET primary_server_name = @secondary_server, primary_database_name = @database, source_directory = @new_source WHERE primary_server_name = @primary_server AND primary_database_name = @database IF (@@ROWCOUNT <> 1) BEGIN ROLLBACK TRANSACTION RAISERROR (14442,-1,-1) return(1) END COMMIT TRANSACTION END go /**************************************************************/ /* sp_create_log_shipping_monitor_account */ /**************************************************************/ PRINT '' PRINT 'Creating procedure sp_create_log_shipping_monitor_account...' go IF (EXISTS (SELECT * from msdb.dbo.sysobjects WHERE name = N'sp_create_log_shipping_monitor_account' AND type = N'P') ) drop procedure sp_create_log_shipping_monitor_account go CREATE PROCEDURE sp_create_log_shipping_monitor_account @password sysname AS BEGIN DECLARE @rv INT SET NOCOUNT ON -- raise an error if the password already exists if exists(select * from master.dbo.syslogins where loginname = N'log_shipping_monitor_probe') begin raiserror(15025,-1,-1,N'log_shipping_monitor_probe') RETURN (1) -- error end IF (@password = N'') BEGIN EXECUTE @rv = sp_addlogin N'log_shipping_monitor_probe', @defdb = N'msdb' IF @@error <>0 or @rv <> 0 RETURN (1) -- error END ELSE BEGIN EXECUTE @rv = sp_addlogin N'log_shipping_monitor_probe', @password, N'msdb' IF @@error <>0 or @rv <> 0 RETURN (1) -- error END EXECUTE @rv = sp_grantdbaccess N'log_shipping_monitor_probe', N'log_shipping_monitor_probe' IF @@error <>0 or @rv <> 0 RETURN (1) -- error GRANT UPDATE ON log_shipping_primaries TO log_shipping_monitor_probe GRANT UPDATE ON log_shipping_secondaries TO log_shipping_monitor_probe GRANT SELECT ON log_shipping_primaries TO log_shipping_monitor_probe GRANT SELECT ON log_shipping_secondaries TO log_shipping_monitor_probe RETURN (0) END go /**************************************************************/ /* INTEGRATION SERVICES SECTION */ /**************************************************************/ USE msdb GO if not exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sysdtspackages90]')) BEGIN CREATE TABLE [dbo].[sysdtspackages90] ( [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__sysdtspackages90] 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__sysdtspackages90_2] DEFAULT (0), [readrolesid] [varbinary] (85) NULL, [writerolesid] [varbinary] (85) NULL, CONSTRAINT [pk_sysdtspackages90] 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='sysdtspackages90')) BEGIN ALTER TABLE [dbo].[sysdtspackages90] ADD [isencrypted] [bit] NOT NULL CONSTRAINT [DF__sysdtspackages90_2] DEFAULT (0) ALTER TABLE [dbo].[sysdtspackages90] ADD [readrolesid] [varbinary] (85) NULL ALTER TABLE [dbo].[sysdtspackages90] 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='sysdtspackages90')) BEGIN ALTER TABLE [dbo].[sysdtspackages90] DROP COLUMN [readrole] ALTER TABLE [dbo].[sysdtspackages90] DROP COLUMN [writerole] ALTER TABLE [dbo].[sysdtspackages90] ADD [readrolesid] [varbinary] (85) NULL ALTER TABLE [dbo].[sysdtspackages90] 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] FROM msdb.dbo.sysdtspackages90 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].[sysdtspackagefolders90]')) BEGIN CREATE TABLE [dbo].[sysdtspackagefolders90] ( [folderid] [uniqueidentifier] NOT NULL , [parentfolderid] [uniqueidentifier] NULL , [foldername] [sysname] NOT NULL , CONSTRAINT [PK_sysdtspackagefolders90] PRIMARY KEY NONCLUSTERED ( [folderid] ) ON [PRIMARY], CONSTRAINT [U_sysdtspackagefolders90uniquepath] UNIQUE NONCLUSTERED ( [parentfolderid], [foldername] ) ON [PRIMARY] ) ON [PRIMARY] END GO -- WARNING! IMPORTANT! If you change sysdtslog90 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].[sysdtslog90]')) BEGIN CREATE TABLE [dbo].[sysdtslog90] ( [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_dts_addlogentry]')) drop procedure [dbo].[sp_dts_addlogentry] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_dts_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 sysdtslog90 ( 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_dts_listpackages]')) drop procedure [dbo].[sp_dts_listpackages] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_dts_listpackages] @folderid uniqueidentifier AS SELECT name, id, description, createdate, folderid, datalength(packagedata), vermajor, verminor, verbuild, vercomments, verid FROM sysdtspackages90 WHERE [folderid] = @folderid ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_dts_listfolders]')) drop procedure [dbo].[sp_dts_listfolders] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_dts_listfolders] @parentfolderid uniqueidentifier = NULL AS SELECT folderid, parentfolderid, foldername FROM sysdtspackagefolders90 WHERE [parentfolderid] = @parentfolderid OR (@parentfolderid IS NULL AND [parentfolderid] IS NULL) ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_dts_deletepackage]')) drop procedure [dbo].[sp_dts_deletepackage] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_dts_deletepackage] @name sysname, @folderid uniqueidentifier AS DECLARE @sid varbinary(85) DECLARE @writerolesid varbinary(85) DECLARE @writerole nvarchar(128) SELECT @sid = [ownersid], @writerolesid = [writerolesid] FROM sysdtspackages90 WHERE [name] = @name AND [folderid] = @folderid AND [packageformat] = 0 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_dtsadmin'' END IF @writerole IS NULL BEGIN IF (IS_MEMBER(''db_dtsadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN IF (@sid<>SUSER_SID()) OR (IS_MEMBER(''db_dtsltduser'')<>1) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END END END ELSE BEGIN IF (IS_MEMBER(@writerole)<>1) BEGIN IF (@sid<>SUSER_SID()) OR (IS_MEMBER(''db_dtsltduser'')<>1) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END END END END DELETE FROM sysdtspackages90 WHERE [name] = @name AND [folderid] = @folderid AND [packageformat] = 0 ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_dts_deletefolder]')) drop procedure [dbo].[sp_dts_deletefolder] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_dts_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 sysdtspackagefolders90 WHERE [folderid] = @folderid IF @name IS NOT NULL BEGIN --// The row exists, check security IF (IS_MEMBER(''db_dtsadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN IF (IS_MEMBER(''db_dtsltduser'')<>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 sysdtspackages90 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 sysdtspackagefolders90 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 sysdtspackagefolders90 WHERE [folderid] = @folderid ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_dts_getpackage]')) drop procedure [dbo].[sp_dts_getpackage] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_dts_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 sysdtspackages90 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_dtsadmin'' END IF @readrole IS NOT NULL BEGIN IF (IS_MEMBER(@readrole)<>1) AND (IS_MEMBER(''db_dtsadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN IF (IS_MEMBER(''db_dtsltduser'')<>1) OR (@sid<>SUSER_SID()) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END END END ELSE BEGIN IF (IS_MEMBER(''db_dtsadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) AND (IS_MEMBER(''db_dtsoperator'')<>1) BEGIN IF (IS_MEMBER(''db_dtsltduser'')<>1) OR (@sid<>SUSER_SID()) BEGIN RAISERROR (14586, -1, -1, @name) RETURN 1 -- Failure END END END END SELECT packagedata FROM sysdtspackages90 WHERE [name] = @name AND [folderid] = @folderid AND [packageformat] = 0 ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_dts_getfolder]')) drop procedure [dbo].[sp_dts_getfolder] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_dts_getfolder] @name sysname, @parentfolderid uniqueidentifier AS SELECT folder.folderid, folder.foldername, folder.parentfolderid, parent.foldername FROM sysdtspackagefolders90 folder LEFT OUTER JOIN sysdtspackagefolders90 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_dts_putpackage]')) drop procedure [dbo].[sp_dts_putpackage] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_dts_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 sysdtspackages90 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_dtsadmin'' END IF @writerole IS NULL BEGIN IF (IS_MEMBER(''db_dtsadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN IF (@sid<>SUSER_SID()) OR (IS_MEMBER(''db_dtsltduser'')<>1) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END END END ELSE BEGIN IF (IS_MEMBER(@writerole)<>1) AND (IS_MEMBER(''db_dtsadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN IF (@sid<>SUSER_SID()) OR (IS_MEMBER(''db_dtsltduser'')<>1) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END END END --// Security check passed, UPDATE now UPDATE sysdtspackages90 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_dtsltduser'')<>1) AND (IS_MEMBER(''db_dtsadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN RAISERROR (14307, -1, -1, @name) RETURN 1 -- Failure END --// Security check passed, INSERT now INSERT INTO sysdtspackages90 ( 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_dts_checkexists]')) drop procedure [dbo].[sp_dts_checkexists] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_dts_checkexists] @name sysname, @folderid uniqueidentifier AS SET NOCOUNT ON SELECT TOP 1 1 FROM sysdtspackages90 WHERE [name] = @name AND [folderid] = @folderid AND [packageformat] = 0 RETURN 0 -- SUCCESS ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_dts_addfolder]')) drop procedure [dbo].[sp_dts_addfolder] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_dts_addfolder] @parentfolderid uniqueidentifier, @name sysname, @folderid uniqueidentifier = NULL AS --Check security IF (IS_MEMBER(''db_dtsltduser'')<>1) AND (IS_MEMBER(''db_dtsadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN RAISERROR (14591, -1, -1, @name) RETURN 1 -- Failure END --// Security check passed, INSERT now INSERT INTO sysdtspackagefolders90 (folderid, parentfolderid, foldername) VALUES (ISNULL(@folderid, NEWID()), @parentfolderid, @name) ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_dts_renamefolder]')) drop procedure [dbo].[sp_dts_renamefolder] execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_dts_renamefolder] @folderid uniqueidentifier, @name sysname AS --Check security IF (IS_MEMBER(''db_dtsltduser'')<>1) AND (IS_MEMBER(''db_dtsadmin'')<>1) AND (IS_SRVROLEMEMBER(''sysadmin'')<>1) BEGIN RAISERROR (14591, -1, -1, @name) RETURN 1 -- Failure END --// Security check passed, INSERT now UPDATE sysdtspackagefolders90 SET [foldername] = @name WHERE [folderid] = @folderid ' GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_dts_setpackageroles]')) DROP PROCEDURE [dbo].[sp_dts_setpackageroles] GO execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_dts_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 sysdtspackages90 WHERE name = @name AND folderid = @folderid IF @sid IS NOT NULL BEGIN --// The row exists, check security IF (IS_MEMBER(''db_dtsadmin'')<>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 sysdtspackages90 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_dts_getpackageroles]')) DROP PROCEDURE [dbo].[sp_dts_getpackageroles] GO execute sp_executesql N'CREATE PROCEDURE [dbo].[sp_dts_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 sysdtspackages90 WHERE [name] = @name AND [folderid] = @folderid AND [packageformat] = 0 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 if not exists (select * from dbo.sysusers where [name] = N'db_dtsadmin' and [issqlrole] = 1) BEGIN EXEC sp_addrole N'db_dtsadmin' END GO if not exists (select * from dbo.sysusers where [name] = N'db_dtsltduser' and [issqlrole] = 1) BEGIN EXEC sp_addrole N'db_dtsltduser' END GO if not exists (select * from dbo.sysusers where [name] = N'db_dtsoperator' and [issqlrole] = 1) BEGIN EXEC sp_addrole N'db_dtsoperator' END GO GRANT EXECUTE ON [dbo].[sp_dts_addlogentry] TO [db_dtsadmin] GRANT EXECUTE ON [dbo].[sp_dts_addlogentry] TO [db_dtsltduser] GRANT EXECUTE ON [dbo].[sp_dts_deletepackage] TO [db_dtsadmin] GRANT EXECUTE ON [dbo].[sp_dts_deletepackage] TO [db_dtsltduser] GRANT EXECUTE ON [dbo].[sp_dts_deletepackage] TO [db_dtsoperator] GRANT EXECUTE ON [dbo].[sp_dts_getpackage] TO [db_dtsadmin] GRANT EXECUTE ON [dbo].[sp_dts_getpackage] TO [db_dtsltduser] GRANT EXECUTE ON [dbo].[sp_dts_getpackage] TO [db_dtsoperator] GRANT EXECUTE ON [dbo].[sp_dts_listpackages] TO [db_dtsadmin] GRANT EXECUTE ON [dbo].[sp_dts_listpackages] TO [db_dtsltduser] GRANT EXECUTE ON [dbo].[sp_dts_listpackages] TO [db_dtsoperator] GRANT EXECUTE ON [dbo].[sp_dts_putpackage] TO [db_dtsadmin] GRANT EXECUTE ON [dbo].[sp_dts_putpackage] TO [db_dtsltduser] GRANT EXECUTE ON [dbo].[sp_dts_putpackage] TO [db_dtsoperator] GRANT EXECUTE ON [dbo].[sp_dts_checkexists] TO [db_dtsadmin] GRANT EXECUTE ON [dbo].[sp_dts_checkexists] TO [db_dtsltduser] GRANT EXECUTE ON [dbo].[sp_dts_checkexists] TO [db_dtsoperator] GRANT EXECUTE ON [dbo].[sp_dts_listfolders] TO [db_dtsadmin] GRANT EXECUTE ON [dbo].[sp_dts_listfolders] TO [db_dtsltduser] GRANT EXECUTE ON [dbo].[sp_dts_listfolders] TO [db_dtsoperator] GRANT EXECUTE ON [dbo].[sp_dts_deletefolder] TO [db_dtsadmin] GRANT EXECUTE ON [dbo].[sp_dts_deletefolder] TO [db_dtsltduser] GRANT EXECUTE ON [dbo].[sp_dts_addfolder] TO [db_dtsadmin] GRANT EXECUTE ON [dbo].[sp_dts_addfolder] TO [db_dtsltduser] GRANT EXECUTE ON [dbo].[sp_dts_renamefolder] TO [db_dtsadmin] GRANT EXECUTE ON [dbo].[sp_dts_renamefolder] TO [db_dtsltduser] GRANT EXECUTE ON [dbo].[sp_dts_getfolder] TO [db_dtsadmin] GRANT EXECUTE ON [dbo].[sp_dts_getfolder] TO [db_dtsltduser] GRANT EXECUTE ON [dbo].[sp_dts_getfolder] TO [db_dtsoperator] GRANT EXECUTE ON [dbo].[sp_dts_setpackageroles] TO [db_dtsadmin] GRANT EXECUTE ON [dbo].[sp_dts_setpackageroles] TO [db_dtsltduser] GRANT EXECUTE ON [dbo].[sp_dts_getpackageroles] TO [db_dtsadmin] GRANT EXECUTE ON [dbo].[sp_dts_getpackageroles] TO [db_dtsltduser] GO GRANT ALL ON [dbo].[sysdtslog90] TO [db_dtsadmin] GRANT INSERT ON [dbo].[sysdtslog90] TO [db_dtsltduser] GRANT SELECT ON [dbo].[sysdtslog90] TO [db_dtsltduser] GRANT INSERT ON [dbo].[sysdtslog90] TO [db_dtsoperator] GRANT SELECT ON [dbo].[sysdtslog90] TO [db_dtsoperator] 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 /**************************************************************/ /* Sign agent sps and add them to OBD component */ /* */ /* Also sign SPs for other components located in MSDB */ /**************************************************************/ PRINT 'Signing sps ...' -- Create certificate to sign Agent sps -- if exists (select * from sys.certificates where name = '##MS_AgentSigningCertificate##') drop certificate [##MS_AgentSigningCertificate##] create certificate [##MS_AgentSigningCertificate##] encryption by password = 'Yukon90_' with subject = 'MS_AgentSigningCertificate' IF (@@error <> 0) RAISERROR('Cannot create ##MS_AgentSigningCertificate## in msdb. INSTMSDB.SQL terminating.', 20, 127) WITH LOG go create table #sp_table (name sysname, sign int, comp int) go insert into #sp_table values(N'sp_sqlagent_is_srvrolemember', 1, 0) insert into #sp_table values(N'sp_verify_category_identifiers', 1, 0) insert into #sp_table values(N'sp_verify_proxy_identifiers', 1, 0) insert into #sp_table values(N'sp_verify_credential_identifiers', 1, 0) insert into #sp_table values(N'sp_verify_subsystem_identifiers', 1, 0) insert into #sp_table values(N'sp_verify_login_identifiers', 1, 0) insert into #sp_table values(N'sp_verify_proxy', 1, 0) insert into #sp_table values(N'sp_add_proxy', 1, 0) insert into #sp_table values(N'sp_delete_proxy', 1, 0) insert into #sp_table values(N'sp_update_proxy', 1, 0) insert into #sp_table values(N'sp_sqlagent_is_member', 1, 0) insert into #sp_table values(N'sp_verify_proxy_permissions', 1, 0) insert into #sp_table values(N'sp_help_proxy', 1, 0) insert into #sp_table values(N'sp_grant_proxy_to_subsystem', 1, 0) insert into #sp_table values(N'sp_grant_login_to_proxy', 1, 0) insert into #sp_table values(N'sp_revoke_login_from_proxy', 1, 0) insert into #sp_table values(N'sp_revoke_proxy_from_subsystem', 1, 0) insert into #sp_table values(N'sp_enum_proxy_for_subsystem', 1, 0) insert into #sp_table values(N'sp_enum_login_for_proxy', 1, 0) insert into #sp_table values(N'sp_sqlagent_get_startup_info', 1, 1) insert into #sp_table values(N'sp_sqlagent_has_server_access', 1, 1) insert into #sp_table values(N'sp_sem_add_message', 1, 0) insert into #sp_table values(N'sp_sem_drop_message', 1, 0) insert into #sp_table values(N'sp_get_message_description', 1, 0) insert into #sp_table values(N'sp_sqlagent_get_perf_counters', 1, 0) insert into #sp_table values(N'sp_sqlagent_notify', 1, 1) insert into #sp_table values(N'sp_is_sqlagent_starting', 1, 1) insert into #sp_table values(N'sp_verify_job_identifiers', 1, 0) insert into #sp_table values(N'sp_verify_schedule_identifiers', 1, 0) insert into #sp_table values(N'sp_verify_jobproc_caller', 1, 0) insert into #sp_table values(N'sp_downloaded_row_limiter', 1, 1) insert into #sp_table values(N'sp_post_msx_operation', 1, 1) insert into #sp_table values(N'sp_verify_performance_condition', 1, 0) insert into #sp_table values(N'sp_verify_job_date', 1, 0) insert into #sp_table values(N'sp_verify_job_time', 1, 0) insert into #sp_table values(N'sp_verify_alert', 1, 1) insert into #sp_table values(N'sp_update_alert', 1, 0) insert into #sp_table values(N'sp_delete_job_references', 1, 0) insert into #sp_table values(N'sp_delete_all_msx_jobs', 1, 0) insert into #sp_table values(N'sp_generate_target_server_job_assignment_sql', 1, 0) insert into #sp_table values(N'sp_generate_server_description', 1, 1) insert into #sp_table values(N'sp_msx_set_account', 1, 1) insert into #sp_table values(N'sp_msx_get_account', 1, 1) insert into #sp_table values(N'sp_delete_operator', 1, 0) insert into #sp_table values(N'sp_msx_defect', 1, 1) insert into #sp_table values(N'sp_msx_enlist', 1, 1) insert into #sp_table values(N'sp_delete_targetserver', 1, 0) insert into #sp_table values(N'sp_get_sqlagent_properties', 1, 1) insert into #sp_table values(N'sp_set_sqlagent_properties', 1, 1) insert into #sp_table values(N'sp_add_targetservergroup', 1, 0) insert into #sp_table values(N'sp_update_targetservergroup', 1, 0) insert into #sp_table values(N'sp_delete_targetservergroup', 1, 0) insert into #sp_table values(N'sp_help_targetservergroup', 1, 0) insert into #sp_table values(N'sp_add_targetsvrgrp_member', 1, 0) insert into #sp_table values(N'sp_delete_targetsvrgrp_member', 1, 0) insert into #sp_table values(N'sp_verify_category', 1, 0) insert into #sp_table values(N'sp_add_category', 1, 0) insert into #sp_table values(N'sp_update_category', 1, 0) insert into #sp_table values(N'sp_delete_category', 1, 0) insert into #sp_table values(N'sp_help_category', 1, 0) insert into #sp_table values(N'sp_help_targetserver', 1, 0) insert into #sp_table values(N'sp_resync_targetserver', 1, 0) insert into #sp_table values(N'sp_purge_jobhistory', 1, 0) insert into #sp_table values(N'sp_help_jobhistory', 1, 0) insert into #sp_table values(N'sp_add_jobserver', 1, 0) insert into #sp_table values(N'sp_delete_jobserver', 1, 0) insert into #sp_table values(N'sp_help_jobserver', 1, 0) insert into #sp_table values(N'sp_help_downloadlist', 1, 0) insert into #sp_table values(N'sp_enum_sqlagent_subsystems', 1, 0) insert into #sp_table values(N'sp_enum_sqlagent_subsystems_internal', 1, 0) insert into #sp_table values(N'sp_verify_subsystem', 1, 1) insert into #sp_table values(N'sp_verify_subsystems', 1, 0) insert into #sp_table values(N'sp_verify_schedule', 1, 0) insert into #sp_table values(N'sp_add_schedule', 1, 0) insert into #sp_table values(N'sp_attach_schedule', 1, 0) insert into #sp_table values(N'sp_detach_schedule', 1, 0) insert into #sp_table values(N'sp_update_schedule', 1, 0) insert into #sp_table values(N'sp_delete_schedule', 1, 0) insert into #sp_table values(N'sp_get_jobstep_db_username', 1, 0) insert into #sp_table values(N'sp_verify_jobstep', 1, 0) insert into #sp_table values(N'sp_add_jobstep_internal', 1, 0) insert into #sp_table values(N'sp_add_jobstep', 1, 0) insert into #sp_table values(N'sp_update_jobstep', 1, 0) insert into #sp_table values(N'sp_delete_jobstep', 1, 0) insert into #sp_table values(N'sp_help_jobstep', 1, 0) insert into #sp_table values(N'sp_write_sysjobstep_log', 1, 0) insert into #sp_table values(N'sp_help_jobsteplog', 1, 0) insert into #sp_table values(N'sp_delete_jobsteplog', 1, 0) insert into #sp_table values(N'sp_get_schedule_description', 1, 1) insert into #sp_table values(N'sp_add_jobschedule', 1, 0) insert into #sp_table values(N'sp_update_replication_job_parameter', 1, 0) insert into #sp_table values(N'sp_update_jobschedule', 1, 0) insert into #sp_table values(N'sp_delete_jobschedule', 1, 0) insert into #sp_table values(N'sp_help_schedule', 1, 0) insert into #sp_table values(N'sp_help_jobschedule', 1, 0) insert into #sp_table values(N'sp_verify_job', 1, 1) insert into #sp_table values(N'sp_add_job', 1, 0) insert into #sp_table values(N'sp_update_job', 1, 0) insert into #sp_table values(N'sp_delete_job', 1, 0) insert into #sp_table values(N'sp_get_composite_job_info', 1, 1) insert into #sp_table values(N'sp_help_job', 1, 0) insert into #sp_table values(N'sp_help_jobcount ', 1, 0) insert into #sp_table values(N'sp_help_jobs_in_schedule', 1, 0) insert into #sp_table values(N'sp_manage_jobs_by_login', 1, 0) insert into #sp_table values(N'sp_apply_job_to_targets', 1, 0) insert into #sp_table values(N'sp_remove_job_from_targets', 1, 0) insert into #sp_table values(N'sp_get_job_alerts', 1, 0) insert into #sp_table values(N'sp_convert_jobid_to_char', 1, 0) insert into #sp_table values(N'sp_start_job', 1, 0) insert into #sp_table values(N'sp_stop_job', 1, 0) insert into #sp_table values(N'sp_cycle_agent_errorlog', 1, 0) insert into #sp_table values(N'sp_get_chunked_jobstep_params', 1, 0) insert into #sp_table values(N'sp_check_for_owned_jobs', 1, 0) insert into #sp_table values(N'sp_check_for_owned_jobsteps', 1, 0) insert into #sp_table values(N'sp_sqlagent_refresh_job', 1, 0) insert into #sp_table values(N'sp_jobhistory_row_limiter', 1, 1) insert into #sp_table values(N'sp_sqlagent_log_jobhistory', 1, 0) insert into #sp_table values(N'sp_sqlagent_check_msx_version', 1, 0) insert into #sp_table values(N'sp_sqlagent_probe_msx', 1, 0) insert into #sp_table values(N'sp_set_local_time', 1, 1) insert into #sp_table values(N'sp_multi_server_job_summary', 1, 0) insert into #sp_table values(N'sp_target_server_summary', 1, 0) insert into #sp_table values(N'sp_uniquetaskname', 1, 0) insert into #sp_table values(N'sp_addtask', 1, 0) insert into #sp_table values(N'sp_droptask', 1, 0) insert into #sp_table values(N'sp_add_alert_internal', 1, 0) insert into #sp_table values(N'sp_add_alert', 1, 0) insert into #sp_table values(N'sp_delete_alert', 1, 0) insert into #sp_table values(N'sp_help_alert', 1, 0) insert into #sp_table values(N'sp_verify_operator', 1, 0) insert into #sp_table values(N'sp_add_operator', 1, 0) insert into #sp_table values(N'sp_update_operator', 1, 1) insert into #sp_table values(N'sp_help_operator', 1, 0) insert into #sp_table values(N'sp_help_operator_jobs', 1, 0) insert into #sp_table values(N'sp_verify_operator_identifiers', 1, 0) insert into #sp_table values(N'sp_notify_operator', 1, 0) insert into #sp_table values(N'sp_verify_notification', 1, 0) insert into #sp_table values(N'sp_add_notification', 1, 0) insert into #sp_table values(N'sp_update_notification', 1, 0) insert into #sp_table values(N'sp_delete_notification', 1, 0) insert into #sp_table values(N'sp_help_notification', 1, 0) insert into #sp_table values(N'sp_help_jobactivity', 1, 0) insert into #sp_table values(N'sp_enlist_tsx', 1, 1) insert into #sp_table values(N'trig_targetserver_insert', 1, 0) -- Database Mail configuration procs insert into #sp_table values(N'sysmail_verify_accountparams_sp', 1, 0) insert into #sp_table values(N'sysmail_verify_principal_sp', 1, 0) insert into #sp_table values(N'sysmail_verify_profile_sp', 1, 0) insert into #sp_table values(N'sysmail_verify_account_sp', 1, 0) insert into #sp_table values(N'sysmail_add_profile_sp', 1, 0) insert into #sp_table values(N'sysmail_update_profile_sp', 1, 0) insert into #sp_table values(N'sysmail_delete_profile_sp', 1, 0) insert into #sp_table values(N'sysmail_help_profile_sp', 1, 0) insert into #sp_table values(N'sysmail_create_user_credential_sp', 1, 0) insert into #sp_table values(N'sysmail_alter_user_credential_sp', 1, 0) insert into #sp_table values(N'sysmail_drop_user_credential_sp', 1, 0) insert into #sp_table values(N'sysmail_add_account_sp', 1, 0) insert into #sp_table values(N'sysmail_update_account_sp', 1, 0) insert into #sp_table values(N'sysmail_delete_account_sp', 1, 0) insert into #sp_table values(N'sysmail_help_account_sp', 1, 0) insert into #sp_table values(N'sysmail_help_admin_account_sp', 1, 0) insert into #sp_table values(N'sysmail_add_profileaccount_sp', 1, 0) insert into #sp_table values(N'sysmail_update_profileaccount_sp', 1, 0) insert into #sp_table values(N'sysmail_delete_profileaccount_sp', 1, 0) insert into #sp_table values(N'sysmail_help_profileaccount_sp', 1, 0) insert into #sp_table values(N'sysmail_configure_sp', 1, 0) insert into #sp_table values(N'sysmail_help_configure_sp', 1, 0) insert into #sp_table values(N'sysmail_help_configure_value_sp', 1, 0) insert into #sp_table values(N'sysmail_add_principalprofile_sp', 1, 0) insert into #sp_table values(N'sysmail_update_principalprofile_sp', 1, 0) insert into #sp_table values(N'sysmail_delete_principalprofile_sp', 1, 0) insert into #sp_table values(N'sysmail_help_principalprofile_sp', 1, 0) -- Database Mail: mail host database specific procs insert into #sp_table values(N'sysmail_start_sp', 1, 2) insert into #sp_table values(N'sysmail_stop_sp', 1, 2) insert into #sp_table values(N'sysmail_logmailevent_sp', 1, 0) insert into #sp_table values(N'sp_SendMailMessage', 1, 0) insert into #sp_table values(N'sp_isprohibited', 1, 0) insert into #sp_table values(N'sp_SendMailQueues', 1, 0) insert into #sp_table values(N'sp_ProcessResponse', 1, 0) insert into #sp_table values(N'sp_MailItemResultSets', 1, 0) insert into #sp_table values(N'sp_process_DialogTimer', 1, 0) insert into #sp_table values(N'sp_readrequest', 1, 0) insert into #sp_table values(N'sp_GetAttachmentData', 1, 0) insert into #sp_table values(N'sp_RunMailQuery', 1, 0) insert into #sp_table values(N'sysmail_help_queue_sp', 1, 0) insert into #sp_table values(N'sysmail_help_status_sp', 1, 2) insert into #sp_table values(N'sysmail_delete_mailitems_sp', 1, 0) insert into #sp_table values(N'sysmail_delete_log_sp', 1, 0) insert into #sp_table values(N'sp_send_dbmail', 1, 2) insert into #sp_table values(N'sp_ExternalMailQueueListener', 1, 0) insert into #sp_table values(N'sp_sysmail_activate', 1, 0) insert into #sp_table values(N'sp_get_script', 1, 0) -- Maintenance Plans insert into #sp_table values(N'sp_maintplan_delete_log', 1, 0) insert into #sp_table values(N'sp_maintplan_delete_subplan', 1, 0) insert into #sp_table values(N'sp_maintplan_open_logentry', 1, 0) insert into #sp_table values(N'sp_maintplan_close_logentry', 1, 0) insert into #sp_table values(N'sp_maintplan_update_log', 1, 0) insert into #sp_table values(N'sp_maintplan_update_subplan', 1, 0) insert into #sp_table values(N'sp_maintplan_delete_plan', 1, 0) insert into #sp_table values(N'sp_maintplan_start', 1, 0) insert into #sp_table values(N'sp_clear_dbmaintplan_by_db', 1, 0) insert into #sp_table values(N'sp_add_maintenance_plan', 1, 0) insert into #sp_table values(N'sp_delete_maintenance_plan', 1, 0) insert into #sp_table values(N'sp_add_maintenance_plan_db', 1, 0) insert into #sp_table values(N'sp_delete_maintenance_plan_db', 1, 0) insert into #sp_table values(N'sp_add_maintenance_plan_job', 1, 1) insert into #sp_table values(N'sp_delete_maintenance_plan_job', 1, 0) insert into #sp_table values(N'sp_help_maintenance_plan', 1, 0) -- Log Shipping insert into #sp_table values(N'sp_add_log_shipping_monitor_jobs', 1, 0) insert into #sp_table values(N'sp_add_log_shipping_primary', 1, 0) insert into #sp_table values(N'sp_add_log_shipping_secondary', 1, 0) insert into #sp_table values(N'sp_delete_log_shipping_monitor_jobs', 1, 0) insert into #sp_table values(N'sp_delete_log_shipping_primary', 1, 0) insert into #sp_table values(N'sp_delete_log_shipping_secondary ', 1, 0) insert into #sp_table values(N'sp_log_shipping_in_sync', 1, 0) insert into #sp_table values(N'sp_log_shipping_get_date_from_file ', 1, 0) insert into #sp_table values(N'sp_get_log_shipping_monitor_info', 1, 0) insert into #sp_table values(N'sp_update_log_shipping_monitor_info', 1, 0) insert into #sp_table values(N'sp_delete_log_shipping_monitor_info', 1, 0) insert into #sp_table values(N'sp_remove_log_shipping_monitor_account', 1, 0) insert into #sp_table values(N'sp_log_shipping_monitor_backup', 1, 0) insert into #sp_table values(N'sp_log_shipping_monitor_restore', 1, 0) insert into #sp_table values(N'sp_change_monitor_role', 1, 0) insert into #sp_table values(N'sp_create_log_shipping_monitor_account', 1, 0) -- DTS insert into #sp_table values(N'sp_get_dtsversion', 1, 0) insert into #sp_table values(N'sp_make_dtspackagename', 1, 0) insert into #sp_table values(N'sp_add_dtspackage', 1, 0) insert into #sp_table values(N'sp_drop_dtspackage', 1, 0) insert into #sp_table values(N'sp_reassign_dtspackageowner', 1, 0) insert into #sp_table values(N'sp_get_dtspackage', 1, 0) insert into #sp_table values(N'sp_reassign_dtspackagecategory', 1, 0) insert into #sp_table values(N'sp_enum_dtspackages', 1, 0) insert into #sp_table values(N'sp_add_dtscategory', 1, 0) insert into #sp_table values(N'sp_drop_dtscategory', 1, 0) insert into #sp_table values(N'sp_modify_dtscategory', 1, 0) insert into #sp_table values(N'sp_enum_dtscategories', 1, 0) insert into #sp_table values(N'sp_log_dtspackage_begin', 1, 0) insert into #sp_table values(N'sp_log_dtspackage_end', 1, 0) insert into #sp_table values(N'sp_log_dtsstep_begin', 1, 0) insert into #sp_table values(N'sp_log_dtsstep_end', 1, 0) insert into #sp_table values(N'sp_log_dtstask', 1, 0) insert into #sp_table values(N'sp_enum_dtspackagelog', 1, 0) insert into #sp_table values(N'sp_enum_dtssteplog', 1, 0) insert into #sp_table values(N'sp_enum_dtstasklog', 1, 0) insert into #sp_table values(N'sp_dump_dtslog_all', 1, 0) insert into #sp_table values(N'sp_dump_dtspackagelog', 1, 0) insert into #sp_table values(N'sp_dump_dtssteplog', 1, 0) insert into #sp_table values(N'sp_dump_dtstasklog', 1, 0) insert into #sp_table values(N'sp_dts_addlogentry', 1, 0) insert into #sp_table values(N'sp_dts_listpackages', 1, 0) insert into #sp_table values(N'sp_dts_listfolders', 1, 0) insert into #sp_table values(N'sp_dts_deletepackage', 1, 0) insert into #sp_table values(N'sp_dts_deletefolder', 1, 0) insert into #sp_table values(N'sp_dts_getpackage', 1, 0) insert into #sp_table values(N'sp_dts_getfolder', 1, 0) insert into #sp_table values(N'sp_dts_putpackage', 1, 0) insert into #sp_table values(N'sp_dts_addfolder', 1, 0) insert into #sp_table values(N'sp_dts_renamefolder', 1, 0) insert into #sp_table values(N'sp_dts_setpackageroles', 1, 0) insert into #sp_table values(N'sp_dts_getpackageroles', 1, 0) go BEGIN TRANSACTION declare @sp sysname declare @exec_str nvarchar(1024) declare @sign int declare @comp int declare ms_crs_sps cursor global for select name, sign, comp from #sp_table open ms_crs_sps fetch next from ms_crs_sps into @sp, @sign, @comp while @@fetch_status = 0 begin if exists(select * from sys.objects where name = @sp) begin print 'processing sp: ' + @sp if (@sign = 1) begin set @exec_str = N'add signature to ' + @sp + N' by certificate [##MS_AgentSigningCertificate##] with password = ''Yukon90_''' Execute(@exec_str) if (@@error <> 0) begin RAISERROR('Cannot sign stored procedure. INSTMSDB.SQL terminating.', 20, 127) WITH LOG ROLLBACK TRANSACTION return end end if (@comp > 0) begin if (@comp = 1) -- SQLAgent set @exec_str = N'exec sp_AddFunctionalUnitToComponent N''Agent XPs'', N''' + @sp + N'''' else if (@comp = 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, @comp end close ms_crs_sps deallocate ms_crs_sps COMMIT TRANSACTION go drop table #sp_table go -- drop certificate private key alter certificate [##MS_AgentSigningCertificate##] remove private key IF (@@error <> 0) RAISERROR('Cannot create ##MS_AgentSigningCertificate## in msdb. INSTMSDB.SQL terminating.', 20, 127) WITH LOG go --create a temporary database in order to get the path to the 'Data' folder --because upon upgrade existing database are in temporary folder IF (EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE (name = N'temp_MS_AgentSigningCertificate_database'))) BEGIN DROP DATABASE temp_MS_AgentSigningCertificate_database END go CREATE DATABASE temp_MS_AgentSigningCertificate_database go -- export certificate to master -- use current directory to persist the file -- DECLARE @certificate_name NVARCHAR(520) DECLARE @certificate_nameQuoted NVARCHAR(1042) -- twice the length + 2, in case every character was a quote SELECT @certificate_name = SUBSTRING(filename, 1, CHARINDEX(N'temp_MS_AgentSigningCertificate_database.mdf', filename) - 1) + CONVERT(NVARCHAR(520), NEWID()) + N'.cer' FROM master.dbo.sysaltfiles WHERE (name = N'temp_MS_AgentSigningCertificate_database') -- Handle single quotes in the directory name (can't use QUOTENAME in case name over 128 chars) SELECT @certificate_nameQuoted = '''' + REPLACE(@certificate_name, '''', '''''') + '''' EXECUTE(N'backup certificate [##MS_AgentSigningCertificate##] to file = ' + @certificate_nameQuoted) IF (@@error <> 0) RAISERROR('Cannot backup ##MS_AgentSigningCertificate##. SYSDBUPG.SQL terminating.', 20, 127) WITH LOG 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##] execute(N'create certificate [##MS_AgentSigningCertificate##] from file = ' + @certificate_nameQuoted) IF (@@error <> 0) RAISERROR('Cannot create ##MS_AgentSigningCertificate## certificate in master. SYSDBUPG.SQL terminating.', 20, 127) WITH LOG -- create login -- 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##] use msdb go -- 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 go PRINT 'Succesfully signed sps' -- -- End of signing sps go if not exists (select * from [dbo].[sysdtspackagefolders90] 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_dts_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_dts_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 PRINT '' PRINT '---------------------------------------------' PRINT 'Execution of Shiloh-era INSTMSDB.SQL complete' PRINT '---------------------------------------------' go CHECKPOINT go use msdb go DECLARE @CMDExec sysname EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent\SubSystems', N'CmdExec', @CMDExec OUTPUT, N'no_output' IF @CMDExec IS NOT NULL BEGIN PRINT '' PRINT 'Remove subsystem definition from registry...' --remove subsystem definition from registry and populate subsystem table DECLARE @ret INT EXEC @ret = master..xp_instance_regdeletekey N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\SQLServerAgent\SubSystems' IF @ret <> 0 RAISERROR('Failed to remove subsystems definition from registry', 10, 127) END PRINT '' PRINT 'Populate syssubsystem table...' EXEC @ret = sp_enum_sqlagent_subsystems IF @ret <> 0 RAISERROR('Failed to add subsystems definition to syssubsystem table. Upgrade script terminating', 20, 127) WITH LOG go --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 static tokens PRINT '' PRINT 'Creating function UpgradeStaticTockens...' go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'UpgradeStaticTockens') AND (type = 'FN'))) DROP FUNCTION dbo.UpgradeStaticTockens go CREATE FUNCTION UpgradeStaticTockens(@n nvarchar(4000)) RETURNS nvarchar(4000) AS BEGIN SELECT @n = REPLACE (@n, N'[<#A#>]', N'$(<#A#>)') SELECT @n = REPLACE (@n, N'[<#S#>]', N'$(<#S#>)') SELECT @n = REPLACE (@n, N'[A-DBN]', N'$(A-DBN)') SELECT @n = REPLACE (@n, N'[A-SVR]', N'$(A-SVR)') SELECT @n = REPLACE (@n, N'[A-ERR]', N'$(A-ERR)') SELECT @n = REPLACE (@n, N'[A-SEV]', N'$(A-SEV)') SELECT @n = REPLACE (@n, N'[A-MSG]', N'$(A-MSG)') SELECT @n = REPLACE (@n, N'[JOBID]', N'$(JOBID)') SELECT @n = REPLACE (@n, N'[STEPID]', N'$(STEPID)') SELECT @n = REPLACE (@n, N'[DATE]', N'$(DATE)') SELECT @n = REPLACE (@n, N'[TIME]', N'$(TIME)') SELECT @n = REPLACE (@n, N'[MACH]', N'$(MACH)') SELECT @n = REPLACE (@n, N'[MSSA]', N'$(MSSA)') SELECT @n = REPLACE (@n, N'[STEPCT]', N'$(STEPCT)') SELECT @n = REPLACE (@n, N'[SQLDIR]', N'$(SQLDIR)') SELECT @n = REPLACE (@n, N'[STRTDT]', N'$(STRTDT)') SELECT @n = REPLACE (@n, N'[STRTTM]', N'$(STRTTM)') SELECT @n = REPLACE (@n, N'[LOGIN]', N'$(LOGIN)') SELECT @n = REPLACE (@n, N'[PSWD]', N'$(PSWD)') SELECT @n = REPLACE (@n, N'[OSCMD]', N'$(OSCMD)') SELECT @n = REPLACE (@n, N'[SRVR]', N'$(SRVR)') SELECT @n = REPLACE (@n, N'[INST]', N'$(INST)') RETURN @n END go PRINT '' PRINT 'Updating static tokens...' UPDATE sysjobsteps SET command = dbo.UpgradeStaticTockens(command) UPDATE sysjobsteps SET output_file_name = dbo.UpgradeStaticTockens(output_file_name) UPDATE sysalerts SET notification_message = dbo.UpgradeStaticTockens(notification_message) go PRINT '' PRINT 'Dropping function UpgradeStaticTockens...' DROP FUNCTION UpgradeStaticTockens 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) 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\sqlagent90_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\sqlagent90_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 -- Yukon LS upgrade declare @retcode int PRINT '' PRINT 'Upgrading SQL Server 2005 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 2005 did not complete successfully. After upgrade, re-execute sp_upgrade_log_shipping from master database.', 10, 1) WITH LOG end PRINT 'Upgraded SQL Server 2005 Log Shipping successfully.' go ----------------------------------------------------------------------------- --Drop legacy objects CREATE TABLE #instmsdb_legacy_objects(object_name sysname, type_name sysname, type_id nvarchar(2)) --tables --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_resync_targetserver', 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 POSTINSTMSDB90.SQL complete' PRINT '----------------------------------' go Print 'Upgrading Database Mail related objects...' /* One of the main functions of this script is to handle migration of multiple Mail Host database to a single Mail Host Database in MSDB. Pre Beta 3 (CPT 15) versions of SQL Server 2005 allowed Databae Mail object to exist in any database. This script will migrate previously created profile, SentItems and log information to MSDB. Finally the script will remove Database Mail objects in these other databases. Another goal of this script is to handle all metadata changes between different releases of the product starting with B-2 through subsequent CTPs */ USE msdb SET NOCOUNT ON -- remove obsolete configuration parameter IF EXISTS(SELECT * FROM msdb.dbo.sysmail_configuration WHERE paramname = N'MaxNumberOfMailsPerDay') DELETE FROM msdb.dbo.sysmail_configuration WHERE paramname=N'MaxNumberOfMailsPerDay' /* Upgrade objects in MSDB if it was previously used as mailhost database */ BEGIN TRY IF EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='profile_name' and id = (SELECT OBJECT_ID(N'dbo.sysmail_mailitems', 'U'))) BEGIN -- convert data from profile_name column to profile_id column exec sp_executesql N' DECLARE @profile_name sysname DECLARE @profile_id int DECLARE profile_name_cursor CURSOR LOCAL FOR SELECT sp.profile_id, sp.name FROM dbo.sysmail_profile sp, dbo.sysmail_mailitems mi WHERE sp.name = mi.profile_name AND mi.profile_name IS NOT NULL OPEN profile_name_cursor FETCH NEXT FROM profile_name_cursor INTO @profile_id, @profile_name WHILE (@@fetch_status = 0) BEGIN UPDATE dbo.sysmail_mailitems SET profile_id = @profile_id WHERE profile_name = @profile_name FETCH NEXT FROM profile_name_cursor INTO @profile_id, @profile_name END Close profile_name_cursor DEALLOCATE profile_name_cursor' -- remove obsolete column ALTER TABLE dbo.sysmail_mailitems DROP COLUMN profile_name END END TRY BEGIN CATCH print 'There was a problem upgrading MSDB mail host database.' print 'Unable to map existing profile name values to profile_id column' print 'Error State: ' + convert(varchar(10),ERROR_STATE() ) print 'Error Message: ' + ERROR_MESSAGE() print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY()) END CATCH GO if EXISTS(Select * from msdb.dbo.sysmail_principalprofile) BEGIN BEGIN TRY -- add existing mail users defined in MSDB to new role DECLARE @DBMailUser sysname DECLARE principal_profile_cursor CURSOR LOCAL FOR SELECT dp.name FROM dbo.sysmail_principalprofile pp INNER JOIN sys.database_principals dp ON pp.principal_sid = dp.sid and dp.principal_id > 4 OPEN principal_profile_cursor FETCH NEXT FROM principal_profile_cursor INTO @DBMailUser WHILE (@@fetch_status = 0) BEGIN EXEC msdb..sp_addrolemember @rolename = 'DatabaseMailUserRole',@membername = @DBMailUser FETCH NEXT FROM principal_profile_cursor INTO @DBMailUser END CLOSE principal_profile_cursor DEALLOCATE principal_profile_cursor END TRY BEGIN CATCH print 'There was a problem upgrading MSDB mail host database.' print 'Unable to add existing mail user to DatabaseMailUserRole' print 'Error State: ' + convert(varchar(10),ERROR_STATE() ) print 'Error Message: ' + ERROR_MESSAGE() print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY()) END CATCH END GO if EXISTS(Select * from msdb.dbo.sysmail_principalprofile) BEGIN BEGIN TRY -- cleaning up database mail related broker conversations if( SELECT is_broker_enabled from msdb.sys.databases WHERE name = 'msdb' ) = 0 BEGIN PRINT 'NEED TO RE-ENABLE SSB' WHILE(1=1) BEGIN DECLARE @handle UNIQUEIDENTIFIER SET @handle = NULL SELECT TOP(1) @handle=conversation_handle FROM msdb.sys.conversation_endpoints WHERE (msdb.sys.conversation_endpoints.far_service IN ('SQL/Notifications/SysMailNotification/v1.0', 'ExternalMailService', 'InternalMailService', 'SqlQueryNotificationService', 'iMailRequestorService', 'iMailResponderService', 'http://schemas.microsoft.com/SQL/Notifications/EventNotificationService', 'http://schemas.microsoft.com/SQL/Notifications/PostEventNotification' ) ) IF @handle IS NULL BREAK PRINT 'ENDING CONVERSATION ' + convert(varchar(256),@handle) END CONVERSATION @handle WITH CLEANUP END END -- We cannot turn the broker on, because we don't know whether it was disabled by the user -- ALTER DATABASE msdb SET ENABLE_BROKER END TRY BEGIN CATCH print 'There was a problem upgrading Mail Host databases.' print 'Unable to re-enable server broker. You might need to manually' print 'end any pending secure conversations.' print 'Error State: ' + convert(varchar(10),ERROR_STATE() ) print 'Error Message: ' + ERROR_MESSAGE() print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY()) END CATCH END GO if EXISTS(SELECT * FROM msdb.dbo.sysmail_principalprofile) AND EXISTS(SELECT * FROM msdb.dbo.syscolumns WHERE name='database_id' and id =(SELECT OBJECT_ID(N'dbo.sysmail_principalprofile', 'U'))) BEGIN /* Handle migration of multiple mail host databases into MSDB */ BEGIN TRY DECLARE @DBName sysname DECLARE @DBNameQuote sysname DECLARE @sql nvarchar(max) DECLARE @new_mailitem_id int DECLARE @old_mailitem_id int DECLARE @profile_name_exists bit DECLARE @Error bit DECLARE @db_id int DECLARE DBName_Cursor CURSOR FOR select name from sys.databases WHERE name NOT IN ('msdb', 'tempdb', 'master') and HAS_DBACCESS([name]) <> 0 OPEN DBName_Cursor FETCH NEXT FROM DBName_Cursor INTO @DBName WHILE @@FETCH_STATUS = 0 BEGIN Print 'Checking database: ' + @DBName SET @DBNameQuote = QUOTENAME(@DBName) SELECT @db_id = db_id(@DBName) IF ( OBJECT_ID(@DBNameQuote + '.dbo.sysmail_log') IS NOT NULL) --Determine if Database Mail objects exist BEGIN Print @DBName + ' is a MailHost database.' --Going to migrate profiles from database to MSDB DECLARE @DBMailUser sysname DECLARE @sid_MSDB varbinary(85) DECLARE @sid_principal varbinary(85) DECLARE @old_profile_id int DECLARE @new_principal_id int DECLARE @old_principal_id int DECLARE @LoginName sysname SET @sql = N'DECLARE DBMail_Cursor CURSOR FOR SELECT p.name , pp.profile_id , msdb_p.sid , p.sid , pp.principal_id FROM msdb..sysmail_principalprofile pp JOIN '+ @DBNameQuote +'.sys.database_principals p ON pp.principal_id = p.principal_id LEFT JOIN msdb.sys.database_principals msdb_p ON p.sid = msdb_p.sid where pp.database_id = ' + convert(nvarchar(10),@db_id) --print @sql EXEC(@sql) OPEN DBMail_Cursor FETCH NEXT FROM DBMail_Cursor INTO @DBMailUser, @old_profile_id, @sid_MSDB, @sid_principal, @old_principal_id WHILE @@FETCH_STATUS = 0 BEGIN Print 'Going to process user: ' + @DBMailUser IF (@sid_MSDB IS NULL) -- does not already exist in MSDB BEGIN IF (NOT EXISTS(Select * from sysusers where name = @DBMailUser)) BEGIN IF (EXISTS(Select * from master.dbo.syslogins where sid = @sid_principal)) BEGIN /* Determine the Login Name from the SID */ SELECT @LoginName = name FROM master.dbo.syslogins WHERE sid = @sid_principal PRINT 'Add USER to MSDB: ' + @DBMailUser SET @sql = N'CREATE USER ' + QUOTENAME(@DBMailUser) + ' FOR LOGIN ' + QUOTENAME(@LoginName) EXEC (@sql) IF (@old_principal_id > 4) -- do not add special accounts such as dbo, public, sys BEGIN Print 'Grant USER permission to send mail.' exec msdb..sp_addrolemember @rolename = 'DatabaseMailUserRole',@membername = @DBMailUser END END ELSE BEGIN PRINT 'Can not add user as the login does not exist.' END END ELSE BEGIN Print 'User has already been added to MSDB: ' + @DBMailUser IF (@old_principal_id > 4) -- do not add special accounts such as dbo, public, sys BEGIN PRINT 'Ensure user has the right to send mail.' exec msdb..sp_addrolemember @rolename = 'DatabaseMailUserRole',@membername = @DBMailUser END END END ELSE BEGIN Print 'Login already mapped to a user in MSDB' END IF (EXISTS(Select * from master.dbo.syslogins where sid = @sid_principal)) BEGIN --Get principle_id SELECT @new_principal_id = principal_id FROM msdb.sys.database_principals Where sid = @sid_principal IF (@new_principal_id is not Null) BEGIN print 'New Principal_id: ' + Convert(varchar(10),@new_principal_id) + ' Old profile_id: ' + convert(varchar(10),@old_profile_id) + ' Old principal id: ' + convert(varchar(10),@old_principal_id) SET @sql = N' IF NOT EXISTS(select * from msdb..sysmail_principalprofile Where profile_id = ' + Convert(varchar(10),@old_profile_id) + ' AND database_id = 4 AND principal_id = ' + Convert(varchar(10),@new_principal_id) + ') BEGIN --Update the Profile UPDATE msdb..sysmail_principalprofile SET database_id = 4 , principal_id = ' + Convert(varchar(10),@new_principal_id) + ' Where profile_id = ' + Convert(varchar(10),@old_profile_id) + ' AND database_id = ' + Convert(varchar(10),@db_id) + ' AND principal_id = ' + Convert(varchar(10),@old_principal_id) + ' IF (@@rowcount = 1) BEGIN Print ''sysmail_principalprofile updated based on moving user to MSDB.'' END END ELSE BEGIN Print ''This user already has already been configured with this profile in MSDB.'' END' EXEC(@sql) END ELSE BEGIN Print 'sysmail_principalprofile table can not be updated for sid: ' + convert(varchar(100),@sid_principal) END END FETCH NEXT FROM DBMail_Cursor INTO @DBMailUser, @old_profile_id, @sid_MSDB, @sid_principal, @old_principal_id END CLOSE DBMail_Cursor DEALLOCATE DBMail_Cursor --/* Move Data from user Mail Host database to MSDB */ --Print 'Move Data from user Mail Host database to MSDB.' SET @sql = N'DECLARE mailitem_id_Cursor CURSOR FOR SELECT mailitem_id FROM ' + @DBNameQuote + '.dbo.sysmail_mailitems' EXEC(@sql) OPEN mailitem_id_Cursor Set @Error = 0 --Assume no errors BEGIN TRANSACTION /* Disable Trigger so the "last_mod_date" and "last_mod_user colums" are not updated during transfer. */ EXEC ('DISABLE TRIGGER [dbo].[trig_sysmail_attachments] ON [dbo].[sysmail_attachments]') EXEC ('DISABLE TRIGGER [dbo].[trig_sysmail_log] ON [dbo].[sysmail_log]') EXEC ('DISABLE TRIGGER [dbo].[trig_sysmail_mailitems]ON [dbo].[sysmail_mailitems]') Print 'Going to move ALL sysmail_log items not associated with a mailitem' SET @sql = N'INSERT INTO msdb.dbo.sysmail_log (event_type, log_date, description, process_id, mailitem_id, account_id, last_mod_date, last_mod_user) SELECT event_type, log_date, description, process_id, NULL, account_id, last_mod_date, last_mod_user FROM '+ @DBNameQuote +'.dbo.sysmail_log WHERE mailitem_id IS NULL ' exec(@sql) FETCH NEXT FROM mailitem_id_Cursor INTO @old_mailitem_id WHILE @@FETCH_STATUS = 0 BEGIN /****************************************/ /* MOVE dbo.sysmail_mailitems DATA */ /****************************************/ /* Need to check the schema defination of the table */ SET @sql = N'USE ' + @DBNameQuote + ' DECLARE @sql nvarchar(max) IF (EXISTS(select * from syscolumns where id = object_id(''[dbo].[sysmail_mailitems]'') AND name = ''profile_name'')) BEGIN SET @sql = N''INSERT INTO msdb.dbo.sysmail_mailitems (profile_id, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_date, send_request_user, sent_account_id, sent_status, sent_date, last_mod_date, last_mod_user) SELECT CASE WHEN p.profile_id IS NULL THEN -1 ELSE p.profile_id END, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_date, send_request_user, sent_account_id, sent_status, sent_date, last_mod_date, mi.last_mod_user FROM '+ @DBNameQuote +'.dbo.sysmail_mailitems mi LEFT JOIN msdb..sysmail_profile p ON mi.profile_name = p.name WHERE mailitem_id = ' + CONVERT(VARCHAR(20),@old_mailitem_id) + ''' END ELSE BEGIN SET @sql = N''INSERT INTO msdb.dbo.sysmail_mailitems (profile_id, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_date, send_request_user, sent_account_id, sent_status, sent_date, last_mod_date, last_mod_user) SELECT profile_id, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_date, send_request_user, sent_account_id, sent_status, sent_date, last_mod_date, last_mod_user FROM '+ @DBNameQuote +'.dbo.sysmail_mailitems WHERE mailitem_id = ' + CONVERT(VARCHAR(20),@old_mailitem_id) + ''' END EXEC(@sql)' EXEC(@sql) IF (@@error <> 0) --Check for error BEGIN Set @Error = 1 END SELECT @new_mailitem_id = @@identity /****************************************/ /* MOVE dbo.sysmail_log DATA */ /****************************************/ SET @sql = N'INSERT INTO msdb.dbo.sysmail_log (event_type, log_date, description, process_id, mailitem_id, account_id, last_mod_date, last_mod_user) SELECT event_type, log_date, description, process_id, ' + convert(varchar(5),@new_mailitem_id) + ', account_id, last_mod_date, last_mod_user FROM '+ @DBNameQuote +'.dbo.sysmail_log WHERE mailitem_id = ' + CONVERT(VARCHAR(20),@old_mailitem_id) EXEC(@sql) IF @@error <> 0 --Check for error BEGIN Set @Error = 1 END /****************************************/ /* MOVE dbo.sysmail_attachments DATA */ /****************************************/ SET @sql = N'USE ' + @DBNameQuote + ' IF (object_id(''dbo.sysmail_attachments'') IS NOT NULL) begin INSERT INTO msdb.dbo.sysmail_attachments (mailitem_id, filename, filesize, attachment, last_mod_date, last_mod_user) SELECT ' + convert(varchar(5),@new_mailitem_id) + ', filename, filesize, attachment, last_mod_date, last_mod_user FROM '+ @DBNameQuote +'.dbo.sysmail_attachments WHERE mailitem_id = ' + CONVERT(VARCHAR(20),@old_mailitem_id) + ' end' EXEC(@sql) IF @@error <> 0 --Check for error BEGIN Set @Error = 1 END FETCH NEXT FROM mailitem_id_Cursor INTO @old_mailitem_id END IF @Error = 0 BEGIN Print 'Completed data transfer to MSDB.' COMMIT TRANSACTION END ELSE BEGIN Print 'Not able to complete data transfer to MSDB.' ROLLBACK TRANSACTION END /* ENABLE Triggers as they were previously DISABLE */ EXEC ('ENABLE TRIGGER [dbo].[trig_sysmail_attachments] ON [dbo].[sysmail_attachments]') EXEC ('ENABLE TRIGGER [dbo].[trig_sysmail_log] ON [dbo].[sysmail_log]') EXEC ('ENABLE TRIGGER [dbo].[trig_sysmail_mailitems]ON [dbo].[sysmail_mailitems]') CLOSE mailitem_id_Cursor DEALLOCATE mailitem_id_Cursor IF @Error = 0 BEGIN /**********************************************************************/ /* */ /* Uninstalls the tables, triggers and stored procedures necessary for*/ /* sqlimail operations */ /* */ /* ** Copyright Microsoft, Inc. 2004 ** All Rights Reserved. */ /**********************************************************************/ /**************************************************************/ -- Drop ALL Database Mail objects (i.e Functions/Procedures ) /**************************************************************/ PRINT '' PRINT 'Dropping old Database Mail FUNCTIONS and PROCEDURES ...' PRINT '' ----- PRINT 'Dropping function ConvertToInt' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.ConvertToInt'', ''FN'') IS NULL DROP FUNCTION ConvertToInt') ----- PRINT 'Dropping procedure sysmail_start_sp' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_start_sp'', ''P'') IS NULL DROP PROCEDURE dbo.sysmail_start_sp') ----- PRINT 'Dropping procedure sysmail_stop_sp' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_stop_sp'', ''P'') IS NULL DROP PROCEDURE dbo.sysmail_stop_sp') ----- PRINT 'Dropping procedure sysmail_logmailevent_sp' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_logmailevent_sp'', ''P'') IS NULL DROP PROCEDURE dbo.sysmail_logmailevent_sp') ----- PRINT 'Dropping procedure sp_has_changedbuser_permission' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_has_changedbuser_permission'', ''P'') IS NULL DROP PROCEDURE dbo.sp_has_changedbuser_permission') ----- PRINT 'Dropping procedure sp_getprofilerequestxml' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_getprofilerequestxml'', ''P'') IS NULL DROP PROCEDURE dbo.sp_getprofilerequestxml') ----- PRINT 'Dropping procedure sp_sendandreturn' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendandreturn'', ''P'') IS NULL DROP PROCEDURE dbo.sp_sendandreturn') ----- PRINT 'Dropping procedure sp_sendandblock' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendandblock'', ''P'') IS NULL DROP PROCEDURE dbo.sp_sendandblock') ----- PRINT 'Dropping procedure sp_isprohibited' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_isprohibited'', ''P'') IS NULL DROP PROCEDURE dbo.sp_isprohibited') ----- PRINT 'Dropping procedure sp_sendimailqueues' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendimailqueues'', ''P'') IS NULL DROP PROCEDURE dbo.sp_sendimailqueues') ----- PRINT 'Dropping procedure sp_gettestprofilexml' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_gettestprofilexml'', ''P'') IS NULL DROP PROCEDURE dbo.sp_gettestprofilexml') ----- PRINT 'Dropping procedure sp_testprofileimailqueues' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_testprofileimailqueues'', ''P'') IS NULL DROP PROCEDURE dbo.sp_testprofileimailqueues') ----- PRINT 'Dropping procedure sp_endconversation' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_endconversation'', ''P'') IS NULL DROP PROCEDURE dbo.sp_endconversation') ----- PRINT 'Dropping procedure sp_endconversation' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_endconversation'', ''P'') IS NULL DROP PROCEDURE dbo.sp_endconversation') ----- PRINT 'Dropping procedure sp_readrequest' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_readrequest'', ''P'') IS NULL DROP PROCEDURE dbo.sp_readrequest') ----- PRINT 'Dropping procedure sp_sendresponse' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendresponse'', ''P'') IS NULL DROP PROCEDURE dbo.sp_sendresponse') ----- PRINT 'Dropping procedure sp_sendtestprofileresponse' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_sendtestprofileresponse'', ''P'') IS NULL DROP PROCEDURE dbo.sp_sendtestprofileresponse') ----- PRINT 'Dropping procedure sp_getsendmailxml' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_getsendmailxml'', ''P'') IS NULL DROP PROCEDURE dbo.sp_getsendmailxml') ----- PRINT 'Dropping procedure sendimail_sp' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sendimail_sp'', ''P'') IS NULL DROP PROCEDURE dbo.sendimail_sp') ----- PRINT 'Dropping procedure sp_testimailprofile' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_testimailprofile'', ''P'') IS NULL DROP PROCEDURE dbo.sp_testimailprofile') ----- PRINT 'Dropping procedure dbo.sp_add_quota_information' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_add_quota_information'', ''P'') IS NULL DROP PROCEDURE dbo.sp_add_quota_information') ----- PRINT 'Dropping procedure dbo.sp_current_principal_mails' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_current_principal_mails'', ''P'') IS NULL DROP PROCEDURE dbo.sp_current_principal_mails') ----- PRINT 'Dropping procedure dbo.sp_delete_quota_information' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_delete_quota_information'', ''P'') IS NULL DROP PROCEDURE dbo.sp_delete_quota_information') ----- PRINT 'Dropping procedure dbo.sp_ExernalMailQueueListener' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_ExernalMailQueueListener'', ''P'') IS NULL DROP PROCEDURE dbo.sp_ExernalMailQueueListener') ----- PRINT 'Dropping procedure dbo.sp_GetAttachmentData' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_GetAttachmentData'', ''P'') IS NULL DROP PROCEDURE dbo.sp_GetAttachmentData') ----- PRINT 'Dropping procedure dbo.sp_RunMailQuery' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_RunMailQuery'', ''P'') IS NULL DROP PROCEDURE dbo.sp_RunMailQuery') ----- PRINT 'Dropping procedure dbo.sp_SendMailMessage' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_SendMailMessage'', ''P'') IS NULL DROP PROCEDURE dbo.sp_SendMailMessage') ----- PRINT 'Dropping procedure dbo.sp_SendMailQueues' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_SendMailQueues'', ''P'') IS NULL DROP PROCEDURE dbo.sp_SendMailQueues') ----- PRINT 'Dropping procedure dbo.sp_verify_quota_mail_count' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_verify_quota_mail_count'', ''P'') IS NULL DROP PROCEDURE dbo.sp_verify_quota_mail_count') ----- PRINT 'Dropping procedure dbo.sp_activate_sqlimail' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sp_activate_sqlimail'', ''P'') IS NULL DROP PROCEDURE dbo.sp_activate_sqlimail') /**************************************************************/ -- Drop all Database Mail TABLES /**************************************************************/ PRINT '' PRINT 'Dropping TABLES...' PRINT '' ----- PRINT 'Dropping table sysmail_log' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_log'', ''U'') IS NULL DROP TABLE sysmail_log') ----- PRINT 'Dropping table sysmail_send_retries' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_send_retries'', ''U'') IS NULL DROP TABLE dbo.sysmail_send_retries') ----- PRINT 'Dropping table sqlimail_data_transfer' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sqlimail_data_transfer'', ''U'') IS NULL DROP TABLE sqlimail_data_transfer') ----- PRINT 'Dropping table sysmail_attachments' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_attachments'', ''U'') IS NULL DROP TABLE sysmail_attachments') ----- PRINT 'Dropping table sysmail_query_transfer' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_query_transfer'', ''U'') IS NULL DROP TABLE sysmail_query_transfer') ----- PRINT 'Dropping table sysmail_attachments_transfer' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_attachments_transfer'', ''U'') IS NULL DROP TABLE sysmail_attachments_transfer') ----- PRINT 'Dropping table sysmail_quota_information' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_quota_information'', ''U'') IS NULL DROP TABLE sysmail_quota_information') ----- PRINT 'Dropping table sysmail_mailitems' ----- EXEC('USE ' + @DBNameQuote + ' IF NOT OBJECT_ID(''dbo.sysmail_mailitems'', ''U'') IS NULL DROP TABLE sysmail_mailitems') /**************************************************************/ -- Drop MESSAGES, CONTRACTS, QUEUES AND SERVICES /**************************************************************/ PRINT '' PRINT 'Dropping MESSAGES, CONTRACTS, QUEUES AND SERVICES...' PRINT '' ----- PRINT 'Dropping service iMailRequestorService' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''iMailRequestorService'') DROP SERVICE iMailRequestorService') ----- PRINT 'Dropping service iMailResponderService' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''iMailResponderService'') DROP SERVICE iMailResponderService') ----- PRINT 'Dropping queue iMailRequestor' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.objects WHERE name = ''iMailRequestor'' AND type = ''SQ'') DROP QUEUE iMailRequestor') ----- PRINT 'Dropping queue iMailResponder' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.objects WHERE name = ''iMailResponder'' AND type = ''SQ'') DROP QUEUE iMailResponder') ----- PRINT 'Dropping service [SQL/Notifications/IMailNotification/v1.0]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''SQL/Notifications/IMailNotification/v1.0'') DROP SERVICE [SQL/Notifications/IMailNotification/v1.0]') ----- PRINT 'Dropping service [ExternalMailService]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''ExternalMailService'') DROP SERVICE [ExternalMailService]') ----- PRINT 'Dropping service [InternalMailService]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.services WHERE name =''InternalMailService'') DROP SERVICE [InternalMailService]') ----- PRINT 'Dropping queue iMailNotificationQueue' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS (SELECT * FROM sys.objects WHERE name = ''iMailNotificationQueue'' AND type = ''SQ'') DROP QUEUE iMailNotificationQueue') ----- PRINT 'Dropping contract [//www.microsoft.com/imail/contracts/SendMail/v1.0]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_contracts WHERE name = ''//www.microsoft.com/imail/contracts/SendMail/v1.0'') DROP CONTRACT [//www.microsoft.com/imail/contracts/SendMail/v1.0]') ----- PRINT 'Dropping message type [{//www.microsoft.com/imail/messages}SendMail]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_message_types WHERE name = ''{//www.microsoft.com/imail/messages}SendMail'') DROP MESSAGE TYPE [{//www.microsoft.com/imail/messages}SendMail]') ----- PRINT 'Dropping contract [//www.microsoft.com/imail/contracts/TestProfile/v1.0]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_contracts WHERE name = ''//www.microsoft.com/imail/contracts/TestProfile/v1.0'') DROP CONTRACT [//www.microsoft.com/imail/contracts/TestProfile/v1.0]') ----- PRINT 'Dropping message type [{//www.microsoft.com/imail/messages}TestProfile]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_message_types WHERE name = ''{//www.microsoft.com/imail/messages}TestProfile'') DROP MESSAGE TYPE [{//www.microsoft.com/imail/messages}TestProfile]') ----- PRINT 'Dropping message type [{//www.microsoft.com/imail/messages}TestProfileResponse]' ----- EXEC('USE ' + @DBNameQuote + ' IF EXISTS(SELECT * FROM sys.service_message_types WHERE name = ''{//www.microsoft.com/imail/messages}TestProfileResponse'') DROP MESSAGE TYPE [{//www.microsoft.com/imail/messages}TestProfileResponse]') ----- PRINT 'Dropping certificates and related users' ----- DECLARE @sqlcmd nvarchar(max), @CertName sysname, @CertNameQuoted sysname, @MailLogin sysname, @MailLoginQuoted sysname SELECT @CertName = N'SQLiMail-Certificate-' + @DBName, @CertNameQuoted = QUOTENAME( @CertName,''''), @MailLogin = N'SQLiMail-' + @DBName + '-Certificate-Login', @MailLoginQuoted= QUOTENAME( @MailLogin,'''') ----- PRINT 'Dropping user in msdb' ----- SET @sqlcmd = N'USE msdb IF(EXISTS (SELECT * FROM sys.database_principals WHERE name = N' + @MailLoginQuoted + ')) DROP USER ' + QUOTENAME( @MailLogin) EXEC sp_executesql @sqlcmd ----- PRINT 'Dropping user and certificate login in master' ----- SET @sqlcmd = N'USE master IF(EXISTS (SELECT * FROM sys.database_principals WHERE name = N' + @MailLoginQuoted + ')) DROP USER ' + QUOTENAME( @MailLogin) + ' IF(EXISTS(select * from sys.server_principals where name = N' + @MailLoginQuoted + ')) DROP LOGIN ' + QUOTENAME( @MailLogin) EXEC sp_executesql @sqlcmd ----- PRINT 'Dropping user in this database' ----- SET @sqlcmd = N'USE ' + @DBNameQuote + ' IF(EXISTS (SELECT * FROM sys.database_principals WHERE name = N' + @MailLoginQuoted + ')) DROP USER ' + QUOTENAME( @MailLogin) EXEC sp_executesql @sqlcmd ----- PRINT 'Dropping the certificate in this db' ----- SET @sqlcmd = N'USE ' + @DBNameQuote + ' IF(EXISTS(SELECT * FROM sys.certificates WHERE name = N' + @CertNameQuoted + ')) BEGIN DROP CERTIFICATE ' + QUOTENAME(@CertName) + ' END' EXEC sp_executesql @sqlcmd ----- PRINT 'Dropping certificate from master' ----- SET @sqlcmd = N'USE master IF(EXISTS(SELECT * FROM sys.certificates WHERE name = N' + @CertNameQuoted + ')) DROP CERTIFICATE ' + QUOTENAME(@CertName) EXEC sp_executesql @sqlcmd END END FETCH NEXT FROM DBName_Cursor INTO @DBName END -- Loop through all databases. CLOSE DBName_Cursor DEALLOCATE DBName_Cursor -- deleting all principal associations that are not in MSDB or public exec sp_executesql N' delete from msdb.dbo.sysmail_principalprofile where database_id <> 4 and database_id <> 0' END TRY BEGIN CATCH print 'There was a problem upgrading Mail Host databases.' print 'Error State: ' + convert(varchar(10),ERROR_STATE() ) print 'Error Message: ' + ERROR_MESSAGE() print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY()) END CATCH END BEGIN TRY IF EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='principal_id' and id = (SELECT OBJECT_ID(N'dbo.sysmail_principalprofile', 'U'))) BEGIN -- convert data from principal_id to principal_sid exec sp_executesql N' DECLARE @principal_sid varbinary(85) DECLARE @principal_id int DECLARE principal_sid_cursor CURSOR LOCAL FOR SELECT distinct principal_id FROM dbo.sysmail_principalprofile OPEN principal_sid_cursor FETCH NEXT FROM principal_sid_cursor INTO @principal_id WHILE (@@fetch_status = 0) BEGIN IF @principal_id = 0 SET @principal_sid = 0x00 ELSE SELECT @principal_sid = dbo.get_principal_sid(@principal_id) IF @principal_sid IS NOT NULL -- principal_id is valid BEGIN UPDATE dbo.sysmail_principalprofile SET principal_sid = @principal_sid WHERE principal_id = @principal_id END ELSE BEGIN DELETE FROM dbo.sysmail_principalprofile WHERE principal_id = @principal_id END FETCH NEXT FROM principal_sid_cursor INTO @principal_id END CLOSE principal_sid_cursor DEALLOCATE principal_sid_cursor' -- safety clean-up DELETE FROM dbo.sysmail_principalprofile WHERE principal_sid = 0xFFFF -- remove obsolete column ALTER TABLE dbo.sysmail_principalprofile DROP CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique] ALTER TABLE dbo.sysmail_principalprofile DROP COLUMN principal_id ALTER TABLE dbo.sysmail_principalprofile ADD CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique] PRIMARY KEY CLUSTERED ([profile_id] ASC,[principal_sid] ASC) END END TRY BEGIN CATCH print 'There was a problem upgrading MSDB mail host database.' print 'Unable to map existing profile id values to profile_sid column' print 'Error State: ' + convert(varchar(10),ERROR_STATE() ) print 'Error Message: ' + ERROR_MESSAGE() print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY()) END CATCH GO BEGIN TRY -- remove database_id column IF EXISTS (SELECT * FROM msdb.dbo.syscolumns WHERE name='database_id' and id = (SELECT OBJECT_ID(N'dbo.sysmail_principalprofile', 'U'))) BEGIN ALTER TABLE dbo.sysmail_principalprofile DROP CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique] ALTER TABLE dbo.sysmail_principalprofile DROP COLUMN database_id ALTER TABLE dbo.sysmail_principalprofile ADD CONSTRAINT [SYSMAIL_PRINCIPALPROFILE_ProfilePrincipalMustBeUnique] PRIMARY KEY CLUSTERED ([profile_id] ASC,[principal_sid] ASC) END END TRY BEGIN CATCH print 'There was a problem upgrading MSDB mail host database.' print 'Unable to create a primary key constraint on sysmail_principalprofile table' print 'Error State: ' + convert(varchar(10),ERROR_STATE() ) print 'Error Message: ' + ERROR_MESSAGE() print 'Error Number: ' + convert(varchar(10),ERROR_NUMBER()) print 'Error Severity: ' + convert(varchar(10),ERROR_SEVERITY()) END CATCH Print 'Completed upgrade of Database Mail related objects...' GO PAD/*------------------------------------------------------------------------------ SYSDBUPG.SQL This script upgrades the system database stored procs from the RTM level. It is used only for system databases which are upgraded in post-RTM servicing. The script is also run during mkmastr to produce upgraded refresh databases for inclusion in the Express skus. Databases which may be updated by this script: master model msdb Databases not updated by this script: mssqlsystemresource distmdl adventureworks adventureworksdw user databases Any changes to the following scripts are made here instead: U_TABLES.SQL PROCSYST.SQL XPSTAR.SQL INSTMSDB.SQL REPL_MASTER.SQL Note: This script does not apply any sysmessages changes. Such changes are delivered via an updated SQLEVN70.RLL instead. ** Copyright (c) Microsoft Corporation. All rights reserved. ------------------------------------------------------------------------------*/ /**************************************************************/ /* Record time of start of creates */ /**************************************************************/ SELECT start = getdate() INTO #sysdbupg go -------------------------------------------------------------------------------- -- U_TABLES.SQL -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -- PROCSYST.SQL -------------------------------------------------------------------------------- -- Grant SELECT on Shiloh views to PUBLIC (only if they were not explicitely denied) -- use master go set nocount on set implicit_transactions off set ansi_nulls on -- default for osql (consistent for suites) set quoted_identifier on -- Force all ye devs to do this correctly! go -- temporary grant select to QE dmv GRANT SELECT on sys.dm_exec_query_resource_semaphores TO PUBLIC GRANT SELECT on sys.dm_exec_query_memory_grants TO PUBLIC go if NOT EXISTS ( SELECT * FROM sys.database_permissions WHERE class = 1 and major_id = object_id (N'master.dbo.syscacheobjects') and minor_id = 0 and grantee_principal_id = 0 and type = 'SL' and state = 'D') GRANT SELECT ON syscacheobjects TO PUBLIC; if NOT EXISTS ( SELECT * FROM sys.database_permissions WHERE class = 1 and major_id = object_id (N'master.dbo.sysperfinfo') and minor_id = 0 and grantee_principal_id = 0 and type = 'SL' and state = 'D') GRANT SELECT ON sysperfinfo TO PUBLIC; go -------------------------------------------------------------------------------- -- XPSTAR.SQL -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -- INSTMSDB.SQL -------------------------------------------------------------------------------- /*********************************************************************/ /* 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 USE msdb go -- Change growth type to 10% for MSDB log and data files declare @growth bigint declare @is_percent_growth int SET @growth = (SELECT growth FROM sys.database_files WHERE name = N'MSDBData') SET @is_percent_growth = (SELECT is_percent_growth FROM sys.database_files WHERE name = N'MSDBData') -- If data file grows by 256k (this is the RTM setting) change that to 10% IF( (@growth IS NOT NULL) AND (@growth = 32) AND (@is_percent_growth IS NOT NULL) AND (@is_percent_growth = 0 )) BEGIN PRINT 'Update [msdb] data file growth to 10%.' ALTER DATABASE [msdb] MODIFY FILE (NAME=N'MSDBData', FILEGROWTH=10%) END SET @growth = (SELECT growth FROM sys.database_files WHERE name = N'MSDBLog') SET @is_percent_growth = (SELECT is_percent_growth FROM sys.database_files WHERE name = N'MSDBLog') -- If log file grows by 256k (this is the RTM setting) change that to 10% IF( (@growth IS NOT NULL) AND (@growth = 32) AND (@is_percent_growth IS NOT NULL) AND (@is_percent_growth = 0 )) BEGIN PRINT 'Update [msdb] log file growth to 10%.' ALTER DATABASE [msdb] MODIFY FILE (NAME=N'MSDBLog', FILEGROWTH=10%) END GO -- end 'Change growth type ...' -- Updating the sysjobsteps.command column that was missed while upgrading Shiloh --> Yukon IF ((SELECT max_length FROM sys.columns WHERE object_id = OBJECT_ID (N'dbo.sysjobsteps') AND name = N'command') <> -1) BEGIN PRINT '' PRINT 'Updating sys.jobsteps.command to nvarchar(max)' ALTER TABLE dbo.sysjobsteps ALTER COLUMN command NVARCHAR (MAX) END GO -- end 'Update the sysjobsteps.command... /**************************************************************/ /* 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 /**************************************************************/ /* sp_verify_subsystems */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_verify_subsystems...' go ALTER PROCEDURE dbo.sp_verify_subsystems @syssubsytems_refresh_needed BIT = 0 AS BEGIN SET NOCOUNT ON DECLARE @retval INT DECLARE @InstRootPath nvarchar(512) DECLARE @ComRootPath nvarchar(512) DECLARE @DtsRootPath nvarchar(512) DECLARE @DTExec nvarchar(512) DECLARE @DTExecExists INT IF ( (@syssubsytems_refresh_needed=1) OR (NOT EXISTS(select * from syssubsystems)) ) BEGIN EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @InstRootPath OUTPUT IF @InstRootPath IS NULL BEGIN RAISERROR(14658, -1, -1) WITH LOG RETURN (1) END SELECT @InstRootPath = @InstRootPath + N'\binn\' EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\Microsoft Sql Server\90', N'VerSpecificRootDir', @ComRootPath OUTPUT IF @ComRootPath IS NULL BEGIN RAISERROR(14659, -1, -1) WITH LOG RETURN(1) END EXEC master.dbo.xp_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSDTS\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 = @ComRootPath + N'COM\' -- Procedure must start its own transaction if we don't have one already. DECLARE @TranCounter INT; SET @TranCounter = @@TRANCOUNT; IF @TranCounter = 0 BEGIN BEGIN TRANSACTION; END -- Obtain processor count to determine maximum number of threads per subsystem DECLARE @xp_results TABLE ( id INT NOT NULL, name NVARCHAR(30) COLLATE database_default NOT NULL, internal_value INT NULL, character_value NVARCHAR(212) COLLATE database_default NULL ) INSERT INTO @xp_results EXECUTE master.dbo.xp_msver DECLARE @processor_count INT SELECT @processor_count = internal_value from @xp_results where id=16 -- ProcessorCount -- Modify database. BEGIN TRY --create subsystems --TSQL subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'TSQL') INSERT syssubsystems VALUES ( 1, N'TSQL',14556, FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), FORMATMESSAGE(14557), 20 * @processor_count ) --ActiveScripting subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'ActiveScripting') INSERT syssubsystems VALUES ( 2, N'ActiveScripting', 14555, @InstRootPath + N'SQLATXSS90.DLL',NULL,N'ActiveScriptStart',N'ActiveScriptEvent',N'ActiveScriptStop', 10 * @processor_count ) --CmdExec subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'CmdExec') INSERT syssubsystems VALUES ( 3, N'CmdExec', 14550, @InstRootPath + N'SQLCMDSS90.DLL',NULL,N'CmdExecStart',N'CmdEvent',N'CmdExecStop', 10 * @processor_count ) --Snapshot subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'Snapshot') INSERT syssubsystems VALUES ( 4, N'Snapshot', 14551, @InstRootPath + N'SQLREPSS90.DLL', @ComRootPath + N'SNAPSHOT.EXE', N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count ) --LogReader subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'LogReader') INSERT syssubsystems VALUES ( 5, N'LogReader', 14552, @InstRootPath + N'SQLREPSS90.DLL', @ComRootPath + N'logread.exe',N'ReplStart',N'ReplEvent',N'ReplStop',25 * @processor_count ) --Distribution subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'Distribution') INSERT syssubsystems VALUES ( 6, N'Distribution', 14553, @InstRootPath + N'SQLREPSS90.DLL', @ComRootPath + N'DISTRIB.EXE',N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count ) --Merge subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'Merge') INSERT syssubsystems VALUES ( 7, N'Merge', 14554, @InstRootPath + N'SQLREPSS90.DLL',@ComRootPath + N'REPLMERG.EXE',N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count ) --QueueReader subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'QueueReader') INSERT syssubsystems VALUES ( 8, N'QueueReader', 14581, @InstRootPath + N'sqlrepss90.dll',@ComRootPath + N'qrdrsvc.exe',N'ReplStart',N'ReplEvent',N'ReplStop',100 * @processor_count ) --ANALYSISQUERY subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'ANALYSISQUERY') INSERT syssubsystems VALUES ( 9, N'ANALYSISQUERY', 14513, @InstRootPath + N'SQLOLAPSS90.DLL',NULL,N'OlapStart',N'OlapQueryEvent',N'OlapStop',100 * @processor_count ) --ANALYSISCOMMAND subsystem IF NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'ANALYSISCOMMAND') INSERT syssubsystems VALUES ( 10, N'ANALYSISCOMMAND', 14514, @InstRootPath + N'SQLOLAPSS90.DLL',NULL,N'OlapStart',N'OlapCommandEvent',N'OlapStop',100 * @processor_count ) IF(@DtsRootPath IS NOT NULL) BEGIN --DTS subsystem IF (NOT EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'SSIS') ) INSERT syssubsystems VALUES ( 11, N'SSIS', 14538, @InstRootPath + N'SQLDTSSS90.DLL',@DtsRootPath + N'DTExec.exe',N'DtsStart',N'DtsEvent',N'DtsStop',100 * @processor_count ) ELSE UPDATE syssubsystems SET agent_exe = @DtsRootPath + N'DTExec.exe' WHERE subsystem = N'SSIS' END ELSE BEGIN IF EXISTS(SELECT * FROM syssubsystems WHERE subsystem = N'SSIS') DELETE FROM syssubsystems WHERE subsystem = N'SSIS' END 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_SCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_verify_schedule...' go ALTER 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', '0x1, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80') 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)) OR ((@freq_subday_type = 0x2) AND (@freq_subday_interval < 10)) 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_ATTACH_SCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_attach_schedule ...' go ALTER PROCEDURE sp_attach_schedule ( @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @schedule_id INT = NULL, -- Must provide either this or schedule_name @schedule_name sysname = NULL, -- Must provide either this or schedule_id @automatic_post BIT = 1 -- If 1 will post notifications to all tsx servers to that run this job ) AS BEGIN DECLARE @retval INT DECLARE @sched_owner_sid VARBINARY(85) DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Check that we can uniquely identify the job EXECUTE @retval = msdb.dbo.sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check authority (only SQLServerAgent can add a schedule to a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- Check that we can uniquely identify the schedule EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = @sched_owner_sid OUTPUT, @orig_server_id = NULL IF (@retval <> 0) RETURN(1) -- Failure --Schedules can only be attached to a job if the job and schedule have the --same owner or the caller is a sysadmin IF ((@sched_owner_sid <> @job_owner_sid) AND ((@sched_owner_sid <> SUSER_SID()) OR (@job_owner_sid <> SUSER_SID())) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(14377, -1, -1) RETURN(1) -- Failure END -- If the record doesn't already exist create it IF( NOT EXISTS(SELECT * FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) AND (job_id = @job_id)) ) BEGIN INSERT INTO msdb.dbo.sysjobschedules (schedule_id, job_id) SELECT @schedule_id, @job_id SELECT @retval = @@ERROR -- Notify SQLServerAgent of the change, but only if we know the job has been cached IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'S', @job_id = @job_id, @schedule_id = @schedule_id, @action_type = N'I' END -- For a multi-server job, remind the user that they need to call sp_post_msx_operation IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines IF (@automatic_post = 1) EXECUTE sp_post_msx_operation @operation = 'INSERT', @object_type = 'JOB', @job_id = @job_id ELSE RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation') -- update this job's subplan to point to this schedule UPDATE msdb.dbo.sysmaintplan_subplans SET schedule_id = @schedule_id WHERE (job_id = @job_id) AND (schedule_id IS NULL) END RETURN(@retval) -- 0 means success END GO /**************************************************************/ /* SP_DETACH_SCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_detach_schedule ...' go ALTER PROCEDURE sp_detach_schedule ( @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @schedule_id INT = NULL, -- Must provide either this or schedule_name @schedule_name sysname = NULL, -- Must provide either this or schedule_id @delete_unused_schedule BIT = 0, -- Can optionally delete schedule if it isn't referenced. -- The default is to keep schedules @automatic_post BIT = 1 -- If 1 will post notifications to all tsx servers to that run this job ) AS BEGIN DECLARE @retval INT DECLARE @sched_owner_sid VARBINARY(85) DECLARE @job_owner_sid VARBINARY(85) SET NOCOUNT ON -- Check that we can uniquely identify the job EXECUTE @retval = msdb.dbo.sp_verify_job_identifiers '@job_name', '@job_id', @job_name OUTPUT, @job_id OUTPUT, @owner_sid = @job_owner_sid OUTPUT IF (@retval <> 0) RETURN(1) -- Failure -- Check authority (only SQLServerAgent can add a schedule to a non-local job) EXECUTE @retval = sp_verify_jobproc_caller @job_id = @job_id, @program_name = N'SQLAgent%' IF (@retval <> 0) RETURN(@retval) -- Check that we can uniquely identify the schedule EXECUTE @retval = msdb.dbo.sp_verify_schedule_identifiers @name_of_name_parameter = '@schedule_name', @name_of_id_parameter = '@schedule_id', @schedule_name = @schedule_name OUTPUT, @schedule_id = @schedule_id OUTPUT, @owner_sid = @sched_owner_sid OUTPUT, @orig_server_id = NULL, @job_id_filter = @job_id IF (@retval <> 0) RETURN(1) -- Failure -- If the record doesn't exist raise an error IF( NOT EXISTS(SELECT * FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id) AND (job_id = @job_id)) ) BEGIN RAISERROR(14374, 0, 1, @schedule_name, @job_name) RETURN(1) -- Failure END ELSE BEGIN -- Only sysadmin can detach schedules from jobs they do not own IF (((@sched_owner_sid <> SUSER_SID()) OR (@job_owner_sid <> SUSER_SID())) AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) <> 1)) BEGIN RAISERROR(14391, -1, -1) RETURN(1) -- Failure END DELETE FROM msdb.dbo.sysjobschedules WHERE (job_id = @job_id) AND (schedule_id = @schedule_id) SELECT @retval = @@ERROR --delete the schedule if requested and it isn't referenced IF(@retval = 0 AND @delete_unused_schedule = 1) BEGIN IF(NOT EXISTS(SELECT * FROM msdb.dbo.sysjobschedules WHERE (schedule_id = @schedule_id))) BEGIN DELETE FROM msdb.dbo.sysschedules WHERE (schedule_id = @schedule_id) END END -- Update the job's version/last-modified information UPDATE msdb.dbo.sysjobs SET version_number = version_number + 1, date_modified = GETDATE() WHERE (job_id = @job_id) -- Notify SQLServerAgent of the change, but only if we know the job has been cached IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id = 0))) BEGIN EXECUTE msdb.dbo.sp_sqlagent_notify @op_type = N'S', @job_id = @job_id, @schedule_id = @schedule_id, @action_type = N'D' END -- For a multi-server job, remind the user that they need to call sp_post_msx_operation IF (EXISTS (SELECT * FROM msdb.dbo.sysjobservers WHERE (job_id = @job_id) AND (server_id <> 0))) -- sp_post_msx_operation will do nothing if the schedule isn't assigned to any tsx machines IF (@automatic_post = 1) EXECUTE sp_post_msx_operation @operation = 'INSERT', @object_type = 'JOB', @job_id = @job_id ELSE RAISERROR(14547, 0, 1, N'INSERT', N'sp_post_msx_operation') -- set this job's subplan to the first schedule in sysjobschedules or NULL if there is none UPDATE msdb.dbo.sysmaintplan_subplans SET schedule_id = ( SELECT TOP(1) schedule_id FROM msdb.dbo.sysjobschedules WHERE (job_id = @job_id) ) WHERE (job_id = @job_id) AND (schedule_id = @schedule_id) END RETURN(@retval) -- 0 means success END GO /**************************************************************/ /* SP_UPDATE_SCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_update_schedule ...' go ALTER 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 'Updating procedure sp_delete_schedule ...' go ALTER 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_ADD_JOBSCHEDULE */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_add_jobschedule...' go ALTER 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 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_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 'Updating procedure sp_update_jobschedule...' go ALTER 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 'Updating procedure sp_delete_jobschedule...' go ALTER 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_SQLAGENT_HAS_SERVER_ACCESS */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_sqlagent_has_server_access...' go ALTER PROCEDURE sp_sqlagent_has_server_access @login_name sysname = NULL, @is_sysadmin_member INT = NULL OUTPUT AS BEGIN DECLARE @has_server_access BIT DECLARE @is_sysadmin BIT DECLARE @actual_login_name sysname DECLARE @cachedate DATETIME SET NOCOUNT ON SELECT @cachedate = NULL -- remove expired entries from the cache DELETE msdb.dbo.syscachedcredentials WHERE DATEDIFF(MINUTE, cachedate, GETDATE()) >= 29 -- query the cache SELECT @is_sysadmin = is_sysadmin_member, @has_server_access = has_server_access, @cachedate = cachedate FROM msdb.dbo.syscachedcredentials WHERE login_name = @login_name AND DATEDIFF(MINUTE, cachedate, GETDATE()) < 29 IF (@cachedate IS NOT NULL) BEGIN -- no output variable IF (@is_sysadmin_member IS NULL) BEGIN -- Return result row SELECT has_server_access = @has_server_access, is_sysadmin = @is_sysadmin, actual_login_name = @login_name RETURN END ELSE BEGIN SELECT @is_sysadmin_member = @is_sysadmin RETURN END END -- select from cache -- Set defaults SELECT @has_server_access = 0 SELECT @is_sysadmin = 0 SELECT @actual_login_name = FORMATMESSAGE(14205) IF (@login_name IS NULL) BEGIN SELECT has_server_access = 1, is_sysadmin = IS_SRVROLEMEMBER(N'sysadmin'), actual_login_name = SUSER_SNAME() RETURN END IF (@login_name LIKE '%\%') BEGIN -- Handle the LocalSystem account ('NT AUTHORITY\SYSTEM') as a special case IF (UPPER(@login_name collate SQL_Latin1_General_CP1_CS_AS) = N'NT AUTHORITY\SYSTEM') BEGIN IF (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (UPPER(loginname collate SQL_Latin1_General_CP1_CS_AS) = N'BUILTIN\ADMINISTRATORS'))) BEGIN SELECT @has_server_access = hasaccess, @is_sysadmin = sysadmin, @actual_login_name = loginname FROM master.dbo.syslogins WHERE (UPPER(loginname collate SQL_Latin1_General_CP1_CS_AS) = N'BUILTIN\ADMINISTRATORS') END END ELSE BEGIN -- Check if the NT login has been explicitly denied access IF (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (loginname = @login_name) AND (denylogin = 1))) BEGIN SELECT @has_server_access = 0, @is_sysadmin = sysadmin, @actual_login_name = loginname FROM master.dbo.syslogins WHERE (loginname = @login_name) END ELSE BEGIN -- declare table variable for storing results DECLARE @xp_results TABLE ( account_name sysname COLLATE database_default NOT NULL PRIMARY KEY, type NVARCHAR(10) COLLATE database_default NOT NULL, privilege NVARCHAR(10) COLLATE database_default NOT NULL, mapped_login_name sysname COLLATE database_default NOT NULL, permission_path sysname COLLATE database_default NULL ) -- Call xp_logininfo to determine server access INSERT INTO @xp_results EXECUTE master.dbo.xp_logininfo @login_name SELECT @has_server_access = CASE COUNT(*) WHEN 0 THEN 0 ELSE 1 END FROM @xp_results SELECT @actual_login_name = mapped_login_name, @is_sysadmin = CASE UPPER(privilege collate SQL_Latin1_General_CP1_CS_AS) WHEN 'ADMIN' THEN 1 ELSE 0 END FROM @xp_results END END END ELSE BEGIN -- Standard login IF (EXISTS (SELECT * FROM master.dbo.syslogins WHERE (loginname = @login_name))) BEGIN SELECT @has_server_access = hasaccess, @is_sysadmin = sysadmin, @actual_login_name = loginname FROM master.dbo.syslogins WHERE (loginname = @login_name) END END -- 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 IF (@is_sysadmin_member IS NULL) -- Return result row SELECT has_server_access = @has_server_access, is_sysadmin = @is_sysadmin, actual_login_name = @actual_login_name ELSE -- output variable only SELECT @is_sysadmin_member = @is_sysadmin END go /**************************************************************/ /* SP_ADD_JOBSTEP_INTERNAL */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_add_jobstep_internal...' go ALTER PROCEDURE dbo.sp_add_jobstep_internal @job_id UNIQUEIDENTIFIER = NULL, -- Must provide either this or job_name @job_name sysname = NULL, -- Must provide either this or job_id @step_id INT = NULL, -- The proc assigns a default @step_name sysname, @subsystem NVARCHAR(40) = N'TSQL', @command NVARCHAR(max) = NULL, @additional_parameters NTEXT = NULL, @cmdexec_success_code INT = 0, @on_success_action TINYINT = 1, -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step @on_success_step_id INT = 0, @on_fail_action TINYINT = 2, -- 1 = Quit With Success, 2 = Quit With Failure, 3 = Goto Next Step, 4 = Goto Step @on_fail_step_id INT = 0, @server sysname = NULL, @database_name sysname = NULL, @database_user_name sysname = NULL, @retry_attempts INT = 0, -- No retries @retry_interval INT = 0, -- 0 minute interval @os_run_priority INT = 0, -- -15 = Idle, -1 = Below Normal, 0 = Normal, 1 = Above Normal, 15 = Time Critical) @output_file_name NVARCHAR(200) = NULL, @flags INT = 0, -- 0 = Normal, -- 1 = Encrypted command (read only), -- 2 = Append output files (if any), -- 4 = Write TSQL step output to step history -- 8 = Write log to table (overwrite existing history) -- 16 = Write log to table (append to existing history) @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_SQLAGENT_REFRESH_JOB */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_sqlagent_refresh_job...' go ALTER 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_DELETE_JOB */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_delete_job...' go ALTER 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 CREATE TABLE #temp_jobs_to_delete (job_id UNIQUEIDENTIFIER NOT NULL, 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 --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) --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_SQLAGENT_IS_SRVROLEMEMBER */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_sqlagent_is_srvrolemember...' go ALTER 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_jobstep */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_verify_jobstep...' go ALTER 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 sysname DECLARE @database_user_name_temp sysname 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 SELECT @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 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 Qith Success (@on_fail_action <> 2) AND -- Quit Qith 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 > 50)) BEGIN RAISERROR(14266, -1, -1, '@flags', '0..50') 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' )) BEGIN RAISERROR(14545, -1, -1, '@output_file_name', @subsystem) RETURN(1) -- Failure END -- Check writing to table flags IF (@flags IS NOT NULL) AND (((@flags & 8) <> 0) OR ((@flags & 16) <> 0)) AND (UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('TSQL', 'CMDEXEC', 'ANALYSISQUERY', 'ANALYSISCOMMAND', 'SSIS' )) 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 = REPLACE(@database_name, N'''', N'''''') 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 /**************************************************************/ /* sysmail_delete_mailitems_sp */ /**************************************************************/ ----- PRINT 'Updating sysmail_delete_mailitems_sp' ----- GO ALTER 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 /**************************************************************/ /* sp_sysmail_activate */ /**************************************************************/ ----- PRINT 'Updating procedure sp_sysmail_activate' ----- GO -- sp_sysmail_activate : Starts the DatabaseMail process if it isn't already running -- ALTER PROCEDURE 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 @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) --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, @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 /**************************************************************/ /* Update the RunMailQuery stored procedure */ /**************************************************************/ /****** Object: StoredProcedure [dbo].[sp_RunMailQuery] Script Date: 06/06/2006 12:26:03 ******/ use msdb GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO ALTER 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 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 @sid varbinary(85) create table #temp ([account name] sysname, [type] char(8), [privilege] char(9), [mapped login name] sysname, [permission path] sysname) insert #temp exec master.dbo.xp_logininfo @send_request_user select @sid = suser_sid([permission path]) from #temp SET @user_sid = NULL IF EXISTS(SELECT * FROM msdb.dbo.sysmail_principalprofile as pp WHERE (pp.is_default = 1) AND (pp.principal_sid = @sid)) BEGIN SET @user_sid = @sid END END GO use msdb GO /****** Object: StoredProcedure [dbo].[sp_send_dbmail] Script Date: 06/06/2006 12:24:17 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO -- sp_send_dbmail : Sends a mail from Yukon outbox. -- ALTER 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 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) SET @sid = NULL -- Initialize SELECT @rc = 0, @QueryResultsExist = 0, @AttachmentsExist = 0, @temp_table_uid = NEWID(), @procName = OBJECT_NAME(@@PROCID), @tranStartedBool = 0, @trancountSave = @@TRANCOUNT 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 EXEC msdb.dbo.sp_validate_user @send_request_user, @sid OUTPUT 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 IF(@profile_id IS NULL) BEGIN RAISERROR(14636, 16, 1) RETURN 1 END END END ELSE BEGIN --Get primary account if profile name is supplied EXEC @rc = msdb.dbo.sysmail_verify_profile_sp @profile_id = NULL, @profile_name = @profile_name, @allow_both_nulls = 0, @allow_id_name_mismatch = 0, @profileid = @profile_id OUTPUT IF (@rc <> 0) RETURN @rc --Make sure this user has access to the specified profile. --sysadmins can send on any profiles IF ( @is_sysadmin <> 1) BEGIN --Not a sysadmin so check users access to profile iF NOT EXISTS(SELECT * FROM msdb.dbo.sysmail_principalprofile WHERE ((profile_id = @profile_id) AND (dbo.get_principal_id(principal_sid) = @database_user_id OR principal_sid = 0x00))) BEGIN EXEC msdb.dbo.sp_validate_user @send_request_user, @sid OUTPUT IF(@sid IS NULL) BEGIN RAISERROR(14607, -1, -1, 'profile') RETURN 1 END END END END --Attach results must be specified IF @attach_query_result_as_file IS NULL BEGIN RAISERROR(14618, 16, 1, 'attach_query_result_as_file') RETURN 2 END --No output must be specified IF @exclude_query_output IS NULL BEGIN RAISERROR(14618, 16, 1, 'exclude_query_output') RETURN 3 END --No header must be specified IF @query_result_header IS NULL BEGIN RAISERROR(14618, 16, 1, 'query_result_header') RETURN 4 END -- Check if query_result_separator is specifed IF @query_result_separator IS NULL OR DATALENGTH(@query_result_separator) = 0 BEGIN RAISERROR(14618, 16, 1, 'query_result_separator') RETURN 5 END --Echo error must be specified IF @append_query_error IS NULL BEGIN RAISERROR(14618, 16, 1, 'append_query_error') RETURN 6 END --@body_format can be TEXT (default) or HTML IF (@body_format IS NULL) BEGIN SET @body_format = 'TEXT' END ELSE BEGIN SET @body_format = UPPER(@body_format) IF @body_format NOT IN ('TEXT', 'HTML') BEGIN RAISERROR(14626, 16, 1, @body_format) RETURN 13 END END --Importance must be specified IF @importance IS NULL BEGIN RAISERROR(14618, 16, 1, 'importance') RETURN 15 END SET @importance = UPPER(@importance) --Importance must be one of the predefined values IF @importance NOT IN ('LOW', 'NORMAL', 'HIGH') BEGIN RAISERROR(14622, 16, 1, @importance) RETURN 16 END --Sensitivity must be specified IF @sensitivity IS NULL BEGIN RAISERROR(14618, 16, 1, 'sensitivity') RETURN 17 END SET @sensitivity = UPPER(@sensitivity) --Sensitivity must be one of predefined values IF @sensitivity NOT IN ('NORMAL', 'PERSONAL', 'PRIVATE', 'CONFIDENTIAL') BEGIN RAISERROR(14623, 16, 1, @sensitivity) RETURN 18 END --Message body cannot be null. Atleast one of message, subject, query, --attachments must be specified. IF( (@body IS NULL AND @query IS NULL AND @file_attachments IS NULL AND @subject IS NULL) OR ( (LEN(@body) IS NULL OR LEN(@body) <= 0) AND (LEN(@query) IS NULL OR LEN(@query) <= 0) AND (LEN(@file_attachments) IS NULL OR LEN(@file_attachments) <= 0) AND (LEN(@subject) IS NULL OR LEN(@subject) <= 0) ) ) BEGIN RAISERROR(14624, 16, 1, '@body, @query, @file_attachments, @subject') RETURN 19 END ELSE IF @subject IS NULL OR LEN(@subject) <= 0 SET @subject='SQL Server Message' --Recipients cannot be empty. Atleast one of the To, Cc, Bcc must be specified IF ( (@recipients IS NULL AND @copy_recipients IS NULL AND @blind_copy_recipients IS NULL ) OR ( (LEN(@recipients) IS NULL OR LEN(@recipients) <= 0) AND (LEN(@copy_recipients) IS NULL OR LEN(@copy_recipients) <= 0) AND (LEN(@blind_copy_recipients) IS NULL OR LEN(@blind_copy_recipients) <= 0) ) ) BEGIN RAISERROR(14624, 16, 1, '@recipients, @copy_recipients, @blind_copy_recipients') RETURN 20 END --If query is not specified, attach results and no header cannot be true. IF ( (@query IS NULL OR LEN(@query) <= 0) AND @attach_query_result_as_file = 1) BEGIN RAISERROR(14625, 16, 1) RETURN 21 END -- -- Execute Query if query is specified IF ((@query IS NOT NULL) AND (LEN(@query) > 0)) BEGIN EXECUTE AS CALLER EXEC @rc = sp_RunMailQuery @query = @query, @attach_results = @attach_query_result_as_file, @query_attachment_filename = @query_attachment_filename, @no_output = @exclude_query_output, @query_result_header = @query_result_header, @separator = @query_result_separator, @echo_error = @append_query_error, @dbuse = @execute_query_database, @width = @query_result_width, @temp_table_uid = @temp_table_uid, @query_no_truncate = @query_no_truncate, @query_result_no_padding = @query_result_no_padding -- This error indicates that query results size was over the configured MaxFileSize. -- Note, an error has already beed raised in this case IF(@rc = 101) GOTO ErrorHandler; REVERT -- Always check the transfer tables for data. They may also contain error messages -- Only one of the tables receives data in the call to sp_RunMailQuery IF(@attach_query_result_as_file = 1) BEGIN IF EXISTS(SELECT * FROM sysmail_attachments_transfer WHERE uid = @temp_table_uid) SET @AttachmentsExist = 1 END ELSE BEGIN IF EXISTS(SELECT * FROM sysmail_query_transfer WHERE uid = @temp_table_uid AND uid IS NOT NULL) SET @QueryResultsExist = 1 END -- Exit if there was an error and caller doesn't want the error appended to the mail IF (@rc <> 0 AND @append_query_error = 0) BEGIN --Error msg with be in either the attachment table or the query table --depending on the setting of @attach_query_result_as_file IF(@attach_query_result_as_file = 1) BEGIN --Copy query results from the attachments table to mail body SELECT @RetErrorMsg = CONVERT(NVARCHAR(4000), attachment) FROM sysmail_attachments_transfer WHERE uid = @temp_table_uid END ELSE BEGIN --Copy query results from the query table to mail body SELECT @RetErrorMsg = text_data FROM sysmail_query_transfer WHERE uid = @temp_table_uid END GOTO ErrorHandler; END SET @AttachmentsExist = @attach_query_result_as_file END ELSE BEGIN --If query is not specified, attach results cannot be true. IF (@attach_query_result_as_file = 1) BEGIN RAISERROR(14625, 16, 1) RETURN 21 END END --Get the prohibited extensions for attachments from sysmailconfig. IF ((@file_attachments IS NOT NULL) AND (LEN(@file_attachments) > 0)) BEGIN EXECUTE AS CALLER EXEC @rc = sp_GetAttachmentData @attachments = @file_attachments, @temp_table_uid = @temp_table_uid, @exclude_query_output = @exclude_query_output REVERT IF (@rc <> 0) GOTO ErrorHandler; IF EXISTS(SELECT * FROM sysmail_attachments_transfer WHERE uid = @temp_table_uid) SET @AttachmentsExist = 1 END -- Start a transaction if not already in one. -- Note: For rest of proc use GOTO ErrorHandler for falures if (@trancountSave = 0) BEGIN TRAN @procName SET @tranStartedBool = 1 -- Store complete mail message for history/status purposes INSERT sysmail_mailitems ( profile_id, recipients, copy_recipients, blind_copy_recipients, subject, body, body_format, importance, sensitivity, file_attachments, attachment_encoding, query, execute_query_database, attach_query_result_as_file, query_result_header, query_result_width, query_result_separator, exclude_query_output, append_query_error, send_request_user ) 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 ) SELECT @rc = @@ERROR, @mailitem_id = @@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 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) 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 USE [msdb] GO /****** Object: StoredProcedure [dbo].[sp_ProcessResponse] Script Date: 08/04/2006 14:54:57 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO -- Processes responses from dbmail -- ALTER PROCEDURE [dbo].[sp_ProcessResponse] @conv_handle uniqueidentifier, @message_type_name NVARCHAR(256), @xml_message_body NVARCHAR(max) AS BEGIN DECLARE @idoc INT, @mailitem_id INT, @sent_status INT, @rc INT, @index INT, @processId INT, @sent_date DATETIME, @localmessage NVARCHAR(max), @LogMessage NVARCHAR(max), @retry_hconv uniqueidentifier, @paramStr NVARCHAR(256), @accRetryDelay INT -------------------------- --Always send the response ;SEND ON CONVERSATION @conv_handle MESSAGE TYPE @message_type_name (@xml_message_body) -- -- Need to handle the case where a sent retry is requested. -- This is done by setting a conversation timer, The timer with go off in the external queue -- Get the handle to the xml document EXEC @rc = sp_xml_preparedocument @idoc OUTPUT, @xml_message_body, N'' IF(@rc <> 0) BEGIN --Log the error. The response has already sent to the Internal queue. -- This will update the mail with the latest staus SET @localmessage = FORMATMESSAGE(14655, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage GOTO ErrorHandler; END -- Execute a SELECT statement that uses the OPENXML rowset provider to get the MailItemId and sent status. SELECT @mailitem_id = MailItemId, @sent_status = SentStatus FROM OPENXML (@idoc, '/responses:SendMail', 1) WITH (MailItemId INT './MailItemId/@Id', SentStatus INT './SentStatus/@Status') --Close the handle to the xml document EXEC sp_xml_removedocument @idoc IF(@mailitem_id IS NULL OR @sent_status IS NULL) BEGIN --Log error and continue. SET @localmessage = FORMATMESSAGE(14652, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage GOTO ErrorHandler; END -- Update the status of the email item UPDATE msdb.dbo.sysmail_mailitems SET sent_status = @sent_status WHERE mailitem_id = @mailitem_id -- -- A send retry has been requested. Set a conversation timer IF(@sent_status = 3) BEGIN -- Get the associated mail item data for the given @conversation_handle (if it exists) SELECT @retry_hconv = conversation_handle FROM sysmail_send_retries as sr RIGHT JOIN sysmail_mailitems as mi ON sr.mailitem_id = mi.mailitem_id WHERE mi.mailitem_id = @mailitem_id --Must be the first retry attempt. Create a sysmail_send_retries record to track retries IF(@retry_hconv IS NULL) BEGIN INSERT sysmail_send_retries(conversation_handle, mailitem_id) --last_send_attempt_date VALUES(@conv_handle, @mailitem_id) END ELSE BEGIN --Update existing retry record UPDATE sysmail_send_retries SET last_send_attempt_date = GETDATE(), send_attempts = send_attempts + 1 WHERE mailitem_id = @mailitem_id END --Get the global retry delay time EXEC msdb.dbo.sysmail_help_configure_value_sp @parameter_name = N'AccountRetryDelay', @parameter_value = @paramStr OUTPUT --ConvertToInt will return the default if @paramStr is null SET @accRetryDelay = dbo.ConvertToInt(@paramStr, 0x7fffffff, 300) -- 5 min default --Now set the dialog timer. This triggers the send retry ;BEGIN CONVERSATION TIMER (@conv_handle) TIMEOUT = @accRetryDelay END ELSE BEGIN --Only end theconversation if a retry isn't being attempted END CONVERSATION @conv_handle END -- All done OK goto ExitProc; ----------------- -- Error Handler ----------------- ErrorHandler: ------------------ -- Exit Procedure ------------------ ExitProc: RETURN (@rc); END go /**************************************************************/ /* 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_full') AND (type = 'P'))) DROP PROCEDURE sp_help_jobhistory_full go IF (EXISTS (SELECT * FROM msdb.dbo.sysobjects WHERE (name = N'sp_help_jobhistory_summary') AND (type = 'P'))) DROP PROCEDURE sp_help_jobhistory_summary go 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_full @job_id UNIQUEIDENTIFIER, @job_name sysname, @step_id INT, @sql_message_id INT, @sql_severity INT, @start_run_date INT, @end_run_date INT, @start_run_time INT, @end_run_time INT, @minimum_run_duration INT, @run_status INT, @minimum_retries INT, @oldest_first INT, @server sysname, @mode VARCHAR(7), @order_by INT, @distributed_job_history BIT AS IF(@distributed_job_history = 1) SELECT null as instance_id, sj.job_id, job_name = sj.name, null as step_id, null as step_name, null as sql_message_id, null as sql_severity, sjh.last_outcome_message as message, sjh.last_run_outcome as run_status, sjh.last_run_date as run_date, sjh.last_run_time as run_time, sjh.last_run_duration as run_duration, null as operator_emailed, null as operator_netsentname, null as operator_paged, null as retries_attempted, sts.server_name as server FROM msdb.dbo.sysjobservers sjh JOIN msdb.dbo.systargetservers sts ON (sts.server_id = sjh.server_id) JOIN msdb.dbo.sysjobs_view sj ON(sj.job_id = sjh.job_id) WHERE (@job_id = sjh.job_id) AND ((@start_run_date IS NULL) OR (sjh.last_run_date >= @start_run_date)) AND ((@end_run_date IS NULL) OR (sjh.last_run_date <= @end_run_date)) AND ((@start_run_time IS NULL) OR (sjh.last_run_time >= @start_run_time)) AND ((@minimum_run_duration IS NULL) OR (sjh.last_run_duration >= @minimum_run_duration)) AND ((@run_status IS NULL) OR (@run_status = sjh.last_run_outcome)) AND ((@server IS NULL) OR (sts.server_name = @server)) ELSE SELECT sjh.instance_id, -- This is included just for ordering purposes sj.job_id, job_name = sj.name, sjh.step_id, sjh.step_name, sjh.sql_message_id, sjh.sql_severity, sjh.message, sjh.run_status, sjh.run_date, sjh.run_time, sjh.run_duration, operator_emailed = so1.name, operator_netsent = so2.name, operator_paged = so3.name, sjh.retries_attempted, sjh.server FROM msdb.dbo.sysjobhistory sjh LEFT OUTER JOIN msdb.dbo.sysoperators so1 ON (sjh.operator_id_emailed = so1.id) LEFT OUTER JOIN msdb.dbo.sysoperators so2 ON (sjh.operator_id_netsent = so2.id) LEFT OUTER JOIN msdb.dbo.sysoperators so3 ON (sjh.operator_id_paged = so3.id), msdb.dbo.sysjobs_view sj WHERE (sj.job_id = sjh.job_id) AND ((@job_id IS NULL) OR (@job_id = sjh.job_id)) AND ((@step_id IS NULL) OR (@step_id = sjh.step_id)) AND ((@sql_message_id IS NULL) OR (@sql_message_id = sjh.sql_message_id)) AND ((@sql_severity IS NULL) OR (@sql_severity = sjh.sql_severity)) AND ((@start_run_date IS NULL) OR (sjh.run_date >= @start_run_date)) AND ((@end_run_date IS NULL) OR (sjh.run_date <= @end_run_date)) AND ((@start_run_time IS NULL) OR (sjh.run_time >= @start_run_time)) AND ((@end_run_time IS NULL) OR (sjh.run_time <= @end_run_time)) AND ((@minimum_run_duration IS NULL) OR (sjh.run_duration >= @minimum_run_duration)) AND ((@run_status IS NULL) OR (@run_status = sjh.run_status)) AND ((@minimum_retries IS NULL) OR (sjh.retries_attempted >= @minimum_retries)) AND ((@server IS NULL) OR (sjh.server = @server)) ORDER BY (sjh.instance_id * @order_by) GO 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 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 ALTER 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 GRANT EXECUTE ON sp_help_jobhistory TO SQLAgentUserRole GRANT EXECUTE ON sp_help_jobhistory_full TO SQLAgentUserRole GRANT EXECUTE ON sp_help_jobhistory_summary TO SQLAgentUserRole GRANT EXECUTE ON sp_help_jobhistory_sem TO SQLAgentUserRole /**************************************************************/ /* 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 GRANT SELECT ON [dbo].[sysmail_event_log] TO DatabaseMailUserRole USE [msdb] GO /****** Object: StoredProcedure [dbo].[sp_sysmail_activate] Script Date: 06/22/2006 17:56:51 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- sp_sysmail_activate : Starts the DatabaseMail process if it isn't already running -- ALTER 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 USE [msdb] GO /****** Object: StoredProcedure [dbo].[sysmail_delete_profile_sp] Script Date: 07/19/2006 16:26:21 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO ALTER 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 /******** [sysmail_update_account_sp] ********/ 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 USE [msdb] GO /****** Object: StoredProcedure [dbo].[sp_ExternalMailQueueListener] Script Date: 08/10/2006 14:31:04 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO -- Processes messages from the external mail queue -- ALTER PROCEDURE [dbo].[sp_ExternalMailQueueListener] AS BEGIN DECLARE @idoc INT, @mailitem_id INT, @sent_status INT, @sent_account_id INT, @rc INT, @processId INT, @sent_date DATETIME, @localmessage NVARCHAR(max), @conv_handle uniqueidentifier, @message_type_name NVARCHAR(256), @xml_message_body NVARCHAR(max), @LogMessage NVARCHAR(max) -- Table to store message information. DECLARE @msgs TABLE ( [conversation_handle] uniqueidentifier, [message_type_name] nvarchar(256), [message_body] varbinary(max) ) --RECEIVE messages from the external queue. --MailItem status messages are sent from the external sql mail process along with other SSB notifications and errors ;RECEIVE conversation_handle, message_type_name, message_body FROM InternalMailQueue INTO @msgs -- Check if there was some error in reading from queue SET @rc = @@ERROR IF (@rc <> 0) BEGIN --Log error and continue. Don't want to block the following messages on the queue SET @localmessage = FORMATMESSAGE(@@ERROR) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage GOTO ErrorHandler; END ----------------------------------- --Process sendmail status messages SELECT @conv_handle = conversation_handle, @message_type_name = message_type_name, @xml_message_body = CAST(message_body AS NVARCHAR(MAX)) FROM @msgs WHERE [message_type_name] = N'{//www.microsoft.com/databasemail/messages}SendMailStatus' IF(@message_type_name IS NOT NULL) BEGIN -- --Expecting the xml body to be n the following form: -- -- -- -- -- -- -- -- -- -- -- -- -- Get the handle to the xml document EXEC @rc = sp_xml_preparedocument @idoc OUTPUT, @xml_message_body, N'' IF(@rc <> 0) BEGIN --Log error and continue. Don't want to block the following messages on the queue SET @localmessage = FORMATMESSAGE(14655, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage GOTO ErrorHandler; END -- Execute a SELECT statement that uses the OPENXML rowset provider to get the MailItemId and sent status. SELECT @mailitem_id = MailItemId, @sent_status = SentStatus, @sent_account_id = SentAccountId, @sent_date = SentDate, @processId = CallingProcess, @LogMessage = LogMessage FROM OPENXML (@idoc, '/responses:SendMail', 1) WITH (MailItemId INT './MailItemId/@Id', SentStatus INT './SentStatus/@Status', SentAccountId INT './SentAccountId/@Id', SentDate DATETIME './SentDate/@Date', --The date was formated using ISO8601 CallingProcess INT './CallingProcess/@Id', LogMessage NVARCHAR(max) './Information/Failure/@Message') --Close the handle to the xml document EXEC sp_xml_removedocument @idoc IF(@mailitem_id IS NULL) BEGIN --Log error and continue. Don't want to block the following messages on the queue by rolling back the tran SET @localmessage = FORMATMESSAGE(14652, CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage END ELSE BEGIN -- check sent_status is valid : 0(PendingSend), 1(SendSuccessful), 2(SendFailed), 3(AttemptingSendRetry) IF(@sent_status NOT IN (1, 2, 3)) BEGIN SET @localmessage = FORMATMESSAGE(14653, N'SentStatus', CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@localmessage --Set value to SendFailed SET @sent_status = 2 END --Make the @sent_account_id NULL if it is 0. IF(@sent_account_id IS NOT NULL AND @sent_account_id = 0) SET @sent_account_id = NULL -- -- Update the mail status if not a retry. Nothing else needs to be done in this case UPDATE sysmail_mailitems SET sent_status = CAST (@sent_status as TINYINT), sent_account_id = @sent_account_id, sent_date = @sent_date WHERE mailitem_id = @mailitem_id -- Report a failure if no record is found in the sysmail_mailitems table IF (@@ROWCOUNT = 0) BEGIN SET @localmessage = FORMATMESSAGE(14653, N'MailItemId', CONVERT(NVARCHAR(50), @conv_handle), @message_type_name, @xml_message_body) exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@localmessage END IF (@LogMessage IS NOT NULL) BEGIN exec msdb.dbo.sysmail_logmailevent_sp @event_type=3, @description=@LogMessage, @process_id=@processId, @mailitem_id=@mailitem_id, @account_id=@sent_account_id END END END ------------------------------------------------------- --Process all other messages by logging to sysmail_log SET @conv_handle = NULL; --Always end the conversion if this message is received SELECT @conv_handle = conversation_handle FROM @msgs WHERE [message_type_name] = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog' IF(@conv_handle IS NOT NULL) BEGIN END CONVERSATION @conv_handle; END DECLARE @queuemessage nvarchar(max) DECLARE queue_messages_cursor CURSOR LOCAL FOR SELECT FORMATMESSAGE(14654, CONVERT(NVARCHAR(50), conversation_handle), message_type_name, message_body) FROM @msgs WHERE [message_type_name] NOT IN (N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog', N'{//www.microsoft.com/databasemail/messages}SendMailStatus') OPEN queue_messages_cursor FETCH NEXT FROM queue_messages_cursor INTO @queuemessage WHILE (@@fetch_status = 0) BEGIN exec msdb.dbo.sysmail_logmailevent_sp @event_type=2, @description=@queuemessage FETCH NEXT FROM queue_messages_cursor INTO @queuemessage END CLOSE queue_messages_cursor DEALLOCATE queue_messages_cursor -- All done OK goto ExitProc; ----------------- -- Error Handler ----------------- ErrorHandler: ------------------ -- Exit Procedure ------------------ ExitProc: RETURN (@rc) END GO ALTER 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 -- 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 exec @rc = msdb.dbo.sysmail_verify_account_sp @account_id, @account_name, 0, 1, @accountid OUTPUT IF @rc <> 0 RETURN(1) IF(@email_address IS NULL) BEGIN SELECT @email_address = email_address FROM msdb.dbo.sysmail_account WHERE account_id=@accountid END IF(@display_name IS NULL) BEGIN SELECT @display_name = display_name FROM msdb.dbo.sysmail_account WHERE account_id=@accountid END IF(@replyto_address IS NULL) BEGIN SELECT @replyto_address = replyto_address FROM msdb.dbo.sysmail_account WHERE account_id=@accountid END IF(@description IS NULL) BEGIN SELECT @description = description FROM msdb.dbo.sysmail_account WHERE account_id=@accountid END IF(@port IS NULL) BEGIN SELECT @port = port FROM msdb.dbo.sysmail_server WHERE account_id=@accountid END IF(@use_default_credentials IS NULL) BEGIN SELECT @use_default_credentials = use_default_credentials FROM msdb.dbo.sysmail_server WHERE account_id=@accountid END IF(@enable_ssl IS NULL) BEGIN SELECT @enable_ssl = enable_ssl FROM msdb.dbo.sysmail_server WHERE account_id=@accountid END IF(@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) 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 (@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 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 WHERE account_id=@accountid AND servertype=@mailserver_type COMMIT TRAN RETURN(0) go IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='msx_job_id' and id = (select id from msdb.dbo.sysobjects where name='sysmaintplan_subplans')) BEGIN PRINT 'Altering table sysmaintplan_subplans...' ALTER TABLE sysmaintplan_subplans ADD msx_job_id UNIQUEIDENTIFIER DEFAULT NULL NULL ALTER TABLE sysmaintplan_subplans ADD CONSTRAINT FK_subplan_msx_job_id FOREIGN KEY (msx_job_id) REFERENCES sysjobs(job_id) END IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='msx_plan' and id = (select id from msdb.dbo.sysobjects where name='sysmaintplan_subplans')) BEGIN ALTER TABLE sysmaintplan_subplans ADD msx_plan bit DEFAULT 0 NOT NULL END GO /**************************************************************/ /* sp_maintplan_update_subplan */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_maintplan_update_subplan...' go ALTER 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_subplan */ /**************************************************************/ PRINT '' PRINT 'Altering procedure sp_maintplan_delete_subplan...' go ALTER 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 'Updating procedure sp_maintplan_update_subplan_tsx...' IF (OBJECT_ID(N'dbo.sp_maintplan_update_subplan_tsx', 'P') IS NULL) execute sp_executesql N'CREATE PROCEDURE sp_maintplan_update_subplan_tsx AS select 1 ' 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. ALTER 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 'Updating procedure sp_maintplan_subplans_by_job...' IF (OBJECT_ID(N'dbo.sp_maintplan_subplans_by_job', 'P') IS NULL) execute sp_executesql N'CREATE PROCEDURE sp_maintplan_subplans_by_job @job_id UNIQUEIDENTIFIER AS select 1 ' go -- If the given job_id is associated with a maintenance plan, -- then matching entries from sysmaintplan_subplans are returned. ALTER 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 -- 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 GO /**************************************************************/ /* SYSMAINTPLAN_LOG */ /**************************************************************/ -- Alter the sysmaintplan_log table to allow for remote logging -- of maintenance plan execution. IF NOT EXISTS ( select * from msdb.dbo.syscolumns where name='logged_remotely' and id = (select id from msdb.dbo.sysobjects where name='sysmaintplan_log')) BEGIN alter table sysmaintplan_log alter column plan_id uniqueidentifier NULL alter table sysmaintplan_log alter column subplan_id uniqueidentifier NULL alter table sysmaintplan_log add 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_PLANS */ /**************************************************************/ -- Alter the sysmaintplan_plans view to include the from_msx and -- has_targets columns, which indicate that this maintenance plan is -- a distributed plan that came from an MSX server, or that it -- a plan that is currently distributed to other servers. ALTER 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.sysdtspackages90 AS s WHERE (s.folderid = '08aa12d5-8f98-4dab-a4fc-980b150a5dc8' and s.packagetype = 6) go /**************************************************************/ /* SP_DELETE_JOB_REFERENCES */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_delete_job_references...' go ALTER 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_dts_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_JOBSERVER */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_delete_jobserver...' go ALTER 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_GET_COMPOSITE_JOB_INFO */ /**************************************************************/ PRINT '' PRINT 'Updating procedure sp_get_composite_job_info...' go ALTER 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 /**************************************************************** * Add new or updated SPs above. A new SP should be added to the list * of procedures in #sp_table below, and if necessary put into the * appropriate ODB bucket. * IMPORTANT NOTE - We CANNOT sign any new procedures added since Yukon * RTM! This is because a cross-SKU upgrade, say from SQL Express SP2 * to SQL Developer, first installs the RTM version of the new SKU. The * RTM version of the instmsdb.sql script tries to drop the Agent * signing certificate in order to recreate it, but since it doesn't * know about new signed SPs added since RTM it fails the drop. As a * result that breaks cross-SKU upgrades, so we can never sign new * procedures with our Agent signing certificate. To handle this, add * your new procedure to the #sp_table with a value of zero for the * 'sign' column. * *****************************************************************/ /**************************************************************/ /* Sign agent sps and add them to Off By Default component */ /* */ /* Also sign SPs for other components located in MSDB */ /**************************************************************/ PRINT 'Signing sps ...' -- Create certificate to sign Agent sps -- if exists (select * from sys.certificates where name = '##MS_AgentSigningCertificate##') drop certificate [##MS_AgentSigningCertificate##] declare @certError int dbcc traceon(4606,-1) create certificate [##MS_AgentSigningCertificate##] encryption by password = 'Yukon90_' with subject = 'MS_AgentSigningCertificate' select @certError = @@error dbcc traceoff(4606,-1) IF (@certError <> 0) BEGIN select '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 create ##MS_AgentSigningCertificate## in msdb. SYSDBUPG.SQL terminating.', 20, 127) WITH LOG END go -- 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', 0, 0, 0) -- added after Yukon RTM, so cannot sign insert into #sp_table values(N'sp_help_jobhistory_summary', 0, 0, 0) -- added after Yukon RTM, so cannot sign insert into #sp_table values(N'sp_help_jobhistory_sem', 0, 0, 0) -- added after Yukon RTM, so cannot sign 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_send_dbmail', 1, 2, 0) insert into #sp_table values(N'sp_validate_user', 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', 0, 0, 0) -- added after Yukon RTM, so cannot sign insert into #sp_table values(N'sp_maintplan_subplans_by_job', 0, 0, 0) -- added after Yukon RTM, so cannot sign -- Log Shipping insert into #sp_table values(N'sp_add_log_shipping_monitor_jobs', 1, 0, 0) insert into #sp_table values(N'sp_add_log_shipping_primary', 1, 0, 0) insert into #sp_table values(N'sp_add_log_shipping_secondary', 1, 0, 0) insert into #sp_table values(N'sp_delete_log_shipping_monitor_jobs', 1, 0, 0) insert into #sp_table values(N'sp_delete_log_shipping_primary', 1, 0, 0) insert into #sp_table values(N'sp_delete_log_shipping_secondary ', 1, 0, 0) insert into #sp_table values(N'sp_log_shipping_in_sync', 1, 0, 0) insert into #sp_table values(N'sp_log_shipping_get_date_from_file ', 1, 0, 0) insert into #sp_table values(N'sp_get_log_shipping_monitor_info', 1, 0, 0) insert into #sp_table values(N'sp_update_log_shipping_monitor_info', 1, 0, 0) insert into #sp_table values(N'sp_delete_log_shipping_monitor_info', 1, 0, 0) insert into #sp_table values(N'sp_remove_log_shipping_monitor_account', 1, 0, 0) insert into #sp_table values(N'sp_log_shipping_monitor_backup', 1, 0, 0) insert into #sp_table values(N'sp_log_shipping_monitor_restore', 1, 0, 0) insert into #sp_table values(N'sp_change_monitor_role', 1, 0, 0) insert into #sp_table values(N'sp_create_log_shipping_monitor_account', 1, 0, 0) -- DTS insert into #sp_table values(N'sp_get_dtsversion', 1, 0, 0) insert into #sp_table values(N'sp_make_dtspackagename', 1, 0, 0) insert into #sp_table values(N'sp_add_dtspackage', 1, 0, 0) insert into #sp_table values(N'sp_drop_dtspackage', 1, 0, 0) insert into #sp_table values(N'sp_reassign_dtspackageowner', 1, 0, 0) insert into #sp_table values(N'sp_get_dtspackage', 1, 0, 0) insert into #sp_table values(N'sp_reassign_dtspackagecategory', 1, 0, 0) insert into #sp_table values(N'sp_enum_dtspackages', 1, 0, 0) insert into #sp_table values(N'sp_add_dtscategory', 1, 0, 0) insert into #sp_table values(N'sp_drop_dtscategory', 1, 0, 0) insert into #sp_table values(N'sp_modify_dtscategory', 1, 0, 0) insert into #sp_table values(N'sp_enum_dtscategories', 1, 0, 0) insert into #sp_table values(N'sp_log_dtspackage_begin', 1, 0, 0) insert into #sp_table values(N'sp_log_dtspackage_end', 1, 0, 0) insert into #sp_table values(N'sp_log_dtsstep_begin', 1, 0, 0) insert into #sp_table values(N'sp_log_dtsstep_end', 1, 0, 0) insert into #sp_table values(N'sp_log_dtstask', 1, 0, 0) insert into #sp_table values(N'sp_enum_dtspackagelog', 1, 0, 0) insert into #sp_table values(N'sp_enum_dtssteplog', 1, 0, 0) insert into #sp_table values(N'sp_enum_dtstasklog', 1, 0, 0) insert into #sp_table values(N'sp_dump_dtslog_all', 1, 0, 0) insert into #sp_table values(N'sp_dump_dtspackagelog', 1, 0, 0) insert into #sp_table values(N'sp_dump_dtssteplog', 1, 0, 0) insert into #sp_table values(N'sp_dump_dtstasklog', 1, 0, 0) insert into #sp_table values(N'sp_dts_addlogentry', 1, 0, 0) insert into #sp_table values(N'sp_dts_listpackages', 1, 0, 0) insert into #sp_table values(N'sp_dts_listfolders', 1, 0, 0) insert into #sp_table values(N'sp_dts_deletepackage', 1, 0, 0) insert into #sp_table values(N'sp_dts_deletefolder', 1, 0, 0) insert into #sp_table values(N'sp_dts_getpackage', 1, 0, 0) insert into #sp_table values(N'sp_dts_getfolder', 1, 0, 0) insert into #sp_table values(N'sp_dts_putpackage', 1, 0, 0) insert into #sp_table values(N'sp_dts_addfolder', 1, 0, 0) insert into #sp_table values(N'sp_dts_renamefolder', 1, 0, 0) insert into #sp_table values(N'sp_dts_setpackageroles', 1, 0, 0) insert into #sp_table values(N'sp_dts_getpackageroles', 1, 0, 0) go /**************************************************************/ /* Mark system objects */ /**************************************************************/ declare @start datetime ,@name sysname select @start = start from #sysdbupg 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 #sysdbupg go BEGIN TRANSACTION declare @sp sysname declare @exec_str nvarchar(1024) declare @sign int declare @obdComponent int declare @bNewProc int 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 set @exec_str = N'add signature to ' + @sp + N' by certificate [##MS_AgentSigningCertificate##] with password = ''Yukon90_''' Execute(@exec_str) if (@@error <> 0) begin declare @err_str nvarchar(1024) 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. SYSDBUPG.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 -- drop certificate private key alter certificate [##MS_AgentSigningCertificate##] remove private key IF (@@error <> 0) RAISERROR('Cannot alter ##MS_AgentSigningCertificate## in msdb. SYSDBUPG.SQL terminating.', 20, 127) WITH LOG go --create a temporary database in order to get the path to the 'Data' folder --because upon upgrade existing database are in temporary folder IF (EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE (name = N'temp_MS_AgentSigningCertificate_database'))) BEGIN DROP DATABASE temp_MS_AgentSigningCertificate_database END go CREATE DATABASE temp_MS_AgentSigningCertificate_database go -- export certificate to master -- use current directory to persist the file -- DECLARE @certificate_name NVARCHAR(520) DECLARE @certificate_nameQuoted NVARCHAR(1042) -- twice the length + 2, in case every character was a quote SELECT @certificate_name = SUBSTRING(filename, 1, CHARINDEX(N'temp_MS_AgentSigningCertificate_database.mdf', filename) - 1) + CONVERT(NVARCHAR(520), NEWID()) + N'.cer' FROM master.dbo.sysaltfiles WHERE (name = N'temp_MS_AgentSigningCertificate_database') -- Handle single quotes in the directory name (can't use QUOTENAME in case name over 128 chars) SELECT @certificate_nameQuoted = '''' + REPLACE(@certificate_name, '''', '''''') + '''' EXECUTE(N'backup certificate [##MS_AgentSigningCertificate##] to file = ' + @certificate_nameQuoted) IF (@@error <> 0) RAISERROR('Cannot backup ##MS_AgentSigningCertificate##. SYSDBUPG.SQL terminating.', 20, 127) WITH LOG 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##] execute(N'create certificate [##MS_AgentSigningCertificate##] from file = ' + @certificate_nameQuoted) IF (@@error <> 0) RAISERROR('Cannot create ##MS_AgentSigningCertificate## certificate in master. SYSDBUPG.SQL terminating.', 20, 127) WITH LOG -- create login -- create login [##MS_AgentSigningCertificate##] from certificate [##MS_AgentSigningCertificate##] IF (@@error <> 0) RAISERROR('Cannot create ##MS_AgentSigningCertificate## login. SYSDBUPG.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. SYSDBUPG.SQL terminating.', 20, 127) WITH LOG -- enable certificate for OBD -- exec sys.sp_SetOBDCertificate N'##MS_AgentSigningCertificate##',N'ON' grant execute to [##MS_AgentSigningCertificate##] use msdb go -- Refresh Subsystem list now that sp_verify_subsystems has been -- signed and our special ##MS_AgentSigningCertificate## exists. We -- have to do this after the certificate exists because -- sp_verify_subsystems makes calls to xp_regread and xp_fileexist, -- which are extended procedures that, if disabled, are only available -- to procedures signed by special certificates like ours. If SSIS is -- not installed but SSMS is installed this call will make SSIS -- subsystem available to Agent jobs. exec sp_verify_subsystems 1 go -- 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 go PRINT 'Succesfully signed sps' -- -- End of signing sps go USE msdb go /**************************************************************/ /* Drop auxilary procedure to enable OBD component */ /**************************************************************/ DROP PROCEDURE #sp_enable_component go DROP PROCEDURE #sp_restore_component_state go -------------------------------------------------------------------------------- -- REPL_MASTER.SQL -------------------------------------------------------------------------------- /**************************************************************************** // Copyright (c) Microsoft Corporation. // // @File: provisionexpress.sql // @Owner: kaloianm // // Purpose: // Script to grant login privileges to BUILTIN\Users for EXPRESS/RANU. // // Notes: // run by the SQL script upgrade framework. // // // @EndHeader@ *****************************************************************************/ use master go PRINT '----------------------------------------' PRINT 'Starting provisionexpress.sql ...'; PRINT '----------------------------------------' go -- Obtain the name of BUILTIN\Users from a well-known SID -- (as it might be on an non-ENU installation) -- DECLARE @builtinUsersSID AS NVARCHAR(85) DECLARE @builtinUsersSIDBinary AS VARBINARY(85) DECLARE @builtinUsers AS NVARCHAR(85); SET @builtinUsersSID = N'S-1-5-32-545'; -- convert textual SID to name -- SELECT @builtinUsersSIDBinary = sid_binary(@builtinUsersSID); SELECT @builtinUsers = suser_sname(@builtinUsersSIDBinary); IF (@builtinUsers IS NOT NULL) BEGIN IF (NOT EXISTS (SELECT * FROM sys.server_principals WHERE NAME = @builtinUsers)) BEGIN PRINT 'Adding login for ' + @builtinUsers DECLARE @strExec NVARCHAR (MAX) SET @strExec = 'CREATE LOGIN ' + QUOTENAME(@builtinUsers) + ' FROM WINDOWS' EXEC (@strExec) END END go PA/*------------------------------------------------------------------------------ upgrade_ucp_cmdw_discovery.sql It uses sys.sp_dbscriptlevel to enable the other upgrade scripts to run. ** Copyright (c) Microsoft Corporation. All rights reserved. ------------------------------------------------------------------------------*/ -- These must match the definitions used in sqlscriptupgrade.cpp PRINT '------------------------------------------------------' PRINT 'Starting execution of UPGRADE_UCP_CMDW_DISCOVERY.SQL' PRINT '------------------------------------------------------' GO DECLARE @mdw_database_name SYSNAME SELECT @mdw_database_name=CAST(current_value as SYSNAME) FROM [msdb].[dbo].[sysutility_ucp_configuration_internal] WHERE name = 'MdwDatabaseName' DECLARE @run_script BIT SET @run_script=1 -- If MdwDatabaseName does not exist then skip execution of instmdw.sql. -- This can happen for example when the instance is not a Ucp. IF (@mdw_database_name IS NULL OR @mdw_database_name = N'') BEGIN SET @run_script=0 RAISERROR ('The Utility MDW does not exist on this instance.', 0, 1) WITH NOWAIT; END -- If the @mdw_database_name does not exist skip the execution of instmdw.sql IF (@run_script = 1) AND NOT EXISTS (SELECT * FROM master.dbo.sysdatabases WHERE name = @mdw_database_name) BEGIN SET @run_script=0 RAISERROR ('The Utility MDW does not exist on this instance.', 0, 1) WITH NOWAIT; END DECLARE @print_expr nvarchar(400) -- If the @mdw_database_name is NOT online, log a message and skip the execution of instmdw.sql DECLARE @state_online SYSNAME SET @state_online = 'ONLINE' SELECT @state_online = UPPER(@state_online collate SQL_Latin1_General_CP1_CS_AS) IF (@run_script = 1) AND NOT EXISTS (SELECT state_desc FROM master.sys.databases where name = @mdw_database_name AND UPPER(state_desc collate SQL_Latin1_General_CP1_CS_AS) LIKE @state_online) BEGIN SET @print_expr = 'WARNING! The database ' + @mdw_database_name + ' is not ONLINE. So skipping execution of instmdw.sql on it. Please run the script manually after the upgrade.' RAISERROR (@print_expr, 0, 1) WITH NOWAIT; SET @run_script=0 END DECLARE @EMPTY_SCRIPT_LEVEL INT DECLARE @KATMAI_SCRIPT_LEVEL INT DECLARE @ID_UTILITY_CMDW_UPGRADE INT SELECT @EMPTY_SCRIPT_LEVEL = 0 SELECT @KATMAI_SCRIPT_LEVEL = 2 SELECT @ID_UTILITY_CMDW_UPGRADE = 12 -- Disable execution of upgrade_ucp_cmdw.sql by default EXEC sys.sp_dbscriptlevel 'master', @ID_UTILITY_CMDW_UPGRADE, @KATMAI_SCRIPT_LEVEL -- Only if this is a UCP and sysutility_mdw exists and is ONLINE trigger execution of upgrade_ucp_cmdw.sql IF (@run_script = 1) BEGIN RAISERROR ('instmdw.sql will be executed on the Utility MDW database.', 0, 1) WITH NOWAIT; EXEC sys.sp_dbscriptlevel 'master', @ID_UTILITY_CMDW_UPGRADE, @EMPTY_SCRIPT_LEVEL END ELSE BEGIN RAISERROR ('Skipping the execution of instmdw.sql.', 0, 1) WITH NOWAIT; END PRINT '------------------------------------------------------' PRINT 'execution of UPGRADE_UCP_CMDW_DISCOVERY.SQL completed' PRINT '------------------------------------------------------' GO P/**********************************************************************/ /* PRE_UPGRADE_UCP_CMDW.SQL */ /* */ /* ** Copyright Microsoft, Inc. 1994 - 2009 ** All Rights Reserved. */ /**********************************************************************/ PRINT '------------------------------------------------' PRINT 'Starting execution of PRE_UPGRADE_UCP_CMDW.SQL' PRINT '------------------------------------------------' PRINT '------------------------------------------------' PRINT 'execution of PRE_UPGRADE_UCP_CMDW.SQL completed' PRINT '------------------------------------------------' GO USE sysutility_mdw /**********************************************************************/ /* INSTMDW.SQL */ /* */ /* Installs the tables and stored procedures necessary for */ /* supporting the Management Data Warehouse in Data Collector */ /* */ /* MDW database is created by Setup and this script is run in the */ /* context of the created MDW database */ /* */ /* To run this script manually on a MDW database: */ /* "sqlcmd -d MDW -i instmdw.sql" */ /* */ /* */ /* Copyright Microsoft, Inc. 2006 */ /* All Rights Reserved. */ /* */ /**********************************************************************/ PRINT '' PRINT '----------------------------------' PRINT 'Starting execution of INSTMDW.SQL ' PRINT '----------------------------------' GO 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 -- This version of instmdw should be executed only against 10.0 servers or higher IF (@@microsoftversion / 0x01000000) < 10 BEGIN RAISERROR('Management Data Warehouse database can only be installed on an instance of SQL Server 2008 or higher.', 21, 127) WITH LOG END GO -- SQL Server Express Edition does not support a management data warehouse. DECLARE @retval sql_variant SELECT @retval = (SELECT SERVERPROPERTY('EngineEdition')) IF (@retval = 4) -- 4: Express BEGIN RAISERROR(14713, 20, -1) WITH LOG END GO -- Take the database to single mode during the installation PRINT '' PRINT 'Taking database to single user mode' DECLARE @dbname sysname SELECT @dbname = QUOTENAME(DB_NAME()) -- Check if someone has turned async statistics autoupdate on DECLARE @async_auto_stat bit SELECT @async_auto_stat = is_auto_update_stats_async_on FROM sys.databases WHERE database_id = DB_ID() CREATE TABLE #tmp_auto_mode (auto_mode bit) IF (@async_auto_stat = 1) -- if yes, turn it off while install BEGIN PRINT 'Disabling asynchronous auto statistics while database in single user mode ...' DECLARE @sql_async_autostat_off nvarchar(256) SET @sql_async_autostat_off = 'ALTER DATABASE ' + @dbname + ' SET AUTO_UPDATE_STATISTICS_ASYNC OFF' EXEC sp_executesql @sql_async_autostat_off -- check for all the currently running background statistics jobs -- and kill them DECLARE @stats_job_id nvarchar(10) DECLARE @sql_kill_stats_job nvarchar(256) DECLARE stats_jobs_id_cursor CURSOR READ_ONLY FOR SELECT CONVERT(nvarchar(10), job_id) FROM sys.dm_exec_background_job_queue WHERE database_id = DB_ID() OPEN stats_jobs_id_cursor FETCH NEXT FROM stats_jobs_id_cursor INTO @stats_job_id WHILE (@@FETCH_STATUS = 0) BEGIN SET @sql_kill_stats_job = 'KILL STATS JOB ' + @stats_job_id EXEC sp_executesql @sql_kill_stats_job END CLOSE stats_jobs_id_cursor DEALLOCATE stats_jobs_id_cursor -- save the fact that async_auto_stats was on INSERT INTO #tmp_auto_mode VALUES(1) END -- Now, put the database in single user mode DECLARE @sql_query nvarchar(256) SET @sql_query = 'ALTER DATABASE ' + @dbname + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE' EXEC sp_executesql @sql_query; -- Allow the use of snapshot transaction isolation level SET @sql_query = 'ALTER DATABASE ' + @dbname + ' SET ALLOW_SNAPSHOT_ISOLATION ON'; EXEC sp_executesql @sql_query; -- Turn on the read_committed_snapshot database option SET @sql_query = 'ALTER DATABASE ' + @dbname + ' SET READ_COMMITTED_SNAPSHOT ON'; EXEC sp_executesql @sql_query; GO -- Set the right options -- These are needed for the correct behavior of check constraint 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 /**********************************************************************/ /* Add an extended database property to identify database as a data */ /* warehouse for the data collector. */ /* */ /* If a version already exists, check that we are not down grading */ /* to a lower version. */ /**********************************************************************/ -- -- This stored procedure compares two build numbers -- It tokenizes the numbers by '.' and compares the portions from left to right -- IF (OBJECT_ID(N'tempdb..#sp_compare_builds', 'P') IS NOT NULL) BEGIN DROP PROCEDURE [tempdb]..[#sp_compare_builds] END GO CREATE PROCEDURE #sp_compare_builds @old nvarchar(100), @new nvarchar(100), @order int OUTPUT -- -1 if @old<@new, 0 if @old=@new, 1 if @old>@new AS BEGIN DECLARE @retVal int DECLARE @old_portion nvarchar(100) DECLARE @new_portion nvarchar(100) DECLARE @old_number int DECLARE @new_number int SET @old = NULLIF(@old+'.', '.') SET @new = NULLIF(@new+'.', '.') SET @order = 0 BEGIN TRY SET @retVal = 0 WHILE ((@old IS NOT NULL) AND (@new IS NOT NULL)) BEGIN --SELECT @old as old, @new as new DECLARE @old_token_mark int DECLARE @new_token_mark int SET @old_token_mark = CHARINDEX('.', @old) SET @new_token_mark = CHARINDEX('.', @new) -- get the first number in the version from the left SET @old_portion = LEFT(@old, @old_token_mark-1) SET @new_portion = LEFT(@new, @new_token_mark-1) -- trim the number from the left SET @old = NULLIF(SUBSTRING(@old, @old_token_mark+1, LEN(@old)-@old_token_mark), '') SET @new = NULLIF(SUBSTRING(@new, @new_token_mark+1, LEN(@new)-@new_token_mark), '') -- compare the portions you have SET @old_number = CONVERT(int, @old_portion) SET @new_number = CONVERT(int, @new_portion) IF (@old_number = @new_number) BEGIN CONTINUE END ELSE BEGIN -- We have a resolution, figure out who comes first and get out IF (@old_number < @new_number) BEGIN SET @order = -1 END ELSE BEGIN SET @order = 1 END BREAK END END RETURN (0) END TRY BEGIN CATCH 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 DECLARE @prop_name sysname DECLARE @new_value sql_variant DECLARE @old_value sql_variant SET @prop_name = 'Microsoft_DataCollector_MDW_Version' SET @new_value = '10.50.1600.1' -- This will be replaced at build time with the sql build number SELECT @old_value = value FROM fn_listextendedproperty(@prop_name, NULL, NULL, NULL, NULL, NULL, NULL) IF (@old_value IS NOT NULL) BEGIN DECLARE @order int DECLARE @old_value_char nvarchar(100) DECLARE @new_value_char nvarchar(100) SET @old_value_char = CONVERT(nvarchar(100), @old_value) SET @new_value_char = CONVERT(nvarchar(100), @new_value) -- check that we are not downgrading the database IF (@old_value_char <> '10.50.1600.1') -- value only used during development BEGIN EXEC #sp_compare_builds @old_value_char, @new_value_char, @order OUTPUT IF (@order > 0) -- the old build version is older than the new, abort the script and kill the connection BEGIN -- Put the database back into multi user mode PRINT '' PRINT 'Restoring database to multi user mode before aborting the script' DECLARE @dbname sysname SET @dbname = QUOTENAME(DB_NAME()) DECLARE @sql_db_multi_mode nvarchar(256) SET @sql_db_multi_mode = 'ALTER DATABASE ' + @dbname + ' SET MULTI_USER WITH ROLLBACK IMMEDIATE' EXEC sp_executesql @sql_db_multi_mode RAISERROR(14714, 21, 1, @old_value_char, @new_value_char) WITH LOG END END EXEC sp_dropextendedproperty @name = @prop_name END EXEC sp_addextendedproperty @name = @prop_name, @value = @new_value GO DROP PROCEDURE #sp_compare_builds /* Procedure [#create_or_alter_primary_key_or_index] Helper proc to create/update primary key constraints and nonclustered/clustered indexes. Avoids the need to duplicate object definition in separate creation and upgrade sections. Prevents cluttering up the script with repetitive logic to repeat these tasks every time we need to define an index or a primary key: - Check for an existing object and skip the create if appropriate - Compare the definition of the existing object (clustered vs. nonclustered, number and order of index key columns, ignore_dup_key bit, included vs. key columns, asc/desc sort direction) to the desired index or constraint definition - Drop the existing constraint/index if it is malformed or out-of-date - Drop nonclustered indexes before dropping clustered indexes - Drop referencing foreign keys before dropping a primary key Parameters: @table_schema - e.g. "snapshots" @table_name - e.g. "query_stats" @object_type - either "PRIMARY KEY" or "INDEX" @constraint_or_index_name - the PK or index name @ignore_dup_key - 1 to enable the IGNORE_DUP_KEY index option (default 0) @clustered - 1 to create a clustered index/PK (default 1) The columns in the table are passed to this proc via this temp table: CREATE TABLE #index_key_columns ( key_ordinal int IDENTITY, constraint_name sysname, column_name sysname, is_included_column bit, is_descending_key bit ); Example usage: TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('IDX_performance_counter_instances1', 'object_name', 0, 0), ('IDX_performance_counter_instances1', 'counter_name', 0, 0), ('IDX_performance_counter_instances1', 'instance_name', 1, 0); GO EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'performance_counter_instances', @object_type = 'INDEX', @constraint_or_index_name = 'IDX_performance_counter_instances1', @ignore_dup_key = 0, @clustered = 0; GO The TRUNCATE is typically not necessary, but is a good practice just in case two indexes have the same name. */ IF OBJECT_ID ('tempdb..#create_or_alter_primary_key_or_index') IS NOT NULL BEGIN DROP PROC #create_or_alter_primary_key_or_index END; GO CREATE PROC #create_or_alter_primary_key_or_index @table_schema sysname, @table_name sysname, @object_type sysname, @constraint_or_index_name sysname, @ignore_dup_key bit = 0, @clustered bit = 1 AS BEGIN RAISERROR (N'Validating %s [%s] on table [%s].[%s]...', 0, 1, @object_type, @constraint_or_index_name, @table_schema, @table_name) WITH NOWAIT; -- Get the list of key columns and included columns that are supposed to be in -- this index/PK constraint DECLARE @column_name sysname; DECLARE @is_included_column bit; DECLARE @is_descending_key bit; DECLARE @key_column_list nvarchar(max); DECLARE @include_column_list nvarchar(max); DECLARE @sql nvarchar(max); DECLARE @expected_column_count int; SET @key_column_list = ''; SET @include_column_list = ''; SET @expected_column_count = 0; DECLARE cols INSENSITIVE CURSOR FOR SELECT column_name, is_included_column, is_descending_key FROM #index_key_columns WHERE constraint_name = @constraint_or_index_name ORDER BY key_ordinal ASC; OPEN cols; FETCH NEXT FROM cols INTO @column_name, @is_included_column, @is_descending_key; WHILE (@@FETCH_STATUS = 0) BEGIN IF (@is_included_column = 1) BEGIN SET @include_column_list = @include_column_list + CASE WHEN LEN (@include_column_list) > 0 THEN ', ' ELSE '' END + QUOTENAME (@column_name) IF (@object_type = 'PRIMARY KEY') BEGIN RAISERROR ('Error in script -- INCLUDEd columns are not allowed in a primary key', 21, 1); END END ELSE BEGIN SET @key_column_list = @key_column_list + CASE WHEN LEN (@key_column_list) > 0 THEN ', ' ELSE '' END + QUOTENAME (@column_name) + CASE @is_descending_key WHEN 1 THEN ' DESC' ELSE ' ASC' END; END; SET @expected_column_count = @expected_column_count + 1; FETCH NEXT FROM cols INTO @column_name, @is_included_column, @is_descending_key; END; CLOSE cols; DEALLOCATE cols; -- Compare the object definition to the expected definition DECLARE @unexpected_column_count int; DECLARE @total_column_count int; DECLARE @total_included_count int; DECLARE @out_of_order_key_count int; DECLARE @wrong_sort_direction_count int; DECLARE @wrong_included_column_count int; DECLARE @key_ordinal_base int; DECLARE @existing_constraint_or_index_name sysname; DECLARE @current_ignore_dup_key bit; DECLARE @current_clustered bit; DECLARE @table_object_id int; -- Get the target table's object id SET @table_object_id = OBJECT_ID (QUOTENAME (@table_schema) + '.' + QUOTENAME (@table_name)); -- Get the indentity value of the first column in this constraint SELECT @key_ordinal_base = MIN (key_ordinal) - 1 FROM #index_key_columns WHERE constraint_name = @constraint_or_index_name; -- Check the constraint schema against expected schema, note any differences SELECT @total_column_count = COUNT(*), @wrong_included_column_count = SUM ( CASE WHEN ic.is_included_column != ckc.is_included_column THEN 1 ELSE 0 END), @wrong_sort_direction_count = SUM ( CASE WHEN ic.is_descending_key != ckc.is_descending_key AND ckc.is_included_column = 0 THEN 1 ELSE 0 END), @unexpected_column_count = SUM ( CASE -- [#index_key_columns].[column_name] will be NULL for any unexpected columns WHEN ckc.column_name IS NULL THEN 1 ELSE 0 END), @out_of_order_key_count = SUM ( CASE WHEN ic.key_ordinal != (ckc.key_ordinal - @key_ordinal_base) AND ckc.is_included_column = 0 THEN 1 ELSE 0 END), @existing_constraint_or_index_name = MIN (i.name), @current_ignore_dup_key = MAX (CASE WHEN i.[ignore_dup_key] = 1 THEN 1 ELSE 0 END), @current_clustered = MAX (CASE WHEN i.type_desc = 'CLUSTERED' THEN 1 ELSE 0 END) FROM sys.indexes AS i INNER JOIN sys.index_columns AS ic ON i.[object_id] = ic.[object_id] AND i.index_id = ic.index_id INNER JOIN sys.columns AS c ON ic.[object_id] = c.[object_id] AND c.column_id = ic.column_id LEFT OUTER JOIN #index_key_columns AS ckc ON c.name = ckc.column_name WHERE i.[object_id] = @table_object_id AND ckc.constraint_name = @constraint_or_index_name AND -- Match index on expected name unless it's a PK, in which case we'll just look for [is_primary_key]=1 (i.name = @constraint_or_index_name OR (@object_type = 'PRIMARY KEY' AND i.is_primary_key = 1)) AND i.is_hypothetical = 0; -- If the constraint doesn't yet exist, or if it exists but is defined incorrectly, we -- need to create it (and may need to drop some related objects before we create it). IF (@total_column_count != @expected_column_count OR @wrong_included_column_count > 0 OR @wrong_sort_direction_count > 0 OR @unexpected_column_count > 0 OR @out_of_order_key_count > 0 OR @current_ignore_dup_key != @ignore_dup_key OR @current_clustered != @clustered) BEGIN -- If this is a clustered index or clustered primary key, drop any NC indexes. -- The calling script will rebuild them later, and if we don't drop them before we -- drop the existing clustered PK/index, we'll end up wasting time by rebuilding -- the nonclustered indexes twice (first when the clustered index is dropped, then -- again when it gets recreated). IF (@clustered = 1) BEGIN DECLARE @nc_index_name sysname; RAISERROR (N' Dropping nonclustered indexes before dropping the clustered index...', 0, 1) WITH NOWAIT; DECLARE nc_indexes INSENSITIVE CURSOR FOR SELECT i.name AS nc_index_name FROM sys.indexes AS i WHERE i.type_desc = 'NONCLUSTERED' AND i.is_unique_constraint = 0 AND i.is_hypothetical = 0 AND i.is_primary_key = 0 AND i.[object_id] = @table_object_id OPEN nc_indexes; FETCH NEXT FROM nc_indexes INTO @nc_index_name; WHILE (@@FETCH_STATUS = 0) BEGIN SET @sql = N'DROP INDEX ' + QUOTENAME (@table_schema) + '.' + QUOTENAME (@table_name) + '.' + QUOTENAME (@nc_index_name) + ';'; RAISERROR (N' Dropping nonclustered index: %s', 0, 1, @sql) WITH NOWAIT; EXEC (@sql); FETCH NEXT FROM nc_indexes INTO @nc_index_name; END; CLOSE nc_indexes; DEALLOCATE nc_indexes; END; -- If this is a primary key referenced by foreign keys, we have to drop the foreign keys before -- we can drop the PK. We also have to drop the foreign keys if we are about to drop a clustered -- primary key to "make room" for a clustered non-PK index. IF ((@object_type = 'PRIMARY KEY') OR (@object_type = 'INDEX' AND @clustered = 1 AND EXISTS (SELECT * FROM sys.indexes AS i WHERE i.[object_id] = @table_object_id AND i.is_primary_key = 1 AND i.type_desc = 'CLUSTERED'))) BEGIN DECLARE @fk_constraint_name sysname; DECLARE @fk_table_schema sysname; DECLARE @fk_table_name sysname; DECLARE fk_constraints INSENSITIVE CURSOR FOR SELECT OBJECT_SCHEMA_NAME (fk.parent_object_id) AS fk_table_schema, OBJECT_NAME (fk.parent_object_id) AS fk_table_name, fk.name AS fk_constraint_name FROM sys.foreign_keys AS fk WHERE referenced_object_id = @table_object_id OPEN fk_constraints; FETCH NEXT FROM fk_constraints INTO @fk_table_schema, @fk_table_name, @fk_constraint_name; WHILE (@@FETCH_STATUS = 0) BEGIN RAISERROR (N' Dropping foreign key [%s].[%s].[%s] because it references table [%s]...', 0, 1, @fk_table_schema, @fk_table_name, @fk_constraint_name, @table_name) WITH NOWAIT; SET @sql = N'ALTER TABLE ' + QUOTENAME (@fk_table_schema) + '.' + QUOTENAME (@fk_table_name) + ' DROP CONSTRAINT ' + QUOTENAME (@fk_constraint_name) + ';'; EXEC (@sql); FETCH NEXT FROM fk_constraints INTO @fk_table_schema, @fk_table_name, @fk_constraint_name; END; CLOSE fk_constraints; DEALLOCATE fk_constraints; END; -- Drop the incorrect constraint, if it exists. IF (@total_column_count > 0) BEGIN RAISERROR (N' Incorrect existing definition (current %d vs. expected %d key columns, %d wrong included columns, %d wrong sort direction, %d unexpected columns, %d out-of-order columns)', 0, 1, @total_column_count, @expected_column_count, @wrong_included_column_count, @wrong_sort_direction_count, @unexpected_column_count, @out_of_order_key_count) WITH NOWAIT; -- Drop the existing malformed constraint or index so that we can recreate it correctly IF @object_type = 'PRIMARY KEY' BEGIN SET @sql = N'ALTER TABLE ' + QUOTENAME (@table_schema) + '.' + QUOTENAME (@table_name) + ' DROP CONSTRAINT ' + QUOTENAME (@existing_constraint_or_index_name) + ';'; END ELSE BEGIN SET @sql = N'DROP INDEX ' + QUOTENAME (@table_schema) + '.' + QUOTENAME (@table_name) + '.' + QUOTENAME (@existing_constraint_or_index_name) + ';'; END; RAISERROR (N' Dropping existing object: %s', 0, 1, @sql) WITH NOWAIT; EXEC (@sql); END; -- If we are about to create a clustered PK index but a clustered (non-PK) index exists, -- we must drop the clustered index before we can create the clustered PK (or vice versa). -- Example: Table has a clustered index, and we need to convert an existing non-clustered -- PK into a clustered PK. IF (@clustered = 1) BEGIN DECLARE @is_primary_key bit; SET @existing_constraint_or_index_name = NULL; SELECT @existing_constraint_or_index_name = i.name, @is_primary_key = is_primary_key FROM sys.indexes AS i WHERE i.type_desc = 'CLUSTERED' AND i.is_hypothetical = 0 AND i.[object_id] = @table_object_id IF (@existing_constraint_or_index_name IS NOT NULL) BEGIN -- Drop the existing clustered index to make way for our clustered PK -- (or drop the existing clustered PK to make way for our clustered index) IF (@is_primary_key = 1) BEGIN SET @sql = N'ALTER TABLE ' + QUOTENAME (@table_schema) + '.' + QUOTENAME (@table_name) + ' DROP CONSTRAINT ' + QUOTENAME (@existing_constraint_or_index_name) + ';'; END ELSE BEGIN SET @sql = N'DROP INDEX ' + QUOTENAME (@table_schema) + '.' + QUOTENAME (@table_name) + '.' + QUOTENAME (@existing_constraint_or_index_name) + ';'; END; RAISERROR (N' Dropping existing clustered index: %s', 0, 1, @sql) WITH NOWAIT; EXEC (@sql); END; END; -- Finally, create the PK or index IF @object_type = 'PRIMARY KEY' BEGIN SET @sql = N'ALTER TABLE ' + QUOTENAME (@table_schema) + '.' + QUOTENAME (@table_name) + ' ADD CONSTRAINT ' + QUOTENAME (@constraint_or_index_name) + ' PRIMARY KEY ' + CASE @clustered WHEN 1 THEN 'CLUSTERED' ELSE 'NONCLUSTERED' END + ' (' + @key_column_list + ')' + CASE @ignore_dup_key WHEN 1 THEN ' WITH (IGNORE_DUP_KEY=ON)' ELSE '' END + ';'; END ELSE BEGIN SET @sql = N'CREATE ' + CASE @clustered WHEN 1 THEN 'CLUSTERED' ELSE 'NONCLUSTERED' END + ' INDEX ' + QUOTENAME (@constraint_or_index_name) + ' ON ' + QUOTENAME (@table_schema) + '.' + QUOTENAME (@table_name) + ' (' + @key_column_list + ')' + CASE WHEN LEN (@include_column_list) > 0 THEN ' INCLUDE (' + @include_column_list + ') ' ELSE '' END + CASE @ignore_dup_key WHEN 1 THEN ' WITH (IGNORE_DUP_KEY=ON)' ELSE '' END + ';'; END; RAISERROR (N' Creating new object: %s', 0, 1, @sql) WITH NOWAIT; EXEC (@sql); END; END; GO -- Create the temp table used to pass column lists to helper proc [#create_or_alter_primary_key_or_index] IF OBJECT_ID ('tempdb..#index_key_columns') IS NOT NULL BEGIN DROP TABLE #index_key_columns; END; GO CREATE TABLE #index_key_columns ( key_ordinal int IDENTITY, constraint_name sysname, column_name sysname, is_included_column bit, is_descending_key bit ); GO -- Start a transaction BEGIN TRANSACTION InstMdwSql GO /**********************************************************************/ /* UPGRADE SECTION - UPDATE EXISTING OBJECTS */ /**********************************************************************/ -- -- >>> CTP5 -> CTP6 Upgrade -- -- Update notable_query_plan IF (OBJECT_ID(N'snapshots.notable_query_plan', 'U') IS NOT NULL) BEGIN RAISERROR ('Updating table [snapshots].[notable_query_plan]...', 0, 1) WITH NOWAIT -- CTP6 added the [plan_generation_num] column to this table IF NOT EXISTS (SELECT column_id FROM sys.all_columns c WHERE c.name = N'plan_generation_num' AND c.object_id = OBJECT_ID(N'snapshots.notable_query_plan', 'U')) BEGIN ALTER TABLE [snapshots].[notable_query_plan] ADD [plan_generation_num] bigint DEFAULT 0 NOT NULL; END; END; GO -- Update active_sessions_and_requests -- Add a column IF (OBJECT_ID(N'snapshots.active_sessions_and_requests', 'U') IS NOT NULL) BEGIN RAISERROR ('Updating table [snapshots].[active_sessions_and_requests]...', 0, 1) WITH NOWAIT IF NOT EXISTS (SELECT column_id FROM sys.all_columns c WHERE c.name = N'is_blocking' AND c.object_id = OBJECT_ID(N'snapshots.active_sessions_and_requests', 'U')) BEGIN ALTER TABLE [snapshots].[active_sessions_and_requests] ADD [is_blocking] bit DEFAULT 0 NOT NULL END; END; GO -- Update performance_counter_instances -- Add Unique constraint for [path] column -- This constraint creation generates a warning. We will deal with the warning when fix for Defect# 130259 is provided IF (OBJECT_ID(N'snapshots.performance_counter_instances', 'U') IS NOT NULL) BEGIN RAISERROR ('Updating table [snapshots].[performance_counter_instances]...', 0, 1) WITH NOWAIT IF NOT EXISTS (SELECT index_id FROM sys.indexes WHERE object_id = OBJECT_ID('snapshots.performance_counter_instances', 'U') AND name = N'UN_performance_counter_path') BEGIN ALTER TABLE [snapshots].[performance_counter_instances] ADD CONSTRAINT [UN_performance_counter_path] UNIQUE ( [path] ) WITH (IGNORE_DUP_KEY = ON) ON [PRIMARY]; END; END; GO -- Update performance_counter_instances IF (OBJECT_ID(N'snapshots.performance_counter_values', 'U') IS NOT NULL) BEGIN RAISERROR ('Updating table [snapshots].[performance_counter_values]...', 0, 1) WITH NOWAIT IF EXISTS (SELECT c.system_type_id FROM sys.columns c WHERE c.object_id = OBJECT_ID('snapshots.performance_counter_values', 'U') AND c.name = N'formatted_value' AND c.system_type_id = 127) BEGIN RAISERROR ('Changing [formatted_value] data type to float...', 0, 1) WITH NOWAIT -- This index will be recreated later (after the table definition) IF EXISTS (SELECT * FROM sys.indexes AS i WHERE i.name = 'IDX_performance_counter_values1' AND [object_id] = OBJECT_ID ('snapshots.performance_counter_values')) BEGIN DROP INDEX [IDX_performance_counter_values1] ON [snapshots].[performance_counter_values]; END; ALTER TABLE [snapshots].[performance_counter_values] ALTER COLUMN [formatted_value] float NOT NULL; END; END; GO -- Change int log_id columns to bigint IF 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 ('core.snapshots_internal') AND c.name = 'log_id' AND t.name = 'int' ) BEGIN RAISERROR ('Changing [core].[snapshots_internal].[log_id] column datatype from int to bigint...', 0, 1) WITH NOWAIT ALTER TABLE [core].[snapshots_internal] ALTER COLUMN [log_id] bigint NOT NULL; END; GO IF (OBJECT_ID ('core.snapshots') IS NOT NULL AND OBJECT_ID ('core.snapshots_internal') IS NOT NULL) BEGIN -- Refresh the view to ensure that it reflects the datatype change EXEC sp_refreshview 'core.snapshots' END GO -- -- >>> CTP6 -> CTP6 Refresh/RC0 Upgrade -- -- Our query stats data collection query merges query stats from sys.dm_exec_query_stats (completed query stats) -- and sys.dm_exec_requests (in-progress query stats). We may not have access to all query stats for any queries -- that were only visible as in-progress queries in dm_exec_requests. For example, this DMV doesn't expose CLR -- stats. Some of the columns in our query_stats table must therefore tolerate NULLs. IF EXISTS ( SELECT c.* FROM INFORMATION_SCHEMA.TABLES AS t INNER JOIN INFORMATION_SCHEMA.COLUMNS AS c ON c.TABLE_SCHEMA = t.TABLE_SCHEMA AND c.TABLE_NAME = t.TABLE_NAME WHERE t.TABLE_SCHEMA = 'snapshots' AND t.TABLE_NAME = 'query_stats' AND c.COLUMN_NAME = 'snapshot_clr_time' AND c.IS_NULLABLE = 'NO' ) BEGIN RAISERROR ('Making [snapshots].[query_stats] columns NULLable...', 0, 1) WITH NOWAIT ALTER TABLE snapshots.query_stats ALTER COLUMN min_clr_time bigint NULL; ALTER TABLE snapshots.query_stats ALTER COLUMN max_clr_time bigint NULL; ALTER TABLE snapshots.query_stats ALTER COLUMN total_clr_time bigint NULL; ALTER TABLE snapshots.query_stats ALTER COLUMN snapshot_clr_time bigint NULL; ALTER TABLE snapshots.query_stats ALTER COLUMN min_worker_time bigint NULL; ALTER TABLE snapshots.query_stats ALTER COLUMN min_physical_reads bigint NULL; ALTER TABLE snapshots.query_stats ALTER COLUMN min_logical_writes bigint NULL; ALTER TABLE snapshots.query_stats ALTER COLUMN min_logical_reads bigint NULL; ALTER TABLE snapshots.query_stats ALTER COLUMN min_elapsed_time bigint NULL; END; GO -- Add the [snapshot_execution_count] column (new to CTP6 Refresh) to [query_stats] IF OBJECT_ID ('snapshots.query_stats', 'U') IS NOT NULL AND NOT EXISTS ( SELECT c.* FROM INFORMATION_SCHEMA.TABLES AS t INNER JOIN INFORMATION_SCHEMA.COLUMNS AS c ON c.TABLE_SCHEMA = t.TABLE_SCHEMA AND c.TABLE_NAME = t.TABLE_NAME WHERE t.TABLE_SCHEMA = 'snapshots' AND t.TABLE_NAME = 'query_stats' AND c.COLUMN_NAME = 'snapshot_execution_count' ) BEGIN RAISERROR ('Adding [snapshot_execution_count] column to [snapshots].[query_stats]...', 0, 1) WITH NOWAIT ALTER TABLE snapshots.query_stats ADD [snapshot_execution_count] bigint NULL; END; GO /**********************************************************************/ /* CORE SCHEMA */ /**********************************************************************/ PRINT '' PRINT 'Create schema core...' GO IF (SCHEMA_ID('core') IS NULL) BEGIN DECLARE @sql nvarchar(128) SET @sql = 'CREATE SCHEMA core' EXEC sp_executesql @sql END GO -- SUPPORTED_COLLECTOR_TYPES -- PRINT '' GO IF (OBJECT_ID(N'[core].[supported_collector_types_internal]', 'U') IS NULL) BEGIN PRINT 'Creating table [core].[supported_collector_types_internal]...' CREATE TABLE [core].[supported_collector_types_internal] ( collector_type_uid uniqueidentifier NOT NULL, ) ON [PRIMARY] END GO -- supported_collector_types_internal.PK_supported_collector_types_internal TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_supported_collector_types_internal', 'collector_type_uid', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'core', @table_name = 'supported_collector_types_internal', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_supported_collector_types_internal', @ignore_dup_key = 0, @clustered = 1; GO IF (NOT OBJECT_ID(N'core.supported_collector_types', 'V') IS NULL) BEGIN PRINT 'Dropping view [core].[supported_collector_types]...' DROP VIEW core.supported_collector_types END GO PRINT 'Creating view [core].[supported_collector_types]...' GO CREATE VIEW core.supported_collector_types AS SELECT collector_type_uid FROM core.supported_collector_types_internal GO -- SOURCE_INFO -- IF (OBJECT_ID(N'[core].[source_info_internal]', 'U') IS NULL) BEGIN PRINT 'Creating table [core].[source_info_internal]...' CREATE TABLE [core].[source_info_internal] ( source_id int IDENTITY NOT NULL, collection_set_uid uniqueidentifier NOT NULL, -- GUID of the collection set that loads the data instance_name sysname COLLATE Latin1_General_CI_AI NOT NULL, -- the name of the machine the data is uploaded from days_until_expiration smallint NOT NULL, -- how many days data from this source should be kept in the warehouse. 0 indicates forever operator sysname NOT NULL, -- login name of the principal who is uploading the data CONSTRAINT [UQ_collection_set_uid_instance_name] UNIQUE (collection_set_uid, instance_name, operator) ) ON [PRIMARY] END GO -- source_info_internal.PK_source_info_internal TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_source_info_internal', 'source_id', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'core', @table_name = 'source_info_internal', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_source_info_internal', @ignore_dup_key = 0, @clustered = 1; GO -- SNAPSHOT_TIMETABLE -- IF (OBJECT_ID(N'[core].[snapshot_timetable_internal]', 'U') IS NULL) BEGIN PRINT 'Creating table [core].[snapshot_timetable_internal]...' CREATE TABLE [core].[snapshot_timetable_internal] ( snapshot_time_id int IDENTITY NOT NULL, snapshot_time datetimeoffset(7) NOT NULL, ) ON [PRIMARY] END GO -- snapshot_timetable_internal.PK_snapshots_timetable_internal TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_snapshots_timetable_internal', 'snapshot_time_id', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'core', @table_name = 'snapshot_timetable_internal', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_snapshots_timetable_internal', @ignore_dup_key = 0, @clustered = 1; GO -- snapshot_timetable_internal.IDX_snapshot_time TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('IDX_snapshot_time', 'snapshot_time', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'core', @table_name = 'snapshot_timetable_internal', @object_type = 'INDEX', @constraint_or_index_name = 'IDX_snapshot_time', @ignore_dup_key = 0, @clustered = 0; GO -- SNAPSHOTS -- IF (OBJECT_ID(N'[core].[snapshots_internal]', 'U') IS NULL) BEGIN PRINT 'Creating table [core].[snapshots_internal]...' CREATE TABLE [core].[snapshots_internal] ( snapshot_id int IDENTITY NOT NULL, snapshot_time_id int NOT NULL, source_id int NOT NULL, log_id bigint NOT NULL, -- reference to the log table ) ON [PRIMARY] CREATE STATISTICS [STAT_snapshots_internal2] ON [core].[snapshots_internal]( [snapshot_time_id], [source_id] ) CREATE STATISTICS [STAT_snapshots_internal3] ON [core].[snapshots_internal]( [snapshot_time_id], [snapshot_id], [source_id]) END GO -- snapshots_internal.PK_snapshots_internal TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_snapshots_internal', 'snapshot_id', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'core', @table_name = 'snapshots_internal', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_snapshots_internal', @ignore_dup_key = 0, @clustered = 1; GO -- snapshots_internal.IDX_snapshot_time_id TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('IDX_snapshot_time_id', 'snapshot_time_id', 0, 0), ('IDX_snapshot_time_id', 'source_id', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'core', @table_name = 'snapshots_internal', @object_type = 'INDEX', @constraint_or_index_name = 'IDX_snapshot_time_id', @ignore_dup_key = 0, @clustered = 0; GO -- snapshots_internal.FK_snapshots_source_info IF OBJECT_ID ('core.FK_snapshots_source_info', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_snapshots_source_info] on core.snapshots_internal ...', 0, 1) WITH NOWAIT; ALTER TABLE core.snapshots_internal ADD CONSTRAINT [FK_snapshots_source_info] FOREIGN KEY(source_id) REFERENCES core.source_info_internal (source_id) END; GO -- snapshots_internal.FK_snapshots_snapshots_timetable IF OBJECT_ID ('core.FK_snapshots_snapshots_timetable', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_snapshots_snapshots_timetable] on core.snapshots_internal ...', 0, 1) WITH NOWAIT; ALTER TABLE core.snapshots_internal ADD CONSTRAINT [FK_snapshots_snapshots_timetable] FOREIGN KEY(snapshot_time_id) REFERENCES core.snapshot_timetable_internal (snapshot_time_id) END; GO -- SNAPSHOTS VIEW -- IF (NOT OBJECT_ID(N'core.snapshots', 'V') IS NULL) BEGIN PRINT 'Dropping view [core].[snapshots]...' DROP VIEW core.snapshots END GO PRINT 'Creating view [core].[snapshots]...' GO CREATE VIEW core.snapshots AS SELECT s.source_id, s.snapshot_id, s.snapshot_time_id, t.snapshot_time, CASE src.days_until_expiration WHEN 0 THEN NULL ELSE DATEADD(DAY, src.days_until_expiration, t.snapshot_time) END AS valid_through, src.instance_name, src.collection_set_uid, src.operator, s.log_id FROM core.source_info_internal src, core.snapshots_internal s, core.snapshot_timetable_internal t WHERE src.source_id = s.source_id AND s.snapshot_time_id = t.snapshot_time_id GO -- -- This stored proc creates a snapshot entry and return the id -- IF (NOT OBJECT_ID(N'core.sp_create_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [core].[sp_create_snapshot] ...' DROP PROCEDURE [core].[sp_create_snapshot] END GO PRINT 'Creating procedure [core].[sp_create_snapshot] ...' GO CREATE PROCEDURE core.sp_create_snapshot @collection_set_uid uniqueidentifier, @collector_type_uid uniqueidentifier, @machine_name sysname, @named_instance sysname, @log_id bigint, @snapshot_id int OUTPUT AS BEGIN SET NOCOUNT ON SET TRANSACTION ISOLATION LEVEL SERIALIZABLE -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'mdw_writer'), 0) = 1) AND NOT (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)) BEGIN RAISERROR(14677, 16, -1, 'mdw_writer') RETURN(1) -- Failure END DECLARE @operator sysname; SELECT @operator = SUSER_SNAME(); DECLARE @instance_name sysname SET @named_instance = NULLIF(RTRIM(LTRIM(@named_instance)), N'') IF (@named_instance = 'MSSQLSERVER') SET @instance_name = @machine_name ELSE SET @instance_name = @machine_name + N'\' + @named_instance -- Parameters check -- Find the source_id that matches the requested collection set and operator DECLARE @source_id int SET @source_id = (SELECT source_id FROM core.source_info_internal WHERE collection_set_uid = @collection_set_uid AND operator = @operator AND instance_name = @instance_name) IF(@source_id IS NULL) BEGIN DECLARE @collection_set_uid_as_char NVARCHAR(36) SELECT @collection_set_uid_as_char = CONVERT(NVARCHAR(36), @collection_set_uid) RAISERROR(14679, -1, -1, N'@collection_set_uid', @collection_set_uid_as_char) END -- Make sure the collector_type is registered in this warehouse IF NOT EXISTS (SELECT collector_type_uid FROM core.supported_collector_types WHERE collector_type_uid = @collector_type_uid) BEGIN DECLARE @collector_type_uid_as_char NVARCHAR(36) SELECT @collector_type_uid_as_char = CONVERT(NVARCHAR(36), @collector_type_uid) RAISERROR(14679, -1, -1, N'@collector_type_uid', @collector_type_uid_as_char) END -- Get the snapshot time BEGIN TRY BEGIN TRAN DECLARE @snapshot_time_id int IF NOT EXISTS (SELECT snapshot_time_id FROM core.snapshot_timetable_internal WITH(UPDLOCK) WHERE snapshot_time > DATEADD (minute, -1, SYSDATETIMEOFFSET())) BEGIN INSERT INTO core.snapshot_timetable_internal ( snapshot_time ) VALUES ( SYSDATETIMEOFFSET() ) SET @snapshot_time_id = SCOPE_IDENTITY() END ELSE BEGIN SET @snapshot_time_id = (SELECT MAX(snapshot_time_id) FROM core.snapshot_timetable_internal) END -- Finally insert an entry into snapshots table INSERT INTO core.snapshots_internal ( snapshot_time_id, source_id, log_id ) VALUES ( @snapshot_time_id, @source_id, @log_id ) SET @snapshot_id = SCOPE_IDENTITY() IF (@snapshot_id IS NULL) BEGIN RAISERROR(14262, -1, -1, '@snapshot_id', @snapshot_id) RETURN(1) END ELSE BEGIN COMMIT TRAN END END TRY BEGIN CATCH IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION -- Rethrow the error 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 -- -- This stored proc updates data in source_info table -- IF (NOT OBJECT_ID(N'core.sp_update_data_source', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [core].[sp_update_data_source] ...' DROP PROCEDURE [core].[sp_update_data_source] END GO PRINT 'Creating procedure [core].[sp_update_data_source] ...' GO CREATE PROCEDURE [core].[sp_update_data_source] @collection_set_uid uniqueidentifier, @machine_name sysname, @named_instance sysname, @days_until_expiration smallint, @source_id int OUTPUT AS BEGIN SET NOCOUNT ON SET TRANSACTION ISOLATION LEVEL SERIALIZABLE -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'mdw_writer'), 0) = 1) AND NOT (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)) BEGIN RAISERROR(14677, 16, -1, 'mdw_writer') RETURN(1) -- Failure END -- Parameters check IF (@collection_set_uid IS NULL) BEGIN RAISERROR(14200, -1, -1, '@collection_set_uid') RETURN(1) -- Failure END SET @machine_name = NULLIF(RTRIM(LTRIM(@machine_name)), N'') IF (@machine_name IS NULL) BEGIN RAISERROR(14200, -1, -1, '@machine_name') RETURN(1) -- Failure END DECLARE @instance_name sysname SET @named_instance = NULLIF(RTRIM(LTRIM(@named_instance)), N'') IF (@named_instance = 'MSSQLSERVER') SET @instance_name = @machine_name ELSE SET @instance_name = @machine_name + N'\' + @named_instance IF (@days_until_expiration IS NULL) BEGIN RAISERROR(14200, -1, -1, '@days_until_expiration') RETURN(1) -- Failure END IF (@days_until_expiration < 0) BEGIN RAISERROR(14266, -1, -1, '@days_until_expiration', ' >= 0') RETURN (1) -- Failure END DECLARE @operator sysname SELECT @operator = SUSER_SNAME() BEGIN TRY BEGIN TRAN -- Insert data into the table -- We specify the lock hint, in order to keep the exlusive lock on the row from the moment we select it till we -- update it SET @source_id = (SELECT source_id FROM core.source_info_internal WITH(UPDLOCK) WHERE collection_set_uid = @collection_set_uid AND instance_name = @instance_name AND operator = @operator) IF @source_id IS NULL BEGIN INSERT INTO core.source_info_internal ( collection_set_uid, instance_name, days_until_expiration, operator ) VALUES ( @collection_set_uid, @instance_name, @days_until_expiration, @operator ) SET @source_id = SCOPE_IDENTITY() END ELSE BEGIN UPDATE core.source_info_internal SET days_until_expiration = @days_until_expiration WHERE source_id = @source_id; END COMMIT TRAN RETURN (0) END TRY BEGIN CATCH IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION -- Rethrow the error 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); RETURN (1) END CATCH END GO -- -- This stored proc adds new entry in supported_collector_types table -- IF (NOT OBJECT_ID(N'core.sp_add_collector_type', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [core].[sp_add_collector_type] ...' DROP PROCEDURE [core].[sp_add_collector_type] END GO PRINT 'Creating procedure [core].[sp_add_collector_type] ...' GO CREATE PROCEDURE [core].[sp_add_collector_type] @collector_type_uid uniqueidentifier AS BEGIN -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'mdw_admin'), 0) = 1) AND NOT (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)) BEGIN RAISERROR(14677, 16, -1, 'mdw_admin') RETURN(1) -- Failure END -- Parameters check IF (@collector_type_uid IS NULL) BEGIN RAISERROR(14200, -1, -1, '@collector_type_uid') RETURN(1) -- Failure END -- Insert new collector type IF NOT EXISTS (SELECT collector_type_uid FROM core.supported_collector_types WHERE collector_type_uid = @collector_type_uid) BEGIN INSERT INTO core.supported_collector_types ( collector_type_uid ) VALUES ( @collector_type_uid ) END ELSE BEGIN -- Raise an info message, but do not fail DECLARE @collector_type_uid_as_char NVARCHAR(36) SELECT @collector_type_uid_as_char = CONVERT(NVARCHAR(36), @collector_type_uid) RAISERROR(14261, 10, -1, '@collector_type_uid', @collector_type_uid_as_char) END RETURN (0) END GO -- -- This stored proc removes and existing entry from supported_collector_types table -- IF (NOT OBJECT_ID(N'core.sp_remove_collector_type', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [core].[sp_remove_collector_type] ...' DROP PROCEDURE [core].[sp_remove_collector_type] END GO PRINT 'Creating procedure [core].[sp_remove_collector_type] ...' GO CREATE PROCEDURE [core].[sp_remove_collector_type] @collector_type_uid uniqueidentifier AS BEGIN -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'mdw_admin'), 0) = 1) AND NOT (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)) BEGIN RAISERROR(14677, 16, -1, 'mdw_admin') RETURN(1) -- Failure END -- Parameters check IF (@collector_type_uid IS NULL) BEGIN RAISERROR(14200, -1, -1, '@collector_type_uid') RETURN(1) -- Failure END -- Delete collector type IF EXISTS (SELECT collector_type_uid FROM core.supported_collector_types WHERE collector_type_uid = @collector_type_uid) BEGIN DELETE FROM core.supported_collector_types WHERE collector_type_uid = @collector_type_uid END ELSE BEGIN DECLARE @collector_type_uid_as_char NVARCHAR(36) SELECT @collector_type_uid_as_char = CONVERT(NVARCHAR(36), @collector_type_uid) RAISERROR(14262, -1, -1, '@collector_type_uid', @collector_type_uid_as_char) RETURN (1) -- Failure END RETURN (0) END GO -- -- This table is used to determine status of current purge operation. -- Contains records only if a sp_stop_purge operation was requested. -- PRINT '' GO IF (OBJECT_ID(N'[core].[purge_info_internal]', 'U') IS NULL) BEGIN PRINT 'Creating table [core].[purge_info_internal]...' CREATE TABLE [core].[purge_info_internal] ( stop_purge bit NOT NULL, ) ON [PRIMARY] END GO -- -- This stored proc removes data from the warehouse that reached its expiration date -- IF (NOT OBJECT_ID(N'core.sp_purge_data', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [core].[sp_purge_data] ...' DROP PROCEDURE [core].[sp_purge_data] END GO PRINT 'Creating procedure [core].[sp_purge_data] ...' GO CREATE PROCEDURE [core].[sp_purge_data] @retention_days smallint = NULL, @instance_name sysname = NULL, @collection_set_uid uniqueidentifier = NULL, @duration smallint = NULL AS BEGIN -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'mdw_admin'), 0) = 1) AND NOT (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)) BEGIN RAISERROR(14677, 16, -1, 'mdw_admin') RETURN(1) -- Failure END -- Validate parameters IF ((@retention_days IS NOT NULL) AND (@retention_days < 0)) BEGIN RAISERROR(14200, -1, -1, '@retention_days') RETURN(1) -- Failure END IF ((@duration IS NOT NULL) AND (@duration < 0)) BEGIN RAISERROR(14200, -1, -1, '@duration') RETURN(1) -- Failure END -- This table will contain a record if somebody requests purge to stop -- If user requested us to purge data - we reset the content of it - and proceed with purge -- If somebody in a different session wants purge operations to stop he adds a record -- that we will discover while purge in progress -- -- We dont clear this flag when we exit since multiple purge operations with differnet -- filters may proceed, and we want all of them to stop. DELETE FROM [core].[purge_info_internal] SET @instance_name = NULLIF(LTRIM(RTRIM(@instance_name)), N'') -- Calculate the time when the operation should stop (NULL otherwise) DECLARE @end_time datetime IF (@duration IS NOT NULL) BEGIN SET @end_time = DATEADD(minute, @duration, GETUTCDATE()) END -- Declare table that will be used to find what are the valid -- candidate snapshots that could be selected for purge DECLARE @purge_candidates table ( snapshot_id int NOT NULL, snapshot_time datetime NOT NULL, instance_name sysname NOT NULL, collection_set_uid uniqueidentifier NOT NULL ) -- Find candidates that match the retention_days criteria (if specified) IF (@retention_days IS NULL) BEGIN -- User did not specified a value for @retention_days, therfore we -- will use the default expiration day as marked in the source info INSERT INTO @purge_candidates SELECT s.snapshot_id, s.snapshot_time, s.instance_name, s.collection_set_uid FROM core.snapshots s WHERE (GETUTCDATE() >= s.valid_through) END ELSE BEGIN -- User specified a value for @retention_days, we will use this overriden value -- when deciding what means old enough to qualify for purge this overrides -- the days_until_expiration value specified in the source_info_internal table INSERT INTO @purge_candidates SELECT s.snapshot_id, s.snapshot_time, s.instance_name, s.collection_set_uid FROM core.snapshots s WHERE GETUTCDATE() >= DATEADD(DAY, @retention_days, s.snapshot_time) END -- Determine which is the oldest snapshot, from the list of candidates DECLARE oldest_snapshot_cursor CURSOR FORWARD_ONLY READ_ONLY FOR SELECT p.snapshot_id, p.instance_name, p.collection_set_uid FROM @purge_candidates p WHERE ((@instance_name IS NULL) or (p.instance_name = @instance_name)) AND ((@collection_set_uid IS NULL) or (p.collection_set_uid = @collection_set_uid)) ORDER BY p.snapshot_time ASC OPEN oldest_snapshot_cursor DECLARE @stop_purge int DECLARE @oldest_snapshot_id int DECLARE @oldest_instance_name sysname DECLARE @oldest_collection_set_uid uniqueidentifier FETCH NEXT FROM oldest_snapshot_cursor INTO @oldest_snapshot_id, @oldest_instance_name, @oldest_collection_set_uid -- As long as there are snapshots that matched the time criteria WHILE @@FETCH_STATUS = 0 BEGIN -- Filter out records that do not match the other filter crieria IF ((@instance_name IS NULL) or (@oldest_instance_name = @instance_name)) BEGIN -- There was no filter specified for instance_name or the instance matches the filter IF ((@collection_set_uid IS NULL) or (@oldest_collection_set_uid = @collection_set_uid)) BEGIN -- There was no filter specified for the collection_set_uid or the collection_set_uid matches the filter BEGIN TRANSACTION tran_sp_purge_data -- Purge data associated with this snapshot. Note: deleting this snapshot -- triggers cascade delete in all warehouse tables based on the foreign key -- relationship to snapshots table -- Cascade cleanup of all data related referencing oldest snapshot DELETE core.snapshots_internal FROM core.snapshots_internal s WHERE s.snapshot_id = @oldest_snapshot_id COMMIT TRANSACTION tran_sp_purge_data PRINT 'Snapshot #' + CONVERT(NVARCHAR(MAX),@oldest_snapshot_id) + ' purged.'; END END -- Check if the execution of the stored proc exceeded the @duration specified IF (@duration IS NOT NULL) BEGIN IF (GETUTCDATE()>=@end_time) BEGIN PRINT 'Stopping purge. More than ' + CONVERT(NVARCHAR(MAX),@duration) + ' minutes passed since the start of operation.'; BREAK END END -- Check if somebody wanted to stop the purge operation SELECT @stop_purge = COUNT(stop_purge) FROM [core].[purge_info_internal] IF (@stop_purge > 0) BEGIN PRINT 'Stopping purge. Detected a user request to stop purge.'; BREAK END -- Move to next oldest snapshot FETCH NEXT FROM oldest_snapshot_cursor INTO @oldest_snapshot_id, @oldest_instance_name, @oldest_collection_set_uid END CLOSE oldest_snapshot_cursor DEALLOCATE oldest_snapshot_cursor -- Delete orphaned rows from snapshots.notable_query_plan. Query plans are not deleted by the generic purge -- process that deletes other data (above) because query plan rows are not tied to a particular snapshot ID. -- Purging query plans table and the smaller query text table as a special case, by looking for plans that -- are no longer referenced by any of the rows in the snapshots.query_stats table. We need to delete these -- rows in small chunks, since deleting many GB in a single delete statement would cause lock escalation and -- an explosion in the size of the transaction log (individual query plans can be 10-50MB). DECLARE @delete_batch_size bigint; DECLARE @rows_affected int; SET @delete_batch_size = 500; SET @rows_affected = 500; WHILE (@rows_affected = @delete_batch_size) BEGIN DELETE TOP (@delete_batch_size) snapshots.notable_query_plan FROM snapshots.notable_query_plan AS qp WHERE NOT EXISTS ( SELECT snapshot_id FROM snapshots.query_stats AS qs WHERE qs.[sql_handle] = qp.[sql_handle] AND qs.plan_handle = qp.plan_handle AND qs.plan_generation_num = qp.plan_generation_num AND qs.statement_start_offset = qp.statement_start_offset AND qs.statement_end_offset = qp.statement_end_offset AND qs.creation_time = qp.creation_time); SET @rows_affected = @@ROWCOUNT; IF(@rows_affected > 0) BEGIN RAISERROR ('Deleted %d orphaned rows from snapshots.notable_query_plan', 0, 1, @rows_affected) WITH NOWAIT; END -- Check if the execution of the stored proc exceeded the @duration specified IF (@duration IS NOT NULL) BEGIN IF (GETUTCDATE()>=@end_time) BEGIN PRINT 'Stopping purge. More than ' + CONVERT(NVARCHAR(MAX),@duration) + ' minutes passed since the start of operation.'; BREAK END END -- Check if somebody wanted to stop the purge operation SELECT @stop_purge = COUNT(stop_purge) FROM [core].[purge_info_internal] IF (@stop_purge > 0) BEGIN PRINT 'Stopping purge. Detected a user request to stop purge.'; BREAK END END; -- Do the same purge process for query text rows in the snapshots.notable_query_text table. SET @rows_affected = 500; WHILE (@rows_affected = @delete_batch_size) BEGIN DELETE TOP (@delete_batch_size) snapshots.notable_query_text FROM snapshots.notable_query_text AS qt WHERE NOT EXISTS ( SELECT snapshot_id FROM snapshots.query_stats AS qs WHERE qs.[sql_handle] = qt.[sql_handle]); SET @rows_affected = @@ROWCOUNT; IF(@rows_affected > 0) BEGIN RAISERROR ('Deleted %d orphaned rows from snapshots.notable_query_text', 0, 1, @rows_affected) WITH NOWAIT; END -- Check if the execution of the stored proc exceeded the @duration specified IF (@duration IS NOT NULL) BEGIN IF (GETUTCDATE()>=@end_time) BEGIN PRINT 'Stopping purge. More than ' + CONVERT(NVARCHAR(MAX),@duration) + ' minutes passed since the start of operation.'; BREAK END END -- Check if somebody wanted to stop the purge operation SELECT @stop_purge = COUNT(stop_purge) FROM [core].[purge_info_internal] IF (@stop_purge > 0) BEGIN PRINT 'Stopping purge. Detected a user request to stop purge.'; BREAK END END; END GO -- -- This stored procedure is used to stop all purge operations in progress -- IF (NOT OBJECT_ID(N'core.sp_stop_purge', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [core].[sp_stop_purge] ...' DROP PROCEDURE [core].[sp_stop_purge] END GO PRINT 'Creating procedure [core].[sp_stop_purge] ...' GO CREATE PROCEDURE [core].[sp_stop_purge] AS BEGIN INSERT INTO [core].[purge_info_internal] (stop_purge) VALUES (1) END GO -- -- This function verifies if the caller matches the operator set for the snapshot -- IF (OBJECT_ID(N'core.fn_check_operator', 'FN') IS NULL) BEGIN PRINT 'Creating function [core].[fn_check_operator] ...' DECLARE @sql nvarchar(max) SET @sql = 'CREATE FUNCTION [core].[fn_check_operator]( @snapshot_id int ) RETURNS bit AS BEGIN DECLARE @retval bit; DECLARE @operator sysname; SELECT @operator=operator FROM core.snapshots WHERE snapshot_id = @snapshot_id; IF (@operator = SUSER_SNAME()) SELECT @retval = 1; ELSE SELECT @retval = 0; RETURN @retval; END;' EXEC sp_executesql @sql; END GO -- Table wait_categories -- -- This table contains CPU and wait categories. -- IF (NOT OBJECT_ID(N'core.wait_types', 'U') IS NULL) BEGIN -- Must drop wait_types first since it references wait_categories via FK PRINT 'Dropping table [core].[wait_types] ...' DROP TABLE [core].[wait_types] END IF (NOT OBJECT_ID(N'core.wait_categories', 'U') IS NULL) BEGIN PRINT 'Dropping table [core].[wait_categories] ...' DROP TABLE [core].[wait_categories] END PRINT 'Creating table [core].[wait_categories] ...' CREATE TABLE [core].[wait_categories]( [category_id] [smallint] NOT NULL, [category_name] [nvarchar](20) NOT NULL, [ignore] [bit] NOT NULL, CONSTRAINT [PK_categories] PRIMARY KEY CLUSTERED ([category_id] ASC)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY] GO -- Table wait_types -- -- This table contains relation between cpu/wait events and cpu/wait categories for each version of SQL Server. -- -- PRINT 'Creating table [core].[wait_types] ...' CREATE TABLE [core].[wait_types]( [category_id] [smallint] NOT NULL, [wait_type] [nvarchar](45) NOT NULL, CONSTRAINT [PK_events] PRIMARY KEY CLUSTERED ([wait_type] ASC) WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY] ALTER TABLE [core].[wait_types] ADD CONSTRAINT [FK_events_categories] FOREIGN KEY([category_id]) REFERENCES [core].[wait_categories] ([category_id]) GO IF (NOT OBJECT_ID(N'core.wait_types_categorized', 'V') IS NULL) BEGIN PRINT 'Dropping view [core].[wait_types_categorized] ...' DROP VIEW [core].[wait_types_categorized]; END; GO PRINT 'Creating view [core].[wait_types_categorized] ...' GO CREATE VIEW [core].[wait_types_categorized] AS SELECT ct.category_name, ev.wait_type, ct.category_id, ct.ignore FROM core.wait_categories ct INNER JOIN core.wait_types ev ON (ev.category_id = ct.category_id) GO -- Insert Categories -- SET NOCOUNT ON; INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (0, N'CPU', 0); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (1, N'Backup', 0); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (2, N'SQLCLR', 0); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (3, N'Parallelism', 1); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (4, N'Latch', 0); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (5, N'Lock', 0); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (6, N'Network I/O', 0); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (7, N'Buffer I/O', 0); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (8, N'Buffer Latch', 0); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (9, N'Memory', 0); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (10, N'Logging', 0); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (11, N'Compilation', 0); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (12, N'Transaction', 0); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (13, N'Idle', 1); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (14, N'User Waits', 1); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (15, N'Other', 0); INSERT INTO [core].[wait_categories] ([category_id], [category_name], [ignore]) VALUES (16, N'Full Text Search', 0); -- Insert Events -- INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (0, N'CPU'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'MISCELLANEOUS'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_SCH_S'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_SCH_M'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_S'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_U'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_X'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_IS'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_IU'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_IX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_SIU'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_SIX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_UIX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_BU'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_RS_S'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_RS_U'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_RIn_NL'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_RIn_S'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_RIn_U'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_RIn_X'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_RX_S'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_RX_U'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (5, N'LCK_M_RX_X'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (4, N'LATCH_NL'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (4, N'LATCH_KP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (4, N'LATCH_SH'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (4, N'LATCH_UP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (4, N'LATCH_EX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (4, N'LATCH_DT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (8, N'PAGELATCH_NL'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (8, N'PAGELATCH_KP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (8, N'PAGELATCH_SH'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (8, N'PAGELATCH_UP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (8, N'PAGELATCH_EX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (8, N'PAGELATCH_DT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (7, N'PAGEIOLATCH_NL'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (7, N'PAGEIOLATCH_KP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (7, N'PAGEIOLATCH_SH'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (7, N'PAGEIOLATCH_UP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (7, N'PAGEIOLATCH_EX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (7, N'PAGEIOLATCH_DT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'TRAN_MARKLATCH_NL'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'TRAN_MARKLATCH_KP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'TRAN_MARKLATCH_SH'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'TRAN_MARKLATCH_UP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'TRAN_MARKLATCH_EX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'TRAN_MARKLATCH_DT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'LAZYWRITER_SLEEP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (7, N'IO_COMPLETION'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (7, N'ASYNC_IO_COMPLETION'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (6, N'ASYNC_NETWORK_IO'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'SLEEP_BPOOL_FLUSH'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'CHKPT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'SLEEP_TASK'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'SLEEP_SYSTEMTASK'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (9, N'RESOURCE_SEMAPHORE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'DTC'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (6, N'OLEDB'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'FAILPOINT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'RESOURCE_QUEUE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (7, N'ASYNC_DISKPOOL_LOCK'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'THREADPOOL'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DEBUG'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (7, N'REPLICA_WRITES'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'BROKER_RECEIVE_WAITFOR'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DBMIRRORING_CMD'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'WAIT_FOR_RESULTS'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (0, N'SOS_SCHEDULER_YIELD'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (9, N'SOS_VIRTUALMEMORY_LOW'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (9, N'SOS_RESERVEDMEMBLOCKLIST'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOS_LOCALALLOCATORLIST'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOS_CALLBACK_REMOVAL'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (9, N'LOWFAIL_MEMMGR_QUEUE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (1, N'BACKUP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (1, N'BACKUPBUFFER'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (1, N'BACKUPIO'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (1, N'BACKUPTHREAD'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DBMIRROR_DBM_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DBMIRROR_DBM_EVENT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (6, N'DBMIRROR_SEND'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'CURSOR_ASYNC'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'HTTP_ENUMERATION'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (16, N'SOAP_READ'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (16, N'SOAP_WRITE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DUMP_LOG_COORDINATOR'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (1, N'DISKIO_SUSPEND'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'IMPPROV_IOWAIT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'QNMANAGER_ACQUIRE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DEADLOCK_TASK_SEARCH'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'REPL_SCHEMA_ACCESS'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'REPL_CACHE_ACCESS'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SQLSORT_SORTMUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SQLSORT_NORMMUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SQLTRACE_WAIT_ENTRIES'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SQLTRACE_LOCK'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'SQLTRACE_BUFFER_FLUSH'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SQLTRACE_SHUTDOWN'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'MSQL_SYNC_PIPE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'QUERY_TRACEOUT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (6, N'DTC_STATE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (7, N'FCB_REPLICA_WRITE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (7, N'FCB_REPLICA_READ'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (10, N'WRITELOG'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'HTTP_ENDPOINT_COLLCREATE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (3, N'EXCHANGE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DBTABLE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'EC'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'TEMPOBJ'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'XACTLOCKINFO'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (10, N'LOGMGR'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (9, N'CMEMTHREAD'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (3, N'CXPACKET'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (14, N'WAITFOR'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'CURSOR'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (3, N'EXECSYNC'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOSHOST_INTERNAL'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOSHOST_SLEEP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOSHOST_WAITFORDONE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOSHOST_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOSHOST_EVENT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOSHOST_SEMAPHORE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOSHOST_RWLOCK'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOSHOST_TRACELOCK'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'MSQL_XP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (6, N'MSQL_DQ'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (10, N'LOGBUFFER'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'TRANSACTION_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (16, N'MSSEARCH'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'XACTWORKSPACE_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'CLR_JOIN'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'CLR_CRST'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'CLR_SEMAPHORE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'CLR_MANUAL_EVENT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'CLR_AUTO_EVENT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'CLR_MONITOR'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'CLR_RWLOCK_READER'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'CLR_RWLOCK_WRITER'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'SQLCLR_QUANTUM_PUNISHMENT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'SQLCLR_APPDOMAIN'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'SQLCLR_ASSEMBLY'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'KTM_ENLISTMENT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'KTM_RECOVERY_RESOLUTION'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'KTM_RECOVERY_MANAGER'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'SQLCLR_DEADLOCK_DETECTION'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'QPJOB_WAITFOR_ABORT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'QPJOB_KILL'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'BAD_PAGE_PROCESS'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (1, N'BACKUP_OPERATOR'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'PRINT_ROLLBACK_PROGRESS'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'ENABLE_VERSIONING'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DISABLE_VERSIONING'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'REQUEST_DISPENSER_PAUSE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DROPTEMP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'FT_RESTART_CRAWL'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'FT_RESUME_CRAWL'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (10, N'LOGMGR_RESERVE_APPEND'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (10, N'LOGMGR_FLUSH'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'XACT_OWN_TRANSACTION'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'XACT_RECLAIM_SESSION'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'DTC_WAITFOR_OUTCOME'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'DTC_RESOLVE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SEC_DROP_TEMP_KEY'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SRVPROC_SHUTDOWN'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (6, N'NET_WAITFOR_PACKET'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'DTC_ABORT_REQUEST'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'DTC_TMDOWN_REQUEST'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'RECOVER_CHANGEDB'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'WORKTBL_DROP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'MIRROR_SEND_MESSAGE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'SNI_HTTP_ACCEPT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SNI_HTTP_WAITFOR_0_DISCON'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (9, N'UTIL_PAGE_ALLOC'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'SERVER_IDLE_CHECK'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (1, N'BACKUP_CLIENTLOCK'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (4, N'DEADLOCK_ENUM_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (4, N'INDEX_USAGE_STATS_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (4, N'VIEW_DEFINITION_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'QUERY_NOTIFICATION_MGR_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'QUERY_NOTIFICATION_TABLE_MGR_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'QUERY_NOTIFICATION_SUBSCRIPTION_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'QUERY_NOTIFICATION_UNITTEST_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'IMP_IMPORT_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (11, N'RESOURCE_SEMAPHORE_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'IO_AUDIT_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'BUILTIN_HASHKEY_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOS_PROCESS_AFFINITY_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'MSQL_XACT_MGR_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (12, N'MSQL_XACT_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'QRY_MEM_GRANT_INFO_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOS_STACKSTORE_INIT_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOS_SYNC_TASK_ENQUEUE_EVENT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOS_OBJECT_STORE_DESTROY_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'EE_PMOLOCK'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (11, N'RESOURCE_SEMAPHORE_QUERY_COMPILE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (11, N'RESOURCE_SEMAPHORE_SMALL_QUERY'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'ABR'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'ASSEMBLY_LOAD'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'BROKER_CONNECTION_RECEIVE_TASK'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'BROKER_ENDPOINT_STATE_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'BROKER_EVENTHANDLER'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'BROKER_INIT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'BROKER_MASTERSTART'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'BROKER_REGISTERALLENDPOINTS'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'BROKER_SHUTDOWN'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'BROKER_TASK_STOP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'BROKER_TRANSMITTER'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'CHECK_PRINT_RECORD'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'CHECKPOINT_QUEUE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'CLR_MEMORY_SPY'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'CLR_TASK_START'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'CLRHOST_STATE_ACCESS'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DAC_INIT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DBCC_COLUMN_TRANSLATION_CACHE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DBMIRROR_EVENTS_QUEUE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DBMIRROR_WORKER_QUEUE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DLL_LOADING_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DUMP_LOG_COORDINATOR_QUEUE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'DUMPTRIGGER'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'EE_SPECPROC_MAP_INIT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'ERROR_REPORTING_MANAGER'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'EXECUTION_PIPE_EVENT_INTERNAL'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (2, N'FS_GARBAGE_COLLECTOR_SHUTDOWN'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'FULLTEXT GATHERER'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'GUARDIAN'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'HTTP_START'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'INTERNAL_TESTING'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'KSOURCE_WAKEUP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'LOGMGR_QUEUE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'ONDEMAND_TASK_QUEUE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'PARALLEL_BACKUP_QUEUE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'QUERY_ERRHDL_SERVICE_DONE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'QUERY_EXECUTION_INDEX_SORT_EVENT_OPEN'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'QUERY_OPTIMIZER_PRINT_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'QUERY_REMOTE_BRICKS_DONE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'REQUEST_FOR_DEADLOCK_SEARCH'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SEQUENTIAL_GUID'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SHUTDOWN'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'SLEEP_DBSTARTUP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'SLEEP_DCOMSTARTUP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'SLEEP_MSDBSTARTUP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'SLEEP_TEMPDBSTARTUP'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SNI_CRITICAL_SECTION'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SNI_LISTENER_ACCESS'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SNI_TASK_COMPLETION'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'SOS_DISPATCHER_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'TIMEPRIV_TIMEPERIOD'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'TRACEWRITE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'VIA_ACCEPT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'WAITFOR_TASKSHUTDOWN'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'WAITSTAT_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'WCC'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'XE_TIMER_EVENT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'XE_DISPATCHER_WAIT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (13, N'FSAGENT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'XE_TIMER_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'XE_TIMER_TASK_DONE'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'XE_BUFFERMGR_ALLPROCECESSED_EVENT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'XE_BUFFERMGR_FREEBUF_EVENT'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'XE_DISPATCHER_JOIN'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'XE_MODULEMGR_SYNC'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'XE_OLS_LOCK'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'XE_SERVICES_MUTEX'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'XE_SESSION_CREATE_SYNC'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'XE_SESSION_SYNC'); INSERT INTO [core].[wait_types] ([category_id],[wait_type]) VALUES (15, N'XE_STM_CREATE') GO IF (NOT OBJECT_ID(N'core.fn_query_text_from_handle', 'TF') IS NULL) BEGIN PRINT 'Dropping function [core].[fn_query_text_from_handle] ...' DROP FUNCTION [core].[fn_query_text_from_handle] END GO PRINT 'Creating function [core].[fn_query_text_from_handle] ...' GO CREATE FUNCTION [core].[fn_query_text_from_handle]( @handle varbinary(64), @statement_start_offset int, @statement_end_offset int) RETURNS @query_text TABLE (database_id smallint, object_id int, encrypted bit, query_text nvarchar(max)) BEGIN IF @handle IS NOT NULL BEGIN DECLARE @start int, @end int DECLARE @dbid smallint, @objectid int, @encrypted bit DECLARE @batch nvarchar(max), @query nvarchar(max) -- statement_end_offset is zero prior to beginning query execution (e.g., compilation) SELECT @start = ISNULL(@statement_start_offset, 0), @end = CASE WHEN @statement_end_offset is null or @statement_end_offset = 0 THEN -1 ELSE @statement_end_offset END SELECT @dbid = t.dbid, @objectid = t.objectid, @encrypted = t.encrypted, @batch = t.text FROM sys.dm_exec_sql_text(@handle) AS t SELECT @query = CASE WHEN @encrypted = CAST(1 as bit) THEN N'encrypted text' ELSE LTRIM(SUBSTRING(@batch, @start / 2 + 1, ((CASE WHEN @end = -1 THEN DATALENGTH(@batch) ELSE @end END) - @start) / 2)) end -- Found internal queries (e.g., CREATE INDEX) with end offset of original batch that is -- greater than the length of the internal query and thus returns nothing if we don''t do this IF DATALENGTH(@query) = 0 BEGIN SELECT @query = @batch END INSERT INTO @query_text (database_id, object_id, encrypted, query_text) VALUES (@dbid, @objectid, @encrypted, @query) END RETURN END GO -- Table performance_counter_report_groups -- -- This table lists the counters that are used by each report. Referenced in [snapshots].[rpt_generic_perfmon]. -- IF (NOT OBJECT_ID(N'[core].[performance_counter_report_group_items]', 'U') IS NULL) BEGIN -- Must drop wait_types first since it references wait_categories via FK PRINT 'Dropping table [core].[performance_counter_report_group_items] ...' DROP TABLE [core].[performance_counter_report_group_items] END GO PRINT 'Creating table [core].[performance_counter_report_group_items] ...' CREATE TABLE [core].[performance_counter_report_group_items]( [counter_group_item_id] int IDENTITY NOT NULL PRIMARY KEY CLUSTERED, [counter_group_id] nvarchar(128) NOT NULL, -- e.g. report name [counter_subgroup_id] nvarchar(128) NOT NULL, -- e.g. chart name, used by the chart to filter out its rows from the larger resultset [series_name] nvarchar(512) NOT NULL, -- data series label used for output [object_name] nvarchar(2048) NOT NULL, -- perfmon object name [object_name_wildcards] bit NOT NULL, -- 1 if wildcard expansion is needed for the perfmon object name (e.g. '%SQL%:General Statistics'), 0 otherwise [counter_name] nvarchar(2048) NOT NULL, -- perfmon counter name [instance_name] nvarchar(2048) NULL, -- perfmon instance name(s) to include (evaluated with LIKE) [not_instance_name] nvarchar(2048) NULL, -- perfmon instance name(s) to omit (evaluated with LIKE, use NULL to skip this criteria) [multiply_by] numeric(28,10) NOT NULL, -- value to multiple the counter by on output (for unit conversion) [divide_by_cpu_count] bit NOT NULL -- 1 if counter should be divided by the machine's CPU count (e.g. "Process(abc)\% Processor Time", 0 otherwise ) GO -- Insert perfmon counter list for each report group -- DECLARE @CONVERT_BYTES_TO_MB numeric(28,10) DECLARE @CONVERT_KB_TO_MB numeric(28,10) SET @CONVERT_BYTES_TO_MB = 1.0 / (1024*1024) SET @CONVERT_KB_TO_MB = 1.0 / (1024); INSERT INTO [core].[performance_counter_report_group_items] ([counter_group_id],[counter_subgroup_id],[series_name], [object_name],[object_name_wildcards],[counter_name],[instance_name],[not_instance_name],[multiply_by],[divide_by_cpu_count]) VALUES ('ServerActivity', 'memoryUsage', 'System', 'Process', 0, 'Working Set', '_Total', NULL, @CONVERT_BYTES_TO_MB, 0 ), ('ServerActivity', 'memoryUsage', 'SQL Server', 'Process', 0, 'Working Set', '$(TARGETPROCESS)', NULL, @CONVERT_BYTES_TO_MB, 0 ), ('ServerActivity', 'IOUsage', 'System', 'Process', 0, 'IO Read Bytes/sec', '_Total', NULL, @CONVERT_BYTES_TO_MB, 0 ), ('ServerActivity', 'IOUsage', 'System', 'Process', 0, 'IO Write Bytes/sec', '_Total', NULL, @CONVERT_BYTES_TO_MB, 0 ), -- changed to fix Bug # 234905 ('ServerActivity', 'IOUsage', 'SQL Server', 'Process', 0, 'IO Read Bytes/sec', '$(TARGETPROCESS)', NULL, @CONVERT_BYTES_TO_MB, 0 ), ('ServerActivity', 'IOUsage', 'SQL Server', 'Process', 0, 'IO Write Bytes/sec', '$(TARGETPROCESS)', NULL, @CONVERT_BYTES_TO_MB, 0 ), -- changed to fix Bug # 234905 ('ServerActivity', 'cpuUsage', 'System', 'Processor', 0, '% Processor Time', '_Total', NULL, 1.0, 0 ), ('ServerActivity', 'cpuUsage', 'SQL Server', 'Process', 0, '% Processor Time', '$(TARGETPROCESS)', NULL, 1.0, 1 ), ('ServerActivity', 'networkUsage', 'System', 'Network Interface', 0, 'Bytes Total/sec', '%', NULL, 1.0, 0 ), ('ServerActivity', 'sqlActivity', 'Logins/sec', '%SQL%:General Statistics', 1, 'Logins/sec', '', NULL, 1.0, 0 ), ('ServerActivity', 'sqlActivity', 'Logouts/sec', '%SQL%:General Statistics', 1, 'Logouts/sec', '', NULL, 1.0, 0 ), ('ServerActivity', 'sqlActivity', 'Transactions', '%SQL%:General Statistics', 1, 'Transactions', '', NULL, 1.0, 0 ), ('ServerActivity', 'sqlActivity', 'User Connections', '%SQL%:General Statistics', 1, 'User Connections', '', NULL, 1.0, 0 ), ('ServerActivity', 'sqlActivity', 'Batch Requests/sec', '%SQL%:SQL Statistics', 1, 'Batch Requests/sec', '', NULL, 1.0, 0 ), ('ServerActivity', 'sqlActivity', 'SQL Compilations/sec', '%SQL%:SQL Statistics', 1, 'SQL Compilations/sec', '', NULL, 1.0, 0 ), ('ServerActivity', 'sqlActivity', 'SQL Re-Compilations/sec', '%SQL%:SQL Statistics', 1, 'SQL Re-Compilations/sec', '', NULL, 1.0, 0 ), ('SystemDiskUsage', 'diskSpeed', '[COUNTER_INSTANCE]', 'LogicalDisk', 0, 'Avg. Disk sec/Transfer', '%', NULL, 1.0, 0 ), ('SystemDiskUsage', 'diskQueues', '[COUNTER_INSTANCE]', 'LogicalDisk', 0, 'Avg. Disk Queue Length', '%', NULL, 1.0, 0 ), ('SystemDiskUsage', 'diskRates', '[COUNTER_INSTANCE]', 'LogicalDisk', 0, 'Disk Bytes/sec', '%', NULL, @CONVERT_BYTES_TO_MB, 0 ), ('SystemDiskUsage', 'processStats', '[COUNTER_INSTANCE]', 'Process', 0, 'IO Read Bytes/sec', '%', '$(TARGETPROCESS)', 1.0, 0 ), ('SystemDiskUsage', 'processStats', '[COUNTER_INSTANCE]', 'Process', 0, 'IO Write Bytes/sec', '%', '$(TARGETPROCESS)', 1.0, 0 ), ('SystemDiskUsage', 'processStats', '[COUNTER_INSTANCE]', 'Process', 0, 'Disk Reads/sec', '%', '$(TARGETPROCESS)', 1.0, 0 ), ('SystemDiskUsage', 'processStats', '[COUNTER_INSTANCE]', 'Process', 0, 'Disk Writes/sec', '%', '$(TARGETPROCESS)', 1.0, 0 ), ('SystemDiskUsage', 'diskSpeed', '[COUNTER_INSTANCE]', 'LogicalDisk', 0, 'Avg. Disk sec/Transfer', '%', NULL, 1.0, 0 ), ('SystemDiskUsage', 'diskQueues', '[COUNTER_INSTANCE]', 'LogicalDisk', 0, 'Avg. Disk Queue Length', '%', NULL, 1.0, 0 ), ('SystemDiskUsage', 'diskRates', '[COUNTER_INSTANCE]', 'LogicalDisk', 0, 'Disk Bytes/sec', '%', NULL, @CONVERT_BYTES_TO_MB, 0 ), ('SystemDiskUsagePivot', 'processStats', '[COUNTER_INSTANCE]', 'Process', 0, 'IO Read Bytes/sec', '%', '$(TARGETPROCESS)', 1.0, 0 ), ('SystemDiskUsagePivot', 'processStats', '[COUNTER_INSTANCE]', 'Process', 0, 'IO Write Bytes/sec', '%', '$(TARGETPROCESS)', 1.0, 0 ), ('SystemDiskUsagePivot', 'processStats', '[COUNTER_INSTANCE]', 'Process', 0, 'Disk Reads/sec', '%', '$(TARGETPROCESS)', 1.0, 0 ), ('SystemDiskUsagePivot', 'processStats', '[COUNTER_INSTANCE]', 'Process', 0, 'Disk Writes/sec', '%', '$(TARGETPROCESS)', 1.0, 0 ), ('SqlMemoryUsage', 'memoryRates', 'Page life expectancy', '%SQL%:Buffer Manager', 1, 'Page life expectancy', '', NULL, 1.0, 0 ), ('SystemMemoryUsage', 'memoryUsage', 'Total Working Set', 'Process', 0, 'Working Set', '_Total', NULL, @CONVERT_BYTES_TO_MB, 0 ), ('SystemMemoryUsage', 'memoryUsage', 'Cache Bytes', 'Memory', 0, 'Cache Bytes', '', NULL, @CONVERT_BYTES_TO_MB, 0 ), ('SystemMemoryUsage', 'memoryUsage', 'Pool Nonpaged Bytes', 'Memory', 0, 'Pool Nonpaged Bytes', '', NULL, @CONVERT_BYTES_TO_MB, 0 ), ('SystemMemoryUsage', 'memoryRates', 'Page Reads/sec', 'Memory', 0, 'Page Reads/sec', '', NULL, 1.0, 0 ), ('SystemMemoryUsage', 'memoryRates', 'Page Writes/sec', 'Memory', 0, 'Page Writes/sec', '', NULL, 1.0, 0 ), ('SystemMemoryUsagePivot', 'processStats', '[COUNTER_INSTANCE]', 'Process', 0, 'Working Set', '%', '$(TARGETPROCESS)', @CONVERT_BYTES_TO_MB, 0 ), ('SystemMemoryUsagePivot', 'processStats', '[COUNTER_INSTANCE]', 'Process', 0, 'Private Bytes', '%', '$(TARGETPROCESS)', @CONVERT_BYTES_TO_MB, 0 ), ('SystemCpuUsage', 'cpuUsage', 'CPU [COUNTER_INSTANCE]', 'Processor', 0, '% Processor Time', '%', NULL, 1.0, 0 ), ('SystemCpuUsagePivot', 'processStats', '[COUNTER_INSTANCE]', 'Process', 0, '% Processor Time', '%', '$(TARGETPROCESS)', 1.0, 1 ), ('SystemCpuUsagePivot', 'processStats', '[COUNTER_INSTANCE]', 'Process', 0, 'Thread Count', '%', '$(TARGETPROCESS)', 1.0, 0 ), ('SqlActivity', 'sessionsAndConnections', 'User Connections', '%SQL%:General Statistics', 1, 'User Connections', '', NULL, 1.0, 0 ), ('SqlActivity', 'sessionsAndConnections', 'Active Transactions', '%SQL%:Databases', 1, 'Active Transactions', '_Total', NULL, 1.0, 0 ), ('SqlActivity', 'sessionsAndConnections', 'Active requests', '%SQL%:Workload Group Stats', 1, 'Active requests', '%', NULL, 1.0, 0 ), ('SqlActivity', 'requestsAndCompilations', 'Batch Requests/sec', '%SQL%:SQL Statistics', 1, 'Batch Requests/sec', '', NULL, 1.0, 0 ), ('SqlActivity', 'requestsAndCompilations', 'SQL Compilations/sec', '%SQL%:SQL Statistics', 1, 'SQL Compilations/sec', '', NULL, 1.0, 0 ), ('SqlActivity', 'requestsAndCompilations', 'SQL Re-Compilations/sec', '%SQL%:SQL Statistics', 1, 'SQL Re-Compilations/sec', '', NULL, 1.0, 0 ), ('SqlActivity', 'planCache', '[COUNTER_INSTANCE]', '%SQL%:Plan Cache', 1, 'Cache Hit Ratio', '%', NULL, 1.0, 0 ), ('SqlActivity', 'tempDb', 'Free Space in tempdb (MB)', '%SQL%:Transactions', 1, 'Free Space in tempdb (KB)', '', NULL, @CONVERT_KB_TO_MB, 0 ), ('SqlActivity', 'tempDb', 'Active Temp Tables', '%SQL%:General Statistics', 1, 'Active Temp Tables', '', NULL, 1.0, 0 ), ('SqlActivity', 'tempDb', 'Transactions/sec', '%SQL%:Databases', 1, 'Transactions/sec', 'tempdb', NULL, 1.0, 0 ) GO /**********************************************************************/ /* SNAPSHOTS SCHEMA */ /**********************************************************************/ PRINT '' PRINT 'Create schema snapshots...' PRINT '' GO IF (SCHEMA_ID('snapshots') IS NULL) BEGIN -- Dynamic SQL here because we must skip the creation attempt if the schema already -- exists, but we can't use a simple IF check because CREATE SCHEMA must be the first -- statement in its batch. DECLARE @sql nvarchar(128) SET @sql = 'CREATE SCHEMA snapshots' EXEC sp_executesql @sql END GO -- -- PERFORMANCE COUNTERS COLLECTOR TYPE SUPPORT -- -- This table holds information about instances of perf counters collected (their path and type) IF (OBJECT_ID(N'[snapshots].[performance_counter_instances]', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[performance_counter_instances]...' CREATE TABLE [snapshots].[performance_counter_instances]( [performance_counter_id] int IDENTITY NOT NULL, [path] nvarchar(2048) NOT NULL, [object_name] nvarchar(2048) NOT NULL, [counter_name] nvarchar(2048) NOT NULL, [instance_name] nvarchar(2048) NULL, [counter_type] int NOT NULL, -- This constraint creation generates a warning. We will deal with the warning when fix for #130259 is provided CONSTRAINT [UN_performance_counter_path] UNIQUE ( [path] ) WITH (IGNORE_DUP_KEY = ON) ) END; GO -- performance_counter_instances.PK_performance_counter_instances TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_performance_counter_instances', 'performance_counter_id', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'performance_counter_instances', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_performance_counter_instances', @ignore_dup_key = 0, @clustered = 1; GO -- performance_counter_instances.IDX_performance_counter_instances1 TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('IDX_performance_counter_instances1', 'object_name', 0, 0), ('IDX_performance_counter_instances1', 'counter_name', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'performance_counter_instances', @object_type = 'INDEX', @constraint_or_index_name = 'IDX_performance_counter_instances1', @ignore_dup_key = 0, @clustered = 0; GO -- This table holds information about actual values collected for perf counters (formatted, raw values, and time when counter was collected) IF (OBJECT_ID(N'[snapshots].[performance_counter_values]', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[performance_counter_values]...' CREATE TABLE [snapshots].[performance_counter_values]( [performance_counter_instance_id] int NOT NULL, [snapshot_id] int NOT NULL, [collection_time] datetimeoffset(7) NOT NULL, [formatted_value] float NOT NULL, [raw_value_first] bigint NOT NULL, [raw_value_second] bigint NULL, ) ON [PRIMARY]; ALTER TABLE [snapshots].[performance_counter_values] ADD CONSTRAINT [CHK_performance_counter_values_check_operator] CHECK (core.fn_check_operator(snapshot_id) = 1) END; GO -- performance_counter_values.PK_performance_counter_values -- -- Ignore duplicate keys on primary key and not fail uploads because of that. -- This is needed because in some cases we may have collection item that -- collects the same counter twice, for valid reasons. To not force users -- to provide some exclusion lists, we will ignore duplicates. TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_performance_counter_values', 'performance_counter_instance_id', 0, 0), ('PK_performance_counter_values', 'snapshot_id', 0, 0), ('PK_performance_counter_values', 'collection_time', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'performance_counter_values', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_performance_counter_values', @ignore_dup_key = 1, @clustered = 1; GO -- performance_counter_values.IDX_performance_counter_values1 TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('IDX_performance_counter_values1', 'snapshot_id', 0, 0), ('IDX_performance_counter_values1', 'performance_counter_instance_id', 0, 0), ('IDX_performance_counter_values1', 'collection_time', 0, 0), ('IDX_performance_counter_values1', 'formatted_value', 1, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'performance_counter_values', @object_type = 'INDEX', @constraint_or_index_name = 'IDX_performance_counter_values1', @ignore_dup_key = 0, @clustered = 0; GO -- performance_counter_values.FK_performance_counter_values_snapshot_id IF OBJECT_ID ('snapshots.FK_performance_counter_values_snapshot_id', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_performance_counter_values_snapshot_id] on snapshots.performance_counter_values ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[performance_counter_values] ADD CONSTRAINT [FK_performance_counter_values_snapshot_id] FOREIGN KEY(snapshot_id) REFERENCES [core].[snapshots_internal] (snapshot_id) ON DELETE CASCADE END; GO -- performance_counter_values.FK_performance_counter_values_instance_id IF OBJECT_ID ('snapshots.FK_performance_counter_values_snapshot_id', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_performance_counter_values_instance_id] on snapshots.performance_counter_values ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[performance_counter_values] ADD CONSTRAINT [FK_performance_counter_values_instance_id] FOREIGN KEY(performance_counter_instance_id) REFERENCES [snapshots].[performance_counter_instances] (performance_counter_id) ON DELETE CASCADE END; GO -- This view shows user friendly information about performance counters IF (OBJECT_ID(N'[snapshots].[performance_counters]', 'V') IS NOT NULL) BEGIN PRINT 'Dropping view [snapshots].[performance_counters]...' DROP VIEW [snapshots].[performance_counters] END PRINT 'Creating view [snapshots].[performance_counters]...' GO CREATE VIEW [snapshots].[performance_counters] AS SELECT pci.performance_counter_id, pcv.snapshot_id AS snapshot_id, pcv.collection_time AS collection_time, pci.path AS path, pci.object_name AS performance_object_name, pci.counter_name AS performance_counter_name, pci.instance_name AS performance_instance_name, pcv.formatted_value AS formatted_value, pcv.raw_value_first, pcv.raw_value_second FROM snapshots.performance_counter_instances pci INNER JOIN snapshots.performance_counter_values pcv ON pci.performance_counter_id = pcv.performance_counter_instance_id GO -- Function to obtain user friendly formatted perf counters values for a given instance and time window IF (OBJECT_ID(N'snapshots.fn_get_performance_counters', 'IF') IS NOT NULL) BEGIN PRINT 'Dropping function [snapshots].[fn_get_performance_counters] ...' DROP FUNCTION [snapshots].[fn_get_performance_counters] END GO PRINT 'Creating function [snapshots].[fn_get_performance_counters] ...' GO CREATE FUNCTION [snapshots].[fn_get_performance_counters] ( @instance_name sysname, @start_time datetimeoffset(7) = NULL, @end_time datetimeoffset(7) = NULL ) RETURNS TABLE AS RETURN ( SELECT pc.performance_counter_id AS performance_counter_id, pc.collection_time AS collection_time, pc.path AS path, pc.performance_object_name AS performance_object_name, pc.performance_counter_name AS performance_counter_name, pc.performance_instance_name AS performance_instance_name, pc.formatted_value AS formatted_value FROM [snapshots].[performance_counters] as pc JOIN [core].[snapshots] s on s.snapshot_id = pc.snapshot_id WHERE @instance_name = s.instance_name AND ISNULL(@start_time,CAST (0 AS DATETIME)) <= pc.collection_time AND ISNULL(@end_time,GETDATE()) >= pc.collection_time ) GO -- Function to obtain user friendly statistics about a perf counter for a given instance, counter path and time window IF (OBJECT_ID(N'snapshots.fn_get_performance_counter_statistics', 'IF') IS NOT NULL) BEGIN PRINT 'Dropping function [snapshots].[fn_get_performance_counter_statistics] ...' DROP FUNCTION [snapshots].[fn_get_performance_counter_statistics] END GO PRINT 'Creating function [snapshots].[fn_get_performance_counter_statistics] ...' GO CREATE FUNCTION [snapshots].[fn_get_performance_counter_statistics] ( @instance_name sysname, @path_pattern nvarchar(2048), @start_time datetimeoffset(7) = NULL, @end_time datetimeoffset(7) = NULL ) RETURNS TABLE AS RETURN ( SELECT pc.path as path, MIN(pc.formatted_value) as minimum_value, MAX(pc.formatted_value) as maximum_value, AVG(pc.formatted_value) as average_value, STDEV(pc.formatted_value) as standard_deviation, VAR(pc.formatted_value) as statistical_variance FROM [snapshots].[performance_counters] as pc JOIN [core].[snapshots] s on s.snapshot_id = pc.snapshot_id WHERE s.instance_name = @instance_name AND pc.path LIKE @path_pattern AND pc.collection_time >= @start_time AND pc.collection_time <= @end_time GROUP BY pc.path ) GO -- -- SQLTRACE COLLECTOR TYPE SUPPORT -- -- This table holds information about captured traces IF (OBJECT_ID(N'[snapshots].[trace_info]', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[trace_info]...' CREATE TABLE [snapshots].[trace_info] ( trace_info_id int IDENTITY NOT NULL, source_id int NOT NULL, -- id of source_info record corresponding to source of this data collection_item_id int NOT NULL, -- id of the trace on the target instance last_snapshot_id int NULL, -- references core.snapshots table. Identifies the most recent snapshot id that was generated for this trace start_time datetime NULL, -- time when this trace was started on the target machine last_event_sequence bigint NULL, -- event sequence of the most recent event for this trace was captured. is_running bit NULL, -- 0 - trace is stopped, 1 - trace is running event_count bigint NULL, -- total number of events captured by this trace dropped_event_count int NULL, -- total number of events dropped by this trace. ); END; GO -- trace_info.PK_trace_info TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_trace_info', 'trace_info_id', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'trace_info', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_trace_info', @ignore_dup_key = 0, @clustered = 1; GO -- trace_info.FK_trace_info_last_snapshot_id IF OBJECT_ID ('snapshots.FK_trace_info_last_snapshot_id', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_trace_info_last_snapshot_id] on snapshots.trace_info ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[trace_info] ADD CONSTRAINT [FK_trace_info_last_snapshot_id] FOREIGN KEY(last_snapshot_id) REFERENCES [core].[snapshots_internal] (snapshot_id) ON DELETE CASCADE END; GO -- trace_info.FK_trace_info_last_snapshot_id IF OBJECT_ID ('snapshots.FK_trace_info_source_id', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_trace_info_source_id] on snapshots.trace_info ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[trace_info] ADD CONSTRAINT [FK_trace_info_source_id] FOREIGN KEY(source_id) REFERENCES [core].[source_info_internal] (source_id) END; GO -- This table holds all trace data IF (OBJECT_ID(N'[snapshots].[trace_data]', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[trace_data]...' CREATE TABLE [snapshots].[trace_data] ( trace_info_id int NOT NULL, -- references id of the trace from trace_info table snapshot_id int NOT NULL, -- references snapshot_id from core.snapshots -- Following all all trace columns that match the list from sys.trace_columns system view TextData nvarchar(max) NULL, BinaryData varbinary(max) NULL, DatabaseID int NULL, TransactionID bigint NULL, LineNumber int NULL, NTUserName nvarchar(256) NULL, NTDomainName nvarchar(256) NULL, HostName nvarchar(256) NULL, ClientProcessID int NULL, ApplicationName nvarchar(256) NULL, LoginName nvarchar(256) NULL, SPID int NULL, Duration bigint NULL, StartTime datetimeoffset(7) NULL, EndTime datetimeoffset(7) NULL, Reads bigint NULL, Writes bigint NULL, CPU int NULL, Permissions bigint NULL, Severity int NULL, EventSubClass int NULL, ObjectID int NULL, Success int NULL, IndexID int NULL, IntegerData int NULL, ServerName nvarchar(256) NULL, EventClass int NULL, ObjectType int NULL, NestLevel int NULL, State int NULL, Error int NULL, Mode int NULL, Handle int NULL, ObjectName nvarchar(256) NULL, DatabaseName nvarchar(256) NULL, FileName nvarchar(256) NULL, OwnerName nvarchar(256) NULL, RoleName nvarchar(256) NULL, TargetUserName nvarchar(256) NULL, DBUserName nvarchar(256) NULL, LoginSid varbinary(max) NULL, TargetLoginName nvarchar(256) NULL, TargetLoginSid varbinary(max) NULL, ColumnPermissions int NULL, LinkedServerName nvarchar(256) NULL, ProviderName nvarchar(256) NULL, MethodName nvarchar(256) NULL, RowCounts bigint NULL, RequestID int NULL, XactSequence bigint NULL, EventSequence bigint NOT NULL, BigintData1 bigint NULL, BigintData2 bigint NULL, GUID uniqueidentifier NULL, IntegerData2 int NULL, ObjectID2 bigint NULL, Type int NULL, OwnerID int NULL, ParentName nvarchar(256) NULL, IsSystem int NULL, Offset int NULL, SourceDatabaseID int NULL, SqlHandle varbinary(64) NULL, SessionLoginName nvarchar(256) NULL, PlanHandle varbinary(64) NULL, GroupID int NULL ) ON [PRIMARY]; ALTER TABLE [snapshots].[trace_data] ADD CONSTRAINT [CHK_trace_data_check_operator] CHECK (core.fn_check_operator(snapshot_id) = 1); END; GO -- trace_data.IDX_trace_data_EventSequence TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('IDX_trace_data_EventSequence', 'snapshot_id', 0, 0), ('IDX_trace_data_EventSequence', 'EventSequence', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'trace_data', @object_type = 'INDEX', @constraint_or_index_name = 'IDX_trace_data_EventSequence', @ignore_dup_key = 0, @clustered = 1; GO -- trace_data.IDX_trace_data_trace_info_id TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('IDX_trace_data_trace_info_id', 'trace_info_id', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'trace_data', @object_type = 'INDEX', @constraint_or_index_name = 'IDX_trace_data_trace_info_id', @ignore_dup_key = 0, @clustered = 0; GO -- trace_data.IDX_trace_data_EventSequence TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('IDX_trace_data_StartTime_EventClass', 'StartTime', 0, 0), ('IDX_trace_data_StartTime_EventClass', 'EventClass', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'trace_data', @object_type = 'INDEX', @constraint_or_index_name = 'IDX_trace_data_StartTime_EventClass', @ignore_dup_key = 0, @clustered = 0; GO -- trace_data.FK_trace_data_trace_info_id IF OBJECT_ID ('snapshots.FK_trace_data_trace_info_id', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_trace_data_trace_info_id] on snapshots.trace_data ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[trace_data] ADD CONSTRAINT [FK_trace_data_trace_info_id] FOREIGN KEY(trace_info_id) REFERENCES [snapshots].[trace_info] (trace_info_id); END; GO -- trace_data.FK_trace_data_snapshot_id IF OBJECT_ID ('snapshots.FK_trace_data_snapshot_id', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_trace_data_snapshot_id] on snapshots.trace_data ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[trace_data] ADD CONSTRAINT [FK_trace_data_snapshot_id] FOREIGN KEY(snapshot_id) REFERENCES [core].[snapshots_internal] (snapshot_id) ON DELETE CASCADE; END; GO -- This stored proc inserts a new row int the snapshots.trace_info table IF (NOT OBJECT_ID(N'snapshots.sp_trace_get_info', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[sp_trace_get_info] ...' DROP PROCEDURE [snapshots].[sp_trace_get_info] END GO PRINT 'Creating procedure [snapshots].[sp_trace_get_info] ...' GO CREATE PROCEDURE [snapshots].[sp_trace_get_info] @source_id int, -- references source of data from core.source_info_internal @collection_item_id int, -- idenitfies the collection item id within the source info @start_time datetime, -- time when the trace has started @last_event_sequence bigint OUTPUT, -- returns the event sequence number for last trace event uploaded, or 0 @trace_info_id int OUTPUT -- returns id of trace_info record AS BEGIN SET NOCOUNT ON; -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'mdw_writer'), 0) = 1) AND NOT (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)) BEGIN RAISERROR(14677, 16, -1, 'mdw_writer'); RETURN(1); -- Failure END; -- Parameters check - mandatory parameters IF (@source_id IS NULL) BEGIN RAISERROR(14200, -1, -1, '@source_id') RETURN(1) -- Failure END; IF (@collection_item_id IS NULL) BEGIN RAISERROR(14200, -1, -1, '@collection_item_id') RETURN(1) -- Failure END; IF (@start_time IS NULL) BEGIN RAISERROR(14200, -1, -1, '@start_time') RETURN(1) -- Failure END; SELECT @trace_info_id = trace_info_id, @last_event_sequence = last_event_sequence FROM snapshots.trace_info ti WHERE ti.source_id = @source_id AND ti.collection_item_id = @collection_item_id AND ti.is_running = 1 AND ti.start_time = @start_time; IF (@trace_info_id IS NULL) BEGIN SELECT @last_event_sequence = 0; -- Insert new record INSERT INTO [snapshots].[trace_info] ( source_id, collection_item_id, start_time, is_running, last_event_sequence ) VALUES ( @source_id, @collection_item_id, @start_time, 1, @last_event_sequence ); SELECT @trace_info_id = SCOPE_IDENTITY(); END; RETURN (0); END GO -- This stored proc updates a row in the snapshots.trace_info table IF (NOT OBJECT_ID(N'snapshots.sp_trace_update_info', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[sp_trace_update_info] ...' DROP PROCEDURE [snapshots].[sp_trace_update_info] END GO PRINT 'Creating procedure [snapshots].[sp_trace_update_info] ...' GO CREATE PROCEDURE [snapshots].[sp_trace_update_info] @trace_info_id int, @snapshot_id int, @last_event_sequence bigint, @is_running bit, @event_count bigint, @dropped_event_count int AS BEGIN SET NOCOUNT ON; -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'mdw_writer'), 0) = 1) AND NOT (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)) BEGIN RAISERROR(14677, 16, -1, 'mdw_writer'); RETURN(1); -- Failure END; -- Parameters check - mandatory parameters IF (@trace_info_id IS NULL) BEGIN RAISERROR(14200, -1, -1, '@trace_info_id') RETURN(1) -- Failure END; IF NOT EXISTS (SELECT trace_info_id from snapshots.trace_info where trace_info_id = @trace_info_id) BEGIN DECLARE @trace_info_id_as_char NVARCHAR(10) SELECT @trace_info_id_as_char = CONVERT(NVARCHAR(36), @trace_info_id) RAISERROR(14679, -1, -1, N'@trace_info_id', @trace_info_id_as_char) RETURN(1) -- Failure END; IF (@snapshot_id IS NULL) BEGIN RAISERROR(14200, -1, -1, '@snapshot_id') RETURN(1) -- Failure END; IF NOT EXISTS (SELECT snapshot_id from core.snapshots where snapshot_id = @snapshot_id) BEGIN DECLARE @snapshot_id_as_char NVARCHAR(36) SELECT @snapshot_id_as_char = CONVERT(NVARCHAR(36), @snapshot_id) RAISERROR(14679, -1, -1, N'@snapshot_id', @snapshot_id_as_char) RETURN(1) -- Failure END; IF (@last_event_sequence IS NULL) BEGIN RAISERROR(14200, -1, -1, '@last_event_sequence') RETURN(1) -- Failure END; IF (@is_running IS NULL) BEGIN RAISERROR(14200, -1, -1, '@is_running') RETURN(1) -- Failure END; IF (@event_count IS NULL) BEGIN RAISERROR(14200, -1, -1, '@event_count') RETURN(1) -- Failure END; IF (@dropped_event_count IS NULL) BEGIN RAISERROR(14200, -1, -1, '@dropped_event_count') RETURN(1) -- Failure END; -- Update existing record UPDATE [snapshots].[trace_info] SET last_snapshot_id = @snapshot_id, last_event_sequence = @last_event_sequence, is_running = @is_running, event_count = ISNULL(event_count,0) + @event_count, dropped_event_count = @dropped_event_count WHERE trace_info_id = @trace_info_id; RETURN(0); END GO -- This function returns all data captured for the specified trace IF (NOT OBJECT_ID(N'snapshots.fn_trace_gettable', 'IF') IS NULL) BEGIN PRINT 'Dropping function [snapshots].[fn_trace_gettable] ...' DROP FUNCTION [snapshots].[fn_trace_gettable] END GO PRINT 'Creating function [snapshots].[fn_trace_gettable] ...' GO CREATE FUNCTION [snapshots].[fn_trace_gettable] ( @trace_info_id int, @start_time datetimeoffset(7) = NULL, @end_time datetimeoffset(7) = NULL ) RETURNS TABLE AS RETURN ( SELECT TextData, BinaryData, DatabaseID, TransactionID, LineNumber, NTUserName, NTDomainName, HostName, ClientProcessID, ApplicationName, LoginName, SPID, Duration, StartTime, EndTime, Reads, Writes, CPU, Permissions, Severity, EventSubClass, ObjectID, Success, IndexID, IntegerData, ServerName, EventClass, ObjectType, NestLevel, State, Error, Mode, Handle, ObjectName, DatabaseName, FileName, OwnerName, RoleName, TargetUserName, DBUserName, LoginSid, TargetLoginName, TargetLoginSid, ColumnPermissions, LinkedServerName, ProviderName, MethodName, RowCounts, RequestID, XactSequence, EventSequence, BigintData1, BigintData2, GUID, IntegerData2, ObjectID2, Type, OwnerID, ParentName, IsSystem, Offset, SourceDatabaseID, SqlHandle, SessionLoginName, PlanHandle FROM snapshots.trace_data WHERE trace_info_id = @trace_info_id AND StartTime >= ISNULL(@start_time, '1753-01-01') AND StartTime <= ISNULL(@end_time, '9999-12-31') ) GO -- -- GENERAL TABLES FOR SYSTEM COLLECTION SETS -- -- Association of batch text with sql_handle and object_name -- IF (OBJECT_ID(N'snapshots.notable_query_text', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[notable_query_text]...' CREATE TABLE [snapshots].[notable_query_text] ( [sql_handle] varbinary(64) NOT NULL, [database_id] smallint NULL, [object_id] int NULL, [object_name] nvarchar(128) NULL, [sql_text] nvarchar(max) NULL, [source_id] int NOT NULL ) ON [PRIMARY] END GO -- notable_query_text.PK_notable_query_text TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_notable_query_text', 'source_id', 0, 0), ('PK_notable_query_text', 'sql_handle', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'notable_query_text', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_notable_query_text', @ignore_dup_key = 1, @clustered = 1; -- IGNORE_DUP_KEY GO -- notable_query_text.FK_distinct_query_to_handle_notable_query_text IF OBJECT_ID ('snapshots.FK_notable_query_text_source_info_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_distinct_query_to_handle_notable_query_text] on snapshots.notable_query_text ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[notable_query_text] ADD CONSTRAINT [FK_notable_query_text_source_info_internal] FOREIGN KEY([source_id]) REFERENCES [core].[source_info_internal] (source_id) ON DELETE CASCADE END; -- Association of batch plan with sql_handle and plan_handle and object_name -- IF (OBJECT_ID(N'snapshots.notable_query_plan', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[notable_query_plan]...' CREATE TABLE [snapshots].[notable_query_plan] ( [sql_handle] varbinary(64) NOT NULL, [plan_handle] varbinary(64) NOT NULL, [statement_start_offset] int NOT NULL, [statement_end_offset] int NOT NULL, [plan_generation_num] bigint NOT NULL, [creation_time] datetimeoffset(7) NOT NULL, [database_id] smallint NULL, [object_id] int NULL, [object_name] nvarchar(128) NULL, [query_plan] nvarchar(max) NULL, [source_id] int NOT NULL, ) ON [PRIMARY] END; -- notable_query_plan.PK_notable_query_plan TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_notable_query_plan', 'source_id', 0, 0), ('PK_notable_query_plan', 'sql_handle', 0, 0), ('PK_notable_query_plan', 'plan_handle', 0, 0), ('PK_notable_query_plan', 'statement_start_offset', 0, 0), ('PK_notable_query_plan', 'statement_end_offset', 0, 0), ('PK_notable_query_plan', 'creation_time', 0, 0), ('PK_notable_query_plan', 'plan_generation_num', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'notable_query_plan', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_notable_query_plan', @ignore_dup_key = 1, @clustered = 1; GO -- notable_query_plan.IDX_notable_query_plan_plan_handle TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('IDX_notable_query_plan_plan_handle', 'source_id', 0, 0), ('IDX_notable_query_plan_plan_handle', 'plan_handle', 0, 0), ('IDX_notable_query_plan_plan_handle', 'statement_start_offset', 0, 0), ('IDX_notable_query_plan_plan_handle', 'statement_end_offset', 0, 0), ('IDX_notable_query_plan_plan_handle', 'creation_time', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'notable_query_plan', @object_type = 'INDEX', @constraint_or_index_name = 'IDX_notable_query_plan_plan_handle', @ignore_dup_key = 0, @clustered = 0; GO -- notable_query_plan.FK_notable_query_plan_source_info_internal IF OBJECT_ID ('snapshots.FK_notable_query_plan_source_info_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_notable_query_plan_source_info_internal] on snapshots.notable_query_plan ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[notable_query_plan] ADD CONSTRAINT [FK_notable_query_plan_source_info_internal] FOREIGN KEY([source_id]) REFERENCES [core].[source_info_internal] (source_id) ON DELETE CASCADE; END; GO IF (OBJECT_ID(N'snapshots.distinct_queries', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[distinct_queries]...' CREATE TABLE [snapshots].[distinct_queries] ( [distinct_query_hash] bigint NOT NULL, [distinct_sql_text] nvarchar(512) NOT NULL, [source_id] int NOT NULL, ); END; GO -- distinct_queries.PK_distinct_queries TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_distinct_queries', 'source_id', 0, 0), ('PK_distinct_queries', 'distinct_query_hash', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'distinct_queries', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_distinct_queries', @ignore_dup_key = 0, @clustered = 1; GO -- distinct_queries.FK_distinct_queries_source_info_internal IF OBJECT_ID ('snapshots.FK_distinct_queries_source_info_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_distinct_queries_source_info_internal] on snapshots.distinct_queries ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[distinct_queries] ADD CONSTRAINT [FK_distinct_queries_source_info_internal] FOREIGN KEY([source_id]) REFERENCES [core].[source_info_internal] (source_id) ON DELETE CASCADE END; GO IF (OBJECT_ID(N'snapshots.distinct_query_to_handle', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[distinct_query_to_handle]...' CREATE TABLE [snapshots].[distinct_query_to_handle] ( [distinct_query_hash] bigint NOT NULL, [sql_handle] varbinary(64) NOT NULL, [source_id] int NOT NULL ) ON [PRIMARY] END GO -- distinct_query_to_handle.PK_distinct_query_to_handle TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_distinct_query_to_handle', 'source_id', 0, 0), ('PK_distinct_query_to_handle', 'distinct_query_hash', 0, 0), ('PK_distinct_query_to_handle', 'sql_handle', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'distinct_query_to_handle', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_distinct_query_to_handle', @ignore_dup_key = 0, @clustered = 1; GO -- distinct_query_to_handle.FK_distinct_query_to_handle_notable_query_text IF OBJECT_ID ('snapshots.FK_distinct_query_to_handle_notable_query_text', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key snapshots.distinct_query_to_handle.FK_distinct_query_to_handle_notable_query_text...', 0, 1) WITH NOWAIT ALTER TABLE [snapshots].[distinct_query_to_handle] ADD CONSTRAINT [FK_distinct_query_to_handle_notable_query_text] FOREIGN KEY([source_id], [sql_handle]) REFERENCES [snapshots].[notable_query_text] ([source_id], [sql_handle]) END; GO -- distinct_query_to_handle.FK_distinct_query_to_handle_distinct_queries IF OBJECT_ID ('snapshots.FK_distinct_query_to_handle_distinct_queries', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key snapshots.distinct_query_to_handle.FK_distinct_query_to_handle_distinct_queries...', 0, 1) WITH NOWAIT ALTER TABLE [snapshots].[distinct_query_to_handle] ADD CONSTRAINT [FK_distinct_query_to_handle_distinct_queries] FOREIGN KEY([source_id], [distinct_query_hash]) REFERENCES [snapshots].[distinct_queries] ([source_id], [distinct_query_hash]) END; GO -- distinct_query_to_handle.FK_distinct_query_to_handle_source_info_internal IF OBJECT_ID ('snapshots.FK_distinct_query_to_handle_source_info_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key snapshots.distinct_query_to_handle.FK_distinct_query_to_handle_source_info_internal...', 0, 1) WITH NOWAIT ALTER TABLE [snapshots].[distinct_query_to_handle] ADD CONSTRAINT [FK_distinct_query_to_handle_source_info_internal] FOREIGN KEY([source_id]) REFERENCES [core].[source_info_internal] (source_id) ON DELETE CASCADE END; GO -- This function returns text and database and object context for a query identified by a sql_handle IF (NOT OBJECT_ID(N'snapshots.fn_get_query_text', 'TF') IS NULL) BEGIN PRINT 'Dropping function [snapshots].[fn_get_query_text] ...' DROP FUNCTION [snapshots].[fn_get_query_text] END GO PRINT 'Creating function [snapshots].[fn_get_query_text] ...' GO CREATE FUNCTION [snapshots].[fn_get_query_text]( @source_id int, @sql_handle varbinary(64), @statement_start_offset int, @statement_end_offset int ) RETURNS @query_text TABLE (database_id smallint NULL, object_id int NULL, object_name sysname NULL, query_text nvarchar(max) NULL) BEGIN IF @sql_handle IS NOT NULL AND EXISTS (SELECT sql_handle FROM snapshots.notable_query_text WHERE sql_handle = @sql_handle AND source_id = @source_id) BEGIN DECLARE @start int DECLARE @end int -- statement_end_offset is zero prior to beginning query execution (e.g., compilation) SELECT @start = ISNULL(@statement_start_offset, 0), @end = CASE WHEN @statement_end_offset IS NULL OR @statement_end_offset = 0 THEN -1 ELSE @statement_end_offset END INSERT INTO @query_text ( database_id, object_id, object_name, query_text ) SELECT t.database_id, t.object_id, t.object_name, LTRIM(SUBSTRING(t.sql_text, @start / 2 + 1, ((CASE WHEN @end = -1 THEN DATALENGTH(t.sql_text) ELSE @end END) - @start) / 2)) FROM snapshots.notable_query_text t WHERE t.sql_handle = @sql_handle AND t.source_id = @source_id END RETURN END GO -- This stored procedure returns all rows from notable_query_text table -- where sql_text value is NULL, meaning we did not caputure the text for that query yet. IF (NOT OBJECT_ID(N'snapshots.sp_get_unknown_query_text', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[sp_get_unknown_query_text] ...' DROP PROCEDURE [snapshots].[sp_get_unknown_query_text] END GO PRINT 'Creating procedure [snapshots].[sp_get_unknown_query_text] ...' GO CREATE PROCEDURE [snapshots].[sp_get_unknown_query_text] @source_id int AS BEGIN SET NOCOUNT ON; SELECT [source_id], [sql_handle] FROM [snapshots].[notable_query_text] WHERE [source_id] = @source_id AND [sql_text] IS NULL END; GO -- This stored procedure returns all rows from notable_query_plan table -- where sql_text value is NULL, meaning we did not caputure the plan for that query yet. IF (NOT OBJECT_ID(N'snapshots.sp_get_unknown_query_plan', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[sp_get_unknown_query_plan] ...' DROP PROCEDURE [snapshots].[sp_get_unknown_query_plan] END GO PRINT 'Creating procedure [snapshots].[sp_get_unknown_query_plan] ...' GO CREATE PROCEDURE [snapshots].[sp_get_unknown_query_plan] @source_id int AS BEGIN SET NOCOUNT ON; SELECT [source_id], [sql_handle], [plan_handle], [statement_start_offset], [statement_end_offset], [plan_generation_num] FROM [snapshots].[notable_query_plan] WHERE [source_id] = @source_id AND [query_plan] IS NULL END; GO -- Updates a single row with new text for the sql_handle IF (NOT OBJECT_ID(N'snapshots.sp_update_query_text', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[sp_update_query_text] ...' DROP PROCEDURE [snapshots].[sp_update_query_text] END GO PRINT 'Creating procedure [snapshots].[sp_update_query_text] ...' GO CREATE PROCEDURE [snapshots].[sp_update_query_text] @source_id int, @sql_handle varbinary(64), @database_id smallint , @object_id int , @object_name nvarchar(128), @sql_text nvarchar(max) AS BEGIN SET NOCOUNT ON; UPDATE [snapshots].[notable_query_text] SET database_id = @database_id, object_id = @object_id, object_name = @object_name, sql_text = @sql_text WHERE source_id = @source_id AND sql_handle = @sql_handle END; GO -- Updates a single row with new plan for the plan_handle IF (NOT OBJECT_ID(N'snapshots.sp_update_query_plan', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[sp_update_query_plan] ...' DROP PROCEDURE [snapshots].[sp_update_query_plan] END GO PRINT 'Creating procedure [snapshots].[sp_update_query_plan] ...' GO CREATE PROCEDURE [snapshots].[sp_update_query_plan] @source_id int, @sql_handle varbinary(64), @plan_handle varbinary(64), @statement_start_offset int , @statement_end_offset int , @plan_generation_num bigint , @database_id smallint , @object_id int , @object_name nvarchar(128), @query_plan nvarchar(max) AS BEGIN SET NOCOUNT ON; UPDATE [snapshots].[notable_query_plan] SET database_id = @database_id, object_id = @object_id, object_name = @object_name, query_plan = @query_plan WHERE source_id = @source_id AND sql_handle = @sql_handle AND plan_handle = @plan_handle AND statement_start_offset = @statement_start_offset AND statement_end_offset = @statement_end_offset AND plan_generation_num = @plan_generation_num END; GO -- -- SERVER ACTIVITY COLLECTION SET SUPPORT -- -- os_wait_stats table -- IF (OBJECT_ID(N'snapshots.os_wait_stats', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[os_wait_stats]...' CREATE TABLE [snapshots].[os_wait_stats]( [wait_type] nvarchar(45) NOT NULL, [waiting_tasks_count] bigint NOT NULL, [wait_time_ms] bigint NOT NULL, [signal_wait_time_ms] bigint NOT NULL, [collection_time] datetimeoffset(7) NOT NULL, [snapshot_id] int NOT NULL, ); ALTER TABLE [snapshots].[os_wait_stats] WITH CHECK ADD CONSTRAINT [CHK_os_wait_stats_check_operator] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))) CREATE STATISTICS [STAT_os_wait_stats1] ON [snapshots].[os_wait_stats]( [collection_time], [snapshot_id], [wait_type] ) CREATE STATISTICS [STAT_os_wait_stats2] ON [snapshots].[os_wait_stats]( [collection_time], [wait_type] ) END; GO -- os_wait_stats.PK_os_wait_stats TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_os_wait_stats', 'snapshot_id', 0, 0), ('PK_os_wait_stats', 'collection_time', 0, 0), ('PK_os_wait_stats', 'wait_type', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'os_wait_stats', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_os_wait_stats', @ignore_dup_key = 0, @clustered = 1; GO -- os_wait_stats.IDX_os_wait_stats1 TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('IDX_os_wait_stats1', 'collection_time', 0, 1), ('IDX_os_wait_stats1', 'snapshot_id', 0, 0), ('IDX_os_wait_stats1', 'wait_type', 1, 0), ('IDX_os_wait_stats1', 'signal_wait_time_ms', 1, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'os_wait_stats', @object_type = 'INDEX', @constraint_or_index_name = 'IDX_os_wait_stats1', @ignore_dup_key = 0, @clustered = 0; GO -- os_wait_stats.IDX_os_wait_stats1 TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('IDX_os_wait_stats2', 'snapshot_id', 0, 0), ('IDX_os_wait_stats2', 'collection_time', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'os_wait_stats', @object_type = 'INDEX', @constraint_or_index_name = 'IDX_os_wait_stats2', @ignore_dup_key = 0, @clustered = 0; GO -- os_wait_stats.FK_os_wait_stats_snapshots_internal IF OBJECT_ID ('snapshots.FK_os_wait_stats_snapshots_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_os_wait_stats_snapshots_internal] on snapshots.os_wait_stats ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[os_wait_stats] ADD CONSTRAINT [FK_os_wait_stats_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE END; GO -- os_latch_stats table -- IF (OBJECT_ID(N'snapshots.os_latch_stats', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[os_latch_stats]...' CREATE TABLE [snapshots].[os_latch_stats]( [latch_class] nvarchar(45) NOT NULL, [waiting_requests_count] bigint NOT NULL, [wait_time_ms] bigint NOT NULL, [collection_time] datetimeoffset(7) NOT NULL, [snapshot_id] int NOT NULL, ); ALTER TABLE [snapshots].[os_latch_stats] WITH CHECK ADD CONSTRAINT [CHK_os_latch_stats_check_operator] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))) END; GO -- os_latch_stats.PK_os_latch_stats TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_os_latch_stats', 'snapshot_id', 0, 0), ('PK_os_latch_stats', 'collection_time', 0, 0), ('PK_os_latch_stats', 'latch_class', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'os_latch_stats', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_os_latch_stats', @ignore_dup_key = 0, @clustered = 1; GO -- os_latch_stats.FK_os_latch_stats_snapshots_internal IF OBJECT_ID ('snapshots.FK_os_latch_stats_snapshots_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_os_latch_stats_snapshots_internal] on snapshots.os_latch_stats ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[os_latch_stats] ADD CONSTRAINT [FK_os_latch_stats_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE END; GO IF (OBJECT_ID(N'snapshots.active_sessions_and_requests', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[active_sessions_and_requests]...' CREATE TABLE [snapshots].[active_sessions_and_requests]( [row_id] int NOT NULL, [session_id] smallint NOT NULL, [request_id] int NOT NULL, [exec_context_id] int NOT NULL, [blocking_session_id] smallint NULL, [blocking_exec_context_id] int NULL, [scheduler_id] int NULL, [database_name] nvarchar(128) NULL, [user_id] int NULL, [task_state] nvarchar(10) NULL, [request_status] nvarchar(15) NULL, [session_status] nvarchar(15) NOT NULL, [executing_managed_code] bit NULL, [login_time] datetimeoffset(7) NOT NULL, [is_user_process] bit NOT NULL, [host_name] nvarchar(20) NOT NULL, [program_name] nvarchar(50) NOT NULL, [login_name] nvarchar(30) NOT NULL, [wait_type] nvarchar(45) NOT NULL, [last_wait_type] nvarchar(45) NOT NULL, [wait_duration_ms] bigint NOT NULL, [wait_resource] nvarchar(50) NOT NULL, [resource_description] nvarchar(140) NOT NULL, [transaction_id] bigint NULL, [open_transaction_count] int NOT NULL, [transaction_isolation_level] smallint NULL, [request_cpu_time] int NULL, [request_logical_reads] bigint NULL, [request_reads] bigint NULL, [request_writes] bigint NULL, [request_total_elapsed_time] int NULL, [request_start_time] datetimeoffset(7) NULL, [memory_usage] int NOT NULL, [session_cpu_time] int NOT NULL, [session_reads] bigint NOT NULL, [session_writes] bigint NOT NULL, [session_logical_reads] bigint NOT NULL, [session_total_scheduled_time] int NOT NULL, [session_total_elapsed_time] int NOT NULL, [session_last_request_start_time] datetimeoffset(7) NOT NULL, [session_last_request_end_time] datetimeoffset(7) NULL, [open_resultsets] int NULL, [session_row_count] bigint NOT NULL, [prev_error] int NOT NULL, [pending_io_count] int NULL, [command] nvarchar(16) NULL, [plan_handle] varbinary(64) NULL, [sql_handle] varbinary(64) NULL, [statement_start_offset] int NULL, [statement_end_offset] int NULL, [collection_time] datetimeoffset(7) NOT NULL, [snapshot_id] int NOT NULL, [is_blocking] bit NOT NULL, ); ALTER TABLE [snapshots].[active_sessions_and_requests] WITH CHECK ADD CONSTRAINT [CHK_active_sessions_and_requests_check_operator] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))) END; GO -- active_sessions_and_requests.PK_active_sessions_and_requests TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_active_sessions_and_requests', 'snapshot_id', 0, 0), ('PK_active_sessions_and_requests', 'collection_time', 0, 0), ('PK_active_sessions_and_requests', 'row_id', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'active_sessions_and_requests', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_active_sessions_and_requests', @ignore_dup_key = 0, @clustered = 1; GO -- active_sessions_and_requests.IDX_blocking_session_id TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('IDX_blocking_session_id', 'blocking_session_id', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'active_sessions_and_requests', @object_type = 'INDEX', @constraint_or_index_name = 'IDX_blocking_session_id', @ignore_dup_key = 0, @clustered = 0; GO -- active_sessions_and_requests.IDX_collection_time TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('IDX_collection_time', 'collection_time', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'active_sessions_and_requests', @object_type = 'INDEX', @constraint_or_index_name = 'IDX_collection_time', @ignore_dup_key = 0, @clustered = 0; GO -- active_sessions_and_requests.FK_active_sessions_and_requests_snapshots_internal IF OBJECT_ID ('snapshots.FK_active_sessions_and_requests_snapshots_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_active_sessions_and_requests_snapshots_internal] on snapshots.active_sessions_and_requests ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[active_sessions_and_requests] ADD CONSTRAINT [FK_active_sessions_and_requests_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE END; GO IF (OBJECT_ID(N'snapshots.os_schedulers', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[os_schedulers]...' CREATE TABLE [snapshots].[os_schedulers] ( [parent_node_id] int NOT NULL, [scheduler_id] int NOT NULL, [cpu_id] int NOT NULL, [status] nvarchar(60) NOT NULL, [is_idle] bit NOT NULL, [preemptive_switches_count] int NOT NULL, [context_switches_count] int NOT NULL, [yield_count] int NOT NULL, [current_tasks_count] int NOT NULL, [runnable_tasks_count] int NOT NULL, [work_queue_count] bigint NOT NULL, [pending_disk_io_count] int NOT NULL, [collection_time] datetimeoffset(7) NOT NULL, [snapshot_id] int NOT NULL, ); ALTER TABLE [snapshots].[os_schedulers] WITH CHECK ADD CONSTRAINT [CHK_os_schedulers_check_operator] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))) END; GO -- os_schedulers.PK_os_schedulers TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_os_schedulers', 'snapshot_id', 0, 0), ('PK_os_schedulers', 'collection_time', 0, 0), ('PK_os_schedulers', 'scheduler_id', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'os_schedulers', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_os_schedulers', @ignore_dup_key = 0, @clustered = 1; GO -- os_schedulers.FK_os_schedulers_snapshots_internal IF OBJECT_ID ('snapshots.FK_os_schedulers_snapshots_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_os_schedulers_snapshots_internal] on snapshots.os_schedulers ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[os_schedulers] ADD CONSTRAINT [FK_os_schedulers_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE END; GO IF (OBJECT_ID(N'snapshots.os_memory_nodes', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[os_memory_nodes]...' CREATE TABLE [snapshots].[os_memory_nodes] ( [memory_node_id] smallint NOT NULL, [virtual_address_space_reserved_kb] bigint NOT NULL, [virtual_address_space_committed_kb] bigint NOT NULL, [locked_page_allocations_kb] bigint NOT NULL, [single_pages_kb] bigint NOT NULL, [multi_pages_kb] bigint NOT NULL, [shared_memory_reserved_kb] bigint NOT NULL, [shared_memory_committed_kb] bigint NOT NULL, [collection_time] datetimeoffset(7) NOT NULL, [snapshot_id] int NOT NULL, ); ALTER TABLE [snapshots].[os_memory_nodes] WITH CHECK ADD CONSTRAINT [CHK_os_memory_nodes_check_operator] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))) END; GO -- os_memory_nodes.PK_os_memory_nodes TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_os_memory_nodes', 'snapshot_id', 0, 0), ('PK_os_memory_nodes', 'collection_time', 0, 0), ('PK_os_memory_nodes', 'memory_node_id', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'os_memory_nodes', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_os_memory_nodes', @ignore_dup_key = 0, @clustered = 1; GO -- os_memory_nodes.FK_os_memory_nodes_snapshots_internal IF OBJECT_ID ('snapshots.FK_os_memory_nodes_snapshots_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_os_memory_nodes_snapshots_internal] on snapshots.os_memory_nodes ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[os_memory_nodes] ADD CONSTRAINT [FK_os_memory_nodes_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE END; GO -- NOTE: Used by System Activity collection set up to CTP6 Refresh/RC0. Not used in RTM and later. IF (OBJECT_ID(N'snapshots.os_process_memory', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[os_process_memory]...' CREATE TABLE [snapshots].[os_process_memory] ( [physical_memory_in_use_kb] bigint NOT NULL, [large_page_allocations_kb] bigint NOT NULL, [locked_page_allocations_kb] bigint NOT NULL, [total_virtual_address_space_kb] bigint NOT NULL, [virtual_address_space_reserved_kb] bigint NOT NULL, [virtual_address_space_committed_kb] bigint NOT NULL, [virtual_address_space_available_kb] bigint NOT NULL, [page_fault_count] bigint NOT NULL, [memory_utilization_percentage] int NOT NULL, [available_commit_limit_kb] bigint NOT NULL, [process_physical_memory_low] bit NOT NULL, [process_virtual_memory_low] bit NOT NULL, [collection_time] datetimeoffset(7) NOT NULL, [snapshot_id] int NOT NULL, ); ALTER TABLE [snapshots].[os_process_memory] WITH CHECK ADD CONSTRAINT [CHK_os_process_memory_check_operator] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))) END; GO -- os_process_memory.PK_os_process_memory TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_os_process_memory', 'snapshot_id', 0, 0), ('PK_os_process_memory', 'collection_time', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'os_process_memory', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_os_process_memory', @ignore_dup_key = 0, @clustered = 1; GO -- os_process_memory.FK_os_process_memory_snapshots_internal IF OBJECT_ID ('snapshots.FK_os_process_memory_snapshots_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_os_process_memory_snapshots_internal] on snapshots.os_process_memory ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[os_process_memory] ADD CONSTRAINT [FK_os_process_memory_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE END; GO IF (OBJECT_ID(N'snapshots.sql_process_and_system_memory', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[sql_process_and_system_memory]...'; CREATE TABLE [snapshots].[sql_process_and_system_memory]( [sql_physical_memory_in_use_kb] bigint NOT NULL, [sql_large_page_allocations_kb] bigint NOT NULL, [sql_locked_page_allocations_kb] bigint NOT NULL, [sql_total_virtual_address_space_kb] bigint NOT NULL, [sql_virtual_address_space_reserved_kb] bigint NOT NULL, [sql_virtual_address_space_committed_kb] bigint NOT NULL, [sql_virtual_address_space_available_kb] bigint NOT NULL, [sql_page_fault_count] bigint NOT NULL, [sql_memory_utilization_percentage] int NOT NULL, [sql_available_commit_limit_kb] bigint NOT NULL, [sql_process_physical_memory_low] bit NOT NULL, [sql_process_virtual_memory_low] bit NOT NULL, [system_total_physical_memory_kb] bigint NOT NULL, [system_available_physical_memory_kb] bigint NOT NULL, [system_total_page_file_kb] bigint NOT NULL, [system_available_page_file_kb] bigint NOT NULL, [system_cache_kb] bigint NOT NULL, [system_kernel_paged_pool_kb] bigint NOT NULL, [system_kernel_nonpaged_pool_kb] bigint NOT NULL, [system_high_memory_signal_state] bit NOT NULL, [system_low_memory_signal_state] bit NOT NULL, [bpool_commit_target] bigint NOT NULL, [bpool_committed] bigint NOT NULL, [bpool_visible] bigint NOT NULL, [collection_time] datetimeoffset(7) NOT NULL, [snapshot_id] int NOT NULL, ); ALTER TABLE [snapshots].[sql_process_and_system_memory] WITH CHECK ADD CONSTRAINT [CHK_sql_process_and_system_memory_check_operator] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))); END; GO -- sql_process_and_system_memory.PK_sql_process_and_system_memory TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_sql_process_and_system_memory', 'snapshot_id', 0, 0), ('PK_sql_process_and_system_memory', 'collection_time', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'sql_process_and_system_memory', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_sql_process_and_system_memory', @ignore_dup_key = 0, @clustered = 1; GO -- sql_process_and_system_memory.FK_sql_process_and_system_memory_internal IF OBJECT_ID ('snapshots.FK_sql_process_and_system_memory_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_sql_process_and_system_memory_internal] on snapshots.sql_process_and_system_memory ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[sql_process_and_system_memory] ADD CONSTRAINT [FK_sql_process_and_system_memory_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE; END; GO IF (OBJECT_ID(N'snapshots.os_memory_clerks', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[os_memory_clerks]...' CREATE TABLE [snapshots].[os_memory_clerks] ( [type] nvarchar(60), [memory_node_id] smallint, [single_pages_kb] bigint, [multi_pages_kb] bigint, [virtual_memory_reserved_kb] bigint, [virtual_memory_committed_kb] bigint, [awe_allocated_kb] bigint, [shared_memory_reserved_kb] bigint, [shared_memory_committed_kb] bigint, [collection_time] datetimeoffset(7) NOT NULL, [snapshot_id] int NOT NULL ) ON [PRIMARY] ALTER TABLE [snapshots].[os_memory_clerks] WITH CHECK ADD CONSTRAINT [CHK_os_memory_clerks_check_operator] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))) END GO -- os_memory_clerks.CIDX_os_memory_clerks TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('CIDX_os_memory_clerks', 'snapshot_id', 0, 0), ('CIDX_os_memory_clerks', 'collection_time', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'os_memory_clerks', @object_type = 'INDEX', @constraint_or_index_name = 'CIDX_os_memory_clerks', @ignore_dup_key = 0, @clustered = 1; GO -- os_memory_clerks.FK_os_memory_clerks_snapshots_internal IF OBJECT_ID ('snapshots.FK_os_memory_clerks_snapshots_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_os_memory_clerks_snapshots_internal] on snapshots.os_memory_clerks ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[os_memory_clerks] ADD CONSTRAINT [FK_os_memory_clerks_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE END; GO -- -- QUERY ACTIVITY COLLECTION SET SUPPORT -- IF (OBJECT_ID(N'snapshots.query_stats', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[query_stats]...' CREATE TABLE [snapshots].[query_stats] ( [sql_handle] varbinary(64) NOT NULL, [statement_start_offset] int NOT NULL, [statement_end_offset] int NOT NULL, [plan_generation_num] bigint NOT NULL, [plan_handle] varbinary(64) NOT NULL, [creation_time] datetimeoffset(7) NOT NULL, [last_execution_time] datetimeoffset(7) NOT NULL, [execution_count] bigint NOT NULL, [snapshot_execution_count] bigint NULL, [total_worker_time] bigint NOT NULL, [snapshot_worker_time] bigint NOT NULL, [min_worker_time] bigint NULL, -- NULLable b/c we can't calc min for queries that are still in-progress [max_worker_time] bigint NOT NULL, [total_physical_reads] bigint NOT NULL, [snapshot_physical_reads] bigint NOT NULL, [min_physical_reads] bigint NULL, -- NULLable b/c we can't calc min for queries that are still in-progress [max_physical_reads] bigint NOT NULL, [total_logical_writes] bigint NOT NULL, [snapshot_logical_writes] bigint NOT NULL, [min_logical_writes] bigint NULL, -- NULLable b/c we can't calc min for queries that are still in-progress [max_logical_writes] bigint NOT NULL, [total_logical_reads] bigint NOT NULL, [snapshot_logical_reads] bigint NOT NULL, [min_logical_reads] bigint NULL, -- NULLable b/c we can't calc min for queries that are still in-progress [max_logical_reads] bigint NOT NULL, [total_clr_time] bigint NULL, -- NULLable b/c dm_exec_requests doesn't expose this stat [snapshot_clr_time] bigint NULL, -- NULLable b/c dm_exec_requests doesn't expose this stat [min_clr_time] bigint NULL, -- NULLable b/c we can't calc min for queries that are still in-progress [max_clr_time] bigint NULL, -- NULLable b/c dm_exec_requests doesn't expose this stat [total_elapsed_time] bigint NOT NULL, [snapshot_elapsed_time] bigint NOT NULL, [min_elapsed_time] bigint NULL, -- NULLable b/c we can't calc min for queries that are still in-progress [max_elapsed_time] bigint NOT NULL, [collection_time] datetimeoffset(7) NOT NULL, [snapshot_id] int NOT NULL ) ON [PRIMARY] ALTER TABLE [snapshots].[query_stats] WITH CHECK ADD CONSTRAINT [CHK_query_stats_check_operator] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))) END GO -- query_stats.PK_query_stats TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('CIDX_query_stats', 'snapshot_id', 0, 0), ('CIDX_query_stats', 'collection_time', 0, 0), ('CIDX_query_stats', 'sql_handle', 0, 0), ('CIDX_query_stats', 'statement_start_offset', 0, 0), ('CIDX_query_stats', 'statement_end_offset', 0, 0), ('CIDX_query_stats', 'plan_handle', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'query_stats', @object_type = 'INDEX', @constraint_or_index_name = 'CIDX_query_stats', @ignore_dup_key = 0, @clustered = 1; GO -- query_stats.FK_query_stats_snapshots_internal IF OBJECT_ID ('snapshots.FK_query_stats_snapshots_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_query_stats_snapshots_internal] on snapshots.query_stats ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[query_stats] ADD CONSTRAINT [FK_query_stats_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE END; GO IF (NOT OBJECT_ID(N'snapshots.distinct_query_stats', 'V') IS NULL) BEGIN PRINT 'Dropping view [snapshots].[distinct_query_stats]...' DROP VIEW [snapshots].[distinct_query_stats] END GO PRINT 'Creating view [snapshots].[distinct_query_stats]...' GO CREATE VIEW [snapshots].[distinct_query_stats] AS SELECT dqth.distinct_query_hash, SUM(qs.execution_count) AS execution_count, SUM(qs.total_worker_time) AS total_worker_time, SUM(qs.total_physical_reads) AS total_physical_reads, SUM(qs.total_logical_reads) AS total_logical_reads, SUM(qs.total_logical_writes) AS total_logical_writes, SUM(qs.total_clr_time) AS total_clr_time, SUM(qs.total_elapsed_time) AS total_elapsed_time FROM [snapshots].[query_stats] qs JOIN [core].[snapshots_internal] s ON (s.snapshot_id = qs.snapshot_id) JOIN [snapshots].[distinct_query_to_handle] dqth ON (s.source_id = dqth.source_id AND qs.sql_handle = dqth.sql_handle) GROUP BY dqth.distinct_query_hash GO -- -- DISK USAGE COLLECTION SET SUPPORT -- -- disk_usage table -- IF (OBJECT_ID(N'snapshots.disk_usage', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[disk_usage]...' CREATE TABLE [snapshots].[disk_usage]( [dbsize] [bigint] NULL, [logsize] [bigint] NULL, [ftsize] [bigint] NULL, [reservedpages] [bigint] NULL, [usedpages] [bigint] NULL, [pages] [bigint] NULL, [database_name] [nvarchar](128) NOT NULL, [collection_time] datetimeoffset(7) NOT NULL, [snapshot_id] [int] NOT NULL, ) ON [PRIMARY] ALTER TABLE [snapshots].[disk_usage] WITH CHECK ADD CONSTRAINT [CHK_disk_usage_check_operator] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))) END GO -- disk_usage.PK_disk_usage TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_disk_usage', 'snapshot_id', 0, 0), ('PK_disk_usage', 'collection_time', 0, 0), ('PK_disk_usage', 'database_name', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'disk_usage', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_disk_usage', @ignore_dup_key = 0, @clustered = 1; GO -- disk_usage.FK_disk_usage_snapshots_internal IF OBJECT_ID ('snapshots.FK_disk_usage_snapshots_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_disk_usage_snapshots_internal] on snapshots.disk_usage ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[disk_usage] WITH CHECK ADD CONSTRAINT [FK_disk_usage_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE END; GO -- log_usage table -- IF (OBJECT_ID(N'snapshots.log_usage', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[log_usage]...' CREATE TABLE [snapshots].[log_usage]( [database_name] [nvarchar](128) NOT NULL, [log_size_mb] [float] NULL, [log_space_used] [float] NULL, [status] [int] NULL, [collection_time] datetimeoffset(7) NOT NULL, [snapshot_id] [int] NOT NULL, ) ON [PRIMARY] ALTER TABLE [snapshots].[log_usage] WITH CHECK ADD CONSTRAINT [CHK_log_usage_check_operator] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))) END GO -- log_usage.PK_log_usage TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_log_usage', 'snapshot_id', 0, 0), ('PK_log_usage', 'collection_time', 0, 0), ('PK_log_usage', 'database_name', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'log_usage', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_log_usage', @ignore_dup_key = 0, @clustered = 1; GO -- log_usage.FK_log_usage_snapshots_internal IF OBJECT_ID ('snapshots.FK_log_usage_snapshots_internal', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_log_usage_snapshots_internal] on snapshots.log_usage ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[log_usage] WITH CHECK ADD CONSTRAINT [FK_log_usage_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE END; GO -- io_virtual_file_stats table -- IF (OBJECT_ID(N'snapshots.io_virtual_file_stats', 'U') IS NULL) BEGIN PRINT 'Creating table [snapshots].[io_virtual_file_stats]...' CREATE TABLE [snapshots].[io_virtual_file_stats] ( [database_name] [nvarchar](128) NOT NULL, [database_id] [int] NOT NULL, [logical_file_name] [nvarchar](128) NOT NULL, [file_id] [int] NOT NULL, [type_desc] [nvarchar](60) NULL, [logical_disk] [nvarchar](255) NOT NULL, [num_of_reads] [bigint] NULL, [num_of_bytes_read] [bigint] NULL, [io_stall_read_ms] [bigint] NULL, [num_of_writes] [bigint] NULL, [num_of_bytes_written] [bigint] NULL, [io_stall_write_ms] [bigint] NULL, [size_on_disk_bytes] [bigint] NULL, [collection_time] [datetimeoffset](7) NOT NULL, [snapshot_id] [int] NOT NULL, ) ON [PRIMARY] ALTER TABLE [snapshots].[io_virtual_file_stats] WITH CHECK ADD CONSTRAINT [CHK_io_virtual_file_stats_check_operator] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))) END GO -- io_virtual_file_stats.PK_io_virtual_file_stats TRUNCATE TABLE #index_key_columns; INSERT INTO #index_key_columns (constraint_name, column_name, is_included_column, is_descending_key) VALUES ('PK_io_virtual_file_stats', 'snapshot_id', 0, 0), ('PK_io_virtual_file_stats', 'collection_time', 0, 0), ('PK_io_virtual_file_stats', 'logical_disk', 0, 0), ('PK_io_virtual_file_stats', 'database_name', 0, 0), ('PK_io_virtual_file_stats', 'file_id', 0, 0); EXEC #create_or_alter_primary_key_or_index @table_schema = 'snapshots', @table_name = 'io_virtual_file_stats', @object_type = 'PRIMARY KEY', @constraint_or_index_name = 'PK_io_virtual_file_stats', @ignore_dup_key = 0, @clustered = 1; GO -- io_virtual_file_stats.FK_io_virtual_file_stats IF OBJECT_ID ('snapshots.FK_io_virtual_file_stats', 'F') IS NULL BEGIN RAISERROR ('Creating foreign key [FK_io_virtual_file_stats] on snapshots.io_virtual_file_stats ...', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[io_virtual_file_stats] WITH CHECK ADD CONSTRAINT [FK_io_virtual_file_stats] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE END; GO /**********************************************************************/ /* REPORTING STORED PROCEDURES */ /**********************************************************************/ -- -- snapshots.rpt_snapshot_times -- Returns all snapshot times for a given collection set. -- Used by (nearly) all reports to return data for the timeline chart that sits at the top of each report. -- -- Parameters: -- @ServerName - SQL Server instance name -- @EndTime - End of the user-selected time window (UTC) -- @WindowSize - Number of minutes in the time window -- @CollectionSetUid - GUID of the collection set with the snapshot_times that the report cares about -- IF (NOT OBJECT_ID(N'snapshots.rpt_snapshot_times', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_snapshot_times] ...' DROP PROCEDURE [snapshots].[rpt_snapshot_times] END GO PRINT 'Creating procedure [snapshots].[rpt_snapshot_times] ...' GO CREATE PROC [snapshots].[rpt_snapshot_times] @ServerName sysname, @EndTime datetime, @WindowSize int, @CollectionSetUid uniqueidentifier AS BEGIN DECLARE @end_time_internal datetimeoffset(7) SET @end_time_internal = TODATETIMEOFFSET (@EndTime, '+00:00') -- Get the time of the earliest and latest snapshots for this collection set DECLARE @min_snapshot_time datetimeoffset(7) DECLARE @max_snapshot_time datetimeoffset(7) DECLARE @total_data_collection_window int SELECT @min_snapshot_time = MIN (snapshot_time), @max_snapshot_time = MAX (snapshot_time) FROM core.snapshots WHERE instance_name = @ServerName AND collection_set_uid = @CollectionSetUid IF @min_snapshot_time IS NULL SET @min_snapshot_time = SYSDATETIMEOFFSET() IF @max_snapshot_time IS NULL SET @max_snapshot_time = SYSDATETIMEOFFSET() SET @total_data_collection_window = DATEDIFF (minute, @min_snapshot_time, @max_snapshot_time) -- First return all snapshot_time values for this collection set SELECT DISTINCT CONVERT (datetime, SWITCHOFFSET (CAST (snapshot_time AS datetimeoffset(7)), '+00:00')) AS snapshot_time, 1 AS value, 'AllSnapshotTimes' AS series_name FROM core.snapshots WHERE instance_name = @ServerName AND collection_set_uid = @CollectionSetUid UNION ALL -- Then return the snapshot_time values that are in the selected time window, with a different series label. SELECT DISTINCT CONVERT (datetime, SWITCHOFFSET (CAST (snapshot_time AS datetimeoffset(7)), '+00:00')) AS snapshot_time, 0.8 AS value, 'SelectedSnapshotTimes' AS series_name FROM core.snapshots WHERE instance_name = @ServerName AND collection_set_uid = @CollectionSetUid AND snapshot_time BETWEEN DATEADD (minute, -1 * @WindowSize, @end_time_internal) AND @end_time_internal UNION ALL SELECT DISTINCT DATEADD (millisecond, 10, CONVERT (datetime, SWITCHOFFSET (CAST (snapshot_time AS datetimeoffset(7)), '+00:00'))) AS snapshot_time, 1.2 AS value, 'SelectedSnapshotTimes' AS series_name FROM core.snapshots WHERE instance_name = @ServerName AND collection_set_uid = @CollectionSetUid AND snapshot_time BETWEEN DATEADD (minute, -1 * @WindowSize, @end_time_internal) AND @end_time_internal -- Return a "fake" data point (will not be plotted) so that the timeline always extends to the current time UNION ALL SELECT GETUTCDATE() AS snapshot_time, -1 AS value, 'Formatting' AS in_selected_time_window -- Order is important here since points are plotted in the order in which they are returned from the query. -- The "SelectedSnapshotTimes" series must be drawn on top of the "AllSnapshotTimes" series, so we return it -- last. ORDER BY series_name ASC, snapshot_time END GO -- -- snapshots.rpt_interval_collection_times -- Helper proc used by other procs that need to return data from N evenly-spaced intervals within a larger user-specified time window -- Sample usage: -- -- CREATE TABLE #intervals ( -- interval_time_id int, -- interval_start_time datetimeoffset(7), -- interval_end_time datetimeoffset(7), -- interval_id int, -- first_collection_time datetimeoffset(7), -- last_collection_time datetimeoffset(7), -- first_snapshot_id int, -- last_snapshot_id int, -- -- the following columns may be ignored if @include_snapshot_detail = 0 -- source_id int, -- snapshot_id int, -- collection_time datetimeoffset(7), -- collection_time_id int -- ) -- -- GUID 49268954-... is the Server Activity CS -- INSERT INTO #intervals -- EXEC [snapshots].[rpt_interval_collection_times] -- @ServerName, @EndTime, @WindowSize, 'snapshots.os_memory_clerks', '49268954-4FD4-4EB6-AA04-CD59D9BB5714', 40 -- -- SELECT ... -- FROM snapshots.dm_os_memory_clerks AS mc -- JOIN #intervals AS col ON mc.collection_time = col.last_collection_time AND mc.snapshot_id = col.last_snapshot_id -- WHERE ... -- -- -- Parameters: -- @ServerName - SQL Server instance name -- @EndTime - End of the user-selected time window (UTC) -- @WindowSize - Number of minutes in the time window -- @TargetCollectionTable - Name of the table from which to harvest the collection_time values. Must have a datetimeoffset [collection_time] -- column and be in the [snapshots] or [custom_snapshots] schema. -- @CollectionSetUid - GUID of the collection set that populates @TargetCollectionTable -- @interval_count int - Number of time intervals to divide the time window up into (default 40) -- @include_snapshot_detail - 0 if the caller only wants -- IF (NOT OBJECT_ID(N'snapshots.rpt_interval_collection_times', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_interval_collection_times] ...' DROP PROCEDURE [snapshots].[rpt_interval_collection_times] END GO PRINT 'Creating procedure [snapshots].[rpt_interval_collection_times] ...' GO CREATE PROCEDURE [snapshots].[rpt_interval_collection_times] @ServerName sysname, @EndTime datetime = NULL, @WindowSize int = NULL, @TargetCollectionTable sysname, @CollectionSetUid varchar(64), @interval_count int = 40, @include_snapshot_detail bit = 0 AS BEGIN SET NOCOUNT ON; DECLARE @start_time_internal datetimeoffset(7); DECLARE @end_time_internal datetimeoffset(7); -- Start time should be passed in as a UTC datetime IF (@EndTime IS NOT NULL) BEGIN -- Assumed time zone offset for this conversion is +00:00 (datetime must be passed in as UTC) SET @end_time_internal = CAST (@EndTime AS datetimeoffset(7)); END ELSE BEGIN SELECT @end_time_internal = MAX(snapshot_time) FROM core.snapshots WHERE instance_name = @ServerName AND collection_set_uid = @CollectionSetUid END SET @start_time_internal = DATEADD (minute, -1 * @WindowSize, @end_time_internal); -- Get the earliest and latest snapshot_id values that could contain data for the selected time interval. -- This will allow a more efficient query plan. DECLARE @start_snapshot_id int; DECLARE @end_snapshot_id int; SELECT @start_snapshot_id = MIN (t.snapshot_id) FROM ( SELECT TOP 2 s.snapshot_id FROM core.snapshots AS s WHERE s.instance_name = @ServerName AND s.collection_set_uid = @CollectionSetUid AND s.snapshot_time < @start_time_internal ORDER BY s.snapshot_id DESC ) AS t SELECT @end_snapshot_id = MAX (t.snapshot_id) FROM ( SELECT TOP 2 snapshot_id FROM core.snapshots AS s WHERE s.instance_name = @ServerName AND s.collection_set_uid = @CollectionSetUid AND s.snapshot_time >= @end_time_internal ORDER BY s.snapshot_id ASC ) AS t IF @start_snapshot_id IS NULL SELECT @start_snapshot_id = MIN (snapshot_id) FROM core.snapshots IF @end_snapshot_id IS NULL SELECT @end_snapshot_id = MAX (snapshot_id) FROM core.snapshots -- Divide the time window up into N equal intervals. -- First, calculate the duration of one interval, in minutes. DECLARE @group_interval_min int SET @group_interval_min = ROUND (DATEDIFF (second, @start_time_internal, @end_time_internal) / 60.0 / @interval_count, 0) IF @group_interval_min = 0 SET @group_interval_min = 1 IF (ISNULL (PARSENAME (@TargetCollectionTable, 2), 'snapshots') IN ('snapshots', 'custom_snapshots')) BEGIN /* Some explanation of the expressions used in the query below: DATEDIFF (minute, ''20000101'', dataTable.collection_time) / @group_interval_min AS interval_time_id - @group_interval_min is the length of one time interval (one Nth of the selected time window), in minutes - "DATEDIFF (minute, ''20000101'', dataTable.collection_time)" converts each collection time into an integer (the # of minutes since a fixed reference date) - This value is divided by @group_interval_min to get an integer "interval ID". The query uses this in the GROUP BY clause to group together all collection times that fall within the same time interval. DATEADD (minute, (<> * @group_interval_min, ''20000101'') AS interval_start_time - This uses (interval number) * (minutes/interval) + (reference date) to generate the time interval's start time - The next column ([interval_end_time]) is the same as the above, except the time is calculated for the following interval ID (an interval's end time is also the start time for the following time interval) */ -- Get the collection times that fall within the specified time window, and compute the time interval ID for each collection time CREATE TABLE #snapshots ( interval_time_id int, interval_start_time datetimeoffset(7), interval_end_time datetimeoffset(7), interval_id int, collection_time datetimeoffset(7), source_id int, snapshot_id int ) -- Dynamic SQL will re-evaluate object permissions -- the current user must have SELECT permission on the target table. DECLARE @sql nvarchar(4000) SET @sql = ' INSERT INTO #snapshots SELECT DISTINCT DATEDIFF (minute, ''20000101'', dataTable.collection_time) / @group_interval_min AS interval_time_id, DATEADD (minute, (DATEDIFF (minute, ''20000101'', dataTable.collection_time) / @group_interval_min) * @group_interval_min, ''20000101'') AS interval_start_time, DATEADD (minute, (DATEDIFF (minute, ''20000101'', dataTable.collection_time) / @group_interval_min + 1) * @group_interval_min, ''20000101'') AS interval_end_time, DENSE_RANK() OVER (ORDER BY DATEDIFF (minute, ''20000101'', dataTable.collection_time) / @group_interval_min) AS interval_id, dataTable.collection_time, s.source_id, s.snapshot_id FROM ' + ISNULL (QUOTENAME (PARSENAME (@TargetCollectionTable, 2)), '[snapshots]') + '.' + QUOTENAME (PARSENAME (@TargetCollectionTable, 1)) + ' AS dataTable INNER JOIN core.snapshots AS s ON dataTable.snapshot_id = s.snapshot_id WHERE dataTable.collection_time BETWEEN @start_time_internal AND @end_time_internal AND s.snapshot_id BETWEEN @start_snapshot_id AND @end_snapshot_id AND s.instance_name = @ServerName AND s.collection_set_uid = @CollectionSetUid' EXEC sp_executesql @sql, N'@ServerName sysname, @CollectionSetUid nvarchar(64), @start_time_internal datetimeoffset(7), @end_time_internal datetimeoffset(7), @group_interval_min int, @start_snapshot_id int, @end_snapshot_id int', @ServerName = @ServerName, @CollectionSetUid = @CollectionSetUid, @start_time_internal = @start_time_internal, @end_time_internal = @end_time_internal, @group_interval_min = @group_interval_min, @start_snapshot_id = @start_snapshot_id, @end_snapshot_id = @end_snapshot_id -- If the caller doesn't care about anything but the interval boundaries, don't bother returning the collection_time/snapshot_id values -- that fall in the middle of an interval. IF (@include_snapshot_detail = 0) BEGIN SELECT interval_time_id, interval_start_time, interval_end_time, interval_id, MIN (collection_time) AS first_collection_time, MAX (collection_time) AS last_collection_time, MIN (snapshot_id) AS first_snapshot_id, MAX (snapshot_id) AS last_snapshot_id, NULL AS source_id, NULL AS snapshot_id, NULL AS collection_time, NULL AS collection_time_id FROM #snapshots GROUP BY interval_time_id, interval_start_time, interval_end_time, interval_id ORDER BY interval_time_id END ELSE BEGIN SELECT interval_info.*, #snapshots.source_id, #snapshots.snapshot_id, #snapshots.collection_time, DENSE_RANK() OVER (ORDER BY #snapshots.collection_time) AS collection_time_id FROM ( SELECT interval_time_id, interval_start_time, interval_end_time, interval_id, MIN (collection_time) AS first_collection_time, MAX (collection_time) AS last_collection_time, MIN (snapshot_id) AS first_snapshot_id, MAX (snapshot_id) AS last_snapshot_id FROM #snapshots GROUP BY interval_time_id, interval_start_time, interval_end_time, interval_id ) AS interval_info INNER JOIN #snapshots ON interval_info.interval_time_id = #snapshots.interval_time_id ORDER BY interval_info.interval_time_id, #snapshots.collection_time END END ELSE BEGIN /* Invalid parameter %s specified for %s. */ RAISERROR (21055, 16, -1, @TargetCollectionTable, '@TargetCollectionTable') END END; GO -- -- snapshots.rpt_next_and_previous_collection_times -- Helper proc used by other procs that need to return data from N evenly-spaced intervals within a larger user-specified time window -- -- -- Parameters: -- @ServerName - SQL Server instance name -- @CollectionTime - End of the user-selected time window (UTC) -- @TargetCollectionTable - Name of the table from which to harvest the collection_time values. Must have a datetimeoffset [collection_time] -- column and be in the [snapshots] or [custom_snapshots] schema. -- IF (NOT OBJECT_ID(N'snapshots.rpt_next_and_previous_collection_times', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_next_and_previous_collection_times] ...' DROP PROCEDURE [snapshots].[rpt_next_and_previous_collection_times] END GO PRINT 'Creating procedure [snapshots].[rpt_next_and_previous_collection_times] ...' GO CREATE PROC [snapshots].[rpt_next_and_previous_collection_times] @ServerName sysname, @CollectionTime datetime, @DataGroupID nvarchar(128) AS BEGIN DECLARE @current_collection_time datetimeoffset(7) -- current collection time DECLARE @current_snapshot_id int -- current collection time''s snapshot ID DECLARE @previous_collection_time datetimeoffset(7) -- next collection time DECLARE @next_collection_time datetimeoffset(7) -- prior collection time DECLARE @snapshot_table sysname -- name of the snapshot table we'll be querying -- The assumed time zone offset for this conversion is +00:00 (datetime must be passed in as UTC) SET @current_collection_time = CAST (@CollectionTime AS datetimeoffset(7)); -- Compensate for RS truncation of fractional seconds SET @current_collection_time = DATEADD(second, 1, @CollectionTime); -- Currently, we only call this stored procedure from one place, and that code only needs next and prev collection times for -- the snapshots.active_sessions_and_requests table. If, in the future, we need to call this for other tables, the three -- SELECT TOP 1 queries below will need to be converted to dynamic SQL, executed via sp_executesql with OUTPUT parameters. -- The correct target table name (@snapshot_table) should be determined based on the @DataGroupID parameter. IF (@DataGroupID = 'SqlActiveRequests') BEGIN SET @snapshot_table = 'snapshots.active_sessions_and_requests'; END ELSE BEGIN /* Invalid parameter %s specified for %s. */ RAISERROR (21055, 16, -1, @DataGroupID, '@DataGroupID'); RETURN; END DECLARE @sql nvarchar(max); -- Find our exact collection time using the approx time passed in SELECT TOP 1 @current_collection_time = r.collection_time, @current_snapshot_id = r.snapshot_id FROM core.snapshots AS s INNER JOIN snapshots.active_sessions_and_requests AS r ON s.snapshot_id = r.snapshot_id WHERE s.instance_name = @ServerName AND r.collection_time <= @current_collection_time ORDER BY collection_time DESC; -- Find the previous collection time SELECT TOP 1 @previous_collection_time = r.collection_time FROM core.snapshots AS s INNER JOIN snapshots.active_sessions_and_requests AS r ON s.snapshot_id = r.snapshot_id WHERE s.instance_name = @ServerName AND r.collection_time < @current_collection_time ORDER BY collection_time DESC; -- Find the next collection time SELECT TOP 1 @next_collection_time = r.collection_time FROM core.snapshots AS s INNER JOIN snapshots.active_sessions_and_requests AS r ON s.snapshot_id = r.snapshot_id WHERE s.instance_name = @ServerName AND r.collection_time > @current_collection_time ORDER BY collection_time ASC; IF @previous_collection_time IS NULL SET @previous_collection_time = @current_collection_time; IF @next_collection_time IS NULL SET @next_collection_time = @current_collection_time; SELECT CONVERT (datetime, SWITCHOFFSET (@current_collection_time, '+00:00')) AS current_collection_time, @current_snapshot_id AS current_snapshot_id, CONVERT (datetime, SWITCHOFFSET (@previous_collection_time, '+00:00')) AS previous_collection_time, CONVERT (datetime, SWITCHOFFSET (@next_collection_time, '+00:00')) AS next_collection_time; END GO -- -- snapshots.rpt_generic_perfmon -- Returns perf counter data for the counters associated with a "data group id" (typically, a report name). -- Parameters: -- @ServerName - SQL Server instance name -- @EndTime - End of selected time window end (UTC) -- @WindowSize - Number of minutes in the time window -- @DataGroupID - Name of the report, used to return only the necessary counters -- @CollectionSetUid - GUID identifier for the target collection set -- @interval_count - Number of time intervals to divide the time window into (default 40) -- IF (NOT OBJECT_ID(N'snapshots.rpt_generic_perfmon', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_generic_perfmon] ...' DROP PROCEDURE [snapshots].[rpt_generic_perfmon] END GO PRINT 'Creating procedure [snapshots].[rpt_generic_perfmon] ...' GO CREATE PROCEDURE [snapshots].[rpt_generic_perfmon] @ServerName sysname, @EndTime datetime, @WindowSize int, @DataGroupID nvarchar(128), @CollectionSetUid nvarchar(64), @interval_count int = 40 AS BEGIN SET NOCOUNT ON; DECLARE @start_time_internal datetimeoffset(7); DECLARE @end_time_internal datetimeoffset(7); -- Start time should be passed in as a UTC datetime IF (@EndTime IS NOT NULL) BEGIN -- Assumed time zone offset for this conversion is +00:00 (datetime must be passed in as UTC) SET @end_time_internal = CAST (@EndTime AS datetimeoffset(7)); END ELSE BEGIN SELECT @end_time_internal = MAX(snapshot_time) FROM core.snapshots WHERE instance_name = @ServerName AND collection_set_uid = @CollectionSetUid END SET @start_time_internal = DATEADD (minute, -1 * @WindowSize, @end_time_internal); -- Divide the time window up into N equal intervals. Each interval will correspond to one -- point on a line chart. Calc the duration of one interval, in seconds. DECLARE @group_interval_sec int IF @interval_count < 1 SET @interval_count = 1 SET @group_interval_sec = ROUND (DATEDIFF (second, @start_time_internal, @end_time_internal) / @interval_count, 0) IF @group_interval_sec < 10 SET @group_interval_sec = 10 -- For counter groups that include the "Process(abc)\% Processor Time" counter (e.g. 'ServerActivity' and 'SystemCpuUsagePivot'), -- we must determine the logical CPU count on the target system by querying the number of "Processor" counter instances we -- captured in a perfmon sample that was captured around the same time. DECLARE @cpu_count smallint SET @cpu_count = 1 IF EXISTS ( SELECT * FROM [core].[performance_counter_report_group_items] WHERE counter_group_id = @DataGroupID AND [divide_by_cpu_count] = 1 ) BEGIN SELECT @cpu_count = COUNT (DISTINCT pc.performance_instance_name) FROM snapshots.performance_counters AS pc INNER JOIN core.snapshots s ON s.snapshot_id = pc.snapshot_id WHERE pc.performance_object_name = 'Processor' AND pc.performance_counter_name = '% Processor Time' AND pc.performance_instance_name != '_Total' AND ISNUMERIC (pc.performance_instance_name) = 1 AND s.instance_name = @ServerName AND s.collection_set_uid = @CollectionSetUid AND s.snapshot_id = (SELECT TOP 1 s2.snapshot_id FROM core.snapshots AS s2 INNER JOIN snapshots.performance_counters AS pc2 ON s2.snapshot_id = pc2.snapshot_id WHERE s2.snapshot_time > @start_time_internal AND s2.instance_name = @ServerName AND s2.collection_set_uid = @CollectionSetUid AND pc2.performance_object_name = 'Processor' AND pc2.performance_counter_name = '% Processor Time') AND pc.collection_time = (SELECT TOP 1 collection_time FROM snapshots.performance_counter_values pcv2 WHERE pcv2.snapshot_id = s.snapshot_id) -- These trace flags are necessary for a good plan, due to the join on ascending PK w/range filter OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390) IF ISNULL (@cpu_count, 0) = 0 BEGIN -- This message will never be shown on a report. It is included here only as a troubleshooting aid. RAISERROR ('Unable to determine CPU count. Assuming 1 CPU for process CPU calculations', 9, 1) SET @cpu_count = 1 END END -- Get the matching performance counter instances for this data group SELECT pci.*, cl.counter_group_item_id, cl.counter_group_id, cl.counter_subgroup_id, cl.series_name, cl.multiply_by, cl.divide_by_cpu_count INTO #pci FROM snapshots.performance_counter_instances AS pci INNER JOIN [core].[performance_counter_report_group_items] AS cl ON cl.counter_group_id = @DataGroupID AND pci.counter_name = cl.counter_name AND ISNULL(pci.instance_name, N'') LIKE cl.instance_name AND ( (cl.object_name_wildcards = 0 AND pci.[object_name] = cl.[object_name]) OR (cl.object_name_wildcards = 1 AND pci.[object_name] LIKE cl.[object_name]) ) AND (cl.not_instance_name IS NULL OR pci.instance_name NOT LIKE cl.not_instance_name); -- Get the perfmon counter values for these counters in each time interval. -- NOTE: If you change the schema of this resultset, you must also update the CREATE TABLE in [rpt_generic_perfmon_pivot]. SELECT #pci.counter_subgroup_id, REPLACE (#pci.series_name, '[COUNTER_INSTANCE]', ISNULL(#pci.instance_name, N'')) AS series_name, -- Using our time window end time (@end_time_internal) as a reference point, divide -- the time window into [@interval_count] intervals of [@group_interval_sec] duration -- per interval. DATEDIFF (second, @end_time_internal, SWITCHOFFSET (CONVERT (datetimeoffset(7), pc.collection_time), '+00:00')) / @group_interval_sec AS interval_id, -- Find the end time for the current time interval, and return as a UTC datetime -- Do this by converting [collection_time] into a second count, dividing and multiplying -- the count by [@group_interval_sec] to discard any fraction of an interval, then -- converting the second count back into a datetime. That datetime is the end point for -- the time interval that this [collection_time] value falls within. CONVERT (datetime, DATEADD ( second, (DATEDIFF (second, @end_time_internal, SWITCHOFFSET (CONVERT (datetimeoffset(7), pc.collection_time), '+00:00')) / @group_interval_sec) * @group_interval_sec, @end_time_internal ) ) AS interval_end_time, #pci.counter_name, AVG( pc.formatted_value * #pci.multiply_by / CASE WHEN #pci.divide_by_cpu_count = 1 THEN @cpu_count ELSE 1 END ) AS avg_formatted_value, MAX( pc.formatted_value * #pci.multiply_by / CASE WHEN #pci.divide_by_cpu_count = 1 THEN @cpu_count ELSE 1 END ) AS max_formatted_value, MIN( pc.formatted_value * #pci.multiply_by / CASE WHEN #pci.divide_by_cpu_count = 1 THEN @cpu_count ELSE 1 END ) AS min_formatted_value, -- This column can be used to simulate a "_Total" instance for multi-instance counters that lack _Total -- use a "%" for #counterlist.instance_name -- Expression "1.0 * pc.formatted_value * cl.multiply_by / CASE WHEN cl.divide_by_cpu_count = 1 THEN @cpu_count ELSE 1 END" is the counter value. -- Expression "AVG() * COUNT (DISTINCT pc.performance_instance_name)" returns the simulated "_Total" instance. -- Only valid for multi-instance counters that don't already have a "_Total"). CONVERT (bigint, AVG( 1.0 * pc.formatted_value * #pci.multiply_by / CASE WHEN #pci.divide_by_cpu_count = 1 THEN @cpu_count ELSE 1 END)) * COUNT (DISTINCT #pci.instance_name) AS multi_instance_avg_formatted_value FROM snapshots.performance_counter_values AS pc INNER JOIN #pci ON #pci.performance_counter_id = pc.performance_counter_instance_id INNER JOIN core.snapshots s ON s.snapshot_id = pc.snapshot_id WHERE s.instance_name = @ServerName AND s.collection_set_uid = @CollectionSetUid AND pc.collection_time BETWEEN @start_time_internal AND @end_time_internal GROUP BY DATEDIFF (second, @end_time_internal, SWITCHOFFSET (CONVERT (datetimeoffset(7), pc.collection_time), '+00:00')) / @group_interval_sec, #pci.counter_subgroup_id, #pci.counter_name, REPLACE (#pci.series_name, '[COUNTER_INSTANCE]', ISNULL(#pci.instance_name, N'')) ORDER BY #pci.counter_subgroup_id, interval_end_time, 2, #pci.counter_name -- These trace flags are necessary for a good plan, due to the join on ascending PK w/range filter OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390) END GO -- -- snapshots.rpt_generic_perfmon_pivot -- Pivots the output of [rpt_generic_perfmon] so that multiple counter values can be returned in a -- single row. The value of all counters with the same [series_name] will be returned as a single row. -- The average of all values in the time window is returned -- Parameters: -- @ServerName - SQL Server instance name -- @EndTime - End of selected time window end (UTC) -- @WindowSize - Number of minutes in the time window -- @DataGroupID - Name of the calling report (used to retrieve the correct counters) -- @CollectionSetUid - GUID identifier for the target collection set -- @interval_count - Number of time intervals to divide the time window into (default 40) -- IF (NOT OBJECT_ID(N'snapshots.rpt_generic_perfmon_pivot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_generic_perfmon_pivot] ...' DROP PROCEDURE [snapshots].[rpt_generic_perfmon_pivot] END GO PRINT 'Creating procedure [snapshots].[rpt_generic_perfmon_pivot] ...' GO CREATE PROCEDURE [snapshots].[rpt_generic_perfmon_pivot] @ServerName sysname, @EndTime datetime, @WindowSize int, @DataGroupID nvarchar(128), @CollectionSetUid varchar(64), @interval_count int = 1 AS BEGIN SET NOCOUNT ON; CREATE TABLE #countervalues ( counter_subgroup_id nvarchar(512), -- name of the data subgroup (e.g. chart or table) that requested the counter (used by a chart to filter out its rows from the larger resultset) series_name nvarchar(1024), -- chart series label interval_id int, -- not used here interval_end_time datetime, -- not used here performance_counter_name nvarchar(2048), -- perfmon counter name avg_formatted_value bigint, -- avg perfmon counter value over the time period max_formatted_value bigint, -- max perfmon counter value over the time period min_formatted_value bigint, -- min perfmon counter value over the time period multi_instance_avg_formatted_value bigint -- simulated "_Total" instance value for multi-instance counters that lack _Total ) SET @DataGroupID = @DataGroupID + 'Pivot' INSERT INTO #countervalues EXEC [snapshots].[rpt_generic_perfmon] @ServerName, @EndTime, @WindowSize, @DataGroupID, @CollectionSetUid, @interval_count IF EXISTS (SELECT * FROM #countervalues) BEGIN -- @counterlist looks like "[Counter 1], [Counter 2]" DECLARE @counterlist nvarchar(max) -- @columnlist_min_inner looks like "[Counter 1] AS [Counter 1_min], [Counter 2] AS [Counter 2_min]" DECLARE @columnlist_min_inner nvarchar(max) -- @columnlist_max_inner looks like "[Counter 1] AS [Counter 1_max], [Counter 2] AS [Counter 2_max]" DECLARE @columnlist_max_inner nvarchar(max) -- @columnlist_min_outer looks like "[Counter 1_min], [Counter 2_min]" DECLARE @columnlist_min_outer nvarchar(max) -- @columnlist_max_outer looks like "[Counter 1_max], [Counter 2_max]" DECLARE @columnlist_max_outer nvarchar(max) SET @counterlist = '' SET @columnlist_min_inner = '' SET @columnlist_min_outer = '' SET @columnlist_max_inner = '' SET @columnlist_max_outer = '' -- Build counter lists SELECT @counterlist = @counterlist -- Escape any embedded ']' chars (we can't use QUOTENAME because it can't handle strings > 128 chars) + ', [' + REPLACE (performance_counter_name, ']', ']]') + ']' , @columnlist_min_outer = @columnlist_min_outer + ', [' + REPLACE (performance_counter_name, ']', ']]') + '_min]' , @columnlist_min_inner = @columnlist_min_inner + ', [' + REPLACE (performance_counter_name, ']', ']]') + ']' + ' AS [' + REPLACE (performance_counter_name, ']', ']]') + '_min]' , @columnlist_max_outer = @columnlist_max_outer + ', [' + REPLACE (performance_counter_name, ']', ']]') + '_max]' , @columnlist_max_inner = @columnlist_max_inner + ', [' + REPLACE (performance_counter_name, ']', ']]') + ']' + ' AS [' + REPLACE (performance_counter_name, ']', ']]') + '_max]' FROM (SELECT DISTINCT performance_counter_name FROM #countervalues) AS t GROUP BY performance_counter_name OPTION (MAXDOP 1) -- Remove the leading comma SET @counterlist = SUBSTRING (@counterlist, 3, LEN (@counterlist)-2) SET @columnlist_min_inner = SUBSTRING (@columnlist_min_inner, 3, LEN (@columnlist_min_inner)-2) SET @columnlist_min_outer = SUBSTRING (@columnlist_min_outer, 3, LEN (@columnlist_min_outer)-2) SET @columnlist_max_inner = SUBSTRING (@columnlist_max_inner, 3, LEN (@columnlist_max_inner)-2) SET @columnlist_max_outer = SUBSTRING (@columnlist_max_outer, 3, LEN (@columnlist_max_outer)-2) -- We have to use three PIVOTs here because SQL only allows one aggregate function -- per PIVOT, and we need AVG, MIN, and MAX. They are over a very small temp table, -- though (by default just one row per counter), so the execution cost isn't -- excessive. DECLARE @sql nvarchar(max) SET @sql = ' SELECT avg_pivot.*, ' + @columnlist_min_outer + ', ' + @columnlist_max_outer + ' FROM ( SELECT series_name, interval_end_time, ' + @counterlist + ' FROM ( SELECT series_name, interval_end_time, performance_counter_name, avg_formatted_value FROM #countervalues ) AS SourceTable PIVOT ( AVG (avg_formatted_value) FOR performance_counter_name IN (' + @counterlist + ') ) AS PivotTable ) AS avg_pivot INNER JOIN ( SELECT series_name, interval_end_time, ' + @columnlist_min_inner + ' FROM ( SELECT series_name, interval_end_time, performance_counter_name, min_formatted_value FROM #countervalues ) AS SourceTable PIVOT ( MIN (min_formatted_value) FOR performance_counter_name IN (' + @counterlist + ') ) AS PivotTable ) AS min_pivot ON min_pivot.series_name = avg_pivot.series_name AND min_pivot.interval_end_time = avg_pivot.interval_end_time INNER JOIN ( SELECT series_name, interval_end_time, ' + @columnlist_max_inner + ' FROM ( SELECT series_name, interval_end_time, performance_counter_name, max_formatted_value FROM #countervalues ) AS SourceTable PIVOT ( MAX (max_formatted_value) FOR performance_counter_name IN (' + @counterlist + ') ) AS PivotTable ) AS max_pivot ON max_pivot.series_name = avg_pivot.series_name AND max_pivot.interval_end_time = avg_pivot.interval_end_time ' EXEC sp_executesql @sql END END; GO -- -- snapshots.rpt_wait_stats -- Returns wait time per wait type over a time interval -- Parameters: -- @ServerName - SQL Server instance name -- @EndTime - End of the user-selected time window (UTC) -- @WindowSize - Number of minutes in the time window -- @CategoryName - (Optional) Name of wait category to filter on (all categories if NULL) -- @WaitType - (Optional) Name of wait type to filter on (all wait types if NULL) -- IF (NOT OBJECT_ID(N'snapshots.rpt_wait_stats', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_wait_stats] ...' DROP PROCEDURE [snapshots].[rpt_wait_stats] END GO PRINT 'Creating procedure [snapshots].[rpt_wait_stats] ...' GO CREATE PROCEDURE [snapshots].[rpt_wait_stats] @ServerName sysname, @EndTime datetime = NULL, @WindowSize int, @CategoryName nvarchar(20) = NULL, @WaitType nvarchar(45) = NULL AS BEGIN SET NOCOUNT ON; -- Clean string params (on drillthrough, RS may pass in empty string instead of NULL) IF @CategoryName = '' SET @CategoryName = NULL IF @WaitType = '' SET @WaitType = NULL -- Divide our time window up into 40 evenly-sized time intervals, and find the last collection_time within each of these intervals CREATE TABLE #intervals ( interval_time_id int, interval_start_time datetimeoffset(7), interval_end_time datetimeoffset(7), interval_id int, first_collection_time datetimeoffset(7), last_collection_time datetimeoffset(7), first_snapshot_id int, last_snapshot_id int, source_id int, snapshot_id int, collection_time datetimeoffset(7), collection_time_id int ) -- GUID 49268954-... is Server Activity INSERT INTO #intervals EXEC [snapshots].[rpt_interval_collection_times] @ServerName, @EndTime, @WindowSize, 'snapshots.os_wait_stats', '49268954-4FD4-4EB6-AA04-CD59D9BB5714', 40, 0 -- Get the earliest and latest snapshot_id values that contain data for the selected time interval. -- This will allow a more efficient query plan. DECLARE @start_snapshot_id int; DECLARE @end_snapshot_id int; SELECT @start_snapshot_id = MIN (first_snapshot_id) FROM #intervals SELECT @end_snapshot_id = MAX (last_snapshot_id) FROM #intervals -- Get the wait stats for these collection times SELECT coll.interval_time_id, coll.interval_id, last_collection_time AS collection_time, coll.interval_start_time, coll.interval_end_time, coll.last_snapshot_id, wt.category_name, ws.wait_type, ws.waiting_tasks_count, ISNULL (ws.signal_wait_time_ms, 0) AS signal_wait_time_ms, ISNULL (ws.wait_time_ms, 0) - ISNULL (ws.signal_wait_time_ms, 0) AS wait_time_ms, wait_time_ms AS raw_wait_time_ms, ISNULL (ws.wait_time_ms, 0) AS wait_time_ms_cumulative INTO #wait_stats FROM snapshots.os_wait_stats AS ws INNER JOIN #intervals AS coll ON coll.last_snapshot_id = ws.snapshot_id AND coll.last_collection_time = ws.collection_time INNER JOIN core.wait_types_categorized AS wt ON wt.wait_type = ws.wait_type WHERE wt.category_name = ISNULL (@CategoryName, wt.category_name) AND wt.wait_type = ISNULL (@WaitType, wt.wait_type) AND wt.ignore != 1 -- Get wait times by waittype for each interval (plus CPU time, modeled as a waittype) ---- First get resource wait stats for this interval. We must convert all datetimeoffset values ---- to UTC datetime values before returning to Reporting Services SELECT CONVERT (datetime, SWITCHOFFSET (CAST (w1.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, CONVERT (datetime, SWITCHOFFSET (CAST (w1.interval_start_time AS datetimeoffset(7)), '+00:00')) AS interval_start, CONVERT (datetime, SWITCHOFFSET (CAST (w2.interval_start_time AS datetimeoffset(7)), '+00:00')) AS interval_end, w2.category_name, w2.wait_type, -- All wait stats will be reset to zero by a service cycle, which will cause -- (snapshot2waittime-snapshot1waittime) calculations to produce an incorrect -- negative wait time for the interval. Detect this and avoid calculating -- negative wait time/wait count/signal time deltas. CASE WHEN (w2.waiting_tasks_count - w1.waiting_tasks_count) < 0 THEN w2.waiting_tasks_count ELSE (w2.waiting_tasks_count - w1.waiting_tasks_count) END AS waiting_tasks_count_delta, CASE WHEN (w2.raw_wait_time_ms - w1.raw_wait_time_ms) < 0 THEN w2.wait_time_ms ELSE (w2.wait_time_ms - w1.wait_time_ms) END AS resource_wait_time_delta, -- / CAST (DATEDIFF (second, w1.collection_time, w2.collection_time) AS decimal) CASE WHEN (w2.signal_wait_time_ms - w1.signal_wait_time_ms) < 0 THEN w2.signal_wait_time_ms ELSE (w2.signal_wait_time_ms - w1.signal_wait_time_ms) END AS resource_signal_time_delta, -- / CAST (DATEDIFF (second, w1.collection_time, w2.collection_time) AS decimal) DATEDIFF (second, w1.collection_time, w2.collection_time) AS interval_sec, w2.wait_time_ms_cumulative -- Self-join - w1 represents wait stats at the beginning of the sample interval, while w2 -- shows the wait stats at the end of the interval. FROM #wait_stats AS w1 INNER JOIN #wait_stats AS w2 ON w1.wait_type = w2.wait_type AND w1.interval_id = w2.interval_id-1 UNION ALL ---- Treat the sum of all signal waits as CPU "wait time" SELECT MAX (CONVERT (datetime, SWITCHOFFSET (CAST (w1.collection_time AS datetimeoffset(7)), '+00:00'))) AS collection_time, MIN (CONVERT (datetime, SWITCHOFFSET (CAST (w1.interval_start_time AS datetimeoffset(7)), '+00:00'))) AS interval_start, MAX (CONVERT (datetime, SWITCHOFFSET (CAST (w2.interval_start_time AS datetimeoffset(7)), '+00:00'))) AS interval_end, 'CPU' AS category_name, 'CPU (Signal Wait)' AS wait_type, 0 AS waiting_tasks_count_delta, -- Handle wait stats resets, as in the previous query SUM ( CASE WHEN (w2.signal_wait_time_ms - w1.signal_wait_time_ms) < 0 THEN w2.signal_wait_time_ms ELSE (w2.signal_wait_time_ms - w1.signal_wait_time_ms) END -- / CAST (DATEDIFF (second, w1.collection_time, w2.collection_time) AS decimal) ) AS resource_wait_time_delta, 0 AS resource_signal_time_delta, DATEDIFF (second, w1.collection_time, w2.collection_time) AS interval_sec, NULL AS wait_time_ms_cumulative FROM #wait_stats AS w1 INNER JOIN #wait_stats AS w2 ON w1.wait_type = w2.wait_type AND w1.interval_id = w2.interval_id-1 -- Only return CPU stats if we were told to return the 'CPU' category or all categories WHERE (@CategoryName IS NULL OR @CategoryName = 'CPU') GROUP BY w1.interval_start_time, w2.interval_start_time, w1.interval_end_time, w2.interval_end_time, w1.collection_time, w2.collection_time UNION ALL -- Get actual used CPU time from perfmon data (average across the whole snapshot_time_id interval, -- and use this average for each sample time in this interval). Note that the "% Processor Time" -- counter in the "Process" object can exceed 100% (for example, it can range from 0-800% on an -- 8 CPU server). SELECT CONVERT (datetime, SWITCHOFFSET (CAST (w1.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, CONVERT (datetime, SWITCHOFFSET (CAST (w1.interval_start_time AS datetimeoffset(7)), '+00:00')) AS interval_start, CONVERT (datetime, SWITCHOFFSET (CAST (w2.interval_start_time AS datetimeoffset(7)), '+00:00')) AS interval_end, 'CPU' AS category_name, 'CPU (Consumed)' AS wait_type, 0 AS waiting_tasks_count_delta, -- Get sqlservr %CPU usage for the perfmon sample that immediately precedes each wait stats sample. -- Multiply by 10 to convert "% CPU" to "ms CPU/sec". This works because (for example) on an 8 proc -- server, Process(...)\% Processor Time ranges from 0 to 800, not 0 to 100. Multiply again by -- the duration of interval in seconds to get the total ms of CPU time used in the interval. DATEDIFF (second, w1.collection_time, w2.collection_time) * 10 * ( SELECT TOP 1 formatted_value FROM snapshots.performance_counters AS pc INNER JOIN core.snapshots s ON pc.snapshot_id = s.snapshot_id WHERE pc.performance_object_name = 'Process' AND pc.performance_counter_name = '% Processor Time' AND pc.performance_instance_name = '$(TARGETPROCESS)' AND pc.collection_time <= w2.collection_time AND s.instance_name = @ServerName AND s.collection_set_uid = '49268954-4FD4-4EB6-AA04-CD59D9BB5714' -- Server Activity CS AND s.snapshot_id BETWEEN @start_snapshot_id AND @end_snapshot_id ORDER BY pc.collection_time DESC ) AS resource_wait_time_delta, 0 AS resource_signal_time_delta, DATEDIFF (second, w1.collection_time, w2.collection_time) AS interval_sec, NULL AS wait_time_ms_cumulative FROM #wait_stats AS w1 INNER JOIN #wait_stats AS w2 ON w1.wait_type = w2.wait_type AND w1.interval_id = w2.interval_id-1 -- Only return CPU stats if we weren't passed in a specific wait category WHERE (@CategoryName IS NULL OR @CategoryName = 'CPU') GROUP BY w1.interval_start_time, w2.interval_start_time, w1.interval_end_time, w2.interval_end_time, w1.collection_time, w2.collection_time ORDER BY category_name, collection_time, wait_type -- These trace flags are necessary for a good plan, due to the join on ascending core.snapshot PK OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390); END GO -- -- snapshots.fn_hexstrtovarbin -- Converts a hex string into a varbinary(max). Reporting Services does not support binary -- form parameters, so sql_handle and plan_handle values must be passed to and from RS as strings. -- For conversion in the opposite direction, use master.dbo.fn_varbintohexstr. -- Parameters: -- @hexStr - a string representation of a binary value (e.g. "0x123C2F") -- Returns: the input value converted to a true varbinary -- IF (NOT OBJECT_ID (N'[snapshots].[fn_hexstrtovarbin]', 'FN') IS NULL) BEGIN PRINT 'Dropping function [snapshots].[fn_hexstrtovarbin] ...' DROP FUNCTION [snapshots].[fn_hexstrtovarbin] END GO PRINT 'Creating function [snapshots].[fn_hexstrtovarbin] ...' GO CREATE FUNCTION [snapshots].[fn_hexstrtovarbin] ( @hexStr varchar(max) ) RETURNS varbinary(max) AS BEGIN DECLARE @ret varbinary(max) DECLARE @len int SET @ret = 0x; SET @len = LEN (@hexStr)-2; IF (@len >= 0) AND (LEFT (@hexStr, 2) = '0x') SET @hexStr = SUBSTRING (@hexStr, 3, @len); ELSE RETURN NULL; DECLARE @leftNibbleChar char(1), @rightNibbleChar char(1), @hexCharStr varchar(2) DECLARE @leftNibble int, @rightNibble int DECLARE @i int; SET @i = 1; WHILE (@i <= @len) BEGIN SET @hexCharStr = SUBSTRING (@hexStr, @i, 2) IF LEN (@hexCharStr) = 1 SET @hexCharStr = '0' + @hexCharStr SET @leftNibbleChar = LOWER (LEFT (@hexCharStr, 1)) SET @rightNibbleChar = LOWER (RIGHT (@hexCharStr, 1)) IF @leftNibbleChar BETWEEN 'a' AND 'f' COLLATE Latin1_General_BIN SET @leftNibble = (CONVERT (int, CONVERT (binary(1), @leftNibbleChar)) - CONVERT (int, CONVERT (binary(1), 'a')) + 10) * 16; ELSE IF @leftNibbleChar BETWEEN '0' AND '9' COLLATE Latin1_General_BIN SET @leftNibble = (CONVERT (int, CONVERT (binary(1), @leftNibbleChar)) - CONVERT (int, CONVERT (binary(1), '0'))) * 16; ELSE RETURN NULL; IF @rightNibbleChar BETWEEN 'a' AND 'f' COLLATE Latin1_General_BIN SET @rightNibble = (CONVERT (int, CONVERT (binary(1), @rightNibbleChar)) - CONVERT (int, CONVERT (binary(1), 'a')) + 10); ELSE IF @rightNibbleChar BETWEEN '0' AND '9' COLLATE Latin1_General_BIN SET @rightNibble = (CONVERT (int, CONVERT (binary(1), @rightNibbleChar)) - CONVERT (int, CONVERT (binary(1), '0'))); ELSE RETURN NULL; SET @ret = @ret + CONVERT (binary(1), @leftNibble + @rightNibble) SET @i = @i + 2 END RETURN @ret END GO -- -- snapshots.rpt_top_query_stats -- Returns aggregate query stats for the most expensive notable queries observed -- over the specified time interval. -- Returns the top 10 most expensive plans (ranking criteria is specified via @order_by_criteria). -- Parameters: -- @instance_name - SQL Server instance name -- @start_time - (Optional) time window start (UTC) -- @end_time - time window end (UTC) -- @time_window_size - Number of intervals in the time window (provide if @start_time is NULL) -- @time_interval_min - Number of minutes in each interval (provide if @start_time is NULL) -- @order_by_criteria - (Optional) 'CPU' (default), 'Physical Reads', 'Logical Writes', 'I/O' (reads + writes), or 'Duration' -- @database_name - Optional filter criteria: Only queries within a particular database -- IF (NOT OBJECT_ID(N'snapshots.rpt_top_query_stats', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_top_query_stats] ...' DROP PROCEDURE [snapshots].[rpt_top_query_stats] END GO PRINT 'Creating procedure [snapshots].[rpt_top_query_stats] ...' GO CREATE PROCEDURE [snapshots].[rpt_top_query_stats] @instance_name sysname, @start_time datetime = NULL, @end_time datetime = NULL, @time_window_size smallint = NULL, @time_interval_min smallint = 1, @order_by_criteria varchar(30) = 'CPU', @database_name nvarchar(255) = NULL AS BEGIN SET NOCOUNT ON; -- Clean string params (on drillthrough, RS may pass in an empty string instead of NULL) IF @database_name = '' SET @database_name = NULL -- @end_time should never be NULL when we are called from the Query Stats report -- Convert snapshot_time (datetimeoffset) to a UTC datetime IF (@end_time IS NULL) SET @end_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MAX(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); IF (@start_time IS NULL) BEGIN -- If time_window_size and time_interval_min are set use them -- to determine the start time -- Otherwise use the earliest available snapshot_time IF @time_window_size IS NOT NULL AND @time_interval_min IS NOT NULL BEGIN SET @start_time = DATEADD(minute, @time_window_size * @time_interval_min * -1.0, @end_time); END ELSE BEGIN -- Convert min snapshot_time (datetimeoffset) to a UTC datetime SET @start_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MIN(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); END END DECLARE @end_snapshot_time_id int; SELECT @end_snapshot_time_id = MAX(snapshot_time_id) FROM core.snapshots WHERE snapshot_time <= @end_time; DECLARE @start_snapshot_time_id int; SELECT @start_snapshot_time_id = MIN(snapshot_time_id) FROM core.snapshots WHERE snapshot_time >= @start_time; DECLARE @interval_sec int; SET @interval_sec = DATEDIFF (s, @start_time, @end_time) SELECT REPLACE (REPLACE (REPLACE (REPLACE (REPLACE (REPLACE ( LEFT (LTRIM (stmtsql.query_text), 100) , CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' '), ' ', ' '), ' ', ' '), ' ', ' ') AS flat_query_text, t.*, master.dbo.fn_varbintohexstr (t.sql_handle) AS sql_handle_str, stmtsql.* FROM ( SELECT TOP 10 stat.sql_handle, stat.statement_start_offset, stat.statement_end_offset, snap.source_id, SUM (stat.snapshot_execution_count) AS execution_count, SUM (stat.snapshot_execution_count) / (@interval_sec / 60) AS executions_per_min, SUM (stat.snapshot_worker_time / 1000) AS total_cpu, SUM (stat.snapshot_worker_time / 1000) / @interval_sec AS avg_cpu_per_sec, SUM (stat.snapshot_worker_time / 1000.0) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_cpu_per_exec, SUM (stat.snapshot_physical_reads) AS total_physical_reads, SUM (stat.snapshot_physical_reads) / @interval_sec AS avg_physical_reads_per_sec, SUM (stat.snapshot_physical_reads) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_physical_reads_per_exec, SUM (stat.snapshot_logical_writes) AS total_logical_writes, SUM (stat.snapshot_logical_writes) / @interval_sec AS avg_logical_writes_per_sec, SUM (stat.snapshot_logical_writes) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_logical_writes_per_exec, SUM (stat.snapshot_elapsed_time / 1000) AS total_elapsed_time, SUM (stat.snapshot_elapsed_time / 1000) / @interval_sec AS avg_elapsed_time_per_sec, SUM (stat.snapshot_elapsed_time / 1000.0) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_elapsed_time_per_exec, COUNT(*) AS row_count, COUNT (DISTINCT stat.creation_time) AS plan_count, SUM (CASE @order_by_criteria WHEN 'Duration' THEN charted_value / 1000 ELSE charted_value END) AS charted_value, -- TODO: Make this "sql.database_name" once database name is available in notable_query_text (VSTS #121662) CONVERT (nvarchar(255), '') AS database_name, ROW_NUMBER() OVER (ORDER BY SUM (charted_value) DESC) as query_rank FROM ( SELECT *, CASE @order_by_criteria WHEN 'CPU' THEN (snapshot_worker_time / 1000.0) / @interval_sec WHEN 'Physical Reads' THEN 1.0 * snapshot_physical_reads / @interval_sec WHEN 'Logical Writes' THEN 1.0 * snapshot_logical_writes / @interval_sec WHEN 'I/O' THEN 1.0 * (snapshot_physical_reads + snapshot_logical_writes) / @interval_sec WHEN 'Duration' THEN snapshot_elapsed_time / 1000.0 ELSE (snapshot_worker_time / 1000.0) / @interval_sec END AS charted_value FROM snapshots.query_stats ) AS stat INNER JOIN core.snapshots snap ON stat.snapshot_id = snap.snapshot_id -- TODO: Uncomment this and the line in the WHERE clause once database name is available in notable_query_text (VSTS #121662) -- LEFT OUTER JOIN snapshots.notable_query_text AS sql ON t.sql_handle = sql.sql_handle and sql.source_id = t.source_id WHERE snap.instance_name = @instance_name AND snap.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id -- AND ISNULL (sql.database_name = ISNULL (@database_name, stat.database_name) GROUP BY stat.sql_handle, stat.statement_start_offset, stat.statement_end_offset, snap.source_id ORDER BY ROW_NUMBER() OVER (ORDER BY SUM (charted_value) DESC) ASC ) AS t LEFT OUTER JOIN snapshots.notable_query_text sql ON t.sql_handle = sql.sql_handle and sql.source_id = t.source_id OUTER APPLY snapshots.fn_get_query_text (t.source_id, t.sql_handle, t.statement_start_offset, t.statement_end_offset) AS stmtsql ORDER BY query_rank ASC -- These trace flags are necessary for a good plan, due to the join on ascending PK w/range filter OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390) END GO -- -- snapshots.rpt_query_stats -- Returns aggregate query stats for all executions of a particular query within a specified time interval -- Parameters: -- @instance_name - SQL Server instance name -- @start_time - (Optional) time window start (UTC) -- @end_time - time window end (UTC) -- @time_window_size - Number of intervals in the time window (provide if @start_time is NULL) -- @time_interval_min - Number of minutes in each interval (provide if @start_time is NULL) -- @sql_handle_str - String representation of a SQL handle (e.g. "0x1F27BC...") -- @statement_start_offset - Start offset (byte count) for the statement within the batch identified by @sql_handle_str -- @statement_start_offset - End offset (byte count) for the statement within the batch identified by @sql_handle_str -- IF (NOT OBJECT_ID(N'snapshots.rpt_query_stats', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_query_stats] ...' DROP PROCEDURE [snapshots].[rpt_query_stats] END GO PRINT 'Creating procedure [snapshots].[rpt_query_stats] ...' GO CREATE PROCEDURE [snapshots].[rpt_query_stats] @instance_name sysname, @start_time datetime = NULL, @end_time datetime = NULL, @time_window_size smallint, @time_interval_min smallint = 1, @sql_handle_str varchar(130), @statement_start_offset int, @statement_end_offset int AS BEGIN SET NOCOUNT ON; -- @end_time should never be NULL when we are called from the Query Stats report -- Convert snapshot_time (datetimeoffset) to a UTC datetime IF (@end_time IS NULL) SET @end_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MAX(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); IF (@start_time IS NULL) BEGIN -- If time_window_size and time_interval_min are set use them -- to determine the start time -- Otherwise use the earliest available snapshot_time IF @time_window_size IS NOT NULL AND @time_interval_min IS NOT NULL BEGIN SET @start_time = DATEADD(minute, @time_window_size * @time_interval_min * -1.0, @end_time); END ELSE BEGIN -- Convert min snapshot_time (datetimeoffset) to a UTC datetime SET @start_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MIN(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); END END DECLARE @end_snapshot_time_id int; SELECT @end_snapshot_time_id = MAX(snapshot_time_id) FROM core.snapshots WHERE snapshot_time <= @end_time; DECLARE @start_snapshot_time_id int; SELECT @start_snapshot_time_id = MIN(snapshot_time_id) FROM core.snapshots WHERE snapshot_time >= @start_time; DECLARE @interval_sec int; SET @interval_sec = DATEDIFF (s, @start_time, @end_time); DECLARE @sql_handle varbinary(64) SET @sql_handle = snapshots.fn_hexstrtovarbin (@sql_handle_str) SELECT REPLACE (REPLACE (REPLACE (REPLACE (REPLACE (REPLACE ( LEFT (LTRIM (stmtsql.query_text), 100) , CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' '), ' ', ' '), ' ', ' '), ' ', ' ') AS flat_query_text, t.*, master.dbo.fn_varbintohexstr (t.sql_handle) AS sql_handle_str, stmtsql.* FROM ( SELECT stat.sql_handle, stat.statement_start_offset, stat.statement_end_offset, snap.source_id, SUM (stat.snapshot_execution_count) AS execution_count, SUM (stat.snapshot_execution_count) / (@interval_sec / 60) AS executions_per_min, SUM (stat.snapshot_worker_time / 1000) AS total_cpu, SUM (stat.snapshot_worker_time / 1000) / @interval_sec AS avg_cpu_per_sec, SUM (stat.snapshot_worker_time / 1000.0) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_cpu_per_exec, SUM (stat.snapshot_physical_reads) AS total_physical_reads, SUM (stat.snapshot_physical_reads) / @interval_sec AS avg_physical_reads_per_sec, SUM (stat.snapshot_physical_reads) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_physical_reads_per_exec, SUM (stat.snapshot_logical_writes) AS total_logical_writes, SUM (stat.snapshot_logical_writes) / @interval_sec AS avg_logical_writes_per_sec, SUM (stat.snapshot_logical_writes) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_logical_writes_per_exec, SUM (stat.snapshot_elapsed_time / 1000) AS total_elapsed_time, SUM (stat.snapshot_elapsed_time / 1000) / @interval_sec AS avg_elapsed_time_per_sec, SUM (stat.snapshot_elapsed_time / 1000.0) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_elapsed_time_per_exec, COUNT(*) AS row_count, COUNT (DISTINCT stat.creation_time) AS plan_count FROM snapshots.query_stats AS stat INNER JOIN core.snapshots snap ON stat.snapshot_id = snap.snapshot_id WHERE snap.instance_name = @instance_name AND stat.sql_handle = @sql_handle AND stat.statement_start_offset = @statement_start_offset AND stat.statement_end_offset = @statement_end_offset AND snap.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id GROUP BY stat.sql_handle, stat.statement_start_offset, stat.statement_end_offset, snap.source_id ) t LEFT OUTER JOIN snapshots.notable_query_text sql ON t.sql_handle = sql.sql_handle and sql.source_id = t.source_id OUTER APPLY snapshots.fn_get_query_text (t.source_id, t.sql_handle, t.statement_start_offset, t.statement_end_offset) AS stmtsql -- These trace flags are necessary for a good plan, due to the join on ascending PK w/range filter OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390) END GO -- -- snapshots.rpt_query_plan_stats -- Returns aggregate stats for the all plans observed for a query within a specified time interval. -- Returns the top 10 most expensive plans (ranking criteria is specified via @order_by_criteria). -- @instance_name - SQL Server instance name -- @start_time - (Optional) time window start (UTC) -- @end_time - time window end (UTC) -- @time_window_size - Number of intervals in the time window (provide if @start_time is NULL) -- @time_interval_min - Number of minutes in each interval (provide if @start_time is NULL) -- @sql_handle_str - String representation of a SQL handle (e.g. "0x1F27BC...") -- @plan_handle_str - String representation of a plan handle (e.g. "0x1F27BC...") -- @plan_creation_time - (Optional) Plan creation time -- @statement_start_offset - Start offset (byte count) for the statement within the batch identified by @sql_handle_str -- @statement_start_offset - End offset (byte count) for the statement within the batch identified by @sql_handle_str -- @order_by_criteria - (Optional) 'CPU' (default), 'Physical Reads', 'Logical Writes', 'I/O' (reads+writes), or 'Duration' -- IF (NOT OBJECT_ID(N'snapshots.rpt_query_plan_stats', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_query_plan_stats] ...' DROP PROCEDURE [snapshots].[rpt_query_plan_stats] END GO PRINT 'Creating procedure [snapshots].[rpt_query_plan_stats] ...' GO CREATE PROCEDURE [snapshots].[rpt_query_plan_stats] @instance_name sysname, @start_time datetime = NULL, @end_time datetime = NULL, @time_window_size smallint, @time_interval_min smallint = 1, @sql_handle_str varchar(130), @plan_handle_str varchar(130) = NULL, @plan_creation_time datetime = NULL, @statement_start_offset int, @statement_end_offset int, @order_by_criteria varchar(30) = 'CPU' AS BEGIN SET NOCOUNT ON; -- @end_time should never be NULL when we are called from the Query Stats report -- Convert snapshot_time (datetimeoffset) to a UTC datetime IF (@end_time IS NULL) SET @end_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MAX(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); IF (@start_time IS NULL) BEGIN -- If time_window_size and time_interval_min are set use them -- to determine the start time -- Otherwise use the earliest available snapshot_time IF @time_window_size IS NOT NULL AND @time_interval_min IS NOT NULL BEGIN SET @start_time = DATEADD(minute, @time_window_size * @time_interval_min * -1.0, @end_time); END ELSE BEGIN -- Convert min snapshot_time (datetimeoffset) to a UTC datetime SET @start_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MIN(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); END END DECLARE @end_snapshot_time_id int; SELECT @end_snapshot_time_id = MAX(snapshot_time_id) FROM core.snapshots WHERE snapshot_time <= @end_time; DECLARE @start_snapshot_time_id int; SELECT @start_snapshot_time_id = MIN(snapshot_time_id) FROM core.snapshots WHERE snapshot_time >= @start_time; DECLARE @interval_sec int; SET @interval_sec = DATEDIFF (s, @start_time, @end_time); -- SQL and plan handles are passed in as a hex-formatted string. Convert to varbinary. DECLARE @sql_handle varbinary(64), @plan_handle varbinary(64) SET @sql_handle = snapshots.fn_hexstrtovarbin (@sql_handle_str) IF LEN (@plan_handle_str) > 0 BEGIN SET @plan_handle = snapshots.fn_hexstrtovarbin (@plan_handle_str) END SELECT t.*, master.dbo.fn_varbintohexstr (t.sql_handle) AS sql_handle_str, master.dbo.fn_varbintohexstr (t.plan_handle) AS plan_handle_str FROM ( SELECT stat.sql_handle, stat.statement_start_offset, stat.statement_end_offset, stat.plan_handle, CONVERT (datetime, SWITCHOFFSET (CAST (stat.creation_time AS datetimeoffset(7)), '+00:00')) AS creation_time, CONVERT (varchar, CONVERT (datetime, SWITCHOFFSET (CAST (MAX (stat.creation_time) AS datetimeoffset(7)), '+00:00')), 126) AS creation_time_str, CONVERT (datetime, SWITCHOFFSET (CAST (MAX (stat.last_execution_time) AS datetimeoffset(7)), '+00:00')) AS last_execution_time, snap.source_id, SUM (stat.snapshot_execution_count) AS execution_count, SUM (stat.snapshot_execution_count) / (@interval_sec / 60) AS executions_per_min, SUM (stat.snapshot_worker_time / 1000) AS total_cpu, SUM (stat.snapshot_worker_time / 1000) / @interval_sec AS avg_cpu_per_sec, SUM (stat.snapshot_worker_time / 1000.0) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_cpu_per_exec, SUM (stat.snapshot_physical_reads) AS total_physical_reads, SUM (stat.snapshot_physical_reads) / @interval_sec AS avg_physical_reads_per_sec, SUM (stat.snapshot_physical_reads) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_physical_reads_per_exec, SUM (stat.snapshot_logical_writes) AS total_logical_writes, SUM (stat.snapshot_logical_writes) / @interval_sec AS avg_logical_writes_per_sec, SUM (stat.snapshot_logical_writes) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_logical_writes_per_exec, SUM (stat.snapshot_elapsed_time / 1000) AS total_elapsed_time, SUM (stat.snapshot_elapsed_time / 1000) / @interval_sec AS avg_elapsed_time_per_sec, SUM (stat.snapshot_elapsed_time / 1000.0) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_elapsed_time_per_exec, COUNT(*) AS row_count, COUNT (DISTINCT stat.creation_time) AS plan_count, SUM (charted_value) AS charted_value, ROW_NUMBER() OVER (ORDER BY SUM (charted_value) DESC) as query_rank FROM ( SELECT *, -- This is the criteria used to rank the returned rowset and determine the order within Top-N plans -- returned from here. It is important that this part of the query stays in sync with a similar -- part of the query in snapshots.rpt_query_plan_stats_timeline procedure CASE @order_by_criteria WHEN 'CPU' THEN ((snapshot_worker_time / 1000.0) / CASE snapshot_execution_count WHEN 0 THEN 1 ELSE snapshot_execution_count END) WHEN 'Physical Reads' THEN 1.0 * (snapshot_physical_reads / CASE snapshot_execution_count WHEN 0 THEN 1 ELSE snapshot_execution_count END) WHEN 'Logical Writes' THEN 1.0 * (snapshot_logical_writes / CASE snapshot_execution_count WHEN 0 THEN 1 ELSE snapshot_execution_count END) WHEN 'I/O' THEN 1.0 * ((snapshot_physical_reads + snapshot_logical_writes) / CASE snapshot_execution_count WHEN 0 THEN 1 ELSE snapshot_execution_count END) WHEN 'Duration' THEN ((snapshot_elapsed_time / 1000.0) / CASE snapshot_execution_count WHEN 0 THEN 1 ELSE snapshot_execution_count END) ELSE ((snapshot_worker_time / 1000.0) / CASE snapshot_execution_count WHEN 0 THEN 1 ELSE snapshot_execution_count END) END AS charted_value FROM snapshots.query_stats WHERE sql_handle = @sql_handle AND (@plan_handle IS NULL OR plan_handle = @plan_handle) AND (@plan_creation_time IS NULL OR creation_time = @plan_creation_time) AND statement_start_offset = @statement_start_offset AND statement_end_offset = @statement_end_offset ) stat INNER JOIN core.snapshots snap ON stat.snapshot_id = snap.snapshot_id WHERE snap.instance_name = @instance_name AND snap.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id GROUP BY stat.sql_handle, stat.statement_start_offset, stat.statement_end_offset, stat.plan_handle, stat.creation_time, snap.source_id ) t WHERE (query_rank <= 10) ORDER BY query_rank ASC -- These trace flags are necessary for a good plan, due to the join on ascending PK w/range filter OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390) END GO -- -- snapshots.rpt_query_plan_stats_timeline -- Returns stats for the top 10 plans observed for a query within a specified time interval. -- Output is intended for plotting this over a time window. -- Parameters: -- @instance_name - SQL Server instance name -- @start_time - (Optional) time window start (UTC) -- @end_time - time window end (UTC) -- @time_window_size - Number of intervals in the time window (provide if @start_time is NULL) -- @time_interval_min - Number of minutes in each interval (provide if @start_time is NULL) -- @sql_handle_str - String representation of a SQL handle (e.g. "0x1F27BC...") -- @plan_handle_str - (Optional) String representation of a plan handle (e.g. "0x1F27BC..."). Omit to see stats for all plans -- @plan_creation_time - (Optional) Plan creation time -- @statement_start_offset - Start offset (byte count) for the statement within the batch identified by @sql_handle_str -- @statement_start_offset - End offset (byte count) for the statement within the batch identified by @sql_handle_str -- @order_by_criteria - (Optional) 'CPU' (default), 'Physical Reads', 'Logical Writes', 'I/O' (reads+writes), or 'Duration' -- IF (NOT OBJECT_ID(N'snapshots.rpt_query_plan_stats_timeline', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_query_plan_stats_timeline] ...' DROP PROCEDURE [snapshots].[rpt_query_plan_stats_timeline] END GO PRINT 'Creating procedure [snapshots].[rpt_query_plan_stats_timeline] ...' GO CREATE PROCEDURE [snapshots].[rpt_query_plan_stats_timeline] @instance_name sysname, @start_time datetime = NULL, @end_time datetime = NULL, @time_window_size smallint, @time_interval_min smallint = 1, @sql_handle_str varchar(130), @plan_handle_str varchar(130) = NULL, @plan_creation_time datetime = NULL, @statement_start_offset int, @statement_end_offset int, @order_by_criteria varchar(30) = 'CPU' AS BEGIN SET NOCOUNT ON; -- @end_time should never be NULL when we are called from the Query Stats report -- Convert snapshot_time (datetimeoffset) to a UTC datetime IF (@end_time IS NULL) SET @end_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MAX(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); IF (@start_time IS NULL) BEGIN -- If time_window_size and time_interval_min are set use them -- to determine the start time -- Otherwise use the earliest available snapshot_time IF @time_window_size IS NOT NULL AND @time_interval_min IS NOT NULL BEGIN SET @start_time = DATEADD(minute, @time_window_size * @time_interval_min * -1.0, @end_time); END ELSE BEGIN -- Convert min snapshot_time (datetimeoffset) to a UTC datetime SET @start_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MIN(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); END END DECLARE @end_snapshot_time_id int; SELECT @end_snapshot_time_id = MAX(snapshot_time_id) FROM core.snapshots WHERE snapshot_time <= @end_time; DECLARE @start_snapshot_time_id int; SELECT @start_snapshot_time_id = MIN(snapshot_time_id) FROM core.snapshots WHERE snapshot_time >= @start_time; -- SQL and plan handles are passed in as a hex-formatted string. Convert to varbinary. DECLARE @sql_handle varbinary(64), @plan_handle varbinary(64) SET @sql_handle = snapshots.fn_hexstrtovarbin (@sql_handle_str) IF LEN (ISNULL (@plan_handle_str, '')) > 0 BEGIN SET @plan_handle = snapshots.fn_hexstrtovarbin (@plan_handle_str) END CREATE TABLE #top_plans ( plan_handle varbinary(64), creation_time datetimeoffset(7), plan_rank int ) -- If we weren't told to focus on a particular plan... IF (@plan_handle IS NULL) BEGIN -- Get the top 10 most expensive plans for this query during the specified -- time window. INSERT INTO #top_plans SELECT * FROM ( SELECT plan_handle, creation_time, ROW_NUMBER() OVER (ORDER BY SUM (ranking_value) DESC) AS plan_rank FROM ( SELECT *, -- This is the criteria used to rank the returned rowset and determine the order within Top-N plans -- returned from here. It is important that this part of the query stays in sync with a similar -- part of the query in snapshots.rpt_query_plan_stats procedure CASE @order_by_criteria WHEN 'CPU' THEN ((snapshot_worker_time / 1000.0) / CASE snapshot_execution_count WHEN 0 THEN 1 ELSE snapshot_execution_count END) WHEN 'Physical Reads' THEN 1.0 * (snapshot_physical_reads / CASE snapshot_execution_count WHEN 0 THEN 1 ELSE snapshot_execution_count END) WHEN 'Logical Writes' THEN 1.0 * (snapshot_logical_writes / CASE snapshot_execution_count WHEN 0 THEN 1 ELSE snapshot_execution_count END) WHEN 'I/O' THEN 1.0 * ((snapshot_physical_reads + snapshot_logical_writes) / CASE snapshot_execution_count WHEN 0 THEN 1 ELSE snapshot_execution_count END) WHEN 'Duration' THEN ((snapshot_elapsed_time / 1000.0) / CASE snapshot_execution_count WHEN 0 THEN 1 ELSE snapshot_execution_count END) ELSE ((snapshot_worker_time / 1000.0) / CASE snapshot_execution_count WHEN 0 THEN 1 ELSE snapshot_execution_count END) END AS ranking_value FROM snapshots.query_stats WHERE sql_handle = @sql_handle AND statement_start_offset = @statement_start_offset AND statement_end_offset = @statement_end_offset ) AS stat INNER JOIN core.snapshots snap ON stat.snapshot_id = snap.snapshot_id WHERE snap.instance_name = @instance_name AND snap.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id GROUP BY plan_handle, creation_time ) AS t WHERE t.plan_rank <= 10 -- These trace flags are necessary for a good plan, due to the join on ascending PK w/range filter OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390); END ELSE BEGIN -- @plan_handle is not NULL; we have been told to focus on a particular plan. INSERT INTO #top_plans VALUES (@plan_handle, @plan_creation_time, 1) END; -- Get statistics for these 10 plans for each collection point in the time window WITH raw_stat AS ( SELECT *, CASE @order_by_criteria WHEN 'CPU' THEN snapshot_worker_time / 1000.0 WHEN 'Physical Reads' THEN snapshot_physical_reads WHEN 'Logical Writes' THEN snapshot_logical_writes WHEN 'I/O' THEN (snapshot_logical_writes + snapshot_physical_reads) WHEN 'Duration' THEN snapshot_elapsed_time / 1000.0 ELSE snapshot_worker_time / 1000.0 END AS charted_value FROM snapshots.query_stats AS stat ) SELECT t.*, master.dbo.fn_varbintohexstr (t.sql_handle) AS sql_handle_str, master.dbo.fn_varbintohexstr (t.plan_handle) AS plan_handle_str FROM ( SELECT stat.sql_handle, stat.statement_start_offset, stat.statement_end_offset, stat.plan_handle, CONVERT (datetime, SWITCHOFFSET (CAST (stat.creation_time AS datetimeoffset(7)), '+00:00')) AS creation_time, CONVERT (varchar, CONVERT (datetime, SWITCHOFFSET (CAST (MAX (stat.creation_time) AS datetimeoffset(7)), '+00:00')), 126) AS creation_time_str, CONVERT (datetime, SWITCHOFFSET (CAST (MAX (stat.last_execution_time) AS datetimeoffset(7)), '+00:00')) AS last_execution_time, CONVERT (datetime, SWITCHOFFSET (CAST (stat.collection_time_chart AS datetimeoffset(7)), '+00:00')) AS collection_time, snap.source_id, SUM (stat.snapshot_execution_count) AS execution_count, SUM (stat.snapshot_worker_time / 1000) AS total_cpu, SUM (stat.snapshot_worker_time / 1000) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_cpu_per_exec, SUM (stat.snapshot_physical_reads) AS total_physical_reads, SUM (stat.snapshot_physical_reads) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_physical_reads_per_exec, SUM (stat.snapshot_logical_writes) AS total_logical_writes, SUM (stat.snapshot_logical_writes) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_logical_writes_per_exec, SUM (stat.snapshot_elapsed_time / 1000) AS total_elapsed_time, SUM (stat.snapshot_elapsed_time / 1000) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS avg_elapsed_time_per_exec, COUNT(*) AS row_count, COUNT (DISTINCT stat.creation_time) AS plan_count, SUM (stat.charted_value) AS charted_value, SUM (stat.charted_value) / CASE SUM (stat.snapshot_execution_count) WHEN 0 THEN 1 ELSE SUM (stat.snapshot_execution_count) END AS charted_value_per_exec, MAX (topN.plan_rank) AS plan_rank -- same value for all rows within a group FROM ( -- Work around a RS chart limitation (single data points do not plot on line charts). -- Fake a second data point shortly after the first so even short-lived plans will -- gets plotted. SELECT *, collection_time AS collection_time_chart FROM raw_stat UNION ALL SELECT *, DATEADD (mi, 1, collection_time) AS collection_time_chart FROM raw_stat ) AS stat INNER JOIN #top_plans AS topN ON topN.plan_handle = stat.plan_handle AND topN.creation_time = stat.creation_time INNER JOIN core.snapshots snap ON stat.snapshot_id = snap.snapshot_id WHERE stat.sql_handle = @sql_handle AND statement_start_offset = @statement_start_offset AND statement_end_offset = @statement_end_offset AND snap.instance_name = @instance_name AND snap.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id GROUP BY stat.sql_handle, stat.statement_start_offset, stat.statement_end_offset, stat.plan_handle, stat.creation_time, snap.source_id, stat.collection_time_chart ) AS t WHERE (plan_rank <= 10) ORDER BY plan_rank ASC, collection_time ASC -- These trace flags are necessary for a good plan, due to the join on ascending PK w/range filter OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390) END GO -- -- snapshots.rpt_query_plan_missing_indexes -- Returns any missing indexes recorded in a query plan, formatted as CREATE INDEX statements. -- Parameters: -- @instance_name - SQL Server instance name -- @plan_handle_str - String representation of a plan handle (e.g. "0x1F27BC...") -- @statement_start_offset - Start offset (byte count) for the statement within the plan identified by @plan_handle_str -- @statement_start_offset - End offset (byte count) for the statement within the plan identified by @plan_handle_str -- IF (NOT OBJECT_ID(N'snapshots.rpt_query_plan_missing_indexes', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_query_plan_missing_indexes] ...' DROP PROCEDURE [snapshots].[rpt_query_plan_missing_indexes] END GO PRINT 'Creating procedure [snapshots].[rpt_query_plan_missing_indexes] ...' GO CREATE PROCEDURE [snapshots].[rpt_query_plan_missing_indexes] @instance_name sysname, @plan_handle_str varchar(130), @statement_start_offset int, @statement_end_offset int AS BEGIN SET NOCOUNT ON; DECLARE @showplan nvarchar(max) DECLARE @plan_handle varbinary(64) DECLARE @element nvarchar(512), @element_start nvarchar(512), @element_end nvarchar(512) DECLARE @xml_fragment xml DECLARE @start_offset int -- We may be invoked with a NULL/empty string plan handle if the user has not yet drilled down into -- a specific query plan on the query_stats_detail report. IF ISNULL (@plan_handle_str, '') = '' BEGIN RETURN END SET @plan_handle = snapshots.fn_hexstrtovarbin (@plan_handle_str) SELECT TOP 1 @showplan = CONVERT (nvarchar(max), qp.query_plan) FROM snapshots.notable_query_plan qp INNER JOIN core.snapshots snap ON snap.source_id = qp.source_id WHERE plan_handle = @plan_handle AND statement_start_offset = @statement_start_offset AND statement_end_offset = @statement_end_offset AND snap.instance_name = @instance_name -- Get sql_handle to enable a clustered index seek on notable_query_plans AND qp.sql_handle = ( SELECT TOP 1 sql_handle FROM snapshots.notable_query_plan AS qp INNER JOIN core.snapshots snap ON snap.source_id = qp.source_id WHERE plan_handle = @plan_handle AND statement_start_offset = @statement_start_offset AND statement_end_offset = @statement_end_offset AND snap.instance_name = @instance_name ); SET @element = 'MissingIndexes' -- Use non-XML methods to extract the fragment. Some query plans may be too complex -- to represent in the T-SQL xml data type, but the node will always be simple enough. -- Doing this ensures that we will always be able to extract any missing index information from the -- plan even if the plan itself is too complex for the T-SQL xml type. SET @element_start = '<' + @element + '>' SET @element_end = '' SET @start_offset = ISNULL (PATINDEX ('%' + @element_start + '%', @showplan), 0) IF @start_offset > 0 BEGIN SET @xml_fragment = SUBSTRING (@showplan, @start_offset, PATINDEX ('%' + @element_end + '%', @showplan) - @start_offset + LEN (@element_end)); -- Sample fragment from an XML query plan: -- -- -- -- -- -- -- -- -- -- -- -- -- SELECT 'CREATE INDEX [ncidx_mdw_' + LEFT (target_object_name, 20) -- Random component to make name conflicts less likely + '_' + CONVERT (varchar(30), ABS (CONVERT (binary(6), NEWID()) % 1000)) + '] ON ' + target_object_fullname + ' (' + ISNULL (equality_columns, '') + CASE WHEN ISNULL (equality_columns, '') != '' AND ISNULL (inequality_columns, '') != '' THEN ',' ELSE '' END + ISNULL (inequality_columns, '') + ')' + CASE WHEN ISNULL (included_columns, '') != '' THEN ' INCLUDE (' + included_columns + ')' ELSE '' END AS create_idx_statement, * FROM ( SELECT index_node.value('(../@Impact)[1]', 'float') as index_impact, REPLACE (REPLACE (index_node.value('(./@Table)[1]', 'nvarchar(512)'), '[', ''), ']', '') AS target_object_name, CONVERT (nvarchar(1024), index_node.query('concat( string((./@Database)[1]), ".", string((./@Schema)[1]), ".", string((./@Table)[1]) )')) AS target_object_fullname, REPLACE (CONVERT (nvarchar(max), index_node.query('for $colgroup in ./ColumnGroup, $col in $colgroup/Column where $colgroup/@Usage = "EQUALITY" return string($col/@Name)')), '] [', '],[') AS equality_columns, REPLACE (CONVERT (nvarchar(max), index_node.query('for $colgroup in ./ColumnGroup, $col in $colgroup/Column where $colgroup/@Usage = "INEQUALITY" return string($col/@Name)')), '] [', '],[') AS inequality_columns, REPLACE (CONVERT (nvarchar(max), index_node.query('for $colgroup in .//ColumnGroup, $col in $colgroup/Column where $colgroup/@Usage = "INCLUDE" return string($col/@Name)')), '] [', '],[') AS included_columns FROM (SELECT @xml_fragment AS fragment) AS missing_indexes_fragment CROSS APPLY missing_indexes_fragment.fragment.nodes('/MissingIndexes/MissingIndexGroup/MissingIndex') AS missing_indexes (index_node) ) AS t END END GO -- -- snapshots.rpt_query_plan_parameters -- Returns the compile-time parameters that a query plan was optimized for -- Parameters: -- @instance_name - SQL Server instance name -- @plan_handle_str - String representation of a plan handle (e.g. "0x1F27BC...") -- @statement_start_offset - Start offset (byte count) for the statement within the plan identified by @plan_handle_str -- @statement_start_offset - End offset (byte count) for the statement within the plan identified by @plan_handle_str -- IF (NOT OBJECT_ID(N'snapshots.rpt_query_plan_parameters', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_query_plan_parameters] ...' DROP PROCEDURE [snapshots].rpt_query_plan_parameters END GO PRINT 'Creating procedure [snapshots].[rpt_query_plan_parameters] ...' GO CREATE PROCEDURE [snapshots].rpt_query_plan_parameters @instance_name sysname, @plan_handle_str varchar(130), @statement_start_offset int, @statement_end_offset int AS BEGIN SET NOCOUNT ON; DECLARE @showplan xml DECLARE @plan_handle varbinary(64) -- We may be invoked with a NULL/empty string plan handle if the user has not yet drilled down into -- a specific query plan on the query_stats_detail report. IF ISNULL (@plan_handle_str, '') = '' BEGIN RETURN END SET @plan_handle = snapshots.fn_hexstrtovarbin (@plan_handle_str) BEGIN TRY SELECT TOP 1 @showplan = CONVERT (xml, qp.query_plan) FROM snapshots.notable_query_plan qp INNER JOIN core.snapshots snap ON snap.source_id = qp.source_id WHERE plan_handle = @plan_handle AND statement_start_offset = @statement_start_offset AND statement_end_offset = @statement_end_offset AND snap.instance_name = @instance_name -- Get sql_handle to enable a clustered index seek on notable_query_plans AND qp.sql_handle = ( SELECT TOP 1 sql_handle FROM snapshots.notable_query_plan AS qp INNER JOIN core.snapshots snap ON snap.source_id = qp.source_id WHERE plan_handle = @plan_handle AND statement_start_offset = @statement_start_offset AND statement_end_offset = @statement_end_offset AND snap.instance_name = @instance_name ); END TRY BEGIN CATCH -- It is expected that we may end up here even under normal circumstances. Some plans are simply too -- complex to represent using T-SQL's xml datatype. Raise a low-severity message with the error details. DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; SELECT @ErrorLine = ERROR_LINE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(); -- "Unable to convert showplan to XML. Error #%d on Line %d: %s" RAISERROR (14697, 0, 1, @ErrorNumber, @ErrorLine, @ErrorMessage); END CATCH; WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS sp) SELECT param_list.param_node.value('(./@Column)[1]', 'nvarchar(512)') AS param_name, param_list.param_node.value('(./@ParameterCompiledValue)[1]', 'nvarchar(max)') AS param_compiled_value FROM (SELECT @showplan AS query_plan) AS p CROSS APPLY p.query_plan.nodes ('/sp:ShowPlanXML/sp:BatchSequence/sp:Batch/sp:Statements/sp:StmtSimple/sp:QueryPlan[1]/sp:ParameterList[1]/sp:ColumnReference') as param_list (param_node) END GO -- -- snapshots.rpt_query_plan_details -- Returns details of a query plan (estimated cost, compile-time CPU, etc) -- Parameters: -- @instance_name - SQL Server instance name -- @plan_handle_str - String representation of a plan handle (e.g. "0x1F27BC...") -- @statement_start_offset - Start offset (byte count) for the statement within the plan identified by @plan_handle_str -- @statement_start_offset - End offset (byte count) for the statement within the plan identified by @plan_handle_str -- IF (NOT OBJECT_ID(N'snapshots.rpt_query_plan_details', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_query_plan_details] ...' DROP PROCEDURE [snapshots].rpt_query_plan_details END GO PRINT 'Creating procedure [snapshots].[rpt_query_plan_details] ...' GO CREATE PROCEDURE [snapshots].rpt_query_plan_details @instance_name sysname, @plan_handle_str varchar(130), @statement_start_offset int, @statement_end_offset int AS BEGIN SET NOCOUNT ON; DECLARE @showplan xml DECLARE @plan_handle varbinary(64) -- We may be invoked with a NULL/empty string plan handle if the user has not yet drilled down into -- a specific query plan on the query_stats_detail report. IF ISNULL (@plan_handle_str, '') = '' BEGIN RETURN END SET @plan_handle = snapshots.fn_hexstrtovarbin (@plan_handle_str) BEGIN TRY SELECT TOP 1 @showplan = CONVERT (xml, qp.query_plan) FROM snapshots.notable_query_plan AS qp INNER JOIN core.snapshots snap ON snap.source_id = qp.source_id WHERE plan_handle = @plan_handle AND statement_start_offset = @statement_start_offset AND statement_end_offset = @statement_end_offset AND snap.instance_name = @instance_name -- Get sql_handle to enable a clustered index seek on notable_query_plans AND qp.sql_handle = ( SELECT TOP 1 sql_handle FROM snapshots.notable_query_plan AS qp INNER JOIN core.snapshots snap ON snap.source_id = qp.source_id WHERE plan_handle = @plan_handle AND statement_start_offset = @statement_start_offset AND statement_end_offset = @statement_end_offset AND snap.instance_name = @instance_name ); END TRY BEGIN CATCH -- It is expected that we may end up here even under normal circumstances. Some plans are simply too -- complex to represent using T-SQL's xml datatype. Raise a low-severity message with the error details. DECLARE @ErrorMessage NVARCHAR(4000); DECLARE @ErrorNumber INT; DECLARE @ErrorLine INT; SELECT @ErrorLine = ERROR_LINE(), @ErrorNumber = ERROR_NUMBER(), @ErrorMessage = ERROR_MESSAGE(); -- "Unable to convert showplan to XML. Error #%d on Line %d: %s" RAISERROR (14697, 0, 1, @ErrorNumber, @ErrorLine, @ErrorMessage); RETURN END CATCH; WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS sp) SELECT TOP 10 CONVERT (bigint, stmt_simple.stmt_node.value('(./@StatementEstRows)[1]', 'decimal(28,10)')) AS stmt_est_rows, stmt_simple.stmt_node.value('(./@StatementOptmLevel)[1]', 'varchar(30)') AS stmt_optimization_level, stmt_simple.stmt_node.value('(./@StatementOptmEarlyAbortReason)[1]', 'varchar(30)') AS stmt_optimization_early_abort_reason, stmt_simple.stmt_node.value('(./@StatementSubTreeCost)[1]', 'float') AS stmt_est_subtree_cost, stmt_simple.stmt_node.value('(./@StatementText)[1]', 'nvarchar(max)') AS stmt_text, stmt_simple.stmt_node.value('(./@ParameterizedText)[1]', 'nvarchar(max)') AS stmt_parameterized_text, stmt_simple.stmt_node.value('(./@StatementType)[1]', 'varchar(30)') AS stmt_type, stmt_simple.stmt_node.value('(./@PlanGuideName)[1]', 'varchar(30)') AS plan_guide_name, stmt_simple.stmt_node.value('(./sp:QueryPlan/@CachedPlanSize)[1]', 'int') AS plan_size, stmt_simple.stmt_node.value('(./sp:QueryPlan/@CompileTime)[1]', 'bigint') AS plan_compile_time, stmt_simple.stmt_node.value('(./sp:QueryPlan/@CompileCPU)[1]', 'bigint') AS plan_compile_cpu, stmt_simple.stmt_node.value('(./sp:QueryPlan/@CompileMemory)[1]', 'int') AS plan_compile_memory FROM (SELECT @showplan AS query_plan) AS p CROSS APPLY p.query_plan.nodes ('/sp:ShowPlanXML/sp:BatchSequence/sp:Batch/sp:Statements/sp:StmtSimple') as stmt_simple (stmt_node) END GO -- -- snapshots.rpt_blocking_chains -- Returns summary of blocking chains that existed in a specified time window -- Parameters: -- @instance_name - SQL Server instance name -- @start_time - time window start (UTC) -- @end_time - time window end (UTC) -- IF (NOT OBJECT_ID(N'snapshots.rpt_blocking_chains', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_blocking_chains] ...' DROP PROCEDURE [snapshots].[rpt_blocking_chains] END GO PRINT 'Creating procedure [snapshots].[rpt_blocking_chains] ...' GO CREATE PROCEDURE [snapshots].[rpt_blocking_chains] @instance_name sysname, @start_time datetime = NULL, @end_time datetime, @WindowSize int = NULL AS BEGIN SET NOCOUNT ON; -- Compensate for RS truncation of fractional seconds SET @end_time = DATEADD (second, 1, @end_time) -- If @start_time is NULL, calc it using @end_time and @WindowSize IF @start_time IS NULL SET @start_time = DATEADD (minute, -1 * @WindowSize, @end_time) -- Get all collection times for the "Active Sessions and Requests" collection item SELECT DISTINCT r1.collection_time, r1.snapshot_id, DENSE_RANK() OVER (ORDER BY r1.collection_time) AS collection_time_id INTO #collection_times FROM snapshots.active_sessions_and_requests AS r1 INNER JOIN core.snapshots s ON s.snapshot_id = r1.snapshot_id WHERE s.instance_name = @instance_name AND r1.collection_time BETWEEN @start_time AND @end_time DECLARE @max_collection_time datetimeoffset(7) SELECT @max_collection_time = MAX (collection_time) FROM #collection_times -- Get all head blockers during the selected time window SELECT r1.*, times.collection_time_id INTO #blocking_participants FROM #collection_times AS times LEFT OUTER JOIN snapshots.active_sessions_and_requests AS r1 ON r1.collection_time = times.collection_time AND r1.snapshot_id = times.snapshot_id WHERE r1.blocking_session_id = 0 AND session_id IN ( SELECT DISTINCT blocking_session_id FROM snapshots.active_sessions_and_requests AS r2 WHERE r2.blocking_session_id != 0 AND r2.collection_time = r1.collection_time AND r2.snapshot_id = r1.snapshot_id ) CREATE NONCLUSTERED INDEX IDX1_blocking_participants ON #blocking_participants ( session_id, collection_time_id, blocking_session_id ) -- List all blocking chains during this time window. -- For the purposes of this overview, we define a blocking chain as a contiguous series -- of samples where the same spid remains the head blocker. Rolling blocking will be viewed -- as multiple discrete chains. SELECT MIN (head_blockers.collection_time) AS blocking_start_time, -- We know when the blocking ended within a (roughly) 10 second window. Assume it stopped approximately -- at the midpoint. DATEADD (second, 5, ISNULL (MAX (blocking_end_times.collection_time), @max_collection_time)) AS blocking_end_time, DATEDIFF (second, MIN (head_blockers.collection_time), ISNULL (MAX (blocking_end_times.collection_time), @max_collection_time)) AS blocking_duration_sec, head_blockers.session_id AS head_blocker_session_id, MIN (head_blockers.[program_name]) AS [program_name], MIN (head_blockers.[database_name]) AS [database_name], COUNT(*) AS observed_sample_count, -- Number of times we saw this blocking chain CASE WHEN MAX (blocking_end_times.collection_time) IS NULL THEN 1 ELSE 0 END AS still_active INTO #blocking_chains FROM ( SELECT ( -- Find the end time for this blocking incident SELECT MIN (collection_time_id) FROM #collection_times AS times1 WHERE times1.collection_time_id > blockers_start.collection_time_id AND times1.collection_time_id <= @end_time AND NOT EXISTS ( SELECT * FROM #blocking_participants AS blk1 WHERE blk1.session_id = blockers_start.session_id AND blk1.[program_name] = blockers_start.[program_name] AND blk1.login_time = blockers_start.login_time AND blk1.collection_time_id = times1.collection_time_id AND blk1.blocking_session_id = 0 ) ) AS blocking_end_collection_time_id, * FROM #blocking_participants AS blockers_start WHERE blockers_start.blocking_session_id = 0 ) AS head_blockers LEFT OUTER JOIN #collection_times AS blocking_end_times ON blocking_end_times.collection_time_id = head_blockers.blocking_end_collection_time_id - 1 GROUP BY head_blockers.session_id, head_blockers.blocking_end_collection_time_id ORDER BY MIN (head_blockers.collection_time) -- This proc supports two different chart elements: a table, with one row per blocking -- chain, and a timeline chart, with one series per blocking chain. The chart must -- return two data points per series in order to correctly plot the blocking chain's -- beginning and end times. The chart uses the output of both of the following UNIONed -- SELECT statements, while the table filters out the second resultset -- ([chart_data_only]=0). Doing this avoids the need to waste time running two procs -- that are almost identical. SELECT blocking_chain_number, CONVERT (datetime, SWITCHOFFSET (CAST (blocking_start_time AS datetimeoffset(7)), '+00:00')) AS blocking_start_time, CONVERT (datetime, SWITCHOFFSET (CAST (blocking_end_time AS datetimeoffset(7)), '+00:00')) AS blocking_end_time, blocking_duration_sec, head_blocker_session_id, [program_name], [database_name], observed_sample_count, still_active, -- Represent this time as a string to avoid RS datetime truncation when the report passes it back to us on drillthrough CONVERT (varchar(40), SWITCHOFFSET (CAST (blocking_start_time AS datetimeoffset(7)), '+00:00'), 126) AS blocking_start_time_str, CONVERT (datetime, SWITCHOFFSET (CAST (chart_time AS datetimeoffset(7)), '+00:00')) AS chart_time, chart_data_only FROM ( SELECT TOP 10 ROW_NUMBER() OVER (ORDER BY blocking_duration_sec DESC) AS blocking_chain_number, *, blocking_start_time AS chart_time, 0 AS chart_data_only FROM #blocking_chains ORDER BY blocking_duration_sec DESC UNION ALL SELECT TOP 10 ROW_NUMBER() OVER (ORDER BY blocking_duration_sec DESC) AS blocking_chain_number, *, blocking_end_time AS chart_time, 1 AS chart_data_only FROM #blocking_chains ORDER BY blocking_duration_sec DESC ) AS t ORDER BY blocking_chain_number, chart_data_only END GO -- -- snapshots.rpt_blocking_chain_detail -- Returns details about a blocking chain -- Parameters: -- @instance_name - SQL Server instance name -- @blocking_time_str - a string representation of a datetimeoffset value during the -- period where @head_blocker_session_id was the cause of blocking (UTC) -- @head_blocker_session_id - session ID (SPID) of the head of the blocker chain -- IF (NOT OBJECT_ID(N'snapshots.rpt_blocking_chain_detail', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_blocking_chain_detail] ...' DROP PROCEDURE [snapshots].[rpt_blocking_chain_detail] END GO PRINT 'Creating procedure [snapshots].[rpt_blocking_chain_detail] ...' GO CREATE PROCEDURE [snapshots].[rpt_blocking_chain_detail] @instance_name sysname, @blocking_time_str varchar(40), @head_blocker_session_id int AS BEGIN SET NOCOUNT ON; DECLARE @blocking_start_time datetimeoffset(7) DECLARE @blocking_end_time datetimeoffset(7) DECLARE @blocking_time datetimeoffset(7) -- The report passed in the blocking time as a string to avoid RS date truncation. -- Convert this to a datetimeoffset value in UTC time. SET @blocking_time = SWITCHOFFSET (CAST (@blocking_time_str AS datetimeoffset(7)), '+00:00') -- The time that we were passed in may have been in the middle of the blocking incident. -- Find the true start time for this blocking chain. This might be 10 seconds prior, or -- might be days prior. For perf reasons, search backwards in time one hour at a time -- until we find the start of the blocking incident. DECLARE @blocking_end_snapshot_id int DECLARE @blocking_end_source_id int DECLARE @hour_count int SET @hour_count = -1 WHILE (@blocking_start_time IS NULL) BEGIN -- Only if we have more rows left to search for the blocking end time IF EXISTS ( SELECT * FROM snapshots.active_sessions_and_requests AS r INNER JOIN core.snapshots AS s ON s.snapshot_id = r.snapshot_id WHERE s.instance_name = @instance_name AND r.collection_time < DATEADD (hour, @hour_count+1, @blocking_time) ) BEGIN SELECT TOP 1 @blocking_start_time = r.collection_time FROM snapshots.active_sessions_and_requests AS r INNER JOIN core.snapshots AS s ON s.snapshot_id = r.snapshot_id WHERE s.instance_name = @instance_name AND r.collection_time BETWEEN DATEADD (hour, @hour_count, @blocking_time) AND @blocking_time AND NOT EXISTS ( SELECT * FROM snapshots.active_sessions_and_requests AS r2 WHERE r.snapshot_id = r2.snapshot_id AND r.collection_time = r2.collection_time AND r2.blocking_session_id = @head_blocker_session_id ) ORDER BY r.collection_time DESC END ELSE BEGIN -- We've reached the beginning of the data in the warehouse, and the blocking incident was already -- in-progress at that time. Use the earliest collection time as the approx blocking start time. SELECT @blocking_start_time = ISNULL (DATEADD (second, -10, MIN (r.collection_time)), GETUTCDATE()) FROM snapshots.active_sessions_and_requests AS r INNER JOIN core.snapshots AS s ON s.snapshot_id = r.snapshot_id WHERE s.instance_name = @instance_name END SET @hour_count = @hour_count - 1 END -- We've found the collection_time just before the blocking began. Get the next collection time, -- which is the first collection time where this blocking incident was detected. SELECT TOP 1 @blocking_start_time = r.collection_time FROM snapshots.active_sessions_and_requests AS r INNER JOIN core.snapshots AS s ON s.snapshot_id = r.snapshot_id WHERE s.instance_name = @instance_name AND r.collection_time > @blocking_start_time ORDER BY r.collection_time ASC -- Now find the end of the blocking incident. Here, again, do an optimistic search in 1-hour blocks. SET @hour_count = 1 WHILE (@blocking_end_time IS NULL) BEGIN -- Only if we have more rows left to search for the blocking end time IF EXISTS ( SELECT * FROM snapshots.active_sessions_and_requests AS r INNER JOIN core.snapshots AS s ON s.snapshot_id = r.snapshot_id WHERE s.instance_name = @instance_name AND r.collection_time > DATEADD (hour, @hour_count-1, @blocking_time) ) BEGIN SELECT TOP 1 @blocking_end_time = r.collection_time FROM snapshots.active_sessions_and_requests AS r INNER JOIN core.snapshots AS s ON s.snapshot_id = r.snapshot_id WHERE s.instance_name = @instance_name AND r.collection_time BETWEEN DATEADD (hour, @hour_count-1, @blocking_time) AND DATEADD (hour, @hour_count, @blocking_time) AND NOT EXISTS ( SELECT * FROM snapshots.active_sessions_and_requests AS r2 WHERE r.snapshot_id = r2.snapshot_id AND r.collection_time = r2.collection_time AND r2.blocking_session_id = @head_blocker_session_id ) ORDER BY r.collection_time ASC END ELSE BEGIN -- We've reached the end of the data in the warehouse, and the blocking incident is still -- in-progress. Use the last collection time as the approx blocking end time. SELECT @blocking_end_time = ISNULL (DATEADD (second, 10, MAX (r.collection_time)), GETUTCDATE()) FROM snapshots.active_sessions_and_requests AS r INNER JOIN core.snapshots AS s ON s.snapshot_id = r.snapshot_id WHERE s.instance_name = @instance_name END SET @hour_count = @hour_count + 1 END -- We've found the collection_time just after before the blocking ended. Get the prior collection time, -- which is the last collection time where the blocking incident was detected. SELECT TOP 1 @blocking_end_time = r.collection_time, @blocking_end_snapshot_id = r.snapshot_id, @blocking_end_source_id = s.source_id FROM snapshots.active_sessions_and_requests AS r INNER JOIN core.snapshots AS s ON s.snapshot_id = r.snapshot_id WHERE s.instance_name = @instance_name AND r.collection_time < @blocking_end_time ORDER BY r.collection_time DESC -- DC captures a snapshot of session state every few seconds, which would mean hundreds of samples for -- moderately long-lived blocking chains. It would be too expensive to summarize the state of the blocking -- chain at every one of these points. Instead, select 10 evenly-spaced intervals during the blocking -- incident to characterize the changes in the head blocker's state over the blocking period. DECLARE @interval_sec int SET @interval_sec = DATEDIFF (second, @blocking_start_time, @blocking_end_time) / 10 CREATE TABLE #sample_collection_times (snapshot_id int, source_id int, collection_time datetimeoffset(7)) DECLARE @i int SET @i = 0 WHILE (@i < 9) BEGIN INSERT INTO #sample_collection_times SELECT TOP 1 r.snapshot_id, s.source_id, r.collection_time FROM snapshots.active_sessions_and_requests AS r INNER JOIN core.snapshots AS s ON s.snapshot_id = r.snapshot_id WHERE s.instance_name = @instance_name AND r.collection_time BETWEEN DATEADD (second, @interval_sec * @i, @blocking_start_time) AND DATEADD (second, @interval_sec * (@i+1)-1, @blocking_start_time) -- Only choose collection times where we have info for the head blocker AND r.session_id = @head_blocker_session_id ORDER BY r.collection_time ASC SET @i = @i + 1 END -- The 10th sample time is always the blocking incident's final collection time INSERT INTO #sample_collection_times VALUES (@blocking_end_snapshot_id, @blocking_end_source_id, @blocking_end_time); -- Use a recursive CTE to walk the tree of the blocking chain at each of these collection times -- and get the state of all the sessions that were part of the tree WITH blocking_hierarchy AS ( -- Head blocker at each of the selected sample times SELECT t.collection_time, t.snapshot_id, t.source_id, 0 AS [level], r.session_id, r.request_id, r.exec_context_id, r.request_status, r.command, r.blocking_session_id, r.blocking_exec_context_id, r.wait_type, r.wait_duration_ms, r.wait_resource, r.resource_description, r.login_name, r.login_time, r.[program_name], r.[host_name], r.database_name, r.open_transaction_count, r.transaction_isolation_level, r.request_cpu_time, r.request_total_elapsed_time, r.request_start_time, r.memory_usage, r.session_cpu_time, r.session_total_scheduled_time, r.session_row_count, r.pending_io_count, r.prev_error, r.session_last_request_start_time, r.session_last_request_end_time, r.open_resultsets, r.plan_handle, r.sql_handle, r.statement_start_offset, r.statement_end_offset FROM #sample_collection_times AS t INNER JOIN snapshots.active_sessions_and_requests AS r ON t.snapshot_id = r.snapshot_id AND t.collection_time = r.collection_time WHERE r.session_id = @head_blocker_session_id AND ISNULL (r.exec_context_id, 0) IN (-1, 0) -- for the head blocker, only return the main worker's state UNION ALL -- Tasks blocked by the head blocker at the same times SELECT r2.collection_time, r2.snapshot_id, parent.source_id, parent.[level] + 1 AS [level], r2.session_id, r2.request_id, r2.exec_context_id, r2.request_status, r2.command, r2.blocking_session_id, r2.blocking_exec_context_id, r2.wait_type, r2.wait_duration_ms, r2.wait_resource, r2.resource_description, r2.login_name, r2.login_time, r2.[program_name], r2.[host_name], r2.database_name, r2.open_transaction_count, r2.transaction_isolation_level, r2.request_cpu_time, r2.request_total_elapsed_time, r2.request_start_time, r2.memory_usage, r2.session_cpu_time, r2.session_total_scheduled_time, r2.session_row_count, r2.pending_io_count, r2.prev_error, r2.session_last_request_start_time, r2.session_last_request_end_time, r2.open_resultsets, r2.plan_handle, r2.sql_handle, r2.statement_start_offset, r2.statement_end_offset FROM snapshots.active_sessions_and_requests AS r2 INNER JOIN blocking_hierarchy AS parent ON parent.snapshot_id = r2.snapshot_id AND parent.collection_time = r2.collection_time AND parent.session_id = r2.blocking_session_id ) SELECT * INTO #blocking_hierarchy FROM blocking_hierarchy -- Summarize the state of the head blocker at each of the sample times, along with -- some aggregate stats (# of blocked sessions, etc) describing the blocked sessions SELECT CONVERT (datetime, SWITCHOFFSET (CAST (@blocking_start_time AS datetimeoffset(7)), '+00:00')) AS blocking_start_time, CONVERT (datetime, SWITCHOFFSET (CAST (@blocking_end_time AS datetimeoffset(7)), '+00:00')) AS blocking_end_time, DATEDIFF (second, @blocking_start_time, @blocking_end_time) AS blocking_duration, -- Return these dates as strings to avoid RS date truncation CONVERT (varchar(40), SWITCHOFFSET (CAST (@blocking_start_time AS datetimeoffset(7)), '+00:00'), 126) AS blocking_start_time_str, CONVERT (varchar(40), SWITCHOFFSET (CAST (@blocking_end_time AS datetimeoffset(7)), '+00:00'), 126) AS blocking_end_time_str, CONVERT (datetime, SWITCHOFFSET (CAST (blocked.chart_collection_time AS datetimeoffset(7)), '+00:00')) AS chart_collection_time, blocked.chart_only, CONVERT (datetime, SWITCHOFFSET (CAST (blocked.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, blocked.snapshot_id, CONVERT (varchar(40), SWITCHOFFSET (CAST (blocked.collection_time AS datetimeoffset(7)), '+00:00'), 126) AS collection_time_str, @head_blocker_session_id AS head_blocker_session_id, COUNT(DISTINCT blocked.session_id) AS blocked_session_count, SUM (blocked.wait_duration_ms) AS total_wait_time, AVG (blocked.wait_duration_ms) AS avg_wait_time, ( -- Get the description of the resource owned by the head blocker that -- has caused the most wait time in this blocking chain SELECT TOP 1 resource_description FROM #blocking_hierarchy bh WHERE bh.collection_time = blocked.collection_time AND bh.snapshot_id = blocked.snapshot_id AND bh.blocking_session_id = @head_blocker_session_id GROUP BY resource_description ORDER BY SUM (wait_duration_ms) DESC ) AS primary_wait_resource_description, ISNULL (blocker.command, 'AWAITING COMMAND') AS command, blocker.request_status, wt.category_name, blocker.wait_type, blocker.wait_duration_ms, blocker.wait_resource, blocker.resource_description, blocker.[program_name], blocker.[host_name], blocker.login_name, CONVERT (datetime, SWITCHOFFSET (CAST (blocker.login_time AS datetimeoffset(7)), '+00:00')) AS login_time, blocker.database_name, MAX (blocker.open_transaction_count) AS open_transaction_count, MAX (blocker.transaction_isolation_level) AS transaction_isolation_level, MAX (blocker.request_cpu_time) AS request_cpu_time, MAX (blocker.request_total_elapsed_time) AS request_total_elapsed_time, MIN (blocker.request_start_time) AS request_start_time, MAX (blocker.memory_usage) AS memory_usage, MAX (blocker.session_cpu_time) AS session_cpu_time, MAX (blocker.session_total_scheduled_time) AS session_total_scheduled_time, MAX (blocker.session_row_count) AS session_row_count, MAX (blocker.pending_io_count) AS pending_io_count, MAX (blocker.prev_error) AS prev_error, CONVERT (datetime, SWITCHOFFSET (CAST (MAX (blocker.session_last_request_start_time) AS datetimeoffset(7)), '+00:00')) AS session_last_request_start_time, CONVERT (datetime, SWITCHOFFSET (CAST (MAX (blocker.session_last_request_end_time) AS datetimeoffset(7)), '+00:00')) AS session_last_request_end_time, MAX (blocker.open_resultsets) AS open_resultsets, blocker.plan_handle, blocker.sql_handle, blocker.statement_start_offset, blocker.statement_end_offset, -- RS can't handle binary values as parameters -- convert plan and sql handles to string types master.dbo.fn_varbintohexstr (blocker.plan_handle) AS plan_handle_str, master.dbo.fn_varbintohexstr (blocker.sql_handle) AS sql_handle_str, sql_text.[object_id], sql_text.[object_name], sql_text.query_text, REPLACE (REPLACE (REPLACE (REPLACE (REPLACE (REPLACE ( LEFT (LTRIM (sql_text.query_text), 100), CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' '), ' ', ' '), ' ', ' '), ' ', ' ') AS flat_query_text -- RS won't plot series that only have a single data point on a line graph. Duplicate each data -- point with a slight time shift, which will allow the chart to provide some visualization even if -- the blocking was transient (only seen during a single sample). For performance reasons, this -- same resultset feeds both a chart and a table. The duplicate records are flagged with -- chart_only=1 so that they can be filtered out in the table. FROM ( SELECT *, collection_time AS chart_collection_time, 0 AS chart_only FROM #blocking_hierarchy WHERE blocking_session_id != 0 UNION ALL SELECT *, DATEADD (second, 10, collection_time) AS chart_collection_time, 1 AS chart_only FROM #blocking_hierarchy WHERE blocking_session_id != 0 ) AS blocked INNER JOIN ( SELECT *, collection_time AS chart_collection_time, 0 AS chart_only FROM #blocking_hierarchy WHERE blocking_session_id = 0 UNION ALL SELECT *, DATEADD (second, 10, collection_time) AS chart_collection_time, 1 AS chart_only FROM #blocking_hierarchy WHERE blocking_session_id = 0 ) AS blocker ON blocked.collection_time = blocker.collection_time AND blocked.snapshot_id = blocker.snapshot_id AND blocked.chart_collection_time = blocker.chart_collection_time OUTER APPLY [snapshots].[fn_get_query_text] (blocker.source_id, blocker.sql_handle, blocker.statement_start_offset, blocker.statement_end_offset) AS sql_text LEFT OUTER JOIN core.wait_types_categorized AS wt ON blocker.wait_type = wt.wait_type WHERE blocker.blocking_session_id = 0 AND blocked.blocking_session_id != 0 GROUP BY blocked.chart_collection_time, blocked.chart_only, blocked.collection_time, blocked.snapshot_id, blocker.command, blocker.request_status, wt.category_name, blocker.wait_type, blocker.wait_duration_ms, blocker.wait_resource, blocker.resource_description, blocker.[program_name], blocker.[host_name], blocker.login_name, blocker.login_time, blocker.database_name, blocker.plan_handle, blocker.sql_handle, blocker.statement_start_offset, blocker.statement_end_offset, sql_text.[object_id], sql_text.[object_name], sql_text.query_text ORDER BY blocked.collection_time ASC END GO -- -- snapshots.rpt_active_sessions_and_requests -- Returns all the active sessions/requests at a particular moment in time -- Parameters: -- @instance_name - SQL Server instance name -- @collection_time - a time that corresponds to a specific [collection_time] in snapshots.active_sessions_and_requests (UTC) -- IF (NOT OBJECT_ID(N'snapshots.rpt_active_sessions_and_requests', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_active_sessions_and_requests] ...' DROP PROCEDURE [snapshots].[rpt_active_sessions_and_requests] END GO PRINT 'Creating procedure [snapshots].[rpt_active_sessions_and_requests] ...' GO CREATE PROCEDURE [snapshots].[rpt_active_sessions_and_requests] @instance_name sysname, @collection_time datetime AS BEGIN SET NOCOUNT ON; -- Find the nearest collection time on or before the user-specified time DECLARE @current_collection_time datetimeoffset(7) DECLARE @current_snapshot_id int DECLARE @query_stats_source_id int -- Compensate for RS truncation of fractional seconds SET @current_collection_time = DATEADD(second, 1, @collection_time) SELECT TOP 1 @current_collection_time = r.collection_time, @current_snapshot_id = r.snapshot_id FROM core.snapshots AS s INNER JOIN snapshots.active_sessions_and_requests AS r ON s.snapshot_id = r.snapshot_id WHERE s.instance_name = @instance_name AND r.collection_time <= @current_collection_time ORDER BY collection_time DESC -- Get the source_id for the Query Stats collection set on this server SELECT @query_stats_source_id = s.source_id FROM core.snapshots AS s WHERE s.instance_name = @instance_name AND s.collection_set_uid = '2DC02BD6-E230-4C05-8516-4E8C0EF21F95' -- Get all active sessions/requests at that time SELECT r.session_id, r.request_id, r.exec_context_id, ISNULL (r.blocking_session_id, 0) AS blocking_session_id, r.blocking_exec_context_id, r.scheduler_id, r.database_name, r.[user_id], r.task_state, r.request_status, r.session_status, r.executing_managed_code, CONVERT (datetime, SWITCHOFFSET (CAST (r.login_time AS datetimeoffset(7)), '+00:00')) AS login_time, r.is_user_process, r.[host_name], r.[program_name], r.login_name, r.wait_type, r.last_wait_type, r.wait_duration_ms, r.wait_resource, r.resource_description, r.transaction_id, r.open_transaction_count, r.transaction_isolation_level, r.request_cpu_time, r.request_logical_reads, r.request_reads, r.request_writes, r.request_total_elapsed_time, CONVERT (datetime, SWITCHOFFSET (CAST (r.request_start_time AS datetimeoffset(7)), '+00:00')) AS request_start_time, r.memory_usage, r.session_cpu_time, r.session_reads, r.session_writes, r.session_logical_reads, r.session_total_scheduled_time, r.session_total_elapsed_time, CONVERT (datetime, SWITCHOFFSET (CAST (r.session_last_request_start_time AS datetimeoffset(7)), '+00:00')) AS session_last_request_start_time, CONVERT (datetime, SWITCHOFFSET (CAST (r.session_last_request_end_time AS datetimeoffset(7)), '+00:00')) AS session_last_request_end_time, r.open_resultsets, r.session_row_count, r.prev_error, r.pending_io_count, ISNULL (r.command, 'AWAITING COMMAND') AS command, r.plan_handle, r.sql_handle, r.statement_start_offset, r.statement_end_offset, CONVERT (datetime, SWITCHOFFSET (CAST (r.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, r.snapshot_id, wt.category_name AS wait_category, sql.*, master.dbo.fn_varbintohexstr (r.sql_handle) AS sql_handle_str, master.dbo.fn_varbintohexstr (r.plan_handle) AS plan_handle_str, REPLACE (REPLACE (REPLACE (REPLACE (REPLACE (REPLACE ( LEFT (LTRIM (sql.query_text), 100) , CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' '), ' ', ' '), ' ', ' '), ' ', ' ') AS flat_query_text FROM snapshots.active_sessions_and_requests AS r LEFT OUTER JOIN core.wait_types_categorized AS wt ON r.wait_type = wt.wait_type OUTER APPLY snapshots.fn_get_query_text (@query_stats_source_id, r.sql_handle, r.statement_start_offset, r.statement_end_offset) AS sql WHERE r.snapshot_id = @current_snapshot_id AND r.collection_time = @current_collection_time END GO -- -- snapshots.rpt_sql_memory_clerks -- Returns data from os_memory_clerks table -- Parameters: -- @ServerName - SQL Server instance name -- @EndTime - End of the user-selected time window (UTC) -- @WindowSize - Number of minutes in the time window -- IF (NOT OBJECT_ID(N'snapshots.rpt_sql_memory_clerks', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_sql_memory_clerks] ...' DROP PROCEDURE [snapshots].[rpt_sql_memory_clerks] END GO PRINT 'Creating procedure [snapshots].[rpt_sql_memory_clerks] ...' GO CREATE PROCEDURE [snapshots].[rpt_sql_memory_clerks] @ServerName sysname, @EndTime datetime = NULL, @WindowSize smallint = NULL AS BEGIN SET NOCOUNT ON; -- Divide our time window up into 40 evenly-sized time intervals, and find the last collection_time within each of these intervals CREATE TABLE #intervals ( interval_time_id int, interval_start_time datetimeoffset(7), interval_end_time datetimeoffset(7), interval_id int, first_collection_time datetimeoffset(7), last_collection_time datetimeoffset(7), first_snapshot_id int, last_snapshot_id int, source_id int, snapshot_id int, collection_time datetimeoffset(7), collection_time_id int ) -- GUID 49268954-... is the Server Activity CS INSERT INTO #intervals EXEC [snapshots].[rpt_interval_collection_times] @ServerName, @EndTime, @WindowSize, 'snapshots.os_memory_clerks', '49268954-4FD4-4EB6-AA04-CD59D9BB5714', 40, 0 -- Get memory clerk stats for these collection times SELECT coll.interval_time_id, -- Convert datetimeoffsets to UTC datetimes CONVERT (datetime, SWITCHOFFSET (coll.interval_start_time, '+00:00')) AS interval_start_time, CONVERT (datetime, SWITCHOFFSET (coll.interval_end_time, '+00:00')) AS interval_end_time, coll.interval_id, CONVERT (datetime, SWITCHOFFSET (coll.first_collection_time, '+00:00')) AS first_collection_time, CONVERT (datetime, SWITCHOFFSET (coll.last_collection_time, '+00:00')) AS last_collection_time, coll.first_snapshot_id, coll.last_snapshot_id, mc.[type], mc.memory_node_id, mc.single_pages_kb, mc.multi_pages_kb, mc.virtual_memory_reserved_kb, mc.virtual_memory_committed_kb, mc.awe_allocated_kb, mc.shared_memory_reserved_kb, mc.shared_memory_committed_kb, CONVERT (datetime, SWITCHOFFSET (mc.collection_time, '+00:00')) AS collection_time, CAST (mc.single_pages_kb AS bigint) + mc.multi_pages_kb + (CASE WHEN type <> 'MEMORYCLERK_SQLBUFFERPOOL' THEN mc.virtual_memory_committed_kb ELSE 0 END) + mc.shared_memory_committed_kb AS total_kb INTO #memory_clerks FROM snapshots.os_memory_clerks AS mc INNER JOIN #intervals AS coll ON coll.last_snapshot_id = mc.snapshot_id AND coll.last_collection_time = mc.collection_time -- Return memory stats to the caller SELECT mc.*, mc.single_pages_kb + mc.multi_pages_kb as allocated_kb, ta.total_kb_all_clerks, mc.total_kb / CONVERT(decimal, ta.total_kb_all_clerks) AS percent_total_kb, -- There are many memory clerks. We'll chart any that make up 5% of SQL memory or more; less significant clerks will be lumped into an "Other" bucket CASE WHEN mc.total_kb / CONVERT(decimal, ta.total_kb_all_clerks) > 0.05 THEN mc.[type] ELSE N'Other' END AS graph_type FROM #memory_clerks AS mc -- Use a self-join to calculate the total memory allocated for each time interval JOIN ( SELECT mc_ta.collection_time, SUM (mc_ta.total_kb) AS total_kb_all_clerks FROM #memory_clerks AS mc_ta GROUP BY mc_ta.collection_time ) AS ta ON (mc.collection_time = ta.collection_time) ORDER BY collection_time END; GO -- -- snapshots.rpt_sql_process_and_system_memory -- Returns system and SQL process memory details over a time interval -- Parameters: -- @ServerName - SQL Server instance name -- @EndTime - End of the user-selected time window (UTC) -- @WindowSize - Number of minutes in the time window -- IF (NOT OBJECT_ID(N'snapshots.rpt_sql_process_and_system_memory', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_sql_process_and_system_memory] ...' DROP PROCEDURE [snapshots].[rpt_sql_process_and_system_memory] END GO PRINT 'Creating procedure [snapshots].[rpt_sql_process_and_system_memory] ...' GO CREATE PROCEDURE [snapshots].[rpt_sql_process_and_system_memory] @ServerName sysname, @EndTime datetime = NULL, @WindowSize int AS BEGIN SET NOCOUNT ON; -- Divide our time window up into 40 evenly-sized time intervals, and find the last collection_time within each of these intervals CREATE TABLE #intervals ( interval_time_id int, interval_start_time datetimeoffset(7), interval_end_time datetimeoffset(7), interval_id int, first_collection_time datetimeoffset(7), last_collection_time datetimeoffset(7), first_snapshot_id int, last_snapshot_id int, source_id int, snapshot_id int, collection_time datetimeoffset(7), collection_time_id int ) -- GUID 49268954-... is Server Activity INSERT INTO #intervals EXEC [snapshots].[rpt_interval_collection_times] @ServerName, @EndTime, @WindowSize, 'snapshots.sql_process_and_system_memory', '49268954-4FD4-4EB6-AA04-CD59D9BB5714', 40, 0 -- Get the earliest and latest snapshot_id values that contain data for the selected time interval. -- This will allow a more efficient query plan. DECLARE @start_snapshot_id int; DECLARE @end_snapshot_id int; SELECT @start_snapshot_id = MIN (first_snapshot_id) FROM #intervals SELECT @end_snapshot_id = MAX (last_snapshot_id) FROM #intervals -- Get sys.dm_os_process_memory for these intervals SELECT coll.interval_time_id, coll.interval_id, CONVERT (datetime, SWITCHOFFSET (CAST (coll.last_collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, CONVERT (datetime, SWITCHOFFSET (CAST (coll.interval_start_time AS datetimeoffset(7)), '+00:00')) AS interval_start_time, CONVERT (datetime, SWITCHOFFSET (CAST (coll.interval_end_time AS datetimeoffset(7)), '+00:00')) AS interval_end_time, coll.last_snapshot_id, AVG (sql_physical_memory_in_use_kb) AS avg_sql_physical_memory_in_use_kb, MAX (sql_physical_memory_in_use_kb) AS max_sql_physical_memory_in_use_kb, MIN (sql_physical_memory_in_use_kb) AS min_sql_physical_memory_in_use_kb, AVG (sql_total_virtual_address_space_kb) AS avg_sql_total_virtual_address_space_kb, AVG (sql_virtual_address_space_reserved_kb) AS avg_sql_virtual_address_space_reserved_kb, AVG (sql_virtual_address_space_committed_kb) AS avg_sql_virtual_address_space_committed_kb, AVG (sql_virtual_address_space_available_kb) AS avg_sql_virtual_address_space_available_kb, MAX (sql_virtual_address_space_available_kb) AS max_sql_virtual_address_space_available_kb, MIN (sql_virtual_address_space_available_kb) AS min_sql_virtual_address_space_available_kb, AVG (sql_memory_utilization_percentage) AS avg_sql_memory_utilization_percentage, MIN (sql_memory_utilization_percentage) AS min_sql_memory_utilization_percentage, AVG (sql_available_commit_limit_kb) AS avg_sql_available_commit_limit_kb, MIN (sql_available_commit_limit_kb) AS min_sql_available_commit_limit_kb, AVG (sql_large_page_allocations_kb) AS avg_sql_large_page_allocations_kb, AVG (sql_locked_page_allocations_kb) AS avg_sql_locked_page_allocations_kb, SUM (CAST (sql_process_physical_memory_low AS int)) AS sql_process_physical_memory_low_count, SUM (CAST (sql_process_virtual_memory_low AS int)) AS sql_process_virtual_memory_low_count, MAX (sql_page_fault_count) - MIN (sql_page_fault_count) AS interval_sql_page_fault_count, AVG (system_total_physical_memory_kb) AS system_total_physical_memory_kb, AVG (system_available_physical_memory_kb) AS avg_system_available_physical_memory_kb, MAX (system_available_physical_memory_kb) AS max_system_available_physical_memory_kb, MIN (system_available_physical_memory_kb) AS min_system_available_physical_memory_kb, AVG (system_total_page_file_kb) AS avg_system_total_page_file_kb, AVG (system_available_page_file_kb) AS avg_system_available_page_file_kb, MIN (system_available_page_file_kb) AS min_system_available_page_file_kb, AVG (system_cache_kb) AS avg_system_cache_kb, AVG (system_kernel_paged_pool_kb) AS avg_system_kernel_paged_pool_kb, AVG (system_kernel_nonpaged_pool_kb) AS avg_system_kernel_nonpaged_pool_kb, SUM (CAST (system_high_memory_signal_state AS int)) AS system_high_memory_signal_state_count, SUM (CAST (system_low_memory_signal_state AS int)) AS system_low_memory_signal_state_count, AVG (bpool_commit_target) AS avg_bpool_commit_target, MAX (bpool_commit_target) AS max_bpool_commit_target, MIN (bpool_commit_target) AS min_bpool_commit_target, AVG (bpool_committed) AS avg_bpool_committed, MAX (bpool_committed) AS max_bpool_committed, MIN (bpool_committed) AS min_bpool_committed, AVG (bpool_visible) AS avg_bpool_visible, MAX (bpool_visible) AS max_bpool_visible, MIN (bpool_visible) AS min_bpool_visible FROM snapshots.sql_process_and_system_memory AS pm INNER JOIN #intervals AS coll ON coll.last_snapshot_id = pm.snapshot_id AND coll.last_collection_time = pm.collection_time GROUP BY coll.interval_start_time, coll.interval_end_time, coll.interval_time_id, coll.last_collection_time, coll.interval_id, coll.last_snapshot_id ORDER BY coll.last_collection_time ASC; END GO -- -- snapshots.rpt_sampled_waits -- Returns a list of the apps and queries that spent the most time waiting for the specified wait category. -- Note that this is based off data collected from regular samples of sys.dm_exec_requests, -- sys.dm_os_waiting_tasks, and related DMVs. Because these are just samples, it is not expected that -- all waits will be captured; at best, a representative sampling will be returned. -- -- Parameters: -- @ServerName - SQL Server instance name -- @EndTime - end of the time window (UTC) -- @WindowSize - number of minutes in the time window -- @CategoryName - Optional filter criteria: Name of wait category -- @WaitType - Optional filter criteria: Name of wait type -- @ProgramName - Optional filter criteria: Application name -- @SqlHandleStr - Optional filter criteria: Handle to a particular query -- @StatementStartOffset - Start offset for a particular statement within the batch/proc specified by @SqlHandleStr -- @StatementEndOffset - End offset for a particular statement within the batch/proc specified by @SqlHandleStr -- @SessionID - Optional filter criteria: Specific SPID -- @Database - Optional filter criteria: Waits within a particular database -- IF (NOT OBJECT_ID(N'snapshots.rpt_sampled_waits', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_sampled_waits] ...' DROP PROCEDURE [snapshots].[rpt_sampled_waits] END GO PRINT 'Creating procedure [snapshots].[rpt_sampled_waits] ...' GO CREATE PROCEDURE [snapshots].[rpt_sampled_waits] @ServerName sysname, @EndTime datetime, @WindowSize int, @CategoryName nvarchar(20) = NULL, @WaitType nvarchar(45) = NULL, @ProgramName nvarchar(50) = NULL, @SqlHandleStr varchar(130) = NULL, @StatementStartOffset int = NULL, @StatementEndOffset int = NULL, @SessionID int = NULL, @Database nvarchar(255) = NULL AS BEGIN SET NOCOUNT ON; DECLARE @start_time_internal datetimeoffset(7); DECLARE @end_time_internal datetimeoffset(7); -- Clean string params (on drillthrough, RS may pass in an empty string instead of NULL) IF @CategoryName = '' SET @CategoryName = NULL IF @WaitType = '' SET @WaitType = NULL IF @ProgramName = '' SET @ProgramName = NULL IF @SqlHandleStr = '' SET @SqlHandleStr = NULL IF @Database = '' SET @Database = NULL -- -1 is a potentially valid offset, but anything less is invalid. RS can't represent NULL in some places, so -- translate int values that are out of range to NULL. IF @StatementStartOffset < -1 SET @StatementStartOffset = NULL IF @StatementEndOffset < -1 SET @StatementStartOffset = NULL IF @SessionID < -1 SET @SessionID = NULL -- NOTE: The logic below is duplicated in snapshots.rpt_sampled_waits_longest. It cannot be moved to a shared -- child proc because of SQL restrictions (no nested INSERT EXECs, and table params are read only). -- Also update snapshots.rpt_sampled_waits_longest if a change to this section is required. /*** BEGIN DUPLICATED CODE SECTION ***/ -- Start time should be passed in as a UTC datetime IF (@EndTime IS NOT NULL) BEGIN -- Assumed time zone offset for this conversion is +00:00 (datetime must be passed in as UTC) SET @end_time_internal = CAST (@EndTime AS datetimeoffset(7)); END ELSE BEGIN SELECT @end_time_internal = MAX(ar.collection_time) FROM core.snapshots AS s INNER JOIN snapshots.active_sessions_and_requests AS ar ON s.snapshot_id = ar.snapshot_id WHERE s.instance_name = @ServerName -- AND collection_set_uid = '49268954-4FD4-4EB6-AA04-CD59D9BB5714' -- Server Activity CS END SET @start_time_internal = DATEADD (minute, -1 * @WindowSize, @end_time_internal); DECLARE @sql_handle varbinary(64) IF LEN (@SqlHandleStr) > 0 BEGIN SET @sql_handle = snapshots.fn_hexstrtovarbin (@SqlHandleStr) END -- Divide our time window up into 40 evenly-sized time intervals, and find the first and last collection_time within each of these intervals CREATE TABLE #intervals ( interval_time_id int, interval_start_time datetimeoffset(7), interval_end_time datetimeoffset(7), interval_id int, first_collection_time datetimeoffset(7), last_collection_time datetimeoffset(7), first_snapshot_id int, last_snapshot_id int, source_id int, snapshot_id int, collection_time datetimeoffset(7), collection_time_id int ) INSERT INTO #intervals EXEC [snapshots].[rpt_interval_collection_times] @ServerName, @EndTime, @WindowSize, 'snapshots.active_sessions_and_requests', '2dc02bd6-e230-4c05-8516-4e8c0ef21f95', 40, 1 SELECT ti.interval_id, ti.interval_time_id, ti.interval_start_time, ti.interval_end_time, ti.collection_time, ti.collection_time_id, r.row_id, r.snapshot_id, r.session_id, r.request_id, r.exec_context_id, r.wait_duration_ms, r.wait_resource, r.login_time, r.program_name, r.sql_handle, r.statement_start_offset, r.statement_end_offset, r.plan_handle, r.database_name, r.task_state, ti.source_id, wt.ignore, -- Model CPU as a "wait type" in the sampling results. Any active request without a wait type is assumed to be CPU-bound. CASE -- same expression here as in the GROUP BY WHEN r.wait_type = '' AND r.task_state IS NOT NULL THEN 'CPU' ELSE wt.category_name END AS category_name, -- "Running" tasks are actively using the CPU. A "runnable" task is able to run, but is momentarily waiting for the active -- task to yield so the next runnable task can get scheduled. Map runnable to the SOS_SCHEDULER_YIELD wait type. CASE WHEN r.wait_type = '' AND r.task_state = 'RUNNING' THEN 'CPU (Consumed)' WHEN r.wait_type = '' AND r.task_state != 'RUNNING' THEN 'SOS_SCHEDULER_YIELD' ELSE r.wait_type END AS wait_type INTO #waiting_tasks FROM snapshots.active_sessions_and_requests AS r LEFT OUTER JOIN core.wait_types_categorized AS wt ON wt.wait_type = r.wait_type INNER JOIN #intervals AS ti ON r.collection_time = ti.collection_time AND r.snapshot_id = ti.snapshot_id WHERE r.collection_time BETWEEN @start_time_internal AND @end_time_internal AND r.command != 'AWAITING COMMAND' AND r.request_id != -1 -- exclude idle spids (e.g. head blockers) AND (r.program_name = @ProgramName OR @ProgramName IS NULL) AND (r.sql_handle = @sql_handle OR @SqlHandleStr IS NULL) AND (r.database_name = @Database OR @Database IS NULL) AND (r.statement_start_offset = @StatementStartOffset OR @SqlHandleStr IS NULL) AND (r.statement_end_offset = @StatementEndOffset OR @SqlHandleStr IS NULL) AND (r.session_id = @SessionID OR @SessionID IS NULL) AND ( -- ... and wait category either matches the user-specified parameter ... (@CategoryName = CASE -- same expression here as in the select column list WHEN r.wait_type = '' AND r.task_state IS NOT NULL THEN 'CPU' ELSE wt.category_name END ) -- ... or a filter parameter for wait category was not provided (return all categories that aren't marked as ignorable). OR (@CategoryName IS NULL AND ISNULL (wt.ignore, 0) = 0) ) AND ( -- ... and wait type either matches the user-specified parameter ... (@WaitType = CASE WHEN r.wait_type = '' AND r.task_state = 'RUNNING' THEN 'CPU (Consumed)' WHEN r.wait_type = '' AND r.task_state != 'RUNNING' THEN 'SOS_SCHEDULER_YIELD' ELSE r.wait_type END ) -- ... or a filter parameter for wait category was not provided OR @WaitType IS NULL ) -- Force a recompile of this statement to take into account the actual values of @start_time_internal and -- @end_time_internal, which were not available at the time of proc compilation. OPTION (RECOMPILE); /*** END DUPLICATED CODE SECTION ***/ -- Get query text (do this here instead of in the following query so that we don't waste time retrieving the -- same query's text more than once). SELECT r.sql_handle, r.statement_start_offset, r.statement_end_offset, REPLACE (REPLACE (REPLACE (REPLACE (REPLACE (REPLACE ( LEFT (LTRIM (qt.query_text), 100) , CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' '), ' ', ' '), ' ', ' '), ' ', ' ') AS flat_query_text INTO #queries FROM ( SELECT DISTINCT source_id, sql_handle, statement_start_offset, statement_end_offset FROM #waiting_tasks WHERE category_name IS NOT NULL ) AS r OUTER APPLY snapshots.fn_get_query_text(r.source_id, r.sql_handle, r.statement_start_offset, r.statement_end_offset) AS qt -- Within each time interval, group the wait counts by waittype, app, db, and query. SELECT CONVERT (datetime, SWITCHOFFSET (r.interval_start_time, '+00:00')) AS interval_start_time, CONVERT (datetime, SWITCHOFFSET (r.interval_end_time, '+00:00')) AS interval_end_time, r.interval_id, r.interval_time_id, r.source_id, r.category_name, r.wait_type, r.[program_name], r.database_name, COUNT (*) AS wait_count, r.sql_handle, r.statement_start_offset, r.statement_end_offset, master.dbo.fn_varbintohexstr (r.sql_handle) AS sql_handle_str, q.flat_query_text FROM #waiting_tasks AS r LEFT OUTER JOIN #queries AS q ON r.sql_handle = q.sql_handle AND r.statement_start_offset = q.statement_start_offset AND r.statement_end_offset = q.statement_end_offset WHERE r.category_name IS NOT NULL GROUP BY r.interval_start_time, r.interval_end_time, r.interval_id, r.interval_time_id, r.category_name, r.wait_type, r.[program_name], r.database_name, r.[sql_handle], r.source_id, r.statement_start_offset, r.statement_end_offset, q.flat_query_text ORDER BY r.category_name, r.interval_id, COUNT(*) DESC END GO -- -- snapshots.rpt_sampled_waits_longest -- Returns list of the N longest waits that meet user-specified filter criteria. -- Note that this is based off data collected from regular samples of sys.dm_exec_requests, -- sys.dm_os_waiting_tasks, and related DMVs. Because these are just samples, it is not expected that -- all waits will be captured; at best, a representative sampling will be returned. -- -- Parameters: -- @ServerName - SQL Server instance name -- @EndTime - end of the time window (UTC) -- @WindowSize - number of minutes in the time window -- @CategoryName - Optional filter criteria: Name of wait category -- @WaitType - Optional filter criteria: Name of wait type -- @ProgramName - Optional filter criteria: Application name -- @SqlHandleStr - Optional filter criteria: Name of the query to filter on -- @StatementStartOffset - Start offset for a particular statement within the batch/proc specified by @SqlHandleStr -- @StatementEndOffset - End offset for a particular statement within the batch/proc specified by @SqlHandleStr -- @SessionID - Optional filter criteria: Specific SPID -- @Database - Optional filter criteria: Waits within a particular database -- IF (NOT OBJECT_ID(N'snapshots.rpt_sampled_waits_longest', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_sampled_waits_longest] ...' DROP PROCEDURE [snapshots].[rpt_sampled_waits_longest] END GO PRINT 'Creating procedure [snapshots].[rpt_sampled_waits_longest] ...' GO CREATE PROCEDURE [snapshots].[rpt_sampled_waits_longest] @ServerName sysname, @EndTime datetime, @WindowSize int, @CategoryName nvarchar(20) = NULL, @WaitType nvarchar(45) = NULL, @ProgramName nvarchar(50) = NULL, @SqlHandleStr varchar(130) = NULL, @StatementStartOffset int = NULL, @StatementEndOffset int = NULL, @SessionID int = NULL, @Database nvarchar(255) = NULL AS BEGIN SET NOCOUNT ON; DECLARE @start_time_internal datetimeoffset(7); DECLARE @end_time_internal datetimeoffset(7); -- Clean string params (on drillthrough, RS may pass in an empty string instead of NULL) IF @CategoryName = '' SET @CategoryName = NULL IF @WaitType = '' SET @WaitType = NULL IF @ProgramName = '' SET @ProgramName = NULL IF @SqlHandleStr = '' SET @SqlHandleStr = NULL IF @Database = '' SET @Database = NULL -- -1 is a potentially valid offset, but anything less is invalid. RS can't represent NULL in some places, so -- translate int values that are out of range to NULL. IF @StatementStartOffset < -1 SET @StatementStartOffset = NULL IF @StatementEndOffset < -1 SET @StatementStartOffset = NULL IF @SessionID < -1 SET @SessionID = NULL -- NOTE: The logic below is duplicated in snapshots.rpt_sampled_waits. It cannot be moved to a shared -- child proc because of SQL restrictions (no nested INSERT EXECs, and table params are read only). -- Also update snapshots.rpt_sampled_waits if a change to this section is required. /*** BEGIN DUPLICATED CODE SECTION ***/ -- Start time should be passed in as a UTC datetime IF (@EndTime IS NOT NULL) BEGIN -- Assumed time zone offset for this conversion is +00:00 (datetime must be passed in as UTC) SET @end_time_internal = CAST (@EndTime AS datetimeoffset(7)); END ELSE BEGIN SELECT @end_time_internal = MAX(ar.collection_time) FROM core.snapshots AS s INNER JOIN snapshots.active_sessions_and_requests AS ar ON s.snapshot_id = ar.snapshot_id WHERE s.instance_name = @ServerName -- AND collection_set_uid = '49268954-4FD4-4EB6-AA04-CD59D9BB5714' -- Server Activity CS END SET @start_time_internal = DATEADD (minute, -1 * @WindowSize, @end_time_internal); DECLARE @sql_handle varbinary(64) IF LEN (@SqlHandleStr) > 0 BEGIN SET @sql_handle = snapshots.fn_hexstrtovarbin (@SqlHandleStr) END -- Divide our time window up into 40 evenly-sized time intervals, and find the first and last collection_time within each of these intervals CREATE TABLE #intervals ( interval_time_id int, interval_start_time datetimeoffset(7), interval_end_time datetimeoffset(7), interval_id int, first_collection_time datetimeoffset(7), last_collection_time datetimeoffset(7), first_snapshot_id int, last_snapshot_id int, source_id int, snapshot_id int, collection_time datetimeoffset(7), collection_time_id int ) INSERT INTO #intervals EXEC [snapshots].[rpt_interval_collection_times] @ServerName, @EndTime, @WindowSize, 'snapshots.active_sessions_and_requests', '2dc02bd6-e230-4c05-8516-4e8c0ef21f95', 40, 1 SELECT ti.interval_id, ti.interval_time_id, ti.interval_start_time, ti.interval_end_time, ti.collection_time, ti.collection_time_id, r.row_id, r.snapshot_id, r.session_id, r.request_id, r.exec_context_id, r.wait_duration_ms, r.wait_resource, r.login_time, r.program_name, r.sql_handle, r.statement_start_offset, r.statement_end_offset, r.plan_handle, r.database_name, r.task_state, ti.source_id, wt.ignore, -- Model CPU as a "wait type" in the sampling results. Any active request without a wait type is assumed to be CPU-bound. CASE -- same expression here as in the GROUP BY WHEN r.wait_type = '' AND r.task_state IS NOT NULL THEN 'CPU' ELSE wt.category_name END AS category_name, -- "Running" tasks are actively using the CPU. A "runnable" task is able to run, but is momentarily waiting for the active -- task to yield so the next runnable task can get scheduled. Map runnable to the SOS_SCHEDULER_YIELD wait type. CASE WHEN r.wait_type = '' AND r.task_state = 'RUNNING' THEN 'CPU (Consumed)' WHEN r.wait_type = '' AND r.task_state != 'RUNNING' THEN 'SOS_SCHEDULER_YIELD' ELSE r.wait_type END AS wait_type INTO #waiting_tasks FROM snapshots.active_sessions_and_requests AS r LEFT OUTER JOIN core.wait_types_categorized AS wt ON wt.wait_type = r.wait_type INNER JOIN #intervals AS ti ON r.collection_time = ti.collection_time AND r.snapshot_id = ti.snapshot_id WHERE r.collection_time BETWEEN @start_time_internal AND @end_time_internal AND r.command != 'AWAITING COMMAND' AND r.request_id != -1 -- exclude idle spids (e.g. head blockers) AND (r.program_name = @ProgramName OR @ProgramName IS NULL) AND (r.sql_handle = @sql_handle OR @SqlHandleStr IS NULL) AND (r.database_name = @Database OR @Database IS NULL) AND (r.statement_start_offset = @StatementStartOffset OR @SqlHandleStr IS NULL) AND (r.statement_end_offset = @StatementEndOffset OR @SqlHandleStr IS NULL) AND (r.session_id = @SessionID OR @SessionID IS NULL) AND ( -- ... and wait category either matches the user-specified parameter ... (@CategoryName = CASE -- same expression here as in the select column list WHEN r.wait_type = '' AND r.task_state IS NOT NULL THEN 'CPU' ELSE wt.category_name END ) -- ... or a filter parameter for wait category was not provided (return all categories that aren't marked as ignorable). OR (@CategoryName IS NULL AND ISNULL (wt.ignore, 0) = 0) ) AND ( -- ... and wait type either matches the user-specified parameter ... (@WaitType = CASE WHEN r.wait_type = '' AND r.task_state = 'RUNNING' THEN 'CPU (Consumed)' WHEN r.wait_type = '' AND r.task_state != 'RUNNING' THEN 'SOS_SCHEDULER_YIELD' ELSE r.wait_type END ) -- ... or a filter parameter for wait category was not provided OR @WaitType IS NULL ) -- Force a recompile of this statement to take into account the actual values of @start_time_internal and -- @end_time_internal, which were not available at the time of proc compilation. OPTION (RECOMPILE); /*** END DUPLICATED CODE SECTION ***/ CREATE INDEX idx1 ON #waiting_tasks (collection_time_id, session_id) -- The same long-lived wait may be captured multiple times. For a given wait, we only care about the max -- wait time within the target time window. If we see that the immediately following sample shows the same -- spid waiting with a wait time that spans both samples, we discard the preceding sample and keep only -- the max wait time. For example, in the data below, spid 54 was stuck in the same long-lived lock wait -- from row 1 through row 3. From this data, we would only keep rows #3 and #4. Rows #1 and #2 are -- discarded to avoid reporting the same long wait several times. -- -- (row#) collection_time session_id wait_type wait_duration_ms -- ------------------- ----------- ------------ ----------------- -- 1 2007-10-25 13:01:00 53 LCK_M_S 8141 -- 2 2007-10-25 13:01:10 53 LCK_M_S 18278 -- 3 2007-10-25 13:01:20 53 LCK_M_S 28318 -- 4 2007-10-25 13:01:30 54 LCK_M_X 755 -- -- Also, if there was one collection time where everyone was blocked momentarily, we want to avoid -- reporting that collection time over and over w/only the spid # varying; that's not the most interesting -- sampling. Instead, report the longest wait in each of the top 10 collection times (top 10 times by max -- wait duration at the collection time). SELECT TOP 10 CONVERT (datetime, SWITCHOFFSET (r1.collection_time, '+00:00')) AS collection_time, CONVERT (varchar(30), CONVERT (datetime, SWITCHOFFSET (r1.collection_time, '+00:00')), 126) AS collection_time_str, r1.snapshot_id, r1.row_id, r1.session_id, r1.request_id, r1.exec_context_id, r1.wait_resource, r1.source_id, r1.category_name, r1.wait_type, r1.wait_duration_ms, r1.database_name, r1.[program_name], r1.[sql_handle], r1.statement_start_offset, r1.statement_end_offset, r1.plan_handle FROM #waiting_tasks AS r1 -- Find the same spid in the next collection time LEFT OUTER JOIN #waiting_tasks AS r2 ON r2.collection_time_id = r1.collection_time_id + 1 AND r2.session_id = r1.session_id AND r2.request_id = r1.request_id AND r2.exec_context_id = r1.exec_context_id AND r2.login_time = r1.login_time WHERE -- Prevent reporting the same wait spanning multiple collection times. ( -- ... where the spid wasn't seen waiting at the next collection time r2.session_id IS NULL -- ... or the wait at the next collection time is shorter than it would have been if the wait had spanned both collection times OR r2.wait_duration_ms < (DATEDIFF (second, r1.collection_time, r2.collection_time) * 1000) ) -- Exclude all but the longest wait in any given collection time AND NOT EXISTS ( SELECT * FROM #waiting_tasks AS r3 WHERE r3.collection_time_id = r1.collection_time_id AND r3.wait_duration_ms > r1.wait_duration_ms OR (r3.wait_duration_ms = r1.wait_duration_ms AND r3.row_id > r1.row_id) ) ORDER BY r1.wait_duration_ms DESC END GO -- -- snapshots.rpt_sampled_waits_hottest_resources -- Returns list of the N longest waits that meet user-specified filter criteria. -- Note that this is based off data collected from regular samples of sys.dm_exec_requests, -- sys.dm_os_waiting_tasks, and related DMVs. Because these are just samples, it is not expected that -- all waits will be captured; at best, a representative sampling will be returned. -- -- Parameters: -- @ServerName - SQL Server instance name -- @EndTime - end of the time window (UTC) -- @WindowSize - number of minutes in the time window -- @CategoryName - Optional filter criteria: Name of wait category -- @WaitType - Optional filter criteria: Name of wait type -- @ProgramName - Optional filter criteria: Application name -- @SqlHandleStr - Optional filter criteria: Name of the query to filter on -- @StatementStartOffset - Start offset for a particular statement within the batch/proc specified by @SqlHandleStr -- @StatementEndOffset - End offset for a particular statement within the batch/proc specified by @SqlHandleStr -- @SessionID - Option filter criteria: Specific SPID -- @Database - Optional filter criteria: Waits within a particular database -- IF (NOT OBJECT_ID(N'snapshots.rpt_sampled_waits_hottest_resources', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_sampled_waits_hottest_resources] ...' DROP PROCEDURE [snapshots].[rpt_sampled_waits_hottest_resources] END GO PRINT 'Creating procedure [snapshots].[rpt_sampled_waits_hottest_resources] ...' GO CREATE PROCEDURE [snapshots].[rpt_sampled_waits_hottest_resources] @ServerName sysname, @EndTime datetime, @WindowSize int, @CategoryName nvarchar(20) = NULL, @WaitType nvarchar(45) = NULL, @ProgramName nvarchar(50) = NULL, @SqlHandleStr varchar(130) = NULL, @StatementStartOffset int = NULL, @StatementEndOffset int = NULL, @SessionID int = NULL, @Database nvarchar(255) = NULL AS BEGIN SET NOCOUNT ON; DECLARE @start_time_internal datetimeoffset(7); DECLARE @end_time_internal datetimeoffset(7); -- Clean string params (on drillthrough, RS may pass in an empty string instead of NULL) IF @CategoryName = '' SET @CategoryName = NULL IF @WaitType = '' SET @WaitType = NULL IF @ProgramName = '' SET @ProgramName = NULL IF @SqlHandleStr = '' SET @SqlHandleStr = NULL IF @Database = '' SET @Database = NULL -- -1 is a potentially valid offset, but anything less is invalid. RS can't represent NULL in some places, so -- translate int values that are out of range to NULL. IF @StatementStartOffset < -1 SET @StatementStartOffset = NULL IF @StatementEndOffset < -1 SET @StatementStartOffset = NULL IF @SessionID < -1 SET @SessionID = NULL -- Start time should be passed in as a UTC datetime IF (@EndTime IS NOT NULL) BEGIN -- Assumed time zone offset for this conversion is +00:00 (datetime must be passed in as UTC) SET @end_time_internal = CAST (@EndTime AS datetimeoffset(7)); END ELSE BEGIN SELECT @end_time_internal = MAX(ar.collection_time) FROM core.snapshots AS s INNER JOIN snapshots.active_sessions_and_requests AS ar ON s.snapshot_id = ar.snapshot_id WHERE s.instance_name = @ServerName -- AND collection_set_uid = '49268954-4FD4-4EB6-AA04-CD59D9BB5714' -- Server Activity CS END SET @start_time_internal = DATEADD (minute, -1 * @WindowSize, @end_time_internal); DECLARE @sql_handle varbinary(64) IF LEN (@SqlHandleStr) > 0 BEGIN SET @sql_handle = snapshots.fn_hexstrtovarbin (@SqlHandleStr) END SELECT TOP 10 wt.category_name, r.wait_type, CASE WHEN LEN (ISNULL (r.wait_resource, '')) = 0 THEN CASE WHEN LEN (r.resource_description) > 30 THEN LEFT (r.resource_description, 27) + '...' ELSE LEFT (r.resource_description, 30) END ELSE r.wait_resource END AS wait_resource, r.resource_description, CONVERT (datetime, SWITCHOFFSET (MAX (r.collection_time), '+00:00')) AS example_collection_time, CONVERT (varchar(30), CONVERT (datetime, SWITCHOFFSET (MAX (r.collection_time), '+00:00')), 126) AS example_collection_time_str, COUNT(*) AS wait_count FROM snapshots.active_sessions_and_requests AS r LEFT OUTER JOIN core.wait_types_categorized AS wt ON wt.wait_type = r.wait_type WHERE r.collection_time BETWEEN @start_time_internal AND @end_time_internal AND r.command != 'AWAITING COMMAND' AND r.request_id != -1 -- exclude idle spids (e.g. head blockers) AND (r.program_name = @ProgramName OR @ProgramName IS NULL) AND (r.sql_handle = @sql_handle OR @SqlHandleStr IS NULL) AND (r.database_name = @Database OR @Database IS NULL) AND (r.statement_start_offset = @StatementStartOffset OR @SqlHandleStr IS NULL) AND (r.statement_end_offset = @StatementEndOffset OR @SqlHandleStr IS NULL) AND (r.session_id = @SessionID OR @SessionID IS NULL) AND ( -- ... and wait category either matches the user-specified parameter ... (@CategoryName = CASE -- same expression here as in the select column list WHEN r.wait_type = '' AND r.task_state IS NOT NULL THEN 'CPU' ELSE wt.category_name END ) -- ... or a filter parameter for wait category was not provided (return all categories that aren't marked as ignorable). OR (@CategoryName IS NULL AND ISNULL (wt.ignore, 0) = 0) ) AND ( -- ... and wait type either matches the user-specified parameter ... (@WaitType = CASE WHEN r.wait_type = '' AND r.task_state = 'RUNNING' THEN 'CPU (Consumed)' WHEN r.wait_type = '' AND r.task_state != 'RUNNING' THEN 'SOS_SCHEDULER_YIELD' ELSE r.wait_type END ) -- ... or a filter parameter for wait category was not provided OR @WaitType IS NULL ) -- Exclude rows where there is no named resource AND (ISNULL (r.resource_description, '') != '' OR ISNULL (r.wait_resource, '') != '') GROUP BY wt.category_name, r.wait_type, r.wait_resource, r.resource_description ORDER BY COUNT(*) DESC -- Force a recompile of this statement to take into account the actual values of @start_time_internal and -- @end_time_internal, which were not available at the time of proc compilation. OPTION (RECOMPILE); END GO -- -- snapshots.rpt_io_virtual_file_stats -- Returns wait time per wait type over a time interval -- Parameters: -- @ServerName - SQL Server instance name -- @EndTime - End of the user-selected time window (UTC) -- @WindowSize - Number of minutes in the time window -- @CategoryName - (Optional) Name of wait category to filter on (all categories if NULL) -- @WaitType - (Optional) Name of wait type to filter on (all wait types if NULL) -- IF (NOT OBJECT_ID(N'snapshots.rpt_io_virtual_file_stats', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_io_virtual_file_stats] ...' DROP PROCEDURE [snapshots].[rpt_io_virtual_file_stats] END GO PRINT 'Creating procedure [snapshots].[rpt_io_virtual_file_stats] ...' GO CREATE PROCEDURE [snapshots].[rpt_io_virtual_file_stats] @ServerName sysname, @EndTime datetime = NULL, @WindowSize int, @LogicalDisk nvarchar(255) = NULL, @Database nvarchar(255) = NULL AS BEGIN SET NOCOUNT ON; -- Clean string params (on drillthrough, RS may pass in empty string instead of NULL) IF @LogicalDisk = '' SET @LogicalDisk = NULL IF @Database = '' SET @Database = NULL -- Divide our time window up into 40 evenly-sized time intervals, and find the last collection_time within each of these intervals CREATE TABLE #intervals ( interval_time_id int, interval_start_time datetimeoffset(7), interval_end_time datetimeoffset(7), interval_id int, first_collection_time datetimeoffset(7), last_collection_time datetimeoffset(7), first_snapshot_id int, last_snapshot_id int, source_id int, snapshot_id int, collection_time datetimeoffset(7), collection_time_id int ) -- GUID 49268954-... is Server Activity INSERT INTO #intervals EXEC [snapshots].[rpt_interval_collection_times] @ServerName, @EndTime, @WindowSize, 'snapshots.io_virtual_file_stats', '49268954-4FD4-4EB6-AA04-CD59D9BB5714', 40, 0 -- Get the earliest and latest snapshot_id values that contain data for the selected time interval. -- This will allow a more efficient query plan. DECLARE @start_snapshot_id int; DECLARE @end_snapshot_id int; SELECT @start_snapshot_id = MIN (first_snapshot_id) FROM #intervals SELECT @end_snapshot_id = MAX (last_snapshot_id) FROM #intervals -- Get the file stats for these collection times SELECT coll.interval_id, coll.interval_time_id, coll.interval_start_time, coll.interval_end_time, coll.first_collection_time, coll.last_collection_time, coll.first_snapshot_id, coll.last_snapshot_id, fs.* INTO #file_stats FROM snapshots.io_virtual_file_stats AS fs INNER JOIN #intervals AS coll ON coll.last_snapshot_id = fs.snapshot_id AND coll.last_collection_time = fs.collection_time WHERE fs.logical_disk = ISNULL (@LogicalDisk, fs.logical_disk) AND fs.database_name = ISNULL (@Database, fs.database_name) -- Get file stats deltas for each interval. SELECT t.*, /**** Combined reads + write values ****/ t.num_of_reads_delta + t.num_of_writes_delta AS num_of_transfers_delta, t.num_of_reads_cumulative + t.num_of_writes_cumulative AS num_of_transfers_cumulative, t.num_of_mb_read_delta + t.num_of_mb_written_delta AS num_of_mb_transferred_delta, t.num_of_mb_read_cumulative + t.num_of_mb_written_cumulative AS num_of_mb_transferred_cumulative, /**** Calc "Disk sec/Transfer" type values ***/ t.io_stall_read_ms_delta + t.io_stall_write_ms_delta AS io_stall_ms_delta, t.io_stall_read_ms_cumulative + t.io_stall_write_ms_cumulative AS io_stall_ms_cumulative, CASE WHEN t.num_of_reads_delta = 0 THEN 0 ELSE t.io_stall_read_ms_delta / t.num_of_reads_delta END AS io_stall_ms_per_read_delta, CASE WHEN t.num_of_reads_cumulative = 0 THEN 0 ELSE t.io_stall_read_ms_cumulative / t.num_of_reads_cumulative END AS io_stall_ms_per_read_cumulative, CASE WHEN t.num_of_writes_delta = 0 THEN 0 ELSE t.io_stall_write_ms_delta / t.num_of_writes_delta END AS io_stall_ms_per_write_delta, CASE WHEN t.num_of_writes_cumulative = 0 THEN 0 ELSE t.io_stall_write_ms_cumulative / t.num_of_writes_cumulative END AS io_stall_ms_per_write_cumulative, CASE WHEN (t.num_of_reads_delta + t.num_of_writes_delta) = 0 THEN 0 ELSE (t.io_stall_read_ms_delta + t.io_stall_write_ms_delta) / (t.num_of_reads_delta + t.num_of_writes_delta) END AS io_stall_ms_per_transfer_delta, CASE WHEN (t.num_of_reads_cumulative + t.num_of_writes_cumulative) = 0 THEN 0 ELSE (t.io_stall_read_ms_cumulative + t.io_stall_write_ms_cumulative) / (t.num_of_reads_cumulative + t.num_of_writes_cumulative) END AS io_stall_ms_per_transfer_cumulative FROM ( SELECT fs1.interval_id, fs1.interval_time_id, fs1.first_snapshot_id, fs1.last_snapshot_id, -- Convert all datetimeoffset values to UTC datetime values before returning to Reporting Services CONVERT (datetime, SWITCHOFFSET (CAST (fs1.first_collection_time AS datetimeoffset(7)), '+00:00')) AS first_collection_time, CONVERT (datetime, SWITCHOFFSET (CAST (fs2.first_collection_time AS datetimeoffset(7)), '+00:00')) AS last_collection_time, CONVERT (datetime, SWITCHOFFSET (CAST (fs1.interval_start_time AS datetimeoffset(7)), '+00:00')) AS interval_start, CONVERT (datetime, SWITCHOFFSET (CAST (fs2.interval_start_time AS datetimeoffset(7)), '+00:00')) AS interval_end, fs2.database_name, fs2.database_id, fs2.logical_file_name, fs2.[file_id], fs2.type_desc, fs2.logical_disk, -- All file stats will be reset to zero by a service cycle, which will cause -- (snapshot2_io_time-snapshot1_io_time) calculations to produce an incorrect -- negative wait time for the interval. Detect this and avoid calculating -- negative IO wait time deltas. /***** READS ****/ CASE WHEN (fs2.num_of_reads - fs1.num_of_reads) < 0 THEN fs2.num_of_reads ELSE (fs2.num_of_reads - fs1.num_of_reads) END AS num_of_reads_delta, -- num_of_reads_delta fs2.num_of_reads AS num_of_reads_cumulative, -- num_of_reads_cumulative CASE WHEN (fs2.num_of_bytes_read - fs1.num_of_bytes_read) < 0 THEN fs2.num_of_bytes_read ELSE (fs2.num_of_bytes_read - fs1.num_of_bytes_read) END / 1024 / 1024 AS num_of_mb_read_delta, -- num_of_mb_read_delta fs2.num_of_bytes_read /1024/1024 AS num_of_mb_read_cumulative, -- num_of_mb_read_cumulative CASE WHEN (fs2.io_stall_read_ms - fs1.io_stall_read_ms) < 0 THEN fs2.io_stall_read_ms ELSE (fs2.io_stall_read_ms - fs1.io_stall_read_ms) END AS io_stall_read_ms_delta, -- io_stall_read_ms_delta fs2.io_stall_read_ms AS io_stall_read_ms_cumulative, -- io_stall_read_ms_cumulative /**** WRITES ****/ CASE WHEN (fs2.num_of_writes - fs1.num_of_writes) < 0 THEN fs2.num_of_writes ELSE (fs2.num_of_writes - fs1.num_of_writes) END AS num_of_writes_delta, -- num_of_writes_delta fs2.num_of_writes AS num_of_writes_cumulative, -- num_of_writes_cumulative CASE WHEN (fs2.num_of_bytes_written - fs1.num_of_bytes_written) < 0 THEN fs2.num_of_bytes_written ELSE (fs2.num_of_bytes_written - fs1.num_of_bytes_written) END / 1024 / 1024 AS num_of_mb_written_delta, -- num_of_mb_written_delta fs2.num_of_bytes_written /1024/1024 AS num_of_mb_written_cumulative, -- num_of_mb_written_cumulative CASE WHEN (fs2.io_stall_write_ms - fs1.io_stall_write_ms) < 0 THEN fs2.io_stall_write_ms ELSE (fs2.io_stall_write_ms - fs1.io_stall_write_ms) END AS io_stall_write_ms_delta, -- io_stall_write_ms_delta fs2.io_stall_write_ms AS io_stall_write_ms_cumulative, -- io_stall_write_ms_cumulative fs1.size_on_disk_bytes / 1024 / 1024 AS size_on_disk_mb_interval_start, -- size_on_disk_mb_interval_start fs2.size_on_disk_bytes / 1024 / 1024 AS size_on_disk_mb_interval_end -- size_on_disk_mb_interval_end FROM #file_stats AS fs1 -- Self-join - fs1 represents IO stats at the beginning of the sample interval, while fs2 -- shows file stats at the end of the interval. INNER JOIN #file_stats AS fs2 ON fs1.database_id = fs2.database_id AND fs1.[file_id] = fs2.[file_id] AND fs1.interval_id = fs2.interval_id-1 ) AS t ORDER BY t.database_name, t.logical_file_name, t.last_collection_time END GO -- -- snapshots.rpt_list_all_servers -- Shows a list of the servers that have data in this MDW database, plus information on how up-to-date -- the data is. Used in the multi-server overview report. -- Parameters: -- IF (NOT OBJECT_ID(N'snapshots.rpt_list_all_servers', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_list_all_servers] ...' DROP PROCEDURE [snapshots].[rpt_list_all_servers] END GO PRINT 'Creating procedure [snapshots].[rpt_list_all_servers] ...' GO CREATE PROCEDURE [snapshots].[rpt_list_all_servers] AS BEGIN SET NOCOUNT ON; SELECT instance_name, query_statistics_last_upload, disk_usage_last_upload, server_activity_last_upload FROM ( SELECT -- Name of the SQL Server instance instance_name, -- Name of the collection set. The names in the syscollector_collection_sets table can be localized. -- We use well-known strings here because we want to defer selection of the appropriate localized -- string to the client. CASE collection_set_uid WHEN '2DC02BD6-E230-4C05-8516-4E8C0EF21F95' THEN 'query_statistics_last_upload' WHEN '7B191952-8ECF-4E12-AEB2-EF646EF79FEF' THEN 'disk_usage_last_upload' WHEN '49268954-4FD4-4EB6-AA04-CD59D9BB5714' THEN 'server_activity_last_upload' ELSE NULL -- custom Collection set, not displayed on this report END AS top_level_report_name, -- Convert datetimeoffset to UTC datetime for RS 2005 compatibility CONVERT (datetime, SWITCHOFFSET (MAX (snapshot_time), '+00:00')) AS latest_snapshot_time FROM core.snapshots -- For this report, only system collection sets matter WHERE collection_set_uid IN ('2DC02BD6-E230-4C05-8516-4E8C0EF21F95', '7B191952-8ECF-4E12-AEB2-EF646EF79FEF', '49268954-4FD4-4EB6-AA04-CD59D9BB5714') GROUP BY instance_name, collection_set_uid ) AS instance_report_list PIVOT ( MAX (latest_snapshot_time) FOR top_level_report_name IN (query_statistics_last_upload, disk_usage_last_upload, server_activity_last_upload) ) AS pvt ORDER BY instance_name; END; GO /**********************************************************************/ /* CUSTOM_SNAPSHOTS SCHEMA */ /**********************************************************************/ PRINT '' PRINT 'Create schema custom_snpshots...' GO IF (SCHEMA_ID('custom_snapshots') IS NULL) BEGIN DECLARE @sql nvarchar(128) SET @sql = 'CREATE SCHEMA custom_snapshots' EXEC sp_executesql @sql END GO /**********************************************************************/ /* Create MDW security roles */ /**********************************************************************/ PRINT '' PRINT 'Create mdw_admin role...' IF ( NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'mdw_admin' AND type = 'R')) BEGIN CREATE ROLE [mdw_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'mdw_admin' AND type = 'R') BEGIN -- if the role has no members drop and recreate it DROP ROLE [mdw_admin] CREATE ROLE [mdw_admin] END END GO PRINT 'Create mdw_writer role...' IF ( NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'mdw_writer' AND type = 'R')) BEGIN CREATE ROLE [mdw_writer] 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'mdw_writer' AND type = 'R') BEGIN -- if the role has no members drop and recreate it DROP ROLE [mdw_writer] CREATE ROLE [mdw_writer] END END GO PRINT 'Create mdw_reader role...' IF ( NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'mdw_reader' AND type = 'R')) BEGIN CREATE ROLE [mdw_reader] 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'mdw_reader' AND type = 'R') BEGIN -- if the role has no members drop and recreate it DROP ROLE [mdw_reader] CREATE ROLE [mdw_reader] END END GO -- permissions of mdw_writer and mdw_reader are inclusive to mdw_admin EXECUTE sp_addrolemember @rolename = 'mdw_writer' , @membername = 'mdw_admin' GO EXECUTE sp_addrolemember @rolename = 'mdw_reader' , @membername = 'mdw_admin' GO PRINT '' PRINT 'Create loginless user ...' IF (NOT EXISTS(SELECT * FROM sys.database_principals WHERE NAME = 'mdw_check_operator_admin')) BEGIN CREATE USER [mdw_check_operator_admin] WITHOUT LOGIN END GO EXECUTE sp_addrolemember @rolename = 'mdw_admin', @membername = 'mdw_check_operator_admin' GO /**********************************************************************/ /* Setup permissions */ /**********************************************************************/ PRINT '' PRINT 'Granting permissions to MDW security roles...' -- Database level permissions GRANT VIEW DEFINITION TO [mdw_writer] -- Core schema permissions GRANT EXECUTE ON [core].[sp_create_snapshot] TO [mdw_writer] GRANT EXECUTE ON [core].[sp_update_data_source] TO [mdw_writer] GRANT EXECUTE ON [core].[sp_add_collector_type] TO [mdw_admin] GRANT EXECUTE ON [core].[sp_remove_collector_type] TO [mdw_admin] GRANT EXECUTE ON [core].[sp_purge_data] TO [mdw_admin] GRANT EXECUTE ON [core].[sp_stop_purge] TO [mdw_admin] GRANT REFERENCES ON [core].[fn_check_operator] TO [mdw_check_operator_admin] GRANT SELECT ON [core].[snapshots] TO [mdw_admin] GRANT SELECT ON [core].[snapshots] TO [mdw_writer] GRANT SELECT ON [core].[snapshots] TO [mdw_reader] GRANT SELECT ON [core].[supported_collector_types] TO [mdw_admin] GRANT SELECT ON [core].[supported_collector_types] TO [mdw_writer] GRANT SELECT ON [core].[supported_collector_types] TO [mdw_reader] GRANT SELECT ON [core].[wait_categories] TO [mdw_reader] GRANT SELECT ON [core].[wait_types] TO [mdw_reader] GRANT SELECT ON [core].[wait_types_categorized] TO [mdw_reader] GRANT SELECT ON [core].[snapshots_internal] TO [mdw_admin] GRANT SELECT ON [core].[source_info_internal] TO [mdw_admin] GRANT SELECT ON [core].[snapshot_timetable_internal] TO [mdw_admin] GRANT SELECT ON [core].[supported_collector_types_internal] TO [mdw_admin] GRANT SELECT ON [core].[fn_query_text_from_handle] TO [mdw_reader] GRANT SELECT ON [core].[performance_counter_report_group_items] TO [mdw_reader] -- Snapshot schema permissions GRANT INSERT ON SCHEMA :: [snapshots] TO [mdw_writer] GRANT EXECUTE ON SCHEMA :: [snapshots] TO [mdw_writer] GRANT DELETE ON SCHEMA :: [snapshots] TO [mdw_admin] GRANT SELECT ON SCHEMA :: [snapshots] TO [mdw_reader] -- mdw_writer needs SELECT permission to perform upload because bcp selects the table to verify its schema GRANT SELECT ON SCHEMA :: [snapshots] TO [mdw_writer] GRANT EXECUTE ON [snapshots].[sp_get_unknown_query_plan] TO [mdw_writer] GRANT EXECUTE ON [snapshots].[sp_get_unknown_query_text] TO [mdw_writer] GRANT EXECUTE ON [snapshots].[sp_update_query_plan] TO [mdw_writer] GRANT EXECUTE ON [snapshots].[sp_update_query_text] TO [mdw_writer] -- Custom_snapshot schema permissions GRANT CREATE TABLE TO [mdw_writer] GRANT ALTER ON SCHEMA :: [custom_snapshots] TO [mdw_writer] GRANT INSERT ON SCHEMA :: [custom_snapshots] TO [mdw_writer] GRANT DELETE ON SCHEMA :: [custom_snapshots] TO [mdw_admin] GRANT SELECT ON SCHEMA :: [custom_snapshots] TO [mdw_reader] -- mdw_writer needs SELECT permission to perform upload because bcp selects the table to verify its schema GRANT SELECT ON SCHEMA :: [custom_snapshots] TO [mdw_writer] GRANT CREATE TABLE TO [mdw_admin] GRANT CONTROL ON SCHEMA :: [custom_snapshots] TO [mdw_admin] -- Report procedures GRANT EXECUTE ON [snapshots].[rpt_snapshot_times] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_interval_collection_times] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_next_and_previous_collection_times] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_generic_perfmon] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_generic_perfmon_pivot] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_wait_stats] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[fn_hexstrtovarbin] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_top_query_stats] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_query_stats] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_query_plan_stats] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_query_plan_stats_timeline] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_query_plan_missing_indexes] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_query_plan_parameters] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_query_plan_details] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_blocking_chains] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_blocking_chain_detail] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_active_sessions_and_requests] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_sql_memory_clerks] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_sql_process_and_system_memory] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_sampled_waits] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_sampled_waits_longest] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_sampled_waits_hottest_resources] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_io_virtual_file_stats] TO [mdw_reader] GRANT EXECUTE ON [snapshots].[rpt_list_all_servers] TO [mdw_reader] -- NOTE: In order to insert data into MDW the login used for uploading data -- needs to have ADMINISTER BULK OPERATIONS server permission granted GO /**********************************************************************/ /* LEGACY OBJECTS - KEPT FOR OLDER CTPs BUT NO LONGER USED */ /**********************************************************************/ /**********************************************************************/ /* Used by CTP5 (but not CTP6): */ /**********************************************************************/ -- -- snapshots.rpt_waiting_sessions -- Returns list of sessions waiting for specified wait type category -- Parameters: -- @ServerName - SQL Server instance name -- @EndTime - end of the time window (UTC) -- @WindowSize - number of minutes in the time window -- @CategoryName - Name of wait category (optional) -- IF (NOT OBJECT_ID(N'snapshots.rpt_waiting_sessions', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_waiting_sessions] ...' DROP PROCEDURE [snapshots].[rpt_waiting_sessions] END GO PRINT 'Creating procedure [snapshots].[rpt_waiting_sessions] ...' GO CREATE PROCEDURE [snapshots].[rpt_waiting_sessions] @ServerName sysname, @EndTime datetime, @WindowSize int, @CategoryName nvarchar(20) AS BEGIN SET NOCOUNT ON; DECLARE @start_time_internal datetimeoffset(7); DECLARE @end_time_internal datetimeoffset(7); -- Start time should be passed in as a UTC datetime IF (@EndTime IS NOT NULL) BEGIN -- Assumed time zone offset for this conversion is +00:00 (datetime must be passed in as UTC) SET @end_time_internal = CAST (@EndTime AS datetimeoffset(7)); END ELSE BEGIN SELECT @end_time_internal = MAX(ar.collection_time) FROM core.snapshots AS s INNER JOIN snapshots.active_sessions_and_requests AS ar ON s.snapshot_id = ar.snapshot_id WHERE s.instance_name = @ServerName -- AND collection_set_uid = '49268954-4FD4-4EB6-AA04-CD59D9BB5714' -- Server Activity CS END SET @start_time_internal = DATEADD (minute, -1 * @WindowSize, @end_time_internal); DECLARE @total_waits TABLE ( wait_type nvarchar(45), wait_count bigint, wait_duration_ms bigint ) INSERT INTO @total_waits SELECT ar.wait_type, COUNT(*) AS wait_count, SUM (wait_duration_ms) FROM snapshots.active_sessions_and_requests ar JOIN core.snapshots s ON (s.snapshot_id = ar.snapshot_id) WHERE s.instance_name = @ServerName -- AND collection_set_uid = '49268954-4FD4-4EB6-AA04-CD59D9BB5714' -- Server Activity CS AND ar.collection_time BETWEEN @start_time_internal AND @end_time_internal AND ar.wait_type != '' GROUP BY ar.wait_type SELECT t.source_id, t.session_id, t.request_id, t.database_name, CONVERT (datetime, SWITCHOFFSET (CAST (t.login_time AS datetimeoffset(7)), '+00:00')) AS login_time, t.login_name, t.[program_name], t.sql_handle, master.dbo.fn_varbintohexstr (t.sql_handle) AS sql_handle_str, t.statement_start_offset, t.statement_end_offset, t.plan_handle, master.dbo.fn_varbintohexstr (t.plan_handle) AS plan_handle_str, t.wait_type, t.wait_count, t.wait_total_percent, t.wait_type_wait_percent, qt.query_text, REPLACE (REPLACE (REPLACE (REPLACE (REPLACE (REPLACE ( LEFT (LTRIM (qt.query_text), 100) , CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' '), ' ', ' '), ' ', ' '), ' ', ' ') AS flat_query_text FROM ( SELECT s.source_id, ar.session_id, ar.request_id, ar.database_name, ar.login_time, ar.login_name, ar.program_name, ar.sql_handle, ar.statement_start_offset, ar.statement_end_offset, ar.plan_handle, ar.wait_type, COUNT(*) AS wait_count, (COUNT(*)) / CONVERT(decimal, (SELECT SUM(wait_count) FROM @total_waits)) AS wait_total_percent, (COUNT(*)) / CONVERT(decimal, (SELECT wait_count FROM @total_waits WHERE wait_type = ar.wait_type)) AS wait_type_wait_percent FROM snapshots.active_sessions_and_requests AS ar INNER JOIN core.snapshots AS s ON (s.snapshot_id = ar.snapshot_id) INNER JOIN core.wait_types_categorized AS wt on (wt.wait_type = ar.wait_type) WHERE s.instance_name = @ServerName -- AND s.collection_set_uid = '49268954-4FD4-4EB6-AA04-CD59D9BB5714' -- Server Activity CS AND ar.collection_time BETWEEN @start_time_internal AND @end_time_internal AND wt.category_name = @CategoryName GROUP BY s.source_id, ar.session_id, ar.request_id, ar.database_name, ar.login_time, ar.login_name, ar.program_name, ar.wait_type, ar.sql_handle, ar.statement_start_offset, ar.statement_end_offset, ar.plan_handle ) AS t OUTER APPLY snapshots.fn_get_query_text(t.source_id, t.sql_handle, t.statement_start_offset, t.statement_end_offset) AS qt END; GO GRANT EXECUTE ON [snapshots].[rpt_waiting_sessions] TO [mdw_reader] GO -- This function returns a list of all the known query plans from a disctinct source IF (NOT OBJECT_ID(N'snapshots.fn_get_notable_query_plans', 'TF') IS NULL) BEGIN PRINT 'Dropping function [snapshots].[fn_get_notable_query_plans] ...' DROP FUNCTION [snapshots].[fn_get_notable_query_plans] END PRINT 'Creating function [snapshots].[fn_get_notable_query_plans]' Go CREATE FUNCTION [snapshots].[fn_get_notable_query_plans]( @source_id int ) RETURNS @notable_queries TABLE (sql_handle varbinary(64) , plan_handle varbinary(64), statement_start_offset int, statement_end_offset int, creation_time datetime) BEGIN INSERT INTO @notable_queries SELECT [sql_handle], [plan_handle], [statement_start_offset], [statement_end_offset], -- Convert datetimeoffset to datetime so that SSIS can easily join the output back -- to the new sys.dm_exec_query_stats data CONVERT (datetime, [creation_time]) AS [creation_time] FROM [snapshots].[notable_query_plan] WHERE [source_id] = @source_id ORDER BY [sql_handle] ASC, [plan_handle], [statement_start_offset], [statement_end_offset], [creation_time] ASC RETURN END GO GRANT SELECT ON [snapshots].[fn_get_notable_query_plans] TO [mdw_writer] GO -- This function returns a list of all the known sql_handles (i.e., query text) from a disctinct source IF (NOT OBJECT_ID(N'snapshots.fn_get_notable_query_text', 'TF') IS NULL) BEGIN PRINT 'Dropping function [snapshots].[fn_get_notable_query_text] ...' DROP FUNCTION [snapshots].[fn_get_notable_query_text] END PRINT 'Creating function [snapshots].[fn_get_notable_query_text]' Go CREATE FUNCTION [snapshots].[fn_get_notable_query_text]( @source_id int ) RETURNS @notable_text TABLE (sql_handle varbinary(64)) BEGIN INSERT INTO @notable_text SELECT [sql_handle] FROM [snapshots].[notable_query_text] WHERE [source_id] = @source_id ORDER BY [sql_handle] ASC RETURN END GO GRANT SELECT ON [snapshots].[fn_get_notable_query_text] TO [mdw_writer] GO -- -- snapshots.rpt_sql_memory_counters_one_snapshot -- Returns values for page life expectancy and other SQL Server memory counters -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_sql_memory_counters_one_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_sql_memory_counters_one_snapshot] ...' DROP PROCEDURE [snapshots].[rpt_sql_memory_counters_one_snapshot] END GO PRINT 'Creating procedure [snapshots].[rpt_sql_memory_counters_one_snapshot] ...' GO CREATE PROCEDURE [snapshots].[rpt_sql_memory_counters_one_snapshot] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT pc.performance_counter_name AS series, CONVERT (datetime, SWITCHOFFSET (CAST (pc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, pc.formatted_value FROM snapshots.performance_counters pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id AND pc.performance_object_name LIKE '%SQL%:Buffer Manager' AND pc.performance_counter_name = 'Page life expectancy' ORDER BY pc.collection_time, pc.performance_counter_name END; GO GRANT EXECUTE ON [snapshots].[rpt_sql_memory_counters_one_snapshot] TO [mdw_reader] GO -- -- snapshots.rpt_sql_memory_clerks_one_snapshot -- Returns data from os_memory_clerks table -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_sql_memory_clerks_one_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_sql_memory_clerks_one_snapshot] ...' DROP PROCEDURE [snapshots].[rpt_sql_memory_clerks_one_snapshot] END GO PRINT 'Creating procedure [snapshots].[rpt_sql_memory_clerks_one_snapshot] ...' GO CREATE PROCEDURE [snapshots].[rpt_sql_memory_clerks_one_snapshot] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; DECLARE @total_allocated TABLE ( collection_time datetimeoffset(7), total_kb bigint ); INSERT INTO @total_allocated SELECT mc.collection_time, SUM(mc.single_pages_kb + mc.multi_pages_kb + (CASE WHEN type <> N'MEMORYCLERK_SQLBUFFERPOOL' THEN mc.virtual_memory_committed_kb ELSE 0 END) + mc.shared_memory_committed_kb) FROM snapshots.os_memory_clerks mc JOIN core.snapshots s ON (s.snapshot_id = mc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id GROUP BY mc.collection_time SELECT CONVERT (datetime, SWITCHOFFSET (CAST (mc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, mc.type, mc.single_pages_kb + mc.multi_pages_kb as allocated_kb, mc.virtual_memory_reserved_kb as virtual_reserved_kb, mc.virtual_memory_committed_kb as virtual_committed_kb, mc.awe_allocated_kb as awe_allocated_kb, mc.shared_memory_reserved_kb as shared_reserved_kb, mc.shared_memory_committed_kb as shared_committed_kb, (mc.single_pages_kb + mc.multi_pages_kb + (CASE WHEN type <> 'MEMORYCLERK_SQLBUFFERPOOL' THEN mc.virtual_memory_committed_kb ELSE 0 END) + mc.shared_memory_committed_kb) as total_kb, (mc.single_pages_kb + mc.multi_pages_kb + (CASE WHEN type <> 'MEMORYCLERK_SQLBUFFERPOOL' THEN mc.virtual_memory_committed_kb ELSE 0 END) + mc.shared_memory_committed_kb) / CONVERT(decimal, ta.total_kb) AS percent_total_kb, CASE WHEN (mc.single_pages_kb + mc.multi_pages_kb + (CASE WHEN type <> 'MEMORYCLERK_SQLBUFFERPOOL' THEN mc.virtual_memory_committed_kb ELSE 0 END) + mc.shared_memory_committed_kb) / CONVERT(decimal, ta.total_kb) > 0.05 THEN mc.type ELSE N'Other' END AS graph_type FROM snapshots.os_memory_clerks mc JOIN @total_allocated ta ON (mc.collection_time = ta.collection_time) ORDER BY collection_time END; GO GRANT EXECUTE ON [snapshots].[rpt_sql_memory_clerks_one_snapshot] TO [mdw_reader] GO -- -- snapshots.rpt_sql_process_memory_one_snapshot -- Returns details on sql process memory usage -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_sql_process_memory_one_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_sql_process_memory_one_snapshot] ...' DROP PROCEDURE [snapshots].[rpt_sql_process_memory_one_snapshot] END GO PRINT 'Creating procedure [snapshots].[rpt_sql_process_memory_one_snapshot] ...' GO CREATE PROCEDURE [snapshots].[rpt_sql_process_memory_one_snapshot] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT CONVERT (datetime, SWITCHOFFSET (CAST (collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, CASE WHEN series = 'virtual_address_space_reserved_kb' THEN 'Virtual Memory Reserved' WHEN series = 'virtual_address_space_committed_kb' THEN 'Virtual Memory Committed' WHEN series = 'physical_memory_in_use_kb' THEN 'Physical Memory In Use' WHEN series = 'process_physical_memory_low' THEN 'Physical Memory Low' WHEN series = 'process_virtual_memory_low' THEN 'Virtual Memory Low' ELSE series END AS series, [value] / 1024 AS [value] -- convert KB to MB FROM ( SELECT pm.collection_time, pm.virtual_address_space_reserved_kb, pm.virtual_address_space_committed_kb, pm.physical_memory_in_use_kb, CONVERT (bigint, pm.process_physical_memory_low) AS process_physical_memory_low, CONVERT (bigint, pm.process_virtual_memory_low) AS process_virtual_memory_low FROM [snapshots].[os_process_memory] pm JOIN core.snapshots s ON (s.snapshot_id = pm.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id ) AS pvt UNPIVOT ( [value] for [series] in (virtual_address_space_reserved_kb, virtual_address_space_committed_kb, physical_memory_in_use_kb) ) AS unpvt UNION ALL SELECT CONVERT (datetime, SWITCHOFFSET (CAST (collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, 'Stolen Buffer Pool' AS [series], pc.formatted_value * 8 AS [value] -- Convert from pages to KB FROM snapshots.performance_counters AS pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id AND pc.performance_object_name LIKE '%SQL%:Buffer Manager' AND pc.performance_counter_name = 'Stolen pages' ORDER BY collection_time END GO GRANT EXECUTE ON [snapshots].[rpt_sql_process_memory_one_snapshot] TO [mdw_reader] GO -- -- snapshots.rpt_memory_counters_one_snapshot -- Returns values for memory usage counters -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_memory_counters_one_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_memory_counters_one_snapshot] ...' DROP PROCEDURE [snapshots].[rpt_memory_counters_one_snapshot] END GO PRINT 'Creating procedure [snapshots].[rpt_memory_counters_one_snapshot] ...' GO CREATE PROCEDURE [snapshots].[rpt_memory_counters_one_snapshot] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT pc.performance_counter_name AS series, CONVERT (datetime, SWITCHOFFSET (CAST (pc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, pc.collection_time, pc.formatted_value / (1024*1024) AS formatted_value FROM snapshots.performance_counters pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id AND ( (pc.performance_object_name = 'Memory' AND pc.performance_counter_name IN ('Cache Bytes', 'Pool Nonpaged Bytes')) OR (pc.performance_object_name = 'Process' AND pc.performance_counter_name = 'Working Set' AND pc.performance_instance_name = '_Total') ) ORDER BY pc.collection_time, series END; GO GRANT EXECUTE ON [snapshots].[rpt_memory_counters_one_snapshot] TO [mdw_reader] GO -- -- snapshots.rpt_memory_rates_one_snapshot -- Returns values for various memory ratios -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_memory_rates_one_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_memory_rates_one_snapshot] ...' DROP PROCEDURE [snapshots].[rpt_memory_rates_one_snapshot] END GO PRINT 'Creating procedure [snapshots].[rpt_memory_rates_one_snapshot] ...' GO CREATE PROCEDURE [snapshots].[rpt_memory_rates_one_snapshot] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT pc.performance_counter_name AS series, CONVERT (datetime, SWITCHOFFSET (CAST (pc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, pc.collection_time, pc.formatted_value FROM snapshots.performance_counters pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id AND pc.performance_object_name = 'Memory' AND pc.performance_counter_name IN ('Page Faults/sec', 'Page Reads/sec', 'Page Writes/sec', 'Cache Faults/sec') ORDER BY pc.collection_time, series END; GO GRANT EXECUTE ON [snapshots].[rpt_memory_rates_one_snapshot] TO [mdw_reader] GO -- -- snapshots.rpt_cpu_counters_one_snapshot -- Returns values for CPU usage counters per processor -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_cpu_counters_one_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_cpu_counters_one_snapshot] ...' DROP PROCEDURE [snapshots].[rpt_cpu_counters_one_snapshot] END GO PRINT 'Creating procedure [snapshots].[rpt_cpu_counters_one_snapshot] ...' GO CREATE PROCEDURE [snapshots].[rpt_cpu_counters_one_snapshot] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT N'CPU ' + CONVERT (nvarchar(10), ISNULL(pc.performance_instance_name, N'')) as series, CONVERT (datetime, SWITCHOFFSET (CAST (pc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, pc.formatted_value FROM snapshots.performance_counters pc JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id AND pc.performance_object_name = 'Processor' AND pc.performance_counter_name = '% Processor Time' AND pc.performance_instance_name != '_Total' ORDER BY pc.collection_time, series -- These trace flags are necessary for a good plan, due to the join on ascending PK w/range filter OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390) END; GO GRANT EXECUTE ON [snapshots].[rpt_cpu_counters_one_snapshot] TO [mdw_reader] GO -- -- snapshots.rpt_cpu_queues_one_snapshot -- Returns values for queue length counter per processor -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_cpu_queues_one_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_cpu_queues_one_snapshot] ...' DROP PROCEDURE [snapshots].[rpt_cpu_queues_one_snapshot] END GO PRINT 'Creating procedure [snapshots].[rpt_cpu_queues_one_snapshot] ...' GO CREATE PROCEDURE [snapshots].[rpt_cpu_queues_one_snapshot] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT CASE pc.performance_counter_name WHEN 'Processor Queue Length' THEN N'Processor Queue Length' ELSE N'CPU ' + CONVERT (nvarchar(10), ISNULL(pc.performance_instance_name, N'')) END AS series, CONVERT (datetime, SWITCHOFFSET (CAST (pc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, pc.formatted_value FROM snapshots.performance_counters pc JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id AND ( (pc.performance_object_name = 'Server Work Queues' AND pc.performance_counter_name = 'Queue Length' AND pc.performance_instance_name != '_Total' AND ISNUMERIC (pc.performance_instance_name) = 1) OR (pc.performance_object_name = 'System' AND pc.performance_counter_name = 'Processor Queue Length') ) ORDER BY pc.collection_time, series END; GO GRANT EXECUTE ON [snapshots].[rpt_cpu_queues_one_snapshot] TO [mdw_reader] GO -- -- snapshots.rpt_cpu_usage_per_process -- Returns min, max, avg CPU usage and avg thread count for top 10 processes -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_cpu_usage_per_process', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_cpu_usage_per_process] ...' DROP PROCEDURE [snapshots].[rpt_cpu_usage_per_process] END GO PRINT 'Creating procedure [snapshots].[rpt_cpu_usage_per_process] ...' GO CREATE PROCEDURE [snapshots].[rpt_cpu_usage_per_process] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; -- Determine the CPU count on the target system by querying the number of "Processor" -- counter instances we captured in a perfmon sample that was captured around the same -- time. DECLARE @cpu_count smallint SELECT @cpu_count = COUNT (DISTINCT pc.performance_instance_name) FROM snapshots.performance_counters AS pc INNER JOIN core.snapshots s ON s.snapshot_id = pc.snapshot_id WHERE pc.performance_object_name = 'Processor' AND pc.performance_counter_name = '% Processor Time' AND pc.performance_instance_name != '_Total' AND s.snapshot_time_id = @snapshot_time_id AND s.instance_name = @instance_name AND pc.collection_time = (SELECT TOP 1 collection_time FROM snapshots.performance_counter_values pcv2 WHERE pcv2.snapshot_id = s.snapshot_id) SELECT TOP 10 cpu.process_name, cpu.minimum_value / @cpu_count AS cpu_minimum_value, cpu.maximum_value / @cpu_count AS cpu_maximum_value, cpu.average_value / @cpu_count AS cpu_average_value, tc.average_value AS tc_average_value FROM ( SELECT ISNULL(pc.performance_instance_name, N'') AS process_name, MIN(pc.formatted_value) AS minimum_value, MAX(pc.formatted_value) AS maximum_value, AVG(pc.formatted_value) AS average_value FROM [snapshots].[performance_counters] AS pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.snapshot_time_id = @snapshot_time_id AND s.instance_name = @instance_name AND (pc.performance_object_name = 'Process' AND pc.performance_counter_name = '% Processor Time' AND pc.performance_instance_name NOT IN ('_Total', 'Idle')) GROUP BY pc.performance_instance_name ) AS cpu INNER JOIN ( SELECT ISNULL(pc.performance_instance_name, N'') AS process_name, MIN(pc.formatted_value) AS minimum_value, MAX(pc.formatted_value) AS maximum_value, AVG(pc.formatted_value) AS average_value FROM [snapshots].[performance_counters] as pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.snapshot_time_id = @snapshot_time_id AND s.instance_name = @instance_name AND (pc.performance_object_name = 'Process' AND pc.performance_counter_name = 'Thread Count' AND pc.performance_instance_name NOT IN ('_Total', 'Idle')) GROUP BY pc.performance_instance_name ) AS tc ON (tc.process_name = cpu.process_name) ORDER BY cpu_average_value DESC END; GO GRANT EXECUTE ON [snapshots].[rpt_cpu_usage_per_process] TO [mdw_reader] GO -- -- snapshots.rpt_sessions_and_connections -- Returns counts for sessions and connections within the given snapshot -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_sessions_and_connections', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_sessions_and_connections] ...' DROP PROCEDURE [snapshots].[rpt_sessions_and_connections] END GO PRINT 'Creating procedure [snapshots].[rpt_sessions_and_connections] ...' GO CREATE PROCEDURE [snapshots].[rpt_sessions_and_connections] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT pc.performance_counter_name AS series, CONVERT (datetime, SWITCHOFFSET (CAST (pc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, pc.formatted_value FROM snapshots.performance_counters pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id AND (pc.performance_object_name LIKE '%SQL%:General Statistics' OR pc.performance_object_name LIKE '%SQL%:Databases') AND pc.performance_counter_name IN ('User Connections', 'Active Transactions') UNION ALL SELECT N'Active sessions' AS series, ar.collection_time, COUNT(DISTINCT ar.session_id) AS formatted_value FROM snapshots.active_sessions_and_requests ar INNER JOIN core.snapshots s ON (s.snapshot_id = ar.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id GROUP BY ar.collection_time ORDER BY collection_time END; GO GRANT EXECUTE ON [snapshots].[rpt_sessions_and_connections] TO [mdw_reader] GO -- -- snapshots.rpt_requests_and_compilations -- Returns counts for number of requests/sec and related compilations counters -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_requests_and_compilations', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_requests_and_compilations] ...' DROP PROCEDURE [snapshots].[rpt_requests_and_compilations] END GO PRINT 'Creating procedure [snapshots].[rpt_requests_and_compilations] ...' GO CREATE PROCEDURE [snapshots].[rpt_requests_and_compilations] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT pc.performance_counter_name AS series, CONVERT (datetime, SWITCHOFFSET (CAST (pc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, pc.formatted_value FROM snapshots.performance_counters pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id AND pc.performance_object_name LIKE '%SQL%:SQL Statistics' AND pc.performance_counter_name IN ('Batch Requests/sec', 'SQL Compilations/sec', 'SQL Re-Compilations/sec', 'Auto-Param Attempts/sec', 'Failed Auto-Params/sec') ORDER BY pc.collection_time, pc.performance_counter_name END; GO GRANT EXECUTE ON [snapshots].[rpt_requests_and_compilations] TO [mdw_reader] GO -- -- snapshots.rpt_plan_cache_hit_ratio -- Returns details on plan cache hit ratio counter -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_plan_cache_hit_ratio', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_plan_cache_hit_ratio] ...' DROP PROCEDURE [snapshots].[rpt_plan_cache_hit_ratio] END GO PRINT 'Creating procedure [snapshots].[rpt_plan_cache_hit_ratio] ...' GO CREATE PROCEDURE [snapshots].[rpt_plan_cache_hit_ratio] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT ISNULL(pc.performance_instance_name, N'') AS series, CONVERT (datetime, SWITCHOFFSET (CAST (pc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, pc.formatted_value FROM snapshots.performance_counters pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id AND pc.performance_object_name LIKE '%SQL%:Plan Cache' AND pc.performance_counter_name = 'Cache Hit Ratio' AND pc.performance_instance_name != '_Total' ORDER BY pc.collection_time, ISNULL(pc.performance_instance_name, N'') END; GO GRANT EXECUTE ON [snapshots].[rpt_plan_cache_hit_ratio] TO [mdw_reader] GO -- -- snapshots.rpt_tempdb -- Returns counters related to tempdb -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_tempdb', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_tempdb] ...' DROP PROCEDURE [snapshots].[rpt_tempdb] END GO PRINT 'Creating procedure [snapshots].[rpt_tempdb] ...' GO CREATE PROCEDURE [snapshots].[rpt_tempdb] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT pc.performance_counter_name AS series, CONVERT (datetime, SWITCHOFFSET (CAST (pc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, pc.formatted_value FROM snapshots.performance_counters pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id AND (pc.performance_object_name LIKE '%SQL%:Transactions' OR pc.performance_object_name LIKE '%SQL%:General Statistics') AND pc.performance_counter_name IN ('Free Space in tempdb (KB)', 'Active Temp Tables') ORDER BY pc.collection_time, pc.performance_counter_name END; GO GRANT EXECUTE ON [snapshots].[rpt_tempdb] TO [mdw_reader] GO -- -- snapshots.rpt_disk_speed_one_snapshot -- Returns values for disk usage counters -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_disk_speed_one_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_disk_speed_one_snapshot] ...' DROP PROCEDURE [snapshots].[rpt_disk_speed_one_snapshot] END GO PRINT 'Creating procedure [snapshots].[rpt_disk_speed_one_snapshot] ...' GO CREATE PROCEDURE [snapshots].[rpt_disk_speed_one_snapshot] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT ISNULL(pc.performance_instance_name, N'') AS disk_letter, pc.performance_counter_name AS counter, CONVERT (datetime, SWITCHOFFSET (CAST (pc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, pc.formatted_value AS formatted_value FROM snapshots.performance_counters pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id AND pc.performance_object_name = 'LogicalDisk' AND pc.performance_counter_name IN ('Avg. Disk sec/Read', 'Avg. Disk sec/Write') AND pc.performance_instance_name != '_Total' ORDER BY pc.collection_time, pc.performance_counter_name, ISNULL(pc.performance_instance_name, N'') END; GO GRANT EXECUTE ON [snapshots].[rpt_disk_speed_one_snapshot] TO [mdw_reader] GO -- -- snapshots.rpt_disk_queues_one_snapshot -- Returns values for disk queue counters -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_disk_queues_one_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_disk_queues_one_snapshot] ...' DROP PROCEDURE [snapshots].[rpt_disk_queues_one_snapshot] END GO PRINT 'Creating procedure [snapshots].[rpt_disk_queues_one_snapshot] ...' GO CREATE PROCEDURE [snapshots].[rpt_disk_queues_one_snapshot] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT ISNULL(pc.performance_instance_name, N'') AS disk_letter, pc.performance_counter_name AS counter, CONVERT (datetime, SWITCHOFFSET (CAST (pc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, pc.formatted_value FROM snapshots.performance_counters pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id AND pc.performance_object_name = 'LogicalDisk' AND pc.performance_counter_name IN ('Current Disk Queue Length', 'Avg. Disk Read Queue Length', 'Avg. Disk Write Queue Length') AND pc.performance_instance_name != '_Total' ORDER BY pc.collection_time, pc.performance_counter_name, ISNULL(pc.performance_instance_name, N'') END; GO GRANT EXECUTE ON [snapshots].[rpt_disk_queues_one_snapshot] TO [mdw_reader] GO -- -- snapshots.rpt_disk_ratios_one_snapshot -- Returns values for disk ratio counters -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_disk_ratios_one_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_disk_ratios_one_snapshot] ...' DROP PROCEDURE [snapshots].[rpt_disk_ratios_one_snapshot] END GO PRINT 'Creating procedure [snapshots].[rpt_disk_ratios_one_snapshot] ...' GO CREATE PROCEDURE [snapshots].[rpt_disk_ratios_one_snapshot] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT ISNULL(pc.performance_instance_name, N'') AS disk_letter, pc.performance_counter_name AS counter, CONVERT (datetime, SWITCHOFFSET (CAST (pc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, pc.formatted_value FROM snapshots.performance_counters pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id = @snapshot_time_id AND pc.performance_object_name = 'LogicalDisk' AND pc.performance_counter_name IN ('Disk Reads/sec', 'Disk Writes/sec') AND pc.performance_instance_name != '_Total' ORDER BY pc.collection_time, pc.performance_counter_name, ISNULL(pc.performance_instance_name, N'') END; GO GRANT EXECUTE ON [snapshots].[rpt_disk_ratios_one_snapshot] TO [mdw_reader] GO -- -- snapshots.rpt_disk_usage_per_process -- Returns min, max, avg CPU usage and avg thread count for top 10 processes -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_disk_usage_per_process', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_disk_usage_per_process] ...' DROP PROCEDURE [snapshots].[rpt_disk_usage_per_process] END GO PRINT 'Creating procedure [snapshots].[rpt_disk_usage_per_process] ...' GO CREATE PROCEDURE [snapshots].[rpt_disk_usage_per_process] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT TOP 10 wb.process_name, wb.minimum_value AS wb_minimum_value, wb.maximum_value AS wb_maximum_value, wb.average_value AS wb_average_value, rb.minimum_value AS rb_minimum_value, rb.maximum_value AS rb_maximum_value, rb.average_value AS rb_average_value FROM ( SELECT ISNULL(pc.performance_instance_name, N'') AS process_name, MIN(pc.formatted_value / (1024)) as minimum_value, MAX(pc.formatted_value / (1024)) as maximum_value, AVG(pc.formatted_value / (1024)) as average_value FROM [snapshots].[performance_counters] as pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.snapshot_time_id = @snapshot_time_id AND s.instance_name = @instance_name AND pc.performance_object_name = 'Process' AND pc.performance_counter_name = 'IO Read Bytes/sec' AND pc.performance_instance_name NOT IN ('_Total', 'Idle') GROUP BY pc.performance_instance_name ) AS rb INNER JOIN ( SELECT ISNULL(pc.performance_instance_name, N'') AS process_name, MIN(pc.formatted_value / (1024)) as minimum_value, MAX(pc.formatted_value / (1024)) as maximum_value, AVG(pc.formatted_value / (1024)) as average_value FROM [snapshots].[performance_counters] as pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.snapshot_time_id = @snapshot_time_id AND s.instance_name = @instance_name AND pc.performance_object_name = 'Process' AND pc.performance_counter_name = 'IO Write Bytes/sec' AND pc.performance_instance_name NOT IN ('_Total', 'Idle') GROUP BY pc.performance_instance_name ) AS wb ON (wb.process_name = rb.process_name) ORDER BY wb_average_value DESC, rb_average_value DESC END; GO GRANT EXECUTE ON [snapshots].[rpt_disk_usage_per_process] TO [mdw_reader] GO -- snapshots.rpt_waiting_sessions_per_snapshot -- Returns list of sessions waiting for specified wait type category -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- @wait_category_name - Name of wait category to filter on -- IF (NOT OBJECT_ID(N'snapshots.rpt_waiting_sessions_per_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_waiting_sessions_per_snapshot] ...' DROP PROCEDURE [snapshots].[rpt_waiting_sessions_per_snapshot] END GO PRINT 'Creating procedure [snapshots].[rpt_waiting_sessions_per_snapshot] ...' GO CREATE PROCEDURE [snapshots].[rpt_waiting_sessions_per_snapshot] @instance_name sysname, @snapshot_time_id int, @wait_category_name nvarchar(20) AS BEGIN SET NOCOUNT ON; DECLARE @total_waits TABLE ( wait_type nvarchar(45), wait_count bigint ) INSERT INTO @total_waits SELECT ar.wait_type, COUNT(ar.wait_type) FROM snapshots.active_sessions_and_requests ar JOIN core.snapshots s ON (s.snapshot_id = ar.snapshot_id) WHERE s.snapshot_time_id = @snapshot_time_id AND s.instance_name = @instance_name GROUP BY ar.wait_type SELECT t.source_id, t.session_id, t.request_id, t.database_name, CONVERT (datetime, SWITCHOFFSET (CAST (t.login_time AS datetimeoffset(7)), '+00:00')) AS login_time, t.login_name, t.[program_name], t.sql_handle, master.dbo.fn_varbintohexstr (t.sql_handle) AS sql_handle_str, t.statement_start_offset, t.statement_end_offset, t.plan_handle, master.dbo.fn_varbintohexstr (t.plan_handle) AS plan_handle_str, t.wait_type, t.wait_count, t.wait_total_precent, t.wait_type_wait_precent, qt.query_text FROM ( SELECT s.source_id, ar.session_id, ar.request_id, ar.database_name, ar.login_time, ar.login_name, ar.program_name, ar.sql_handle, ar.statement_start_offset, ar.statement_end_offset, ar.plan_handle, ar.wait_type, COUNT(ar.wait_type) AS wait_count, (COUNT(ar.wait_type)) / CONVERT(decimal, (SELECT SUM(wait_count) FROM @total_waits)) AS wait_total_precent, (COUNT(ar.wait_type)) / CONVERT(decimal, (SELECT wait_count FROM @total_waits WHERE wait_type = ar.wait_type)) AS wait_type_wait_precent FROM snapshots.active_sessions_and_requests ar JOIN core.snapshots s ON (s.snapshot_id = ar.snapshot_id) JOIN core.wait_types ev on (ev.wait_type = ar.wait_type) JOIN core.wait_categories ct on (ct.category_id = ev.category_id) WHERE s.snapshot_time_id = @snapshot_time_id AND s.instance_name = @instance_name AND ct.category_name = @wait_category_name GROUP BY s.source_id, ar.session_id, ar.request_id, ar.database_name, ar.login_time, ar.login_name, ar.program_name, ar.wait_type, ar.sql_handle, ar.statement_start_offset, ar.statement_end_offset, ar.plan_handle ) t OUTER APPLY snapshots.fn_get_query_text(t.source_id, t.sql_handle, t.statement_start_offset, t.statement_end_offset) AS qt END; GO GRANT EXECUTE ON [snapshots].[rpt_waiting_sessions_per_snapshot] TO [mdw_reader] GO -- -- snapshots.rpt_memory_usage_per_process -- Returns min, max, avg CPU usage and avg thread count for top 10 processes -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- IF (NOT OBJECT_ID(N'snapshots.rpt_memory_usage_per_process', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_memory_usage_per_process] ...' DROP PROCEDURE [snapshots].[rpt_memory_usage_per_process] END GO PRINT 'Creating procedure [snapshots].[rpt_memory_usage_per_process] ...' GO CREATE PROCEDURE [snapshots].[rpt_memory_usage_per_process] @instance_name sysname, @snapshot_time_id int AS BEGIN SET NOCOUNT ON; SELECT TOP 10 ws.process_name, ws.minimum_value AS ws_minimum_value, ws.maximum_value AS ws_maximum_value, ws.average_value AS ws_average_value, vb.minimum_value AS vb_minimum_value, vb.maximum_value AS vb_maximum_value, vb.average_value AS vb_average_value FROM ( SELECT ISNULL(pc.performance_instance_name, N'') as process_name, MIN(pc.formatted_value / (1024*1024)) as minimum_value, MAX(pc.formatted_value / (1024*1024)) as maximum_value, AVG(pc.formatted_value / (1024*1024)) as average_value FROM [snapshots].[performance_counters] as pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.snapshot_time_id = @snapshot_time_id AND s.instance_name = @instance_name AND pc.performance_object_name = 'Process' AND pc.performance_counter_name = 'Working Set' AND pc.performance_instance_name NOT IN ('_Total', 'Idle') GROUP BY pc.performance_instance_name ) AS ws INNER JOIN ( SELECT ISNULL(pc.performance_instance_name, N'') as process_name, MIN(pc.formatted_value / (1024*1024)) as minimum_value, MAX(pc.formatted_value / (1024*1024)) as maximum_value, AVG(pc.formatted_value / (1024*1024)) as average_value FROM [snapshots].[performance_counters] as pc INNER JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.snapshot_time_id = @snapshot_time_id AND s.instance_name = @instance_name AND pc.performance_object_name = 'Process' AND pc.performance_counter_name = 'Virtual Bytes' AND pc.performance_instance_name NOT IN ('_Total', 'Idle') GROUP BY pc.performance_instance_name ) AS vb ON (vb.process_name = ws.process_name) ORDER BY ws_average_value DESC END; GO GRANT EXECUTE ON [snapshots].[rpt_memory_usage_per_process] TO [mdw_reader] GO -- snapshots.rpt_wait_stats_by_category_by_snapshot -- Returns deltas of wait stats with one snapshot interval and aggregated by category -- Parameters: -- @instance_name - SQL Server instance name -- @start_time - (Optional) time window start (UTC) -- @end_time - time window end (UTC) -- @time_window_size - Number of intervals in the time window (provide if @start_time is NULL) -- @time_interval_min - Number of minutes in each interval (provide if @start_time is NULL) -- @category_name - (Optional) Name of wait category to filter on (all categories if NULL) -- IF (NOT OBJECT_ID(N'snapshots.rpt_wait_stats_by_category_by_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_wait_stats_by_category_by_snapshot] ...' DROP PROCEDURE [snapshots].[rpt_wait_stats_by_category_by_snapshot] END GO PRINT 'Creating procedure [snapshots].[rpt_wait_stats_by_category_by_snapshot] ...' GO CREATE PROCEDURE [snapshots].[rpt_wait_stats_by_category_by_snapshot] @instance_name sysname, @start_time datetime = NULL, @end_time datetime = NULL, @time_window_size smallint = NULL, @time_interval_min smallint = NULL, @category_name nvarchar(20) = NULL AS BEGIN SET NOCOUNT ON; -- Convert snapshot_time (datetimeoffset) to a UTC datetime IF (@end_time IS NULL) SET @end_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MAX(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); IF (@start_time IS NULL) BEGIN -- If time_window_size and time_interval_min are set use them -- to determine the start time -- Otherwise use the earliest available snapshot_time IF @time_window_size IS NOT NULL AND @time_interval_min IS NOT NULL BEGIN SET @start_time = DATEADD(minute, @time_window_size * @time_interval_min * -1.0, @end_time); END ELSE BEGIN -- Convert min snapshot_time (datetimeoffset(7)) to a UTC datetime SET @start_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MIN(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); END END DECLARE @end_snapshot_time_id int; SELECT @end_snapshot_time_id = MAX(snapshot_time_id) FROM core.snapshots WHERE snapshot_time <= @end_time; DECLARE @start_snapshot_time_id int; SELECT @start_snapshot_time_id = MIN(snapshot_time_id) FROM core.snapshots WHERE snapshot_time >= @start_time; -- If the selected time window is > 1 hour, we'll chart wait times at 15 minute intervals. If the selected -- time window is < 1 hour, we'll use 5 minute intervals. DECLARE @group_interval_min int IF DATEDIFF (minute, @start_time, @end_time) > 60 BEGIN SET @group_interval_min = 15 END ELSE BEGIN SET @group_interval_min = 5 END; -- Get wait times by waittype (plus CPU time, modeled as a waittype) WITH wait_times AS ( SELECT s.snapshot_id, s.snapshot_time_id, s.snapshot_time, DENSE_RANK() OVER (ORDER BY ws.collection_time) AS [rank], ws.collection_time, wt.category_name, ws.wait_type, ws.waiting_tasks_count, ISNULL (ws.signal_wait_time_ms, 0) AS signal_wait_time_ms, ISNULL (ws.wait_time_ms, 0) - ISNULL (ws.signal_wait_time_ms, 0) AS wait_time_ms, ISNULL (ws.wait_time_ms, 0) AS wait_time_ms_cumulative FROM snapshots.os_wait_stats AS ws INNER JOIN core.wait_types_categorized wt ON wt.wait_type = ws.wait_type INNER JOIN core.snapshots s ON ws.snapshot_id = s.snapshot_id WHERE s.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id AND s.instance_name = @instance_name AND wt.category_name = ISNULL (@category_name, wt.category_name) AND wt.ignore != 1 AND ws.collection_time IN ( SELECT MAX(collection_time) FROM snapshots.os_wait_stats ws2 WHERE ws2.collection_time BETWEEN @start_time AND @end_time GROUP BY DATEDIFF (minute, '19000101', ws2.collection_time) / @group_interval_min ) ) -- Get resource wait stats for this snapshot_time_id interval -- We must convert all datetimeoffset values to UTC datetime values before returning to Reporting Services SELECT CONVERT (datetime, SWITCHOFFSET (CAST (w2.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, CONVERT (datetime, SWITCHOFFSET (CAST (w2.snapshot_time AS datetimeoffset(7)), '+00:00')) AS snapshot_time, w2.snapshot_time_id, CONVERT (datetime, SWITCHOFFSET (CAST (w1.collection_time AS datetimeoffset(7)), '+00:00')) AS interval_start, CONVERT (datetime, SWITCHOFFSET (CAST (w2.collection_time AS datetimeoffset(7)), '+00:00')) AS interval_end, w2.category_name, w2.wait_type, -- All wait stats will be reset to zero by a service cycle, which will cause -- (snapshot2waittime-snapshot1waittime) calculations to produce an incorrect -- negative wait time for the interval. Detect this and avoid calculating -- negative wait time/wait count/signal time deltas. CASE WHEN (w2.waiting_tasks_count - w1.waiting_tasks_count) < 0 THEN w2.waiting_tasks_count ELSE (w2.waiting_tasks_count - w1.waiting_tasks_count) END AS waiting_tasks_count_delta, CASE WHEN (w2.wait_time_ms - w1.wait_time_ms) < 0 THEN w2.wait_time_ms ELSE (w2.wait_time_ms - w1.wait_time_ms) END / CAST (DATEDIFF (second, w1.collection_time, w2.collection_time) AS decimal) AS resource_wait_time_delta, CASE WHEN (w2.signal_wait_time_ms - w1.signal_wait_time_ms) < 0 THEN w2.signal_wait_time_ms ELSE (w2.signal_wait_time_ms - w1.signal_wait_time_ms) END / CAST (DATEDIFF (second, w1.collection_time, w2.collection_time) AS decimal) AS resource_signal_time_delta, DATEDIFF (second, w1.collection_time, w2.collection_time) AS interval_sec, w2.wait_time_ms_cumulative -- Self-join - w1 represents wait stats at the beginning of the sample interval, while w2 -- shows the wait stats at the end of the interval. FROM wait_times AS w1 INNER JOIN wait_times AS w2 ON w1.wait_type = w2.wait_type AND w1.[rank] = w2.[rank]-1 UNION ALL -- Treat sum of all signal waits as CPU "wait time" SELECT CONVERT (datetime, SWITCHOFFSET (CAST (w2.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, CONVERT (datetime, SWITCHOFFSET (CAST (w2.snapshot_time AS datetimeoffset(7)), '+00:00')) AS snapshot_time, w2.snapshot_time_id, CONVERT (datetime, SWITCHOFFSET (CAST (w1.collection_time AS datetimeoffset(7)), '+00:00')) AS interval_start, CONVERT (datetime, SWITCHOFFSET (CAST (w2.collection_time AS datetimeoffset(7)), '+00:00')) AS interval_end, 'CPU' AS category_name, 'CPU (Signal Wait)' AS wait_type, 0 AS waiting_tasks_count_delta, -- Handle wait stats resets CASE WHEN (SUM (w2.signal_wait_time_ms) - SUM (w1.signal_wait_time_ms)) < 0 THEN SUM (w2.signal_wait_time_ms) ELSE (SUM (w2.signal_wait_time_ms) - SUM (w1.signal_wait_time_ms)) END / CAST (DATEDIFF (second, w1.collection_time, w2.collection_time) AS decimal) AS resource_wait_time_delta, 0 AS resource_signal_time_delta, DATEDIFF (second, w1.collection_time, w2.collection_time) AS interval_sec, w2.wait_time_ms_cumulative FROM wait_times AS w1 INNER JOIN wait_times AS w2 ON w1.wait_type = w2.wait_type AND w1.[rank] = w2.[rank]-1 -- Only return CPU stats if we were told to return the 'CPU' category or all categories WHERE (@category_name IS NULL OR @category_name = 'CPU') GROUP BY w1.collection_time, w1.collection_time, w2.collection_time, w2.snapshot_time, w2.snapshot_time_id, w2.wait_time_ms_cumulative UNION ALL -- Get actual used CPU time from perfmon data (average across the whole snapshot_time_id interval, -- and use this average for each sample time in this interval). Note that the "% Processor Time" -- counter in the "Process" object can exceed 100% (for example, it can range from 0-800% on an -- 8 CPU server). SELECT CONVERT (datetime, SWITCHOFFSET (CAST (w2.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, CONVERT (datetime, SWITCHOFFSET (CAST (w2.snapshot_time AS datetimeoffset(7)), '+00:00')) AS snapshot_time, w2.snapshot_time_id, CONVERT (datetime, SWITCHOFFSET (CAST (w1.collection_time AS datetimeoffset(7)), '+00:00')) AS interval_start, CONVERT (datetime, SWITCHOFFSET (CAST (w2.collection_time AS datetimeoffset(7)), '+00:00')) AS interval_end, 'CPU' AS category_name, 'CPU (Consumed)' AS wait_type, 0 AS waiting_tasks_count_delta, -- Get sqlservr %CPU usage for the perfmon sample that immediately precedes -- each wait stats sample. Multiply by 10 to convert "% CPU" to "ms CPU/sec". 10 * ( SELECT TOP 1 formatted_value FROM snapshots.performance_counters AS pc INNER JOIN core.snapshots s ON pc.snapshot_id = s.snapshot_id WHERE pc.performance_object_name = 'Process' AND pc.performance_counter_name = '% Processor Time' AND pc.performance_instance_name = 'sqlservr' AND s.snapshot_time_id <= @end_snapshot_time_id AND s.instance_name = @instance_name AND pc.snapshot_id < w2.snapshot_id ORDER BY pc.snapshot_id DESC ) AS resource_wait_time_delta, 0 AS resource_signal_time_delta, DATEDIFF (second, w1.collection_time, w2.collection_time) AS interval_sec, NULL FROM wait_times AS w1 INNER JOIN wait_times AS w2 ON w1.wait_type = w2.wait_type AND w1.[rank] = w2.[rank]-1 -- Only return CPU stats if we weren't passed in a specific wait category WHERE (@category_name IS NULL OR @category_name = 'CPU') GROUP BY w1.collection_time, w2.collection_time, w2.snapshot_time, w2.snapshot_time_id, w2.snapshot_id ORDER BY collection_time, category_name, wait_type -- These trace flags are necessary for a good plan, due to the join on ascending core.snapshot PK OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390); END GO GRANT EXECUTE ON [snapshots].[rpt_wait_stats_by_category_by_snapshot] TO [mdw_reader] GO -- -- snapshots.rpt_cpu_usage -- Returns values for System CPU usage and SQL Server CPU usage collected through perf counters. -- Parameters: -- @instance_name - SQL Server instance name -- @start_time - (Optional) time window start (UTC) -- @end_time - time window end (UTC) -- @time_window_size - Number of intervals in the time window (provide if @start_time is NULL) -- @time_interval_min - Number of minutes in each interval (provide if @start_time is NULL) -- IF (NOT OBJECT_ID(N'snapshots.rpt_cpu_usage', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_cpu_usage] ...' DROP PROCEDURE [snapshots].[rpt_cpu_usage] END GO PRINT 'Creating procedure [snapshots].[rpt_cpu_usage] ...' GO CREATE PROCEDURE [snapshots].[rpt_cpu_usage] @instance_name sysname, @start_time datetime = NULL, @end_time datetime = NULL, @time_window_size smallint = NULL, @time_interval_min smallint = NULL AS BEGIN SET NOCOUNT ON; -- Convert snapshot_time (datetimeoffset(7)) to a UTC datetime IF (@end_time IS NULL) SET @end_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MAX(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); IF (@start_time IS NULL) BEGIN -- If time_window_size and time_interval_min are set use them -- to determine the start time -- Otherwise use the earliest available snapshot_time IF @time_window_size IS NOT NULL AND @time_interval_min IS NOT NULL BEGIN SET @start_time = DATEADD(minute, @time_window_size * @time_interval_min * -1.0, @end_time); END ELSE BEGIN -- Convert min snapshot_time (datetimeoffset(7)) to a UTC datetime SET @start_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MIN(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); END END DECLARE @end_snapshot_time_id int; SELECT @end_snapshot_time_id = MAX(snapshot_time_id) FROM core.snapshots WHERE snapshot_time <= @end_time; DECLARE @start_snapshot_time_id int; SELECT @start_snapshot_time_id = MIN(snapshot_time_id) FROM core.snapshots WHERE snapshot_time >= @start_time; -- Determine the CPU count on the target system by querying the number of "Processor" -- counter instances we captured in a perfmon sample that was captured around the same -- time. DECLARE @cpu_count smallint SELECT @cpu_count = COUNT (DISTINCT pc.performance_instance_name) FROM snapshots.performance_counters AS pc INNER JOIN core.snapshots s ON s.snapshot_id = pc.snapshot_id WHERE pc.performance_object_name = 'Processor' AND pc.performance_counter_name = '% Processor Time' AND pc.performance_instance_name != '_Total' AND s.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id AND s.instance_name = @instance_name AND pc.collection_time = (SELECT TOP 1 collection_time FROM snapshots.performance_counter_values pcv2 WHERE pcv2.snapshot_id = s.snapshot_id) SELECT CASE pc.performance_object_name WHEN 'Processor' THEN 'System' ELSE 'SQL Server' END AS series, s.snapshot_time_id, CONVERT (datetime, SWITCHOFFSET (CAST (s.snapshot_time AS datetimeoffset(7)), '+00:00')) AS snapshot_time, -- Processor time on an eight proc system is 0-800% for the Process object, -- but 0-100% for the Processor object CASE pc.performance_object_name WHEN 'Processor' THEN pc.formatted_value ELSE pc.formatted_value / @cpu_count END AS formatted_value FROM snapshots.performance_counters pc JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id AND ( (pc.performance_object_name = 'Processor' AND pc.performance_counter_name = '% Processor Time' AND pc.performance_instance_name = '_Total') OR (pc.performance_object_name = 'Process' AND pc.performance_counter_name = '% Processor Time' AND pc.performance_instance_name = 'sqlservr') ) ORDER BY pc.collection_time, series -- These trace flags are necessary for a good plan, due to the join on ascending PK w/range filter OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390) END; GO GRANT EXECUTE ON [snapshots].[rpt_cpu_usage] TO [mdw_reader] GO -- -- snapshots.rpt_mem_usage -- Returns values for System memory usage and SQL Server memory usage collected through perf counters. -- Parameters: -- @instance_name - SQL Server instance name -- @start_time - (Optional) time window start (UTC) -- @end_time - time window end (UTC) -- @time_window_size - Number of intervals in the time window (provide if @start_time is NULL) -- @time_interval_min - Number of minutes in each interval (provide if @start_time is NULL) -- IF (NOT OBJECT_ID(N'snapshots.rpt_mem_usage', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_mem_usage] ...' DROP PROCEDURE [snapshots].[rpt_mem_usage] END GO PRINT 'Creating procedure [snapshots].[rpt_mem_usage] ...' GO CREATE PROCEDURE [snapshots].[rpt_mem_usage] @instance_name sysname, @start_time datetime = NULL, @end_time datetime = NULL, @time_window_size smallint = NULL, @time_interval_min smallint = NULL AS BEGIN SET NOCOUNT ON; -- Convert snapshot_time (datetimeoffset) to a UTC datetime IF (@end_time IS NULL) SET @end_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MAX(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); IF (@start_time IS NULL) BEGIN -- If time_window_size and time_interval_min are set use them -- to determine the start time -- Otherwise use the earliest available snapshot_time IF @time_window_size IS NOT NULL AND @time_interval_min IS NOT NULL BEGIN SET @start_time = DATEADD(minute, @time_window_size * @time_interval_min * -1.0, @end_time); END ELSE BEGIN -- Convert min snapshot_time (datetimeoffset) to a UTC datetime SET @start_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MIN(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); END END DECLARE @end_snapshot_time_id int; SELECT @end_snapshot_time_id = MAX(snapshot_time_id) FROM core.snapshots WHERE snapshot_time <= @end_time; DECLARE @start_snapshot_time_id int; SELECT @start_snapshot_time_id = MIN(snapshot_time_id) FROM core.snapshots WHERE snapshot_time >= @start_time; SELECT N'System' AS series, s.snapshot_time_id, CONVERT (datetime, SWITCHOFFSET (CAST (s.snapshot_time AS datetimeoffset(7)), '+00:00')) AS snapshot_time, AVG(pc.formatted_value/(1024*1024)) AS formatted_value FROM snapshots.performance_counters pc JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id AND (pc.performance_object_name = 'Process' AND pc.performance_counter_name = 'Working Set' AND pc.performance_instance_name = '_Total') -- uncomment when defect 109313 is fixed --OR pc.path LIKE N'\Memory\Pool Nonpaged Bytes' --OR pc.path LIKE N'\Memory\Cache Bytes') GROUP BY s.snapshot_time_id, s.snapshot_time UNION ALL SELECT N'SQL Server' AS series, s.snapshot_time_id, CONVERT (datetime, SWITCHOFFSET (CAST (s.snapshot_time AS datetimeoffset(7)), '+00:00')) AS snapshot_time, AVG(pc.formatted_value/(1024*1024)) AS formatted_value FROM snapshots.performance_counters pc JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id AND (pc.performance_object_name = 'Process' AND pc.performance_counter_name = 'Working Set' AND pc.performance_instance_name = 'sqlservr') GROUP BY s.snapshot_time_id, s.snapshot_time ORDER BY snapshot_time_id, series -- These trace flags are necessary for a good plan, due to the join on ascending PK w/range filter OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390) END; GO GRANT EXECUTE ON [snapshots].[rpt_mem_usage] TO [mdw_reader] GO -- -- snapshots.rpt_io_usage -- Returns values for System Disk IO usage and SQL Server Disk IO usage collected through perf counters. -- Parameters: -- @instance_name - SQL Server instance name -- @start_time - (Optional) time window start (UTC) -- @end_time - time window end (UTC) -- @time_window_size - Number of intervals in the time window (provide if @start_time is NULL) -- @time_interval_min - Number of minutes in each interval (provide if @start_time is NULL) -- IF (NOT OBJECT_ID(N'snapshots.rpt_io_usage', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_io_usage] ...' DROP PROCEDURE [snapshots].[rpt_io_usage] END GO PRINT 'Creating procedure [snapshots].[rpt_io_usage] ...' GO CREATE PROCEDURE [snapshots].[rpt_io_usage] @instance_name sysname, @start_time datetime = NULL, @end_time datetime = NULL, @time_window_size smallint = NULL, @time_interval_min smallint = NULL AS BEGIN SET NOCOUNT ON; -- Convert snapshot_time (datetimeoffset) to a UTC datetime IF (@end_time IS NULL) SET @end_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MAX(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); IF (@start_time IS NULL) BEGIN -- If time_window_size and time_interval_min are set use them -- to determine the start time -- Otherwise use the earliest available snapshot_time IF @time_window_size IS NOT NULL AND @time_interval_min IS NOT NULL BEGIN SET @start_time = DATEADD(minute, @time_window_size * @time_interval_min * -1.0, @end_time); END ELSE BEGIN -- Convert min snapshot_time (datetimeoffset) to a UTC datetime SET @start_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MIN(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); END END DECLARE @end_snapshot_time_id int; SELECT @end_snapshot_time_id = MAX(snapshot_time_id) FROM core.snapshots WHERE snapshot_time <= @end_time; DECLARE @start_snapshot_time_id int; SELECT @start_snapshot_time_id = MIN(snapshot_time_id) FROM core.snapshots WHERE snapshot_time >= @start_time; SELECT N'System' AS series, s.snapshot_time_id, CONVERT (datetime, SWITCHOFFSET (CAST (s.snapshot_time AS datetimeoffset(7)), '+00:00')) AS snapshot_time, AVG(pc.formatted_value/(1024)) AS formatted_value FROM snapshots.performance_counters pc JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id AND (pc.performance_object_name = 'Process' AND pc.performance_counter_name = 'IO Read Bytes/sec' AND pc.performance_instance_name = '_Total') -- uncomment when defect 109313 is fixed --OR pc.path LIKE N'\Process(!_Total)\IO Write Bytes/sec' ESCAPE N'!') GROUP BY s.snapshot_time_id, s.snapshot_time UNION ALL SELECT N'SQL Server' AS series, s.snapshot_time_id, CONVERT (datetime, SWITCHOFFSET (CAST (s.snapshot_time AS datetimeoffset(7)), '+00:00')) AS snapshot_time, AVG(pc.formatted_value/(1024)) AS formatted_value FROM snapshots.performance_counters pc JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id AND (pc.performance_object_name = 'Process' AND pc.performance_counter_name = 'IO Read Bytes/sec' AND pc.performance_instance_name = 'sqlservr') -- uncomment when defect 109313 is fixed --OR pc.path LIKE N'\Process(sqlservr)\IO Write Bytes/sec') GROUP BY s.snapshot_time_id, s.snapshot_time ORDER BY snapshot_time_id, series -- These trace flags are necessary for a good plan, due to the join on ascending PK w/range filter OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390) END; GO GRANT EXECUTE ON [snapshots].[rpt_io_usage] TO [mdw_reader] GO -- -- snapshots.rpt_network_usage -- Returns values for System network usage and SQL Server network usage collected through perf counters. -- Parameters: -- @instance_name - SQL Server instance name -- @start_time - (Optional) time window start (UTC) -- @end_time - time window end (UTC) -- @time_window_size - Number of intervals in the time window (provide if @start_time is NULL) -- @time_interval_min - Number of minutes in each interval (provide if @start_time is NULL) -- IF (NOT OBJECT_ID(N'snapshots.rpt_network_usage', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_network_usage] ...' DROP PROCEDURE [snapshots].[rpt_network_usage] END GO PRINT 'Creating procedure [snapshots].[rpt_network_usage] ...' GO CREATE PROCEDURE [snapshots].[rpt_network_usage] @instance_name sysname, @start_time datetime = NULL, @end_time datetime = NULL, @time_window_size smallint = NULL, @time_interval_min smallint = NULL AS BEGIN SET NOCOUNT ON; -- Convert snapshot_time (datetimeoffset) to a UTC datetime IF (@end_time IS NULL) SET @end_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MAX(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); IF (@start_time IS NULL) BEGIN -- If time_window_size and time_interval_min are set use them -- to determine the start time -- Otherwise use the earliest available snapshot_time IF @time_window_size IS NOT NULL AND @time_interval_min IS NOT NULL BEGIN SET @start_time = DATEADD(minute, @time_window_size * @time_interval_min * -1.0, @end_time); END ELSE BEGIN -- Convert min snapshot_time (datetimeoffset) to a UTC datetime SET @start_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MIN(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); END END DECLARE @end_snapshot_time_id int; SELECT @end_snapshot_time_id = MAX(snapshot_time_id) FROM core.snapshots WHERE snapshot_time <= @end_time; DECLARE @start_snapshot_time_id int; SELECT @start_snapshot_time_id = MIN(snapshot_time_id) FROM core.snapshots WHERE snapshot_time >= @start_time; SELECT N'System' AS series, s.snapshot_time_id, CONVERT (datetime, SWITCHOFFSET (CAST (s.snapshot_time AS datetimeoffset(7)), '+00:00')) AS snapshot_time, AVG(pc.formatted_value/(1024)) AS formatted_value FROM snapshots.performance_counters pc JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id -- all instances AND (pc.performance_object_name = 'Network Interface' AND pc.performance_counter_name = 'Bytes Total/sec') GROUP BY s.snapshot_time_id, s.snapshot_time ORDER BY snapshot_time_id -- These trace flags are necessary for a good plan, due to the join on ascending PK w/range filter OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390) END; GO GRANT EXECUTE ON [snapshots].[rpt_network_usage] TO [mdw_reader] GO -- -- snapshots.rpt_wait_stats_one_snapshot -- Returns summary of wait stats grouped per wait category for one snapshot -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- @category_name - (Optional) Name of wait category to filter on (all categories if NULL) -- IF (NOT OBJECT_ID(N'snapshots.rpt_wait_stats_one_snapshot', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_wait_stats_one_snapshot] ...' DROP PROCEDURE [snapshots].[rpt_wait_stats_one_snapshot] END GO PRINT 'Creating procedure [snapshots].[rpt_wait_stats_one_snapshot] ...' GO CREATE PROCEDURE [snapshots].[rpt_wait_stats_one_snapshot] @instance_name sysname, @snapshot_time_id int, @category_name nvarchar(20) = NULL AS BEGIN SET NOCOUNT ON; -- Find the snapshot_id of the last wait stats data set in the following -- snapshot_time_id interval DECLARE @current_snapshot_id int SELECT TOP 1 @current_snapshot_id = s.snapshot_id FROM core.snapshots AS s INNER JOIN snapshots.os_wait_stats ws ON s.snapshot_id = ws.snapshot_id WHERE s.instance_name = @instance_name AND s.snapshot_time_id > @snapshot_time_id ORDER BY s.snapshot_time_id ASC, s.snapshot_id ASC, ws.collection_time ASC; -- Find the snapshot_id of the last wait stats data set in the preceding -- snapshot_time_id interval DECLARE @previous_snapshot_id int SELECT TOP 1 @previous_snapshot_id = ISNULL (s.snapshot_id, 0) FROM core.snapshots AS s INNER JOIN snapshots.os_wait_stats ws ON s.snapshot_id = ws.snapshot_id WHERE s.instance_name = @instance_name AND s.snapshot_time_id < @snapshot_time_id ORDER BY s.snapshot_time_id DESC, s.snapshot_id DESC, ws.collection_time DESC; -- Get wait times by waittype (plus CPU time, modeled as a waittype) WITH wait_times AS ( SELECT s.snapshot_id, s.snapshot_time_id, DENSE_RANK() OVER (ORDER BY collection_time) AS [rank], ws.collection_time, wt.category_name, ws.wait_type, ws.waiting_tasks_count, ISNULL (ws.signal_wait_time_ms, 0) AS signal_wait_time_ms, ISNULL (ws.wait_time_ms - ISNULL (ws.signal_wait_time_ms, 0), 0) AS wait_time_ms, ws.wait_time_ms AS wait_time_ms_cumulative FROM snapshots.os_wait_stats AS ws INNER JOIN core.wait_types_categorized wt ON wt.wait_type = ws.wait_type INNER JOIN core.snapshots s ON ws.snapshot_id = s.snapshot_id WHERE s.snapshot_id BETWEEN @previous_snapshot_id AND @current_snapshot_id AND s.instance_name = @instance_name AND (wt.category_name = ISNULL (@category_name, wt.category_name)) AND wt.ignore != 1 ) -- Get resource wait stats for this snapshot_time_id interval SELECT CONVERT (datetime, SWITCHOFFSET (CAST (w2.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, w2.category_name, w2.wait_type, -- All wait stats will be reset to zero by a service cycle, which will cause -- (snapshot2waittime-snapshot1waittime) calculations to produce an incorrect -- negative wait time for the interval. Detect this and avoid calculating -- negative wait time/wait count/signal time deltas. CASE WHEN (w2.waiting_tasks_count - w1.waiting_tasks_count) < 0 THEN w2.waiting_tasks_count ELSE (w2.waiting_tasks_count - w1.waiting_tasks_count) END AS waiting_tasks_count_delta, CASE WHEN (w2.wait_time_ms - w1.wait_time_ms) < 0 THEN w2.wait_time_ms ELSE (w2.wait_time_ms - w1.wait_time_ms) END / CAST (DATEDIFF (second, w1.collection_time, w2.collection_time) AS decimal) AS resource_wait_time_delta, CASE WHEN (w2.signal_wait_time_ms - w1.signal_wait_time_ms) < 0 THEN w2.signal_wait_time_ms ELSE (w2.signal_wait_time_ms - w1.signal_wait_time_ms) END / CAST (DATEDIFF (second, w1.collection_time, w2.collection_time) AS decimal) AS resource_signal_time_delta, DATEDIFF (second, w1.collection_time, w2.collection_time) AS interval_sec, w2.wait_time_ms_cumulative -- Self-join - w1 represents wait stats at the beginning of the sample interval, while w2 -- shows the wait stats at the end of the interval. FROM wait_times AS w1 INNER JOIN wait_times AS w2 ON w1.wait_type = w2.wait_type AND w1.[rank] = w2.[rank]-1 UNION ALL -- Treat sum of all signal waits as CPU "wait time" SELECT CONVERT (datetime, SWITCHOFFSET (CAST (w2.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, 'CPU' AS category_name, 'CPU (Signal Wait)' AS wait_type, 0 AS waiting_tasks_count_delta, -- Handle wait stats resets CASE WHEN (SUM (w2.signal_wait_time_ms) - SUM (w1.signal_wait_time_ms)) < 0 THEN SUM (w2.signal_wait_time_ms) ELSE (SUM (w2.signal_wait_time_ms) - SUM (w1.signal_wait_time_ms)) END / CAST (DATEDIFF (second, w1.collection_time, w2.collection_time) AS decimal) AS resource_wait_time_delta, 0 AS resource_signal_time_delta, DATEDIFF (second, w1.collection_time, w2.collection_time) AS interval_sec, w2.wait_time_ms_cumulative FROM wait_times AS w1 INNER JOIN wait_times AS w2 ON w1.wait_type = w2.wait_type AND w1.[rank] = w2.[rank]-1 -- Only return CPU stats if we weren't passed in a specific wait category WHERE (@category_name IS NULL OR @category_name = 'CPU') GROUP BY w1.collection_time, w2.collection_time, w2.wait_time_ms_cumulative UNION ALL -- Get actual used CPU time from perfmon data (average across the whole snapshot_time_id interval, -- and use this average for each sample time in this interval). Note that the "% Processor Time" -- counter in the "Process" object can exceed 100% (for example, it can range from 0-800% on an -- 8 CPU server). SELECT CONVERT (datetime, SWITCHOFFSET (CAST (w2.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, 'CPU' AS category_name, 'CPU (Consumed)' AS wait_type, 0 AS waiting_tasks_count_delta, -- Get sqlservr %CPU usage for the perfmon sample that immediately precedes -- each wait stats sample. Multiply by 10 to convert "% CPU" to "ms CPU/sec". 10 * ( SELECT TOP 1 formatted_value FROM snapshots.performance_counters AS pc INNER JOIN core.snapshots s ON pc.snapshot_id = s.snapshot_id WHERE pc.performance_object_name = 'Process' AND pc.performance_counter_name = '% Processor Time' AND pc.performance_instance_name = 'sqlservr' AND s.snapshot_id < @current_snapshot_id AND s.instance_name = @instance_name AND pc.snapshot_id < w2.snapshot_id ORDER BY pc.snapshot_id DESC ) AS resource_wait_time_delta, 0 AS resource_signal_time_delta, DATEDIFF (second, w1.collection_time, w2.collection_time) AS interval_sec, NULL FROM wait_times AS w1 INNER JOIN wait_times AS w2 ON w1.wait_type = w2.wait_type AND w1.[rank] = w2.[rank]-1 -- Only return CPU stats if we weren't passed in a specific wait category WHERE (@category_name IS NULL OR @category_name = 'CPU') GROUP BY w1.collection_time, w2.collection_time, w2.snapshot_id ORDER BY collection_time, category_name, wait_type -- These trace flags are necessary for a good plan, due to the join on ascending core.snapshot PK OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390); END; GO GRANT EXECUTE ON [snapshots].[rpt_wait_stats_one_snapshot] TO [mdw_reader] GO -- -- snapshots.rpt_server_activity -- Returns values for various perf counters indicating server's activity -- Parameters: -- @instance_name - SQL Server instance name -- @start_time - (Optional) time window start (UTC) -- @end_time - time window end (UTC) -- @time_window_size - Number of intervals in the time window (provide if @start_time is NULL) -- @time_interval_min - Number of minutes in each interval (provide if @start_time is NULL) -- IF (NOT OBJECT_ID(N'snapshots.rpt_server_activity', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_server_activity] ...' DROP PROCEDURE [snapshots].[rpt_server_activity] END GO PRINT 'Creating procedure [snapshots].[rpt_server_activity] ...' GO CREATE PROCEDURE [snapshots].[rpt_server_activity] @instance_name sysname, @start_time datetime = NULL, @end_time datetime = NULL, @time_window_size smallint = NULL, @time_interval_min smallint = NULL AS BEGIN SET NOCOUNT ON; -- Convert snapshot_time (datetimeoffset) to a UTC datetime IF (@end_time IS NULL) SET @end_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MAX(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); IF (@start_time IS NULL) BEGIN -- If time_window_size and time_interval_min are set use them -- to determine the start time -- Otherwise use the earliest available snapshot_time IF @time_window_size IS NOT NULL AND @time_interval_min IS NOT NULL BEGIN SET @start_time = DATEADD(minute, @time_window_size * @time_interval_min * -1.0, @end_time); END ELSE BEGIN -- Convert min snapshot_time (datetimeoffset) to a UTC datetime SET @start_time = CONVERT (datetime, SWITCHOFFSET (CAST ((SELECT MIN(snapshot_time) FROM core.snapshots) AS datetimeoffset(7)), '+00:00')); END END DECLARE @end_snapshot_time_id int; SELECT @end_snapshot_time_id = MAX(snapshot_time_id) FROM core.snapshots WHERE snapshot_time <= @end_time; DECLARE @start_snapshot_time_id int; SELECT @start_snapshot_time_id = MIN(snapshot_time_id) FROM core.snapshots WHERE snapshot_time >= @start_time; SELECT SUBSTRING(pc.path, CHARINDEX(N'\', pc.path, 2)+1, LEN(pc.path) - CHARINDEX(N'\', pc.path, 2)) as series, s.snapshot_time_id, CONVERT (datetime, SWITCHOFFSET (CAST (s.snapshot_time AS datetimeoffset(7)), '+00:00')) AS snapshot_time, CONVERT (datetime, SWITCHOFFSET (CAST (pc.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, pc.formatted_value FROM snapshots.performance_counters pc JOIN core.snapshots s ON (s.snapshot_id = pc.snapshot_id) WHERE s.instance_name = @instance_name AND s.snapshot_time_id BETWEEN @start_snapshot_time_id AND @end_snapshot_time_id AND (pc.performance_object_name LIKE '%SQL%:General Statistics' OR pc.performance_object_name LIKE '%SQL%:SQL Statistics') AND pc.performance_counter_name IN ('Logins/sec', 'Logouts/sec', 'Batch Requests/sec', 'Transactions', 'User Connections', 'SQL Compilations/sec', 'SQL Re-Compilations/sec') ORDER BY pc.collection_time, series -- These trace flags are necessary for a good plan, due to the join on ascending PK w/range filter OPTION (QUERYTRACEON 2389, QUERYTRACEON 2390) END; GO GRANT EXECUTE ON [snapshots].[rpt_server_activity] TO [mdw_reader] GO -- -- snapshots.rpt_wait_stats_one_snapshot_one_category -- Returns details of wait stats for one snapshot and one wait category -- Parameters: -- @instance_name - SQL Server instance name -- @snapshot_time_id - time window identifier (snapshot ID from core.snapshots) -- @category_name - Name of wait category to filter on -- IF (NOT OBJECT_ID(N'snapshots.rpt_wait_stats_one_snapshot_one_category', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_wait_stats_one_snapshot_one_category] ...' DROP PROCEDURE [snapshots].[rpt_wait_stats_one_snapshot_one_category] END GO PRINT 'Creating procedure [snapshots].[rpt_wait_stats_one_snapshot_one_category] ...' GO CREATE PROCEDURE [snapshots].[rpt_wait_stats_one_snapshot_one_category] @instance_name sysname, @snapshot_time_id int, @category_name nvarchar(20) AS BEGIN SET NOCOUNT ON; -- Use CTE to select first all raw data that we neeed -- For each snapshot append a [rank] column, which later will be used to do the self-join WITH wait_times AS ( -- First part gets resource wait times for each wait_type, -- calculated as wait_time_ms - signal_wait_time_ms SELECT DENSE_RANK() OVER (ORDER BY ws1.collection_time) AS [rank], s.snapshot_time_id, ws1.collection_time, ct.category_name, ev.wait_type, ws1.waiting_tasks_count, ws1.wait_time_ms - ws1.signal_wait_time_ms as resource_wait_time_ms FROM core.snapshots s JOIN snapshots.os_wait_stats ws1 on (ws1.snapshot_id = s.snapshot_id) JOIN core.wait_types ev on (ev.wait_type = ws1.wait_type) JOIN core.wait_categories ct on (ct.category_id = ev.category_id) WHERE ct.category_name = @category_name AND s.instance_name = @instance_name AND (s.snapshot_time_id = @snapshot_time_id OR s.snapshot_time_id = @snapshot_time_id - 1) ) -- Do a self-join to calculate deltas between snapshots SELECT CONVERT (datetime, SWITCHOFFSET (CAST (t1.collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, t1.wait_type, (t1.waiting_tasks_count - t2.waiting_tasks_count) AS waiting_tasks_count_delta, (t1.resource_wait_time_ms - ISNULL(t2.resource_wait_time_ms,0))/CAST(DATEDIFF(second, t2.collection_time, t1.collection_time) AS decimal) AS resource_wait_time_delta FROM wait_times t1 LEFT OUTER JOIN wait_times t2 on (t2.rank = t1.rank-1 and t2.wait_type = t1.wait_type) WHERE t1.snapshot_time_id = @snapshot_time_id ORDER BY collection_time, wait_type END; GO GRANT EXECUTE ON [snapshots].[rpt_wait_stats_one_snapshot_one_category] TO [mdw_reader] GO /**********************************************************************/ /* Used by CTP6 and CTP6 Refresh/RC0 (but not RTM): */ /**********************************************************************/ -- -- snapshots.rpt_sql_process_memory -- Returns details of the SQL process' memory usage -- Parameters: -- @ServerName - SQL Server instance name -- @EndTime - End of the user-selected time window (UTC) -- @WindowSize - Number of minutes in the time window -- IF (NOT OBJECT_ID(N'snapshots.rpt_sql_process_memory', 'P') IS NULL) BEGIN PRINT 'Dropping procedure [snapshots].[rpt_sql_process_memory] ...' DROP PROCEDURE [snapshots].[rpt_sql_process_memory] END GO PRINT 'Creating procedure [snapshots].[rpt_sql_process_memory] ...' GO CREATE PROCEDURE [snapshots].[rpt_sql_process_memory] @ServerName sysname, @EndTime datetime = NULL, @WindowSize int AS BEGIN SET NOCOUNT ON; -- Divide our time window up into 40 evenly-sized time intervals, and find the last collection_time within each of these intervals CREATE TABLE #intervals ( interval_time_id int, interval_start_time datetimeoffset(7), interval_end_time datetimeoffset(7), interval_id int, first_collection_time datetimeoffset(7), last_collection_time datetimeoffset(7), first_snapshot_id int, last_snapshot_id int, source_id int, snapshot_id int, collection_time datetimeoffset(7), collection_time_id int ) -- GUID 49268954-... is the Server Activity CS INSERT INTO #intervals EXEC [snapshots].[rpt_interval_collection_times] @ServerName, @EndTime, @WindowSize, 'snapshots.os_process_memory', '49268954-4FD4-4EB6-AA04-CD59D9BB5714', 40, 0 SELECT CONVERT (datetime, SWITCHOFFSET (CAST (collection_time AS datetimeoffset(7)), '+00:00')) AS collection_time, CASE WHEN series = 'virtual_address_space_reserved_kb' THEN 'Virtual Memory Reserved' WHEN series = 'virtual_address_space_committed_kb' THEN 'Virtual Memory Committed' WHEN series = 'physical_memory_in_use_kb' THEN 'Physical Memory In Use' WHEN series = 'process_physical_memory_low' THEN 'Physical Memory Low' WHEN series = 'process_virtual_memory_low' THEN 'Virtual Memory Low' ELSE series END AS series, [value] / 1024 AS [value] -- convert KB to MB FROM ( SELECT pm.collection_time, pm.virtual_address_space_reserved_kb, pm.virtual_address_space_committed_kb, pm.physical_memory_in_use_kb, CONVERT (bigint, pm.process_physical_memory_low) AS process_physical_memory_low, CONVERT (bigint, pm.process_virtual_memory_low) AS process_virtual_memory_low FROM [snapshots].[os_process_memory] AS pm INNER JOIN #intervals AS i ON (i.last_snapshot_id = pm.snapshot_id AND i.last_collection_time = pm.collection_time) ) AS pvt UNPIVOT ( [value] for [series] in (virtual_address_space_reserved_kb, virtual_address_space_committed_kb, physical_memory_in_use_kb) ) AS unpvt END GO GRANT EXECUTE ON [snapshots].[rpt_sql_process_memory] TO [mdw_reader] GO /**********************************************************************/ /* END OF LEGACY OBJECTS */ /**********************************************************************/ /**********************************************************************/ /* OPERATOR CHECK */ /**********************************************************************/ -- This trigger needs to be defined after we have created all our tables, -- otherwise it would interfere with them IF EXISTS (SELECT object_id FROM sys.triggers WHERE name = N'add_operator_check' AND type = N'TR') BEGIN PRINT 'Dropping database trigger [add_operator_check] ...' DROP TRIGGER [add_operator_check] ON DATABASE END GO PRINT 'Creating database trigger [add_operator_check] ...' GO CREATE TRIGGER [add_operator_check] ON DATABASE WITH EXECUTE AS 'mdw_check_operator_admin' FOR CREATE_TABLE AS BEGIN DECLARE @schema_name sysname; DECLARE @table_name sysname; -- Set options required by the rest of the code in this SP. 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 SELECT @schema_name = EVENTDATA().value('(/EVENT_INSTANCE/SchemaName)[1]', 'sysname') IF (@schema_name = N'custom_snapshots') BEGIN SELECT @table_name = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]', 'sysname') -- Dynamically add a constraint on the newly created table -- Table must have the snapshot_id column DECLARE @check_name sysname; SELECT @check_name = N'CHK_check_operator_' + CONVERT(nvarchar(36), NEWID()); DECLARE @sql nvarchar(2000); SELECT @sql = N'ALTER TABLE ' + QUOTENAME(@schema_name) + N'.' + QUOTENAME(@table_name) + N' ADD CONSTRAINT ' + QUOTENAME(@check_name) + ' CHECK (core.fn_check_operator(snapshot_id) = 1);'; EXEC(@sql); -- Dynamically revoke the CONTROL right on the table for mdw_writer -- That way mdw_writer creates the table but cannot remove it or alter it SELECT @sql = N'DENY ALTER ON ' + QUOTENAME(@schema_name) + N'.' + QUOTENAME(@table_name) + N'TO [mdw_writer]'; EXEC(@sql); END END; GO IF EXISTS (SELECT object_id FROM sys.triggers WHERE name = N'deny_drop_table' AND type = N'TR') BEGIN PRINT 'Dropping database trigger [deny_drop_table] ...' DROP TRIGGER [deny_drop_table] ON DATABASE END GO PRINT 'Creating database trigger [deny_drop_table] ...' GO CREATE TRIGGER [deny_drop_table] ON DATABASE FOR DROP_TABLE AS BEGIN -- Security check (role membership) IF (NOT (ISNULL(IS_MEMBER(N'mdw_admin'), 0) = 1) AND NOT (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 1)) BEGIN RAISERROR(14677, 16, -1, 'mdw_admin'); END; END; GO CHECKPOINT GO /**********************************************************************/ /* install system collector types */ /**********************************************************************/ PRINT '' PRINT 'Adding collector types...' GO -- Install TSQL Query collector type EXEC [core].[sp_add_collector_type] @collector_type_uid = '302e93d1-3424-4be7-aa8e-84813ecf2419' GO -- Install SqlTrace collector type EXEC [core].[sp_add_collector_type] @collector_type_uid = '0E218CF8-ECB5-417B-B533-D851C0251271' GO -- Install Performance Counters collector type EXEC [core].[sp_add_collector_type] @collector_type_uid = '294605dd-21de-40b2-b20f-f3e170ea1ec3' -- Install Query Activity collectory type EXEC [core].[sp_add_collector_type] @collector_type_uid = '14AF3C12-38E6-4155-BD29-F33E7966BA23' GO /**********************************************************************/ /* Data Purge job */ /**********************************************************************/ PRINT '' PRINT 'Creating purge data job...' GO BEGIN TRY BEGIN TRANSACTION; DECLARE @job_owner NVARCHAR(256); SELECT @job_owner = SUSER_SNAME(); DECLARE @db_name sysname SELECT @db_name = DB_NAME(); DECLARE @job_name_for_purge sysname SELECT @job_name_for_purge = N'mdw_purge_data_' + QUOTENAME(@db_name) DECLARE @job_id UNIQUEIDENTIFIER; SELECT @job_id = job_id FROM [msdb].[dbo].[sysjobs_view] WHERE [name] = @job_name_for_purge IF @job_id IS NULL BEGIN -- Add job category if it does not exist. On Katmai server this will be already created. IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'Data Collector' AND category_class=1) BEGIN EXEC msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'Data Collector' END -- Add job EXEC msdb.dbo.sp_add_job @job_name=@job_name_for_purge, @enabled=1, @notify_level_eventlog=0, @notify_level_email=0, @notify_level_netsend=0, @notify_level_page=0, @delete_level=0, @description=N'Runs every day and removes data from Management Data Warehouse database that reached its expiration date.', @category_name=N'Data Collector', @owner_login_name=@job_owner, @job_id = @job_id OUTPUT; -- Add job step EXEC msdb.dbo.sp_add_jobstep @job_id=@job_id, @step_name=N'Step 1', @step_id=1, @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'TSQL', @command=N'exec core.sp_purge_data', @database_name=@db_name, @flags=4; -- Set the job step to start EXEC msdb.dbo.sp_update_job @job_id = @job_id, @start_step_id = 1; -- Add schedule EXEC msdb.dbo.sp_add_jobschedule @job_id=@job_id, @name=N'mdw_purge_data_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_time=20000; -- Set the job server EXEC msdb.dbo.sp_add_jobserver @job_id = @job_id, @server_name = N'(local)'; END COMMIT TRANSACTION; END TRY BEGIN CATCH IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION -- Rethrow the error 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 GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO /**********************************************************************/ /* UTILITY SCHEMAS */ /* Utility objects (in MDW) live in one of the following schemas */ /* [Snapshots] - stores the "live" tables (the tables loaded by DC */ /* collection items */ /* [Sysutility_ucp_staging] - contains the views, SPs, functions */ /* that help to transform the "live" table data into */ /* the "cache" tables (see below) */ /* [sysutility_ucp_core] - contains the cleansed tables, and */ /* views, SPs, and functions that represent the guts */ /* of the Utility data warehouse */ /* [sysutility_ucp_misc] - contains other objects that don't fit */ /* the schemas above. This schema currently contains */ /* only two objects (both functions), and should be */ /* eliminated at some point */ /* */ /* In an ideal world, the UCP tables in the [snapshots] schema would */ /* live in the [sysutility_ucp_staging] schema instead. Current DC */ /* limitations do not allow for that */ /* */ /* The objects in the [snapshots] and [sysutility_ucp_staging] schema */ /* should be entirely private to the MDW database. Only objects in */ /* the [sysutility_ucp_core] schema and the [sysutility_ucp_misc] */ /* schemas should be referenced externally - either by objects in MSDB*/ /* or by Utility object model code */ /**********************************************************************/ PRINT '' BEGIN DECLARE @sql nvarchar(128) -- -- Unfortunately, CREATE SCHEMA needs to be the only statement in its -- batch. That's why we use dynamic sql below -- RAISERROR('Create schema [sysutility_ucp_misc]', 0, 1) WITH NOWAIT; IF (SCHEMA_ID('sysutility_ucp_misc') IS NULL) BEGIN SET @sql = 'CREATE SCHEMA [sysutility_ucp_misc]' EXEC sp_executesql @sql END RAISERROR('Create schema [sysutility_ucp_staging]', 0, 1) WITH NOWAIT; IF (SCHEMA_ID('sysutility_ucp_staging') IS NULL) BEGIN SET @sql = 'CREATE SCHEMA [sysutility_ucp_staging]' EXEC sp_executesql @sql END RAISERROR('Create schema [sysutility_ucp_core]', 0, 1) WITH NOWAIT; IF (SCHEMA_ID('sysutility_ucp_core') IS NULL) BEGIN SET @sql = 'CREATE SCHEMA [sysutility_ucp_core]' EXEC sp_executesql @sql END END GO ----------------------------------------------------------------------- -- View sysutility_ucp_misc.utility_objects_internal -- Categorizes all Utility-owned objects in the MDW database. Used, for -- example, to dynamically discover which tables are cache tables in order -- to update stats on them, or to purge data from all tables that play a -- given role. ----------------------------------------------------------------------- IF OBJECT_ID ('sysutility_ucp_misc.utility_objects_internal') IS NOT NULL BEGIN RAISERROR('Dropping view sysutility_ucp_misc.utility_objects_internal', 0, 1) WITH NOWAIT; DROP VIEW sysutility_ucp_misc.utility_objects_internal; END GO RAISERROR('Creating view sysutility_ucp_misc.utility_objects_internal', 0, 1) WITH NOWAIT; GO CREATE VIEW sysutility_ucp_misc.utility_objects_internal AS SELECT SCHEMA_NAME (obj.[schema_id]) AS [object_schema], obj.name AS [object_name], obj.type_desc AS sql_object_type, CAST (prop.value AS varchar(60)) AS utility_object_type FROM sys.extended_properties AS prop INNER JOIN sys.objects AS obj ON prop.major_id = obj.[object_id] WHERE prop.name = 'MS_UtilityObjectType'; GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'MISC', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_misc', @level1type = 'VIEW', @level1name = 'utility_objects_internal'; GO ----------------------------------------------------------------------- -- Function to get max file size using growth type = KB. Parameters include file_size, max_size, growth size (all in KB) -- The algorithm is: -- the max file size = (max_size - ((max_size - file_size) % growth_size)) -- -- max_size - file_size means the remaining available free space. -- because we grow a fixed chunk specified by growth_size, so we need to figure out -- what would the remainder free space after the maximum growth, and then subtract that -- from max_size. ----------------------------------------------------------------------- IF (OBJECT_ID(N'[sysutility_ucp_misc].[fn_get_max_size_available_by_growth_type_kb]') IS NOT NULL) BEGIN RAISERROR('Dropping function [sysutility_ucp_misc].[fn_get_max_size_available_by_growth_type_kb]', 0, 1) WITH NOWAIT; DROP FUNCTION [sysutility_ucp_misc].[fn_get_max_size_available_by_growth_type_kb] END GO RAISERROR('Creating function [sysutility_ucp_misc].[fn_get_max_size_available_by_growth_type_kb]', 0, 1) WITH NOWAIT; GO CREATE FUNCTION [sysutility_ucp_misc].[fn_get_max_size_available_by_growth_type_kb]( @file_size_kb REAL, @max_size_kb REAL, @growth_size_kb REAL) RETURNS REAL AS BEGIN DECLARE @max_size_available_kb REAL; SELECT @max_size_available_kb = @file_size_kb; IF (@growth_size_kb > 0 AND @max_size_kb > @file_size_kb) BEGIN SELECT @max_size_available_kb = (@max_size_kb - CONVERT(REAL, CONVERT(BIGINT, @max_size_kb - @file_size_kb) % CONVERT(BIGINT, @growth_size_kb))) END RETURN @max_size_available_kb END GO ----------------------------------------------------------------------- -- Function to get max file size using growth type = percentage. Parameters include file_size, max_size (in KB) and percentage -- The algorithm is: -- exp = log( (max_size / file_size) / (1 + growth_percent / 100) ) -- the max file size = file_size * (1 + growth_percent / 100) ^ exp ----------------------------------------------------------------------- IF (OBJECT_ID(N'[sysutility_ucp_misc].[fn_get_max_size_available_by_growth_type_percent]') IS NOT NULL) BEGIN RAISERROR('Dropping function [sysutility_ucp_misc].[fn_get_max_size_available_by_growth_type_percent]', 0, 1) WITH NOWAIT; DROP FUNCTION [sysutility_ucp_misc].[fn_get_max_size_available_by_growth_type_percent] END GO RAISERROR('Creating function [sysutility_ucp_misc].[fn_get_max_size_available_by_growth_type_percent]', 0, 1) WITH NOWAIT; GO CREATE FUNCTION [sysutility_ucp_misc].[fn_get_max_size_available_by_growth_type_percent]( @file_size_kb REAL, @max_size_kb REAL, @growth_percent REAL) RETURNS REAL AS BEGIN DECLARE @max_size_available_kb REAL; DECLARE @one_plus_growth_percent REAL; DECLARE @exponent REAL; SELECT @max_size_available_kb = @file_size_kb; SELECT @one_plus_growth_percent = 1 + @growth_percent / 100; IF (@growth_percent > 0 AND @max_size_kb > @file_size_kb) BEGIN SELECT @exponent = FLOOR(LOG10(@max_size_kb / @file_size_kb) / LOG10(@one_plus_growth_percent)); SELECT @max_size_available_kb = @file_size_kb * POWER(@one_plus_growth_percent, @exponent); END RETURN @max_size_available_kb END GO ----------------------------------------------------------------------- -- Function to get max size available depending on the file_size, its mx_size, growth and free_space_on_drive (everything in KB). -- Algorithm used for calculating max size available on the drive: -- -- if no auto grow then return the current file size, done. -- -- assuming auto grow now: -- if max size is configured and it's less than the (current file size + volume free space) -- which means the max size boundary is in effect, then we use the max_size as the boundary. -- if max size is not configured (unlimited growth) or it's already bigger than the remaining size, -- which means the max size boundary is NOT in effect, then we use the (curent file size + volume free space) -- as the boundary. -- -- now we got the boundary size, we need to take a look the growth type: -- if growth type = kb (0), then we simply try to fit as many chunks as possible (need to use % operation here) -- if growth type = percent (1), then it's a little tricky, we need to find the max exp where -- (current file size) * (1 + growth percent) ^ exp <= boundary size. -- log/power operations involved. one we figure out the exp, we can compute the max available size. -- -- NOTE: the return value is -- (the current file size + the max possible additional space the file can grow into) ----------------------------------------------------------------------- IF (OBJECT_ID(N'[sysutility_ucp_misc].[fn_get_max_size_available]') IS NOT NULL) BEGIN RAISERROR('Dropping function [sysutility_ucp_misc].[fn_get_max_size_available]', 0, 1) WITH NOWAIT; DROP FUNCTION [sysutility_ucp_misc].[fn_get_max_size_available] END GO RAISERROR('Creating function [sysutility_ucp_misc].[fn_get_max_size_available]', 0, 1) WITH NOWAIT; GO CREATE FUNCTION [sysutility_ucp_misc].[fn_get_max_size_available]( @file_size_kb REAL, @max_size_kb REAL, @growth REAL, -- growth is KB when type is not percentage, or a whole number percentage when percentage @smo_growth_type SMALLINT, -- @smo_growth_type == 0 is KB growth, == 1 means percentage, or == 99 not supported to grow @free_space_on_drive_kb BIGINT) RETURNS REAL AS BEGIN DECLARE @max_size_available_kb REAL; DECLARE @projected_max_file_size_kb REAL; -- Be conservative and initialize total space to @file_size_kb (assume no autogrow) SELECT @max_size_available_kb = @file_size_kb; -- Let projected size be the current file size + volume free space (assuming no one else is competing and its completely available for this file) SELECT @projected_max_file_size_kb = @file_size_kb + @free_space_on_drive_kb; -- No auto grow, return the configured file size IF (@smo_growth_type = 99) BEGIN SELECT @max_size_available_kb = @file_size_kb; END ELSE BEGIN IF (0 < @max_size_kb AND @max_size_kb < @projected_max_file_size_kb) BEGIN -- if maxsize is configured and it's less than the project space -- then we use the maxsize as the growth boundary. SELECT @max_size_available_kb = CASE WHEN (@smo_growth_type = 1) -- percent growth THEN sysutility_ucp_misc.fn_get_max_size_available_by_growth_type_percent(@file_size_kb, @max_size_kb, @growth) WHEN (@smo_growth_type = 0) -- KB growth THEN sysutility_ucp_misc.fn_get_max_size_available_by_growth_type_kb(@file_size_kb, @max_size_kb, @growth) ELSE @max_size_kb END END ELSE BEGIN -- either maxsize is not configured, in this case we use the project space -- or maxsize is bigger than the project space, and we suse the project space as well. SELECT @max_size_available_kb = CASE WHEN (@smo_growth_type = 1) -- percent growth THEN sysutility_ucp_misc.fn_get_max_size_available_by_growth_type_percent(@file_size_kb, @projected_max_file_size_kb, @growth) WHEN (@smo_growth_type = 0) -- KB growth THEN sysutility_ucp_misc.fn_get_max_size_available_by_growth_type_kb(@file_size_kb, @projected_max_file_size_kb, @growth) ELSE @projected_max_file_size_kb END END END -- VSTS 351411 -- In SQL2008 and SQL2008 R2, unfortunately the support for file stream file -- in SMO and dmv aren't complete: it always return 0 for everything including -- @file_size_kb, @max_size_kb, growth, thus walking through the code path above, -- we'll return 0, then the our data file storage utilization property use this -- value (as the denominator) to compute the percentage which would result -- in divide by zero (DBZ). -- -- So for that case, we simply return the @projected_max_file_size_kb to avoid DBZ. Of course -- there is a tiny chance @projected_max_file_size_kb is also 0 (due to volume free space is 0 -- the volume is full!) so we'll simply return 1 (kb) -- -- Note 1: to avoid comparing equality of double-typed variable to 0, check against 1 KB -- it wouldn't be any faster but readability is lower. IF (@max_size_available_kb < 1.0) BEGIN SELECT @max_size_available_kb = @projected_max_file_size_kb; -- what if @projected_max_file_size_kb is still 0 (or near 0)? use 1 kb. IF (@max_size_available_kb < 1.0) BEGIN SELECT @max_size_available_kb = 1.0; END END RETURN @max_size_available_kb; END GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'MISC', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_misc', @level1type = 'FUNCTION', @level1name = 'fn_get_max_size_available'; GO ------------------------------------------------------------------------- -- Create table: sysutility_ucp_batch_manifests_internal -- This is the target table to store the manifest information for every batch collected -- The purpose of the manifest is to qualify the integrity of the data uploaded on the UCP. -- The batch manifest primarily includes: -- 1. server_instance_name: the server\instance name of the MI -- 2. xx_row_count: row count for each of the table collected/uploaded by the utility T-SQL collection item query -- 3. batch_time: the batch creation date-time stamp -- -- Note: the manifest info is stored in the form of name/value pair. This pattern is -- used is to minimize structural changes to the table in case there are collection -- queries added/removed in future that affects the manifest. ------------------------------------------------------------------------- IF NOT EXISTS (SELECT name FROM sys.objects WHERE object_id = OBJECT_ID(N'[snapshots].[sysutility_ucp_batch_manifests_internal]') ) BEGIN RAISERROR('Creating table [snapshots].[sysutility_ucp_batch_manifests_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [snapshots].[sysutility_ucp_batch_manifests_internal] ( [server_instance_name] SYSNAME NOT NULL, [batch_time] DATETIMEOFFSET(7) NOT NULL, [parameter_name] SYSNAME NOT NULL, -- Name, representing the collection query uploading data to the live table [parameter_value] SQL_VARIANT NULL, -- Value, indicating the number of rows collected/uploaded by the respective query [collection_time] DATETIMEOFFSET(7) NULL, [snapshot_id] INT NULL ) ON [PRIMARY] ALTER TABLE [snapshots].[sysutility_ucp_batch_manifests_internal] WITH CHECK ADD CONSTRAINT [FK_sysutility_ucp_batch_manifests_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE ALTER TABLE [snapshots].[sysutility_ucp_batch_manifests_internal] CHECK CONSTRAINT [FK_sysutility_ucp_batch_manifests_internal] ALTER TABLE [snapshots].[sysutility_ucp_batch_manifests_internal] WITH CHECK ADD CONSTRAINT [CHK_check_operator_E4F8A95D-2C44-48B6-85BA-E78E47C7ACCE] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))) ALTER TABLE [snapshots].[sysutility_ucp_batch_manifests_internal] CHECK CONSTRAINT [CHK_check_operator_E4F8A95D-2C44-48B6-85BA-E78E47C7ACCE] -- This index is used by the caching job to identify the latest consistent batches -- This index is also used by the DC purge job to seek against snapshot_id CREATE CLUSTERED INDEX CI_sysutility_ucp_batch_manifests_internal ON [snapshots].[sysutility_ucp_batch_manifests_internal](snapshot_id) EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'LIVE', @level0type = 'SCHEMA', @level0name = 'snapshots', @level1type = 'TABLE', @level1name = 'sysutility_ucp_batch_manifests_internal'; END GO --********************************************************************* -- Create table: consistent_batch_manifests_internal -- This table stores the consistent batch information for the enrolled MI's. -- The data returned by the view consistent_batch_manifests is stored -- in this table and is consumed by the caching job step. --********************************************************************* IF (OBJECT_ID(N'[sysutility_ucp_staging].[consistent_batch_manifests_internal]', 'U') IS NULL) BEGIN RAISERROR('Creating [sysutility_ucp_staging].[consistent_batch_manifests_internal] table', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_staging].[consistent_batch_manifests_internal] ( [server_instance_name] SYSNAME NOT NULL, [batch_time] DATETIMEOFFSET NOT NULL CONSTRAINT PK_consistent_batch_manifests_internal PRIMARY KEY CLUSTERED (server_instance_name, batch_time) ) EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'STAGING', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_staging', @level1type = 'TABLE', @level1name = 'consistent_batch_manifests_internal'; END; GO ---------------------------------------------------------------------------- -- Table [sysutility_ucp_dac_collected_execution_statistics_internal] -- Stores the output of sp_sysutility_instance_retrieve_dac_execution_statistics, -- which is executed by the Utility Data Collector collection set to retrieve DAC -- info, including execution statistics like the amount of CPU time being consumed -- by each DAC. The execution statistics (%CPU) returned by this query are -- actually collected by a different SQL Agent job that runs every 15 seconds on -- the managed instance. ---------------------------------------------------------------------------- IF (OBJECT_ID(N'[snapshots].[sysutility_ucp_dac_collected_execution_statistics_internal]', 'U') IS NULL) BEGIN RAISERROR('Creating [snapshots].[sysutility_ucp_dac_collected_execution_statistics_internal] table', 0, 1) WITH NOWAIT; CREATE TABLE [snapshots].[sysutility_ucp_dac_collected_execution_statistics_internal] ( [physical_server_name] SYSNAME, [server_instance_name] SYSNAME, [dac_db] SYSNAME, [dac_deploy_date] DATETIME, [dac_description] NVARCHAR(4000) NULL, [dac_name] SYSNAME, [interval_start_time] DATETIMEOFFSET NULL,-- The first every-15-second DAC CPU collection time included in this row's time interval [interval_end_time] DATETIMEOFFSET NULL, -- The last every-15-second DAC CPU collection time included in this row's time interval (typically ~15 min after interval_start_time) [interval_cpu_time_ms] BIGINT NULL, -- The total amount of CPU time consumed by this DAC in the time interval [interval_cpu_pct] REAL NULL, -- The average %CPU utilization for this DAC in the time interval (% is of all available CPU cycles, not just a single CPU) [lifetime_cpu_time_ms] BIGINT NULL, -- The cumulative total CPU time consumed by this DAC since we started monitoring it [batch_time] datetimeoffset(7), -- Start time for one execution of the Utility data collection job [collection_time] [datetimeoffset](7), -- The execution time of the DC query that gathered this data from the managed instance [snapshot_id] [int], -- This index is used by the caching job to copy the latest consistent batch from live to cache table CONSTRAINT PK_sysutility_ucp_dac_collected_execution_statistics_internal PRIMARY KEY CLUSTERED (server_instance_name, batch_time, dac_name) ); -- This index is used by the DC purge job to seek against snapshot_id CREATE NONCLUSTERED INDEX NCI_sysutility_ucp_dac_collected_execution_statistics_internal ON [snapshots].[sysutility_ucp_dac_collected_execution_statistics_internal](snapshot_id) -- Create a foreign key referencing snapshots_internal so that rows in this table will be deleted by the DC purge job ALTER TABLE [snapshots].[sysutility_ucp_dac_collected_execution_statistics_internal] WITH CHECK ADD CONSTRAINT [fk_dac_collected_execution_statistics_internal_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE; EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'LIVE', @level0type = 'SCHEMA', @level0name = 'snapshots', @level1type = 'TABLE', @level1name = 'sysutility_ucp_dac_collected_execution_statistics_internal'; END; GO ---------------------------------------------------------------------------------- -- View latest_dac_cpu_utilization -- Gets the latest DAC-related information and CPU utilization for each DAC on a -- managed instanced ---------------------------------------------------------------------------------- IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_staging].[latest_dac_cpu_utilization]')) BEGIN RAISERROR('Dropping [sysutility_ucp_staging].[latest_dac_cpu_utilization] view', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_staging].[latest_dac_cpu_utilization] END GO RAISERROR('Creating [sysutility_ucp_staging].[latest_dac_cpu_utilization] view', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_staging].[latest_dac_cpu_utilization] AS SELECT physical_server_name, ds.server_instance_name, dac_db, dac_deploy_date, dac_description, dac_name, lifetime_cpu_time_ms, interval_cpu_pct AS latest_cpu_pct, interval_cpu_time_ms AS latest_interval_cpu_time_ms, interval_start_time AS latest_interval_start_time, interval_end_time AS latest_interval_end_time, ds.batch_time, N'Utility[@Name=''' + CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')) + N''']/DeployedDac[@Name=''' + ds.dac_name + N''' and @ServerInstanceName=''' + ds.server_instance_name + N''']' AS urn, N'SQLSERVER:\Utility\'+CASE WHEN 0 = CHARINDEX(N'\', CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')), 1) THEN CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')) + N'\DEFAULT' ELSE CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')) END +N'\DeployedDacs\'+msdb.dbo.fn_encode_sqlname_for_powershell(ds.dac_name+'.'+ds.server_instance_name) AS powershell_path FROM [snapshots].[sysutility_ucp_dac_collected_execution_statistics_internal] ds INNER JOIN [sysutility_ucp_staging].[consistent_batch_manifests_internal] cb ON ds.server_instance_name = cb.server_instance_name AND ds.batch_time = cb.batch_time GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'STAGING', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_staging', @level1type = 'VIEW', @level1name = 'latest_dac_cpu_utilization'; GO ---------------------------------------------------------------------------- --- MDW Table for storing SMO server collection set information. ---------------------------------------------------------------------------- IF NOT EXISTS (SELECT name FROM .[sys].[objects] WHERE object_id = OBJECT_ID(N'[snapshots].[sysutility_ucp_smo_properties_internal]')) BEGIN RAISERROR('Creating [snapshots].[sysutility_ucp_smo_properties_internal] table', 0, 1) WITH NOWAIT; CREATE TABLE [snapshots].[sysutility_ucp_smo_properties_internal] ( [physical_server_name] SYSNAME, [server_instance_name] SYSNAME, [object_type] INT, [urn] NVARCHAR(4000), [property_name] NVARCHAR(128), [property_value] SQL_VARIANT, [batch_time] DATETIMEOFFSET(7), -- Start time for one execution of the Utility data collection job [collection_time] DATETIMEOFFSET(7) NULL, [snapshot_id] INT NULL, -- NOTE: compression is enabled on this table at runtime (during Create UCP) in sp_initialize_mdw_internal ) -- This index is used by the caching job to copy the latest consistent batch from live to cache table CREATE CLUSTERED INDEX CI_sysutility_ucp_smo_properties_internal ON [snapshots].[sysutility_ucp_smo_properties_internal](server_instance_name, batch_time); -- This index is used by the DC purge job to seek against snapshot_id CREATE NONCLUSTERED INDEX NCI_sysutility_ucp_smo_properties_internal ON [snapshots].[sysutility_ucp_smo_properties_internal](snapshot_id) -- Create a foreign key referencing snapshots_internal so that rows in this table will be deleted by the DC purge job ALTER TABLE [snapshots].[sysutility_ucp_smo_properties_internal] WITH CHECK ADD CONSTRAINT [FK_sysutility_ucp_smo_properties_internal_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE; EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'LIVE', @level0type = 'SCHEMA', @level0name = 'snapshots', @level1type = 'TABLE', @level1name = 'sysutility_ucp_smo_properties_internal'; END GO ------------------------------------------------------------------------------ -- SQL Server View to read latest snapshot data for SMO properties ------------------------------------------------------------------------------ IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_staging].[latest_smo_properties]')) BEGIN RAISERROR('Dropping [sysutility_ucp_staging].[latest_smo_properties] view', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_staging].[latest_smo_properties] END GO RAISERROR('Creating [sysutility_ucp_staging].[latest_smo_properties] view', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_staging].[latest_smo_properties] AS SELECT s.physical_server_name, s.server_instance_name, s.urn, s.object_type, s.property_name,s.property_value, s.snapshot_id, s.batch_time FROM [snapshots].[sysutility_ucp_smo_properties_internal] AS s INNER JOIN [sysutility_ucp_staging].[consistent_batch_manifests_internal] cb ON s.server_instance_name = cb.server_instance_name AND s.batch_time = cb.batch_time GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'STAGING', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_staging', @level1type = 'VIEW', @level1name = 'latest_smo_properties'; GO ----------------------------------------------------------------------- -- Created [snapshots].[sysutility_ucp_cpu_memory_configurations_internal] and [snapshots].[sysutility_ucp_cpu_affinity_internal] tables if they are not existing. ----------------------------------------------------------------------- IF NOT EXISTS (SELECT name FROM sys.objects WHERE object_id = OBJECT_ID(N'[snapshots].[sysutility_ucp_cpu_memory_configurations_internal]')) BEGIN RAISERROR('Creating table [snapshots].[sysutility_ucp_cpu_memory_configurations_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [snapshots].[sysutility_ucp_cpu_memory_configurations_internal]( [server_instance_name] SYSNAME NOT NULL, [is_clustered_server] SMALLINT NULL, [virtual_server_name] SYSNAME, [physical_server_name] SYSNAME NOT NULL, [num_processors] INT NULL, [server_processor_usage] REAL, [instance_processor_usage] REAL, [cpu_name] NVARCHAR(128) NULL, [cpu_caption] NVARCHAR(128) NULL, [cpu_family] NVARCHAR(128) NULL, [cpu_architecture] NVARCHAR(64) NULL, [cpu_max_clock_speed] DECIMAL(10,0) NULL, -- this is in MHz (Mega Hertz) [cpu_clock_speed] DECIMAL(10,0) NULL, [l2_cache_size] DECIMAL(10,0) NULL, [l3_cache_size] DECIMAL(10,0) NULL, [batch_time] [datetimeoffset](7), -- Start time for one execution of the Utility data collection job [collection_time] [datetimeoffset](7) NULL, [snapshot_id] [int] NULL -- This index is used by the caching job to copy the latest consistent batch from live to cache table CONSTRAINT PK_sysutility_cpu_memory_related_info_internal_clustered PRIMARY KEY CLUSTERED ([server_instance_name], [batch_time], [physical_server_name]) ) ON [PRIMARY]; -- This index is used by the DC purge job to seek against snapshot_id CREATE NONCLUSTERED INDEX NCI_sysutility_ucp_cpu_memory_configurations_internal ON [snapshots].[sysutility_ucp_cpu_memory_configurations_internal](snapshot_id) ALTER TABLE [snapshots].[sysutility_ucp_cpu_memory_configurations_internal] WITH CHECK ADD CONSTRAINT [FK_sysutility_cpu_memory_related_info_snapshots_internal_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE; ALTER TABLE [snapshots].[sysutility_ucp_cpu_memory_configurations_internal] CHECK CONSTRAINT [FK_sysutility_cpu_memory_related_info_snapshots_internal_snapshots_internal]; ALTER TABLE [snapshots].[sysutility_ucp_cpu_memory_configurations_internal] WITH CHECK ADD CONSTRAINT [CHK_check_operator_9DAA9ACC-F1E1-44F8-8B74-D081734E5F39] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))); ALTER TABLE [snapshots].[sysutility_ucp_cpu_memory_configurations_internal] CHECK CONSTRAINT [CHK_check_operator_9DAA9ACC-F1E1-44F8-8B74-D081734E5F39]; EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'LIVE', @level0type = 'SCHEMA', @level0name = 'snapshots', @level1type = 'TABLE', @level1name = 'sysutility_ucp_cpu_memory_configurations_internal'; END GO ----------------------------------------------------------------------- -- Gets the latest CPU/memory configurations for each computer - along -- with the current CPU utilization for the computer. -- -- NOTE: If there are multiple SQL instances (MIs) on a computer, each of -- them uploads its snapshot of information about the computer. We -- only want one entry for the computer - so we pick the entry that -- was uploaded last ----------------------------------------------------------------------- IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_staging].[latest_computer_cpu_memory_configuration]')) BEGIN RAISERROR('Dropping [sysutility_ucp_staging].[latest_computer_cpu_memory_configuration] view', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_staging].[latest_computer_cpu_memory_configuration] END GO RAISERROR('Creating view [sysutility_ucp_staging].[latest_computer_cpu_memory_configuration]', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_staging].[latest_computer_cpu_memory_configuration] AS SELECT * FROM ( SELECT ROW_NUMBER() OVER (PARTITION BY t.physical_server_name ORDER BY t.batch_time DESC) AS Rank, [virtual_server_name], [is_clustered_server],[physical_server_name], [num_processors], [cpu_name], [cpu_caption], [cpu_family], [cpu_architecture], [cpu_max_clock_speed], [cpu_clock_speed], [l2_cache_size], [l3_cache_size], [server_processor_usage], t.[batch_time], N'Utility[@Name=''' + CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')) + N''']/Computer[@Name=''' + t.physical_server_name + N''']' AS urn, N'SQLSERVER:\Utility\'+CASE WHEN 0 = CHARINDEX(N'\', CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')), 1) THEN CONVERT(SYSNAME, SERVERPROPERTY(N'ServerName')) + N'\DEFAULT' ELSE @@SERVERNAME END +N'\Computers\'+msdb.dbo.fn_encode_sqlname_for_powershell(t.physical_server_name) AS powershell_path FROM [snapshots].[sysutility_ucp_cpu_memory_configurations_internal] AS t INNER JOIN [sysutility_ucp_staging].[consistent_batch_manifests_internal] cb ON t.server_instance_name = cb.server_instance_name AND t.batch_time = cb.batch_time ) AS S WHERE S.Rank = 1 GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'STAGING', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_staging', @level1type = 'VIEW', @level1name = 'latest_computer_cpu_memory_configuration'; GO ----------------------------------------------------------------------- ----------------------------------------------------------------------- IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_staging].[latest_instance_cpu_utilization]')) BEGIN RAISERROR('Dropping [sysutility_ucp_staging].[latest_instance_cpu_utilization] view', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_staging].[latest_instance_cpu_utilization] END GO RAISERROR('Creating view [sysutility_ucp_staging].[latest_instance_cpu_utilization]', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_staging].[latest_instance_cpu_utilization] AS SELECT t.[server_instance_name], [instance_processor_usage], t.[batch_time] FROM [snapshots].[sysutility_ucp_cpu_memory_configurations_internal] AS t INNER JOIN [sysutility_ucp_staging].[consistent_batch_manifests_internal] cb ON t.server_instance_name = cb.server_instance_name AND t.batch_time = cb.batch_time GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'STAGING', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_staging', @level1type = 'VIEW', @level1name = 'latest_instance_cpu_utilization'; GO ----------------------------------------------------------------------- -- Created [snapshots].[sysutility_ucp_volumes_internal] tables to collect volume-related -- information from the MIs ----------------------------------------------------------------------- IF NOT EXISTS (SELECT name FROM sys.objects WHERE object_id = OBJECT_ID(N'[snapshots].[sysutility_ucp_volumes_internal]') ) BEGIN RAISERROR('Creating table [snapshots].[sysutility_ucp_volumes_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [snapshots].[sysutility_ucp_volumes_internal]( [server_instance_name] SYSNAME, [virtual_server_name] SYSNAME, [physical_server_name] SYSNAME, [volume_device_id] SYSNAME NOT NULL, [volume_name] [nvarchar](260) NOT NULL, [total_space_available] [real] NULL, -- in MB [free_space] [real] NULL, -- in MB [batch_time] datetimeoffset(7), -- Start time for one execution of the Utility data collection job [collection_time] datetimeoffset(7) NULL, [snapshot_id] [int] NULL -- This index is used by the caching job to copy the latest consistent batch from live to cache table CONSTRAINT PK_sysutility_volumes_info_internal PRIMARY KEY CLUSTERED (server_instance_name, batch_time, volume_device_id) ) ON [PRIMARY] -- This index is used by the DC purge job to seek against snapshot_id CREATE NONCLUSTERED INDEX NCI_sysutility_ucp_volumes_internal ON [snapshots].[sysutility_ucp_volumes_internal](snapshot_id) ALTER TABLE [snapshots].[sysutility_ucp_volumes_internal] WITH CHECK ADD CONSTRAINT [FK_volumes_info_snapshots_internal] FOREIGN KEY([snapshot_id]) REFERENCES [core].[snapshots_internal] ([snapshot_id]) ON DELETE CASCADE ALTER TABLE [snapshots].[sysutility_ucp_volumes_internal] CHECK CONSTRAINT [FK_volumes_info_snapshots_internal] ALTER TABLE [snapshots].[sysutility_ucp_volumes_internal] WITH CHECK ADD CONSTRAINT [CHK_check_operator_D79F8519-D243-4176-8291-6F3BA8EF776D] CHECK (([core].[fn_check_operator]([snapshot_id])=(1))) ALTER TABLE [snapshots].[sysutility_ucp_volumes_internal] CHECK CONSTRAINT [CHK_check_operator_D79F8519-D243-4176-8291-6F3BA8EF776D] EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'LIVE', @level0type = 'SCHEMA', @level0name = 'snapshots', @level1type = 'TABLE', @level1name = 'sysutility_ucp_volumes_internal'; END GO ----------------------------------------------------------------------- -- View to get latest volumes information. -- NOTE: If you change the output of this view in any way, be sure to also update the -- corresponding "stub" object in instmsdb.sql. -- -- NOTE: When you have multiple SQL instances on a machine, each of them is uploading -- volume information (as it sees it). However, we only want one entry for each -- volume on the computer. What we do here is to simply pick the entry from the -- instance that uploaded last - hence the partition-by (physical_server_name,volume_device_id) -- The "latest_computers" view exhibits very similar behavior. ----------------------------------------------------------------------- IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_staging].[latest_volumes]')) BEGIN RAISERROR('Dropping view [sysutility_ucp_staging].[latest_volumes]', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_staging].[latest_volumes] END GO RAISERROR('Creating view [sysutility_ucp_staging].[latest_volumes]', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_staging].[latest_volumes] AS SELECT [virtual_server_name], [physical_server_name], [volume_device_id], [volume_name], [total_space_available], -- in MB [free_space], -- in MB [batch_time], [snapshot_id] FROM ( SELECT [virtual_server_name], [physical_server_name], [volume_device_id], [volume_name], [total_space_available], [free_space], V.[batch_time], [snapshot_id], ROW_NUMBER() OVER (PARTITION BY physical_server_name,volume_device_id ORDER BY V.batch_time DESC) rk FROM [snapshots].[sysutility_ucp_volumes_internal] AS V INNER JOIN [sysutility_ucp_staging].[consistent_batch_manifests_internal] cb ON V.server_instance_name = cb.server_instance_name AND V.batch_time = cb.batch_time ) AS T WHERE T.rk = 1 GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'STAGING', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_staging', @level1type = 'VIEW', @level1name = 'latest_volumes'; GO --********************************************************************* -- Create procedure: sp_get_consistent_batches -- This procedure gets the consistent batch information for the enrolled MI's -- and stores it into the consistent_batch_manifests_internal table. -- This SP is executed by the caching job to identify the consistent batches -- whose data needs to be copied from the live to cache tables for every run. -- -- A batch is marked consistent if the number of records in each table uploaded -- match to the respective row count in the batch manifest. The evaluation of -- last batch (a) and second-last batch (b) for a given MI is based on following rules: -- . Both a and b exists and are consistent: -- Most recent batch (a) is returned -- . Either a or b exists -- Returns a or b only if it is consistent. Possiblity of delayed upload from MI side. -- . Both a and b exists and are inconsistent: -- No batch info is returned for that MI. Possiblity of failure in collection/upload on MI side. -- . Both a and b does not exist -- No batch info is not returned for that MI. Aged-out data is currently not considered, VSTS #319498 --********************************************************************* IF OBJECT_ID ('[sysutility_ucp_staging].[sp_get_consistent_batches]') IS NOT NULL BEGIN RAISERROR ('Dropping procedure [sysutility_ucp_staging].[sp_get_consistent_batches]', 0, 1) WITH NOWAIT; DROP PROCEDURE [sysutility_ucp_staging].[sp_get_consistent_batches] END; GO RAISERROR ('Creating procedure [sysutility_ucp_staging].[sp_get_consistent_batches]', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE [sysutility_ucp_staging].[sp_get_consistent_batches] AS BEGIN SET NOCOUNT ON; -- Note: As we are not currently caching the aged-out data, this SP -- clears the existing records and inserts the new data. However, as a fix for -- VSTS #319498 (display aged-out data) this behavior needs to be changed -- to UPSERT for any existing or new data and delete the entries whose -- data is purged (> 7 days). -- Get the manifest information for the latest uploaded batches. The "manifest" info includes -- the expected number of rows that should have been uploaded into each live table for the -- batch. This query captures the manifest for most recent unprocessed batch (T) and the -- immediately prior (T-1) unprocessed batch from each managed instance since the last execution -- of the sp_copy_live_table_data_into_cache_tables stored proc. -- -- This rowset is staged in a temp table b/c the query optimizer cannot accurately predict the -- number of rows that qualify for "WHERE bm.snapshot_id > sp.latest_consistent_snapshot_id". -- -- Note: This view may fetch the last 2 batch manifest rows for a given MI. The reason for -- considering two batches is to use the latest one that is consistent. If the latest one is -- missing (delayed upload) or inconsistent (failed or still-in-progress upload), we will use -- the second-to-last batch, assuming that it is consistent. This makes the caching job -- resilient to occasional delays in the MI upload job. SELECT server_instance_name , batch_time , CONVERT(INT, dac_packages_row_count) AS dac_packages_row_count , CONVERT(INT, cpu_memory_configurations_row_count) AS cpu_memory_configurations_row_count , CONVERT(INT, volumes_row_count) AS volumes_row_count , CONVERT(INT, smo_properties_row_count) AS smo_properties_row_count INTO #batch_manifests_latest FROM (SELECT bm.server_instance_name, bm.batch_time, bm.parameter_name, bm.parameter_value FROM snapshots.sysutility_ucp_batch_manifests_internal bm , msdb.dbo.sysutility_ucp_snapshot_partitions_internal AS sp WHERE bm.snapshot_id > sp.latest_consistent_snapshot_id -- The [time_id] = 1 partition gives us the max snapshot_id the last time that the -- sp_copy_live_table_data_into_cache_tables proc was executed (previous high water -- mark). We will consider for processing any snapshots that have been uploaded -- since then. AND sp.time_id = 1) AS lbm PIVOT (MAX(parameter_value) FOR parameter_name IN (dac_packages_row_count , cpu_memory_configurations_row_count , volumes_row_count , smo_properties_row_count)) pvt; -- Truncate the table TRUNCATE TABLE [sysutility_ucp_staging].[consistent_batch_manifests_internal]; -- Get the set of latest batches that are consistent with respect to the data uploaded to -- each live table. A check is made to verify that the number of rows uploaded matches the -- expected row count in that batch's manifest. -- -- These rowsets are staged in temp tables b/c the query optimizer cannot accurately predict -- the number of rows that qualify for "HAVING COUNT(*) = bm.cpu_memory_configurations_row_count". SELECT bm.server_instance_name, bm.batch_time INTO #dac_statistics_consistent_batches FROM #batch_manifests_latest bm -- Note: No records in DAC table doesn't mean issue with upload -- a MI with no DACs is -- perfectly valid; use an outer join so that we tolerate the no-DACs case. LEFT JOIN [snapshots].[sysutility_ucp_dac_collected_execution_statistics_internal] ds ON bm.server_instance_name = ds.server_instance_name AND bm.batch_time = ds.batch_time GROUP BY bm.server_instance_name, bm.batch_time, bm.dac_packages_row_count, ds.batch_time HAVING SUM(CASE WHEN ds.batch_time IS NULL THEN 0 ELSE 1 END) = bm.dac_packages_row_count SELECT bm.server_instance_name, bm.batch_time INTO #cpu_memory_configurations_consistent_batches FROM #batch_manifests_latest bm INNER JOIN [snapshots].[sysutility_ucp_cpu_memory_configurations_internal] cm ON bm.server_instance_name = cm.server_instance_name AND bm.batch_time = cm.batch_time GROUP BY bm.server_instance_name, bm.batch_time, bm.cpu_memory_configurations_row_count HAVING COUNT(*) = bm.cpu_memory_configurations_row_count SELECT bm.server_instance_name, bm.batch_time INTO #volumes_consistent_batches FROM #batch_manifests_latest bm INNER JOIN [snapshots].[sysutility_ucp_volumes_internal] vo ON bm.server_instance_name = vo.server_instance_name AND bm.batch_time = vo.batch_time GROUP BY bm.server_instance_name, bm.batch_time, bm.volumes_row_count HAVING COUNT(*) = bm.volumes_row_count SELECT bm.server_instance_name, bm.batch_time INTO #smo_properties_consistent_batches FROM #batch_manifests_latest bm INNER JOIN [snapshots].[sysutility_ucp_smo_properties_internal] sp ON bm.server_instance_name = sp.server_instance_name AND bm.batch_time = sp.batch_time GROUP BY bm.server_instance_name, bm.batch_time, bm.smo_properties_row_count HAVING COUNT(*) = bm.smo_properties_row_count -- Insert the new consistent batch information. A consistent batch is a batch where all of -- the live tables have the expected number of rows. INSERT INTO [sysutility_ucp_staging].[consistent_batch_manifests_internal] SELECT bm.server_instance_name , bm.batch_time FROM ( -- Fetch the latest (order by DESC) consistent batches uploaded by the MI's SELECT ROW_NUMBER() OVER (PARTITION BY bm.server_instance_name ORDER BY bm.batch_time DESC) AS [rank] , bm.server_instance_name , bm.batch_time FROM #batch_manifests_latest AS bm INNER JOIN #dac_statistics_consistent_batches AS ds ON bm.server_instance_name = ds.server_instance_name AND bm.batch_time = ds.batch_time INNER JOIN #cpu_memory_configurations_consistent_batches AS cm ON bm.server_instance_name = cm.server_instance_name AND bm.batch_time = cm.batch_time INNER JOIN #volumes_consistent_batches AS vo ON bm.server_instance_name = vo.server_instance_name AND bm.batch_time = vo.batch_time INNER JOIN #smo_properties_consistent_batches AS sp ON bm.server_instance_name = sp.server_instance_name AND bm.batch_time = sp.batch_time ) bm WHERE bm.[rank] = 1; END GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'STAGING', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_staging', @level1type = 'PROCEDURE', @level1name = 'sp_get_consistent_batches'; GO ----------------------------------------------------------------------------------------- -- Procedure sp_copy_live_table_data_into_cache_tables -- Copies the latest snapshot of data from the "live" tables into the "cache" tables ----------------------------------------------------------------------------------------- IF OBJECT_ID ('sysutility_ucp_staging.sp_copy_live_table_data_into_cache_tables') IS NOT NULL BEGIN RAISERROR('Dropping procedure sysutility_ucp_staging.sp_copy_live_table_data_into_cache_tables procedure', 0, 1) WITH NOWAIT; DROP PROCEDURE sysutility_ucp_staging.sp_copy_live_table_data_into_cache_tables; END GO RAISERROR('Creating procedure sysutility_ucp_staging.sp_copy_live_table_data_into_cache_tables', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE sysutility_ucp_staging.sp_copy_live_table_data_into_cache_tables 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 @max_snapshot_id INT, @num_snapshots_partitions INT SELECT @max_snapshot_id = ISNULL(MAX(snapshot_id),0) FROM [core].[snapshots] SELECT @num_snapshots_partitions = COUNT(*) FROM [msdb].[dbo].[sysutility_ucp_snapshot_partitions_internal] DECLARE @task_start_time DATETIME = GETUTCDATE(); DECLARE @task_elapsed_ms INT; DECLARE @row_count INT; -- Initialize the snapshot partitions to default (0) IF(@num_snapshots_partitions = 0) BEGIN INSERT INTO [msdb].[dbo].[sysutility_ucp_snapshot_partitions_internal] VALUES (2, 0) INSERT INTO [msdb].[dbo].[sysutility_ucp_snapshot_partitions_internal] VALUES (1, 0) INSERT INTO [msdb].[dbo].[sysutility_ucp_snapshot_partitions_internal] VALUES (0, 0) END DECLARE @processing_time_current DATETIMEOFFSET(7) = SYSDATETIMEOFFSET(); -- -- Stage 0: -- Identify the batches that were recently uploaded and are consistent -- Data belonging to these batches will be copied from live to cache table. EXEC [sysutility_ucp_staging].[sp_get_consistent_batches] SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('sp_get_consistent_batches: %d ms', 0, 1, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); ----- ----- Stage 1: Insert into all the dimension tables ----- computers_internal, dacs_internal, volumes_internal, ----- smo_servers_internal, databases_internal, filegroups_internal, ----- datafiles_internal, logfiles_internal ----- (Then move to Stage 2: the "measure" tables) ----- -- A note about the expression used for [batch_time] in the INSERT queries, below: -- -- We want to expose batch_time w/a UCP-local time zone so it can be exposed as a UCP-local datetime -- in the GUI (the GUI consumes directly from the enumerator). The batch_time values in each of the -- queries below have their time zone offset switched to produce a datetimeoffset with the local UCP -- server's time zone offset. -- -- This works well except when the server's time zone offset has changed since the [batch_time] was -- generated due to a Daylight Saving Time change. (Unfortunately, there is no way in T-SQL to -- determine what the server's time zone offset was at some arbitrary point in the past.) The risk -- of this is low since we generally do this processing within 15 minutes of timestamp generation. -- This doesn't actually result in truly incorrect batch_times that would affect data processing -- since the UTC time value that underlies every datetimeoffset is unchanged when you switch the -- value's time zone offset. -- -- Insert into the "computers" dimension table -- INSERT INTO [sysutility_ucp_core].[computers_internal] ( virtual_server_name, is_clustered_server, physical_server_name, num_processors, cpu_name, cpu_caption, cpu_family, cpu_architecture, cpu_max_clock_speed, cpu_clock_speed, l2_cache_size, l3_cache_size, percent_total_cpu_utilization, batch_time, processing_time, urn, powershell_path) SELECT virtual_server_name, is_clustered_server, physical_server_name, num_processors, cpu_name, cpu_caption, cpu_family, cpu_architecture, cpu_max_clock_speed, cpu_clock_speed, l2_cache_size, l3_cache_size, server_processor_usage, SWITCHOFFSET (batch_time, DATENAME(TZoffset, SYSDATETIMEOFFSET())) AS batch_time, @processing_time_current AS processing_time, urn, powershell_path FROM [sysutility_ucp_staging].[latest_computer_cpu_memory_configuration] SET @row_count = @@ROWCOUNT; SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('Insert into [computers_internal]: %d rows, %d ms', 0, 1, @row_count, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); -- -- Insert into the "dacs_internal" dimension-table -- INSERT INTO [sysutility_ucp_core].[dacs_internal] ( server_instance_name, dac_name, physical_server_name, dac_deploy_date, dac_description, dac_percent_total_cpu_utilization, batch_time, processing_time, urn, powershell_path) SELECT server_instance_name, dac_name, physical_server_name, dac_deploy_date, dac_description, latest_cpu_pct, SWITCHOFFSET (batch_time, DATENAME(TZoffset, SYSDATETIMEOFFSET())) AS batch_time, @processing_time_current AS processing_time, urn, powershell_path FROM [sysutility_ucp_staging].[latest_dac_cpu_utilization] SET @row_count = @@ROWCOUNT; SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('Insert into [dacs_internal]: %d rows, %d ms', 0, 1, @row_count, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); --- Insert into the volumes_internal dimension table INSERT INTO [sysutility_ucp_core].[volumes_internal] ( virtual_server_name, physical_server_name, volume_device_id, volume_name, total_space_available, free_space, batch_time, processing_time) SELECT virtual_server_name, physical_server_name, volume_device_id, volume_name, total_space_available, free_space, SWITCHOFFSET (batch_time, DATENAME(TZoffset, SYSDATETIMEOFFSET())) AS batch_time, @processing_time_current AS processing_time FROM [sysutility_ucp_staging].[latest_volumes] SET @row_count = @@ROWCOUNT; SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('Insert into [volumes_internal]: %d rows, %d ms', 0, 1, @row_count, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); INSERT INTO [sysutility_ucp_core].[smo_servers_internal] ( [urn] , [powershell_path] , [processing_time] , [batch_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] ) SELECT urn , CONVERT(NVARCHAR(MAX), [powershell_path]) AS [powershell_path] , @processing_time_current AS [processing_time] -- $FIXED: SQLBUVSTS-316258 , SWITCHOFFSET (batch_time, DATENAME(TZoffset, SYSDATETIMEOFFSET())) AS [batch_time] , CONVERT(SMALLINT,[AuditLevel]) AS [AuditLevel] , CONVERT(NVARCHAR(260) ,[BackupDirectory]) AS [BackupDirectory] , CONVERT(NVARCHAR(128) ,[BrowserServiceAccount]) AS [BrowserServiceAccount] , CONVERT(SMALLINT,[BrowserStartMode]) AS [BrowserStartMode] , CONVERT(NVARCHAR(20) ,[BuildClrVersionString]) AS [BuildClrVersionString] , CONVERT(INT,[BuildNumber]) AS [BuildNumber] , CONVERT(NVARCHAR(128),[Collation]) AS [Collation] , CONVERT(INT,[CollationID]) AS [CollationID] , CONVERT(INT,[ComparisonStyle]) AS [ComparisonStyle] , CONVERT(NVARCHAR(128),[ComputerNamePhysicalNetBIOS]) AS [ComputerNamePhysicalNetBIOS] , CONVERT(NVARCHAR(260),[DefaultFile]) AS [DefaultFile] , CONVERT(NVARCHAR(260),[DefaultLog]) AS [DefaultLog] , CONVERT(NVARCHAR(64),[Edition]) AS [Edition] , CONVERT(SMALLINT,[EngineEdition]) AS [EngineEdition] , CONVERT(NVARCHAR(260) ,[ErrorLogPath]) AS [ErrorLogPath] , CONVERT(NVARCHAR(260) ,[FilestreamShareName]) AS [FilestreamShareName] , CONVERT(NVARCHAR(260) ,[InstallDataDirectory]) AS [InstallDataDirectory] , CONVERT(NVARCHAR(260) ,[InstallSharedDirectory]) AS [InstallSharedDirectory] , CONVERT(NVARCHAR(128) ,[InstanceName]) AS [InstanceName] , CONVERT(BIT,[IsCaseSensitive]) AS [IsCaseSensitive] , CONVERT(BIT,[IsClustered]) AS [IsClustered] , CONVERT(BIT,[IsFullTextInstalled]) AS [IsFullTextInstalled] , CONVERT(BIT,[IsSingleUser]) AS [IsSingleUser] , CONVERT(NVARCHAR(64) ,[Language]) AS [Language] , CONVERT(NVARCHAR(128),[MailProfile]) AS [MailProfile] , CONVERT(NVARCHAR(260),[MasterDBLogPath]) AS [MasterDBLogPath] , CONVERT(NVARCHAR(260),[MasterDBPath]) AS [MasterDBPath] , CONVERT(TINYINT,[MaxPrecision]) AS [MaxPrecision] , CONVERT(NVARCHAR(128) ,[Name]) AS [Name] , CONVERT(BIT,[NamedPipesEnabled]) AS [NamedPipesEnabled] , CONVERT(NVARCHAR(128) ,[NetName]) AS [NetName] , CONVERT(INT,[NumberOfLogFiles]) AS [NumberOfLogFiles] , CONVERT(NVARCHAR(32) ,[OSVersion]) AS [OSVersion] , CONVERT(SMALLINT,[PerfMonMode]) AS [PerfMonMode] , CONVERT(INT,[PhysicalMemory]) AS [PhysicalMemory] , CONVERT(NVARCHAR(32) ,[Platform]) AS [Platform] , CONVERT(SMALLINT,[Processors]) AS [Processors] , CONVERT(INT,[ProcessorUsage]) AS [ProcessorUsage] , CONVERT(NVARCHAR(48) ,[Product]) AS [Product] , CONVERT(NVARCHAR(32) ,[ProductLevel]) AS [ProductLevel] , CONVERT(NVARCHAR(32) ,[ResourceVersionString]) AS [ResourceVersionString] , CONVERT(NVARCHAR(260) ,[RootDirectory]) AS [RootDirectory] , CONVERT(SMALLINT,[ServerType]) AS [ServerType] , CONVERT(NVARCHAR(128),[ServiceAccount]) AS [ServiceAccount] , CONVERT(NVARCHAR(64),[ServiceInstanceId]) AS [ServiceInstanceId] , CONVERT(NVARCHAR(64),[ServiceName]) AS [ServiceName] , CONVERT(SMALLINT,[ServiceStartMode]) AS [ServiceStartMode] , CONVERT(SMALLINT,[SqlCharSet]) AS [SqlCharSet] , CONVERT(NVARCHAR(32),[SqlCharSetName]) AS [SqlCharSetName] , CONVERT(NVARCHAR(128),[SqlDomainGroup]) AS [SqlDomainGroup] , CONVERT(SMALLINT,[SqlSortOrder]) AS [SqlSortOrder] , CONVERT(NVARCHAR(64),[SqlSortOrderName]) AS [SqlSortOrderName] , CONVERT(SMALLINT,[Status]) AS [Status] , CONVERT(INT,[TapeLoadWaitTime]) AS [TapeLoadWaitTime] , CONVERT(BIT,[TcpEnabled]) AS [TcpEnabled] , CONVERT(INT,[VersionMajor]) AS [VersionMajor] , CONVERT(INT,[VersionMinor]) AS [VersionMinor] , CONVERT(NVARCHAR(32),[VersionString]) AS [VersionString] FROM (SELECT urn, property_name, property_value, batch_time FROM [sysutility_ucp_staging].[latest_smo_properties] WHERE object_type = 1) props -- object_type = 1 is Server PIVOT ( MAX(property_value) FOR property_name IN ( [powershell_path] , [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] ) ) AS pvt SET @row_count = @@ROWCOUNT; SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('Insert into [smo_servers_internal]: %d rows, %d ms', 0, 1, @row_count, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); INSERT INTO [sysutility_ucp_core].[databases_internal] ([urn] , [powershell_path] , [processing_time] , [batch_time] , [server_instance_name] , [parent_urn] , [Collation] , [CompatibilityLevel] , [CreateDate] , [EncryptionEnabled] , [Name] , [RecoveryModel] , [Trustworthy]) SELECT [urn] , CONVERT(NVARCHAR(MAX), [powershell_path]) AS [powershell_path] , @processing_time_current AS processing_time -- $FIXED: SQLBUVSTS-316258 , SWITCHOFFSET ([batch_time], DATENAME(TZoffset, SYSDATETIMEOFFSET())) AS [batch_time] , [server_instance_name] , Left(urn, CHARINDEX('/Database[', urn, 1)-1) AS parent_urn , CONVERT(NVARCHAR(128),[Collation]) AS Collation , CONVERT(SMALLINT,[CompatibilityLevel]) AS CompatibilityLevel -- DC (SSIS) doesn't support sql_variant, so all properties are uploaded as nvarchar(4000). To successfully round-trip -- the property values through nvarchar, we use the same language-independent conversion style on MI and UCP. The shared -- fn_sysutility_get_culture_invariant_conversion_style_internal function gives us a consistent conversion style for each -- property data type that is language-independent and that won't cause data loss. We also use this function on the MI -- when converting to nvarchar so that the two conversions are symmetrical. (Ref: VSTS 361531, 359504, 12967) , CONVERT(DATETIME, [CreateDate], msdb.dbo.fn_sysutility_get_culture_invariant_conversion_style_internal('datetime')) AS CreateDate , CONVERT(BIT,[EncryptionEnabled]) AS EncryptionEnabled , CONVERT(SYSNAME,[Name])AS [Name] , CONVERT(SMALLINT,[RecoveryModel]) AS RecoveryModel , CONVERT(BIT,[Trustworthy]) AS Trustworthy FROM (SELECT urn, server_instance_name, property_name, property_value, batch_time FROM [sysutility_ucp_staging].[latest_smo_properties] WHERE object_type = 2) props -- object_type = 1 is Database PIVOT ( MAX(property_value) FOR property_name IN ( [powershell_path] , [ID] , [Collation] , [CompatibilityLevel] , [CreateDate] , [EncryptionEnabled] , [Name] , [RecoveryModel] , [Trustworthy] ) ) AS pvt SET @row_count = @@ROWCOUNT; SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('Insert into [databases_internal]: %d rows, %d ms', 0, 1, @row_count, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); INSERT INTO [sysutility_ucp_core].[filegroups_internal] ([urn] , [powershell_path] , [processing_time] , [batch_time] , [server_instance_name] , [database_name] , [parent_urn] , [Name]) SELECT [urn] , CONVERT(NVARCHAR(MAX), [powershell_path]) AS [powershell_path] , @processing_time_current AS processing_time -- $FIXED: SQLBUVSTS-316258 , SWITCHOFFSET ([batch_time], DATENAME(TZoffset, SYSDATETIMEOFFSET())) AS [batch_time] , [server_instance_name] , CONVERT(SYSNAME,[parent_name]) AS [database_name] , Left(urn, CHARINDEX('/FileGroup[', urn, 1)-1) AS parent_urn , CONVERT(SYSNAME,[Name]) AS Name FROM (SELECT urn, server_instance_name, property_name, property_value, batch_time FROM [sysutility_ucp_staging].[latest_smo_properties] WHERE object_type = 4) props -- object_type = 4 is FileGroup PIVOT ( MAX(property_value) FOR property_name IN ( [powershell_path] , [parent_name] , [ID] , [Name]) ) AS pvt SET @row_count = @@ROWCOUNT; SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('Insert into [filegroups_internal]: %d rows, %d ms', 0, 1, @row_count, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); INSERT INTO [sysutility_ucp_core].[datafiles_internal] ([urn] , [powershell_path] , [processing_time] , [batch_time] , [server_instance_name] , [database_name] , [filegroup_name] , [parent_urn] , [Growth] , [GrowthType] , [MaxSize] , [Name] , [Size] , [UsedSpace] , [FileName] , [VolumeFreeSpace] , [volume_name] , [volume_device_id] , [physical_server_name]) SELECT [urn] , CONVERT(NVARCHAR(MAX), [powershell_path]) AS [powershell_path] , @processing_time_current AS processing_time -- $FIXED: SQLBUVSTS-316258 , SWITCHOFFSET (pvt.[batch_time], DATENAME(TZoffset, SYSDATETIMEOFFSET())) AS [batch_time] , pvt.[server_instance_name] , CONVERT(SYSNAME,[grandparent_name]) AS [database_name] , CONVERT(SYSNAME,[parent_name]) AS [filegroup_name] , Left(urn, CHARINDEX('/File[', urn, 1)-1) AS parent_urn , CONVERT(REAL,[Growth]) AS Growth , CONVERT(SMALLINT,[GrowthType]) AS GrowthType , CONVERT(REAL,[MaxSize]) AS MaxSize , CONVERT(SYSNAME,[Name]) AS Name , CONVERT(REAL,[Size]) AS Size , CONVERT(REAL,[UsedSpace]) AS UsedSpace , CONVERT(NVARCHAR(260),[FileName]) AS FileName , ISNULL(v.free_space, 0.0) * 1024 AS VolumeFreeSpace -- volumes_internal.free_space is MB, and VolumeFreeSpace is expected to be KB. , ISNULL(v.volume_name, N'') AS volume_name , v.[volume_device_id] AS [volume_device_id] , pvt.[physical_server_name] FROM (SELECT urn, physical_server_name, server_instance_name, property_name, property_value, batch_time FROM [sysutility_ucp_staging].[latest_smo_properties] WHERE object_type = 5) props -- object_type = 5 is DataFile PIVOT ( MAX(property_value) FOR property_name IN ( [powershell_path] , [parent_name] , [grandparent_name] , [ID] , [Growth] , [GrowthType] , [MaxSize] , [Name] , [Size] , [UsedSpace] , [FileName] , [volume_device_id]) ) AS pvt LEFT OUTER JOIN [sysutility_ucp_core].[volumes_internal] v ON v.physical_server_name = pvt.physical_server_name AND CONVERT(SYSNAME, pvt.[volume_device_id]) = v.volume_device_id WHERE v.processing_time = @processing_time_current SET @row_count = @@ROWCOUNT; SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('Insert into [datafiles_internal]: %d rows, %d ms', 0, 1, @row_count, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); INSERT INTO [sysutility_ucp_core].[logfiles_internal] ([urn] , [powershell_path] , [processing_time] , [batch_time] , [server_instance_name] , [database_name] , [parent_urn] , [Growth] , [GrowthType] , [MaxSize] , [Name] , [Size] , [UsedSpace] , [FileName] , [VolumeFreeSpace] , [volume_name] , [volume_device_id] , [physical_server_name]) SELECT[urn] , CONVERT(NVARCHAR(MAX), [powershell_path]) AS [powershell_path] , @processing_time_current AS processing_time -- $FIXED: SQLBUVSTS-316258 , SWITCHOFFSET (pvt.[batch_time], DATENAME(TZoffset, SYSDATETIMEOFFSET())) AS [batch_time] , pvt.[server_instance_name] , CONVERT(SYSNAME,[parent_name]) AS [database_name] , Left(urn, CHARINDEX('/LogFile[', urn, 1)-1) AS parent_urn , CONVERT(REAL,[Growth]) AS Growth , CONVERT(SMALLINT,[GrowthType]) AS GrowthType , CONVERT(REAL,[MaxSize]) AS MaxSize , CONVERT(SYSNAME,[Name]) AS Name , CONVERT(REAL,[Size]) AS Size , CONVERT(REAL,[UsedSpace]) AS UsedSpace , CONVERT(NVARCHAR(260),[FileName]) AS FileName , ISNULL(v.free_space, 0.0) * 1024 AS VolumeFreeSpace -- volumes_internal.free_space is MB, and VolumeFreeSpace is expected to be KB. , ISNULL(v.volume_name, N'') AS volume_name , v.[volume_device_id] AS [volume_device_id] , pvt.[physical_server_name] FROM (SELECT urn, physical_server_name, server_instance_name, property_name, property_value, batch_time FROM [sysutility_ucp_staging].[latest_smo_properties] WHERE object_type = 3) props -- object_type = 3 is LogFile PIVOT ( MAX(property_value) FOR property_name IN ( [powershell_path] , [parent_name] , [ID] , [Growth] , [GrowthType] , [MaxSize] , [Name] , [Size] , [UsedSpace] , [FileName] , [volume_device_id]) ) AS pvt LEFT OUTER JOIN [sysutility_ucp_core].[volumes_internal] v ON v.physical_server_name = pvt.physical_server_name AND CONVERT(SYSNAME, pvt.[volume_device_id]) = v.volume_device_id WHERE v.processing_time = @processing_time_current SET @row_count = @@ROWCOUNT; SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('Insert into [logfiles_internal]: %d rows, %d ms', 0, 1, @row_count, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); ----- ----- Stage 2: Insert into all the measure tables ----- cpu_utilization_internal (computers, instances, dacs) ----- space_utilization_internal (volumes, instances, databases, filegroups, datafiles, logfiles) ----- ----- INSERT INTO [sysutility_ucp_core].[cpu_utilization_internal]( aggregation_type, object_type, processing_time, physical_server_name, server_instance_name, database_name, percent_total_cpu_utilization) SELECT 0, -- No aggregation 1, -- Computer Object @processing_time_current, physical_server_name, N'', N'', percent_total_cpu_utilization FROM [sysutility_ucp_core].[computers_internal] WHERE processing_time = @processing_time_current UNION ALL SELECT 0, -- No aggregation 3, -- Instance object @processing_time_current, N'', server_instance_name, N'', instance_processor_usage FROM [sysutility_ucp_staging].[latest_instance_cpu_utilization] UNION ALL SELECT 0, -- No aggregation 4, -- Database/DAC object @processing_time_current, N'', -- computer_name server_instance_name, dac_name, dac_percent_total_cpu_utilization FROM [sysutility_ucp_core].[dacs_internal] WHERE processing_time = @processing_time_current SET @row_count = @@ROWCOUNT; SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('Insert into [cpu_utilization_internal]: %d rows, %d ms', 0, 1, @row_count, @task_elapsed_ms); SET @task_start_time = GETUTCDATE(); INSERT INTO [sysutility_ucp_core].[space_utilization_internal] ( aggregation_type, object_type, processing_time, virtual_server_name, volume_device_id, server_instance_name, database_name, [filegroup_name], dbfile_name, total_space_bytes, allocated_space_bytes, used_space_bytes, available_space_bytes) SELECT 0 AS aggregation_type, CASE WHEN group_id = 0 AND [filegroup_name] = N'' THEN 7 -- logfile WHEN group_id = 0 THEN 6 -- datafile WHEN group_id = 1 THEN 5 -- filegroup WHEN group_id = 3 THEN 4 -- database WHEN group_id = 7 THEN 3 -- instance ELSE NULL -- should never get here END as [object_type], @processing_time_current AS processing_time, N'' as virtual_server_name, N'' as volume_device_id, ISNULL(server_instance_name, N''), -- shouldn't ever get to be null ISNULL(database_name, N''), ISNULL([filegroup_name], N''), ISNULL([dbfile_name], N''), CASE WHEN group_id = 0 THEN total_space_kb * 1024 ELSE NULL END, CASE WHEN group_id = 0 THEN allocated_space_kb * 1024 ELSE NULL END, used_space_kb * 1024, CASE WHEN group_id = 0 THEN available_space_kb * 1024 ELSE NULL END FROM ( SELECT server_instance_name, database_name, [filegroup_name], dbfile_name, SUM(MaxSize) AS total_space_kb, -- Is this right? SUM([Size]) AS allocated_space_kb, SUM(UsedSpace) AS used_space_kb, SUM(available_space) AS available_space_kb, GROUPING_ID(server_instance_name, database_name, [filegroup_name], dbfile_name) AS group_id FROM (SELECT server_instance_name, database_name, [filegroup_name], [Name] as dbfile_name, MaxSize, [Size], UsedSpace, [sysutility_ucp_misc].[fn_get_max_size_available]([Size], [MaxSize], [Growth], [GrowthType], [VolumeFreeSpace]) AS available_space FROM [sysutility_ucp_core].[datafiles_internal] WHERE processing_time = @processing_time_current UNION ALL SELECT server_instance_name, database_name, N'' AS [filegroup_name], [Name] AS dbfile_name, MaxSize, [Size], UsedSpace, [sysutility_ucp_misc].[fn_get_max_size_available]([Size], [MaxSize], [Growth], [GrowthType], [VolumeFreeSpace]) AS available_space FROM [sysutility_ucp_core].[logfiles_internal] WHERE processing_time = @processing_time_current) as dbfiles GROUP BY GROUPING SETS((server_instance_name), (server_instance_name, database_name), (server_instance_name, database_name, [filegroup_name]), (server_instance_name, database_name, [filegroup_name], [dbfile_name]) ) ) AS instance_space_utilizations UNION ALL SELECT 0 AS aggregation_type, CASE WHEN GROUPING_ID(virtual_server_name, volume_device_id) = 3 THEN 0 -- utility WHEN GROUPING_ID(virtual_server_name, volume_device_id) = 1 THEN 1 -- computer WHEN GROUPING_ID(virtual_server_name, volume_device_id) = 0 THEN 2 -- volume ELSE NULL -- should never get here END AS object_type, @processing_time_current as processing_time, ISNULL(virtual_server_name, N''), ISNULL(volume_device_id, N'') AS volume_device_id, N'' as server_instance_name, N'' as database_name, N'' as [filegroup_name], N'' as dbfile_name, SUM(total_space_available)*1048576 AS total_space_bytes, SUM(total_space_available)*1048576 AS allocated_space_bytes, SUM(total_space_available - free_space)*1048576 AS used_space_bytes, SUM(free_space)*1048576 AS available_space_bytes FROM [sysutility_ucp_core].[volumes_internal] WHERE processing_time = @processing_time_current GROUP BY GROUPING SETS ((), (virtual_server_name), (virtual_server_name, volume_device_id) ) SET @row_count = @@ROWCOUNT; SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('Insert into [space_utilization_internal]: %d rows, %d ms', 0, 1, @row_count, @task_elapsed_ms); -- -- State changes -- UPDATE [msdb].[dbo].[sysutility_ucp_processing_state_internal] SET latest_processing_time = @processing_time_current -- Update the snapshot partitions table -- Push down the previous snapshot partition values and -- store the current max snapshot in the latest (top) record UPDATE [msdb].[dbo].[sysutility_ucp_snapshot_partitions_internal] SET latest_consistent_snapshot_id = (SELECT TOP 1 latest_consistent_snapshot_id FROM [msdb].[dbo].[sysutility_ucp_snapshot_partitions_internal] WHERE time_id = 1) WHERE time_id = 2 UPDATE [msdb].[dbo].[sysutility_ucp_snapshot_partitions_internal] SET latest_consistent_snapshot_id = (SELECT TOP 1 latest_consistent_snapshot_id FROM [msdb].[dbo].[sysutility_ucp_snapshot_partitions_internal] WHERE time_id = 0) WHERE time_id = 1 UPDATE [msdb].[dbo].[sysutility_ucp_snapshot_partitions_internal] SET latest_consistent_snapshot_id = @max_snapshot_id WHERE time_id = 0 -- As we have inserted chunk of data in the cache tables, the stats on these tables -- get stale there by leading to performance degradation of the health state queries -- Force update the stats on these tables so that the QO is able to generate a more -- realisitc query execution plan SET @task_start_time = GETUTCDATE(); UPDATE STATISTICS [msdb].[dbo].[sysutility_ucp_processing_state_internal]; -- Update stats on all dimension and measure cache tables DECLARE @schema sysname DECLARE @name sysname DECLARE @query NVARCHAR(MAX) DECLARE cache_tables CURSOR FOR SELECT object_schema, [object_name] FROM sysutility_ucp_misc.utility_objects_internal WHERE utility_object_type IN ('DIMENSION', 'MEASURE'); OPEN cache_tables; FETCH NEXT FROM cache_tables INTO @schema, @name; WHILE (@@FETCH_STATUS <> -1) BEGIN SET @query = 'UPDATE STATISTICS ' + QUOTENAME (@schema) + '.' + QUOTENAME (@name); EXEC (@query); FETCH NEXT FROM cache_tables INTO @schema, @name; END; CLOSE cache_tables; DEALLOCATE cache_tables; SET @task_elapsed_ms = DATEDIFF (ms, @task_start_time, GETUTCDATE()); RAISERROR ('Update statistics: %d ms', 0, 1, @task_elapsed_ms); END GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'STAGING', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_staging', @level1type = 'PROCEDURE', @level1name = 'sp_copy_live_table_data_into_cache_tables'; GO /***********************************************************************/ /* Utility SCHEMA: (sysutility_ucp_core) */ /* Dimension Tables and Views */ /* */ /* We currently handle the following dimensions (and hence dimension */ /* tables). */ /* Computers (Table: computers_internal; view: latest_computers) */ /* Volumes (Table: volumes_internal; view: latest_volumes) */ /* Instances (Table: smo_servers_internal; view: latest_smo_servers) */ /* Databases (Table: databases_internal; view: latest_databases) */ /* FileGroups (Table: filegroups_internal; view: latest_filegroups) */ /* DataFiles (Table: datafiles_internal; view: latest_datafiles) */ /* LogFiles (Table: logfiles_internal; view: latest_logfiles) */ /* Dacs (Table: dacs_internal; view: latest_dacs) */ /* */ /* Every dimension table is clustered on its primary key. Each table */ /* has processing_time as the prefix of its primary. This allows for */ /* efficient queue-like behavior - inserts at the end, purges at the */ /* beginning, and queries typically at the end. */ /* */ /* Each of these dimension tables has a corresponding view that picks */ /* the latest consistent information for that dimension. The latest */ /* consistent information is determined by the value of the */ /* "latest_processing_time" column in sysutility_ucp_processing_state */ /* table in MSDB */ /***********************************************************************/ ------------------------------------------------------------------------------------------------------------ -- Table dacs_internal -- This is technically a dimension table for DACs. -- (For various logisical reasons, this also contains CPU utilization information for DACs.) -- The key of this table is (processing_time, server_instance_name, dac_name). -- Machine_name is a regular column, but does not need to be part of the key (server_instance_name already -- includes the appropriate information) ------------------------------------------------------------------------------------------------------------ IF (OBJECT_ID(N'[sysutility_ucp_core].[dacs_internal]', 'U') IS NULL) BEGIN RAISERROR('Creating [sysutility_ucp_core].[dacs_internal] table', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_core].[dacs_internal] ( -- todo (VSTS #345036): This column will be removed [dac_id] INT IDENTITY, [server_instance_name] SYSNAME, -- the server-qualified instance name [dac_name] SYSNAME, [urn] NVARCHAR(4000), [powershell_path] NVARCHAR(4000), [physical_server_name] SYSNAME, [dac_deploy_date] DATETIME, [dac_description] NVARCHAR(4000) NULL, -- todo (VSTS #345040) -- This is technically a "measure" column and should not be part of this dimension table [dac_percent_total_cpu_utilization] REAL, [processing_time] DATETIMEOFFSET(7), [batch_time] DATETIMEOFFSET(7), CONSTRAINT [PK_dacs_internal] PRIMARY KEY CLUSTERED (processing_time, server_instance_name, dac_name) ) EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'DIMENSION', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'TABLE', @level1name = 'dacs_internal'; END GO ------------------------------------------------------------------------------ -- SQL Server View to read latest data for all DACs from cache table -- NOTE: If you change the output of this view in any way, be sure to also update the -- corresponding "stub" object in instmsdb.sql. ------------------------------------------------------------------------------ IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_core].[latest_dacs]')) BEGIN RAISERROR('Dropping [sysutility_ucp_core].[latest_dacs] view', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_core].[latest_dacs] END GO RAISERROR('Creating [sysutility_ucp_core].[latest_dacs] view', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_core].[latest_dacs] AS SELECT dac_id, server_instance_name, dac_name, physical_server_name, dac_deploy_date, dac_description, urn, powershell_path, processing_time, batch_time, dac_percent_total_cpu_utilization FROM [sysutility_ucp_core].[dacs_internal] S WHERE S.processing_time = (SELECT latest_processing_time FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal]) GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'CORE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'VIEW', @level1name = 'latest_dacs'; GO ----------------------------------------------------------------------- -- -- Dimension table: computers_internal. -- (For logistical reasons, also includes percent_total_cpu_consumption) -- Key: (processing_time, physical_server_name) -- -- NOTE: We use physical_server_name as part of the key rather than the -- (logical) virtual_server_name. This is to allow for scenarios with clustering -- where we have two or more servers clustered together but with potentially -- different configurations. -- ----------------------------------------------------------------------- IF NOT EXISTS (SELECT name FROM sys.objects WHERE object_id = OBJECT_ID(N'[sysutility_ucp_core].[computers_internal]')) BEGIN RAISERROR('Creating table [sysutility_ucp_core].[computers_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_core].[computers_internal] ( -- todo (VSTS #345036): This column will be removed [id] INT IDENTITY, virtual_server_name SYSNAME, physical_server_name SYSNAME, -- differs from virtual_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), -- todo (VSTS #345040) -- This is technically a "measure" column and should not be part of this dimension table percent_total_cpu_utilization REAL, urn NVARCHAR(4000), powershell_path NVARCHAR(4000), processing_time DATETIMEOFFSET(7), batch_time DATETIMEOFFSET(7), CONSTRAINT [PK_computers_internal] PRIMARY KEY CLUSTERED (processing_time, physical_server_name) ) EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'DIMENSION', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'TABLE', @level1name = 'computers_internal'; END GO ----------------------------------------------------------------------- -- View to select latest data from [computers_internal] dimension table. -- NOTE: If you change the shape of this view in any way, be sure to also -- update the corresponding "stub" object in instmsdb.sql. ----------------------------------------------------------------------- IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_core].[latest_computers]')) BEGIN RAISERROR('Dropping [sysutility_ucp_core].[latest_computers] view', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_core].[latest_computers] END GO RAISERROR('Creating view [sysutility_ucp_core].[latest_computers]', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_core].[latest_computers] AS SELECT [id], virtual_server_name, physical_server_name, is_clustered_server, num_processors, cpu_name, cpu_caption, cpu_family, cpu_architecture, cpu_max_clock_speed, cpu_clock_speed, l2_cache_size, l3_cache_size, urn, powershell_path, processing_time, batch_time, percent_total_cpu_utilization FROM [sysutility_ucp_core].[computers_internal] WHERE processing_time = (SELECT latest_processing_time FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal]); GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'CORE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'VIEW', @level1name = 'latest_computers'; GO ----------------------------------------------------------------------- -- Dimension table: volumes_internal -- - PK (processing_time, physical_server_name, volume_name) -- - also includes physical_server_name -- -- - Includes "measure" columns (total_space_available, free_space) -- Created cache table [sysutility_ucp_core].[volumes_internal] for storage view. ----------------------------------------------------------------------- IF NOT EXISTS (SELECT name FROM sys.objects WHERE object_id = OBJECT_ID(N'[sysutility_ucp_core].[volumes_internal]') ) BEGIN RAISERROR('Creating table [sysutility_ucp_core].[volumes_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_core].[volumes_internal] ( -- todo (VSTS #345036): This column will be removed [ID] INT IDENTITY, virtual_server_name SYSNAME, physical_server_name SYSNAME, volume_device_id SYSNAME, volume_name NVARCHAR(260), -- todo (VSTS #345040) -- These are technically "measure" columns and should not be part of this dimension table total_space_available real, -- in MB free_space real, -- in MB processing_time DATETIMEOFFSET(7), batch_time DATETIMEOFFSET(7), CONSTRAINT pk_volumes_internal PRIMARY KEY CLUSTERED(processing_time, physical_server_name, volume_device_id) ) EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'DIMENSION', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'TABLE', @level1name = 'volumes_internal'; END GO ------------------------------------------------------------------------------ -- SQL Server View to read latest information for volumes -- NOTE: If you change the output of this view in any way, be sure to also update the -- corresponding "stub" object in instmsdb.sql. ------------------------------------------------------------------------------ IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_core].[latest_volumes]')) BEGIN RAISERROR('Dropping view [sysutility_ucp_core].[latest_volumes]', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_core].[latest_volumes] END GO RAISERROR('Creating view [sysutility_ucp_core].[latest_volumes]', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_core].[latest_volumes] AS SELECT [ID], [virtual_server_name], [physical_server_name], [volume_device_id], [volume_name], [processing_time], [batch_time], [total_space_available], (total_space_available - free_space) AS [total_space_utilized], (CASE WHEN total_space_available = 0 THEN 0 ELSE (100 * (total_space_available - free_space))/total_space_available END) AS [percent_total_space_utilization] FROM [sysutility_ucp_core].[volumes_internal] WHERE processing_time = (SELECT latest_processing_time FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal]) GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'CORE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'VIEW', @level1name = 'latest_volumes'; GO -- ============================================= -- Dimension Table for SQL Server Instances -- Table: smo_servers_internal -- Key: processing_time, server_instance_name -- ============================================= IF(OBJECT_ID(N'[sysutility_ucp_core].[smo_servers_internal]', 'U') IS NULL) BEGIN RAISERROR ('Creating table [sysutility_ucp_core].[smo_servers_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_core].[smo_servers_internal] ( [urn] NVARCHAR(320) , [powershell_path] NVARCHAR(4000) , [processing_time] DATETIMEOFFSET(7) , [batch_time] DATETIMEOFFSET(7) -- SMO properties , [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) -- This is SERVERPROPERTY('ServerName') , [NamedPipesEnabled] BIT , [NetName] NVARCHAR(128) -- This is SERVERPROPERTY('MachineName') , [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) CONSTRAINT [PK_smo_servers_internal] PRIMARY KEY CLUSTERED (processing_time, [Name]) -- NOTE: compression is enabled on this table at runtime (during Create UCP) in sp_initialize_mdw_internal ); EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'DIMENSION', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'TABLE', @level1name = 'smo_servers_internal'; END GO -- ===================================================================== -- Dimension Table for databases in a SQL Instance -- Table: databases_internal -- Key: processing_time, server_instance_name, name -- ===================================================================== IF(OBJECT_ID(N'[sysutility_ucp_core].[databases_internal]', 'U') IS NULL) BEGIN RAISERROR ('Creating table [sysutility_ucp_core].[databases_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_core].[databases_internal] ( [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] SYSNAME , [RecoveryModel] SMALLINT , [Trustworthy] BIT, CONSTRAINT [PK_databases_internal] PRIMARY KEY CLUSTERED (processing_time, server_instance_name, [Name]) -- Note: compression is enabled on this table at runtime (during Create UCP) in sp_initialize_mdw_internal ); EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'DIMENSION', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'TABLE', @level1name = 'databases_internal'; END GO -- ===================================================================== -- Dimension Table for filegroups in a database (in a SQL Instance) -- Table: filegroups_internal -- Key: processing_time, server_instance_name, database_name, name -- ===================================================================== IF(OBJECT_ID(N'[sysutility_ucp_core].[filegroups_internal]', 'U') IS NULL) BEGIN RAISERROR ('Creating table [sysutility_ucp_core].[filegroups_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_core].[filegroups_internal] ( [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) -- SMO Properties , [Name] SYSNAME , CONSTRAINT PK_filegroups_internal PRIMARY KEY CLUSTERED(processing_time, server_instance_name, database_name, [Name]) -- Note: compression is enabled on this table at runtime (during Create UCP) in sp_initialize_mdw_internal ); EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'DIMENSION', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'TABLE', @level1name = 'filegroups_internal'; END GO -- ===================================================================== -- Dimension Table for datafiles in a database (in a SQL Instance) -- Table: datafiles_internal -- Key: processing_time, server_instance_name, database_name, filegroup_name, name -- -- VSTS #345570: The key length of the clustered index may be larger than 900 bytes. -- -- ===================================================================== IF(OBJECT_ID(N'[sysutility_ucp_core].[datafiles_internal]', 'U') IS NULL) BEGIN RAISERROR ('Creating table [sysutility_ucp_core].[datafiles_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_core].[datafiles_internal] ( [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] NVARCHAR(260) , [volume_device_id] SYSNAME -- SMO Properties , [Growth] REAL , [GrowthType] SMALLINT , [MaxSize] REAL , [Name] SYSNAME , [Size] REAL , [UsedSpace] REAL , [FileName] NVARCHAR(260) , [VolumeFreeSpace] BIGINT , CONSTRAINT PK_datafiles_internal PRIMARY KEY CLUSTERED (processing_time, server_instance_name, database_name, [filegroup_name], [Name]) -- Note: compression is enabled on this table at runtime (during Create UCP) in sp_initialize_mdw_internal ); EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'DIMENSION', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'TABLE', @level1name = 'datafiles_internal'; END GO -- ===================================================================== -- Dimension Table for logfiles in a database (in a SQL Instance) -- Table: logfiles_internal -- Key: processing_time, server_instance_name, database_name, name -- ===================================================================== IF(OBJECT_ID(N'[sysutility_ucp_core].[logfiles_internal]', 'U') IS NULL) BEGIN RAISERROR ('Creating table [sysutility_ucp_core].[logfiles_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_core].[logfiles_internal] ( [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] NVARCHAR(260) , [volume_device_id] SYSNAME -- SMO Properties , [Growth] REAL , [GrowthType] SMALLINT , [MaxSize] REAL , [Name] SYSNAME , [Size] REAL , [UsedSpace] REAL , [FileName] NVARCHAR(260) , [VolumeFreeSpace] BIGINT , CONSTRAINT PK_logfiles_internal PRIMARY KEY CLUSTERED (processing_time, server_instance_name, database_name, [Name]) -- Note: compression is enabled on this table at runtime (during Create UCP) in sp_initialize_mdw_internal ); EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'DIMENSION', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'TABLE', @level1name = 'logfiles_internal'; END GO ----------------------------------------------------------------------------- -- The view which returns server properties -- NOTE: If you change the output of this view in any way, be sure to also update the -- corresponding "stub" object in instmsdb.sql. ----------------------------------------------------------------------------- IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_core].[latest_smo_servers]')) BEGIN RAISERROR('Dropping [sysutility_ucp_core].[latest_smo_servers] view', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_core].[latest_smo_servers] END GO RAISERROR('Creating [sysutility_ucp_core].[latest_smo_servers] view', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_core].[latest_smo_servers] AS SELECT urn , [powershell_path] , [processing_time] , [batch_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 [sysutility_ucp_core].[smo_servers_internal] AS SI WHERE SI.processing_time = (SELECT latest_processing_time FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal]) GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'CORE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'VIEW', @level1name = 'latest_smo_servers'; GO ------------------------------------------------------------------------------ -- SQL Server View to read latest snapshot data for SMO Database object -- NOTE: If you change the output of this view in any way, be sure to also update the -- corresponding "stub" object in instmsdb.sql. ------------------------------------------------------------------------------ IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_core].[latest_databases]')) BEGIN RAISERROR('Dropping [sysutility_ucp_core].[latest_databases] view', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_core].[latest_databases] END GO RAISERROR('Creating [sysutility_ucp_core].[latest_databases] view', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_core].[latest_databases] AS SELECT [urn] , [powershell_path] , [processing_time] , [batch_time] , [server_instance_name] , [parent_urn] , [Collation] , [CompatibilityLevel] , [CreateDate] , [EncryptionEnabled] , [Name] , [RecoveryModel] , [Trustworthy] FROM [sysutility_ucp_core].[databases_internal] AS SI WHERE SI.processing_time = (SELECT latest_processing_time FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal]) GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'CORE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'VIEW', @level1name = 'latest_databases'; GO ------------------------------------------------------------------------------ -- SQL Server View to read latest snapshot data for SMO FileGroup object -- NOTE: If you change the output of this view in any way, be sure to also update the -- corresponding "stub" object in instmsdb.sql. ------------------------------------------------------------------------------ IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_core].[latest_filegroups]')) BEGIN RAISERROR('Dropping [sysutility_ucp_core].[latest_filegroups] view', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_core].[latest_filegroups] END GO RAISERROR('Creating [sysutility_ucp_core].[latest_filegroups] view', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_core].[latest_filegroups] AS SELECT [urn] , [powershell_path] , [processing_time] , [batch_time] , [server_instance_name] , [database_name] , [parent_urn] , [Name] FROM [sysutility_ucp_core].[filegroups_internal] AS SI WHERE SI.processing_time = (SELECT latest_processing_time FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal]) GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'CORE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'VIEW', @level1name = 'latest_filegroups'; GO ------------------------------------------------------------------------------ -- SQL Server View to read latest snapshot data for SMO DataFile object -- NOTE: If you change the output of this view in any way, be sure to also update the -- corresponding "stub" object in instmsdb.sql. ------------------------------------------------------------------------------ IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_core].[latest_datafiles]')) BEGIN RAISERROR('Dropping [sysutility_ucp_core].[latest_datafiles] view', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_core].[latest_datafiles] END GO RAISERROR('Creating [sysutility_ucp_core].[latest_datafiles] view', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_core].[latest_datafiles] AS SELECT [urn] , [powershell_path] , [processing_time] , [batch_time] , [server_instance_name] , [database_name] , [filegroup_name] , [parent_urn] , [physical_server_name] , [volume_name] , [volume_device_id] , [Growth] , [GrowthType] , [MaxSize] , [Name] , [Size] , [UsedSpace] , [FileName] , [VolumeFreeSpace] , [sysutility_ucp_misc].[fn_get_max_size_available]([Size], [MaxSize], [Growth], [GrowthType], [VolumeFreeSpace]) AS [available_space] FROM [sysutility_ucp_core].[datafiles_internal] AS SI WHERE SI.processing_time = (SELECT latest_processing_time FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal]) GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'CORE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'VIEW', @level1name = 'latest_datafiles'; GO ------------------------------------------------------------------------------ -- SQL Server View to read latest snapshot data for SMO DataFile object -- NOTE: If you change the output of this view in any way, be sure to also update the -- corresponding "stub" object in instmsdb.sql. ------------------------------------------------------------------------------ IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_core].[latest_logfiles]')) BEGIN RAISERROR('Dropping [sysutility_ucp_core].[latest_logfiles] view', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_core].[latest_logfiles] END GO RAISERROR('Creating [sysutility_ucp_core].[latest_logfiles] view', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_core].[latest_logfiles] AS SELECT [urn] , [powershell_path] , [processing_time] , [batch_time] , [server_instance_name] , [database_name] , [parent_urn] , [physical_server_name] , [volume_name] , [volume_device_id] , [Growth] , [GrowthType] , [MaxSize] , [Name] , [Size] , [UsedSpace] , [FileName] , [VolumeFreeSpace] , [sysutility_ucp_misc].[fn_get_max_size_available]([Size], [MaxSize], [Growth], [GrowthType], [VolumeFreeSpace]) AS [available_space] FROM [sysutility_ucp_core].[logfiles_internal] AS SI WHERE SI.processing_time = (SELECT latest_processing_time FROM [msdb].[dbo].[sysutility_ucp_processing_state_internal]) GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'CORE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'VIEW', @level1name = 'latest_logfiles'; GO /***********************************************************************/ /* Utility SCHEMA: (sysutility_ucp_core) */ /* Measure Tables and Views */ /* */ /* We currently handle the following measures (and hence measure */ /* tables). */ /* CPU (Table: cpu_utilization_internal) */ /* Storage Space (Table: space_utilization_internal) */ /* */ /* CPU information is stored for the following dimensions */ /* - Computers */ /* - Instances */ /* - Dacs */ /* */ /* Storage space information is stored for the following dimensions */ /* - Computers */ /* - Volumes */ /* - Instances */ /* - Databases */ /* - FileGroups */ /* - DataFiles */ /* - LogFiles */ /* */ /* Each measure table stores information at different levels of */ /* aggregation. Currently, we support 3 levels of aggregation */ /* - No aggregation (i.e.) latest "detail" information */ /* - Hourly */ /* - Daily */ /* We expect to add additional aggregation levels in the future */ /* */ /* Information for each aggregation level is stored in a different */ /* partition of the measure table. This allows us to leverage */ /* partition pruning (for queries), and different maintenance operations */ /* for each partition (especially wrt purging of data. */ /* */ /* Within each partition, (processing_time, object_type) */ /* is the prefix of the key. This allows for inserts at the end, and */ /* purges at the front for new data. It also ensures that information */ /* about each object type is collocated within a given collection time */ /* */ /***********************************************************************/ --- -- Describes the aggregation level -- 0 = Non-aggregated; 1 = Hourly, 2 = Daily, ... -- IF NOT EXISTS(SELECT 0 FROM sys.types t WHERE t.name = N'AggregationType' AND t.schema_id = SCHEMA_ID(N'sysutility_ucp_core')) BEGIN RAISERROR ('Creating type [sysutility_ucp_core].[AggregationType]', 0, 1) WITH NOWAIT; CREATE TYPE [sysutility_ucp_core].[AggregationType] FROM TINYINT END GO -- 0 = utility -- 1 = computer -- 2 = volume -- 3 = instance -- 4 = database (also dac) -- 5 = filegroup -- 6 = datafile -- 7 = logfile IF NOT EXISTS(SELECT 0 FROM sys.types t WHERE t.name = N'ObjectType' AND t.[schema_id] = SCHEMA_ID(N'sysutility_ucp_core')) BEGIN RAISERROR ('Creating type [sysutility_ucp_core].[ObjectType]', 0, 1) WITH NOWAIT; CREATE TYPE [sysutility_ucp_core].[ObjectType] FROM TINYINT END GO -- ============================================================================ -- Measure Table: CPU Utilization information -- Supported dimensions: Computers, Instances, Databases (DACs) -- Supported aggregation-levels: none, hourly, daily -- -- The object_type field describes the dimension that the current entry (row) -- defines. -- The following conditions must hold. -- If object_type = 1, server_instance_name = NULL and database_name = NULL and physical_server_name <> NULL -- If object type = 3, physical_server_name = NULL, database_name = NULL, server_instance_name <> NULL -- If object_type = 4, physical_server_name = NULL, server_instance_name <> NULL, database_name <> NULL -- No other legal combinations -- ============================================================================ IF(OBJECT_ID(N'[sysutility_ucp_core].[cpu_utilization_internal]', N'U') IS NULL) BEGIN RAISERROR ('Creating table [sysutility_ucp_core].[cpu_utilization_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_core].[cpu_utilization_internal] ( [processing_time] DATETIMEOFFSET(7) NOT NULL, [aggregation_type] [sysutility_ucp_core].AggregationType NOT NULL, [object_type] [sysutility_ucp_core].ObjectType NOT NULL, -- Dimension keys [physical_server_name] SYSNAME DEFAULT N'', [server_instance_name] SYSNAME DEFAULT N'', [database_name] SYSNAME DEFAULT N'', -- The actual measure columns. percent_total_cpu_utilization REAL, -- NOTE: This index is redefined at runtime (during Create UCP) in sp_initialize_mdw_internal CONSTRAINT pk_cpu_utilization_internal PRIMARY KEY CLUSTERED(aggregation_type, processing_time, object_type, physical_server_name, server_instance_name, database_name) ) EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'MEASURE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'TABLE', @level1name = 'cpu_utilization_internal'; END GO --------------------------------------------------------------------------- -- View to select information from the [cpu_utilization_internal] measure -- table. -- NOTE: If you change the shape of this view in any way, be sure to also -- update the corresponding "stub" object in instmsdb.sql. ----------------------------------------------------------------------- IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_core].[cpu_utilization]')) BEGIN RAISERROR('Dropping [sysutility_ucp_core].[cpu_utilization] view', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_core].[cpu_utilization] END GO RAISERROR('Creating [sysutility_ucp_core].[cpu_utilization] view', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_core].[cpu_utilization] AS SELECT aggregation_type, processing_time, object_type, physical_server_name, server_instance_name, database_name, percent_total_cpu_utilization FROM [sysutility_ucp_core].[cpu_utilization_internal] GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'CORE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'VIEW', @level1name = 'cpu_utilization'; GO -- ============================================================================ -- Measure Table: Space Utilization information -- Supported dimensions: Computers, Volumes, Instances, Databases, -- Filegroups, DataFiles, LogFiles -- Supported aggregation-levels: none, hourly, daily -- -- The object_type field describes the dimension that the current entry (row) -- defines. -- The following conditions must hold. -- Utility (type=0), all dimension columns must be '' -- Computer (type=1), virtual_server_name must be non-null; the rest must be '' -- Volume (type=2), virtual_server_name, volume_device_id must be non-null; rest '' -- Instance (type=3), server_instance_name must be non-NULL, everything else must be '' -- Database (type=4), server_instance_name, database_name must be non-null -- other keys must be '' -- FileGroup(type=5), server_instance_name, datbase_name, filegroup_name must be non-NULL -- other keys must be '' -- DataFile (type=6), server_instance_name, database_name, filegroup_name, dbfile_name -- must be non-null. Other keys must be '' -- LogFile (type=7), server_instance_name, database_name, dbfile_name must be non-null -- other keys must be '' -- -- IMPORTANT: Unlike the cpu_utilization measure table, the space_utilization measure -- table uses virtual_server_name to represent a computer (and volume). This is -- because storage is shared (potentially) across a failover-cluster-instance, -- and we want to track history of the space usage regardless of the specific -- current "owner" of the storage -- -- The space_utilization_internal table stores information about two distinct hierarchies. -- The Utility->Computer->Volume hierarchy and the Instance->Database->FileGroup->File -- hiererachy. A row in the table is part of only one of these hierarchies. -- -- There are 4 distinct measure columns we maintain in this table. Not all of them are -- really need. See VSTS #345039 -- total_space_bytes : the maximum amount of storage available -- allocated_space_bytes: the currently allocated amount of storage. Typically, the -- same as total_space_bytes, except for files -- used_space_bytes : the current used up space -- available_space_bytes: the amount of space that's available for further use. -- Typically this is total_space_bytes - used_space_bytes. -- Except for files, where this is more complicated -- -- We should be able to live with just two of these columns (total_space and used_space) -- -- For the instance-database-... hierarchy, only used_space_bytes is rolled up. All -- the other values are rolled up to NULL -- For the Utility-computer-volume hierarchy, all values are rolled up. -- -- -- -- VSTS #345570: The key length of the clustered index may be larger than 900 bytes. -- -- ============================================================================ IF(OBJECT_ID(N'[sysutility_ucp_core].[space_utilization_internal]', N'U') IS NULL) BEGIN RAISERROR ('Creating table [sysutility_ucp_core].[space_utilization_internal]', 0, 1) WITH NOWAIT; CREATE TABLE [sysutility_ucp_core].[space_utilization_internal] ( [processing_time] DATETIMEOFFSET(7) NOT NULL, [aggregation_type] [sysutility_ucp_core].AggregationType NOT NULL, [object_type] [sysutility_ucp_core].ObjectType NOT NULL, -- The dimension columns [virtual_server_name] SYSNAME DEFAULT N'', [server_instance_name] SYSNAME DEFAULT N'', [volume_device_id] SYSNAME DEFAULT N'', [database_name] SYSNAME DEFAULT N'', [filegroup_name] SYSNAME DEFAULT N'', [dbfile_name] SYSNAME DEFAULT N'', -- todo (VSTS #345039) -- we don't need all 4 of the columns below. We only need used_space and available_space [used_space_bytes] REAL, [allocated_space_bytes] REAL, [total_space_bytes] REAL, [available_space_bytes] REAL, -- NOTE: This index is redefined at runtime (during Create UCP) in sp_initialize_mdw_internal CONSTRAINT pk_storage_utilization PRIMARY KEY CLUSTERED( aggregation_type, processing_time, object_type, virtual_server_name, volume_device_id, server_instance_name, database_name, [filegroup_name], dbfile_name) ) EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'MEASURE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'TABLE', @level1name = 'space_utilization_internal'; END GO --------------------------------------------------------------------------- -- View to select information from the [space_utilization_internal] measure -- table. -- NOTE: If you change the shape of this view in any way, be sure to also -- update the corresponding "stub" object in instmsdb.sql. ----------------------------------------------------------------------- IF EXISTS (SELECT name FROM sys.views WHERE object_id = OBJECT_ID(N'[sysutility_ucp_core].[space_utilization]')) BEGIN RAISERROR('Dropping [sysutility_ucp_core].[space_utilization] view', 0, 1) WITH NOWAIT; DROP VIEW [sysutility_ucp_core].[space_utilization] END GO RAISERROR('Creating [sysutility_ucp_core].[space_utilization] view', 0, 1) WITH NOWAIT; GO CREATE VIEW [sysutility_ucp_core].[space_utilization] AS SELECT aggregation_type, processing_time, object_type, virtual_server_name, volume_device_id, server_instance_name, database_name, [filegroup_name], dbfile_name, total_space_bytes, allocated_space_bytes, used_space_bytes, available_space_bytes FROM [sysutility_ucp_core].[space_utilization_internal] GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'CORE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'VIEW', @level1name = 'space_utilization'; GO ----------------------------------------------------------------------------------------- -- Procedure sp_copy_cache_table_data_into_aggregate_tables -- Aggregates the latest round of data from the "cache" tables into the "aggregate" tables -- for the appropriate aggregation interval ----------------------------------------------------------------------------------------- IF OBJECT_ID ('sysutility_ucp_core.sp_copy_cache_table_data_into_aggregate_tables') IS NOT NULL BEGIN RAISERROR('Dropping procedure sysutility_ucp_core.sp_copy_cache_table_data_into_aggregate_tables procedure', 0, 1) WITH NOWAIT; DROP PROCEDURE sysutility_ucp_core.sp_copy_cache_table_data_into_aggregate_tables; END GO RAISERROR('Creating procedure sysutility_ucp_core.sp_copy_cache_table_data_into_aggregate_tables', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE sysutility_ucp_core.sp_copy_cache_table_data_into_aggregate_tables @aggregation_type INT, @endTime DATETIMEOFFSET(7) AS BEGIN DECLARE @startTime DATETIMEOFFSET(7) DECLARE @lowerAggregationLevel [sysutility_ucp_core].AggregationType SELECT @lowerAggregationLevel = 0 -- compute from detail rows IF (@aggregation_type = 1) SELECT @startTime = DATEADD(hour, -1, @endTime) ELSE IF (@aggregation_type = 2) SELECT @startTime = DATEADD(day, -1, @endTime) -- todo (VSTS #345038) -- Ideally, we would be using the hourly aggregation values to compute the -- daily aggregation ("SELECT @lowerAggregationLevel = 1") -- ELSE BEGIN -- todo. Raise an error instead RETURN(1) END INSERT INTO [sysutility_ucp_core].[cpu_utilization_internal] ( aggregation_type, object_type, processing_time, physical_server_name, server_instance_name, database_name, percent_total_cpu_utilization) SELECT @aggregation_type, object_type, @endTime, physical_server_name, server_instance_name, database_name, AVG(percent_total_cpu_utilization) FROM [sysutility_ucp_core].[cpu_utilization_internal] WHERE (processing_time BETWEEN @startTime and @endTime) AND aggregation_type = @lowerAggregationLevel GROUP BY object_type, physical_server_name, server_instance_name, database_name INSERT INTO [sysutility_ucp_core].[space_utilization_internal] ( aggregation_type, object_type, processing_time, virtual_server_name, volume_device_id, server_instance_name, database_name, [filegroup_name], dbfile_name, total_space_bytes, allocated_space_bytes, used_space_bytes, available_space_bytes) SELECT @aggregation_type, object_type, @endTime, virtual_server_name, volume_device_id, server_instance_name, database_name, [filegroup_name], dbfile_name, -- Is AVG the right aggregate to use - should this instead be LAST() AVG(total_space_bytes), AVG(allocated_space_bytes), AVG(used_space_bytes), AVG(available_space_bytes) FROM [sysutility_ucp_core].[space_utilization_internal] WHERE (processing_time BETWEEN @startTime and @endTime) AND aggregation_type = @lowerAggregationLevel GROUP BY object_type, virtual_server_name, volume_device_id, server_instance_name, database_name, [filegroup_name], dbfile_name END GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'CORE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'PROCEDURE', @level1name = 'sp_copy_cache_table_data_into_aggregate_tables'; GO ----------------------------------------------------------------------------------------- -- Procedure sp_purge_cache_tables -- Deletes the data in Utility's MDW cache tables according to the data retention -- periods specified in msdb.dbo.sysutility_ucp_configuration_internal. ----------------------------------------------------------------------------------------- IF OBJECT_ID ('sysutility_ucp_core.sp_purge_cache_tables') IS NOT NULL BEGIN RAISERROR('Dropping procedure sysutility_ucp_core.sp_purge_cache_tables procedure', 0, 1) WITH NOWAIT; DROP PROCEDURE sysutility_ucp_core.sp_purge_cache_tables; END GO RAISERROR('Creating procedure sysutility_ucp_core.sp_purge_cache_tables', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE sysutility_ucp_core.sp_purge_cache_tables AS BEGIN DECLARE @rows_affected bigint; DECLARE @delete_batch_size varchar(30); SET @delete_batch_size = 500; SET @rows_affected = -1; DECLARE @days_to_retain_minute_data int; DECLARE @days_to_retain_hour_data int; DECLARE @days_to_retain_day_data int; SELECT @days_to_retain_minute_data = CONVERT (int,current_value) FROM [msdb].[dbo].[sysutility_ucp_configuration_internal] WHERE name = 'MdwRetentionLengthInDaysForMinutesHistory'; SELECT @days_to_retain_hour_data = CONVERT (int,current_value) FROM [msdb].[dbo].[sysutility_ucp_configuration_internal] WHERE name = 'MdwRetentionLengthInDaysForHoursHistory'; SELECT @days_to_retain_day_data = CONVERT (int,current_value) FROM [msdb].[dbo].[sysutility_ucp_configuration_internal] WHERE name = 'MdwRetentionLengthInDaysForDaysHistory'; DECLARE @date_threshold_minute_data DATETIMEOFFSET(7) = DATEADD(day, -@days_to_retain_minute_data, SYSDATETIMEOFFSET()); DECLARE @date_threshold_hour_data DATETIMEOFFSET(7) = DATEADD(day, -@days_to_retain_hour_data, SYSDATETIMEOFFSET()); DECLARE @date_threshold_day_data DATETIMEOFFSET(7) = DATEADD(day, -@days_to_retain_day_data, SYSDATETIMEOFFSET()); DECLARE @schema sysname DECLARE @name sysname DECLARE @query NVARCHAR(MAX) DECLARE dimensions_cursor CURSOR FOR SELECT object_schema, [object_name] FROM sysutility_ucp_misc.utility_objects_internal WHERE utility_object_type = 'DIMENSION'; -- Purge the dimension tables. -- The number of rows that can be deleted from these tables can be very large. If we deleted -- all of these rows in a single delete statement, we would hold locks for an arbitrarily-long -- time (and potentially escalate to table locks), causing long-duration blocking. This could -- also lead to transaction log growth, since log records after the oldest still-open transaction -- can't be truncated. To avoid these two problems, we delete rows in batches of 500 and loop -- until we've deleted all rows that we no longer need. OPEN dimensions_cursor; FETCH NEXT FROM dimensions_cursor INTO @schema, @name; WHILE (@@FETCH_STATUS <> -1) BEGIN SET @rows_affected = -1; WHILE (@rows_affected != 0) BEGIN -- We use dynamic SQL here because the table name is variable, but this also has the benefit of -- providing the optimizer with the final value for @delete_batch_size and @date_threshold. SET @query = 'DELETE TOP (' + @delete_batch_size + ') FROM ' + QUOTENAME(@schema) + '.' + QUOTENAME(@name) + ' WHERE processing_time < @date_threshold'; EXEC sp_executesql @query, N'@date_threshold datetimeoffset(7)', @date_threshold = @date_threshold_minute_data; SET @rows_affected = @@ROWCOUNT; END; FETCH NEXT FROM dimensions_cursor INTO @schema, @name; END; CLOSE dimensions_cursor; DEALLOCATE dimensions_cursor; DECLARE measures_cursor CURSOR FOR SELECT object_schema, [object_name] FROM sysutility_ucp_misc.utility_objects_internal WHERE utility_object_type = 'MEASURE'; -- Delete "per-minute" (15 minute) data from measure tables OPEN measures_cursor; FETCH NEXT FROM measures_cursor INTO @schema, @name; WHILE (@@FETCH_STATUS <> -1) BEGIN SET @rows_affected = -1; WHILE (@rows_affected != 0) BEGIN SET @query = 'DELETE TOP (' + @delete_batch_size + ') FROM ' + QUOTENAME(@schema) + '.' + QUOTENAME(@name) + ' WHERE processing_time < @date_threshold AND aggregation_type = 0'; EXEC sp_executesql @query, N'@date_threshold datetimeoffset(7)', @date_threshold = @date_threshold_minute_data; SET @rows_affected = @@ROWCOUNT; END; FETCH NEXT FROM measures_cursor INTO @schema, @name; END; CLOSE measures_cursor; -- Delete "per-hour" data from our measure-tables OPEN measures_cursor; FETCH NEXT FROM measures_cursor INTO @schema, @name; WHILE (@@FETCH_STATUS <> -1) BEGIN SET @rows_affected = -1; WHILE (@rows_affected != 0) BEGIN SET @query = 'DELETE TOP (' + @delete_batch_size + ') FROM ' + QUOTENAME(@schema) + '.' + QUOTENAME(@name) + ' WHERE processing_time < @date_threshold AND aggregation_type = 1'; EXEC sp_executesql @query, N'@date_threshold datetimeoffset(7)', @date_threshold = @date_threshold_hour_data; SET @rows_affected = @@ROWCOUNT; END; FETCH NEXT FROM measures_cursor INTO @schema, @name; END; CLOSE measures_cursor; -- Delete "per-day" data from measure tables OPEN measures_cursor; FETCH NEXT FROM measures_cursor INTO @schema, @name; WHILE (@@FETCH_STATUS <> -1) BEGIN SET @rows_affected = -1; WHILE (@rows_affected != 0) BEGIN SET @query = 'DELETE TOP (' + @delete_batch_size + ') FROM ' + QUOTENAME(@schema) + '.' + QUOTENAME(@name) + ' WHERE processing_time < @date_threshold AND aggregation_type = 2'; EXEC sp_executesql @query, N'@date_threshold datetimeoffset(7)', @date_threshold = @date_threshold_day_data; SET @rows_affected = @@ROWCOUNT; END; FETCH NEXT FROM measures_cursor INTO @schema, @name; END; CLOSE measures_cursor; DEALLOCATE measures_cursor; END; GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'CORE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'PROCEDURE', @level1name = 'sp_purge_cache_tables'; GO ----------------------------------------------------------------------------------------- -- Procedure sp_initialize_mdw_internal -- Performs runtime schema modifications that must be deferred until Create UCP. -- Executed by sp_sysutility_ucp_initialize_mdw in msdb. ----------------------------------------------------------------------------------------- IF OBJECT_ID ('sysutility_ucp_core.sp_initialize_mdw_internal') IS NOT NULL BEGIN RAISERROR('Dropping procedure sysutility_ucp_core.sp_initialize_mdw_internal procedure', 0, 1) WITH NOWAIT; DROP PROCEDURE sysutility_ucp_core.sp_initialize_mdw_internal; END GO RAISERROR('Creating procedure sysutility_ucp_core.sp_initialize_mdw_internal', 0, 1) WITH NOWAIT; GO CREATE PROCEDURE sysutility_ucp_core.sp_initialize_mdw_internal AS BEGIN IF (msdb.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; -- The Utility schema uses two Enterprise-only engine features: compression and partitioning. -- Utility is an Enterprise-only feature, but we share instmdw.sql and the MDW database with other -- features that are not Enterprise only. Therefore we can't include the following things as part -- of the CREATE TABLE statements because instmdw.sql would fail when run on a non-Enterprise SKU. -- To work around this, we defer this part of the UCP schema creation until Create UCP is run. IF (3 = CONVERT (int, SERVERPROPERTY('EngineEdition'))) -- Enterprise/Enterprise Eval/Developer/Data Center BEGIN -- Enable data compression on tables that benefit from it the most RAISERROR ('Enabling compression on MDW tables', 0, 1) WITH NOWAIT; ALTER TABLE [snapshots].[sysutility_ucp_smo_properties_internal] REBUILD WITH (DATA_COMPRESSION = PAGE); ALTER TABLE [sysutility_ucp_core].[smo_servers_internal] REBUILD WITH (DATA_COMPRESSION = PAGE); ALTER TABLE [sysutility_ucp_core].[databases_internal] REBUILD WITH (DATA_COMPRESSION = PAGE); ALTER TABLE [sysutility_ucp_core].[filegroups_internal] REBUILD WITH (DATA_COMPRESSION = PAGE); ALTER TABLE [sysutility_ucp_core].[datafiles_internal] REBUILD WITH (DATA_COMPRESSION = PAGE); ALTER TABLE [sysutility_ucp_core].[logfiles_internal] REBUILD WITH (DATA_COMPRESSION = PAGE); ALTER TABLE [sysutility_ucp_core].[cpu_utilization_internal] REBUILD WITH (DATA_COMPRESSION = PAGE); ALTER TABLE [sysutility_ucp_core].[space_utilization_internal] REBUILD WITH (DATA_COMPRESSION = PAGE); -- Partitioning (and compression) on measure tables -- Create a partitioning scheme based on aggregationType. In effect, we'd like -- each aggregation-level to get its own partition. Currently, we only support -- 3 aggregation levels. -- -- FUTURE: It would be nice to support composite partitioning in the future. -- 1. We could further create a sub-partition for each object-type (computer etc.) -- 2. If we could have sliding partitions, we could avoid deletes altogether, and -- instead work with truncate/drop-partition style operations -- IF NOT EXISTS (SELECT name FROM sys.partition_functions WHERE name = N'sysutility_ucp_aggregation_type_partition_function') BEGIN RAISERROR ('Creating partition function [sysutility_ucp_aggregation_type_partition_function]', 0, 1) WITH NOWAIT; -- Use dynamic SQL here (and in the next create stmt) b/c otherwise SQL will fail the creation of this -- proc on Workgroup or Standard edition. EXEC (' CREATE PARTITION FUNCTION [sysutility_ucp_aggregation_type_partition_function](TINYINT) AS RANGE LEFT FOR VALUES(0, 1, 2)'); END; IF NOT EXISTS (SELECT name FROM sys.partition_schemes WHERE name = N'sysutility_ucp_aggregation_type_partition_scheme') BEGIN RAISERROR ('Creating partition scheme [sysutility_ucp_aggregation_type_partition_scheme]', 0, 1) WITH NOWAIT; EXEC (' CREATE PARTITION SCHEME [sysutility_ucp_aggregation_type_partition_scheme] AS PARTITION [sysutility_ucp_aggregation_type_partition_function] ALL TO ([PRIMARY])'); END; -- ALTER INDEX can't change partition scheme. Instead we must drop and recreate the clustered PKs. IF OBJECT_ID ('[sysutility_ucp_core].[pk_cpu_utilization_internal]') IS NOT NULL BEGIN RAISERROR ('Dropping primary key [sysutility_ucp_core].[cpu_utilization_internal].[pk_cpu_utilization_internal]', 0, 1) WITH NOWAIT; ALTER TABLE [sysutility_ucp_core].[cpu_utilization_internal] DROP CONSTRAINT [pk_cpu_utilization_internal]; END; RAISERROR ('Creating partitioned primary key [sysutility_ucp_core].[cpu_utilization_internal].[pk_cpu_utilization_internal]', 0, 1) WITH NOWAIT; ALTER TABLE [sysutility_ucp_core].[cpu_utilization_internal] ADD CONSTRAINT pk_cpu_utilization_internal PRIMARY KEY CLUSTERED (aggregation_type, processing_time, object_type, physical_server_name, server_instance_name, database_name) WITH (DATA_COMPRESSION = PAGE) ON [sysutility_ucp_aggregation_type_partition_scheme](aggregation_type); IF OBJECT_ID ('[sysutility_ucp_core].[pk_storage_utilization]') IS NOT NULL BEGIN RAISERROR ('Dropping primary key [sysutility_ucp_core].[space_utilization_internal].[pk_storage_utilization]', 0, 1) WITH NOWAIT; ALTER TABLE [sysutility_ucp_core].[space_utilization_internal] DROP CONSTRAINT [pk_storage_utilization]; END; RAISERROR ('Creating partitioned primary key [sysutility_ucp_core].[space_utilization_internal].[pk_storage_utilization]', 0, 1) WITH NOWAIT; ALTER TABLE [sysutility_ucp_core].[space_utilization_internal] ADD CONSTRAINT pk_storage_utilization PRIMARY KEY CLUSTERED( aggregation_type, processing_time, object_type, virtual_server_name, volume_device_id, server_instance_name, database_name, [filegroup_name], dbfile_name) WITH (DATA_COMPRESSION = PAGE) ON [sysutility_ucp_aggregation_type_partition_scheme](aggregation_type); END; END; GO EXEC sp_addextendedproperty @name = 'MS_UtilityObjectType', @value = 'CORE', @level0type = 'SCHEMA', @level0name = 'sysutility_ucp_core', @level1type = 'PROCEDURE', @level1name = 'sp_initialize_mdw_internal'; GO BEGIN TRANSACTION ----------------------------------------------------------------------- -- Remove the sysutility_get_views_data_into_cache_tables job if it is already existing. ----------------------------------------------------------------------- DECLARE @job_id UNIQUEIDENTIFIER SELECT @job_id = job_id FROM msdb.dbo.sysjobs_view WHERE name = N'sysutility_get_views_data_into_cache_tables' IF EXISTS (SELECT job_id FROM msdb.dbo.sysjobs_view WHERE name = N'sysutility_get_views_data_into_cache_tables') EXEC msdb.dbo.sp_delete_job @job_id=@job_id, @delete_unused_schedule=1 DECLARE @ReturnCode INT -- Get the current logged in user name. DECLARE @CurrentLoggedIn nvarchar(128) Set @CurrentLoggedIn=SYSTEM_USER SELECT @ReturnCode = 0 ----------------------------------------------------------------------- -- 'sysutility_get_views_data_into_cache_tables job' has following steps: -- 1. Insert [sysutility_ucp_core].[fn_get_cpu_utilizations](0) data into -- [sysutility_ucp_core].[computer_cpu_utilizations_internal] cache table. -- 2. Insert [snapshots].[dac_view] data into -- [snapshots].[dac_table] -- 3. Insert storage live information into -- storage cache table -- 4. Insert SMO live information into -- SMO cache table ----------------------------------------------------------------------- IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'[Uncategorized (Local)]' AND category_class=1) BEGIN EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'[Uncategorized (Local)]' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback END DECLARE @mdwDBName NVARCHAR(256) SELECT @mdwDBName = DB_NAME() DECLARE @jobId BINARY(16) EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'sysutility_get_views_data_into_cache_tables', @enabled=1, @notify_level_eventlog=0, @notify_level_email=0, @notify_level_netsend=0, @notify_level_page=0, @delete_level=0, @description=N'Gets all the views data into corresponding cache tables after every 15 minutes', @category_name=N'[Uncategorized (Local)]', @owner_login_name= @CurrentLoggedIn , @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'Insert latest data from live tables into cache tables', @step_id=1, @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 [sysutility_ucp_staging].sp_copy_live_table_data_into_cache_tables', @database_name = @mdwDBName, @flags=0 DECLARE @execute_policy_script NVARCHAR(MAX) -- Powershell script to evaluate utility resource health policies -- Note 1: 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 -- Note 2: the current approach filters on policy name to identify the resource health policy -- this might turn out to be an issue if we start localizing the resource health policies. SET @execute_policy_script = N'$serverName = ''$(ESCAPE_SQUOTE(SRVR))''; $path = Convert-UrnToPath "PolicyStore[@Name=`''$serverName`'']"; dir $path\Policies -FORCE | where { $_.IsSystemObject -eq $true -and $_.Name -like ''Utility*'' } | Invoke-PolicyEvaluation -TargetServerName $serverName;' EXEC msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Execute resource health policy evaluation job', @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'PowerShell', @command=@execute_policy_script, @database_name=N'msdb', @flags=0 EXEC msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Compute resource health states', @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'TSQL', @command=N'EXEC msdb.dbo.sp_sysutility_ucp_calculate_health', @database_name=N'msdb', @flags=0 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_jobschedule @job_id=@jobId, @name=N'OccursEvery15Minutes', @enabled=1, @freq_type=4, @freq_interval=1, @freq_subday_type=4, @freq_subday_interval=15, @freq_relative_interval=0, @freq_recurrence_factor=0, @schedule_uid=N'7c3e972b-6e4b-4c61-9061-715d8b9ba531' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback ----------------------------------------------------------------------- -- Remove the sysutility_get_cache_tables_data_into_aggregate_tables_hourly job if it is already existing. ----------------------------------------------------------------------- DECLARE @job_id1 UNIQUEIDENTIFIER SELECT @job_id1 = job_id FROM msdb.dbo.sysjobs_view WHERE name = N'sysutility_get_cache_tables_data_into_aggregate_tables_hourly' IF EXISTS (SELECT job_id FROM msdb.dbo.sysjobs_view WHERE name = N'sysutility_get_cache_tables_data_into_aggregate_tables_hourly') EXEC msdb.dbo.sp_delete_job @job_id=@job_id1, @delete_unused_schedule=1 ----------------------------------------------------------------------- -- sysutility_get_cache_tables_data_into_aggregate_tables_hourly job has following steps: -- 1. Aggregate current days data from [sysutility_ucp_core].[computer_cpu_utilizations_internal] -- and put it into [sysutility_ucp_core].[aggregated_computer_cpu_utilizations_internal]. -- 2. Aggregate current days data from [sysutility_ucp_core].[dac_execution_statistics_internal] -- and put it into [sysutility_ucp_core].[aggregated_dac_cpu_utilizations_internal]. -- 3. Aggregate current days data storage cache table into -- storage aggregation table ----------------------------------------------------------------------- IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'[Uncategorized (Local)]' AND category_class=1) BEGIN EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'[Uncategorized (Local)]' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback END DECLARE @jobId1 BINARY(16) EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'sysutility_get_cache_tables_data_into_aggregate_tables_hourly', @enabled=1, @notify_level_eventlog=0, @notify_level_email=0, @notify_level_netsend=0, @notify_level_page=0, @delete_level=0, @description=N'At every hour''s stroke, the data of the cache tables get aggregated and put into corresponding aggregate by hour tables.', @category_name=N'[Uncategorized (Local)]', @owner_login_name=@CurrentLoggedIn, @job_id = @jobId1 OUTPUT IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId1, @step_name=N'Aggregate current hours data from the cache tables into the server aggregation table', @step_id=1, @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'TSQL', @command=N'DECLARE @now DATETIMEOFFSET(7); SELECT @now = SYSDATETIMEOFFSET(); EXEC [sysutility_ucp_core].sp_copy_cache_table_data_into_aggregate_tables @aggregation_type=1, @endTime=@now', @database_name = @mdwDBName, @flags=0 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id=@jobId1, @name=N'OccursEveryOneHour', @enabled=1, @freq_type=4, @freq_interval=1, @freq_subday_type=8, @freq_subday_interval=1, @freq_relative_interval=0, @freq_recurrence_factor=0, @active_start_time=100 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback ----------------------------------------------------------------------- -- Remove the sysutility_get_cache_tables_data_into_aggregate_tables_daily job if it is already existing. ----------------------------------------------------------------------- SELECT @job_id1 = job_id FROM msdb.dbo.sysjobs_view WHERE name = N'sysutility_get_cache_tables_data_into_aggregate_tables_daily' IF EXISTS (SELECT job_id FROM msdb.dbo.sysjobs_view WHERE name = N'sysutility_get_cache_tables_data_into_aggregate_tables_daily') EXEC msdb.dbo.sp_delete_job @job_id=@job_id1, @delete_unused_schedule=1 ----------------------------------------------------------------------- -- sysutility_get_cache_tables_data_into_aggregate_tables_daily job has following steps: -- 1. Aggregate current days data from [sysutility_ucp_core].[computer_cpu_utilizations_internal] -- and put it into [sysutility_ucp_core].[aggregated_computer_cpu_utilizations_internal]. -- 2. Aggregate current days data from [sysutility_ucp_core].[dac_execution_statistics_internal] -- and put it into [sysutility_ucp_core].[aggregated_dac_cpu_utilizations_internal]. -- 3. Aggregate current days data from storage cache table into -- storage aggregation table ----------------------------------------------------------------------- IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'[Uncategorized (Local)]' AND category_class=1) BEGIN EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'[Uncategorized (Local)]' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback END DECLARE @jobId2 BINARY(16) EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'sysutility_get_cache_tables_data_into_aggregate_tables_daily', @enabled=1, @notify_level_eventlog=0, @notify_level_email=0, @notify_level_netsend=0, @notify_level_page=0, @delete_level=0, @description=N'At every 12:01 AM stroke, the data of the cache tables get aggregated and put into corresponding aggregate by day tables.', @category_name=N'[Uncategorized (Local)]', @owner_login_name=@CurrentLoggedIn, @job_id = @jobId2 OUTPUT IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId2, @step_name=N'Aggregate current days data from the cache tables into the server aggregation table', @step_id=1, @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'DECLARE @now DATETIME; SELECT @now = GETUTCDATE(); EXEC [sysutility_ucp_core].sp_copy_cache_table_data_into_aggregate_tables @aggregation_type=2, @endTime=@now', @database_name = @mdwDBName, @flags=0 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId2, @step_name=N'Purge cache table history data based on retention period', @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 [sysutility_ucp_core].[sp_purge_cache_tables];', @database_name = @mdwDBName, @flags=0 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId2, @step_name=N'Purge resource health policy evaluation history based on trailing window', @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'TSQL', @command=N'EXEC msdb.dbo.sp_sysutility_ucp_delete_policy_history', @database_name=N'msdb', @flags=0 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId1, @start_step_id = 1 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id=@jobId2, @name=N'OccursOnceADayAt12:01AM', @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=20080218, @active_end_date=99991231, @active_start_time=100, @active_end_time=235959, @schedule_uid=N'acb4d2d5-d2ee-4d33-b82e-a296a41fc225' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId1, @server_name = N'(local)' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId2, @server_name = N'(local)' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback -- Commit our transaction COMMIT TRANSACTION GOTO EndSave QuitWithRollback: IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION EndSave: GO -- Commit our transaction COMMIT TRANSACTION GO /**********************************************************************/ /* Setup Utility object permissions */ /**********************************************************************/ /********************************************************************** Create the UtilityMDWWriter role. This role is granted to proxy accounts that run the DC upload step on each MI. It allows these accounts to insert data into the Utility "live" tables in MDW. ***********************************************************************/ RAISERROR ('Create UtilityMDWWriter role...', 0, 1) WITH NOWAIT; IF ( NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'UtilityMDWWriter' AND type = 'R')) BEGIN CREATE ROLE [UtilityMDWWriter] 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'UtilityMDWWriter' AND type = 'R') BEGIN -- if the role has no members drop and recreate it DROP ROLE [UtilityMDWWriter] CREATE ROLE [UtilityMDWWriter] END END GO -- UtilityMDWWriter is a member of the mdw_writer role; this will give it -- INSERT, EXEC, and SELECT permissions on anything in the [snapshots] schema. EXECUTE sp_addrolemember @rolename = 'mdw_writer' , @membername = 'UtilityMDWWriter' GO /********************************************************************** Create the UtilityMDWCacheReader role. This sysutility_mdw role corresponds to the UtilityCMRReader role in msdb. A "CMRReader" can select from objects in msdb that expose data in cache tables in sysutility_mdw. The "CacheReader" role in sysutility_mdw secures these cache tables/functions in the MDW db. A login should be a member of both roles in order to run the UCP data processing job. ***********************************************************************/ RAISERROR ('Create UtilityMDWCacheReader role...', 0, 1) WITH NOWAIT; IF ( NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'UtilityMDWCacheReader' AND type = 'R')) BEGIN CREATE ROLE [UtilityMDWCacheReader]; 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'UtilityMDWCacheReader' AND type = 'R') BEGIN -- if the role has no members drop and recreate it DROP ROLE [UtilityMDWCacheReader]; CREATE ROLE [UtilityMDWCacheReader]; END; END; GO -- Grant SELECT or EXECUTE on the cache functions/views at the boundary of msdb and -- sysutility_mdw. These objects are directly referenced by objects in msdb. RAISERROR ('Granting permission on MDW cache objects to UtilityMDWCacheReader role', 0, 1) WITH NOWAIT; -- Dimensions GRANT SELECT ON [sysutility_ucp_core].[latest_dacs] TO [UtilityMDWCacheReader]; GRANT SELECT ON [sysutility_ucp_core].[latest_computers] TO [UtilityMDWCacheReader]; GRANT SELECT ON [sysutility_ucp_core].[latest_volumes] TO [UtilityMDWCacheReader]; GRANT SELECT ON [sysutility_ucp_core].[latest_smo_servers] TO [UtilityMDWCacheReader]; GRANT SELECT ON [sysutility_ucp_core].[latest_databases] TO [UtilityMDWCacheReader]; GRANT SELECT ON [sysutility_ucp_core].[latest_filegroups] TO [UtilityMDWCacheReader]; GRANT SELECT ON [sysutility_ucp_core].[latest_datafiles] TO [UtilityMDWCacheReader]; GRANT SELECT ON [sysutility_ucp_core].[latest_logfiles] TO [UtilityMDWCacheReader]; -- Measures GRANT SELECT ON [sysutility_ucp_core].[cpu_utilization] TO [UtilityMDWCacheReader]; GRANT SELECT ON [sysutility_ucp_core].[space_utilization] TO [UtilityMDWCacheReader]; GO -- Put the database back into multi user mode PRINT '' PRINT 'Restoring database to multi user mode' DECLARE @dbname sysname SET @dbname = QUOTENAME(DB_NAME()) DECLARE @sql_db_multi_mode nvarchar(256) SET @sql_db_multi_mode = 'ALTER DATABASE ' + @dbname + ' SET MULTI_USER WITH ROLLBACK IMMEDIATE' EXEC sp_executesql @sql_db_multi_mode -- check if sync auto stats was on IF (EXISTS (SELECT * FROM #tmp_auto_mode)) -- if yes, turn it back on BEGIN PRINT '' PRINT 'Re-enabling asynchronous auto statistics ...' DECLARE @sql_async_autostat_on nvarchar(256) SET @sql_async_autostat_on = 'ALTER DATABASE ' + @dbname + ' SET AUTO_UPDATE_STATISTICS_ASYNC ON' EXEC sp_executesql @sql_async_autostat_on END DROP TABLE #tmp_auto_mode PRINT '' PRINT '----------------------------------' PRINT 'Execution of INSTMDW.SQL complete' PRINT '----------------------------------' GO /**********************************************************************/ /* POST_UPGRADE_UCP_CMDW.SQL */ /* */ /* ** Copyright Microsoft, Inc. 1994 - 2009 ** All Rights Reserved. */ /**********************************************************************/ 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 -- The sp_sysutility_ucp_initialize_mdw proc needs agent XP's to be enabled to check SQL edition DECLARE @advopt_old_value int DECLARE @comp_old_value int EXEC #sp_enable_component 'Agent XPs', @advopt_old_value out, @comp_old_value out -- Get the MDW database name DECLARE @mdw_name SYSNAME SELECT @mdw_name=CAST(current_value as SYSNAME) FROM [msdb].[dbo].[sysutility_ucp_configuration_internal] WHERE name = 'MdwDatabaseName' -- Now run the SProc sp_sysutility_ucp_initialize_mdw which re-creates the msdb synonyms to point to the MDW objects EXEC [msdb].[dbo].[sp_sysutility_ucp_initialize_mdw] @mdw_database_name=@mdw_name -- Restore Agent XPs to previous state EXECUTE #sp_restore_component_state 'Agent XPs', @advopt_old_value, @comp_old_value GO PRINT '----------------------------------------' PRINT 'Execution of POST_UPGRADE_UCP_CMDW.SQL complete' PRINT '----------------------------------------' GO PA4VS_VERSION_INFOd@2 @? StringFileInfo040904B0PlatformNTILegalTrademarksMicrosoft SQL Server is a registered trademark of Microsoft Corporation. CommentsSQL&GoldenBitsTrueLCompanyNameMicrosoft CorporationXFileDescriptionSQL Upgrade Scripts DLLt*FileVersion2009.0100.1600.01 ((KJ_RTM).100402-1539 )BInternalNameSQLSCRIPTUPGRADEn%LegalCopyrightMicrosoft Corp. All rights reserved.ROriginalFilenameSQLSCRIPTUPGRADE.DLLJProductNameMicrosoft SQL Server> ProductVersion10.50.1600.1DVarFileInfo$Translation  PPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADD ؠ0$(Hhȡ(Hh`0S *H D0@1 0 +0h +7Z0X03 +70% <<<Obsolete>>>0!0 + KG;~fV10`0L .P\0 +0p1+0)U "Copyright (c) 1997 Microsoft Corp.10U Microsoft Corporation1!0UMicrosoft Root Authority0 070822223102Z 120825070000Z0y1 0 UUS10U Washington10URedmond10U Microsoft Corporation1#0!UMicrosoft Code Signing PCA0"0  *H 0 y}]E941%5IwEqFԌkLRbMIl/$>e# HuEP%+ #A$bEJͳ/"J-|o;99ݽ)f;-2'Hlc\򸔣8P'N0==l9.4. }bxfs Oc,2EJ;PSfQyV>Pn5{$Rf=N+~3nGўJnS00U% 0 +0U0[pir#Q~Mˡr0p1+0)U "Copyright (c) 1997 Microsoft Corp.10U Microsoft Corporation1!0UMicrosoft Root Authority<<>c@0U00Uvp[NQD.Dc0 U0 +{~J&μNtX't*uLxMi|CʇSŸVocDDȚ  )}s9=j8m҈#i 4|.)Bk(q8 ]hͽAkf4|zB{ p֒O8|-=4 b7j#\cZ9`U3; _,˫ 0  *RA^0(ip΀Brv0z0b a>0  *H 0y1 0 UUS10U Washington10URedmond10U Microsoft Corporation1#0!UMicrosoft Code Signing PCA0 091207224029Z 110307224029Z01 0 UUS10U Washington10URedmond10U Microsoft Corporation1 0 U MOPR10UMicrosoft Corporation0"0  *H 0 0ErSkO#=Y@8s&S<B8auM\F^i[s)DdY/]nǫ9eħuꇗ&&v89&+ZT!򗍇)I,?}͖ q B5׸?ݎE }K[vrw#}5]PKGmUTN؁B1U SI0奈|^zZ7i2$00U% 0 +0U8xs2_Uƙt0U0U#0vp[NQD.Dc0DU=0;09753http://crl.microsoft.com/pki/crl/products/CSPCA.crl0H+<0:08+0,http://www.microsoft.com/pki/certs/CSPCA.crt0  *H ( oBvCB$e4uq0r7:pQ q(ps{V4ZrN {DO aun+Z 9`jT;1_,nM*v{Ƈֱ*rTn*ȞobKk=/zw9gj{cI&Bޕ" _s2r6)$ROfH&9iPNl.r!m$H@|t8JO00j O%EXzg0  *H 0p1+0)U "Copyright (c) 1997 Microsoft Corp.10U Microsoft Corporation1!0UMicrosoft Root Authority0 060916010447Z 190915070000Z0y1 0 UUS10U Washington10URedmond10U Microsoft Corporation1#0!UMicrosoft Timestamping PCA0"0  *H 0 7nBJqH>S,2ORȃ>3I1(dPKuǨծipfx'f趷 Y")/@VvmdmJT޿ǀL7VhGv\/}%V[jc|<%M9wt]\؆7,u9 |vlnz>q_*Ob`2҃N+"\hE/Pl%ׅvs6ƕz`3[AXn,HoCj&k(0$0U% 0 +0U0[pir#Q~Mˡr0p1+0)U "Copyright (c) 1997 Microsoft Corp.10U Microsoft Corporation1!0UMicrosoft Root Authority<<>c@0 +70UoN?4K;AC0 +7  SubCA0 U0U00  *H M1|PapEsT? -QS9V ތ;ɷQ!oi~k"Flm|"Fӄ6~p]Eݎ*|ɮ2Շc6!v;s!شTeJ(&`;exHϭ:ObX099!dcC/{FeJtn ̝(a|H!8Ŗ2@S=f7"̰wTQ:rD#00 a- 0  *H 0y1 0 UUS10U Washington10URedmond10U Microsoft Corporation1#0!UMicrosoft Timestamping PCA0 080725190217Z 130725191217Z01 0 UUS10U Washington10URedmond10U Microsoft Corporation1 0 U MOPR1'0%U nCipher DSE ESN:7A82-688A-9F921%0#UMicrosoft Time-Stamp Service0"0  *H 0  BQzٕ܇7(vMPv*GVi~MquۻCloq+|Δź~a-0&-v?F؉7[hd֋=vP'`]),<ϥZ5O8a?=6Vw;]7'c,HUl+~w$*p*$Ţ"KLYwTrC>dݓXI 63~Ly2Y)0 +0 *H  1  +70 +7 10  +70# *H  1{2ZA8oM_Mʙm0X +7 1J0H&$SQL Server 2008 R2http://www.microsoft.com/sql0  *H  / nņH%khruγxbv! R*4W̘>%Ey)")tZG;db~v&BVC;XI?ҳ'KlaL 0ˡ=,/as DW8}밈rtoC*=13 ƹLE0£PD$+z!4%KU |9apv!_uwF?#PUl*MoƟbw0 *H  1 000y1 0 UUS10U Washington10URedmond10U Microsoft Corporation1#0!UMicrosoft Timestamping PCA a- 0+]0 *H  1  *H 0 *H  1 100403175906Z0# *H  1 Gp_ ZLP<0  *H wA- 6ωhUNoZN{.i1`oh>LIܹ)+-Py?z\̀l\n34䫸;`,@6B')NW5Y)0ޗ\u=2ؐjpo\K$m`ЕqƳ8 bĵW36(hC"*lw]rLuB>pJ:v2T@eM۵