From 94be4e48e8244f5202d310776a215c5d5423f905 Mon Sep 17 00:00:00 2001 From: Thierry Schork Date: Fri, 15 Aug 2025 10:17:18 +0200 Subject: [PATCH] sync --- DBG - Scripter les jobs.sql | 4 +- ...- db maintenance - check jobs schedule.sql | 18 + ...geLogs-cleanup-Compendium2020UsageLogs.sql | 28 - ...ageLogs-cleanup-Documedis2020UsageLogs.sql | 28 - ...eLogs-cleanup-Pharmavista2020UsageLogs.sql | 28 - ...edisUsageLogs-cleanup-api2020UsageLogs.sql | 26 - TODO sl2007.md | 22 + list non AD group logins and permissions.sql | 1084 ++ maintenance_devdb.sql | 9880 +++++++++++++++++ 9 files changed, 11006 insertions(+), 112 deletions(-) create mode 100644 HCI - db maintenance - check jobs schedule.sql delete mode 100644 MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-Compendium2020UsageLogs.sql delete mode 100644 MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-Documedis2020UsageLogs.sql delete mode 100644 MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-Pharmavista2020UsageLogs.sql delete mode 100644 MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-api2020UsageLogs.sql create mode 100644 TODO sl2007.md create mode 100644 list non AD group logins and permissions.sql create mode 100644 maintenance_devdb.sql diff --git a/DBG - Scripter les jobs.sql b/DBG - Scripter les jobs.sql index fef97e8..1d94c45 100644 --- a/DBG - Scripter les jobs.sql +++ b/DBG - Scripter les jobs.sql @@ -70,7 +70,7 @@ WHERE 1=1 AND LOWER(sj.name) NOT LIKE '%subscription%' AND LOWER(sj.name) NOT LIKE '%replication%' AND LOWER(sj.name) NOT LIKE '%ActivePos_read-%' - AND sj.name LIKE '%-%' + AND sj.name LIKE '%IndexOptimize %' AND sc.[name] NOT LIKE '%REPL%' ORDER BY sj.name @@ -83,7 +83,7 @@ FETCH NEXT FROM c_jobs WHILE @@fetch_status = 0 BEGIN - SET @path = 'D:\dev\'+@name+'.sql' + SET @path = 'D:\dbjobs\'+@name+'.sql' DECLARE @cleanCmd VARCHAR(8000)= 'del "'+@path+'"'; DECLARE @catchOutput TABLE(output VARCHAR(max)); diff --git a/HCI - db maintenance - check jobs schedule.sql b/HCI - db maintenance - check jobs schedule.sql new file mode 100644 index 0000000..0dc0b17 --- /dev/null +++ b/HCI - db maintenance - check jobs schedule.sql @@ -0,0 +1,18 @@ +USE msdb; + + +SELECT j.[job_id], j.[name] AS [job_name], c.[name] AS [category_name], s.[next_run_date], s.[next_run_time], s.[schedule_id], +TRY_CAST( + CAST(s.[next_run_date] AS CHAR(8)) + ' ' + + STUFF(STUFF(RIGHT('000000' + CAST(s.[next_run_time] AS VARCHAR(6)), 6), 3, 0, ':'), 6, 0, ':') +AS DATETIME) +AS [next_run_datetime] + +FROM [dbo].[sysjobs] j + JOIN [dbo].[syscategories] c ON c.[category_id]=j.[category_id] + JOIN [dbo].[sysjobschedules] s ON s.[job_id] = j.[job_id] +WHERE c.[name]='Database Maintenance' +ORDER BY j.[name]; + + + diff --git a/MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-Compendium2020UsageLogs.sql b/MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-Compendium2020UsageLogs.sql deleted file mode 100644 index cb177de..0000000 --- a/MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-Compendium2020UsageLogs.sql +++ /dev/null @@ -1,28 +0,0 @@ -USE [DocumedisUsageLogs] -GO - -/* -MDDOC-878 - -Purge of the table DocumedisUsageLogs.dbo.Compendium2020UsageLogs - -Records that are more than 7 months old will be purged. - -Deletion is made in batches to avoid locking. -*/ -CREATE PROCEDURE [purge_Compendium2020UsageLogs] -AS -BEGIN - DECLARE @cutoff DATE = DATEADD(MONTH, -7, CURRENT_TIMESTAMP); - DECLARE @batch INT = 5000; - DECLARE @row_count INT = 1; - - WHILE @row_count > 0 - BEGIN - DELETE TOP(@batch) s - FROM [dbo].[Compendium2020UsageLogs] s - WHERE [s].[LogDateTime] > @cutoff; - - SET @row_count = @@rowcount; - END -END diff --git a/MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-Documedis2020UsageLogs.sql b/MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-Documedis2020UsageLogs.sql deleted file mode 100644 index a797ee6..0000000 --- a/MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-Documedis2020UsageLogs.sql +++ /dev/null @@ -1,28 +0,0 @@ -USE [DocumedisUsageLogs] -GO - -/* -MDDOC-878 - -Purge of the table DocumedisUsageLogs.dbo.Documedis2020UsageLogs - -Records that are more than 7 months old will be purged. - -Deletion is made in batches to avoid locking. -*/ -CREATE PROCEDURE [purge_Documedis2020UsageLogs] -AS -BEGIN - DECLARE @cutoff DATE = DATEADD(MONTH, -7, CURRENT_TIMESTAMP); - DECLARE @batch INT = 5000; - DECLARE @row_count INT = 1; - - WHILE @row_count > 0 - BEGIN - DELETE TOP(@batch) s - FROM [dbo].[Documedis2020UsageLogs] s - WHERE [s].[LogDateTime] > @cutoff; - - SET @row_count = @@rowcount; - END -END diff --git a/MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-Pharmavista2020UsageLogs.sql b/MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-Pharmavista2020UsageLogs.sql deleted file mode 100644 index f77098b..0000000 --- a/MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-Pharmavista2020UsageLogs.sql +++ /dev/null @@ -1,28 +0,0 @@ -USE [DocumedisUsageLogs] -GO - -/* -MDDOC-878 - -Purge of the table DocumedisUsageLogs.dbo.Pharmavista2020UsageLogs - -Records that are more than 7 months old will be purged. - -Deletion is made in batches to avoid locking. -*/ -CREATE PROCEDURE [purge_Pharmavista2020UsageLogs] -AS -BEGIN - DECLARE @cutoff DATE = DATEADD(MONTH, -7, CURRENT_TIMESTAMP); - DECLARE @batch INT = 5000; - DECLARE @row_count INT = 1; - - WHILE @row_count > 0 - BEGIN - DELETE TOP(@batch) s - FROM [dbo].[Pharmavista2020UsageLogs] s - WHERE [s].[LogDateTime] > @cutoff; - - SET @row_count = @@rowcount; - END -END diff --git a/MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-api2020UsageLogs.sql b/MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-api2020UsageLogs.sql deleted file mode 100644 index 804e87e..0000000 --- a/MDDOC-878 - purge logdb/DocumedisUsageLogs-cleanup-api2020UsageLogs.sql +++ /dev/null @@ -1,26 +0,0 @@ -USE [DocumedisUsageLogs] -GO - -/* -MDDOC-878 - -Purge of the table DocumedisUsageLogs.dbo.Api2020UsageLogs - -Records that are more than 7 months old will be purged. - -Deletion is made in batches to avoid locking. -*/ -CREATE PROCEDURE [purge_Api2020UsageLogs] -AS -BEGIN - DECLARE @cutoff DATE = DATEADD(MONTH, -7, CURRENT_TIMESTAMP); - DECLARE @batch INT = 5000; - DECLARE @row_count INT = 1; - - WHILE @row_count > 0 - BEGIN - DELETE TOP(@batch) s - FROM [dbo].[Api2020UsageLogs] s - WHERE [s].[LogDateTime] > @cutoff; - END -END diff --git a/TODO sl2007.md b/TODO sl2007.md new file mode 100644 index 0000000..dd1f54b --- /dev/null +++ b/TODO sl2007.md @@ -0,0 +1,22 @@ +a lancer dans une query ssms pour tester + +tester step 19 et si ok exécuter step 22 +tester aussi step 21 + + + +ALTER ROLE [db_owner] ADD MEMBER [sql-au_bag_apv]; +ALTER ROLE [db_owner] ADD MEMBER [sql-repl_agent]; +ALTER ROLE [db_owner] ADD MEMBER [sql-md_devs]; +ALTER ROLE [db_ddladmin] ADD MEMBER [sql-bag_sl2007]; +ALTER ROLE [db_datareader] ADD MEMBER [sql-au_bag_apv]; +ALTER ROLE [db_datareader] ADD MEMBER [sql-bag_sl2007]; +ALTER ROLE [db_datawriter] ADD MEMBER [sql-au_bag_apv]; +ALTER ROLE [db_datawriter] ADD MEMBER [sql-bag_sl2007]; +GRANT CONNECT ON DATABASE::[SL2007] TO [sql-au_bag_apv]; +GRANT CONNECT ON DATABASE::[SL2007] TO [sql-repl_agent]; +GRANT CONNECT ON DATABASE::[SL2007] TO [sql-md_devs]; +GRANT CONNECT ON DATABASE::[SL2007] TO [sql-bag_sl2007]; + + +GRANT EXECUTE ON DATABASE::[SL2007] TO [sql-bag_sl2007]; \ No newline at end of file diff --git a/list non AD group logins and permissions.sql b/list non AD group logins and permissions.sql new file mode 100644 index 0000000..7a0c8a1 --- /dev/null +++ b/list non AD group logins and permissions.sql @@ -0,0 +1,1084 @@ +USE master; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED +SET XACT_ABORT ON; +SET NOCOUNT ON; + +GO +IF OBJECT_ID('dbo.sp_DBPermissions') IS NULL + EXECUTE sp_executesql N'CREATE PROCEDURE dbo.sp_DBPermissions AS PRINT ''Stub'';'; +GO +/********************************************************************************************* +sp_DBPermissions V7.0 +Kenneth Fisher + +http://www.sqlstudies.com +https://github.com/sqlstudent144/SQL-Server-Scripts/blob/master/sp_DBPermissions.sql + +This stored procedure returns 3 data sets. The first dataset is the list of database +principals, the second is role membership, and the third is object and database level +permissions. + +The final 2 columns of each query are "Un-Do"/"Do" scripts. For example removing a member +from a role or adding them to a role. I am fairly confident in the role scripts, however, +the scripts in the database principals query and database/object permissions query are +works in progress. In particular certificates, keys and column level permissions are not +scripted out. Also while the scripts have worked flawlessly on the systems I've tested +them on, these systems are fairly similar when it comes to security so I can't say that +in a more complicated system there won't be the odd bug. + +Standard disclaimer: You use scripts off of the web at your own risk. I fully expect this + script to work without issue but I've been known to be wrong before. + +Parameters: + @DBName + If NULL use the current database, otherwise give permissions based on the parameter. + + There is a special case where you pass in ALL to the @DBName. In this case the SP + will loop through (yes I'm using a cursor) all of the DBs in sysdatabases and run + the queries into temp tables before returning the results. WARNINGS: If you use + this option and have a large number of databases it will be SLOW. If you use this + option and don't specify any other parameters (say a specific @Principal) and have + even a medium number of databases it will be SLOW. Also the undo/do scripts do + not have USE statements in them so please take that into account. + @Principal + If NOT NULL then all three queries only pull for that database principal. @Principal + is a pattern check. The queries check for any row where the passed in value exists. + It uses the pattern '%' + @Principal + '%' + @Role + If NOT NULL then the roles query will pull members of the role. If it is NOT NULL and + @DBName is NULL then DB principal and permissions query will pull the principal row for + the role and the permissions for the role. @Role is a pattern check. The queries + check for any row where the passed in value exists. It uses the pattern '%' + @Role + + '%' + @Type + If NOT NULL then all three queries will only pull principals of that type. + S = SQL login + U = Windows login + G = Windows group + R = Server role + C = Login mapped to a certificate + K = Login mapped to an asymmetric key + @ObjectName + If NOT NULL then the third query will display permissions specific to the object + specified and the first two queries will display only those users with those specific + permissions. Unfortunately at this point only objects in sys.all_objects will work. + This parameter uses the pattern '%' + @ObjectName + '%' + @Permission + If NOT NULL then the third query will display only permissions that match what is in + the parameter. The first two queries will display only those users with that specific + permission. + @LoginName + If NOT NULL then each of the queries will only pull back database principals that + have the same SID as a login that matches the pattern '%' + @LoginName + '%' + @UseLikeSearch + When this is set to 1 (the default) then the search parameters will use LIKE (and + %'s will be added around the @Principal, @Role, @ObjectName, and @LoginName parameters). + When set to 0 searchs will use =. + @IncludeMSShipped + When this is set to 1 (the default) then all principals will be included. When set + to 0 the fixed server roles and SA and Public principals will be excluded. + @CopyTo + If @Principal is filled in then the value in @CopyTo is used in the drop and create + scripts instead of @Principal. In the case of the CREATE USER statement @CopyTo + also replaces the name of the server level principal, however it does not affect the + default schema name. + NOTE: It is very important to note that if @CopyTo is not a valid name the drop/create + scripts may fail. + @DropTempTables + When this is set to 1 (the default) the temp tables used are dropped. If it's 0 + then the tempt ables are kept for references after the code has finished. + The temp tables are: + ##DBPrincipals + ##DBRoles + ##DBPermissions + @ShowOrphans + By default this is 0. If it is 1 then it shows only orphaned principals and scripts to fix them. + Note: This option is 2012 and up only. + @Output + What type of output is desired. + Default - Either 'Default' or it doesn't match any of the allowed values then the SP + will return the standard 3 outputs. + None - No output at all. Usually used if you keeping the temp tables to do your own + reporting. + CreateOnly - Only return the create scripts where they aren't NULL. + DropOnly - Only return the drop scripts where they aren't NULL. + ScriptOnly - Return drop and create scripts where they aren't NULL. + Report - Returns one output with one row per principal and a comma delimited list of + roles the principal is a member of and a comma delimited list of the + individual permissions they have. + @Print + Defaults to 0, but if a 1 is passed in then the queries are not run but printed + out instead. This is primarily for debugging. + +Data is ordered as follows + 1st result set: DBPrincipal + 2nd result set: RoleName, UserName if the parameter @Role is used else + UserName, RoleName + 3rd result set: If @ObjectName is used then DBName, SchemaName, ObjectName, Grantee_Name, permission_name + otherwise DBName, GranteeName, SchemaName, ObjectName, permission_name + +-- V2.0 +-- 8/18/2013 – Create a stub if the SP doesn’t exist, then always do an alter +-- 8/18/2013 - Use instance collation for all concatenated strings +-- 9/04/2013 - dbo can’t be added or removed from roles. Don’t script. +-- 9/04/2013 - Fix scripts for schema level permissions. +-- 9/04/2013 – Change print option to show values of variables not the +-- Variable names. +-- V3.0 +-- 10/5/2013 - Added @Type parameter to pull only principals of a given type. +-- 10/10/2013 - Added @ObjectName parameter to pull only permissions for a given object. +-- V4.0 +-- 11/18/2013 - Added parameter names to sp_addrolemember and sp_droprolemember. +-- 11/19/2013 - Added an ORDER BY to each of the result sets. See above for details. +-- 01/04/2014 - Add an ALL option to the DBName parameter. +-- V4.1 +-- 02/07/2014 - Fix bug scripting permissions where object and schema have the same ID +-- 02/15/2014 - Add support for user defined types +-- 02/15/2014 - Fix: Add schema to object GRANT and REVOKE scripts +-- V5.0 +-- 4/29/2014 - Fix: Removed extra print statements +-- 4/29/2014 - Fix: Added SET NOCOUNT ON +-- 4/29/2014 - Added a USE statement to the scripts when using the @DBName = 'All' option +-- 5/01/2014 - Added @Permission parameter +-- 5/14/2014 - Added additional permissions based on information from Kendal Van Dyke's + post http://www.kendalvandyke.com/2014/02/using-sysobjects-when-scripting.html +-- 6/02/2014 - Added @LoginName parameter +-- V5.5 +-- 7/15/2014 - Bunch of changes recommended by @SQLSoldier/"https://twitter.com/SQLSoldier" + Primarily changing the strings to unicode & adding QUOTENAME in a few places + I'd missed it. +-- V6.0 +-- 10/19/2014 - Add @UserLikeSearch and @IncludeMSShipped parameters. +-- 11/29/2016 - Fixed permissions for symmetric keys +-- Found and fixed by Brenda Grossnickle +-- 03/25/2017 - Move SID towards the end of the first output so the more important +-- columns are closer to the front. +-- 03/25/2017 - Add IF Exists to drop and create user scripts +-- 03/25/2017 - Remove create/drop user scripts for guest, public, sys and INFORMATION_SCHEMA +-- 03/25/2017 - Add @DropTempTables to keep the temp tables after the SP is run. +-- 03/26/2017 - Add @Output to allow different types of output. +-- V6.1 +-- 06/25/2018 - Skip snapshots +-- 02/13/2019 - Fix to direct permissions column in the report output to show schema permissions correctly +-- 04/05/2019 - For 'All' DB parameter fix to only look at ONLINE and EMERGENCY DBs. +-- 06/04/2019 - Add SchemaName and permission_name to the order of the third data set. + This makes the order more reliable. +-- 06/04/2019 - Begin cleanup of the dynamic SQL (specifically removing carrage return & extra quotes) +-- 06/04/2019 - Fix @print where part of the permissions query was being truncated. +-- V6.2 +-- 07/15/2022 - Add @CopyTo parameter to handle requests like "Please copy permissions from x to y." +-- 07/15/2022 - Clean up dyanmic formatting to remove most of the N' and "' + CHAR(13) + " strings. +-- 07/31/2022 - Formatting: Replace tabs with spaces +-- 01/14/2023 - Fixes for unicode strings +-- V7.0 +-- 08/15/2023 - Add orphan functionality with @ShowOrphans parameter. +*********************************************************************************************/ + +ALTER PROCEDURE [dbo].sp_DBPermissions +( + @DBName sysname = NULL, + @Principal sysname = NULL, + @Role sysname = NULL, + @Type NVARCHAR(30) = NULL, + @ObjectName sysname = NULL, + @Permission sysname = NULL, + @LoginName sysname = NULL, + @UseLikeSearch BIT = 1, + @IncludeMSShipped BIT = 1, + @CopyTo sysname = NULL, + @DropTempTables BIT = 1, + @ShowOrphans BIT = 0, + @Output VARCHAR(30) = 'Default', + @Print BIT = 0 +) +AS + +SET NOCOUNT ON; + +DECLARE @Collation NVARCHAR(75); +SET @Collation = N' COLLATE ' + CAST(SERVERPROPERTY('Collation') AS NVARCHAR(50)); + +DECLARE @sql NVARCHAR(MAX); +DECLARE @sql2 NVARCHAR(MAX); +DECLARE @ObjectList NVARCHAR(MAX); +DECLARE @ObjectList2 NVARCHAR(MAX); +DECLARE @use NVARCHAR(500); +DECLARE @AllDBNames sysname; + +IF @DBName IS NULL OR @DBName = N'All' + BEGIN + SET @use = ''; + IF @DBName IS NULL + SET @DBName = DB_NAME(); + --SELECT @DBName = db_name(database_id) + --FROM sys.dm_exec_requests + --WHERE session_id = @@SPID + END; +ELSE +-- IF EXISTS (SELECT 1 FROM sys.databases WHERE name = @DBName) + IF DB_ID(@DBName) IS NOT NULL + SET @use = N'USE ' + QUOTENAME(@DBName) + N';' + NCHAR(13); + ELSE + BEGIN + RAISERROR (N'%s is not a valid database name.', + 16, + 1, + @DBName); + RETURN; + END; + +DECLARE @LikeOperator NVARCHAR(4); + +IF @UseLikeSearch = 1 + SET @LikeOperator = N'LIKE'; +ELSE + SET @LikeOperator = N'='; + +IF @UseLikeSearch = 1 +BEGIN + IF LEN(ISNULL(@Principal,'')) > 0 + SET @Principal = N'%' + @Principal + N'%'; + + IF LEN(ISNULL(@Role,'')) > 0 + SET @Role = N'%' + @Role + N'%'; + + IF LEN(ISNULL(@ObjectName,'')) > 0 + SET @ObjectName = N'%' + @ObjectName + N'%'; + + IF LEN(ISNULL(@LoginName,'')) > 0 + SET @LoginName = N'%' + @LoginName + N'%'; +END; + +IF (@Principal IS NULL AND @CopyTo IS NOT NULL) OR LEN(@CopyTo) = 0 + SET @CopyTo = NULL; + +IF @Print = 1 AND @DBName = N'All' + BEGIN + PRINT 'DECLARE @AllDBNames sysname'; + PRINT 'SET @AllDBNames = ''master'''; + PRINT ''; + END; +--========================================================================= +-- Database Principals +SET @sql = + N'SELECT ' + CASE WHEN @DBName = 'All' THEN N'@AllDBNames' ELSE N'N''' + @DBName + N'''' END + N' AS DBName, + DBPrincipals.principal_id AS DBPrincipalId, DBPrincipals.name AS DBPrincipal, SrvPrincipals.name AS SrvPrincipal, + DBPrincipals.type, DBPrincipals.type_desc, DBPrincipals.default_schema_name, DBPrincipals.create_date, + DBPrincipals.modify_date, DBPrincipals.is_fixed_role, + Authorizations.name AS RoleAuthorization, DBPrincipals.sid, + CASE WHEN DBPrincipals.is_fixed_role = 0 AND DBPrincipals.name NOT IN (''dbo'',''guest'', ''INFORMATION_SCHEMA'', ''public'', ''sys'') THEN ' + NCHAR(13) + + CASE WHEN @DBName = 'All' THEN N' ''USE '' + QUOTENAME(@AllDBNames) + ''; '' + ' + NCHAR(13) ELSE N'' END + + N' ''IF DATABASE_PRINCIPAL_ID(N'''''' + ' + ISNULL('N'+QUOTENAME(@CopyTo,''''),'DBPrincipals.name') + ' + '''''') IS NOT NULL '' + + ''DROP '' + CASE DBPrincipals.[type] WHEN ''C'' THEN NULL + WHEN ''K'' THEN NULL + WHEN ''R'' THEN ''ROLE'' + WHEN ''A'' THEN ''APPLICATION ROLE'' + ELSE ''USER'' END + + '' ''+QUOTENAME(' + ISNULL('N'+QUOTENAME(@CopyTo,''''),'DBPrincipals.name') + '' + @Collation + N') + '';'' ELSE NULL END AS DropScript, + CASE WHEN DBPrincipals.is_fixed_role = 0 AND DBPrincipals.name NOT IN (''dbo'',''guest'', ''INFORMATION_SCHEMA'', ''public'', ''sys'') THEN ' + NCHAR(13) + + CASE WHEN @DBName = 'All' THEN N' ''USE '' + QUOTENAME(@AllDBNames) + ''; '' + ' +NCHAR(13) ELSE N'' END + + N' ''IF DATABASE_PRINCIPAL_ID(N'''''' + ' + ISNULL('N'+QUOTENAME(@CopyTo,''''),'DBPrincipals.name') + ' + '''''') IS NULL '' + + ''CREATE '' + CASE DBPrincipals.[type] WHEN ''C'' THEN NULL + WHEN ''K'' THEN NULL + WHEN ''R'' THEN ''ROLE'' + WHEN ''A'' THEN ''APPLICATION ROLE'' + ELSE ''USER'' END + + '' ''+QUOTENAME(' + ISNULL('N'+QUOTENAME(@CopyTo,''''),'DBPrincipals.name') + '' + @Collation + N') END + + CASE WHEN DBPrincipals.[type] = ''R'' THEN + ISNULL('' AUTHORIZATION ''+QUOTENAME(Authorizations.name' + @Collation + N'),'''') + WHEN DBPrincipals.[type] = ''A'' THEN + '''' + WHEN DBPrincipals.[type] NOT IN (''C'',''K'') THEN + ISNULL('' FOR LOGIN '' + + QUOTENAME(' + ISNULL('N'+QUOTENAME(@CopyTo,''''),'SrvPrincipals.name') + '' + @Collation + N'),'' WITHOUT LOGIN'') + + ISNULL('' WITH DEFAULT_SCHEMA = ''+ + QUOTENAME(DBPrincipals.default_schema_name' + @Collation + N'),'''') + ELSE '''' + END + '';'' + + CASE WHEN DBPrincipals.[type] NOT IN (''C'',''K'',''R'',''A'') + AND SrvPrincipals.name IS NULL + AND DBPrincipals.sid IS NOT NULL + AND DBPrincipals.sid NOT IN (0x00, 0x01) + THEN '' -- Possible missing server principal'' + ELSE '''' END + AS CreateScript' + + CASE WHEN SERVERPROPERTY('ProductVersion') >= '12' AND @ShowOrphans = 1 THEN N', + CASE WHEN DBPrincipals.name = ''dbo'' THEN ''NULL'' ELSE + ''IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = N'' + QUOTENAME(DBPrincipals.name,'''''''') + '') ' + + N'CREATE LOGIN '' + QUOTENAME(DBPrincipals.name) + + CASE WHEN DBPrincipals.type = (''S'') THEN '' WITH PASSWORD = '''''''', '' + + '' SID = '' + CONVERT(varchar(85), DBPrincipals.sid, 1) + WHEN DBPrincipals.type IN (''U'',''G'') THEN '' FROM WINDOWS '' + ELSE '''' END END AS CreateLogin, ' + + CASE WHEN @DBName = 'All' THEN N' ''USE '' + QUOTENAME(@AllDBNames) + ''; '' + ' +NCHAR(13) ELSE N'' END + + N' CASE WHEN DBPrincipals.name = ''dbo'' THEN ''EXEC sp_changedbowner '''''''';'' ELSE + ''ALTER USER '' + QUOTENAME(DBPrincipals.name) + '' WITH LOGIN = '' + QUOTENAME(DBPrincipals.name) + '';'' END AS AlterUser' ELSE '' END + ' + FROM sys.database_principals DBPrincipals + LEFT OUTER JOIN sys.database_principals Authorizations + ON DBPrincipals.owning_principal_id = Authorizations.principal_id + LEFT OUTER JOIN sys.server_principals SrvPrincipals + ON DBPrincipals.sid = SrvPrincipals.sid + AND DBPrincipals.sid NOT IN (0x00, 0x01) + WHERE 1=1 '; + +IF SERVERPROPERTY('ProductVersion') >= '12' AND @ShowOrphans = 1 + SET @sql = @sql + NCHAR(13) + N' AND DBPrincipals.authentication_type_desc <> ''NONE'' + AND SrvPrincipals.principal_id IS NULL + AND DBPrincipals.sid NOT IN (0x00, 0x01)'; + +IF LEN(ISNULL(@Principal,@Role)) > 0 + IF @Print = 1 + SET @sql = @sql + NCHAR(13) + N' AND DBPrincipals.name ' + @LikeOperator + N' N' + + ISNULL(QUOTENAME(@Principal,N''''),QUOTENAME(@Role,'''')); + ELSE + SET @sql = @sql + NCHAR(13) + N' AND DBPrincipals.name ' + @LikeOperator + N' ISNULL(@Principal,@Role) '; + +IF LEN(@Type) > 0 + IF @Print = 1 + SET @sql = @sql + NCHAR(13) + N' AND DBPrincipals.type ' + @LikeOperator + N' N' + QUOTENAME(@Type,''''); + ELSE + SET @sql = @sql + NCHAR(13) + N' AND DBPrincipals.type ' + @LikeOperator + N' @Type'; + +IF LEN(@LoginName) > 0 + IF @Print = 1 + SET @sql = @sql + NCHAR(13) + N' AND SrvPrincipals.name ' + @LikeOperator + N' N' + QUOTENAME(@LoginName,''''); + ELSE + SET @sql = @sql + NCHAR(13) + N' AND SrvPrincipals.name ' + @LikeOperator + N' @LoginName'; + +IF LEN(@ObjectName) > 0 + BEGIN + SET @sql = @sql + NCHAR(13) + + N' AND EXISTS (SELECT 1 ' + NCHAR(13) + + N' FROM sys.all_objects [Objects] ' + NCHAR(13) + + N' INNER JOIN sys.database_permissions Permission ' + NCHAR(13) + + N' ON Permission.major_id = [Objects].object_id ' + NCHAR(13) + + N' WHERE Permission.major_id = [Objects].object_id ' + NCHAR(13) + + N' AND Permission.grantee_principal_id = DBPrincipals.principal_id ' + NCHAR(13); + + IF @Print = 1 + SET @sql = @sql + N' AND [Objects].name ' + @LikeOperator + N' N' + QUOTENAME(@ObjectName,''''); + ELSE + SET @sql = @sql + N' AND [Objects].name ' + @LikeOperator + N' @ObjectName'; + + SET @sql = @sql + N')'; + END; + +IF LEN(@Permission) > 0 + BEGIN + SET @sql = @sql + NCHAR(13) + + N' AND EXISTS (SELECT 1 ' + NCHAR(13) + + N' FROM sys.database_permissions Permission ' + NCHAR(13) + + N' WHERE Permission.grantee_principal_id = DBPrincipals.principal_id ' + NCHAR(13); + + IF @Print = 1 + SET @sql = @sql + N' AND Permission.permission_name ' + @LikeOperator + N' N' + QUOTENAME(@Permission,''''); + ELSE + SET @sql = @sql + N' AND Permission.permission_name ' + @LikeOperator + N' @Permission'; + + SET @sql = @sql + N')'; + END; + +IF @IncludeMSShipped = 0 + SET @sql = @sql + NCHAR(13) + N' AND DBPrincipals.is_fixed_role = 0 ' + NCHAR(13) + + ' AND DBPrincipals.name NOT IN (''dbo'',''public'',''INFORMATION_SCHEMA'',''guest'',''sys'') '; + +IF @Print = 1 +BEGIN + PRINT N'-- Database Principals'; + PRINT CAST(@sql AS NVARCHAR(MAX)); + PRINT ''; -- Spacing before the next print + PRINT ''; +END; +ELSE +BEGIN + IF OBJECT_ID('tempdb..##DBPrincipals') IS NOT NULL + DROP TABLE ##DBPrincipals; + + -- Create temp table to store the data in + CREATE TABLE ##DBPrincipals ( + [dbname] sysname NULL, + DBPrincipalId INT NULL, + DBPrincipal sysname NULL, + SrvPrincipal sysname NULL, + TYPE CHAR(1) NULL, + [type_desc] NCHAR(60) NULL, + [default_schema_name] sysname NULL, + [create_date] DATETIME NULL, + [modify_date] DATETIME NULL, + [is_fixed_role] BIT NULL, + RoleAuthorization sysname NULL, + [sid] VARBINARY(85) NULL, + DropScript NVARCHAR(MAX) NULL, + CreateScript NVARCHAR(MAX) NULL + ); + + IF SERVERPROPERTY('ProductVersion') >= '12' AND @ShowOrphans = 1 + BEGIN + ALTER TABLE ##DBPrincipals ADD CreateLogin NVARCHAR(MAX) NULL; + ALTER TABLE ##DBPrincipals ADD AlterUser NVARCHAR(MAX) NULL; + END; + + SET @sql = @use + N'INSERT INTO ##DBPrincipals ' + NCHAR(13) + @sql; + + IF @DBName = 'All' + BEGIN + -- Declare a READ_ONLY cursor to loop through the databases + DECLARE cur_DBList CURSOR + READ_ONLY + FOR SELECT [name] FROM [sys].[databases] + WHERE STATE IN (0,5) + AND [source_database_id] IS NULL + ORDER BY [name]; + + OPEN cur_DBList; + + FETCH NEXT FROM cur_DBList INTO @AllDBNames; + WHILE (@@fetch_status <> -1) + BEGIN + IF (@@fetch_status <> -2) + BEGIN + SET @sql2 = N'USE ' + QUOTENAME(@AllDBNames) + N';' + NCHAR(13) + @sql; + EXECUTE sp_executesql @sql2, + N'@Principal sysname, @Role sysname, @Type nvarchar(30), @ObjectName sysname, + @AllDBNames sysname, @Permission sysname, @LoginName sysname', + @Principal, @Role, @Type, @ObjectName, @AllDBNames, @Permission, @LoginName; + -- PRINT @sql2 + END; + FETCH NEXT FROM cur_DBList INTO @AllDBNames; + END; + + CLOSE cur_DBList; + DEALLOCATE cur_DBList; + END; + ELSE + EXECUTE sp_executesql @sql, N'@Principal sysname, @Role sysname, @Type nvarchar(30), + @ObjectName sysname, @Permission sysname, @LoginName sysname', + @Principal, @Role, @Type, @ObjectName, @Permission, @LoginName; +END; +--========================================================================= +-- Database Role Members +IF NOT (SERVERPROPERTY('ProductVersion') >= '12' AND @ShowOrphans = 1) +BEGIN +SET @sql = + N'SELECT ' + CASE WHEN @DBName = 'All' THEN N'@AllDBNames' ELSE N'N''' + @DBName + N'''' END + N' AS DBName, + Users.principal_id AS UserPrincipalId, Users.name AS UserName, Roles.name AS RoleName, ' + NCHAR(13) + + CASE WHEN @DBName = 'All' THEN N' ''USE '' + QUOTENAME(@AllDBNames) + ''; '' + ' + NCHAR(13) ELSE N'' END + + N' CASE WHEN Users.is_fixed_role = 0 AND Users.name <> ''dbo'' THEN + ''EXEC sp_droprolemember @rolename = N''+QUOTENAME(Roles.name' + @Collation + + N','''''''')+'', @membername = N''+QUOTENAME(CASE WHEN Users.name = ''dbo'' THEN NULL + ELSE ' + ISNULL('N'+QUOTENAME(@CopyTo,''''),'Users.name') + ' END' + @Collation + + N','''''''')+'';'' END AS DropScript, ' + NCHAR(13) + + CASE WHEN @DBName = 'All' THEN N' ''USE '' + QUOTENAME(@AllDBNames) + ''; '' + ' + NCHAR(13) ELSE N'' END + + N' CASE WHEN Users.is_fixed_role = 0 AND Users.name <> ''dbo'' THEN + ''EXEC sp_addrolemember @rolename = N''+QUOTENAME(Roles.name' + @Collation + + N','''''''')+'', @membername = N''+QUOTENAME(CASE WHEN Users.name = ''dbo'' THEN NULL + ELSE ' + ISNULL('N'+QUOTENAME(@CopyTo,''''),'Users.name') + ' END' + @Collation + + N','''''''')+'';'' END AS AddScript + FROM sys.database_role_members RoleMembers + JOIN sys.database_principals Users + ON RoleMembers.member_principal_id = Users.principal_id + JOIN sys.database_principals Roles + ON RoleMembers.role_principal_id = Roles.principal_id + WHERE 1=1 '; + +IF LEN(ISNULL(@Principal,'')) > 0 + IF @Print = 1 + SET @sql = @sql + NCHAR(13) + N' AND Users.name ' + @LikeOperator + N' N'+QUOTENAME(@Principal,''''); + ELSE + SET @sql = @sql + NCHAR(13) + N' AND Users.name ' + @LikeOperator + N' @Principal'; + +IF LEN(ISNULL(@Role,'')) > 0 + IF @Print = 1 + SET @sql = @sql + NCHAR(13) + N' AND Roles.name ' + @LikeOperator + N' N'+QUOTENAME(@Role,''''); + ELSE + SET @sql = @sql + NCHAR(13) + N' AND Roles.name ' + @LikeOperator + N' @Role'; + +IF LEN(@Type) > 0 + IF @Print = 1 + SET @sql = @sql + NCHAR(13) + N' AND Users.type ' + @LikeOperator + N' N' + QUOTENAME(@Type,''''); + ELSE + SET @sql = @sql + NCHAR(13) + N' AND Users.type ' + @LikeOperator + N' @Type'; + +IF LEN(@LoginName) > 0 + BEGIN + SET @sql = @sql + NCHAR(13) + + N' AND EXISTS (SELECT 1 + FROM sys.server_principals SrvPrincipals + WHERE Users.sid NOT IN (0x00, 0x01) + AND SrvPrincipals.sid = Users.sid + AND Users.type NOT IN (''R'') ' + NCHAR(13); + IF @Print = 1 + SET @sql = @sql + NCHAR(13) + ' AND SrvPrincipals.name ' + @LikeOperator + N' N' + QUOTENAME(@LoginName,''''); + ELSE + SET @sql = @sql + NCHAR(13) + ' AND SrvPrincipals.name ' + @LikeOperator + N' @LoginName'; + + SET @sql = @sql + N')'; + END; + +IF LEN(@ObjectName) > 0 + BEGIN + SET @sql = @sql + NCHAR(13) + + N' AND EXISTS (SELECT 1 + FROM sys.all_objects [Objects] + INNER JOIN sys.database_permissions Permission + ON Permission.major_id = [Objects].object_id + WHERE Permission.major_id = [Objects].object_id + AND Permission.grantee_principal_id = Users.principal_id ' + NCHAR(13); + + IF @Print = 1 + SET @sql = @sql + N' AND [Objects].name ' + @LikeOperator + N' N' + QUOTENAME(@ObjectName,''''); + ELSE + SET @sql = @sql + N' AND [Objects].name ' + @LikeOperator + N' @ObjectName'; + + SET @sql = @sql + N')'; + END; + +IF LEN(@Permission) > 0 + BEGIN + SET @sql = @sql + NCHAR(13) + + N' AND EXISTS (SELECT 1 + FROM sys.database_permissions Permission + WHERE Permission.grantee_principal_id = Users.principal_id ' + NCHAR(13); + + IF @Print = 1 + SET @sql = @sql + N' AND Permission.permission_name ' + @LikeOperator + N' N' + QUOTENAME(@Permission,''''); + ELSE + SET @sql = @sql + N' AND Permission.permission_name ' + @LikeOperator + N' @Permission'; + + SET @sql = @sql + N')'; + END; + +IF @IncludeMSShipped = 0 + SET @sql = @sql + NCHAR(13) + N' AND Users.is_fixed_role = 0 ' + NCHAR(13) + + ' AND Users.name NOT IN (''dbo'',''public'',''INFORMATION_SCHEMA'',''guest'',''sys'') '; + +IF @Print = 1 +BEGIN + PRINT N'-- Database Role Members'; + PRINT CAST(@sql AS NVARCHAR(MAX)); + PRINT ''; -- Spacing before the next print + PRINT ''; +END; +ELSE +BEGIN + IF OBJECT_ID('tempdb..##DBRoles') IS NOT NULL + DROP TABLE ##DBRoles; + + -- Create temp table to store the data in + CREATE TABLE ##DBRoles ( + [dbname] sysname NULL, + UserPrincipalId INT NULL, + UserName sysname NULL, + RoleName sysname NULL, + DropScript NVARCHAR(MAX) NULL, + AddScript NVARCHAR(MAX) NULL + ); + + SET @sql = @use + NCHAR(13) + 'INSERT INTO ##DBRoles ' + NCHAR(13) + @sql; + + IF @DBName = 'All' + BEGIN + -- Declare a READ_ONLY cursor to loop through the databases + DECLARE cur_DBList CURSOR + READ_ONLY + FOR SELECT [name] FROM [sys].[databases] + WHERE STATE IN (0,5) + AND [source_database_id] IS NULL + ORDER BY [name]; + + OPEN cur_DBList; + + FETCH NEXT FROM cur_DBList INTO @AllDBNames; + WHILE (@@fetch_status <> -1) + BEGIN + IF (@@fetch_status <> -2) + BEGIN + SET @sql2 = 'USE ' + QUOTENAME(@AllDBNames) + ';' + NCHAR(13) + @sql; + EXECUTE sp_executesql @sql2, + N'@Principal sysname, @Role sysname, @Type nvarchar(30), @ObjectName sysname, + @AllDBNames sysname, @Permission sysname, @LoginName sysname', + @Principal, @Role, @Type, @ObjectName, @AllDBNames, @Permission, @LoginName; + -- PRINT @sql2 + END; + FETCH NEXT FROM cur_DBList INTO @AllDBNames; + END; + + CLOSE cur_DBList; + DEALLOCATE cur_DBList; + END; + ELSE + EXECUTE sp_executesql @sql, N'@Principal sysname, @Role sysname, @Type nvarchar(30), + @ObjectName sysname, @Permission sysname, @LoginName sysname', + @Principal, @Role, @Type, @ObjectName, @Permission, @LoginName; +END; +END; +--========================================================================= +-- Database & object Permissions +IF NOT (SERVERPROPERTY('ProductVersion') >= '12' AND @ShowOrphans = 1) +BEGIN +SET @ObjectList = + N'; WITH ObjectList AS ( + SELECT NULL AS SchemaName , + name ' + @Collation + ' AS name, + database_id AS id, + ''DATABASE'' AS class_desc, + '''' AS class + FROM master.sys.databases + UNION ALL + SELECT SCHEMA_NAME(sys.all_objects.schema_id) ' + @Collation + N' AS SchemaName, + name ' + @Collation + N' AS name, + object_id AS id, + ''OBJECT_OR_COLUMN'' AS class_desc, + ''OBJECT'' AS class + FROM sys.all_objects + UNION ALL + SELECT name ' + @Collation + N' AS SchemaName, + NULL AS name, + schema_id AS id, + ''SCHEMA'' AS class_desc, + ''SCHEMA'' AS class + FROM sys.schemas + UNION ALL + SELECT NULL AS SchemaName, + name ' + @Collation + N' AS name, + principal_id AS id, + ''DATABASE_PRINCIPAL'' AS class_desc, + CASE type_desc + WHEN ''APPLICATION_ROLE'' THEN ''APPLICATION ROLE'' + WHEN ''DATABASE_ROLE'' THEN ''ROLE'' + ELSE ''USER'' END AS class + FROM sys.database_principals + UNION ALL + SELECT SCHEMA_NAME(schema_id) ' + @Collation + N' AS SchemaName, + name ' + @Collation + N' AS name, + xml_collection_id AS id, + ''XML_SCHEMA_COLLECTION'' AS class_desc, + ''XML SCHEMA COLLECTION'' AS class + FROM sys.xml_schema_collections + UNION ALL + SELECT NULL AS SchemaName, + name ' + @Collation + N' AS name, + message_type_id AS id, + ''MESSAGE_TYPE'' AS class_desc, + ''MESSAGE TYPE'' AS class + FROM sys.service_message_types + UNION ALL + SELECT NULL AS SchemaName, + name ' + @Collation + N' AS name, + assembly_id AS id, + ''ASSEMBLY'' AS class_desc, + ''ASSEMBLY'' AS class + FROM sys.assemblies + UNION ALL'; + +SET @ObjectList2 = N' + SELECT SCHEMA_NAME(sys.types.schema_id) ' + @Collation + N' AS SchemaName, + name ' + @Collation + N' AS name, + user_type_id AS id, + ''TYPE'' AS class_desc, + ''TYPE'' AS class + FROM sys.types + UNION ALL + SELECT NULL AS SchemaName, + name ' + @Collation + N' AS name, + service_contract_id AS id, + ''SERVICE_CONTRACT'' AS class_desc, + ''CONTRACT'' AS class + FROM sys.service_contracts + UNION ALL + SELECT NULL AS SchemaName, + name ' + @Collation + N' AS name, + service_id AS id, + ''SERVICE'' AS class_desc, + ''SERVICE'' AS class + FROM sys.services + UNION ALL + SELECT NULL AS SchemaName, + name ' + @Collation + N' AS name, + remote_service_binding_id AS id, + ''REMOTE_SERVICE_BINDING'' AS class_desc, + ''REMOTE SERVICE BINDING'' AS class + FROM sys.remote_service_bindings + UNION ALL + SELECT NULL AS SchemaName, + name ' + @Collation + N' AS name, + route_id AS id, + ''ROUTE'' AS class_desc, + ''ROUTE'' AS class + FROM sys.routes + UNION ALL + SELECT NULL AS SchemaName, + name ' + @Collation + N' AS name, + fulltext_catalog_id AS id, + ''FULLTEXT_CATALOG'' AS class_desc, + ''FULLTEXT CATALOG'' AS class + FROM sys.fulltext_catalogs + UNION ALL + SELECT NULL AS SchemaName, + name ' + @Collation + N' AS name, + symmetric_key_id AS id, + ''SYMMETRIC_KEYS'' AS class_desc, + ''SYMMETRIC KEY'' AS class + FROM sys.symmetric_keys + UNION ALL + SELECT NULL AS SchemaName, + name ' + @Collation + N' AS name, + certificate_id AS id, + ''CERTIFICATE'' AS class_desc, + ''CERTIFICATE'' AS class + FROM sys.certificates + UNION ALL + SELECT NULL AS SchemaName, + name ' + @Collation + N' AS name, + asymmetric_key_id AS id, + ''ASYMMETRIC_KEY'' AS class_desc, + ''ASYMMETRIC KEY'' AS class + FROM sys.asymmetric_keys + ) ' + NCHAR(13); + + SET @sql = + N'SELECT ' + CASE WHEN @DBName = 'All' THEN N'@AllDBNames' ELSE N'N''' + @DBName + N'''' END + N' AS DBName, + Grantee.principal_id AS GranteePrincipalId, Grantee.name AS GranteeName, Grantor.name AS GrantorName, + Permission.class_desc, Permission.permission_name, + ObjectList.name + CASE WHEN Columns.name IS NOT NULL THEN '' ('' + Columns.name + '')'' ELSE '''' END AS ObjectName, + ObjectList.SchemaName, + Permission.state_desc, + CASE WHEN Grantee.is_fixed_role = 0 AND Grantee.name <> ''dbo'' THEN ' + NCHAR(13) + + CASE WHEN @DBName = 'All' THEN N' ''USE '' + QUOTENAME(@AllDBNames) + ''; '' + ' + NCHAR(13) ELSE N'' END + + N' ''REVOKE '' + + CASE WHEN Permission.[state] = ''W'' THEN ''GRANT OPTION FOR '' ELSE '''' END + + '' '' + Permission.permission_name' + @Collation + N' + + CASE WHEN Permission.major_id <> 0 THEN '' ON '' + + ObjectList.class + ''::'' + + ISNULL(QUOTENAME(ObjectList.SchemaName),'''') + + CASE WHEN ObjectList.SchemaName + ObjectList.name IS NULL THEN '''' ELSE ''.'' END + + ISNULL(QUOTENAME(ObjectList.name),'''') + ISNULL('' (''+ QUOTENAME(Columns.name) + '')'','''') + ' + @Collation + ' + '' '' ELSE '''' END + + '' FROM '' + QUOTENAME(' + ISNULL('N'+QUOTENAME(@CopyTo,''''),'Grantee.name') + '' + @Collation + N') + ''; '' END AS RevokeScript, + CASE WHEN Grantee.is_fixed_role = 0 AND Grantee.name <> ''dbo'' THEN ' + NCHAR(13) + + CASE WHEN @DBName = 'All' THEN N' ''USE '' + QUOTENAME(@AllDBNames) + ''; '' + ' + NCHAR(13) ELSE N'' END + + N' CASE WHEN Permission.[state] = ''W'' THEN ''GRANT'' ELSE Permission.state_desc' + @Collation + + N' END + + '' '' + Permission.permission_name' + @Collation + N' + + CASE WHEN Permission.major_id <> 0 THEN '' ON '' + + ObjectList.class + ''::'' + + ISNULL(QUOTENAME(ObjectList.SchemaName),'''') + + CASE WHEN ObjectList.SchemaName + ObjectList.name IS NULL THEN '''' ELSE ''.'' END + + ISNULL(QUOTENAME(ObjectList.name),'''') + ISNULL('' (''+ QUOTENAME(Columns.name) + '')'','''') + ' + @Collation + N' + '' '' ELSE '''' END + + '' TO '' + QUOTENAME(' + ISNULL('N'+QUOTENAME(@CopyTo,''''),'Grantee.name') + '' + @Collation + N') + '' '' + + CASE WHEN Permission.[state] = ''W'' THEN '' WITH GRANT OPTION '' ELSE '''' END + + '' AS ''+ QUOTENAME(Grantor.name' + @Collation + N')+'';'' END AS GrantScript + FROM sys.database_permissions Permission + JOIN sys.database_principals Grantee + ON Permission.grantee_principal_id = Grantee.principal_id + JOIN sys.database_principals Grantor + ON Permission.grantor_principal_id = Grantor.principal_id + LEFT OUTER JOIN ObjectList + ON Permission.major_id = ObjectList.id + AND Permission.class_desc = ObjectList.class_desc + LEFT OUTER JOIN sys.columns AS Columns + ON Permission.major_id = Columns.object_id + AND Permission.minor_id = Columns.column_id + WHERE 1=1 '; + +IF LEN(ISNULL(@Principal,@Role)) > 0 + IF @Print = 1 + SET @sql = @sql + NCHAR(13) + N' AND Grantee.name ' + @LikeOperator + N' N' + ISNULL(QUOTENAME(@Principal,''''),QUOTENAME(@Role,'''')); + ELSE + SET @sql = @sql + NCHAR(13) + N' AND Grantee.name ' + @LikeOperator + N' ISNULL(@Principal,@Role) '; + +IF LEN(@Type) > 0 + IF @Print = 1 + SET @sql = @sql + NCHAR(13) + N' AND Grantee.type ' + @LikeOperator + N' N' + QUOTENAME(@Type,''''); + ELSE + SET @sql = @sql + NCHAR(13) + N' AND Grantee.type ' + @LikeOperator + N' @Type'; + +IF LEN(@ObjectName) > 0 + IF @Print = 1 + SET @sql = @sql + NCHAR(13) + N' AND ObjectList.name ' + @LikeOperator + N' N' + QUOTENAME(@ObjectName,''''); + ELSE + SET @sql = @sql + NCHAR(13) + N' AND ObjectList.name ' + @LikeOperator + N' @ObjectName '; + +IF LEN(@Permission) > 0 + IF @Print = 1 + SET @sql = @sql + NCHAR(13) + N' AND Permission.permission_name ' + @LikeOperator + N' N' + QUOTENAME(@Permission,''''); + ELSE + SET @sql = @sql + NCHAR(13) + N' AND Permission.permission_name ' + @LikeOperator + N' @Permission'; + +IF LEN(@LoginName) > 0 + BEGIN + SET @sql = @sql + NCHAR(13) + + N' AND EXISTS (SELECT 1 + FROM sys.server_principals SrvPrincipals + WHERE SrvPrincipals.sid = Grantee.sid + AND Grantee.sid NOT IN (0x00, 0x01) + AND Grantee.type NOT IN (''R'') ' + NCHAR(13); + IF @Print = 1 + SET @sql = @sql + NCHAR(13) + N' AND SrvPrincipals.name ' + @LikeOperator + N' N' + QUOTENAME(@LoginName,''''); + ELSE + SET @sql = @sql + NCHAR(13) + N' AND SrvPrincipals.name ' + @LikeOperator + N' @LoginName'; + + SET @sql = @sql + ')'; + END; + +IF @IncludeMSShipped = 0 + SET @sql = @sql + NCHAR(13) + N' AND Grantee.is_fixed_role = 0 ' + NCHAR(13) + + ' AND Grantee.name NOT IN (''dbo'',''public'',''INFORMATION_SCHEMA'',''guest'',''sys'') '; + +IF @Print = 1 + BEGIN + PRINT '-- Database & object Permissions'; + PRINT CAST(@use AS NVARCHAR(MAX)); + PRINT CAST(@ObjectList AS NVARCHAR(MAX)); + PRINT CAST(@ObjectList2 AS NVARCHAR(MAX)); + PRINT CAST(@sql AS NVARCHAR(MAX)); + END; +ELSE +BEGIN + IF OBJECT_ID('tempdb..##DBPermissions') IS NOT NULL + DROP TABLE ##DBPermissions; + + -- Create temp table to store the data in + CREATE TABLE ##DBPermissions ( + [dbname] sysname NULL, + GranteePrincipalId INT NULL, + GranteeName sysname NULL, + GrantorName sysname NULL, + [class_desc] NVARCHAR(60) NULL, + [permission_name] NVARCHAR(128) NULL, + ObjectName sysname NULL, + SchemaName sysname NULL, + [state_desc] NVARCHAR(60) NULL, + RevokeScript NVARCHAR(MAX) NULL, + GrantScript NVARCHAR(MAX) NULL + ); + + -- Add insert statement to @sql + SET @sql = @use + @ObjectList + @ObjectList2 + + N'INSERT INTO ##DBPermissions ' + NCHAR(13) + + @sql; + + IF @DBName = 'All' + BEGIN + -- Declare a READ_ONLY cursor to loop through the databases + DECLARE cur_DBList CURSOR + READ_ONLY + FOR SELECT [name] FROM [sys].[databases] + WHERE STATE IN (0,5) + AND [source_database_id] IS NULL + ORDER BY [name]; + + OPEN cur_DBList; + + FETCH NEXT FROM cur_DBList INTO @AllDBNames; + WHILE (@@fetch_status <> -1) + BEGIN + IF (@@fetch_status <> -2) + BEGIN + SET @sql2 = 'USE ' + QUOTENAME(@AllDBNames) + ';' + NCHAR(13) + @sql; + EXECUTE sp_executesql @sql2, + N'@Principal sysname, @Role sysname, @Type nvarchar(30), @ObjectName sysname, + @AllDBNames sysname, @Permission sysname, @LoginName sysname', + @Principal, @Role, @Type, @ObjectName, @AllDBNames, @Permission, @LoginName; + -- PRINT @sql2 + END; + FETCH NEXT FROM cur_DBList INTO @AllDBNames; + END; + + CLOSE cur_DBList; + DEALLOCATE cur_DBList; + END; + ELSE + BEGIN + EXECUTE sp_executesql @sql, N'@Principal sysname, @Role sysname, @Type nvarchar(30), + @ObjectName sysname, @Permission sysname, @LoginName sysname', + @Principal, @Role, @Type, @ObjectName, @Permission, @LoginName; + END; +END; +END; + +IF @Print <> 1 +IF (SERVERPROPERTY('ProductVersion') >= '12' AND @ShowOrphans = 1) + IF @Output IN ('CreateOnly', 'DropOnly', 'ScriptOnly') + SELECT DropScript, CreateScript, CreateLogin, AlterUser + FROM ##DBPrincipals ORDER BY [dbname], DBPrincipal; + ELSE + SELECT [dbname], DBPrincipal, SrvPrincipal, TYPE, [type_desc], [default_schema_name], + [create_date], [modify_date], [is_fixed_role], RoleAuthorization, [sid], + DropScript, CreateScript, CreateLogin, AlterUser + FROM ##DBPrincipals ORDER BY [dbname], DBPrincipal; +ELSE +BEGIN + IF @Output = 'None' + PRINT ''; + ELSE IF @Output = 'CreateOnly' + BEGIN + SELECT CreateScript FROM ##DBPrincipals WHERE CreateScript IS NOT NULL + UNION ALL + SELECT AddScript FROM ##DBRoles WHERE AddScript IS NOT NULL + UNION ALL + SELECT GrantScript FROM ##DBPermissions WHERE GrantScript IS NOT NULL; + END; + ELSE IF @Output = 'DropOnly' + BEGIN + SELECT DropScript FROM ##DBPrincipals WHERE DropScript IS NOT NULL + UNION ALL + SELECT DropScript FROM ##DBRoles WHERE DropScript IS NOT NULL + UNION ALL + SELECT RevokeScript FROM ##DBPermissions WHERE RevokeScript IS NOT NULL; + END; + ELSE IF @Output = 'ScriptOnly' + BEGIN + SELECT DropScript, CreateScript FROM ##DBPrincipals WHERE DropScript IS NOT NULL OR CreateScript IS NOT NULL + UNION ALL + SELECT DropScript, AddScript FROM ##DBRoles WHERE DropScript IS NOT NULL OR AddScript IS NOT NULL + UNION ALL + SELECT RevokeScript, GrantScript FROM ##DBPermissions WHERE RevokeScript IS NOT NULL OR GrantScript IS NOT NULL; + END; + ELSE IF @Output = 'Report' + BEGIN + SELECT [dbname], DBPrincipal, SrvPrincipal, TYPE, [type_desc], + STUFF((SELECT N', ' + ##DBRoles.RoleName + FROM ##DBRoles + WHERE ##DBPrincipals.[dbname] = ##DBRoles.[dbname] + AND ##DBPrincipals.DBPrincipalId = ##DBRoles.UserPrincipalId + ORDER BY ##DBRoles.RoleName + FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)') + , 1, 2, '') AS RoleMembership, + STUFF((SELECT N', ' + ##DBPermissions.[state_desc] + N' ' + ##DBPermissions.[permission_name] + N' on ' + + COALESCE(N'OBJECT:'+##DBPermissions.SchemaName + N'.' + ##DBPermissions.ObjectName, + N'SCHEMA:'+##DBPermissions.SchemaName, + N'DATABASE:'+##DBPermissions.[dbname]) + FROM ##DBPermissions + WHERE ##DBPrincipals.[dbname] = ##DBPermissions.[dbname] + AND ##DBPrincipals.DBPrincipalId = ##DBPermissions.GranteePrincipalId + ORDER BY ##DBPermissions.[state_desc], ISNULL(##DBPermissions.ObjectName, ##DBPermissions.[dbname]), ##DBPermissions.[permission_name] + FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)') + , 1, 2, '') AS DirectPermissions + FROM ##DBPrincipals + ORDER BY [dbname], TYPE, DBPrincipal; + END; + ELSE -- 'Default' or no match + BEGIN + SELECT [dbname], DBPrincipal, SrvPrincipal, TYPE, [type_desc], [default_schema_name], + [create_date], [modify_date], [is_fixed_role], RoleAuthorization, [sid], + DropScript, CreateScript + FROM ##DBPrincipals ORDER BY [dbname], DBPrincipal; + IF LEN(@Role) > 0 + SELECT [dbname], UserName, RoleName, DropScript, AddScript + FROM ##DBRoles ORDER BY [dbname], RoleName, UserName; + ELSE + SELECT [dbname], UserName, RoleName, DropScript, AddScript + FROM ##DBRoles ORDER BY [dbname], UserName, RoleName; + + IF LEN(@ObjectName) > 0 + SELECT [dbname], GranteeName, GrantorName, [class_desc], [permission_name], ObjectName, + SchemaName, [state_desc], RevokeScript, GrantScript + FROM ##DBPermissions ORDER BY [dbname], SchemaName, ObjectName, GranteeName, [permission_name]; + ELSE + SELECT [dbname], GranteeName, GrantorName, [class_desc], [permission_name], ObjectName, + SchemaName, [state_desc], RevokeScript, GrantScript + FROM ##DBPermissions ORDER BY [dbname], GranteeName, SchemaName, ObjectName, [permission_name]; + END; + + IF @DropTempTables = 1 + BEGIN + DROP TABLE ##DBPrincipals; + DROP TABLE ##DBRoles; + DROP TABLE ##DBPermissions; + END; +END; +GO + +IF OBJECT_ID('tempdb..#logins')IS NOT NULL BEGIN; + DROP TABLE #logins; +END; +CREATE TABLE #logins ( [DBName] NVARCHAR(128), [DBPrincipal] NVARCHAR(128), [SrvPrincipal] NVARCHAR(128), [type] CHAR(1), [type_desc] NCHAR(60), [RoleMembership] NVARCHAR(MAX), [DirectPermissions] NVARCHAR(MAX) ); + +INSERT INTO [#logins] ([DBName], + [DBPrincipal], + [SrvPrincipal], + [type], + [type_desc], + [RoleMembership], + [DirectPermissions]) +EXECUTE master.[dbo].sp_DBPermissions @DBName='ALL', @type='U', @Output='report'; + +INSERT INTO [#logins] ([DBName], + [DBPrincipal], + [SrvPrincipal], + [type], + [type_desc], + [RoleMembership], + [DirectPermissions]) +EXECUTE master.[dbo].sp_DBPermissions @DBName='ALL', @type='S', @Output='report'; + +SELECT DISTINCT + 'sql and ad logins' AS [description] + ,[l].[DBPrincipal] + ,[l].[SrvPrincipal] + ,[l].[type] + --,[l].[type_desc] + --,[l].[RoleMembership] + --,[l].[DirectPermissions] + ,STUFF(p.[perm],1,1,'') AS directPermissions + ,STUFF(o.[dbName],1,1,'') AS dbNames +FROM [#logins] l + CROSS APPLY ( + SELECT ','+[l2].[DBName] + FROM [#logins] l2 + WHERE [l2].[DBPrincipal]=l.[DBPrincipal] + GROUP BY [l2].[DBName] + ORDER BY [l2].[DBName] + FOR XML PATH('') + ) o([dbName]) + CROSS APPLY ( + SELECT ';'+[l2].[DirectPermissions] + FROM [#logins] l2 + WHERE [l2].[DBPrincipal]=l.[DBPrincipal] + GROUP BY [l2].[DirectPermissions] + ORDER BY [l2].[DirectPermissions] + FOR XML PATH('') + ) p(perm) +WHERE [l].[SrvPrincipal] IS NOT NULL +AND [l].[SrvPrincipal] NOT IN ( + '##MS_PolicyEventProcessingLogin##' + ,'##MS_PolicyTsqlExecutionLogin##' +) +AND [l].[DBPrincipal] NOT IN( + 'dbo' + ,'guest' + ,'INFORMATION_SCHEMA' + ,'sys' +) + +ORDER BY [SrvPrincipal]; + +SELECT DISTINCT + 'orphan logins' AS [description] + ,[l].[DBPrincipal] + ,[l].[SrvPrincipal] + ,STUFF(o.[dbName],1,1,'') AS dbs +FROM [#logins] l + CROSS APPLY ( + SELECT ','+[l2].[DBName] + FROM [#logins] l2 + WHERE [l2].[DBPrincipal]=l.[DBPrincipal] + GROUP BY [l2].[DBName] + ORDER BY [l2].[DBName] + FOR XML PATH('') + ) o([dbName]) +WHERE [l].[DBPrincipal] IS NOT NULL +AND [l].[SrvPrincipal] IS NULL +AND [l].[DBPrincipal] NOT IN( + 'dbo' + ,'guest' + ,'INFORMATION_SCHEMA' + ,'sys' + ,'MS_DataCollectorInternalUser' +) + +ORDER BY [l].[DBPrincipal]; + diff --git a/maintenance_devdb.sql b/maintenance_devdb.sql new file mode 100644 index 0000000..d5d6c0b --- /dev/null +++ b/maintenance_devdb.sql @@ -0,0 +1,9880 @@ +/* +Deploy the db maintenance objects and setup jobs + + 1. Deploy the Ola Hallengren objects + 2. Drop any maintenance plan owned by Paul or Mehdi + 3. Create default jobs for db integrity, index and statistics maintenance (no backups) + +12.08.2025, TSC +*/ +BEGIN TRANSACTION; +SET XACT_ABORT ON; +SET NOCOUNT ON; + +--#region SQL Server Maintenance Solution + +/* + +SQL Server Maintenance Solution - SQL Server 2008, SQL Server 2008 R2, SQL Server 2012, SQL Server 2014, SQL Server 2016, SQL Server 2017, SQL Server 2019, and SQL Server 2022 + +Backup: https://ola.hallengren.com/sql-server-backup.html +Integrity Check: https://ola.hallengren.com/sql-server-integrity-check.html +Index and Statistics Maintenance: https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html + +License: https://ola.hallengren.com/license.html + +GitHub: https://github.com/olahallengren/sql-server-maintenance-solution + +Version: 2025-07-22 16:49:16 + +You can contact me by e-mail at ola@hallengren.com. + +Ola Hallengren +https://ola.hallengren.com + +*/ + +USE [master]; -- Specify the database in which the objects will be created. + +SET NOCOUNT ON; + +DECLARE @CreateJobs NVARCHAR(MAX) = 'N'; -- Specify whether jobs should be created. +DECLARE @BackupDirectory NVARCHAR(MAX) = NULL; -- Specify the backup root directory. If no directory is specified, the default backup directory is used. +DECLARE @BackupURL NVARCHAR(MAX) = NULL; -- Specify the backup root URL. +DECLARE @CleanupTime INT = NULL; -- Time in hours, after which backup files are deleted. If no time is specified, then no backup files are deleted. +DECLARE @OutputFileDirectory NVARCHAR(MAX) = NULL; -- Specify the output file directory. If no directory is specified, then the SQL Server error log directory is used. +DECLARE @LogToTable NVARCHAR(MAX) = 'Y'; -- Log commands to a table. + +DECLARE @ErrorMessage NVARCHAR(MAX); + +IF IS_SRVROLEMEMBER('sysadmin') = 0 AND NOT (EXISTS (SELECT * FROM [sys].[databases] WHERE [name] = 'rdsadmin') AND SUSER_SNAME(0x01) = 'rdsa') +BEGIN + SET @ErrorMessage = 'You need to be a member of the SysAdmin server role to install the SQL Server Maintenance Solution.'; + RAISERROR(@ErrorMessage,16,1) WITH NOWAIT; +END; + +IF NOT (SELECT [compatibility_level] FROM [sys].[databases] WHERE [name] = DB_NAME()) >= 90 +BEGIN + SET @ErrorMessage = 'The database ' + QUOTENAME(DB_NAME()) + ' has to be in compatibility level 90 or higher.'; + RAISERROR(@ErrorMessage,16,1) WITH NOWAIT; +END; + +IF @BackupDirectory IS NOT NULL AND @BackupURL IS NOT NULL +BEGIN + SET @ErrorMessage = 'Only one of the variables @BackupDirectory and @BackupURL can be set.'; + RAISERROR(@ErrorMessage,16,1) WITH NOWAIT; +END; + +IF @BackupURL IS NOT NULL AND @CleanupTime IS NOT NULL +BEGIN + SET @ErrorMessage = 'The variable @CleanupTime is not supported with backup to URL.'; + RAISERROR(@ErrorMessage,16,1) WITH NOWAIT; +END; + +IF OBJECT_ID('tempdb..#Config') IS NOT NULL DROP TABLE #Config; + +CREATE TABLE #Config ([Name] NVARCHAR(MAX), + [Value] NVARCHAR(MAX)); + +INSERT INTO #Config ([Name], [Value]) VALUES('CreateJobs', @CreateJobs); +INSERT INTO #Config ([Name], [Value]) VALUES('BackupDirectory', @BackupDirectory); +INSERT INTO #Config ([Name], [Value]) VALUES('BackupURL', @BackupURL); +INSERT INTO #Config ([Name], [Value]) VALUES('CleanupTime', @CleanupTime); +INSERT INTO #Config ([Name], [Value]) VALUES('OutputFileDirectory', @OutputFileDirectory); +INSERT INTO #Config ([Name], [Value]) VALUES('LogToTable', @LogToTable); +INSERT INTO #Config ([Name], [Value]) VALUES('DatabaseName', DB_NAME()); +GO +SET ANSI_NULLS ON; +GO +SET QUOTED_IDENTIFIER ON; +GO +IF NOT EXISTS (SELECT * FROM [sys].[objects] WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[CommandLog]') AND TYPE IN (N'U')) +BEGIN +CREATE TABLE [dbo].[CommandLog]( + [ID] [int] IDENTITY(1,1) NOT NULL, + [DatabaseName] [sysname] NULL, + [SchemaName] [sysname] NULL, + [ObjectName] [sysname] NULL, + [ObjectType] [char](2) NULL, + [IndexName] [sysname] NULL, + [IndexType] [tinyint] NULL, + [StatisticsName] [sysname] NULL, + [PartitionNumber] [int] NULL, + [ExtendedInfo] [xml] NULL, + [Command] [nvarchar](MAX) NOT NULL, + [CommandType] [nvarchar](60) NOT NULL, + [StartTime] [datetime2](7) NOT NULL, + [EndTime] [datetime2](7) NULL, + [ErrorNumber] [int] NULL, + [ErrorMessage] [nvarchar](MAX) NULL, + CONSTRAINT [PK_CommandLog] PRIMARY KEY CLUSTERED +( + [ID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +); +END; +GO +SET ANSI_NULLS ON; +GO +SET QUOTED_IDENTIFIER ON; +GO +IF NOT EXISTS (SELECT * FROM [sys].[objects] WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[CommandExecute]') AND TYPE IN (N'P', N'PC')) +BEGIN +EXECUTE [dbo].sp_executesql @statement = N'CREATE PROCEDURE [dbo].[CommandExecute] AS'; +END; +GO +ALTER PROCEDURE [dbo].[CommandExecute] + +@DatabaseContext NVARCHAR(MAX), +@Command NVARCHAR(MAX), +@CommandType NVARCHAR(MAX), +@Mode INT, +@Comment NVARCHAR(MAX) = NULL, +@DatabaseName NVARCHAR(MAX) = NULL, +@SchemaName NVARCHAR(MAX) = NULL, +@ObjectName NVARCHAR(MAX) = NULL, +@ObjectType NVARCHAR(MAX) = NULL, +@IndexName NVARCHAR(MAX) = NULL, +@IndexType INT = NULL, +@StatisticsName NVARCHAR(MAX) = NULL, +@PartitionNumber INT = NULL, +@ExtendedInfo XML = NULL, +@LockMessageSeverity INT = 16, +@ExecuteAsUser NVARCHAR(MAX) = NULL, +@LogToTable NVARCHAR(MAX), +@Execute NVARCHAR(MAX) + +AS + +BEGIN + + ---------------------------------------------------------------------------------------------------- + --// Source: https://ola.hallengren.com //-- + --// License: https://ola.hallengren.com/license.html //-- + --// GitHub: https://github.com/olahallengren/sql-server-maintenance-solution //-- + --// Version: 2025-07-22 16:49:16 //-- + ---------------------------------------------------------------------------------------------------- + + SET NOCOUNT ON; + + DECLARE @StartMessage NVARCHAR(MAX); + DECLARE @EndMessage NVARCHAR(MAX); + DECLARE @ErrorMessage NVARCHAR(MAX); + DECLARE @ErrorMessageOriginal NVARCHAR(MAX); + DECLARE @Severity INT; + + DECLARE @Errors TABLE ([Id] INT IDENTITY PRIMARY KEY, + [Message] NVARCHAR(MAX) NOT NULL, + [severity] INT NOT NULL, + [State] INT); + + DECLARE @CurrentMessage NVARCHAR(MAX); + DECLARE @CurrentSeverity INT; + DECLARE @CurrentState INT; + + DECLARE @sp_executesql NVARCHAR(MAX) = QUOTENAME(@DatabaseContext) + '.sys.sp_executesql'; + + DECLARE @StartTime DATETIME2; + DECLARE @EndTime DATETIME2; + + DECLARE @ID INT; + + DECLARE @Error INT = 0; + DECLARE @ReturnCode INT = 0; + + DECLARE @EmptyLine NVARCHAR(MAX) = CHAR(9); + + DECLARE @RevertCommand NVARCHAR(MAX); + + ---------------------------------------------------------------------------------------------------- + --// Check core requirements //-- + ---------------------------------------------------------------------------------------------------- + + IF NOT (SELECT [compatibility_level] FROM [sys].[databases] WHERE [name] = DB_NAME()) >= 90 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The database ' + QUOTENAME(DB_NAME()) + ' has to be in compatibility level 90 or higher.', 16, 1; + END; + + IF NOT (SELECT [uses_ansi_nulls] FROM [sys].[sql_modules] WHERE [object_id] = @@PROCID) = 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'ANSI_NULLS has to be set to ON for the stored procedure.', 16, 1; + END; + + IF NOT (SELECT [uses_quoted_identifier] FROM [sys].[sql_modules] WHERE [object_id] = @@PROCID) = 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'QUOTED_IDENTIFIER has to be set to ON for the stored procedure.', 16, 1; + END; + + IF @LogToTable = 'Y' AND NOT EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'U' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'CommandLog') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The table CommandLog is missing. Download https://ola.hallengren.com/scripts/CommandLog.sql.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Check input parameters //-- + ---------------------------------------------------------------------------------------------------- + + IF @DatabaseContext IS NULL OR NOT EXISTS (SELECT * FROM [sys].[databases] WHERE [name] = @DatabaseContext) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabaseContext is not supported.', 16, 1; + END; + + IF @Command IS NULL OR @Command = '' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Command is not supported.', 16, 1; + END; + + IF @CommandType IS NULL OR @CommandType = '' OR LEN(@CommandType) > 60 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CommandType is not supported.', 16, 1; + END; + + IF @Mode NOT IN(1,2) OR @Mode IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Mode is not supported.', 16, 1; + END; + + IF @LockMessageSeverity NOT IN(10,16) OR @LockMessageSeverity IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @LockMessageSeverity is not supported.', 16, 1; + END; + + IF LEN(@ExecuteAsUser) > 128 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ExecuteAsUser is not supported.', 16, 1; + END; + + IF @LogToTable NOT IN('Y','N') OR @LogToTable IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @LogToTable is not supported.', 16, 1; + END; + + IF @Execute NOT IN('Y','N') OR @Execute IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Execute is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Raise errors //-- + ---------------------------------------------------------------------------------------------------- + + DECLARE ErrorCursor CURSOR FAST_FORWARD FOR SELECT [Message], [severity], [State] FROM @Errors ORDER BY [ID] ASC; + + OPEN ErrorCursor; + + FETCH ErrorCursor INTO @CurrentMessage, @CurrentSeverity, @CurrentState; + + WHILE @@FETCH_STATUS = 0 + BEGIN + RAISERROR('%s', @CurrentSeverity, @CurrentState, @CurrentMessage) WITH NOWAIT; + RAISERROR(@EmptyLine, 10, 1) WITH NOWAIT; + + FETCH NEXT FROM ErrorCursor INTO @CurrentMessage, @CurrentSeverity, @CurrentState; + END; + + CLOSE ErrorCursor; + + DEALLOCATE ErrorCursor; + + IF EXISTS (SELECT * FROM @Errors WHERE [severity] >= 16) + BEGIN + SET @ReturnCode = 50000; + GOTO ReturnCode; + END; + + ---------------------------------------------------------------------------------------------------- + --// Execute as user //-- + ---------------------------------------------------------------------------------------------------- + + IF @ExecuteAsUser IS NOT NULL + BEGIN + SET @Command = 'EXECUTE AS USER = ''' + REPLACE(@ExecuteAsUser,'''','''''') + '''; ' + @Command + '; REVERT;'; + + SET @RevertCommand = 'REVERT'; + END; + + ---------------------------------------------------------------------------------------------------- + --// Log initial information //-- + ---------------------------------------------------------------------------------------------------- + + SET @StartTime = SYSDATETIME(); + + SET @StartMessage = 'Date and time: ' + CONVERT(NVARCHAR,@StartTime,120); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Database context: ' + QUOTENAME(@DatabaseContext); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Command: ' + @Command; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + IF @Comment IS NOT NULL + BEGIN + SET @StartMessage = 'Comment: ' + @Comment; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + END; + + IF @LogToTable = 'Y' + BEGIN + INSERT INTO [dbo].CommandLog (DatabaseName, SchemaName, ObjectName, ObjectType, IndexName, IndexType, StatisticsName, PartitionNumber, ExtendedInfo, CommandType, [command], [starttime]) + VALUES (@DatabaseName, @SchemaName, @ObjectName, @ObjectType, @IndexName, @IndexType, @StatisticsName, @PartitionNumber, @ExtendedInfo, @CommandType, @Command, @StartTime); + END; + + SET @ID = SCOPE_IDENTITY(); + + ---------------------------------------------------------------------------------------------------- + --// Execute command //-- + ---------------------------------------------------------------------------------------------------- + + IF @Mode = 1 AND @Execute = 'Y' + BEGIN + EXECUTE @sp_executesql @stmt = @Command; + SET @Error = @@ERROR; + SET @ReturnCode = @Error; + END; + + IF @Mode = 2 AND @Execute = 'Y' + BEGIN + BEGIN TRY + EXECUTE @sp_executesql @stmt = @Command; + END TRY + BEGIN CATCH + SET @Error = ERROR_NUMBER(); + SET @ErrorMessageOriginal = ERROR_MESSAGE(); + + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS NVARCHAR) + ', ' + ISNULL(ERROR_MESSAGE(),''); + SET @Severity = CASE WHEN ERROR_NUMBER() IN(1205,1222) THEN @LockMessageSeverity ELSE 16 END; + RAISERROR('%s',@Severity,1,@ErrorMessage) WITH NOWAIT; + + IF NOT (ERROR_NUMBER() IN(1205,1222) AND @LockMessageSeverity = 10) + BEGIN + SET @ReturnCode = ERROR_NUMBER(); + END; + + IF @ExecuteAsUser IS NOT NULL + BEGIN + EXECUTE @sp_executesql @RevertCommand; + END; + END CATCH; + END; + + ---------------------------------------------------------------------------------------------------- + --// Log completing information //-- + ---------------------------------------------------------------------------------------------------- + + SET @EndTime = SYSDATETIME(); + + SET @EndMessage = 'Outcome: ' + CASE WHEN @Execute = 'N' THEN 'Not Executed' WHEN @Error = 0 THEN 'Succeeded' ELSE 'Failed' END; + RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT; + + SET @EndMessage = 'Duration: ' + CASE WHEN (DATEDIFF(SECOND,@StartTime,@EndTime) / (24 * 3600)) > 0 THEN CAST((DATEDIFF(SECOND,@StartTime,@EndTime) / (24 * 3600)) AS NVARCHAR) + '.' ELSE '' END + CONVERT(NVARCHAR,DATEADD(SECOND,DATEDIFF(SECOND,@StartTime,@EndTime),'1900-01-01'),108); + RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT; + + SET @EndMessage = 'Date and time: ' + CONVERT(NVARCHAR,@EndTime,120); + RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT; + + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + IF @LogToTable = 'Y' + BEGIN + UPDATE [dbo].CommandLog + SET [endtime] = @EndTime, + ErrorNumber = CASE WHEN @Execute = 'N' THEN NULL ELSE @Error END, + ErrorMessage = @ErrorMessageOriginal + WHERE [Id] = @ID; + END; + + ReturnCode:; + IF @ReturnCode <> 0 + BEGIN + RETURN @ReturnCode; + END; + + ---------------------------------------------------------------------------------------------------- + +END; +GO +SET ANSI_NULLS ON; +GO +SET QUOTED_IDENTIFIER ON; +GO +IF NOT EXISTS (SELECT * FROM [sys].[objects] WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[DatabaseBackup]') AND TYPE IN (N'P', N'PC')) +BEGIN +EXECUTE [dbo].sp_executesql @statement = N'CREATE PROCEDURE [dbo].[DatabaseBackup] AS'; +END; +GO +ALTER PROCEDURE [dbo].[DatabaseBackup] + +@Databases NVARCHAR(MAX) = NULL, +@Directory NVARCHAR(MAX) = NULL, +@BackupType NVARCHAR(MAX), +@Verify NVARCHAR(MAX) = 'N', +@CleanupTime INT = NULL, +@CleanupMode NVARCHAR(MAX) = 'AFTER_BACKUP', +@Compress NVARCHAR(MAX) = NULL, +@CompressionAlgorithm NVARCHAR(MAX) = NULL, +@CompressionLevel NVARCHAR(MAX) = NULL, +@CopyOnly NVARCHAR(MAX) = 'N', +@ChangeBackupType NVARCHAR(MAX) = 'N', +@BackupSoftware NVARCHAR(MAX) = NULL, +@Checksum NVARCHAR(MAX) = NULL, +@BlockSize INT = NULL, +@BufferCount INT = NULL, +@MaxTransferSize INT = NULL, +@NumberOfFiles INT = NULL, +@MinBackupSizeForMultipleFiles INT = NULL, +@MaxFileSize INT = NULL, +@CompressionLevelNumeric INT = NULL, +@Description NVARCHAR(MAX) = NULL, +@BackupSetName NVARCHAR(MAX) = NULL, +@Threads INT = NULL, +@Throttle INT = NULL, +@Encrypt NVARCHAR(MAX) = 'N', +@EncryptionAlgorithm NVARCHAR(MAX) = NULL, +@ServerCertificate NVARCHAR(MAX) = NULL, +@ServerAsymmetricKey NVARCHAR(MAX) = NULL, +@EncryptionKey NVARCHAR(MAX) = NULL, +@ReadWriteFileGroups NVARCHAR(MAX) = 'N', +@OverrideBackupPreference NVARCHAR(MAX) = 'N', +@NoRecovery NVARCHAR(MAX) = 'N', +@URL NVARCHAR(MAX) = NULL, +@Credential NVARCHAR(MAX) = NULL, +@MirrorDirectory NVARCHAR(MAX) = NULL, +@MirrorCleanupTime INT = NULL, +@MirrorCleanupMode NVARCHAR(MAX) = 'AFTER_BACKUP', +@MirrorURL NVARCHAR(MAX) = NULL, +@AvailabilityGroups NVARCHAR(MAX) = NULL, +@Updateability NVARCHAR(MAX) = 'ALL', +@AdaptiveCompression NVARCHAR(MAX) = NULL, +@ModificationLevel INT = NULL, +@MinDatabaseSizeForDifferentialBackup INT = NULL, +@LogSizeSinceLastLogBackup INT = NULL, +@TimeSinceLastLogBackup INT = NULL, +@DataDomainBoostHost NVARCHAR(MAX) = NULL, +@DataDomainBoostUser NVARCHAR(MAX) = NULL, +@DataDomainBoostDevicePath NVARCHAR(MAX) = NULL, +@DataDomainBoostLockboxPath NVARCHAR(MAX) = NULL, +@DirectoryStructure NVARCHAR(MAX) = '{ServerName}${InstanceName}{DirectorySeparator}{DatabaseName}{DirectorySeparator}{BackupType}_{Partial}_{CopyOnly}', +@AvailabilityGroupDirectoryStructure NVARCHAR(MAX) = '{ClusterName}${AvailabilityGroupName}{DirectorySeparator}{DatabaseName}{DirectorySeparator}{BackupType}_{Partial}_{CopyOnly}', +@DirectoryStructureCase NVARCHAR(MAX) = NULL, +@FileName NVARCHAR(MAX) = '{ServerName}${InstanceName}_{DatabaseName}_{BackupType}_{Partial}_{CopyOnly}_{Year}{Month}{Day}_{Hour}{Minute}{Second}_{FileNumber}.{FileExtension}', +@AvailabilityGroupFileName NVARCHAR(MAX) = '{ClusterName}${AvailabilityGroupName}_{DatabaseName}_{BackupType}_{Partial}_{CopyOnly}_{Year}{Month}{Day}_{Hour}{Minute}{Second}_{FileNumber}.{FileExtension}', +@FileNameCase NVARCHAR(MAX) = NULL, +@TokenTimezone NVARCHAR(MAX) = 'LOCAL', +@FileExtensionFull NVARCHAR(MAX) = NULL, +@FileExtensionDiff NVARCHAR(MAX) = NULL, +@FileExtensionLog NVARCHAR(MAX) = NULL, +@Init NVARCHAR(MAX) = 'N', +@Format NVARCHAR(MAX) = 'N', +@ObjectLevelRecoveryMap NVARCHAR(MAX) = 'N', +@ExcludeLogShippedFromLogBackup NVARCHAR(MAX) = 'Y', +@DirectoryCheck NVARCHAR(MAX) = 'Y', +@BackupOptions NVARCHAR(MAX) = NULL, +@Stats INT = NULL, +@ExpireDate DATETIME = NULL, +@RetainDays INT = NULL, +@StringDelimiter NVARCHAR(MAX) = ',', +@DatabaseOrder NVARCHAR(MAX) = NULL, +@DatabasesInParallel NVARCHAR(MAX) = 'N', +@LogToTable NVARCHAR(MAX) = 'N', +@Execute NVARCHAR(MAX) = 'Y' + +AS + +BEGIN + + ---------------------------------------------------------------------------------------------------- + --// Source: https://ola.hallengren.com //-- + --// License: https://ola.hallengren.com/license.html //-- + --// GitHub: https://github.com/olahallengren/sql-server-maintenance-solution //-- + --// Version: 2025-07-22 16:49:16 //-- + ---------------------------------------------------------------------------------------------------- + + SET NOCOUNT ON; + + DECLARE @StartMessage NVARCHAR(MAX); + DECLARE @EndMessage NVARCHAR(MAX); + DECLARE @DatabaseMessage NVARCHAR(MAX); + DECLARE @ErrorMessage NVARCHAR(MAX); + + DECLARE @StartTime DATETIME2 = SYSDATETIME(); + DECLARE @SchemaName NVARCHAR(MAX) = OBJECT_SCHEMA_NAME(@@PROCID); + DECLARE @ObjectName NVARCHAR(MAX) = OBJECT_NAME(@@PROCID); + DECLARE @VersionTimestamp NVARCHAR(MAX) = SUBSTRING(OBJECT_DEFINITION(@@PROCID),CHARINDEX('--// Version: ',OBJECT_DEFINITION(@@PROCID)) + LEN('--// Version: ') + 1, 19); + DECLARE @Parameters NVARCHAR(MAX); + + DECLARE @HostPlatform NVARCHAR(MAX); + DECLARE @DirectorySeparator NVARCHAR(MAX); + + DECLARE @Updated BIT; + + DECLARE @Cluster NVARCHAR(MAX); + + DECLARE @DefaultDirectory NVARCHAR(4000); + + DECLARE @QueueID INT; + DECLARE @QueueStartTime DATETIME2; + + DECLARE @CurrentRootDirectoryID INT; + DECLARE @CurrentRootDirectoryPath NVARCHAR(4000); + + DECLARE @CurrentDBID INT; + DECLARE @CurrentDatabaseName NVARCHAR(MAX); + + DECLARE @CurrentDatabase_sp_executesql NVARCHAR(MAX); + + DECLARE @CurrentUserAccess NVARCHAR(MAX); + DECLARE @CurrentIsReadOnly BIT; + DECLARE @CurrentDatabaseState NVARCHAR(MAX); + DECLARE @CurrentInStandby BIT; + DECLARE @CurrentRecoveryModel NVARCHAR(MAX); + DECLARE @CurrentDatabaseSize BIGINT; + + DECLARE @CurrentIsEncrypted BIT; + + DECLARE @CurrentBackupType NVARCHAR(MAX); + DECLARE @CurrentMaxTransferSize INT; + DECLARE @CurrentNumberOfFiles INT; + DECLARE @CurrentFileExtension NVARCHAR(MAX); + DECLARE @CurrentFileNumber INT; + DECLARE @CurrentDifferentialBaseLSN NUMERIC(25,0); + DECLARE @CurrentDifferentialBaseIsSnapshot BIT; + DECLARE @CurrentLogLSN NUMERIC(25,0); + DECLARE @CurrentLatestBackup DATETIME2; + DECLARE @CurrentDatabaseNameFS NVARCHAR(MAX); + DECLARE @CurrentDirectoryStructure NVARCHAR(MAX); + DECLARE @CurrentDatabaseFileName NVARCHAR(MAX); + DECLARE @CurrentMaxFilePathLength NVARCHAR(MAX); + DECLARE @CurrentFileName NVARCHAR(MAX); + DECLARE @CurrentDirectoryID INT; + DECLARE @CurrentDirectoryPath NVARCHAR(4000); + DECLARE @CurrentFilePath NVARCHAR(MAX); + DECLARE @CurrentDate DATETIME2; + DECLARE @CurrentDateUTC DATETIME2; + DECLARE @CurrentCleanupDate DATETIME2; + DECLARE @CurrentReplicaID UNIQUEIDENTIFIER; + DECLARE @CurrentAvailabilityGroupID UNIQUEIDENTIFIER; + DECLARE @CurrentAvailabilityGroup NVARCHAR(MAX); + DECLARE @CurrentAvailabilityGroupRole NVARCHAR(MAX); + DECLARE @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState NVARCHAR(MAX); + DECLARE @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth NVARCHAR(MAX); + DECLARE @CurrentAvailabilityGroupBackupPreference NVARCHAR(MAX); + DECLARE @CurrentIsPreferredBackupReplica BIT; + DECLARE @CurrentDatabaseMirroringRole NVARCHAR(MAX); + DECLARE @CurrentLogShippingRole NVARCHAR(MAX); + DECLARE @CurrentBackupOperationSupportedOnSecondaryReplicas BIT; + + DECLARE @CurrentBackupSetID INT; + DECLARE @CurrentIsMirror BIT; + DECLARE @CurrentLastLogBackup DATETIME2; + DECLARE @CurrentLogSizeSinceLastLogBackup FLOAT; + DECLARE @CurrentAllocatedExtentPageCount BIGINT; + DECLARE @CurrentModifiedExtentPageCount BIGINT; + + DECLARE @CurrentDatabaseContext NVARCHAR(MAX); + DECLARE @CurrentCommand NVARCHAR(MAX); + DECLARE @CurrentCommandOutput INT; + DECLARE @CurrentCommandType NVARCHAR(MAX); + + DECLARE @Errors TABLE ([Id] INT IDENTITY PRIMARY KEY, + [Message] NVARCHAR(MAX) NOT NULL, + [severity] INT NOT NULL, + [State] INT); + + DECLARE @CurrentMessage NVARCHAR(MAX); + DECLARE @CurrentSeverity INT; + DECLARE @CurrentState INT; + + DECLARE @Directories TABLE ([Id] INT PRIMARY KEY, + DirectoryPath NVARCHAR(MAX), + [mirror] BIT, + Completed BIT); + + DECLARE @URLs TABLE ([Id] INT PRIMARY KEY, + DirectoryPath NVARCHAR(MAX), + [mirror] BIT); + + DECLARE @DirectoryInfo TABLE (FileExists BIT, + FileIsADirectory BIT, + ParentDirectoryExists BIT); + + DECLARE @tmpDatabases TABLE ([Id] INT IDENTITY, + DatabaseName NVARCHAR(MAX), + DatabaseNameFS NVARCHAR(MAX), + DatabaseType NVARCHAR(MAX), + AvailabilityGroup BIT, + StartPosition INT, + DatabaseSize BIGINT, + LogSizeSinceLastLogBackup FLOAT, + [Order] INT, + Selected BIT, + Completed BIT, + PRIMARY KEY(Selected, Completed, [Order], [Id])); + + DECLARE @tmpAvailabilityGroups TABLE ([Id] INT IDENTITY PRIMARY KEY, + AvailabilityGroupName NVARCHAR(MAX), + StartPosition INT, + Selected BIT); + + DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName NVARCHAR(MAX), + AvailabilityGroupName NVARCHAR(MAX)); + + DECLARE @SelectedDatabases TABLE (DatabaseName NVARCHAR(MAX), + DatabaseType NVARCHAR(MAX), + AvailabilityGroup NVARCHAR(MAX), + StartPosition INT, + Selected BIT); + + DECLARE @SelectedAvailabilityGroups TABLE (AvailabilityGroupName NVARCHAR(MAX), + StartPosition INT, + Selected BIT); + + DECLARE @CurrentBackupOutput BIT; + + DECLARE @CurrentBackupSet TABLE ([Id] INT IDENTITY PRIMARY KEY, + [mirror] BIT, + VerifyCompleted BIT, + VerifyOutput INT); + + DECLARE @CurrentDirectories TABLE ([Id] INT PRIMARY KEY, + DirectoryPath NVARCHAR(MAX), + [mirror] BIT, + DirectoryNumber INT, + CleanupDate DATETIME2, + CleanupMode NVARCHAR(MAX), + CreateCompleted BIT, + CleanupCompleted BIT, + CreateOutput INT, + CleanupOutput INT); + + DECLARE @CurrentURLs TABLE ([Id] INT PRIMARY KEY, + DirectoryPath NVARCHAR(MAX), + [mirror] BIT, + DirectoryNumber INT); + + DECLARE @CurrentFiles TABLE ([Type] NVARCHAR(MAX), + FilePath NVARCHAR(MAX), + [mirror] BIT); + + DECLARE @CurrentCleanupDates TABLE (CleanupDate DATETIME2, + [mirror] BIT); + + DECLARE @Error INT = 0; + DECLARE @ReturnCode INT = 0; + + DECLARE @EmptyLine NVARCHAR(MAX) = CHAR(9); + + DECLARE @Version NUMERIC(18,10) = CAST(LEFT(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)),CHARINDEX('.',CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX))) - 1) + '.' + REPLACE(RIGHT(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)), LEN(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX))) - CHARINDEX('.',CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)))),'.','') AS NUMERIC(18,10)); + + IF @Version >= 14 + BEGIN + SELECT @HostPlatform = [host_platform] + FROM [sys].[dm_os_host_info]; + END; + ELSE + BEGIN + SET @HostPlatform = 'Windows'; + END; + + DECLARE @AmazonRDS BIT = CASE WHEN EXISTS (SELECT * FROM [sys].[databases] WHERE [name] = 'rdsadmin') AND SUSER_SNAME(0x01) = 'rdsa' THEN 1 ELSE 0 END; + + ---------------------------------------------------------------------------------------------------- + --// Log initial information //-- + ---------------------------------------------------------------------------------------------------- + + SET @Parameters = '@Databases = ' + ISNULL('''' + REPLACE(@Databases,'''','''''') + '''','NULL'); + SET @Parameters += ', @Directory = ' + ISNULL('''' + REPLACE(@Directory,'''','''''') + '''','NULL'); + SET @Parameters += ', @BackupType = ' + ISNULL('''' + REPLACE(@BackupType,'''','''''') + '''','NULL'); + SET @Parameters += ', @Verify = ' + ISNULL('''' + REPLACE(@Verify,'''','''''') + '''','NULL'); + SET @Parameters += ', @CleanupTime = ' + ISNULL(CAST(@CleanupTime AS NVARCHAR),'NULL'); + SET @Parameters += ', @CleanupMode = ' + ISNULL('''' + REPLACE(@CleanupMode,'''','''''') + '''','NULL'); + SET @Parameters += ', @Compress = ' + ISNULL('''' + REPLACE(@Compress,'''','''''') + '''','NULL'); + SET @Parameters += ', @CompressionAlgorithm = ' + ISNULL('''' + REPLACE(@CompressionAlgorithm,'''','''''') + '''','NULL'); + SET @Parameters += ', @CompressionLevel = ' + ISNULL('''' + REPLACE(@CompressionLevel,'''','''''') + '''','NULL'); + SET @Parameters += ', @CopyOnly = ' + ISNULL('''' + REPLACE(@CopyOnly,'''','''''') + '''','NULL'); + SET @Parameters += ', @ChangeBackupType = ' + ISNULL('''' + REPLACE(@ChangeBackupType,'''','''''') + '''','NULL'); + SET @Parameters += ', @BackupSoftware = ' + ISNULL('''' + REPLACE(@BackupSoftware,'''','''''') + '''','NULL'); + SET @Parameters += ', @Checksum = ' + ISNULL('''' + REPLACE(@Checksum,'''','''''') + '''','NULL'); + SET @Parameters += ', @BlockSize = ' + ISNULL(CAST(@BlockSize AS NVARCHAR),'NULL'); + SET @Parameters += ', @BufferCount = ' + ISNULL(CAST(@BufferCount AS NVARCHAR),'NULL'); + SET @Parameters += ', @MaxTransferSize = ' + ISNULL(CAST(@MaxTransferSize AS NVARCHAR),'NULL'); + SET @Parameters += ', @NumberOfFiles = ' + ISNULL(CAST(@NumberOfFiles AS NVARCHAR),'NULL'); + SET @Parameters += ', @MinBackupSizeForMultipleFiles = ' + ISNULL(CAST(@MinBackupSizeForMultipleFiles AS NVARCHAR),'NULL'); + SET @Parameters += ', @MaxFileSize = ' + ISNULL(CAST(@MaxFileSize AS NVARCHAR),'NULL'); + SET @Parameters += ', @CompressionLevelNumeric = ' + ISNULL(CAST(@CompressionLevelNumeric AS NVARCHAR),'NULL'); + SET @Parameters += ', @Description = ' + ISNULL('''' + REPLACE(@Description,'''','''''') + '''','NULL'); + SET @Parameters += ', @BackupSetName = ' + ISNULL('''' + REPLACE(@BackupSetName,'''','''''') + '''','NULL'); + SET @Parameters += ', @Threads = ' + ISNULL(CAST(@Threads AS NVARCHAR),'NULL'); + SET @Parameters += ', @Throttle = ' + ISNULL(CAST(@Throttle AS NVARCHAR),'NULL'); + SET @Parameters += ', @Encrypt = ' + ISNULL('''' + REPLACE(@Encrypt,'''','''''') + '''','NULL'); + SET @Parameters += ', @EncryptionAlgorithm = ' + ISNULL('''' + REPLACE(@EncryptionAlgorithm,'''','''''') + '''','NULL'); + SET @Parameters += ', @ServerCertificate = ' + ISNULL('''' + REPLACE(@ServerCertificate,'''','''''') + '''','NULL'); + SET @Parameters += ', @ServerAsymmetricKey = ' + ISNULL('''' + REPLACE(@ServerAsymmetricKey,'''','''''') + '''','NULL'); + SET @Parameters += ', @EncryptionKey = ' + ISNULL('''' + REPLACE(@EncryptionKey,'''','''''') + '''','NULL'); + SET @Parameters += ', @ReadWriteFileGroups = ' + ISNULL('''' + REPLACE(@ReadWriteFileGroups,'''','''''') + '''','NULL'); + SET @Parameters += ', @OverrideBackupPreference = ' + ISNULL('''' + REPLACE(@OverrideBackupPreference,'''','''''') + '''','NULL'); + SET @Parameters += ', @NoRecovery = ' + ISNULL('''' + REPLACE(@NoRecovery,'''','''''') + '''','NULL'); + SET @Parameters += ', @URL = ' + ISNULL('''' + REPLACE(@URL,'''','''''') + '''','NULL'); + SET @Parameters += ', @Credential = ' + ISNULL('''' + REPLACE(@Credential,'''','''''') + '''','NULL'); + SET @Parameters += ', @MirrorDirectory = ' + ISNULL('''' + REPLACE(@MirrorDirectory,'''','''''') + '''','NULL'); + SET @Parameters += ', @MirrorCleanupTime = ' + ISNULL(CAST(@MirrorCleanupTime AS NVARCHAR),'NULL'); + SET @Parameters += ', @MirrorCleanupMode = ' + ISNULL('''' + REPLACE(@MirrorCleanupMode,'''','''''') + '''','NULL'); + SET @Parameters += ', @MirrorURL = ' + ISNULL('''' + REPLACE(@MirrorURL,'''','''''') + '''','NULL'); + SET @Parameters += ', @AvailabilityGroups = ' + ISNULL('''' + REPLACE(@AvailabilityGroups,'''','''''') + '''','NULL'); + SET @Parameters += ', @Updateability = ' + ISNULL('''' + REPLACE(@Updateability,'''','''''') + '''','NULL'); + SET @Parameters += ', @AdaptiveCompression = ' + ISNULL('''' + REPLACE(@AdaptiveCompression,'''','''''') + '''','NULL'); + SET @Parameters += ', @ModificationLevel = ' + ISNULL(CAST(@ModificationLevel AS NVARCHAR),'NULL'); + SET @Parameters += ', @MinDatabaseSizeForDifferentialBackup = ' + ISNULL('''' + REPLACE(@MinDatabaseSizeForDifferentialBackup,'''','''''') + '''','NULL'); + SET @Parameters += ', @LogSizeSinceLastLogBackup = ' + ISNULL(CAST(@LogSizeSinceLastLogBackup AS NVARCHAR),'NULL'); + SET @Parameters += ', @TimeSinceLastLogBackup = ' + ISNULL(CAST(@TimeSinceLastLogBackup AS NVARCHAR),'NULL'); + SET @Parameters += ', @DataDomainBoostHost = ' + ISNULL('''' + REPLACE(@DataDomainBoostHost,'''','''''') + '''','NULL'); + SET @Parameters += ', @DataDomainBoostUser = ' + ISNULL('''' + REPLACE(@DataDomainBoostUser,'''','''''') + '''','NULL'); + SET @Parameters += ', @DataDomainBoostDevicePath = ' + ISNULL('''' + REPLACE(@DataDomainBoostDevicePath,'''','''''') + '''','NULL'); + SET @Parameters += ', @DataDomainBoostLockboxPath = ' + ISNULL('''' + REPLACE(@DataDomainBoostLockboxPath,'''','''''') + '''','NULL'); + SET @Parameters += ', @DirectoryStructure = ' + ISNULL('''' + REPLACE(@DirectoryStructure,'''','''''') + '''','NULL'); + SET @Parameters += ', @AvailabilityGroupDirectoryStructure = ' + ISNULL('''' + REPLACE(@AvailabilityGroupDirectoryStructure,'''','''''') + '''','NULL'); + SET @Parameters += ', @DirectoryStructureCase = ' + ISNULL('''' + REPLACE(@DirectoryStructureCase,'''','''''') + '''','NULL'); + SET @Parameters += ', @FileName = ' + ISNULL('''' + REPLACE(@FileName,'''','''''') + '''','NULL'); + SET @Parameters += ', @AvailabilityGroupFileName = ' + ISNULL('''' + REPLACE(@AvailabilityGroupFileName,'''','''''') + '''','NULL'); + SET @Parameters += ', @FileNameCase = ' + ISNULL('''' + REPLACE(@FileNameCase,'''','''''') + '''','NULL'); + SET @Parameters += ', @TokenTimezone = ' + ISNULL('''' + REPLACE(@TokenTimezone,'''','''''') + '''','NULL'); + SET @Parameters += ', @FileExtensionFull = ' + ISNULL('''' + REPLACE(@FileExtensionFull,'''','''''') + '''','NULL'); + SET @Parameters += ', @FileExtensionDiff = ' + ISNULL('''' + REPLACE(@FileExtensionDiff,'''','''''') + '''','NULL'); + SET @Parameters += ', @FileExtensionLog = ' + ISNULL('''' + REPLACE(@FileExtensionLog,'''','''''') + '''','NULL'); + SET @Parameters += ', @Init = ' + ISNULL('''' + REPLACE(@Init,'''','''''') + '''','NULL'); + SET @Parameters += ', @Format = ' + ISNULL('''' + REPLACE(@Format,'''','''''') + '''','NULL'); + SET @Parameters += ', @ObjectLevelRecoveryMap = ' + ISNULL('''' + REPLACE(@ObjectLevelRecoveryMap,'''','''''') + '''','NULL'); + SET @Parameters += ', @ExcludeLogShippedFromLogBackup = ' + ISNULL('''' + REPLACE(@ExcludeLogShippedFromLogBackup,'''','''''') + '''','NULL'); + SET @Parameters += ', @DirectoryCheck = ' + ISNULL('''' + REPLACE(@DirectoryCheck,'''','''''') + '''','NULL'); + SET @Parameters += ', @BackupOptions = ' + ISNULL('''' + REPLACE(@BackupOptions,'''','''''') + '''','NULL'); + SET @Parameters += ', @Stats = ' + ISNULL(CAST(@Stats AS NVARCHAR),'NULL'); + SET @Parameters += ', @ExpireDate = ' + ISNULL('''' + CONVERT(NVARCHAR, @ExpireDate, 21) + '''','NULL'); + SET @Parameters += ', @RetainDays = ' + ISNULL(CAST(@RetainDays AS NVARCHAR),'NULL'); + SET @Parameters += ', @StringDelimiter = ' + ISNULL('''' + REPLACE(@StringDelimiter,'''','''''') + '''','NULL'); + SET @Parameters += ', @DatabaseOrder = ' + ISNULL('''' + REPLACE(@DatabaseOrder,'''','''''') + '''','NULL'); + SET @Parameters += ', @DatabasesInParallel = ' + ISNULL('''' + REPLACE(@DatabasesInParallel,'''','''''') + '''','NULL'); + SET @Parameters += ', @LogToTable = ' + ISNULL('''' + REPLACE(@LogToTable,'''','''''') + '''','NULL'); + SET @Parameters += ', @Execute = ' + ISNULL('''' + REPLACE(@Execute,'''','''''') + '''','NULL'); + + SET @StartMessage = 'Date and time: ' + CONVERT(NVARCHAR,@StartTime,120); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Server: ' + CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(MAX)); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Version: ' + CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Edition: ' + CAST(SERVERPROPERTY('Edition') AS NVARCHAR(MAX)); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Platform: ' + @HostPlatform; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Procedure: ' + QUOTENAME(DB_NAME()) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Parameters: ' + @Parameters; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Version: ' + @VersionTimestamp; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Source: https://ola.hallengren.com'; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + ---------------------------------------------------------------------------------------------------- + --// Check core requirements //-- + ---------------------------------------------------------------------------------------------------- + + IF NOT (SELECT [compatibility_level] FROM [sys].[databases] WHERE [name] = DB_NAME()) >= 90 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The database ' + QUOTENAME(DB_NAME()) + ' has to be in compatibility level 90 or higher.', 16, 1; + END; + + IF NOT (SELECT [uses_ansi_nulls] FROM [sys].[sql_modules] WHERE [object_id] = @@PROCID) = 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'ANSI_NULLS has to be set to ON for the stored procedure.', 16, 1; + END; + + IF NOT (SELECT [uses_quoted_identifier] FROM [sys].[sql_modules] WHERE [object_id] = @@PROCID) = 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'QUOTED_IDENTIFIER has to be set to ON for the stored procedure.', 16, 1; + END; + + IF NOT EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'P' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'CommandExecute') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The stored procedure CommandExecute is missing. Download https://ola.hallengren.com/scripts/CommandExecute.sql.', 16, 1; + END; + + IF EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'P' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'CommandExecute' AND OBJECT_DEFINITION([objects].[object_id]) NOT LIKE '%@DatabaseContext%') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The stored procedure CommandExecute needs to be updated. Download https://ola.hallengren.com/scripts/CommandExecute.sql.', 16, 1; + END; + + IF @LogToTable = 'Y' AND NOT EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'U' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'CommandLog') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The table CommandLog is missing. Download https://ola.hallengren.com/scripts/CommandLog.sql.', 16, 1; + END; + + IF @DatabasesInParallel = 'Y' AND NOT EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'U' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'Queue') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The table Queue is missing. Download https://ola.hallengren.com/scripts/Queue.sql.', 16, 1; + END; + + IF @DatabasesInParallel = 'Y' AND NOT EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'U' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'QueueDatabase') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The table QueueDatabase is missing. Download https://ola.hallengren.com/scripts/QueueDatabase.sql.', 16, 1; + END; + + IF @@TRANCOUNT <> 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The transaction count is not 0.', 16, 1; + END; + + IF @AmazonRDS = 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The stored procedure DatabaseBackup is not supported on Amazon RDS.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Select databases //-- + ---------------------------------------------------------------------------------------------------- + + SET @Databases = REPLACE(@Databases, CHAR(10), ''); + SET @Databases = REPLACE(@Databases, CHAR(13), ''); + + WHILE CHARINDEX(@StringDelimiter + ' ', @Databases) > 0 SET @Databases = REPLACE(@Databases, @StringDelimiter + ' ', @StringDelimiter); + WHILE CHARINDEX(' ' + @StringDelimiter, @Databases) > 0 SET @Databases = REPLACE(@Databases, ' ' + @StringDelimiter, @StringDelimiter); + + SET @Databases = LTRIM(RTRIM(@Databases)); + + WITH Databases1 (StartPosition, EndPosition, DatabaseItem) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Databases, 1), 0), LEN(@Databases) + 1) AS EndPosition, + SUBSTRING(@Databases, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Databases, 1), 0), LEN(@Databases) + 1) - 1) AS DatabaseItem + WHERE @Databases IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Databases, EndPosition + 1), 0), LEN(@Databases) + 1) AS EndPosition, + SUBSTRING(@Databases, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Databases, EndPosition + 1), 0), LEN(@Databases) + 1) - EndPosition - 1) AS DatabaseItem + FROM Databases1 + WHERE EndPosition < LEN(@Databases) + 1 + ), + Databases2 (DatabaseItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN DatabaseItem LIKE '-%' THEN RIGHT(DatabaseItem,LEN(DatabaseItem) - 1) ELSE DatabaseItem END AS DatabaseItem, + StartPosition, + CASE WHEN DatabaseItem LIKE '-%' THEN 0 ELSE 1 END AS Selected + FROM Databases1 + ), + Databases3 (DatabaseItem, DatabaseType, AvailabilityGroup, StartPosition, Selected) AS + ( + SELECT CASE WHEN DatabaseItem IN('ALL_DATABASES','SYSTEM_DATABASES','USER_DATABASES','AVAILABILITY_GROUP_DATABASES') THEN '%' ELSE DatabaseItem END AS DatabaseItem, + CASE WHEN DatabaseItem = 'SYSTEM_DATABASES' THEN 'S' WHEN DatabaseItem = 'USER_DATABASES' THEN 'U' ELSE NULL END AS DatabaseType, + CASE WHEN DatabaseItem = 'AVAILABILITY_GROUP_DATABASES' THEN 1 ELSE NULL END AvailabilityGroup, + StartPosition, + Selected + FROM Databases2 + ), + Databases4 (DatabaseName, DatabaseType, AvailabilityGroup, StartPosition, Selected) AS + ( + SELECT CASE WHEN LEFT(DatabaseItem,1) = '[' AND RIGHT(DatabaseItem,1) = ']' THEN PARSENAME(DatabaseItem,1) ELSE DatabaseItem END AS DatabaseItem, + DatabaseType, + AvailabilityGroup, + StartPosition, + Selected + FROM Databases3 + ) + INSERT INTO @SelectedDatabases (DatabaseName, DatabaseType, AvailabilityGroup, StartPosition, Selected) + SELECT DatabaseName, + DatabaseType, + AvailabilityGroup, + StartPosition, + Selected + FROM Databases4 + OPTION (MAXRECURSION 0); + + IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + BEGIN + INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName, Selected) + SELECT [name] AS AvailabilityGroupName, + 0 AS Selected + FROM [sys].[availability_groups]; + + INSERT INTO @tmpDatabasesAvailabilityGroups (DatabaseName, AvailabilityGroupName) + SELECT [databases].[name], + [availability_groups].[name] + FROM [sys].[databases] [databases] + INNER JOIN [sys].[availability_replicas] [availability_replicas] ON [databases].[replica_id] = [availability_replicas].[replica_id] + INNER JOIN [sys].[availability_groups] [availability_groups] ON [availability_replicas].[group_id] = [availability_groups].[group_id]; + END; + + INSERT INTO @tmpDatabases (DatabaseName, DatabaseNameFS, DatabaseType, AvailabilityGroup, [Order], Selected, Completed) + SELECT [name] AS DatabaseName, + RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE([name],'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|','')) AS DatabaseNameFS, + CASE WHEN [name] IN('master','msdb','model') OR [is_distributor] = 1 THEN 'S' ELSE 'U' END AS DatabaseType, + NULL AS AvailabilityGroup, + 0 AS [Order], + 0 AS Selected, + 0 AS Completed + FROM [sys].[databases] + WHERE [name] <> 'tempdb' + AND [source_database_id] IS NULL + ORDER BY [name] ASC; + + UPDATE tmpDatabases + SET AvailabilityGroup = CASE WHEN EXISTS (SELECT * FROM @tmpDatabasesAvailabilityGroups WHERE DatabaseName = tmpDatabases.DatabaseName) THEN 1 ELSE 0 END + FROM @tmpDatabases tmpDatabases; + + UPDATE tmpDatabases + SET tmpDatabases.Selected = SelectedDatabases.Selected + FROM @tmpDatabases tmpDatabases + INNER JOIN @SelectedDatabases SelectedDatabases + ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + AND (tmpDatabases.DatabaseType = SelectedDatabases.DatabaseType OR SelectedDatabases.DatabaseType IS NULL) + AND (tmpDatabases.AvailabilityGroup = SelectedDatabases.AvailabilityGroup OR SelectedDatabases.AvailabilityGroup IS NULL) + WHERE SelectedDatabases.Selected = 1; + + UPDATE tmpDatabases + SET tmpDatabases.Selected = SelectedDatabases.Selected + FROM @tmpDatabases tmpDatabases + INNER JOIN @SelectedDatabases SelectedDatabases + ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + AND (tmpDatabases.DatabaseType = SelectedDatabases.DatabaseType OR SelectedDatabases.DatabaseType IS NULL) + AND (tmpDatabases.AvailabilityGroup = SelectedDatabases.AvailabilityGroup OR SelectedDatabases.AvailabilityGroup IS NULL) + WHERE SelectedDatabases.Selected = 0; + + UPDATE tmpDatabases + SET tmpDatabases.StartPosition = SelectedDatabases2.StartPosition + FROM @tmpDatabases tmpDatabases + INNER JOIN (SELECT tmpDatabases.DatabaseName, MIN(SelectedDatabases.StartPosition) AS StartPosition + FROM @tmpDatabases tmpDatabases + INNER JOIN @SelectedDatabases SelectedDatabases + ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + AND (tmpDatabases.DatabaseType = SelectedDatabases.DatabaseType OR SelectedDatabases.DatabaseType IS NULL) + AND (tmpDatabases.AvailabilityGroup = SelectedDatabases.AvailabilityGroup OR SelectedDatabases.AvailabilityGroup IS NULL) + WHERE SelectedDatabases.Selected = 1 + GROUP BY tmpDatabases.DatabaseName) SelectedDatabases2 + ON tmpDatabases.DatabaseName = SelectedDatabases2.DatabaseName; + + IF @Databases IS NOT NULL AND (NOT EXISTS(SELECT * FROM @SelectedDatabases) OR EXISTS(SELECT * FROM @SelectedDatabases WHERE DatabaseName IS NULL OR DATALENGTH(DatabaseName) = 0)) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Databases is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Select availability groups //-- + ---------------------------------------------------------------------------------------------------- + + IF @AvailabilityGroups IS NOT NULL AND @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + BEGIN + + SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, CHAR(10), ''); + SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, CHAR(13), ''); + + WHILE CHARINDEX(@StringDelimiter + ' ', @AvailabilityGroups) > 0 SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, @StringDelimiter + ' ', @StringDelimiter); + WHILE CHARINDEX(' ' + @StringDelimiter, @AvailabilityGroups) > 0 SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, ' ' + @StringDelimiter, @StringDelimiter); + + SET @AvailabilityGroups = LTRIM(RTRIM(@AvailabilityGroups)); + + WITH AvailabilityGroups1 (StartPosition, EndPosition, AvailabilityGroupItem) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @AvailabilityGroups, 1), 0), LEN(@AvailabilityGroups) + 1) AS EndPosition, + SUBSTRING(@AvailabilityGroups, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @AvailabilityGroups, 1), 0), LEN(@AvailabilityGroups) + 1) - 1) AS AvailabilityGroupItem + WHERE @AvailabilityGroups IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @AvailabilityGroups, EndPosition + 1), 0), LEN(@AvailabilityGroups) + 1) AS EndPosition, + SUBSTRING(@AvailabilityGroups, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @AvailabilityGroups, EndPosition + 1), 0), LEN(@AvailabilityGroups) + 1) - EndPosition - 1) AS AvailabilityGroupItem + FROM AvailabilityGroups1 + WHERE EndPosition < LEN(@AvailabilityGroups) + 1 + ), + AvailabilityGroups2 (AvailabilityGroupItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN AvailabilityGroupItem LIKE '-%' THEN RIGHT(AvailabilityGroupItem,LEN(AvailabilityGroupItem) - 1) ELSE AvailabilityGroupItem END AS AvailabilityGroupItem, + StartPosition, + CASE WHEN AvailabilityGroupItem LIKE '-%' THEN 0 ELSE 1 END AS Selected + FROM AvailabilityGroups1 + ), + AvailabilityGroups3 (AvailabilityGroupItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN AvailabilityGroupItem = 'ALL_AVAILABILITY_GROUPS' THEN '%' ELSE AvailabilityGroupItem END AS AvailabilityGroupItem, + StartPosition, + Selected + FROM AvailabilityGroups2 + ), + AvailabilityGroups4 (AvailabilityGroupName, StartPosition, Selected) AS + ( + SELECT CASE WHEN LEFT(AvailabilityGroupItem,1) = '[' AND RIGHT(AvailabilityGroupItem,1) = ']' THEN PARSENAME(AvailabilityGroupItem,1) ELSE AvailabilityGroupItem END AS AvailabilityGroupItem, + StartPosition, + Selected + FROM AvailabilityGroups3 + ) + INSERT INTO @SelectedAvailabilityGroups (AvailabilityGroupName, StartPosition, Selected) + SELECT AvailabilityGroupName, StartPosition, Selected + FROM AvailabilityGroups4 + OPTION (MAXRECURSION 0); + + UPDATE tmpAvailabilityGroups + SET tmpAvailabilityGroups.Selected = SelectedAvailabilityGroups.Selected + FROM @tmpAvailabilityGroups tmpAvailabilityGroups + INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + WHERE SelectedAvailabilityGroups.Selected = 1; + + UPDATE tmpAvailabilityGroups + SET tmpAvailabilityGroups.Selected = SelectedAvailabilityGroups.Selected + FROM @tmpAvailabilityGroups tmpAvailabilityGroups + INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + WHERE SelectedAvailabilityGroups.Selected = 0; + + UPDATE tmpAvailabilityGroups + SET tmpAvailabilityGroups.StartPosition = SelectedAvailabilityGroups2.StartPosition + FROM @tmpAvailabilityGroups tmpAvailabilityGroups + INNER JOIN (SELECT tmpAvailabilityGroups.AvailabilityGroupName, MIN(SelectedAvailabilityGroups.StartPosition) AS StartPosition + FROM @tmpAvailabilityGroups tmpAvailabilityGroups + INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + WHERE SelectedAvailabilityGroups.Selected = 1 + GROUP BY tmpAvailabilityGroups.AvailabilityGroupName) SelectedAvailabilityGroups2 + ON tmpAvailabilityGroups.AvailabilityGroupName = SelectedAvailabilityGroups2.AvailabilityGroupName; + + UPDATE tmpDatabases + SET tmpDatabases.StartPosition = tmpAvailabilityGroups.StartPosition, + tmpDatabases.Selected = 1 + FROM @tmpDatabases tmpDatabases + INNER JOIN @tmpDatabasesAvailabilityGroups tmpDatabasesAvailabilityGroups ON tmpDatabases.DatabaseName = tmpDatabasesAvailabilityGroups.DatabaseName + INNER JOIN @tmpAvailabilityGroups tmpAvailabilityGroups ON tmpDatabasesAvailabilityGroups.AvailabilityGroupName = tmpAvailabilityGroups.AvailabilityGroupName + WHERE tmpAvailabilityGroups.Selected = 1; + + END; + + IF @AvailabilityGroups IS NOT NULL AND (NOT EXISTS(SELECT * FROM @SelectedAvailabilityGroups) OR EXISTS(SELECT * FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName IS NULL OR AvailabilityGroupName = '') OR @Version < 11 OR SERVERPROPERTY('IsHadrEnabled') = 0) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AvailabilityGroups is not supported.', 16, 1; + END; + + IF (@Databases IS NULL AND @AvailabilityGroups IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'You need to specify one of the parameters @Databases and @AvailabilityGroups.', 16, 2; + END; + + IF (@Databases IS NOT NULL AND @AvailabilityGroups IS NOT NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'You can only specify one of the parameters @Databases and @AvailabilityGroups.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + --// Check database names //-- + ---------------------------------------------------------------------------------------------------- + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + ', ' + FROM @tmpDatabases + WHERE Selected = 1 + AND DATALENGTH(DatabaseNameFS) = 0 + ORDER BY DatabaseName ASC; + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The names of the following databases are not supported: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 16, 1; + END; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + ', ' + FROM @tmpDatabases + WHERE UPPER(DatabaseNameFS) IN(SELECT UPPER(DatabaseNameFS) FROM @tmpDatabases GROUP BY UPPER(DatabaseNameFS) HAVING COUNT(*) > 1 AND MAX(CAST(Selected AS INT)) = 1) + + AND DATALENGTH(DatabaseNameFS) > 0 + ORDER BY DatabaseName ASC + OPTION (RECOMPILE); + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The names of the following databases are not unique in the file system: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Select default directory //-- + ---------------------------------------------------------------------------------------------------- + + IF @Directory IS NULL AND @URL IS NULL AND (@BackupSoftware <> 'DATA_DOMAIN_BOOST' OR @BackupSoftware IS NULL) + BEGIN + IF @Version >= 15 + BEGIN + SET @DefaultDirectory = CAST(SERVERPROPERTY('InstanceDefaultBackupPath') AS NVARCHAR(MAX)); + END; + ELSE + BEGIN + EXECUTE [master].[dbo].xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer', N'BackupDirectory', @DefaultDirectory OUTPUT; + END; + + IF @DefaultDirectory LIKE 'http://%' OR @DefaultDirectory LIKE 'https://%' + BEGIN + SET @URL = @DefaultDirectory; + END; + ELSE + BEGIN + INSERT INTO @Directories ([Id], DirectoryPath, [mirror], Completed) + SELECT 1, @DefaultDirectory, 0, 0; + END; + END; + + ---------------------------------------------------------------------------------------------------- + --// Select directories //-- + ---------------------------------------------------------------------------------------------------- + + SET @Directory = REPLACE(@Directory, CHAR(10), ''); + SET @Directory = REPLACE(@Directory, CHAR(13), ''); + + WHILE CHARINDEX(@StringDelimiter + ' ', @Directory) > 0 SET @Directory = REPLACE(@Directory, @StringDelimiter + ' ', @StringDelimiter); + WHILE CHARINDEX(' ' + @StringDelimiter, @Directory) > 0 SET @Directory = REPLACE(@Directory, ' ' + @StringDelimiter, @StringDelimiter); + + SET @Directory = LTRIM(RTRIM(@Directory)); + + WITH Directories (StartPosition, EndPosition, Directory) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Directory, 1), 0), LEN(@Directory) + 1) AS EndPosition, + SUBSTRING(@Directory, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Directory, 1), 0), LEN(@Directory) + 1) - 1) AS Directory + WHERE @Directory IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Directory, EndPosition + 1), 0), LEN(@Directory) + 1) AS EndPosition, + SUBSTRING(@Directory, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Directory, EndPosition + 1), 0), LEN(@Directory) + 1) - EndPosition - 1) AS Directory + FROM Directories + WHERE EndPosition < LEN(@Directory) + 1 + ) + INSERT INTO @Directories ([Id], DirectoryPath, [mirror], Completed) + SELECT ROW_NUMBER() OVER(ORDER BY StartPosition ASC) AS [Id], + Directory, + 0, + 0 + FROM Directories + OPTION (MAXRECURSION 0); + + SET @MirrorDirectory = REPLACE(@MirrorDirectory, CHAR(10), ''); + SET @MirrorDirectory = REPLACE(@MirrorDirectory, CHAR(13), ''); + + WHILE CHARINDEX(', ',@MirrorDirectory) > 0 SET @MirrorDirectory = REPLACE(@MirrorDirectory,', ',','); + WHILE CHARINDEX(' ,',@MirrorDirectory) > 0 SET @MirrorDirectory = REPLACE(@MirrorDirectory,' ,',','); + + SET @MirrorDirectory = LTRIM(RTRIM(@MirrorDirectory)); + + WITH Directories (StartPosition, EndPosition, Directory) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @MirrorDirectory, 1), 0), LEN(@MirrorDirectory) + 1) AS EndPosition, + SUBSTRING(@MirrorDirectory, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @MirrorDirectory, 1), 0), LEN(@MirrorDirectory) + 1) - 1) AS Directory + WHERE @MirrorDirectory IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @MirrorDirectory, EndPosition + 1), 0), LEN(@MirrorDirectory) + 1) AS EndPosition, + SUBSTRING(@MirrorDirectory, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @MirrorDirectory, EndPosition + 1), 0), LEN(@MirrorDirectory) + 1) - EndPosition - 1) AS Directory + FROM Directories + WHERE EndPosition < LEN(@MirrorDirectory) + 1 + ) + INSERT INTO @Directories ([Id], DirectoryPath, [mirror], Completed) + SELECT (SELECT COUNT(*) FROM @Directories) + ROW_NUMBER() OVER(ORDER BY StartPosition ASC) AS [Id], + Directory, + 1, + 0 + FROM Directories + OPTION (MAXRECURSION 0); + + ---------------------------------------------------------------------------------------------------- + --// Check directories //-- + ---------------------------------------------------------------------------------------------------- + + IF EXISTS (SELECT * FROM @Directories WHERE [mirror] = 0 AND (NOT (DirectoryPath LIKE '_:' OR DirectoryPath LIKE '_:\%' OR DirectoryPath LIKE '\\%\%' OR (DirectoryPath LIKE '/%' AND @HostPlatform = 'Linux') OR DirectoryPath = 'NUL') OR DirectoryPath IS NULL OR LEFT(DirectoryPath,1) = ' ' OR RIGHT(DirectoryPath,1) = ' ')) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Directory is not supported.', 16, 1; + END; + + IF EXISTS (SELECT * FROM @Directories GROUP BY DirectoryPath HAVING COUNT(*) <> 1) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Directory is not supported.', 16, 2; + END; + + IF (SELECT COUNT(*) FROM @Directories WHERE [mirror] = 0) <> (SELECT COUNT(*) FROM @Directories WHERE [mirror] = 1) AND (SELECT COUNT(*) FROM @Directories WHERE [mirror] = 1) > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The number of directories for the parameters @Directory and @MirrorDirectory has to be the same.', 16, 3; + END; + + IF (@Directory IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 8) OR (@Directory IS NOT NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Directory is not supported.', 16, 4; + END; + + IF EXISTS (SELECT * FROM @Directories WHERE [mirror] = 0 AND DirectoryPath = 'NUL') AND EXISTS(SELECT * FROM @Directories WHERE [mirror] = 0 AND DirectoryPath <> 'NUL') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Directory is not supported.', 16, 5; + END; + + IF EXISTS (SELECT * FROM @Directories WHERE [mirror] = 0 AND DirectoryPath = 'NUL') AND EXISTS(SELECT * FROM @Directories WHERE [mirror] = 1) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'Mirrored backup is not supported when backing up to NUL', 16, 6; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS(SELECT * FROM @Directories WHERE [mirror] = 1 AND (NOT (DirectoryPath LIKE '_:' OR DirectoryPath LIKE '_:\%' OR DirectoryPath LIKE '\\%\%' OR (DirectoryPath LIKE '/%' AND @HostPlatform = 'Linux')) OR DirectoryPath IS NULL OR LEFT(DirectoryPath,1) = ' ' OR RIGHT(DirectoryPath,1) = ' ')) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorDirectory is not supported.', 16, 1; + END; + + IF EXISTS (SELECT * FROM @Directories GROUP BY DirectoryPath HAVING COUNT(*) <> 1) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorDirectory is not supported.', 16, 2; + END; + + IF @BackupSoftware IN('SQLBACKUP','SQLSAFE') AND (SELECT COUNT(*) FROM @Directories WHERE [mirror] = 1) > 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorDirectory is not supported.', 16, 4; + END; + + IF @MirrorDirectory IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 8 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorDirectory is not supported.', 16, 5; + END; + + IF @MirrorDirectory IS NOT NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorDirectory is not supported.', 16, 6; + END; + + IF (@BackupSoftware IS NULL AND EXISTS(SELECT * FROM @Directories WHERE [mirror] = 1) AND SERVERPROPERTY('EngineEdition') <> 3) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorDirectory is not supported. Mirrored backup to disk is only available in Enterprise and Developer Edition.', 16, 8; + END; + + ---------------------------------------------------------------------------------------------------- + + IF NOT EXISTS (SELECT * FROM @Errors WHERE [severity] >= 16) AND @DirectoryCheck = 'Y' + BEGIN + WHILE (1 = 1) + BEGIN + SELECT TOP 1 @CurrentRootDirectoryID = [Id], + @CurrentRootDirectoryPath = DirectoryPath + FROM @Directories + WHERE Completed = 0 + AND DirectoryPath <> 'NUL' + ORDER BY [Id] ASC; + + IF @@ROWCOUNT = 0 + BEGIN + BREAK; + END; + + IF @Version >= 14 + BEGIN + INSERT INTO @DirectoryInfo (FileExists, FileIsADirectory, ParentDirectoryExists) + SELECT file_exists, + file_is_a_directory, + parent_directory_exists + FROM [sys].dm_os_file_exists (@CurrentRootDirectoryPath); + END; + ELSE + BEGIN + INSERT INTO @DirectoryInfo (FileExists, FileIsADirectory, ParentDirectoryExists) + EXECUTE [master].[dbo].xp_fileexist @CurrentRootDirectoryPath; + END; + + IF NOT EXISTS (SELECT * FROM @DirectoryInfo WHERE FileExists = 0 AND FileIsADirectory = 1 AND ParentDirectoryExists = 1) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The directory ' + @CurrentRootDirectoryPath + ' does not exist.', 16, 1; + END; + + UPDATE @Directories + SET Completed = 1 + WHERE [Id] = @CurrentRootDirectoryID; + + SET @CurrentRootDirectoryID = NULL; + SET @CurrentRootDirectoryPath = NULL; + + DELETE FROM @DirectoryInfo; + END; + END; + + ---------------------------------------------------------------------------------------------------- + --// Select URLs //-- + ---------------------------------------------------------------------------------------------------- + + SET @URL = REPLACE(@URL, CHAR(10), ''); + SET @URL = REPLACE(@URL, CHAR(13), ''); + + WHILE CHARINDEX(@StringDelimiter + ' ', @URL) > 0 SET @URL = REPLACE(@URL, @StringDelimiter + ' ', @StringDelimiter); + WHILE CHARINDEX(' ' + @StringDelimiter, @URL) > 0 SET @URL = REPLACE(@URL, ' ' + @StringDelimiter, @StringDelimiter); + + SET @URL = LTRIM(RTRIM(@URL)); + + WITH URLs (StartPosition, EndPosition, [URL]) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @URL, 1), 0), LEN(@URL) + 1) AS EndPosition, + SUBSTRING(@URL, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @URL, 1), 0), LEN(@URL) + 1) - 1) AS [URL] + WHERE @URL IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @URL, EndPosition + 1), 0), LEN(@URL) + 1) AS EndPosition, + SUBSTRING(@URL, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @URL, EndPosition + 1), 0), LEN(@URL) + 1) - EndPosition - 1) AS [URL] + FROM URLs + WHERE EndPosition < LEN(@URL) + 1 + ) + INSERT INTO @URLs ([Id], DirectoryPath, [mirror]) + SELECT ROW_NUMBER() OVER(ORDER BY StartPosition ASC) AS [Id], + [URL], + 0 + FROM URLs + OPTION (MAXRECURSION 0); + + SET @MirrorURL = REPLACE(@MirrorURL, CHAR(10), ''); + SET @MirrorURL = REPLACE(@MirrorURL, CHAR(13), ''); + + WHILE CHARINDEX(@StringDelimiter + ' ', @MirrorURL) > 0 SET @MirrorURL = REPLACE(@MirrorURL, @StringDelimiter + ' ', @StringDelimiter); + WHILE CHARINDEX(' ' + @StringDelimiter ,@MirrorURL) > 0 SET @MirrorURL = REPLACE(@MirrorURL, ' ' + @StringDelimiter, @StringDelimiter); + + SET @MirrorURL = LTRIM(RTRIM(@MirrorURL)); + + WITH URLs (StartPosition, EndPosition, [URL]) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @MirrorURL, 1), 0), LEN(@MirrorURL) + 1) AS EndPosition, + SUBSTRING(@MirrorURL, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @MirrorURL, 1), 0), LEN(@MirrorURL) + 1) - 1) AS [URL] + WHERE @MirrorURL IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @MirrorURL, EndPosition + 1), 0), LEN(@MirrorURL) + 1) AS EndPosition, + SUBSTRING(@MirrorURL, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @MirrorURL, EndPosition + 1), 0), LEN(@MirrorURL) + 1) - EndPosition - 1) AS [URL] + FROM URLs + WHERE EndPosition < LEN(@MirrorURL) + 1 + ) + INSERT INTO @URLs ([Id], DirectoryPath, [mirror]) + SELECT (SELECT COUNT(*) FROM @URLs) + ROW_NUMBER() OVER(ORDER BY StartPosition ASC) AS [Id], + [URL], + 1 + FROM URLs + OPTION (MAXRECURSION 0); + + ---------------------------------------------------------------------------------------------------- + --// Check URLs //-- + ---------------------------------------------------------------------------------------------------- + + IF EXISTS(SELECT * FROM @URLs WHERE [mirror] = 0 AND NOT (DirectoryPath LIKE 'https://%/%' OR DirectoryPath LIKE 's3://%/%')) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @URL is not supported.', 16, 1; + END; + + IF EXISTS (SELECT * FROM @URLs GROUP BY DirectoryPath HAVING COUNT(*) <> 1) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @URL is not supported.', 16, 2; + END; + + IF (SELECT COUNT(*) FROM @URLs WHERE [mirror] = 0) <> (SELECT COUNT(*) FROM @URLs WHERE [mirror] = 1) AND (SELECT COUNT(*) FROM @URLs WHERE [mirror] = 1) > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @URL is not supported.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS(SELECT * FROM @URLs WHERE [mirror] = 1 AND NOT (DirectoryPath LIKE 'https://%/%' OR DirectoryPath LIKE 's3://%/%')) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 1; + END; + + IF EXISTS (SELECT * FROM @URLs GROUP BY DirectoryPath HAVING COUNT(*) <> 1) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 2; + END; + + IF (SELECT COUNT(*) FROM @URLs WHERE [mirror] = 0) <> (SELECT COUNT(*) FROM @URLs WHERE [mirror] = 1) AND (SELECT COUNT(*) FROM @URLs WHERE [mirror] = 1) > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + --// Get directory separator //-- + ---------------------------------------------------------------------------------------------------- + + SELECT @DirectorySeparator = CASE + WHEN @URL IS NOT NULL THEN '/' + WHEN @HostPlatform = 'Windows' THEN '\' + WHEN @HostPlatform = 'Linux' THEN '/' + END; + + UPDATE @Directories + SET DirectoryPath = LEFT(DirectoryPath,LEN(DirectoryPath) - 1) + WHERE RIGHT(DirectoryPath,1) = @DirectorySeparator; + + UPDATE @URLs + SET DirectoryPath = LEFT(DirectoryPath,LEN(DirectoryPath) - 1) + WHERE RIGHT(DirectoryPath,1) = @DirectorySeparator; + + ---------------------------------------------------------------------------------------------------- + --// Get file extension //-- + ---------------------------------------------------------------------------------------------------- + + IF @FileExtensionFull IS NULL + BEGIN + SELECT @FileExtensionFull = CASE + WHEN @BackupSoftware IS NULL THEN 'bak' + WHEN @BackupSoftware = 'LITESPEED' THEN 'bak' + WHEN @BackupSoftware = 'SQLBACKUP' THEN 'sqb' + WHEN @BackupSoftware = 'SQLSAFE' THEN 'safe' + END; + END; + + IF @FileExtensionDiff IS NULL + BEGIN + SELECT @FileExtensionDiff = CASE + WHEN @BackupSoftware IS NULL THEN 'bak' + WHEN @BackupSoftware = 'LITESPEED' THEN 'bak' + WHEN @BackupSoftware = 'SQLBACKUP' THEN 'sqb' + WHEN @BackupSoftware = 'SQLSAFE' THEN 'safe' + END; + END; + + IF @FileExtensionLog IS NULL + BEGIN + SELECT @FileExtensionLog = CASE + WHEN @BackupSoftware IS NULL THEN 'trn' + WHEN @BackupSoftware = 'LITESPEED' THEN 'trn' + WHEN @BackupSoftware = 'SQLBACKUP' THEN 'sqb' + WHEN @BackupSoftware = 'SQLSAFE' THEN 'safe' + END; + END; + + ---------------------------------------------------------------------------------------------------- + --// Get default checksum //-- + ---------------------------------------------------------------------------------------------------- + + IF @Checksum IS NULL + BEGIN + SELECT @Checksum = CASE WHEN EXISTS(SELECT * FROM [sys].[configurations] WHERE [name] = 'backup checksum default' AND [value_in_use] = 1) THEN 'Y' + WHEN NOT EXISTS(SELECT * FROM [sys].[configurations] WHERE [name] = 'backup checksum default' AND [value_in_use] = 1) THEN 'N' END; + END; + + ---------------------------------------------------------------------------------------------------- + --// Get default compression //-- + ---------------------------------------------------------------------------------------------------- + + IF @Compress IS NULL + BEGIN + SELECT @Compress = CASE WHEN @BackupSoftware IS NULL AND EXISTS(SELECT * FROM [sys].[configurations] WHERE [name] = 'backup compression default' AND [value_in_use] = 1) THEN 'Y' + WHEN @BackupSoftware IS NULL AND NOT EXISTS(SELECT * FROM [sys].[configurations] WHERE [name] = 'backup compression default' AND [value_in_use] = 1) THEN 'N' + WHEN @BackupSoftware IS NOT NULL AND (@CompressionLevelNumeric IS NULL OR @CompressionLevelNumeric > 0) THEN 'Y' + WHEN @BackupSoftware IS NOT NULL AND @CompressionLevelNumeric = 0 THEN 'N' END; + END; + + ---------------------------------------------------------------------------------------------------- + --// Get default compression algorithm //-- + ---------------------------------------------------------------------------------------------------- + + IF @CompressionAlgorithm IS NULL AND @BackupSoftware IS NULL AND @Version >= 16 + BEGIN + SELECT @CompressionAlgorithm = CASE WHEN @BackupSoftware IS NULL AND EXISTS(SELECT * FROM [sys].[configurations] WHERE [name] = 'backup compression algorithm' AND [value_in_use] IN (0, 1)) THEN 'MS_XPRESS' + WHEN @BackupSoftware IS NULL AND EXISTS(SELECT * FROM [sys].[configurations] WHERE [name] = 'backup compression algorithm' AND [value_in_use] = 2) THEN 'QAT_DEFLATE' + WHEN @BackupSoftware IS NULL AND EXISTS(SELECT * FROM [sys].[configurations] WHERE [name] = 'backup compression algorithm' AND [value_in_use] = 3) THEN 'ZSTD' END; + END; + + ---------------------------------------------------------------------------------------------------- + --// Get default compression level //-- + ---------------------------------------------------------------------------------------------------- + + IF @CompressionLevel IS NULL AND @BackupSoftware IS NULL AND @Version >= 17 + BEGIN + SET @CompressionLevel = 'LOW'; + END; + + ---------------------------------------------------------------------------------------------------- + --// Check input parameters //-- + ---------------------------------------------------------------------------------------------------- + + IF @BackupType NOT IN ('FULL','DIFF','LOG') OR @BackupType IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @BackupType is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF SERVERPROPERTY('EngineEdition') = 8 AND NOT (@BackupType = 'FULL' AND @CopyOnly = 'Y') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'SQL Database Managed Instance only supports COPY_ONLY full backups.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Verify NOT IN ('Y','N') OR @Verify IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Verify is not supported.', 16, 1; + END; + + IF @BackupSoftware = 'SQLSAFE' AND @Encrypt = 'Y' AND @Verify = 'Y' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Verify is not supported. Verify is not supported with encrypted backups with Idera SQL Safe Backup', 16, 2; + END; + + IF @Verify = 'Y' AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Verify is not supported. Verify is not supported with Data Domain Boost', 16, 3; + END; + + IF @Verify = 'Y' AND EXISTS(SELECT * FROM @Directories WHERE DirectoryPath = 'NUL') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Verify is not supported. Verify is not supported when backing up to NUL.', 16, 4; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @CleanupTime < 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CleanupTime is not supported.', 16, 1; + END; + + IF @CleanupTime IS NOT NULL AND @URL IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CleanupTime is not supported. Cleanup is not supported on Azure Blob Storage.', 16, 2; + END; + + IF @CleanupTime IS NOT NULL AND EXISTS(SELECT * FROM @Directories WHERE DirectoryPath = 'NUL') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CleanupTime is not supported. Cleanup is not supported when backing up to NUL.', 16, 4; + END; + + IF @CleanupTime IS NOT NULL AND ((@DirectoryStructure NOT LIKE '%{DatabaseName}%' OR @DirectoryStructure IS NULL) OR (SERVERPROPERTY('IsHadrEnabled') = 1 AND (@AvailabilityGroupDirectoryStructure NOT LIKE '%{DatabaseName}%' OR @AvailabilityGroupDirectoryStructure IS NULL))) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CleanupTime is not supported. Cleanup is not supported if the token {DatabaseName} is not part of the directory.', 16, 5; + END; + + IF @CleanupTime IS NOT NULL AND ((@DirectoryStructure NOT LIKE '%{BackupType}%' OR @DirectoryStructure IS NULL) OR (SERVERPROPERTY('IsHadrEnabled') = 1 AND (@AvailabilityGroupDirectoryStructure NOT LIKE '%{BackupType}%' OR @AvailabilityGroupDirectoryStructure IS NULL))) AND (SELECT COUNT(*) FROM (SELECT @FileExtensionFull AS FileExtension UNION SELECT @FileExtensionDiff UNION SELECT @FileExtensionLog) FileExtension) <> 3 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CleanupTime is not supported. Cleanup is not supported if the token {BackupType} is not part of the directory and the file extensions are not unique.', 16, 6; + END; + + IF @CleanupTime IS NOT NULL AND @CopyOnly = 'Y' AND ((@DirectoryStructure NOT LIKE '%{CopyOnly}%' OR @DirectoryStructure IS NULL) OR (SERVERPROPERTY('IsHadrEnabled') = 1 AND (@AvailabilityGroupDirectoryStructure NOT LIKE '%{CopyOnly}%' OR @AvailabilityGroupDirectoryStructure IS NULL))) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CleanupTime is not supported. Cleanup is not supported if the token {CopyOnly} is not part of the directory.', 16, 7; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @CleanupMode NOT IN('BEFORE_BACKUP','AFTER_BACKUP') OR @CleanupMode IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CleanupMode is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Compress NOT IN ('Y','N') OR @Compress IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Compress is not supported.', 16, 1; + END; + + IF @Compress = 'Y' AND @BackupSoftware IS NULL AND NOT ((@Version >= 10 AND @Version < 10.5 AND SERVERPROPERTY('EngineEdition') = 3) OR (@Version >= 10.5 AND (SERVERPROPERTY('EngineEdition') IN (3, 8) OR SERVERPROPERTY('EditionID') IN (-1534726760, 284895786)))) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Compress is not supported. Backup compression is not supported in this version and edition of SQL Server.', 16, 2; + END; + + IF @Compress = 'N' AND @BackupSoftware IN ('LITESPEED','SQLBACKUP','SQLSAFE') AND (@CompressionLevelNumeric IS NULL OR @CompressionLevelNumeric >= 1) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Compress is not supported.', 16, 3; + END; + + IF @Compress = 'Y' AND @BackupSoftware IN ('LITESPEED','SQLBACKUP','SQLSAFE') AND @CompressionLevelNumeric = 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Compress is not supported.', 16, 4; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @CompressionAlgorithm NOT IN ('MS_XPRESS','QAT_DEFLATE','ZSTD') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CompressionAlgorithm is not supported. The allowed values are MS_XPRESS, QAT_DEFLATE and ZSTD.', 16, 1; + END; + + IF @CompressionAlgorithm IS NOT NULL AND NOT (@Version >= 16) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CompressionAlgorithm is not supported. Specifying the compression algorithm is only supported in SQL Server 2022 and later.', 16, 2; + END; + + IF @CompressionAlgorithm = 'QAT_DEFLATE' AND NOT (SERVERPROPERTY('EngineEdition') IN(2, 3)) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CompressionAlgorithm is not supported. Setting the compression algorithm to QAT_DEFLATE is only supported in Standard and Enterprise Edition.', 16, 3; + END; + + IF @CompressionAlgorithm = 'ZSTD' AND NOT (@Version >= 17) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CompressionAlgorithm is not supported. Setting the compression algorithm to ZSTD is only supported in SQL Server 2025 and later.', 16, 4; + END; + + IF @CompressionAlgorithm IS NOT NULL AND @BackupSoftware IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CompressionAlgorithm is not supported. Setting the compression algorithm is only supported with SQL Server native backup', 16, 5; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @CompressionLevel IS NOT NULL AND @BackupSoftware IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CompressionLevel is not supported. For third-party backup software, use the parameter @CompressionLevelNumeric.', 16, 1; + END; + + IF @CompressionLevel NOT IN ('LOW','MEDIUM','HIGH') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CompressionLevel is not supported. The supported values are LOW, MEDIUM and HIGH.', 16, 2; + END; + + IF @CompressionLevel IS NOT NULL AND NOT (@Version >= 17) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CompressionLevel is not supported. Setting the compression level is only supported in SQL Server 2025 and later.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @CopyOnly NOT IN ('Y','N') OR @CopyOnly IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CopyOnly is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @ChangeBackupType NOT IN ('Y','N') OR @ChangeBackupType IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ChangeBackupType is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @BackupSoftware NOT IN ('LITESPEED','SQLBACKUP','SQLSAFE','DATA_DOMAIN_BOOST') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @BackupSoftware is not supported.', 16, 1; + END; + + IF @BackupSoftware IS NOT NULL AND @HostPlatform = 'Linux' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @BackupSoftware is not supported. Only native backups are supported on Linux', 16, 2; + END; + + IF @BackupSoftware = 'LITESPEED' AND NOT EXISTS (SELECT * FROM [master].[sys].[objects] WHERE [type] = 'X' AND [name] = 'xp_backup_database') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'LiteSpeed for SQL Server is not installed. Download https://www.quest.com/products/litespeed-for-sql-server/.', 16, 3; + END; + + IF @BackupSoftware = 'SQLBACKUP' AND NOT EXISTS (SELECT * FROM [master].[sys].[objects] WHERE [type] = 'X' AND [name] = 'sqlbackup') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'Red Gate SQL Backup Pro is not installed. Download https://www.red-gate.com/products/dba/sql-backup/.', 16, 4; + END; + + IF @BackupSoftware = 'SQLSAFE' AND NOT EXISTS (SELECT * FROM [master].[sys].[objects] WHERE [type] = 'X' AND [name] = 'xp_ss_backup') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'Idera SQL Safe Backup is not installed. Download https://www.idera.com/productssolutions/sqlserver/sqlsafebackup.', 16, 5; + END; + + IF @BackupSoftware = 'DATA_DOMAIN_BOOST' AND NOT EXISTS (SELECT * FROM [master].[sys].[objects] WHERE [type] = 'PC' AND [name] = 'emc_run_backup') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'EMC Data Domain Boost is not installed. Download https://www.emc.com/en-us/data-protection/data-domain.htm.', 16, 6; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Checksum NOT IN ('Y','N') OR @Checksum IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Checksum is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @BlockSize NOT IN (512,1024,2048,4096,8192,16384,32768,65536) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @BlockSize is not supported.', 16, 1; + END; + + IF @BlockSize IS NOT NULL AND @BackupSoftware = 'SQLBACKUP' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @BlockSize is not supported. This parameter is not supported with Redgate SQL Backup Pro', 16, 2; + END; + + IF @BlockSize IS NOT NULL AND @BackupSoftware = 'SQLSAFE' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @BlockSize is not supported. This parameter is not supported with Idera SQL Safe', 16, 3; + END; + + IF @BlockSize IS NOT NULL AND @URL IS NOT NULL AND @Credential IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'BLOCKSIZE is not supported when backing up to URL with page blobs. See https://docs.microsoft.com/en-us/sql/relational-databases/backup-restore/sql-server-backup-to-url', 16, 4; + END; + + IF @BlockSize IS NOT NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @BlockSize is not supported. This parameter is not supported with Data Domain Boost', 16, 5; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @BufferCount <= 0 OR @BufferCount > 2147483647 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @BufferCount is not supported.', 16, 1; + END; + + IF @BufferCount IS NOT NULL AND @BackupSoftware = 'SQLBACKUP' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @BufferCount is not supported.', 16, 2; + END; + + IF @BufferCount IS NOT NULL AND @BackupSoftware = 'SQLSAFE' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @BufferCount is not supported.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @MaxTransferSize < 65536 OR @MaxTransferSize > 20971520 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MaxTransferSize is not supported.', 16, 1; + END; + + IF @MaxTransferSize > 1048576 AND @BackupSoftware = 'SQLBACKUP' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MaxTransferSize is not supported.', 16, 2; + END; + + IF @MaxTransferSize IS NOT NULL AND @BackupSoftware = 'SQLSAFE' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MaxTransferSize is not supported.', 16, 3; + END; + + IF @MaxTransferSize IS NOT NULL AND @URL IS NOT NULL AND @Credential IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'MAXTRANSFERSIZE is not supported when backing up to URL with page blobs. See https://docs.microsoft.com/en-us/sql/relational-databases/backup-restore/sql-server-backup-to-url', 16, 4; + END; + + IF @MaxTransferSize IS NOT NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MaxTransferSize is not supported.', 16, 5; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @NumberOfFiles < 1 OR @NumberOfFiles > 64 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NumberOfFiles is not supported.', 16, 1; + END; + + IF @NumberOfFiles > 32 AND @BackupSoftware = 'SQLBACKUP' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NumberOfFiles is not supported.', 16, 2; + END; + + IF @NumberOfFiles < (SELECT COUNT(*) FROM @Directories WHERE [mirror] = 0) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NumberOfFiles is not supported.', 16, 3; + END; + + IF @NumberOfFiles % (SELECT NULLIF(COUNT(*),0) FROM @Directories WHERE [mirror] = 0) > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NumberOfFiles is not supported.', 16, 4; + END; + + IF @URL IS NOT NULL AND @Credential IS NOT NULL AND @NumberOfFiles <> 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'Backup striping to URL with page blobs is not supported. See https://docs.microsoft.com/en-us/sql/relational-databases/backup-restore/sql-server-backup-to-url', 16, 5; + END; + + IF @NumberOfFiles > 1 AND @BackupSoftware IN('SQLBACKUP','SQLSAFE') AND EXISTS(SELECT * FROM @Directories WHERE [mirror] = 1) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NumberOfFiles is not supported.', 16, 6; + END; + + IF @NumberOfFiles > 32 AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NumberOfFiles is not supported.', 16, 7; + END; + + IF @NumberOfFiles < (SELECT COUNT(*) FROM @URLs WHERE [mirror] = 0) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NumberOfFiles is not supported.', 16, 8; + END; + + IF @NumberOfFiles % (SELECT NULLIF(COUNT(*),0) FROM @URLs WHERE [mirror] = 0) > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NumberOfFiles is not supported.', 16, 9; + END; + + IF @NumberOfFiles > 32 AND @URL LIKE 's3%' AND @MirrorURL LIKE 's3%' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NumberOfFiles is not supported. The maximum number of files when performing mirrored backups to S3 storage is 32.', 16, 10; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @MinBackupSizeForMultipleFiles <= 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MinBackupSizeForMultipleFiles is not supported.', 16, 1; + END; + + IF @MinBackupSizeForMultipleFiles IS NOT NULL AND @NumberOfFiles IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MinBackupSizeForMultipleFiles is not supported. This parameter can only be used together with @NumberOfFiles.', 16, 2; + END; + + IF @MinBackupSizeForMultipleFiles IS NOT NULL AND @BackupType = 'DIFF' AND NOT EXISTS(SELECT * FROM [sys].[all_columns] WHERE OBJECT_ID = OBJECT_ID('sys.dm_db_file_space_usage') AND [name] = 'modified_extent_page_count') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MinBackupSizeForMultipleFiles is not supported. The column sys.dm_db_file_space_usage.modified_extent_page_count is not available in this version of SQL Server.', 16, 3; + END; + + IF @MinBackupSizeForMultipleFiles IS NOT NULL AND @BackupType = 'LOG' AND NOT EXISTS(SELECT * FROM [sys].[all_columns] WHERE OBJECT_ID = OBJECT_ID('sys.dm_db_log_stats') AND [name] = 'log_since_last_log_backup_mb') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MinBackupSizeForMultipleFiles is not supported. The column sys.dm_db_log_stats.log_since_last_log_backup_mb is not available in this version of SQL Server.', 16, 4; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @MaxFileSize <= 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MaxFileSize is not supported.', 16, 1; + END; + + IF @MaxFileSize IS NOT NULL AND @NumberOfFiles IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The parameters @MaxFileSize and @NumberOfFiles cannot be used together.', 16, 2; + END; + + IF @MaxFileSize IS NOT NULL AND @BackupType = 'DIFF' AND NOT EXISTS(SELECT * FROM [sys].[all_columns] WHERE OBJECT_ID = OBJECT_ID('sys.dm_db_file_space_usage') AND [name] = 'modified_extent_page_count') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MaxFileSize is not supported. The column sys.dm_db_file_space_usage.modified_extent_page_count is not available in this version of SQL Server.', 16, 3; + END; + + IF @MaxFileSize IS NOT NULL AND @BackupType = 'LOG' AND NOT EXISTS(SELECT * FROM [sys].[all_columns] WHERE OBJECT_ID = OBJECT_ID('sys.dm_db_log_stats') AND [name] = 'log_since_last_log_backup_mb') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MaxFileSize is not supported. The column sys.dm_db_log_stats.log_since_last_log_backup_mb is not available in this version of SQL Server.', 16, 4; + END; + + ---------------------------------------------------------------------------------------------------- + + IF (@BackupSoftware IS NULL AND @CompressionLevelNumeric IS NOT NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CompressionLevelNumeric is not supported.', 16, 1; + END; + + IF @BackupSoftware = 'LITESPEED' AND (@CompressionLevelNumeric < 0 OR @CompressionLevelNumeric > 8) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CompressionLevelNumeric is not supported.', 16, 2; + END; + + IF @BackupSoftware = 'SQLBACKUP' AND (@CompressionLevelNumeric < 0 OR @CompressionLevelNumeric > 4) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CompressionLevelNumeric is not supported.', 16, 3; + END; + + IF @BackupSoftware = 'SQLSAFE' AND (@CompressionLevelNumeric < 1 OR @CompressionLevelNumeric > 4) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CompressionLevelNumeric is not supported.', 16, 4; + END; + + IF @CompressionLevelNumeric IS NOT NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CompressionLevelNumeric is not supported.', 16, 5; + END; + + ---------------------------------------------------------------------------------------------------- + + IF LEN(@Description) > 255 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Description is not supported.', 16, 1; + END; + + IF @BackupSoftware = 'LITESPEED' AND LEN(@Description) > 128 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Description is not supported.', 16, 2; + END; + + IF @BackupSoftware = 'DATA_DOMAIN_BOOST' AND LEN(@Description) > 254 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Description is not supported.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + + IF LEN(@BackupSetName) > 128 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @BackupSetName is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Threads IS NOT NULL AND (@BackupSoftware NOT IN('LITESPEED','SQLBACKUP','SQLSAFE') OR @BackupSoftware IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Threads is not supported.', 16, 1; + END; + + IF @BackupSoftware = 'LITESPEED' AND (@Threads < 1 OR @Threads > 32) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Threads is not supported.', 16, 2; + END; + + IF @BackupSoftware = 'SQLBACKUP' AND (@Threads < 2 OR @Threads > 32) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Threads is not supported.', 16, 3; + END; + + IF @BackupSoftware = 'SQLSAFE' AND (@Threads < 1 OR @Threads > 64) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Threads is not supported.', 16, 4; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Throttle < 1 OR @Throttle > 100 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Throttle is not supported.', 16, 1; + END; + + IF @Throttle IS NOT NULL AND (@BackupSoftware NOT IN('LITESPEED') OR @BackupSoftware IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Throttle is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Encrypt NOT IN('Y','N') OR @Encrypt IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Encrypt is not supported.', 16, 1; + END; + + IF @Encrypt = 'Y' AND @BackupSoftware IS NULL AND NOT (@Version >= 12 AND (SERVERPROPERTY('EngineEdition') IN(3, 8) OR SERVERPROPERTY('EditionID') IN(-1534726760, 284895786))) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Encrypt is not supported.', 16, 2; + END; + + IF @Encrypt = 'Y' AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Encrypt is not supported.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @BackupSoftware IS NULL AND @Encrypt = 'Y' AND (@EncryptionAlgorithm NOT IN('AES_128','AES_192','AES_256','TRIPLE_DES_3KEY') OR @EncryptionAlgorithm IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @EncryptionAlgorithm is not supported.', 16, 1; + END; + + IF @BackupSoftware = 'LITESPEED' AND @Encrypt = 'Y' AND (@EncryptionAlgorithm NOT IN('RC2_40','RC2_56','RC2_112','RC2_128','TRIPLE_DES_3KEY','RC4_128','AES_128','AES_192','AES_256') OR @EncryptionAlgorithm IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @EncryptionAlgorithm is not supported.', 16, 2; + END; + + IF @BackupSoftware = 'SQLBACKUP' AND @Encrypt = 'Y' AND (@EncryptionAlgorithm NOT IN('AES_128','AES_256') OR @EncryptionAlgorithm IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @EncryptionAlgorithm is not supported.', 16, 3; + END; + + IF @BackupSoftware = 'SQLSAFE' AND @Encrypt = 'Y' AND (@EncryptionAlgorithm NOT IN('AES_128','AES_256') OR @EncryptionAlgorithm IS NULL) + OR (@EncryptionAlgorithm IS NOT NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @EncryptionAlgorithm is not supported.', 16, 4; + END; + + IF @EncryptionAlgorithm IS NOT NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @EncryptionAlgorithm is not supported.', 16, 5; + END; + + ---------------------------------------------------------------------------------------------------- + + IF (NOT (@BackupSoftware IS NULL AND @Encrypt = 'Y') AND @ServerCertificate IS NOT NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ServerCertificate is not supported.', 16, 1; + END; + + IF @BackupSoftware IS NULL AND @Encrypt = 'Y' AND @ServerCertificate IS NULL AND @ServerAsymmetricKey IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ServerCertificate is not supported.', 16, 2; + END; + + IF @BackupSoftware IS NULL AND @Encrypt = 'Y' AND @ServerCertificate IS NOT NULL AND @ServerAsymmetricKey IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ServerCertificate is not supported.', 16, 3; + END; + + IF @ServerCertificate IS NOT NULL AND NOT EXISTS(SELECT * FROM master.[sys].[certificates] WHERE [name] = @ServerCertificate) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ServerCertificate is not supported.', 16, 4; + END; + + ---------------------------------------------------------------------------------------------------- + + IF NOT (@BackupSoftware IS NULL AND @Encrypt = 'Y') AND @ServerAsymmetricKey IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ServerAsymmetricKey is not supported.', 16, 1; + END; + + IF @BackupSoftware IS NULL AND @Encrypt = 'Y' AND @ServerAsymmetricKey IS NULL AND @ServerCertificate IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ServerAsymmetricKey is not supported.', 16, 2; + END; + + IF @BackupSoftware IS NULL AND @Encrypt = 'Y' AND @ServerAsymmetricKey IS NOT NULL AND @ServerCertificate IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ServerAsymmetricKey is not supported.', 16, 3; + END; + + IF @ServerAsymmetricKey IS NOT NULL AND NOT EXISTS(SELECT * FROM master.[sys].[asymmetric_keys] WHERE [name] = @ServerAsymmetricKey) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ServerAsymmetricKey is not supported.', 16, 4; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @EncryptionKey IS NOT NULL AND @BackupSoftware IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @EncryptionKey is not supported.', 16, 1; + END; + + IF @EncryptionKey IS NOT NULL AND @Encrypt = 'N' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @EncryptionKey is not supported.', 16, 2; + END; + + IF @EncryptionKey IS NULL AND @Encrypt = 'Y' AND @BackupSoftware IN('LITESPEED','SQLBACKUP','SQLSAFE') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @EncryptionKey is not supported.', 16, 3; + END; + + IF @EncryptionKey IS NOT NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @EncryptionKey is not supported.', 16, 4; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @ReadWriteFileGroups NOT IN('Y','N') OR @ReadWriteFileGroups IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ReadWriteFileGroups is not supported.', 16, 1; + END; + + IF @ReadWriteFileGroups = 'Y' AND @BackupType = 'LOG' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ReadWriteFileGroups is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @OverrideBackupPreference NOT IN('Y','N') OR @OverrideBackupPreference IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @OverrideBackupPreference is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @NoRecovery NOT IN('Y','N') OR @NoRecovery IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NoRecovery is not supported.', 16, 1; + END; + + IF @NoRecovery = 'Y' AND @BackupType <> 'LOG' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NoRecovery is not supported.', 16, 2; + END; + + IF @NoRecovery = 'Y' AND @BackupSoftware = 'SQLSAFE' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NoRecovery is not supported.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @URL IS NOT NULL AND @Directory IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @URL is not supported.', 16, 1; + END; + + IF @URL IS NOT NULL AND @MirrorDirectory IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @URL is not supported.', 16, 2; + END; + + IF @URL IS NOT NULL AND @Version < 11.03339 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @URL is not supported.', 16, 3; + END; + + IF @URL IS NOT NULL AND @BackupSoftware IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @URL is not supported.', 16, 4; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Credential IS NULL AND @URL IS NOT NULL AND NOT (@Version >= 13 OR SERVERPROPERTY('EngineEdition') = 8) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Credential is not supported.', 16, 1; + END; + + IF @Credential IS NOT NULL AND @URL IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Credential is not supported.', 16, 2; + END; + + IF @URL IS NOT NULL AND @Credential IS NULL AND NOT EXISTS(SELECT * FROM [sys].[credentials] WHERE UPPER([credential_identity]) IN('SHARED ACCESS SIGNATURE','MANAGED IDENTITY','S3 ACCESS KEY')) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Credential is not supported.', 16, 3; + END; + + IF @Credential IS NOT NULL AND NOT EXISTS(SELECT * FROM [sys].[credentials] WHERE [name] = @Credential) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Credential is not supported.', 16, 4; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @MirrorCleanupTime < 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorCleanupTime is not supported.', 16, 1; + END; + + IF @MirrorCleanupTime IS NOT NULL AND @MirrorDirectory IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorCleanupTime is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @MirrorCleanupMode NOT IN('BEFORE_BACKUP','AFTER_BACKUP') OR @MirrorCleanupMode IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorCleanupMode is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @MirrorURL IS NOT NULL AND @Directory IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 1; + END; + + IF @MirrorURL IS NOT NULL AND @MirrorDirectory IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 2; + END; + + IF @MirrorURL IS NOT NULL AND @Version < 11.03339 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 3; + END; + + IF @MirrorURL IS NOT NULL AND @BackupSoftware IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 4; + END; + + IF @MirrorURL IS NOT NULL AND @URL IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 5; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Updateability NOT IN('READ_ONLY','READ_WRITE','ALL') OR @Updateability IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Updateability is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @AdaptiveCompression NOT IN('SIZE','SPEED') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AdaptiveCompression is not supported.', 16, 1; + END; + + IF @AdaptiveCompression IS NOT NULL AND (@BackupSoftware NOT IN('LITESPEED') OR @BackupSoftware IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AdaptiveCompression is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @ModificationLevel IS NOT NULL AND NOT EXISTS(SELECT * FROM [sys].[all_columns] WHERE OBJECT_ID = OBJECT_ID('sys.dm_db_file_space_usage') AND [name] = 'modified_extent_page_count') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ModificationLevel is not supported.', 16, 1; + END; + + IF @ModificationLevel <= 0 OR @ModificationLevel > 100 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ModificationLevel is not supported.', 16, 2; + END; + + IF @ModificationLevel IS NOT NULL AND @ChangeBackupType = 'N' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The parameter @ModificationLevel can only be used together with @ChangeBackupType = ''Y''.', 16, 3; + END; + + IF @ModificationLevel IS NOT NULL AND @BackupType <> 'DIFF' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The parameter @ModificationLevel can only be used for differential backups.', 16, 4; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @MinDatabaseSizeForDifferentialBackup <= 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MinDatabaseSizeForDifferentialBackup is not supported.', 16, 1; + END; + + IF @MinDatabaseSizeForDifferentialBackup IS NOT NULL AND @BackupType <> 'DIFF' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The parameter @MinDatabaseSizeForDifferentialBackup can only be used for differential backups.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @LogSizeSinceLastLogBackup IS NOT NULL AND NOT EXISTS(SELECT * FROM [sys].[all_columns] WHERE OBJECT_ID = OBJECT_ID('sys.dm_db_log_stats') AND [name] = 'log_since_last_log_backup_mb') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @LogSizeSinceLastLogBackup is not supported.', 16, 1; + END; + + IF @LogSizeSinceLastLogBackup IS NOT NULL AND @BackupType <> 'LOG' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @LogSizeSinceLastLogBackup is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @TimeSinceLastLogBackup IS NOT NULL AND NOT EXISTS(SELECT * FROM [sys].[all_columns] WHERE OBJECT_ID = OBJECT_ID('sys.dm_db_log_stats') AND [name] = 'log_backup_time') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @TimeSinceLastLogBackup is not supported.', 16, 1; + END; + + IF @TimeSinceLastLogBackup IS NOT NULL AND @BackupType <> 'LOG' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @TimeSinceLastLogBackup is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF (@TimeSinceLastLogBackup IS NOT NULL AND @LogSizeSinceLastLogBackup IS NULL) OR (@TimeSinceLastLogBackup IS NULL AND @LogSizeSinceLastLogBackup IS NOT NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The parameters @TimeSinceLastLogBackup and @LogSizeSinceLastLogBackup can only be used together.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DataDomainBoostHost IS NOT NULL AND (@BackupSoftware <> 'DATA_DOMAIN_BOOST' OR @BackupSoftware IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DataDomainBoostHost is not supported.', 16, 1; + END; + + IF @DataDomainBoostHost IS NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DataDomainBoostHost is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DataDomainBoostUser IS NOT NULL AND (@BackupSoftware <> 'DATA_DOMAIN_BOOST' OR @BackupSoftware IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DataDomainBoostUser is not supported.', 16, 1; + END; + + IF @DataDomainBoostUser IS NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DataDomainBoostUser is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DataDomainBoostDevicePath IS NOT NULL AND (@BackupSoftware <> 'DATA_DOMAIN_BOOST' OR @BackupSoftware IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DataDomainBoostDevicePath is not supported.', 16, 1; + END; + + IF @DataDomainBoostDevicePath IS NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DataDomainBoostDevicePath is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DataDomainBoostLockboxPath IS NOT NULL AND (@BackupSoftware <> 'DATA_DOMAIN_BOOST' OR @BackupSoftware IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DataDomainBoostLockboxPath is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DirectoryStructure = '' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DirectoryStructure is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @AvailabilityGroupDirectoryStructure = '' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AvailabilityGroupDirectoryStructure is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DirectoryStructureCase NOT IN('LOWER','UPPER') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DirectoryStructureCase is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @FileName IS NULL OR @FileName = '' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FileName is not supported.', 16, 1; + END; + + IF @FileName NOT LIKE '%.{FileExtension}' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FileName is not supported.', 16, 2; + END; + + IF (@NumberOfFiles > 1 AND @FileName NOT LIKE '%{FileNumber}%') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FileName is not supported.', 16, 3; + END; + + IF @FileName LIKE '%{DirectorySeparator}%' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FileName is not supported.', 16, 4; + END; + + IF @FileName LIKE '%/%' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FileName is not supported.', 16, 5; + END; + + IF @FileName LIKE '%\%' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FileName is not supported.', 16, 6; + END; + + ---------------------------------------------------------------------------------------------------- + + IF (SERVERPROPERTY('IsHadrEnabled') = 1 AND @AvailabilityGroupFileName IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 1; + END; + + IF @AvailabilityGroupFileName = '' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 2; + END; + + IF @AvailabilityGroupFileName NOT LIKE '%.{FileExtension}' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 3; + END; + + IF (@NumberOfFiles > 1 AND @AvailabilityGroupFileName NOT LIKE '%{FileNumber}%') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 4; + END; + + IF @AvailabilityGroupFileName LIKE '%{DirectorySeparator}%' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 5; + END; + + IF @AvailabilityGroupFileName LIKE '%/%' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 6; + END; + + IF @AvailabilityGroupFileName LIKE '%\%' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 7; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @FileNameCase NOT IN('LOWER','UPPER') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FileNameCase is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS (SELECT * FROM (SELECT REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@DirectoryStructure,'{DirectorySeparator}',''),'{ServerName}',''),'{InstanceName}',''),'{ServiceName}',''),'{ClusterName}',''),'{AvailabilityGroupName}',''),'{DatabaseName}',''),'{BackupType}',''),'{Partial}',''),'{CopyOnly}',''),'{Description}',''),'{BackupSetName}',''),'{Year}',''),'{Month}',''),'{Day}',''),'{Week}',''),'{Weekday}',''),'{Hour}',''),'{Minute}',''),'{Second}',''),'{Millisecond}',''),'{Microsecond}',''),'{MajorVersion}',''),'{MinorVersion}','') AS DirectoryStructure) Temp WHERE DirectoryStructure LIKE '%{%' OR DirectoryStructure LIKE '%}%') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The parameter @DirectoryStructure contains one or more tokens that are not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS (SELECT * FROM (SELECT REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@AvailabilityGroupDirectoryStructure,'{DirectorySeparator}',''),'{ServerName}',''),'{InstanceName}',''),'{ServiceName}',''),'{ClusterName}',''),'{AvailabilityGroupName}',''),'{DatabaseName}',''),'{BackupType}',''),'{Partial}',''),'{CopyOnly}',''),'{Description}',''),'{BackupSetName}',''),'{Year}',''),'{Month}',''),'{Day}',''),'{Week}',''),'{Weekday}',''),'{Hour}',''),'{Minute}',''),'{Second}',''),'{Millisecond}',''),'{Microsecond}',''),'{MajorVersion}',''),'{MinorVersion}','') AS AvailabilityGroupDirectoryStructure) Temp WHERE AvailabilityGroupDirectoryStructure LIKE '%{%' OR AvailabilityGroupDirectoryStructure LIKE '%}%') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The parameter @AvailabilityGroupDirectoryStructure contains one or more tokens that are not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS (SELECT * FROM (SELECT REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@FileName,'{DirectorySeparator}',''),'{ServerName}',''),'{InstanceName}',''),'{ServiceName}',''),'{ClusterName}',''),'{AvailabilityGroupName}',''),'{DatabaseName}',''),'{BackupType}',''),'{Partial}',''),'{CopyOnly}',''),'{Description}',''),'{BackupSetName}',''),'{Year}',''),'{Month}',''),'{Day}',''),'{Week}',''),'{Weekday}',''),'{Hour}',''),'{Minute}',''),'{Second}',''),'{Millisecond}',''),'{Microsecond}',''),'{FileNumber}',''),'{NumberOfFiles}',''),'{FileExtension}',''),'{MajorVersion}',''),'{MinorVersion}','') AS [FileName]) Temp WHERE [FileName] LIKE '%{%' OR [FileName] LIKE '%}%') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The parameter @FileName contains one or more tokens that are not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS (SELECT * FROM (SELECT REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@AvailabilityGroupFileName,'{DirectorySeparator}',''),'{ServerName}',''),'{InstanceName}',''),'{ServiceName}',''),'{ClusterName}',''),'{AvailabilityGroupName}',''),'{DatabaseName}',''),'{BackupType}',''),'{Partial}',''),'{CopyOnly}',''),'{Description}',''),'{BackupSetName}',''),'{Year}',''),'{Month}',''),'{Day}',''),'{Week}',''),'{Weekday}',''),'{Hour}',''),'{Minute}',''),'{Second}',''),'{Millisecond}',''),'{Microsecond}',''),'{FileNumber}',''),'{NumberOfFiles}',''),'{FileExtension}',''),'{MajorVersion}',''),'{MinorVersion}','') AS AvailabilityGroupFileName) Temp WHERE AvailabilityGroupFileName LIKE '%{%' OR AvailabilityGroupFileName LIKE '%}%') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The parameter @AvailabilityGroupFileName contains one or more tokens that are not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @TokenTimezone NOT IN('LOCAL','UTC') OR @TokenTimezone IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @TokenTimezone is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @FileExtensionFull LIKE '%.%' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FileExtensionFull is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @FileExtensionDiff LIKE '%.%' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FileExtensionDiff is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @FileExtensionLog LIKE '%.%' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FileExtensionLog is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Init NOT IN('Y','N') OR @Init IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Init is not supported.', 16, 1; + END; + + IF @Init = 'Y' AND @BackupType = 'LOG' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Init is not supported.', 16, 2; + END; + + IF @Init = 'Y' AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Init is not supported.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Format NOT IN('Y','N') OR @Format IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Format is not supported.', 16, 1; + END; + + IF @Format = 'Y' AND @BackupType = 'LOG' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Format is not supported.', 16, 2; + END; + + IF @Format = 'Y' AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Format is not supported.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @ObjectLevelRecoveryMap NOT IN('Y','N') OR @ObjectLevelRecoveryMap IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ObjectLevelRecovery is not supported.', 16, 1; + END; + + IF @ObjectLevelRecoveryMap = 'Y' AND @BackupSoftware IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ObjectLevelRecovery is not supported.', 16, 2; + END; + + IF @ObjectLevelRecoveryMap = 'Y' AND @BackupSoftware <> 'LITESPEED' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ObjectLevelRecovery is not supported.', 16, 3; + END; + + IF @ObjectLevelRecoveryMap = 'Y' AND @BackupType = 'LOG' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ObjectLevelRecovery is not supported.', 16, 4; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @ExcludeLogShippedFromLogBackup NOT IN('Y','N') OR @ExcludeLogShippedFromLogBackup IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ExcludeLogShippedFromLogBackup is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DirectoryCheck NOT IN('Y','N') OR @DirectoryCheck IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DirectoryCheck is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @BackupOptions IS NOT NULL AND @URL IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @BackupOptions is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Stats <= 0 OR @Stats > 100 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Stats is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @ExpireDate IS NOT NULL AND @BackupSoftware <> 'LITESPEED' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ExpireDate is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @RetainDays < 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @RetainDays is not supported.', 16, 1; + END; + + IF @RetainDays IS NOT NULL AND @BackupSoftware <> 'LITESPEED' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @RetainDays is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) > 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @StringDelimiter is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DatabaseOrder NOT IN('DATABASE_NAME_ASC','DATABASE_NAME_DESC','DATABASE_SIZE_ASC','DATABASE_SIZE_DESC','LOG_SIZE_SINCE_LAST_LOG_BACKUP_ASC','LOG_SIZE_SINCE_LAST_LOG_BACKUP_DESC') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabaseOrder is not supported.', 16, 1; + END; + + IF @DatabaseOrder IN('LOG_SIZE_SINCE_LAST_LOG_BACKUP_ASC','LOG_SIZE_SINCE_LAST_LOG_BACKUP_DESC') AND NOT EXISTS(SELECT * FROM [sys].[all_columns] WHERE OBJECT_ID = OBJECT_ID('sys.dm_db_log_stats') AND [name] = 'log_since_last_log_backup_mb') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabaseOrder is not supported. The column sys.dm_db_log_stats.log_since_last_log_backup_mb is not available in this version of SQL Server.', 16, 2; + END; + + IF @DatabaseOrder IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 5 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabaseOrder is not supported.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DatabasesInParallel NOT IN('Y','N') OR @DatabasesInParallel IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabasesInParallel is not supported.', 16, 1; + END; + + IF @DatabasesInParallel = 'Y' AND SERVERPROPERTY('EngineEdition') = 5 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabasesInParallel is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @LogToTable NOT IN('Y','N') OR @LogToTable IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @LogToTable is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Execute NOT IN('Y','N') OR @Execute IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Execute is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS(SELECT * FROM @Errors) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The documentation is available at https://ola.hallengren.com/sql-server-backup.html.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Check that selected databases and availability groups exist //-- + ---------------------------------------------------------------------------------------------------- + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + ', ' + FROM @SelectedDatabases + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases); + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The following databases in the @Databases parameter do not exist: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 10, 1; + END; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(AvailabilityGroupName) + ', ' + FROM @SelectedAvailabilityGroups + WHERE AvailabilityGroupName NOT LIKE '%[%]%' + AND AvailabilityGroupName NOT IN (SELECT AvailabilityGroupName FROM @tmpAvailabilityGroups); + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The following availability groups do not exist: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 10, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Check @@SERVERNAME //-- + ---------------------------------------------------------------------------------------------------- + + IF UPPER(@@SERVERNAME) <> UPPER(CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(MAX))) AND SERVERPROPERTY('IsHadrEnabled') = 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The @@SERVERNAME does not match SERVERPROPERTY(''ServerName''). See ' + CASE WHEN SERVERPROPERTY('IsClustered') = 0 THEN 'https://docs.microsoft.com/en-us/sql/database-engine/install-windows/rename-a-computer-that-hosts-a-stand-alone-instance-of-sql-server' WHEN SERVERPROPERTY('IsClustered') = 1 THEN 'https://docs.microsoft.com/en-us/sql/sql-server/failover-clusters/install/rename-a-sql-server-failover-cluster-instance' END + '.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Raise errors //-- + ---------------------------------------------------------------------------------------------------- + + DECLARE ErrorCursor CURSOR FAST_FORWARD FOR SELECT [Message], [severity], [State] FROM @Errors ORDER BY [ID] ASC; + + OPEN ErrorCursor; + + FETCH ErrorCursor INTO @CurrentMessage, @CurrentSeverity, @CurrentState; + + WHILE @@FETCH_STATUS = 0 + BEGIN + RAISERROR('%s', @CurrentSeverity, @CurrentState, @CurrentMessage) WITH NOWAIT; + RAISERROR(@EmptyLine, 10, 1) WITH NOWAIT; + + FETCH NEXT FROM ErrorCursor INTO @CurrentMessage, @CurrentSeverity, @CurrentState; + END; + + CLOSE ErrorCursor; + + DEALLOCATE ErrorCursor; + + IF EXISTS (SELECT * FROM @Errors WHERE [severity] >= 16) + BEGIN + SET @ReturnCode = 50000; + GOTO Logging; + END; + + ---------------------------------------------------------------------------------------------------- + --// Check Availability Group cluster name //-- + ---------------------------------------------------------------------------------------------------- + + IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + BEGIN + SELECT @Cluster = NULLIF([cluster_name],'') + FROM [sys].[dm_hadr_cluster]; + END; + + ---------------------------------------------------------------------------------------------------- + --// Update database order //-- + ---------------------------------------------------------------------------------------------------- + + IF @DatabaseOrder IN('DATABASE_SIZE_ASC','DATABASE_SIZE_DESC') + BEGIN + UPDATE tmpDatabases + SET DatabaseSize = (SELECT SUM(CAST(SIZE AS BIGINT)) FROM [sys].[master_files] WHERE [type] = 0 AND [database_id] = DB_ID(tmpDatabases.DatabaseName)) + FROM @tmpDatabases tmpDatabases; + END; + + IF @DatabaseOrder IN('LOG_SIZE_SINCE_LAST_LOG_BACKUP_ASC','LOG_SIZE_SINCE_LAST_LOG_BACKUP_DESC') + BEGIN + UPDATE tmpDatabases + SET LogSizeSinceLastLogBackup = (SELECT log_since_last_log_backup_mb FROM [sys].dm_db_log_stats(DB_ID(tmpDatabases.DatabaseName))) + FROM @tmpDatabases tmpDatabases; + END; + + IF @DatabaseOrder IS NULL + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY StartPosition ASC, DatabaseName ASC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_NAME_ASC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY DatabaseName ASC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_NAME_DESC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY DatabaseName DESC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_SIZE_ASC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY DatabaseSize ASC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_SIZE_DESC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY DatabaseSize DESC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'LOG_SIZE_SINCE_LAST_LOG_BACKUP_ASC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY LogSizeSinceLastLogBackup ASC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'LOG_SIZE_SINCE_LAST_LOG_BACKUP_DESC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY LogSizeSinceLastLogBackup DESC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + + ---------------------------------------------------------------------------------------------------- + --// Update the queue //-- + ---------------------------------------------------------------------------------------------------- + + IF @DatabasesInParallel = 'Y' + BEGIN + + BEGIN TRY + + SELECT @QueueID = QueueID + FROM [dbo].[Queue] + WHERE SchemaName = @SchemaName + AND ObjectName = @ObjectName + AND [Parameters] = @Parameters; + + IF @QueueID IS NULL + BEGIN + BEGIN TRANSACTION; + + SELECT @QueueID = QueueID + FROM [dbo].[Queue] WITH (UPDLOCK, HOLDLOCK) + WHERE SchemaName = @SchemaName + AND ObjectName = @ObjectName + AND [Parameters] = @Parameters; + + IF @QueueID IS NULL + BEGIN + INSERT INTO [dbo].[Queue] (SchemaName, ObjectName, [Parameters]) + SELECT @SchemaName, @ObjectName, @Parameters; + + SET @QueueID = SCOPE_IDENTITY(); + END; + + COMMIT TRANSACTION; + END; + + BEGIN TRANSACTION; + + UPDATE [Queue] + SET QueueStartTime = SYSDATETIME(), + SessionID = @@SPID, + RequestID = (SELECT [request_id] FROM [sys].[dm_exec_requests] WHERE [session_id] = @@SPID), + RequestStartTime = (SELECT [start_time] FROM [sys].[dm_exec_requests] WHERE [session_id] = @@SPID) + FROM [dbo].[Queue] [Queue] + WHERE QueueID = @QueueID + AND NOT EXISTS (SELECT * + FROM [sys].[dm_exec_requests] + WHERE [session_id] = [Queue].SessionID + AND [request_id] = [Queue].RequestID + AND [start_time] = [Queue].RequestStartTime) + AND NOT EXISTS (SELECT * + FROM [dbo].QueueDatabase QueueDatabase + INNER JOIN [sys].[dm_exec_requests] ON QueueDatabase.SessionID = [session_id] AND QueueDatabase.RequestID = [request_id] AND QueueDatabase.RequestStartTime = [start_time] + WHERE QueueDatabase.QueueID = @QueueID); + + IF @@ROWCOUNT = 1 + BEGIN + INSERT INTO [dbo].QueueDatabase (QueueID, DatabaseName) + SELECT @QueueID AS QueueID, + DatabaseName + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + AND NOT EXISTS (SELECT * FROM [dbo].QueueDatabase WHERE DatabaseName = tmpDatabases.DatabaseName AND QueueID = @QueueID); + + DELETE QueueDatabase + FROM [dbo].QueueDatabase QueueDatabase + WHERE QueueID = @QueueID + AND NOT EXISTS (SELECT * FROM @tmpDatabases tmpDatabases WHERE DatabaseName = QueueDatabase.DatabaseName AND Selected = 1); + + UPDATE QueueDatabase + SET DatabaseOrder = tmpDatabases.[Order] + FROM [dbo].QueueDatabase QueueDatabase + INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName = tmpDatabases.DatabaseName + WHERE QueueID = @QueueID; + END; + + COMMIT TRANSACTION; + + SELECT @QueueStartTime = QueueStartTime + FROM [dbo].[Queue] + WHERE QueueID = @QueueID; + + END TRY + + BEGIN CATCH + IF XACT_STATE() <> 0 + BEGIN + ROLLBACK TRANSACTION; + END; + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS NVARCHAR) + ', ' + ISNULL(ERROR_MESSAGE(),''); + RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + SET @ReturnCode = ERROR_NUMBER(); + GOTO Logging; + END CATCH; + + END; + + ---------------------------------------------------------------------------------------------------- + --// Execute backup commands //-- + ---------------------------------------------------------------------------------------------------- + + WHILE (1 = 1) + BEGIN -- Start of database loop + + IF @DatabasesInParallel = 'Y' + BEGIN + UPDATE QueueDatabase + SET DatabaseStartTime = NULL, + SessionID = NULL, + RequestID = NULL, + RequestStartTime = NULL + FROM [dbo].QueueDatabase QueueDatabase + WHERE QueueID = @QueueID + AND DatabaseStartTime IS NOT NULL + AND DatabaseEndTime IS NULL + AND NOT EXISTS (SELECT * FROM [sys].[dm_exec_requests] WHERE [session_id] = QueueDatabase.SessionID AND [request_id] = QueueDatabase.RequestID AND [start_time] = QueueDatabase.RequestStartTime); + + UPDATE QueueDatabase + SET DatabaseStartTime = SYSDATETIME(), + DatabaseEndTime = NULL, + SessionID = @@SPID, + RequestID = (SELECT [request_id] FROM [sys].[dm_exec_requests] WHERE [session_id] = @@SPID), + RequestStartTime = (SELECT [start_time] FROM [sys].[dm_exec_requests] WHERE [session_id] = @@SPID), + @CurrentDatabaseName = DatabaseName, + @CurrentDatabaseNameFS = (SELECT DatabaseNameFS FROM @tmpDatabases WHERE DatabaseName = QueueDatabase.DatabaseName) + FROM (SELECT TOP 1 DatabaseStartTime, + DatabaseEndTime, + SessionID, + RequestID, + RequestStartTime, + DatabaseName + FROM [dbo].QueueDatabase + WHERE QueueID = @QueueID + AND (DatabaseStartTime < @QueueStartTime OR DatabaseStartTime IS NULL) + AND NOT (DatabaseStartTime IS NOT NULL AND DatabaseEndTime IS NULL) + ORDER BY DatabaseOrder ASC + ) QueueDatabase; + END; + ELSE + BEGIN + SELECT TOP 1 @CurrentDBID = [Id], + @CurrentDatabaseName = DatabaseName, + @CurrentDatabaseNameFS = DatabaseNameFS + FROM @tmpDatabases + WHERE Selected = 1 + AND Completed = 0 + ORDER BY [Order] ASC; + END; + + IF @@ROWCOUNT = 0 + BEGIN + BREAK; + END; + + SET @CurrentDatabase_sp_executesql = QUOTENAME(@CurrentDatabaseName) + '.sys.sp_executesql'; + + BEGIN + SET @DatabaseMessage = 'Date and time: ' + CONVERT(NVARCHAR,SYSDATETIME(),120); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Database: ' + QUOTENAME(@CurrentDatabaseName); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + SELECT @CurrentUserAccess = [user_access_desc], + @CurrentIsReadOnly = [is_read_only], + @CurrentDatabaseState = [state_desc], + @CurrentInStandby = [is_in_standby], + @CurrentRecoveryModel = [recovery_model_desc], + @CurrentIsEncrypted = [is_encrypted], + @CurrentDatabaseSize = (SELECT SUM(CAST(SIZE AS BIGINT)) FROM [sys].[master_files] WHERE [type] = 0 AND [database_id] = [sys].[databases].[database_id]) + FROM [sys].[databases] + WHERE [name] = @CurrentDatabaseName; + + BEGIN + SET @DatabaseMessage = 'State: ' + @CurrentDatabaseState; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Standby: ' + CASE WHEN @CurrentInStandby = 1 THEN 'Yes' ELSE 'No' END; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Updateability: ' + CASE WHEN @CurrentIsReadOnly = 1 THEN 'READ_ONLY' WHEN @CurrentIsReadOnly = 0 THEN 'READ_WRITE' END; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'User access: ' + @CurrentUserAccess; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Recovery model: ' + @CurrentRecoveryModel; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Encrypted: ' + CASE WHEN @CurrentIsEncrypted = 1 THEN 'Yes' WHEN @CurrentIsEncrypted = 0 THEN 'No' ELSE 'N/A' END; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + SELECT @CurrentMaxTransferSize = CASE + WHEN @MaxTransferSize IS NOT NULL THEN @MaxTransferSize + WHEN @MaxTransferSize IS NULL AND @Compress = 'Y' AND @CurrentIsEncrypted = 1 AND @BackupSoftware IS NULL AND (@Version >= 13 AND @Version < 15.0404316) AND @Credential IS NULL THEN 65537 + END; + + IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + BEGIN + SELECT @CurrentReplicaID = [databases].[replica_id] + FROM [sys].[databases] [databases] + INNER JOIN [sys].[availability_replicas] [availability_replicas] ON [databases].[replica_id] = [availability_replicas].[replica_id] + WHERE [databases].[name] = @CurrentDatabaseName; + + SELECT @CurrentAvailabilityGroupID = [group_id] + FROM [sys].[availability_replicas] + WHERE [replica_id] = @CurrentReplicaID; + + SELECT @CurrentAvailabilityGroupRole = [role_desc] + FROM [sys].[dm_hadr_availability_replica_states] + WHERE [replica_id] = @CurrentReplicaID; + + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = [synchronization_state_desc], + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = [synchronization_health_desc] + FROM [sys].[dm_hadr_database_replica_states] + WHERE [replica_id] = @CurrentReplicaID + AND [database_id] = DB_ID(@CurrentDatabaseName); + + SELECT @CurrentAvailabilityGroup = [name], + @CurrentAvailabilityGroupBackupPreference = UPPER([automated_backup_preference_desc]) + FROM [sys].[availability_groups] + WHERE [group_id] = @CurrentAvailabilityGroupID; + END; + + IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 AND @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SELECT @CurrentIsPreferredBackupReplica = [sys].fn_hadr_backup_is_preferred_replica(@CurrentDatabaseName); + END; + + SELECT @CurrentDifferentialBaseLSN = [differential_base_lsn] + FROM [sys].[master_files] + WHERE [database_id] = DB_ID(@CurrentDatabaseName) + AND [type] = 0 + AND [file_id] = 1; + + IF @CurrentDatabaseState = 'ONLINE' AND NOT (@CurrentInStandby = 1) + BEGIN + SELECT @CurrentLogLSN = [last_log_backup_lsn] + FROM [sys].[database_recovery_status] + WHERE [database_id] = DB_ID(@CurrentDatabaseName); + END; + + IF @CurrentDatabaseState = 'ONLINE' AND NOT (@CurrentInStandby = 1) + AND EXISTS(SELECT * FROM [sys].[all_columns] WHERE OBJECT_ID = OBJECT_ID('sys.dm_db_file_space_usage') AND [name] = 'modified_extent_page_count') + AND (@CurrentAvailabilityGroupRole = 'PRIMARY' OR @CurrentAvailabilityGroupRole IS NULL) + AND (@BackupType IN('DIFF','FULL') OR (@ChangeBackupType = 'Y' AND @CurrentBackupType = 'LOG' AND @CurrentRecoveryModel IN('FULL','BULK_LOGGED') AND @CurrentLogLSN IS NULL AND @CurrentDatabaseName <> 'master')) + AND (@ModificationLevel IS NOT NULL OR @MinBackupSizeForMultipleFiles IS NOT NULL OR @MaxFileSize IS NOT NULL OR @MinDatabaseSizeForDifferentialBackup IS NOT NULL) + BEGIN + SET @CurrentCommand = 'SELECT @ParamAllocatedExtentPageCount = SUM(allocated_extent_page_count), @ParamModifiedExtentPageCount = SUM(modified_extent_page_count) FROM sys.dm_db_file_space_usage'; + + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamAllocatedExtentPageCount bigint OUTPUT, @ParamModifiedExtentPageCount bigint OUTPUT', @ParamAllocatedExtentPageCount = @CurrentAllocatedExtentPageCount OUTPUT, @ParamModifiedExtentPageCount = @CurrentModifiedExtentPageCount OUTPUT; + END; + + SET @CurrentBackupType = @BackupType; + + IF @ChangeBackupType = 'Y' + BEGIN + IF @CurrentBackupType = 'LOG' AND @CurrentRecoveryModel IN('FULL','BULK_LOGGED') AND @CurrentLogLSN IS NULL AND @CurrentDatabaseName <> 'master' + BEGIN + SET @CurrentBackupType = 'DIFF'; + END; + IF @CurrentBackupType = 'DIFF' AND (@CurrentDatabaseName = 'master' OR @CurrentDifferentialBaseLSN IS NULL OR (@CurrentModifiedExtentPageCount * 1. / @CurrentAllocatedExtentPageCount * 100 >= @ModificationLevel) OR (COALESCE(CAST(@CurrentAllocatedExtentPageCount AS BIGINT) * 8192, CAST(@CurrentDatabaseSize AS BIGINT) * 8192) < CAST(@MinDatabaseSizeForDifferentialBackup AS BIGINT) * 1024 * 1024)) + BEGIN + SET @CurrentBackupType = 'FULL'; + END; + END; + + IF @CurrentDatabaseState = 'ONLINE' AND NOT (@CurrentInStandby = 1) + AND EXISTS(SELECT * FROM [sys].[all_columns] WHERE OBJECT_ID = OBJECT_ID('sys.dm_db_log_stats') AND [name] = 'log_since_last_log_backup_mb') + BEGIN + SELECT @CurrentLastLogBackup = log_backup_time, + @CurrentLogSizeSinceLastLogBackup = log_since_last_log_backup_mb + FROM [sys].dm_db_log_stats (DB_ID(@CurrentDatabaseName)); + END; + + IF @CurrentBackupType = 'DIFF' + BEGIN + SELECT @CurrentDifferentialBaseIsSnapshot = [is_snapshot] + FROM msdb.[dbo].[backupset] + WHERE [database_name] = @CurrentDatabaseName + AND [type] = 'D' + AND [checkpoint_lsn] = @CurrentDifferentialBaseLSN; + END; + + IF @ChangeBackupType = 'Y' + BEGIN + IF @CurrentBackupType = 'DIFF' AND @CurrentDifferentialBaseIsSnapshot = 1 + BEGIN + SET @CurrentBackupType = 'FULL'; + END; + END; + + WITH CurrentDatabase AS + ( + SELECT BackupSize = CASE WHEN @CurrentBackupType = 'FULL' THEN COALESCE(CAST(@CurrentAllocatedExtentPageCount AS BIGINT) * 8192, CAST(@CurrentDatabaseSize AS BIGINT) * 8192) + WHEN @CurrentBackupType = 'DIFF' THEN CAST(@CurrentModifiedExtentPageCount AS BIGINT) * 8192 + WHEN @CurrentBackupType = 'LOG' THEN CAST(@CurrentLogSizeSinceLastLogBackup * 1024 * 1024 AS BIGINT) + END, + MaxNumberOfFiles = CASE WHEN @BackupSoftware IN('SQLBACKUP','DATA_DOMAIN_BOOST') THEN 32 ELSE 64 END, + CASE WHEN (SELECT COUNT(*) FROM @Directories WHERE [mirror] = 0) > 0 THEN (SELECT COUNT(*) FROM @Directories WHERE [mirror] = 0) ELSE (SELECT COUNT(*) FROM @URLs WHERE [mirror] = 0) END AS NumberOfDirectories, + CAST(@MinBackupSizeForMultipleFiles AS BIGINT) * 1024 * 1024 AS MinBackupSizeForMultipleFiles, + CAST(@MaxFileSize AS BIGINT) * 1024 * 1024 AS MaxFileSize + ) + SELECT @CurrentNumberOfFiles = CASE WHEN @NumberOfFiles IS NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST' THEN 1 + WHEN @NumberOfFiles IS NULL AND @MaxFileSize IS NULL THEN NumberOfDirectories + WHEN @NumberOfFiles = 1 THEN @NumberOfFiles + WHEN @NumberOfFiles > 1 AND (BackupSize >= MinBackupSizeForMultipleFiles OR MinBackupSizeForMultipleFiles IS NULL OR BackupSize IS NULL) THEN @NumberOfFiles + WHEN @NumberOfFiles > 1 AND (BackupSize < MinBackupSizeForMultipleFiles) THEN NumberOfDirectories + WHEN @NumberOfFiles IS NULL AND @MaxFileSize IS NOT NULL AND (BackupSize IS NULL OR BackupSize = 0) THEN NumberOfDirectories + WHEN @NumberOfFiles IS NULL AND @MaxFileSize IS NOT NULL THEN (SELECT MIN(NumberOfFilesInEachDirectory) + FROM (SELECT ((BackupSize / NumberOfDirectories) / MaxFileSize + CASE WHEN (BackupSize / NumberOfDirectories) % MaxFileSize = 0 THEN 0 ELSE 1 END) AS NumberOfFilesInEachDirectory + UNION + SELECT MaxNumberOfFiles / NumberOfDirectories) Files) * NumberOfDirectories + END + + FROM CurrentDatabase; + + SELECT @CurrentDatabaseMirroringRole = UPPER([mirroring_role_desc]) + FROM [sys].[database_mirroring] [database_mirroring] + INNER JOIN [sys].[databases] [databases] ON [database_mirroring].[database_id] = [databases].[database_id] + WHERE [databases].[name] = @CurrentDatabaseName; + + IF EXISTS (SELECT * FROM msdb.[dbo].[log_shipping_primary_databases] WHERE [primary_database] = @CurrentDatabaseName) + BEGIN + SET @CurrentLogShippingRole = 'PRIMARY'; + END; + ELSE + IF EXISTS (SELECT * FROM msdb.[dbo].[log_shipping_secondary_databases] WHERE [secondary_database] = @CurrentDatabaseName) + BEGIN + SET @CurrentLogShippingRole = 'SECONDARY'; + END; + + IF @CurrentAvailabilityGroup IS NOT NULL + BEGIN + IF ((@CurrentBackupType = 'FULL' AND @CopyOnly = 'N' AND @Version >= 17) + OR (@CurrentBackupType = 'DIFF' AND @CopyOnly = 'N' AND @Version >= 17) + OR (@CurrentBackupType = 'FULL' AND @CopyOnly = 'Y') + OR (@CurrentBackupType = 'LOG' AND @CopyOnly = 'N')) + BEGIN + SET @CurrentBackupOperationSupportedOnSecondaryReplicas = 1; + END; + ELSE + BEGIN + SET @CurrentBackupOperationSupportedOnSecondaryReplicas = 0; + END; + END; + + IF @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Availability group: ' + ISNULL(@CurrentAvailabilityGroup,'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Availability group role: ' + ISNULL(@CurrentAvailabilityGroupRole,'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Availability group database replica synchronization state: ' + ISNULL(@CurrentAvailabilityGroupDatabaseReplicaSynchronizationState,'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Availability group database replica synchronization health: ' + ISNULL(@CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth,'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Availability group backup preference: ' + ISNULL(@CurrentAvailabilityGroupBackupPreference,'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Is preferred backup replica: ' + CASE WHEN @CurrentIsPreferredBackupReplica = 1 THEN 'Yes' WHEN @CurrentIsPreferredBackupReplica = 0 THEN 'No' ELSE 'N/A' END; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + IF @CurrentAvailabilityGroupBackupPreference IN('SECONDARY', 'SECONDARY_ONLY') + BEGIN + SET @DatabaseMessage = 'Is backup operation supported on secondary replicas: ' + CASE WHEN @CurrentBackupOperationSupportedOnSecondaryReplicas = 1 THEN 'Yes' WHEN @CurrentBackupOperationSupportedOnSecondaryReplicas = 0 THEN 'No' ELSE 'N/A' END; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + END; + + IF @CurrentDatabaseMirroringRole IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Database mirroring role: ' + @CurrentDatabaseMirroringRole; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + IF @CurrentLogShippingRole IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Log shipping role: ' + @CurrentLogShippingRole; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + SET @DatabaseMessage = 'Differential base LSN: ' + ISNULL(CAST(@CurrentDifferentialBaseLSN AS NVARCHAR),'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + IF @CurrentBackupType = 'DIFF' OR @CurrentDifferentialBaseIsSnapshot IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Differential base is snapshot: ' + CASE WHEN @CurrentDifferentialBaseIsSnapshot = 1 THEN 'Yes' WHEN @CurrentDifferentialBaseIsSnapshot = 0 THEN 'No' ELSE 'N/A' END; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + SET @DatabaseMessage = 'Last log backup LSN: ' + ISNULL(CAST(@CurrentLogLSN AS NVARCHAR),'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + IF @CurrentBackupType IN('DIFF','FULL') AND EXISTS(SELECT * FROM [sys].[all_columns] WHERE OBJECT_ID = OBJECT_ID('sys.dm_db_file_space_usage') AND [name] = 'modified_extent_page_count') + BEGIN + SET @DatabaseMessage = 'Allocated extent page count: ' + ISNULL(CAST(@CurrentAllocatedExtentPageCount AS NVARCHAR) + ' (' + CAST(@CurrentAllocatedExtentPageCount * 1. * 8 / 1024 AS NVARCHAR) + ' MB)','N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Modified extent page count: ' + ISNULL(CAST(@CurrentModifiedExtentPageCount AS NVARCHAR) + ' (' + CAST(@CurrentModifiedExtentPageCount * 1. * 8 / 1024 AS NVARCHAR) + ' MB)','N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + IF @CurrentBackupType = 'LOG' AND EXISTS(SELECT * FROM [sys].[all_columns] WHERE OBJECT_ID = OBJECT_ID('sys.dm_db_log_stats') AND [name] = 'log_since_last_log_backup_mb') + BEGIN + SET @DatabaseMessage = 'Last log backup: ' + ISNULL(CONVERT(NVARCHAR(19),NULLIF(@CurrentLastLogBackup,'1900-01-01'),120),'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Log size since last log backup (MB): ' + ISNULL(CAST(@CurrentLogSizeSinceLastLogBackup AS NVARCHAR),'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + IF @CurrentDatabaseState = 'ONLINE' + AND NOT (@CurrentUserAccess = 'SINGLE_USER') + AND NOT (@CurrentInStandby = 1) + AND NOT (@CurrentBackupType = 'LOG' AND @CurrentRecoveryModel = 'SIMPLE') + AND NOT (@CurrentBackupType = 'LOG' AND @CurrentRecoveryModel IN('FULL','BULK_LOGGED') AND @CurrentLogLSN IS NULL) + AND NOT (@CurrentBackupType = 'DIFF' AND @CurrentDifferentialBaseLSN IS NULL) + AND NOT (@CurrentBackupType IN('DIFF','LOG') AND @CurrentDatabaseName = 'master') + AND NOT (@CurrentAvailabilityGroup IS NOT NULL AND @CurrentBackupOperationSupportedOnSecondaryReplicas = 0 AND (@CurrentAvailabilityGroupRole <> 'PRIMARY' OR @CurrentAvailabilityGroupRole IS NULL)) + AND NOT (@CurrentAvailabilityGroup IS NOT NULL AND @CurrentBackupOperationSupportedOnSecondaryReplicas = 1 AND (@CurrentIsPreferredBackupReplica <> 1 OR @CurrentIsPreferredBackupReplica IS NULL) AND @OverrideBackupPreference = 'N') + AND NOT (@CurrentIsReadOnly = 1 AND @Updateability = 'READ_WRITE') + AND NOT (@CurrentIsReadOnly = 0 AND @Updateability = 'READ_ONLY') + AND NOT (@CurrentBackupType = 'LOG' AND @LogSizeSinceLastLogBackup IS NOT NULL AND @TimeSinceLastLogBackup IS NOT NULL AND NOT(@CurrentLogSizeSinceLastLogBackup >= @LogSizeSinceLastLogBackup OR @CurrentLogSizeSinceLastLogBackup IS NULL OR DATEDIFF(SECOND,@CurrentLastLogBackup,SYSDATETIME()) >= @TimeSinceLastLogBackup OR @CurrentLastLogBackup IS NULL)) + AND NOT (@CurrentBackupType = 'LOG' AND @Updateability = 'READ_ONLY' AND @BackupSoftware = 'DATA_DOMAIN_BOOST') + AND NOT (@CurrentBackupType = 'DIFF' AND @MinDatabaseSizeForDifferentialBackup IS NOT NULL AND (COALESCE(CAST(@CurrentAllocatedExtentPageCount AS BIGINT) * 8192, CAST(@CurrentDatabaseSize AS BIGINT) * 8192) < CAST(@MinDatabaseSizeForDifferentialBackup AS BIGINT) * 1024 * 1024)) + BEGIN -- Start of database backup check + + IF @CurrentBackupType = 'LOG' AND (@CleanupTime IS NOT NULL OR @MirrorCleanupTime IS NOT NULL) + BEGIN + SELECT @CurrentLatestBackup = MAX([backup_start_date]) + FROM msdb.[dbo].[backupset] + WHERE ([type] IN('D','I') + OR ([type] = 'L' AND [last_lsn] < @CurrentDifferentialBaseLSN)) + AND [is_damaged] = 0 + AND [database_name] = @CurrentDatabaseName; + END; + + SET @CurrentDate = SYSDATETIME(); + SET @CurrentDateUTC = SYSUTCDATETIME(); + + INSERT INTO @CurrentCleanupDates (CleanupDate) + SELECT @CurrentDate; + + IF @CurrentBackupType = 'LOG' + BEGIN + INSERT INTO @CurrentCleanupDates (CleanupDate) + SELECT @CurrentLatestBackup; + END; + + SELECT @CurrentDirectoryStructure = CASE + WHEN @CurrentAvailabilityGroup IS NOT NULL THEN @AvailabilityGroupDirectoryStructure + ELSE @DirectoryStructure + END; + + IF @CurrentDirectoryStructure IS NOT NULL + BEGIN + -- Directory structure - remove tokens that are not needed + IF @ReadWriteFileGroups = 'N' SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Partial}',''); + IF @CopyOnly = 'N' SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{CopyOnly}',''); + IF @Cluster IS NULL SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{ClusterName}',''); + IF @CurrentAvailabilityGroup IS NULL SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{AvailabilityGroupName}',''); + IF SERVERPROPERTY('InstanceName') IS NULL SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{InstanceName}',''); + IF @@SERVICENAME IS NULL SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{ServiceName}',''); + IF @Description IS NULL SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Description}',''); + IF @BackupSetName IS NULL SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{BackupSetName}',''); + + IF @Directory IS NULL AND @MirrorDirectory IS NULL AND @URL IS NULL AND @DefaultDirectory LIKE '%' + '.' + @@SERVICENAME + @DirectorySeparator + 'MSSQL' + @DirectorySeparator + 'Backup' + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{ServerName}',''); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{InstanceName}',''); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{ClusterName}',''); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{AvailabilityGroupName}',''); + END; + + WHILE (@Updated = 1 OR @Updated IS NULL) + BEGIN + SET @Updated = 0; + + IF CHARINDEX('\',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'\','{DirectorySeparator}'); + SET @Updated = 1; + END; + + IF CHARINDEX('/',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'/','{DirectorySeparator}'); + SET @Updated = 1; + END; + + IF CHARINDEX('__',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'__','_'); + SET @Updated = 1; + END; + + IF CHARINDEX('--',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'--','-'); + SET @Updated = 1; + END; + + IF CHARINDEX('{DirectorySeparator}{DirectorySeparator}',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{DirectorySeparator}{DirectorySeparator}','{DirectorySeparator}'); + SET @Updated = 1; + END; + + IF CHARINDEX('{DirectorySeparator}$',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{DirectorySeparator}$','{DirectorySeparator}'); + SET @Updated = 1; + END; + IF CHARINDEX('${DirectorySeparator}',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'${DirectorySeparator}','{DirectorySeparator}'); + SET @Updated = 1; + END; + + IF CHARINDEX('{DirectorySeparator}_',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{DirectorySeparator}_','{DirectorySeparator}'); + SET @Updated = 1; + END; + IF CHARINDEX('_{DirectorySeparator}',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'_{DirectorySeparator}','{DirectorySeparator}'); + SET @Updated = 1; + END; + + IF CHARINDEX('{DirectorySeparator}-',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{DirectorySeparator}-','{DirectorySeparator}'); + SET @Updated = 1; + END; + IF CHARINDEX('-{DirectorySeparator}',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'-{DirectorySeparator}','{DirectorySeparator}'); + SET @Updated = 1; + END; + + IF CHARINDEX('_$',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'_$','_'); + SET @Updated = 1; + END; + IF CHARINDEX('$_',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'$_','_'); + SET @Updated = 1; + END; + + IF CHARINDEX('-$',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'-$','-'); + SET @Updated = 1; + END; + IF CHARINDEX('$-',@CurrentDirectoryStructure) > 0 + BEGIN + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'$-','-'); + SET @Updated = 1; + END; + + IF LEFT(@CurrentDirectoryStructure,1) = '_' + BEGIN + SET @CurrentDirectoryStructure = RIGHT(@CurrentDirectoryStructure,LEN(@CurrentDirectoryStructure) - 1); + SET @Updated = 1; + END; + IF RIGHT(@CurrentDirectoryStructure,1) = '_' + BEGIN + SET @CurrentDirectoryStructure = LEFT(@CurrentDirectoryStructure,LEN(@CurrentDirectoryStructure) - 1); + SET @Updated = 1; + END; + + IF LEFT(@CurrentDirectoryStructure,1) = '-' + BEGIN + SET @CurrentDirectoryStructure = RIGHT(@CurrentDirectoryStructure,LEN(@CurrentDirectoryStructure) - 1); + SET @Updated = 1; + END; + IF RIGHT(@CurrentDirectoryStructure,1) = '-' + BEGIN + SET @CurrentDirectoryStructure = LEFT(@CurrentDirectoryStructure,LEN(@CurrentDirectoryStructure) - 1); + SET @Updated = 1; + END; + + IF LEFT(@CurrentDirectoryStructure,1) = '$' + BEGIN + SET @CurrentDirectoryStructure = RIGHT(@CurrentDirectoryStructure,LEN(@CurrentDirectoryStructure) - 1); + SET @Updated = 1; + END; + IF RIGHT(@CurrentDirectoryStructure,1) = '$' + BEGIN + SET @CurrentDirectoryStructure = LEFT(@CurrentDirectoryStructure,LEN(@CurrentDirectoryStructure) - 1); + SET @Updated = 1; + END; + + IF LEFT(@CurrentDirectoryStructure,20) = '{DirectorySeparator}' + BEGIN + SET @CurrentDirectoryStructure = RIGHT(@CurrentDirectoryStructure,LEN(@CurrentDirectoryStructure) - 20); + SET @Updated = 1; + END; + IF RIGHT(@CurrentDirectoryStructure,20) = '{DirectorySeparator}' + BEGIN + SET @CurrentDirectoryStructure = LEFT(@CurrentDirectoryStructure,LEN(@CurrentDirectoryStructure) - 20); + SET @Updated = 1; + END; + END; + + SET @Updated = NULL; + + -- Directory structure - replace tokens with real values + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{DirectorySeparator}',@DirectorySeparator); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{ServerName}',CASE WHEN SERVERPROPERTY('EngineEdition') = 8 THEN LEFT(CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(MAX)),CHARINDEX('.',CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(MAX))) - 1) ELSE CAST(SERVERPROPERTY('MachineName') AS NVARCHAR(MAX)) END); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{InstanceName}',ISNULL(CAST(SERVERPROPERTY('InstanceName') AS NVARCHAR(MAX)),'')); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{ServiceName}',ISNULL(@@SERVICENAME,'')); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{ClusterName}',ISNULL(@Cluster,'')); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{AvailabilityGroupName}',ISNULL(@CurrentAvailabilityGroup,'')); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{DatabaseName}',@CurrentDatabaseNameFS); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{BackupType}',@CurrentBackupType); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Partial}','PARTIAL'); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{CopyOnly}','COPY_ONLY'); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Description}',LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(ISNULL(@Description,''),'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|','')))); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{BackupSetName}',LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(ISNULL(@BackupSetName,''),'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|','')))); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Year}',CAST(DATEPART(YEAR,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR)); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Month}',RIGHT('0' + CAST(DATEPART(MONTH,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),2)); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Day}',RIGHT('0' + CAST(DATEPART(DAY,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),2)); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Week}',RIGHT('0' + CAST(DATEPART(WEEK,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),2)); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Weekday}',DATENAME(WEEKDAY,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END)); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Hour}',RIGHT('0' + CAST(DATEPART(HOUR,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),2)); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Minute}',RIGHT('0' + CAST(DATEPART(MINUTE,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),2)); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Second}',RIGHT('0' + CAST(DATEPART(SECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),2)); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Millisecond}',RIGHT('00' + CAST(DATEPART(MILLISECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),3)); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Microsecond}',RIGHT('00000' + CAST(DATEPART(MICROSECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),6)); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{MajorVersion}',ISNULL(CAST(SERVERPROPERTY('ProductMajorVersion') AS NVARCHAR),PARSENAME(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR),4))); + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{MinorVersion}',ISNULL(CAST(SERVERPROPERTY('ProductMinorVersion') AS NVARCHAR),PARSENAME(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR),3))); + END; + + IF @DirectoryStructureCase IS NOT NULL + BEGIN + SET @CurrentDirectoryStructure = CASE WHEN @DirectoryStructureCase = 'LOWER' THEN LOWER(@CurrentDirectoryStructure) + WHEN @DirectoryStructureCase = 'UPPER' THEN UPPER(@CurrentDirectoryStructure) END; + END; + + INSERT INTO @CurrentDirectories ([Id], DirectoryPath, [mirror], DirectoryNumber, CreateCompleted, CleanupCompleted) + SELECT ROW_NUMBER() OVER (ORDER BY [Id]), + DirectoryPath + CASE WHEN DirectoryPath = 'NUL' THEN '' WHEN @CurrentDirectoryStructure IS NOT NULL THEN @DirectorySeparator + @CurrentDirectoryStructure ELSE '' END, + [mirror], + ROW_NUMBER() OVER (PARTITION BY [mirror] ORDER BY [Id] ASC), + 0, + 0 + FROM @Directories + ORDER BY [Id] ASC; + + INSERT INTO @CurrentURLs ([Id], DirectoryPath, [mirror], DirectoryNumber) + SELECT ROW_NUMBER() OVER (ORDER BY [Id]), + DirectoryPath + CASE WHEN @CurrentDirectoryStructure IS NOT NULL THEN @DirectorySeparator + @CurrentDirectoryStructure ELSE '' END, + [mirror], + ROW_NUMBER() OVER (PARTITION BY [mirror] ORDER BY [Id] ASC) + FROM @URLs + ORDER BY [Id] ASC; + + SELECT @CurrentDatabaseFileName = CASE + WHEN @CurrentAvailabilityGroup IS NOT NULL THEN @AvailabilityGroupFileName + ELSE @FileName + END; + + -- File name - remove tokens that are not needed + IF @ReadWriteFileGroups = 'N' SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Partial}',''); + IF @CopyOnly = 'N' SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{CopyOnly}',''); + IF @Cluster IS NULL SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{ClusterName}',''); + IF @CurrentAvailabilityGroup IS NULL SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{AvailabilityGroupName}',''); + IF SERVERPROPERTY('InstanceName') IS NULL SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{InstanceName}',''); + IF @@SERVICENAME IS NULL SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{ServiceName}',''); + IF @Description IS NULL SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Description}',''); + IF @BackupSetName IS NULL SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{BackupSetName}',''); + IF @CurrentNumberOfFiles = 1 SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{FileNumber}',''); + IF @CurrentNumberOfFiles = 1 SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{NumberOfFiles}',''); + + WHILE (@Updated = 1 OR @Updated IS NULL) + BEGIN + SET @Updated = 0; + + IF CHARINDEX('__',@CurrentDatabaseFileName) > 0 + BEGIN + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'__','_'); + SET @Updated = 1; + END; + + IF CHARINDEX('--',@CurrentDatabaseFileName) > 0 + BEGIN + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'--','-'); + SET @Updated = 1; + END; + + IF CHARINDEX('_$',@CurrentDatabaseFileName) > 0 + BEGIN + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'_$','_'); + SET @Updated = 1; + END; + IF CHARINDEX('$_',@CurrentDatabaseFileName) > 0 + BEGIN + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'$_','_'); + SET @Updated = 1; + END; + + IF CHARINDEX('-$',@CurrentDatabaseFileName) > 0 + BEGIN + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'-$','-'); + SET @Updated = 1; + END; + IF CHARINDEX('$-',@CurrentDatabaseFileName) > 0 + BEGIN + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'$-','-'); + SET @Updated = 1; + END; + + IF CHARINDEX('_.',@CurrentDatabaseFileName) > 0 + BEGIN + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'_.','.'); + SET @Updated = 1; + END; + + IF CHARINDEX('-.',@CurrentDatabaseFileName) > 0 + BEGIN + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'-.','.'); + SET @Updated = 1; + END; + + IF CHARINDEX('of.',@CurrentDatabaseFileName) > 0 + BEGIN + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'of.','.'); + SET @Updated = 1; + END; + + IF LEFT(@CurrentDatabaseFileName,1) = '_' + BEGIN + SET @CurrentDatabaseFileName = RIGHT(@CurrentDatabaseFileName,LEN(@CurrentDatabaseFileName) - 1); + SET @Updated = 1; + END; + IF RIGHT(@CurrentDatabaseFileName,1) = '_' + BEGIN + SET @CurrentDatabaseFileName = LEFT(@CurrentDatabaseFileName,LEN(@CurrentDatabaseFileName) - 1); + SET @Updated = 1; + END; + + IF LEFT(@CurrentDatabaseFileName,1) = '-' + BEGIN + SET @CurrentDatabaseFileName = RIGHT(@CurrentDatabaseFileName,LEN(@CurrentDatabaseFileName) - 1); + SET @Updated = 1; + END; + IF RIGHT(@CurrentDatabaseFileName,1) = '-' + BEGIN + SET @CurrentDatabaseFileName = LEFT(@CurrentDatabaseFileName,LEN(@CurrentDatabaseFileName) - 1); + SET @Updated = 1; + END; + + IF LEFT(@CurrentDatabaseFileName,1) = '$' + BEGIN + SET @CurrentDatabaseFileName = RIGHT(@CurrentDatabaseFileName,LEN(@CurrentDatabaseFileName) - 1); + SET @Updated = 1; + END; + IF RIGHT(@CurrentDatabaseFileName,1) = '$' + BEGIN + SET @CurrentDatabaseFileName = LEFT(@CurrentDatabaseFileName,LEN(@CurrentDatabaseFileName) - 1); + SET @Updated = 1; + END; + END; + + SET @Updated = NULL; + + SELECT @CurrentFileExtension = CASE + WHEN @CurrentBackupType = 'FULL' THEN @FileExtensionFull + WHEN @CurrentBackupType = 'DIFF' THEN @FileExtensionDiff + WHEN @CurrentBackupType = 'LOG' THEN @FileExtensionLog + END; + + -- File name - replace tokens with real values + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{ServerName}',CASE WHEN SERVERPROPERTY('EngineEdition') = 8 THEN LEFT(CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(MAX)),CHARINDEX('.',CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(MAX))) - 1) ELSE CAST(SERVERPROPERTY('MachineName') AS NVARCHAR(MAX)) END); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{InstanceName}',ISNULL(CAST(SERVERPROPERTY('InstanceName') AS NVARCHAR(MAX)),'')); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{ServiceName}',ISNULL(@@SERVICENAME,'')); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{ClusterName}',ISNULL(@Cluster,'')); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{AvailabilityGroupName}',ISNULL(@CurrentAvailabilityGroup,'')); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{BackupType}',@CurrentBackupType); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Partial}','PARTIAL'); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{CopyOnly}','COPY_ONLY'); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Description}',LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(ISNULL(@Description,''),'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|','')))); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{BackupSetName}',LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(ISNULL(@BackupSetName,''),'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|','')))); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Year}',CAST(DATEPART(YEAR,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR)); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Month}',RIGHT('0' + CAST(DATEPART(MONTH,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),2)); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Day}',RIGHT('0' + CAST(DATEPART(DAY,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),2)); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Week}',RIGHT('0' + CAST(DATEPART(WEEK,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),2)); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Weekday}',DATENAME(WEEKDAY,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END)); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Hour}',RIGHT('0' + CAST(DATEPART(HOUR,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),2)); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Minute}',RIGHT('0' + CAST(DATEPART(MINUTE,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),2)); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Second}',RIGHT('0' + CAST(DATEPART(SECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),2)); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Millisecond}',RIGHT('00' + CAST(DATEPART(MILLISECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),3)); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Microsecond}',RIGHT('00000' + CAST(DATEPART(MICROSECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS NVARCHAR),6)); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{NumberOfFiles}',@CurrentNumberOfFiles); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{FileExtension}',@CurrentFileExtension); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{MajorVersion}',ISNULL(CAST(SERVERPROPERTY('ProductMajorVersion') AS NVARCHAR),PARSENAME(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR),4))); + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{MinorVersion}',ISNULL(CAST(SERVERPROPERTY('ProductMinorVersion') AS NVARCHAR),PARSENAME(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR),3))); + + SELECT @CurrentMaxFilePathLength = CASE + WHEN EXISTS (SELECT * FROM @CurrentDirectories) THEN (SELECT MAX(LEN(DirectoryPath + @DirectorySeparator)) FROM @CurrentDirectories) + WHEN EXISTS (SELECT * FROM @CurrentURLs) THEN (SELECT MAX(LEN(DirectoryPath + @DirectorySeparator)) FROM @CurrentURLs) + END + + LEN(REPLACE(REPLACE(@CurrentDatabaseFileName,'{DatabaseName}',@CurrentDatabaseNameFS), '{FileNumber}', CASE WHEN @CurrentNumberOfFiles >= 1 AND @CurrentNumberOfFiles <= 9 THEN '1' WHEN @CurrentNumberOfFiles >= 10 THEN '01' END)); + + -- The maximum length of a backup device is 259 characters + IF @CurrentMaxFilePathLength > 259 + BEGIN + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{DatabaseName}',LEFT(@CurrentDatabaseNameFS,CASE WHEN (LEN(@CurrentDatabaseNameFS) + 259 - @CurrentMaxFilePathLength - 3) < 20 THEN 20 ELSE (LEN(@CurrentDatabaseNameFS) + 259 - @CurrentMaxFilePathLength - 3) END) + '...'); + END; + ELSE + BEGIN + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{DatabaseName}',@CurrentDatabaseNameFS); + END; + + IF @FileNameCase IS NOT NULL + BEGIN + SET @CurrentDatabaseFileName = CASE WHEN @FileNameCase = 'LOWER' THEN LOWER(@CurrentDatabaseFileName) + WHEN @FileNameCase = 'UPPER' THEN UPPER(@CurrentDatabaseFileName) END; + END; + + IF EXISTS (SELECT * FROM @CurrentDirectories WHERE [mirror] = 0) + BEGIN + SET @CurrentFileNumber = 0; + + WHILE @CurrentFileNumber < @CurrentNumberOfFiles + BEGIN + SET @CurrentFileNumber = @CurrentFileNumber + 1; + + SELECT @CurrentDirectoryPath = DirectoryPath + FROM @CurrentDirectories + WHERE @CurrentFileNumber >= (DirectoryNumber - 1) * (SELECT @CurrentNumberOfFiles / COUNT(*) FROM @CurrentDirectories WHERE [mirror] = 0) + 1 + AND @CurrentFileNumber <= DirectoryNumber * (SELECT @CurrentNumberOfFiles / COUNT(*) FROM @CurrentDirectories WHERE [mirror] = 0) + AND [mirror] = 0; + + SET @CurrentFileName = REPLACE(@CurrentDatabaseFileName, '{FileNumber}', CASE WHEN @CurrentNumberOfFiles >= 1 AND @CurrentNumberOfFiles <= 9 THEN CAST(@CurrentFileNumber AS NVARCHAR) WHEN @CurrentNumberOfFiles >= 10 THEN RIGHT('0' + CAST(@CurrentFileNumber AS NVARCHAR),2) END); + + IF @CurrentDirectoryPath = 'NUL' + BEGIN + SET @CurrentFilePath = 'NUL'; + END; + ELSE + BEGIN + SET @CurrentFilePath = @CurrentDirectoryPath + @DirectorySeparator + @CurrentFileName; + END; + + INSERT INTO @CurrentFiles ([Type], FilePath, [mirror]) + SELECT 'DISK', @CurrentFilePath, 0; + + SET @CurrentDirectoryPath = NULL; + SET @CurrentFileName = NULL; + SET @CurrentFilePath = NULL; + END; + + INSERT INTO @CurrentBackupSet ([mirror], VerifyCompleted) + SELECT 0, 0; + END; + + IF EXISTS (SELECT * FROM @CurrentDirectories WHERE [mirror] = 1) + BEGIN + SET @CurrentFileNumber = 0; + + WHILE @CurrentFileNumber < @CurrentNumberOfFiles + BEGIN + SET @CurrentFileNumber = @CurrentFileNumber + 1; + + SELECT @CurrentDirectoryPath = DirectoryPath + FROM @CurrentDirectories + WHERE @CurrentFileNumber >= (DirectoryNumber - 1) * (SELECT @CurrentNumberOfFiles / COUNT(*) FROM @CurrentDirectories WHERE [mirror] = 1) + 1 + AND @CurrentFileNumber <= DirectoryNumber * (SELECT @CurrentNumberOfFiles / COUNT(*) FROM @CurrentDirectories WHERE [mirror] = 1) + AND [mirror] = 1; + + SET @CurrentFileName = REPLACE(@CurrentDatabaseFileName, '{FileNumber}', CASE WHEN @CurrentNumberOfFiles > 1 AND @CurrentNumberOfFiles <= 9 THEN CAST(@CurrentFileNumber AS NVARCHAR) WHEN @CurrentNumberOfFiles >= 10 THEN RIGHT('0' + CAST(@CurrentFileNumber AS NVARCHAR),2) ELSE '' END); + + SET @CurrentFilePath = @CurrentDirectoryPath + @DirectorySeparator + @CurrentFileName; + + INSERT INTO @CurrentFiles ([Type], FilePath, [mirror]) + SELECT 'DISK', @CurrentFilePath, 1; + + SET @CurrentDirectoryPath = NULL; + SET @CurrentFileName = NULL; + SET @CurrentFilePath = NULL; + END; + + INSERT INTO @CurrentBackupSet ([mirror], VerifyCompleted) + SELECT 1, 0; + END; + + IF EXISTS (SELECT * FROM @CurrentURLs WHERE [mirror] = 0) + BEGIN + SET @CurrentFileNumber = 0; + + WHILE @CurrentFileNumber < @CurrentNumberOfFiles + BEGIN + SET @CurrentFileNumber = @CurrentFileNumber + 1; + + SELECT @CurrentDirectoryPath = DirectoryPath + FROM @CurrentURLs + WHERE @CurrentFileNumber >= (DirectoryNumber - 1) * (SELECT @CurrentNumberOfFiles / COUNT(*) FROM @CurrentURLs WHERE [mirror] = 0) + 1 + AND @CurrentFileNumber <= DirectoryNumber * (SELECT @CurrentNumberOfFiles / COUNT(*) FROM @CurrentURLs WHERE [mirror] = 0) + AND [mirror] = 0; + + SET @CurrentFileName = REPLACE(@CurrentDatabaseFileName, '{FileNumber}', CASE WHEN @CurrentNumberOfFiles > 1 AND @CurrentNumberOfFiles <= 9 THEN CAST(@CurrentFileNumber AS NVARCHAR) WHEN @CurrentNumberOfFiles >= 10 THEN RIGHT('0' + CAST(@CurrentFileNumber AS NVARCHAR),2) ELSE '' END); + + SET @CurrentFilePath = @CurrentDirectoryPath + @DirectorySeparator + @CurrentFileName; + + INSERT INTO @CurrentFiles ([Type], FilePath, [mirror]) + SELECT 'URL', @CurrentFilePath, 0; + + SET @CurrentDirectoryPath = NULL; + SET @CurrentFileName = NULL; + SET @CurrentFilePath = NULL; + END; + + INSERT INTO @CurrentBackupSet ([mirror], VerifyCompleted) + SELECT 0, 0; + END; + + IF EXISTS (SELECT * FROM @CurrentURLs WHERE [mirror] = 1) + BEGIN + SET @CurrentFileNumber = 0; + + WHILE @CurrentFileNumber < @CurrentNumberOfFiles + BEGIN + SET @CurrentFileNumber = @CurrentFileNumber + 1; + + SELECT @CurrentDirectoryPath = DirectoryPath + FROM @CurrentURLs + WHERE @CurrentFileNumber >= (DirectoryNumber - 1) * (SELECT @CurrentNumberOfFiles / COUNT(*) FROM @CurrentURLs WHERE [mirror] = 0) + 1 + AND @CurrentFileNumber <= DirectoryNumber * (SELECT @CurrentNumberOfFiles / COUNT(*) FROM @CurrentURLs WHERE [mirror] = 0) + AND [mirror] = 1; + + SET @CurrentFileName = REPLACE(@CurrentDatabaseFileName, '{FileNumber}', CASE WHEN @CurrentNumberOfFiles > 1 AND @CurrentNumberOfFiles <= 9 THEN CAST(@CurrentFileNumber AS NVARCHAR) WHEN @CurrentNumberOfFiles >= 10 THEN RIGHT('0' + CAST(@CurrentFileNumber AS NVARCHAR),2) ELSE '' END); + + SET @CurrentFilePath = @CurrentDirectoryPath + @DirectorySeparator + @CurrentFileName; + + INSERT INTO @CurrentFiles ([Type], FilePath, [mirror]) + SELECT 'URL', @CurrentFilePath, 1; + + SET @CurrentDirectoryPath = NULL; + SET @CurrentFileName = NULL; + SET @CurrentFilePath = NULL; + END; + + INSERT INTO @CurrentBackupSet ([mirror], VerifyCompleted) + SELECT 1, 0; + END; + + -- Create directory + IF @HostPlatform = 'Windows' + AND (@BackupSoftware <> 'DATA_DOMAIN_BOOST' OR @BackupSoftware IS NULL) + AND NOT EXISTS(SELECT * FROM @CurrentDirectories WHERE DirectoryPath = 'NUL' OR DirectoryPath IN(SELECT DirectoryPath FROM @Directories)) + BEGIN + WHILE (1 = 1) + BEGIN + SELECT TOP 1 @CurrentDirectoryID = [Id], + @CurrentDirectoryPath = DirectoryPath + FROM @CurrentDirectories + WHERE CreateCompleted = 0 + ORDER BY [Id] ASC; + + IF @@ROWCOUNT = 0 + BEGIN + BREAK; + END; + + IF @DirectoryCheck = 'Y' + BEGIN + IF @Version >= 14 + BEGIN + INSERT INTO @DirectoryInfo (FileExists, FileIsADirectory, ParentDirectoryExists) + SELECT file_exists, + file_is_a_directory, + parent_directory_exists + FROM [sys].dm_os_file_exists (@CurrentDirectoryPath); + END; + ELSE + BEGIN + INSERT INTO @DirectoryInfo (FileExists, FileIsADirectory, ParentDirectoryExists) + EXECUTE [master].[dbo].xp_fileexist @CurrentDirectoryPath; + END; + END; + + IF NOT EXISTS (SELECT * FROM @DirectoryInfo WHERE FileExists = 0 AND FileIsADirectory = 1 AND ParentDirectoryExists = 1) + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'xp_create_subdir'; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_create_subdir N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + ''' IF @ReturnCode <> 0 RAISERROR(''Error creating directory.'', 16, 1)'; + + EXECUTE @CurrentCommandOutput = [dbo].CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @LogToTable = @LogToTable, @Execute = @Execute; + SET @Error = @@ERROR; + IF @Error <> 0 SET @CurrentCommandOutput = @Error; + IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput; + + UPDATE @CurrentDirectories + SET CreateCompleted = 1, + CreateOutput = @CurrentCommandOutput + WHERE [Id] = @CurrentDirectoryID; + END; + ELSE + BEGIN + UPDATE @CurrentDirectories + SET CreateCompleted = 1, + CreateOutput = 0 + WHERE [Id] = @CurrentDirectoryID; + END; + + SET @CurrentDirectoryID = NULL; + SET @CurrentDirectoryPath = NULL; + + SET @CurrentDatabaseContext = NULL; + SET @CurrentCommand = NULL; + SET @CurrentCommandOutput = NULL; + SET @CurrentCommandType = NULL; + + DELETE FROM @DirectoryInfo; + END; + END; + + IF @CleanupMode = 'BEFORE_BACKUP' + BEGIN + INSERT INTO @CurrentCleanupDates (CleanupDate, [mirror]) + SELECT DATEADD(hh,-(@CleanupTime),SYSDATETIME()), 0; + + IF NOT EXISTS(SELECT * FROM @CurrentCleanupDates WHERE ([mirror] = 0 OR [mirror] IS NULL) AND CleanupDate IS NULL) + BEGIN + UPDATE @CurrentDirectories + SET CleanupDate = (SELECT MIN(CleanupDate) + FROM @CurrentCleanupDates + WHERE ([mirror] = 0 OR [mirror] IS NULL)), + CleanupMode = 'BEFORE_BACKUP' + WHERE [mirror] = 0; + END; + END; + + IF @MirrorCleanupMode = 'BEFORE_BACKUP' + BEGIN + INSERT INTO @CurrentCleanupDates (CleanupDate, [mirror]) + SELECT DATEADD(hh,-(@MirrorCleanupTime),SYSDATETIME()), 1; + + IF NOT EXISTS(SELECT * FROM @CurrentCleanupDates WHERE ([mirror] = 1 OR [mirror] IS NULL) AND CleanupDate IS NULL) + BEGIN + UPDATE @CurrentDirectories + SET CleanupDate = (SELECT MIN(CleanupDate) + FROM @CurrentCleanupDates + WHERE ([mirror] = 1 OR [mirror] IS NULL)), + CleanupMode = 'BEFORE_BACKUP' + WHERE [mirror] = 1; + END; + END; + + -- Delete old backup files, before backup + IF (NOT EXISTS (SELECT * FROM @CurrentDirectories WHERE CreateOutput <> 0 OR CreateOutput IS NULL) OR @HostPlatform = 'Linux') + AND (@BackupSoftware <> 'DATA_DOMAIN_BOOST' OR @BackupSoftware IS NULL) + AND @CurrentBackupType = @BackupType + BEGIN + WHILE (1 = 1) + BEGIN + SELECT TOP 1 @CurrentDirectoryID = [Id], + @CurrentDirectoryPath = DirectoryPath, + @CurrentCleanupDate = CleanupDate + FROM @CurrentDirectories + WHERE CleanupDate IS NOT NULL + AND CleanupMode = 'BEFORE_BACKUP' + AND CleanupCompleted = 0 + ORDER BY [Id] ASC; + + IF @@ROWCOUNT = 0 + BEGIN + BREAK; + END; + + IF @BackupSoftware IS NULL + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'xp_delete_file'; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_delete_file 0, N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + ''', ''' + @CurrentFileExtension + ''', ''' + CONVERT(NVARCHAR(19),@CurrentCleanupDate,126) + ''' IF @ReturnCode <> 0 RAISERROR(''Error deleting files.'', 16, 1)'; + END; + + IF @BackupSoftware = 'LITESPEED' + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'xp_slssqlmaint'; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_slssqlmaint N''-MAINTDEL -DELFOLDER "' + REPLACE(@CurrentDirectoryPath,'''','''''') + '" -DELEXTENSION "' + @CurrentFileExtension + '" -DELUNIT "' + CAST(DATEDIFF(mi,@CurrentCleanupDate,SYSDATETIME()) + 1 AS NVARCHAR) + '" -DELUNITTYPE "minutes" -DELUSEAGE'' IF @ReturnCode <> 0 RAISERROR(''Error deleting LiteSpeed backup files.'', 16, 1)'; + END; + + IF @BackupSoftware = 'SQLBACKUP' + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'sqbutility'; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.sqbutility 1032, N''' + REPLACE(@CurrentDatabaseName,'''','''''') + ''', N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + ''', ''' + CASE WHEN @CurrentBackupType = 'FULL' THEN 'D' WHEN @CurrentBackupType = 'DIFF' THEN 'I' WHEN @CurrentBackupType = 'LOG' THEN 'L' END + ''', ''' + CAST(DATEDIFF(hh,@CurrentCleanupDate,SYSDATETIME()) + 1 AS NVARCHAR) + 'h'', ' + ISNULL('''' + REPLACE(@EncryptionKey,'''','''''') + '''','NULL') + ' IF @ReturnCode <> 0 RAISERROR(''Error deleting SQLBackup backup files.'', 16, 1)'; + END; + + IF @BackupSoftware = 'SQLSAFE' + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'xp_ss_delete'; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_ss_delete @filename = N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + '\*.' + @CurrentFileExtension + ''', @age = ''' + CAST(DATEDIFF(mi,@CurrentCleanupDate,SYSDATETIME()) + 1 AS NVARCHAR) + 'Minutes'' IF @ReturnCode <> 0 RAISERROR(''Error deleting SQLsafe backup files.'', 16, 1)'; + END; + + EXECUTE @CurrentCommandOutput = [dbo].CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @LogToTable = @LogToTable, @Execute = @Execute; + SET @Error = @@ERROR; + IF @Error <> 0 SET @CurrentCommandOutput = @Error; + IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput; + + UPDATE @CurrentDirectories + SET CleanupCompleted = 1, + CleanupOutput = @CurrentCommandOutput + WHERE [Id] = @CurrentDirectoryID; + + SET @CurrentDirectoryID = NULL; + SET @CurrentDirectoryPath = NULL; + SET @CurrentCleanupDate = NULL; + + SET @CurrentDatabaseContext = NULL; + SET @CurrentCommand = NULL; + SET @CurrentCommandOutput = NULL; + SET @CurrentCommandType = NULL; + END; + END; + + -- Perform a backup + IF NOT EXISTS (SELECT * FROM @CurrentDirectories WHERE DirectoryPath <> 'NUL' AND DirectoryPath NOT IN(SELECT DirectoryPath FROM @Directories) AND (CreateOutput <> 0 OR CreateOutput IS NULL)) + OR @HostPlatform = 'Linux' + BEGIN + IF @BackupSoftware IS NULL + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SELECT @CurrentCommandType = CASE + WHEN @CurrentBackupType IN('DIFF','FULL') THEN 'BACKUP_DATABASE' + WHEN @CurrentBackupType = 'LOG' THEN 'BACKUP_LOG' + END; + + SELECT @CurrentCommand = CASE + WHEN @CurrentBackupType IN('DIFF','FULL') THEN 'BACKUP DATABASE ' + QUOTENAME(@CurrentDatabaseName) + WHEN @CurrentBackupType = 'LOG' THEN 'BACKUP LOG ' + QUOTENAME(@CurrentDatabaseName) + END; + + IF @ReadWriteFileGroups = 'Y' AND @CurrentDatabaseName <> 'master' SET @CurrentCommand += ' READ_WRITE_FILEGROUPS'; + + SET @CurrentCommand += ' TO'; + + SELECT @CurrentCommand += ' ' + [Type] + ' = N''' + REPLACE(FilePath,'''','''''') + '''' + CASE WHEN ROW_NUMBER() OVER (ORDER BY FilePath ASC) <> @CurrentNumberOfFiles THEN ',' ELSE '' END + FROM @CurrentFiles + WHERE [mirror] = 0 + ORDER BY FilePath ASC; + + IF EXISTS(SELECT * FROM @CurrentFiles WHERE [mirror] = 1) + BEGIN + SET @CurrentCommand += ' MIRROR TO'; + + SELECT @CurrentCommand += ' ' + [Type] + ' = N''' + REPLACE(FilePath,'''','''''') + '''' + CASE WHEN ROW_NUMBER() OVER (ORDER BY FilePath ASC) <> @CurrentNumberOfFiles THEN ',' ELSE '' END + FROM @CurrentFiles + WHERE [mirror] = 1 + ORDER BY FilePath ASC; + END; + + SET @CurrentCommand += ' WITH '; + IF @Checksum = 'Y' SET @CurrentCommand += 'CHECKSUM'; + IF @Checksum = 'N' SET @CurrentCommand += 'NO_CHECKSUM'; + + IF @Version >= 10 + BEGIN + SET @CurrentCommand += CASE WHEN @Compress = 'Y' AND (@CurrentIsEncrypted = 0 OR (@CurrentIsEncrypted = 1 AND ((@Version >= 13 AND @CurrentMaxTransferSize >= 65537) OR @Version >= 15.0404316 OR SERVERPROPERTY('EngineEdition') = 8))) THEN ', COMPRESSION' ELSE ', NO_COMPRESSION' END; + END; + + IF @Compress = 'Y' AND @CompressionAlgorithm IS NOT NULL + BEGIN + SET @CurrentCommand += ' (ALGORITHM = ' + @CompressionAlgorithm + CASE WHEN @CompressionLevel IS NOT NULL THEN ', LEVEL = ' + @CompressionLevel ELSE '' END + ')'; + END; + + IF @CurrentBackupType = 'DIFF' SET @CurrentCommand += ', DIFFERENTIAL'; + + IF EXISTS(SELECT * FROM @CurrentFiles WHERE [mirror] = 1) + BEGIN + SET @CurrentCommand += ', FORMAT'; + END; + + IF @CopyOnly = 'Y' SET @CurrentCommand += ', COPY_ONLY'; + IF @NoRecovery = 'Y' AND @CurrentBackupType = 'LOG' SET @CurrentCommand += ', NORECOVERY'; + IF @Init = 'Y' SET @CurrentCommand += ', INIT'; + IF @Format = 'Y' SET @CurrentCommand += ', FORMAT'; + IF @BlockSize IS NOT NULL SET @CurrentCommand += ', BLOCKSIZE = ' + CAST(@BlockSize AS NVARCHAR); + IF @BufferCount IS NOT NULL SET @CurrentCommand += ', BUFFERCOUNT = ' + CAST(@BufferCount AS NVARCHAR); + IF @CurrentMaxTransferSize IS NOT NULL SET @CurrentCommand += ', MAXTRANSFERSIZE = ' + CAST(@CurrentMaxTransferSize AS NVARCHAR); + IF @Description IS NOT NULL SET @CurrentCommand += ', DESCRIPTION = N''' + REPLACE(@Description,'''','''''') + ''''; + IF @BackupSetName IS NOT NULL SET @CurrentCommand += ', NAME = N''' + REPLACE(@BackupSetName,'''','''''') + ''''; + IF @Stats IS NOT NULL SET @CurrentCommand += ', STATS = ' + CAST(@Stats AS NVARCHAR); + IF @BackupOptions IS NOT NULL SET @CurrentCommand += ', BACKUP_OPTIONS = N''' + REPLACE(@BackupOptions,'''','''''') + ''''; + IF @Encrypt = 'Y' SET @CurrentCommand += ', ENCRYPTION (ALGORITHM = ' + UPPER(@EncryptionAlgorithm) + ', '; + IF @Encrypt = 'Y' AND @ServerCertificate IS NOT NULL SET @CurrentCommand += 'SERVER CERTIFICATE = ' + QUOTENAME(@ServerCertificate); + IF @Encrypt = 'Y' AND @ServerAsymmetricKey IS NOT NULL SET @CurrentCommand += 'SERVER ASYMMETRIC KEY = ' + QUOTENAME(@ServerAsymmetricKey); + IF @Encrypt = 'Y' SET @CurrentCommand += ')'; + IF @URL IS NOT NULL AND @Credential IS NOT NULL SET @CurrentCommand += ', CREDENTIAL = N''' + REPLACE(@Credential,'''','''''') + ''''; + IF @ExpireDate IS NOT NULL SET @CurrentCommand += ', EXPIREDATE = ''' + CONVERT(NVARCHAR, @ExpireDate, 21) + ''''; + IF @RetainDays IS NOT NULL SET @CurrentCommand += ', RETAINDAYS = ' + CAST(@RetainDays AS NVARCHAR); + END; + + IF @BackupSoftware = 'LITESPEED' + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SELECT @CurrentCommandType = CASE + WHEN @CurrentBackupType IN('DIFF','FULL') THEN 'xp_backup_database' + WHEN @CurrentBackupType = 'LOG' THEN 'xp_backup_log' + END; + + SELECT @CurrentCommand = CASE + WHEN @CurrentBackupType IN('DIFF','FULL') THEN 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_backup_database @database = N''' + REPLACE(@CurrentDatabaseName,'''','''''') + '''' + WHEN @CurrentBackupType = 'LOG' THEN 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_backup_log @database = N''' + REPLACE(@CurrentDatabaseName,'''','''''') + '''' + END; + + SELECT @CurrentCommand += ', @filename = N''' + REPLACE(FilePath,'''','''''') + '''' + FROM @CurrentFiles + WHERE [mirror] = 0 + ORDER BY FilePath ASC; + + IF EXISTS(SELECT * FROM @CurrentFiles WHERE [mirror] = 1) + BEGIN + SELECT @CurrentCommand += ', @mirror = N''' + REPLACE(FilePath,'''','''''') + '''' + FROM @CurrentFiles + WHERE [mirror] = 1 + ORDER BY FilePath ASC; + END; + + SET @CurrentCommand += ', @with = '''; + IF @Checksum = 'Y' SET @CurrentCommand += 'CHECKSUM'; + IF @Checksum = 'N' SET @CurrentCommand += 'NO_CHECKSUM'; + IF @CurrentBackupType = 'DIFF' SET @CurrentCommand += ', DIFFERENTIAL'; + IF @CopyOnly = 'Y' SET @CurrentCommand += ', COPY_ONLY'; + IF @NoRecovery = 'Y' AND @CurrentBackupType = 'LOG' SET @CurrentCommand += ', NORECOVERY'; + IF @BlockSize IS NOT NULL SET @CurrentCommand += ', BLOCKSIZE = ' + CAST(@BlockSize AS NVARCHAR); + SET @CurrentCommand += ''''; + IF @ReadWriteFileGroups = 'Y' AND @CurrentDatabaseName <> 'master' SET @CurrentCommand += ', @read_write_filegroups = 1'; + IF @CompressionLevelNumeric IS NOT NULL SET @CurrentCommand += ', @compressionlevel = ' + CAST(@CompressionLevelNumeric AS NVARCHAR); + IF @AdaptiveCompression IS NOT NULL SET @CurrentCommand += ', @adaptivecompression = ''' + CASE WHEN @AdaptiveCompression = 'SIZE' THEN 'Size' WHEN @AdaptiveCompression = 'SPEED' THEN 'Speed' END + ''''; + IF @BufferCount IS NOT NULL SET @CurrentCommand += ', @buffercount = ' + CAST(@BufferCount AS NVARCHAR); + IF @CurrentMaxTransferSize IS NOT NULL SET @CurrentCommand += ', @maxtransfersize = ' + CAST(@CurrentMaxTransferSize AS NVARCHAR); + IF @Threads IS NOT NULL SET @CurrentCommand += ', @threads = ' + CAST(@Threads AS NVARCHAR); + IF @Init = 'Y' SET @CurrentCommand += ', @init = 1'; + IF @Format = 'Y' SET @CurrentCommand += ', @format = 1'; + IF @Throttle IS NOT NULL SET @CurrentCommand += ', @throttle = ' + CAST(@Throttle AS NVARCHAR); + IF @Description IS NOT NULL SET @CurrentCommand += ', @desc = N''' + REPLACE(@Description,'''','''''') + ''''; + IF @ObjectLevelRecoveryMap = 'Y' SET @CurrentCommand += ', @olrmap = 1'; + IF @ExpireDate IS NOT NULL SET @CurrentCommand += ', @expiration = ''' + CONVERT(NVARCHAR, @ExpireDate, 21) + ''''; + IF @RetainDays IS NOT NULL SET @CurrentCommand += ', @retaindays = ' + CAST(@RetainDays AS NVARCHAR); + + IF @EncryptionAlgorithm IS NOT NULL SET @CurrentCommand += ', @cryptlevel = ' + CASE + WHEN @EncryptionAlgorithm = 'RC2_40' THEN '0' + WHEN @EncryptionAlgorithm = 'RC2_56' THEN '1' + WHEN @EncryptionAlgorithm = 'RC2_112' THEN '2' + WHEN @EncryptionAlgorithm = 'RC2_128' THEN '3' + WHEN @EncryptionAlgorithm = 'TRIPLE_DES_3KEY' THEN '4' + WHEN @EncryptionAlgorithm = 'RC4_128' THEN '5' + WHEN @EncryptionAlgorithm = 'AES_128' THEN '6' + WHEN @EncryptionAlgorithm = 'AES_192' THEN '7' + WHEN @EncryptionAlgorithm = 'AES_256' THEN '8' + END; + + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', @encryptionkey = N''' + REPLACE(@EncryptionKey,'''','''''') + ''''; + SET @CurrentCommand += ' IF @ReturnCode <> 0 RAISERROR(''Error performing LiteSpeed backup.'', 16, 1)'; + END; + + IF @BackupSoftware = 'SQLBACKUP' + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'sqlbackup'; + + SELECT @CurrentCommand = CASE + WHEN @CurrentBackupType IN('DIFF','FULL') THEN 'BACKUP DATABASE ' + QUOTENAME(@CurrentDatabaseName) + WHEN @CurrentBackupType = 'LOG' THEN 'BACKUP LOG ' + QUOTENAME(@CurrentDatabaseName) + END; + + IF @ReadWriteFileGroups = 'Y' AND @CurrentDatabaseName <> 'master' SET @CurrentCommand += ' READ_WRITE_FILEGROUPS'; + + SET @CurrentCommand += ' TO'; + + SELECT @CurrentCommand += ' DISK = N''' + REPLACE(FilePath,'''','''''') + '''' + CASE WHEN ROW_NUMBER() OVER (ORDER BY FilePath ASC) <> @CurrentNumberOfFiles THEN ',' ELSE '' END + FROM @CurrentFiles + WHERE [mirror] = 0 + ORDER BY FilePath ASC; + + SET @CurrentCommand += ' WITH '; + + IF EXISTS(SELECT * FROM @CurrentFiles WHERE [mirror] = 1) + BEGIN + SET @CurrentCommand += ' MIRRORFILE' + ' = N''' + REPLACE((SELECT FilePath FROM @CurrentFiles WHERE [mirror] = 1),'''','''''') + ''', '; + END; + + IF @Checksum = 'Y' SET @CurrentCommand += 'CHECKSUM'; + IF @Checksum = 'N' SET @CurrentCommand += 'NO_CHECKSUM'; + IF @CurrentBackupType = 'DIFF' SET @CurrentCommand += ', DIFFERENTIAL'; + IF @CopyOnly = 'Y' SET @CurrentCommand += ', COPY_ONLY'; + IF @NoRecovery = 'Y' AND @CurrentBackupType = 'LOG' SET @CurrentCommand += ', NORECOVERY'; + IF @Init = 'Y' SET @CurrentCommand += ', INIT'; + IF @Format = 'Y' SET @CurrentCommand += ', FORMAT'; + IF @CompressionLevelNumeric IS NOT NULL SET @CurrentCommand += ', COMPRESSION = ' + CAST(@CompressionLevelNumeric AS NVARCHAR); + IF @Threads IS NOT NULL SET @CurrentCommand += ', THREADCOUNT = ' + CAST(@Threads AS NVARCHAR); + IF @CurrentMaxTransferSize IS NOT NULL SET @CurrentCommand += ', MAXTRANSFERSIZE = ' + CAST(@CurrentMaxTransferSize AS NVARCHAR); + IF @Description IS NOT NULL SET @CurrentCommand += ', DESCRIPTION = N''' + REPLACE(@Description,'''','''''') + ''''; + + IF @EncryptionAlgorithm IS NOT NULL SET @CurrentCommand += ', KEYSIZE = ' + CASE + WHEN @EncryptionAlgorithm = 'AES_128' THEN '128' + WHEN @EncryptionAlgorithm = 'AES_256' THEN '256' + END; + + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', PASSWORD = N''' + REPLACE(@EncryptionKey,'''','''''') + ''''; + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.sqlbackup N''-SQL "' + REPLACE(@CurrentCommand,'''','''''') + '"''' + ' IF @ReturnCode <> 0 RAISERROR(''Error performing SQLBackup backup.'', 16, 1)'; + END; + + IF @BackupSoftware = 'SQLSAFE' + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'xp_ss_backup'; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_ss_backup @database = N''' + REPLACE(@CurrentDatabaseName,'''','''''') + ''''; + + SELECT @CurrentCommand += ', ' + CASE WHEN ROW_NUMBER() OVER (ORDER BY FilePath ASC) = 1 THEN '@filename' ELSE '@backupfile' END + ' = N''' + REPLACE(FilePath,'''','''''') + '''' + FROM @CurrentFiles + WHERE [mirror] = 0 + ORDER BY FilePath ASC; + + SELECT @CurrentCommand += ', @mirrorfile = N''' + REPLACE(FilePath,'''','''''') + '''' + FROM @CurrentFiles + WHERE [mirror] = 1 + ORDER BY FilePath ASC; + + SET @CurrentCommand += ', @backuptype = ' + CASE WHEN @CurrentBackupType = 'FULL' THEN '''Full''' WHEN @CurrentBackupType = 'DIFF' THEN '''Differential''' WHEN @CurrentBackupType = 'LOG' THEN '''Log''' END; + IF @ReadWriteFileGroups = 'Y' AND @CurrentDatabaseName <> 'master' SET @CurrentCommand += ', @readwritefilegroups = 1'; + SET @CurrentCommand += ', @checksum = ' + CASE WHEN @Checksum = 'Y' THEN '1' WHEN @Checksum = 'N' THEN '0' END; + SET @CurrentCommand += ', @copyonly = ' + CASE WHEN @CopyOnly = 'Y' THEN '1' WHEN @CopyOnly = 'N' THEN '0' END; + IF @CompressionLevelNumeric IS NOT NULL SET @CurrentCommand += ', @compressionlevel = ' + CAST(@CompressionLevelNumeric AS NVARCHAR); + IF @Threads IS NOT NULL SET @CurrentCommand += ', @threads = ' + CAST(@Threads AS NVARCHAR); + IF @Init = 'Y' SET @CurrentCommand += ', @overwrite = 1'; + IF @Description IS NOT NULL SET @CurrentCommand += ', @desc = N''' + REPLACE(@Description,'''','''''') + ''''; + + IF @EncryptionAlgorithm IS NOT NULL SET @CurrentCommand += ', @encryptiontype = N''' + CASE + WHEN @EncryptionAlgorithm = 'AES_128' THEN 'AES128' + WHEN @EncryptionAlgorithm = 'AES_256' THEN 'AES256' + END + ''''; + + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', @encryptedbackuppassword = N''' + REPLACE(@EncryptionKey,'''','''''') + ''''; + SET @CurrentCommand += ' IF @ReturnCode <> 0 RAISERROR(''Error performing SQLsafe backup.'', 16, 1)'; + END; + + IF @BackupSoftware = 'DATA_DOMAIN_BOOST' + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'emc_run_backup'; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.emc_run_backup '''; + + SET @CurrentCommand += ' -c ' + CASE WHEN @Cluster IS NOT NULL AND @CurrentAvailabilityGroup IS NOT NULL THEN @Cluster ELSE CAST(SERVERPROPERTY('MachineName') AS NVARCHAR) END; + + SET @CurrentCommand += ' -l ' + CASE + WHEN @CurrentBackupType = 'FULL' THEN 'full' + WHEN @CurrentBackupType = 'DIFF' THEN 'diff' + WHEN @CurrentBackupType = 'LOG' THEN 'incr' + END; + + IF @NoRecovery = 'Y' SET @CurrentCommand += ' -H'; + + IF @CleanupTime IS NOT NULL SET @CurrentCommand += ' -y +' + CAST(@CleanupTime/24 + CASE WHEN @CleanupTime%24 > 0 THEN 1 ELSE 0 END AS NVARCHAR) + 'd'; + + IF @Checksum = 'Y' SET @CurrentCommand += ' -k'; + + SET @CurrentCommand += ' -S ' + CAST(@CurrentNumberOfFiles AS NVARCHAR); + + IF @Description IS NOT NULL SET @CurrentCommand += ' -b "' + REPLACE(@Description,'''','''''') + '"'; + + IF @BufferCount IS NOT NULL SET @CurrentCommand += ' -O "BUFFERCOUNT=' + CAST(@BufferCount AS NVARCHAR) + '"'; + + IF @ReadWriteFileGroups = 'Y' AND @CurrentDatabaseName <> 'master' SET @CurrentCommand += ' -O "READ_WRITE_FILEGROUPS"'; + + IF @DataDomainBoostHost IS NOT NULL SET @CurrentCommand += ' -a "NSR_DFA_SI_DD_HOST=' + REPLACE(@DataDomainBoostHost,'''','''''') + '"'; + IF @DataDomainBoostUser IS NOT NULL SET @CurrentCommand += ' -a "NSR_DFA_SI_DD_USER=' + REPLACE(@DataDomainBoostUser,'''','''''') + '"'; + IF @DataDomainBoostDevicePath IS NOT NULL SET @CurrentCommand += ' -a "NSR_DFA_SI_DEVICE_PATH=' + REPLACE(@DataDomainBoostDevicePath,'''','''''') + '"'; + IF @DataDomainBoostLockboxPath IS NOT NULL SET @CurrentCommand += ' -a "NSR_DFA_SI_DD_LOCKBOX_PATH=' + REPLACE(@DataDomainBoostLockboxPath,'''','''''') + '"'; + SET @CurrentCommand += ' -a "NSR_SKIP_NON_BACKUPABLE_STATE_DB=TRUE"'; + SET @CurrentCommand += ' -a "BACKUP_PROMOTION=NONE"'; + IF @CopyOnly = 'Y' SET @CurrentCommand += ' -a "NSR_COPY_ONLY=TRUE"'; + IF @BackupSetName IS NOT NULL SET @CurrentCommand += ' -N "' + REPLACE(@BackupSetName,'''','''''') + '"'; + + IF SERVERPROPERTY('InstanceName') IS NULL SET @CurrentCommand += ' "MSSQL'; + IF SERVERPROPERTY('InstanceName') IS NOT NULL SET @CurrentCommand += ' "MSSQL$' + CAST(SERVERPROPERTY('InstanceName') AS NVARCHAR); + SET @CurrentCommand += ':' + REPLACE(REPLACE(@CurrentDatabaseName,'''',''''''),'.','\.') + '"'; + + SET @CurrentCommand += ''''; + + SET @CurrentCommand += ' IF @ReturnCode <> 0 RAISERROR(''Error performing Data Domain Boost backup.'', 16, 1)'; + END; + + EXECUTE @CurrentCommandOutput = [dbo].CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @LogToTable = @LogToTable, @Execute = @Execute; + SET @Error = @@ERROR; + IF @Error <> 0 SET @CurrentCommandOutput = @Error; + IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput; + SET @CurrentBackupOutput = @CurrentCommandOutput; + END; + + -- Verify the backup + IF @CurrentBackupOutput = 0 AND @Verify = 'Y' + BEGIN + WHILE (1 = 1) + BEGIN + SELECT TOP 1 @CurrentBackupSetID = [Id], + @CurrentIsMirror = [mirror] + FROM @CurrentBackupSet + WHERE VerifyCompleted = 0 + ORDER BY [Id] ASC; + + IF @@ROWCOUNT = 0 + BEGIN + BREAK; + END; + + IF @BackupSoftware IS NULL + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'RESTORE_VERIFYONLY'; + + SET @CurrentCommand = 'RESTORE VERIFYONLY FROM'; + + SELECT @CurrentCommand += ' ' + [Type] + ' = N''' + REPLACE(FilePath,'''','''''') + '''' + CASE WHEN ROW_NUMBER() OVER (ORDER BY FilePath ASC) <> @CurrentNumberOfFiles THEN ',' ELSE '' END + FROM @CurrentFiles + WHERE [mirror] = @CurrentIsMirror + ORDER BY FilePath ASC; + + SET @CurrentCommand += ' WITH '; + IF @Checksum = 'Y' SET @CurrentCommand += 'CHECKSUM'; + IF @Checksum = 'N' SET @CurrentCommand += 'NO_CHECKSUM'; + IF @Stats IS NOT NULL SET @CurrentCommand += ', STATS = ' + CAST(@Stats AS NVARCHAR); + IF @BackupOptions IS NOT NULL SET @CurrentCommand += ', RESTORE_OPTIONS = N''' + REPLACE(@BackupOptions,'''','''''') + ''''; + IF @URL IS NOT NULL AND @Credential IS NOT NULL SET @CurrentCommand += ', CREDENTIAL = N''' + REPLACE(@Credential,'''','''''') + ''''; + END; + + IF @BackupSoftware = 'LITESPEED' + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'xp_restore_verifyonly'; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_restore_verifyonly'; + + SELECT @CurrentCommand += ' @filename = N''' + REPLACE(FilePath,'''','''''') + '''' + CASE WHEN ROW_NUMBER() OVER (ORDER BY FilePath ASC) <> @CurrentNumberOfFiles THEN ',' ELSE '' END + FROM @CurrentFiles + WHERE [mirror] = @CurrentIsMirror + ORDER BY FilePath ASC; + + SET @CurrentCommand += ', @with = '''; + IF @Checksum = 'Y' SET @CurrentCommand += 'CHECKSUM'; + IF @Checksum = 'N' SET @CurrentCommand += 'NO_CHECKSUM'; + SET @CurrentCommand += ''''; + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', @encryptionkey = N''' + REPLACE(@EncryptionKey,'''','''''') + ''''; + + SET @CurrentCommand += ' IF @ReturnCode <> 0 RAISERROR(''Error verifying LiteSpeed backup.'', 16, 1)'; + END; + + IF @BackupSoftware = 'SQLBACKUP' + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'sqlbackup'; + + SET @CurrentCommand = 'RESTORE VERIFYONLY FROM'; + + SELECT @CurrentCommand += ' DISK = N''' + REPLACE(FilePath,'''','''''') + '''' + CASE WHEN ROW_NUMBER() OVER (ORDER BY FilePath ASC) <> @CurrentNumberOfFiles THEN ',' ELSE '' END + FROM @CurrentFiles + WHERE [mirror] = @CurrentIsMirror + ORDER BY FilePath ASC; + + SET @CurrentCommand += ' WITH '; + IF @Checksum = 'Y' SET @CurrentCommand += 'CHECKSUM'; + IF @Checksum = 'N' SET @CurrentCommand += 'NO_CHECKSUM'; + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', PASSWORD = N''' + REPLACE(@EncryptionKey,'''','''''') + ''''; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.sqlbackup N''-SQL "' + REPLACE(@CurrentCommand,'''','''''') + '"''' + ' IF @ReturnCode <> 0 RAISERROR(''Error verifying SQLBackup backup.'', 16, 1)'; + END; + + IF @BackupSoftware = 'SQLSAFE' + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'xp_ss_verify'; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_ss_verify @database = N''' + REPLACE(@CurrentDatabaseName,'''','''''') + ''''; + + SELECT @CurrentCommand += ', ' + CASE WHEN ROW_NUMBER() OVER (ORDER BY FilePath ASC) = 1 THEN '@filename' ELSE '@backupfile' END + ' = N''' + REPLACE(FilePath,'''','''''') + '''' + FROM @CurrentFiles + WHERE [mirror] = @CurrentIsMirror + ORDER BY FilePath ASC; + + SET @CurrentCommand += ' IF @ReturnCode <> 0 RAISERROR(''Error verifying SQLsafe backup.'', 16, 1)'; + END; + + EXECUTE @CurrentCommandOutput = [dbo].CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @LogToTable = @LogToTable, @Execute = @Execute; + SET @Error = @@ERROR; + IF @Error <> 0 SET @CurrentCommandOutput = @Error; + IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput; + + UPDATE @CurrentBackupSet + SET VerifyCompleted = 1, + VerifyOutput = @CurrentCommandOutput + WHERE [Id] = @CurrentBackupSetID; + + SET @CurrentBackupSetID = NULL; + SET @CurrentIsMirror = NULL; + + SET @CurrentDatabaseContext = NULL; + SET @CurrentCommand = NULL; + SET @CurrentCommandOutput = NULL; + SET @CurrentCommandType = NULL; + END; + END; + + IF @CleanupMode = 'AFTER_BACKUP' + BEGIN + INSERT INTO @CurrentCleanupDates (CleanupDate, [mirror]) + SELECT DATEADD(hh,-(@CleanupTime),SYSDATETIME()), 0; + + IF NOT EXISTS(SELECT * FROM @CurrentCleanupDates WHERE ([mirror] = 0 OR [mirror] IS NULL) AND CleanupDate IS NULL) + BEGIN + UPDATE @CurrentDirectories + SET CleanupDate = (SELECT MIN(CleanupDate) + FROM @CurrentCleanupDates + WHERE ([mirror] = 0 OR [mirror] IS NULL)), + CleanupMode = 'AFTER_BACKUP' + WHERE [mirror] = 0; + END; + END; + + IF @MirrorCleanupMode = 'AFTER_BACKUP' + BEGIN + INSERT INTO @CurrentCleanupDates (CleanupDate, [mirror]) + SELECT DATEADD(hh,-(@MirrorCleanupTime),SYSDATETIME()), 1; + + IF NOT EXISTS(SELECT * FROM @CurrentCleanupDates WHERE ([mirror] = 1 OR [mirror] IS NULL) AND CleanupDate IS NULL) + BEGIN + UPDATE @CurrentDirectories + SET CleanupDate = (SELECT MIN(CleanupDate) + FROM @CurrentCleanupDates + WHERE ([mirror] = 1 OR [mirror] IS NULL)), + CleanupMode = 'AFTER_BACKUP' + WHERE [mirror] = 1; + END; + END; + + -- Delete old backup files, after backup + IF ((@CurrentBackupOutput = 0 AND @Verify = 'N') + OR (@CurrentBackupOutput = 0 AND @Verify = 'Y' AND NOT EXISTS (SELECT * FROM @CurrentBackupSet WHERE VerifyOutput <> 0 OR VerifyOutput IS NULL))) + AND (@BackupSoftware <> 'DATA_DOMAIN_BOOST' OR @BackupSoftware IS NULL) + AND @CurrentBackupType = @BackupType + BEGIN + WHILE (1 = 1) + BEGIN + SELECT TOP 1 @CurrentDirectoryID = [Id], + @CurrentDirectoryPath = DirectoryPath, + @CurrentCleanupDate = CleanupDate + FROM @CurrentDirectories + WHERE CleanupDate IS NOT NULL + AND CleanupMode = 'AFTER_BACKUP' + AND CleanupCompleted = 0 + ORDER BY [Id] ASC; + + IF @@ROWCOUNT = 0 + BEGIN + BREAK; + END; + + IF @BackupSoftware IS NULL + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'xp_delete_file'; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_delete_file 0, N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + ''', ''' + @CurrentFileExtension + ''', ''' + CONVERT(NVARCHAR(19),@CurrentCleanupDate,126) + ''' IF @ReturnCode <> 0 RAISERROR(''Error deleting files.'', 16, 1)'; + END; + + IF @BackupSoftware = 'LITESPEED' + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'xp_slssqlmaint'; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_slssqlmaint N''-MAINTDEL -DELFOLDER "' + REPLACE(@CurrentDirectoryPath,'''','''''') + '" -DELEXTENSION "' + @CurrentFileExtension + '" -DELUNIT "' + CAST(DATEDIFF(mi,@CurrentCleanupDate,SYSDATETIME()) + 1 AS NVARCHAR) + '" -DELUNITTYPE "minutes" -DELUSEAGE'' IF @ReturnCode <> 0 RAISERROR(''Error deleting LiteSpeed backup files.'', 16, 1)'; + END; + + IF @BackupSoftware = 'SQLBACKUP' + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'sqbutility'; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.sqbutility 1032, N''' + REPLACE(@CurrentDatabaseName,'''','''''') + ''', N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + ''', ''' + CASE WHEN @CurrentBackupType = 'FULL' THEN 'D' WHEN @CurrentBackupType = 'DIFF' THEN 'I' WHEN @CurrentBackupType = 'LOG' THEN 'L' END + ''', ''' + CAST(DATEDIFF(hh,@CurrentCleanupDate,SYSDATETIME()) + 1 AS NVARCHAR) + 'h'', ' + ISNULL('''' + REPLACE(@EncryptionKey,'''','''''') + '''','NULL') + ' IF @ReturnCode <> 0 RAISERROR(''Error deleting SQLBackup backup files.'', 16, 1)'; + END; + + IF @BackupSoftware = 'SQLSAFE' + BEGIN + SET @CurrentDatabaseContext = 'master'; + + SET @CurrentCommandType = 'xp_ss_delete'; + + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_ss_delete @filename = N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + '\*.' + @CurrentFileExtension + ''', @age = ''' + CAST(DATEDIFF(mi,@CurrentCleanupDate,SYSDATETIME()) + 1 AS NVARCHAR) + 'Minutes'' IF @ReturnCode <> 0 RAISERROR(''Error deleting SQLsafe backup files.'', 16, 1)'; + END; + + EXECUTE @CurrentCommandOutput = [dbo].CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @LogToTable = @LogToTable, @Execute = @Execute; + SET @Error = @@ERROR; + IF @Error <> 0 SET @CurrentCommandOutput = @Error; + IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput; + + UPDATE @CurrentDirectories + SET CleanupCompleted = 1, + CleanupOutput = @CurrentCommandOutput + WHERE [Id] = @CurrentDirectoryID; + + SET @CurrentDirectoryID = NULL; + SET @CurrentDirectoryPath = NULL; + SET @CurrentCleanupDate = NULL; + + SET @CurrentDatabaseContext = NULL; + SET @CurrentCommand = NULL; + SET @CurrentCommandOutput = NULL; + SET @CurrentCommandType = NULL; + END; + END; + END; -- End of database backup check + + IF @CurrentDatabaseState = 'SUSPECT' + BEGIN + SET @ErrorMessage = 'The database ' + QUOTENAME(@CurrentDatabaseName) + ' is in a SUSPECT state.'; + RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT; + SET @Error = @@ERROR; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + END; + + -- Update that the database is completed + IF @DatabasesInParallel = 'Y' + BEGIN + UPDATE [dbo].QueueDatabase + SET DatabaseEndTime = SYSDATETIME() + WHERE QueueID = @QueueID + AND DatabaseName = @CurrentDatabaseName; + END; + ELSE + BEGIN + UPDATE @tmpDatabases + SET Completed = 1 + WHERE Selected = 1 + AND Completed = 0 + AND [Id] = @CurrentDBID; + END; + + -- Clear variables + SET @CurrentDBID = NULL; + SET @CurrentDatabaseName = NULL; + + SET @CurrentDatabase_sp_executesql = NULL; + + SET @CurrentUserAccess = NULL; + SET @CurrentIsReadOnly = NULL; + SET @CurrentDatabaseState = NULL; + SET @CurrentInStandby = NULL; + SET @CurrentRecoveryModel = NULL; + SET @CurrentIsEncrypted = NULL; + SET @CurrentDatabaseSize = NULL; + + SET @CurrentBackupType = NULL; + SET @CurrentMaxTransferSize = NULL; + SET @CurrentNumberOfFiles = NULL; + SET @CurrentFileExtension = NULL; + SET @CurrentFileNumber = NULL; + SET @CurrentDifferentialBaseLSN = NULL; + SET @CurrentDifferentialBaseIsSnapshot = NULL; + SET @CurrentLogLSN = NULL; + SET @CurrentLatestBackup = NULL; + SET @CurrentDatabaseNameFS = NULL; + SET @CurrentDirectoryStructure = NULL; + SET @CurrentDatabaseFileName = NULL; + SET @CurrentMaxFilePathLength = NULL; + SET @CurrentDate = NULL; + SET @CurrentDateUTC = NULL; + SET @CurrentCleanupDate = NULL; + SET @CurrentReplicaID = NULL; + SET @CurrentAvailabilityGroupID = NULL; + SET @CurrentAvailabilityGroup = NULL; + SET @CurrentAvailabilityGroupRole = NULL; + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = NULL; + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = NULL; + SET @CurrentAvailabilityGroupBackupPreference = NULL; + SET @CurrentIsPreferredBackupReplica = NULL; + SET @CurrentDatabaseMirroringRole = NULL; + SET @CurrentLogShippingRole = NULL; + SET @CurrentBackupOperationSupportedOnSecondaryReplicas = NULL; + SET @CurrentLastLogBackup = NULL; + SET @CurrentLogSizeSinceLastLogBackup = NULL; + SET @CurrentAllocatedExtentPageCount = NULL; + SET @CurrentModifiedExtentPageCount = NULL; + + SET @CurrentDatabaseContext = NULL; + SET @CurrentCommand = NULL; + SET @CurrentCommandOutput = NULL; + SET @CurrentCommandType = NULL; + + SET @CurrentBackupOutput = NULL; + + DELETE FROM @CurrentDirectories; + DELETE FROM @CurrentURLs; + DELETE FROM @CurrentFiles; + DELETE FROM @CurrentCleanupDates; + DELETE FROM @CurrentBackupSet; + + END; -- End of database loop + + ---------------------------------------------------------------------------------------------------- + --// Log completing information //-- + ---------------------------------------------------------------------------------------------------- + + Logging:; + SET @EndMessage = 'Date and time: ' + CONVERT(NVARCHAR,SYSDATETIME(),120); + RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT; + + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + IF @ReturnCode <> 0 + BEGIN + RETURN @ReturnCode; + END; + + ---------------------------------------------------------------------------------------------------- + +END; +GO +SET ANSI_NULLS ON; +GO +SET QUOTED_IDENTIFIER ON; +GO +IF NOT EXISTS (SELECT * FROM [sys].[objects] WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[DatabaseIntegrityCheck]') AND TYPE IN (N'P', N'PC')) +BEGIN +EXECUTE [dbo].sp_executesql @statement = N'CREATE PROCEDURE [dbo].[DatabaseIntegrityCheck] AS'; +END; +GO +ALTER PROCEDURE [dbo].[DatabaseIntegrityCheck] + +@Databases NVARCHAR(MAX) = NULL, +@CheckCommands NVARCHAR(MAX) = 'CHECKDB', +@PhysicalOnly NVARCHAR(MAX) = 'N', +@DataPurity NVARCHAR(MAX) = 'N', +@NoIndex NVARCHAR(MAX) = 'N', +@ExtendedLogicalChecks NVARCHAR(MAX) = 'N', +@NoInformationalMessages NVARCHAR(MAX) = 'N', +@TabLock NVARCHAR(MAX) = 'N', +@FileGroups NVARCHAR(MAX) = NULL, +@Objects NVARCHAR(MAX) = NULL, +@MaxDOP INT = NULL, +@AvailabilityGroups NVARCHAR(MAX) = NULL, +@AvailabilityGroupReplicas NVARCHAR(MAX) = 'ALL', +@Updateability NVARCHAR(MAX) = 'ALL', +@TimeLimit INT = NULL, +@LockTimeout INT = NULL, +@LockMessageSeverity INT = 16, +@StringDelimiter NVARCHAR(MAX) = ',', +@DatabaseOrder NVARCHAR(MAX) = NULL, +@DatabasesInParallel NVARCHAR(MAX) = 'N', +@LogToTable NVARCHAR(MAX) = 'N', +@Execute NVARCHAR(MAX) = 'Y' + +AS + +BEGIN + + ---------------------------------------------------------------------------------------------------- + --// Source: https://ola.hallengren.com //-- + --// License: https://ola.hallengren.com/license.html //-- + --// GitHub: https://github.com/olahallengren/sql-server-maintenance-solution //-- + --// Version: 2025-07-22 16:49:16 //-- + ---------------------------------------------------------------------------------------------------- + + SET NOCOUNT ON; + + DECLARE @StartMessage NVARCHAR(MAX); + DECLARE @EndMessage NVARCHAR(MAX); + DECLARE @DatabaseMessage NVARCHAR(MAX); + DECLARE @ErrorMessage NVARCHAR(MAX); + DECLARE @Severity INT; + + DECLARE @StartTime DATETIME2 = SYSDATETIME(); + DECLARE @SchemaName NVARCHAR(MAX) = OBJECT_SCHEMA_NAME(@@PROCID); + DECLARE @ObjectName NVARCHAR(MAX) = OBJECT_NAME(@@PROCID); + DECLARE @VersionTimestamp NVARCHAR(MAX) = SUBSTRING(OBJECT_DEFINITION(@@PROCID),CHARINDEX('--// Version: ',OBJECT_DEFINITION(@@PROCID)) + LEN('--// Version: ') + 1, 19); + DECLARE @Parameters NVARCHAR(MAX); + + DECLARE @HostPlatform NVARCHAR(MAX); + + DECLARE @QueueID INT; + DECLARE @QueueStartTime DATETIME2; + + DECLARE @CurrentDBID INT; + DECLARE @CurrentDatabaseName NVARCHAR(MAX); + + DECLARE @CurrentDatabase_sp_executesql NVARCHAR(MAX); + + DECLARE @CurrentUserAccess NVARCHAR(MAX); + DECLARE @CurrentIsReadOnly BIT; + DECLARE @CurrentDatabaseState NVARCHAR(MAX); + DECLARE @CurrentInStandby BIT; + DECLARE @CurrentRecoveryModel NVARCHAR(MAX); + + DECLARE @CurrentReplicaID UNIQUEIDENTIFIER; + DECLARE @CurrentAvailabilityGroupID UNIQUEIDENTIFIER; + DECLARE @CurrentAvailabilityGroup NVARCHAR(MAX); + DECLARE @CurrentAvailabilityGroupRole NVARCHAR(MAX); + DECLARE @CurrentAvailabilityGroupBackupPreference NVARCHAR(MAX); + DECLARE @CurrentSecondaryRoleAllowConnections NVARCHAR(MAX); + DECLARE @CurrentIsPreferredBackupReplica BIT; + DECLARE @CurrentDatabaseMirroringRole NVARCHAR(MAX); + + DECLARE @CurrentFGID INT; + DECLARE @CurrentFileGroupID INT; + DECLARE @CurrentFileGroupName NVARCHAR(MAX); + DECLARE @CurrentFileGroupExists BIT; + + DECLARE @CurrentOID INT; + DECLARE @CurrentSchemaID INT; + DECLARE @CurrentSchemaName NVARCHAR(MAX); + DECLARE @CurrentObjectID INT; + DECLARE @CurrentObjectName NVARCHAR(MAX); + DECLARE @CurrentObjectType NVARCHAR(MAX); + DECLARE @CurrentObjectExists BIT; + + DECLARE @CurrentDatabaseContext NVARCHAR(MAX); + DECLARE @CurrentCommand NVARCHAR(MAX); + DECLARE @CurrentCommandOutput INT; + DECLARE @CurrentCommandType NVARCHAR(MAX); + + DECLARE @Errors TABLE ([Id] INT IDENTITY PRIMARY KEY, + [Message] NVARCHAR(MAX) NOT NULL, + [severity] INT NOT NULL, + [State] INT); + + DECLARE @CurrentMessage NVARCHAR(MAX); + DECLARE @CurrentSeverity INT; + DECLARE @CurrentState INT; + + DECLARE @tmpDatabases TABLE ([Id] INT IDENTITY, + DatabaseName NVARCHAR(MAX), + DatabaseType NVARCHAR(MAX), + AvailabilityGroup BIT, + [Snapshot] BIT, + StartPosition INT, + LastCommandTime DATETIME2, + DatabaseSize BIGINT, + LastGoodCheckDbTime DATETIME2, + [Order] INT, + Selected BIT, + Completed BIT, + PRIMARY KEY(Selected, Completed, [Order], [Id])); + + DECLARE @tmpAvailabilityGroups TABLE ([Id] INT IDENTITY PRIMARY KEY, + AvailabilityGroupName NVARCHAR(MAX), + StartPosition INT, + Selected BIT); + + DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName NVARCHAR(MAX), + AvailabilityGroupName NVARCHAR(MAX)); + + DECLARE @tmpFileGroups TABLE ([Id] INT IDENTITY, + FileGroupID INT, + FileGroupName NVARCHAR(MAX), + StartPosition INT, + [Order] INT, + Selected BIT, + Completed BIT, + PRIMARY KEY(Selected, Completed, [Order], [Id])); + + DECLARE @tmpObjects TABLE ([Id] INT IDENTITY, + SchemaID INT, + SchemaName NVARCHAR(MAX), + [objectid] INT, + ObjectName NVARCHAR(MAX), + ObjectType NVARCHAR(MAX), + StartPosition INT, + [Order] INT, + Selected BIT, + Completed BIT, + PRIMARY KEY(Selected, Completed, [Order], [Id])); + + DECLARE @SelectedDatabases TABLE (DatabaseName NVARCHAR(MAX), + DatabaseType NVARCHAR(MAX), + AvailabilityGroup NVARCHAR(MAX), + StartPosition INT, + Selected BIT); + + DECLARE @SelectedAvailabilityGroups TABLE (AvailabilityGroupName NVARCHAR(MAX), + StartPosition INT, + Selected BIT); + + DECLARE @SelectedFileGroups TABLE (DatabaseName NVARCHAR(MAX), + FileGroupName NVARCHAR(MAX), + StartPosition INT, + Selected BIT); + + DECLARE @SelectedObjects TABLE (DatabaseName NVARCHAR(MAX), + SchemaName NVARCHAR(MAX), + ObjectName NVARCHAR(MAX), + StartPosition INT, + Selected BIT); + + DECLARE @SelectedCheckCommands TABLE (CheckCommand NVARCHAR(MAX)); + + DECLARE @Error INT = 0; + DECLARE @ReturnCode INT = 0; + + DECLARE @EmptyLine NVARCHAR(MAX) = CHAR(9); + + DECLARE @Version NUMERIC(18,10) = CAST(LEFT(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)),CHARINDEX('.',CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX))) - 1) + '.' + REPLACE(RIGHT(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)), LEN(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX))) - CHARINDEX('.',CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)))),'.','') AS NUMERIC(18,10)); + + IF @Version >= 14 + BEGIN + SELECT @HostPlatform = [host_platform] + FROM [sys].[dm_os_host_info]; + END; + ELSE + BEGIN + SET @HostPlatform = 'Windows'; + END; + + DECLARE @AmazonRDS BIT = CASE WHEN SERVERPROPERTY('EngineEdition') IN (5, 8) THEN 0 WHEN EXISTS (SELECT * FROM [sys].[databases] WHERE [name] = 'rdsadmin') AND SUSER_SNAME(0x01) = 'rdsa' THEN 1 ELSE 0 END; + + ---------------------------------------------------------------------------------------------------- + --// Log initial information //-- + ---------------------------------------------------------------------------------------------------- + + SET @Parameters = '@Databases = ' + ISNULL('''' + REPLACE(@Databases,'''','''''') + '''','NULL'); + SET @Parameters += ', @CheckCommands = ' + ISNULL('''' + REPLACE(@CheckCommands,'''','''''') + '''','NULL'); + SET @Parameters += ', @PhysicalOnly = ' + ISNULL('''' + REPLACE(@PhysicalOnly,'''','''''') + '''','NULL'); + SET @Parameters += ', @DataPurity = ' + ISNULL('''' + REPLACE(@DataPurity,'''','''''') + '''','NULL'); + SET @Parameters += ', @NoIndex = ' + ISNULL('''' + REPLACE(@NoIndex,'''','''''') + '''','NULL'); + SET @Parameters += ', @ExtendedLogicalChecks = ' + ISNULL('''' + REPLACE(@ExtendedLogicalChecks,'''','''''') + '''','NULL'); + SET @Parameters += ', @NoInformationalMessages = ' + ISNULL('''' + REPLACE(@NoInformationalMessages,'''','''''') + '''','NULL'); + SET @Parameters += ', @TabLock = ' + ISNULL('''' + REPLACE(@TabLock,'''','''''') + '''','NULL'); + SET @Parameters += ', @FileGroups = ' + ISNULL('''' + REPLACE(@FileGroups,'''','''''') + '''','NULL'); + SET @Parameters += ', @Objects = ' + ISNULL('''' + REPLACE(@Objects,'''','''''') + '''','NULL'); + SET @Parameters += ', @MaxDOP = ' + ISNULL(CAST(@MaxDOP AS NVARCHAR),'NULL'); + SET @Parameters += ', @AvailabilityGroups = ' + ISNULL('''' + REPLACE(@AvailabilityGroups,'''','''''') + '''','NULL'); + SET @Parameters += ', @AvailabilityGroupReplicas = ' + ISNULL('''' + REPLACE(@AvailabilityGroupReplicas,'''','''''') + '''','NULL'); + SET @Parameters += ', @Updateability = ' + ISNULL('''' + REPLACE(@Updateability,'''','''''') + '''','NULL'); + SET @Parameters += ', @TimeLimit = ' + ISNULL(CAST(@TimeLimit AS NVARCHAR),'NULL'); + SET @Parameters += ', @LockTimeout = ' + ISNULL(CAST(@LockTimeout AS NVARCHAR),'NULL'); + SET @Parameters += ', @LockMessageSeverity = ' + ISNULL(CAST(@LockMessageSeverity AS NVARCHAR),'NULL'); + SET @Parameters += ', @StringDelimiter = ' + ISNULL('''' + REPLACE(@StringDelimiter,'''','''''') + '''','NULL'); + SET @Parameters += ', @DatabaseOrder = ' + ISNULL('''' + REPLACE(@DatabaseOrder,'''','''''') + '''','NULL'); + SET @Parameters += ', @DatabasesInParallel = ' + ISNULL('''' + REPLACE(@DatabasesInParallel,'''','''''') + '''','NULL'); + SET @Parameters += ', @LogToTable = ' + ISNULL('''' + REPLACE(@LogToTable,'''','''''') + '''','NULL'); + SET @Parameters += ', @Execute = ' + ISNULL('''' + REPLACE(@Execute,'''','''''') + '''','NULL'); + + SET @StartMessage = 'Date and time: ' + CONVERT(NVARCHAR,@StartTime,120); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Server: ' + CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(MAX)); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Version: ' + CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Edition: ' + CAST(SERVERPROPERTY('Edition') AS NVARCHAR(MAX)); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Platform: ' + @HostPlatform; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Procedure: ' + QUOTENAME(DB_NAME()) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Parameters: ' + @Parameters; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Version: ' + @VersionTimestamp; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Source: https://ola.hallengren.com'; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + ---------------------------------------------------------------------------------------------------- + --// Check core requirements //-- + ---------------------------------------------------------------------------------------------------- + + IF NOT (SELECT [compatibility_level] FROM [sys].[databases] WHERE [name] = DB_NAME()) >= 90 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The database ' + QUOTENAME(DB_NAME()) + ' has to be in compatibility level 90 or higher.', 16, 1; + END; + + IF NOT (SELECT [uses_ansi_nulls] FROM [sys].[sql_modules] WHERE [object_id] = @@PROCID) = 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'ANSI_NULLS has to be set to ON for the stored procedure.', 16, 1; + END; + + IF NOT (SELECT [uses_quoted_identifier] FROM [sys].[sql_modules] WHERE [object_id] = @@PROCID) = 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'QUOTED_IDENTIFIER has to be set to ON for the stored procedure.', 16, 1; + END; + + IF NOT EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'P' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'CommandExecute') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The stored procedure CommandExecute is missing. Download https://ola.hallengren.com/scripts/CommandExecute.sql.', 16, 1; + END; + + IF EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'P' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'CommandExecute' AND OBJECT_DEFINITION([objects].[object_id]) NOT LIKE '%@DatabaseContext%') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The stored procedure CommandExecute needs to be updated. Download https://ola.hallengren.com/scripts/CommandExecute.sql.', 16, 1; + END; + + IF @LogToTable = 'Y' AND NOT EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'U' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'CommandLog') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The table CommandLog is missing. Download https://ola.hallengren.com/scripts/CommandLog.sql.', 16, 1; + END; + + IF @DatabasesInParallel = 'Y' AND NOT EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'U' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'Queue') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The table Queue is missing. Download https://ola.hallengren.com/scripts/Queue.sql.', 16, 1; + END; + + IF @DatabasesInParallel = 'Y' AND NOT EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'U' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'QueueDatabase') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The table QueueDatabase is missing. Download https://ola.hallengren.com/scripts/QueueDatabase.sql.', 16, 1; + END; + + IF @@TRANCOUNT <> 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The transaction count is not 0.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Select databases //-- + ---------------------------------------------------------------------------------------------------- + + SET @Databases = REPLACE(@Databases, CHAR(10), ''); + SET @Databases = REPLACE(@Databases, CHAR(13), ''); + + WHILE CHARINDEX(@StringDelimiter + ' ', @Databases) > 0 SET @Databases = REPLACE(@Databases, @StringDelimiter + ' ', @StringDelimiter); + WHILE CHARINDEX(' ' + @StringDelimiter, @Databases) > 0 SET @Databases = REPLACE(@Databases, ' ' + @StringDelimiter, @StringDelimiter); + + SET @Databases = LTRIM(RTRIM(@Databases)); + + WITH Databases1 (StartPosition, EndPosition, DatabaseItem) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Databases, 1), 0), LEN(@Databases) + 1) AS EndPosition, + SUBSTRING(@Databases, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Databases, 1), 0), LEN(@Databases) + 1) - 1) AS DatabaseItem + WHERE @Databases IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Databases, EndPosition + 1), 0), LEN(@Databases) + 1) AS EndPosition, + SUBSTRING(@Databases, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Databases, EndPosition + 1), 0), LEN(@Databases) + 1) - EndPosition - 1) AS DatabaseItem + FROM Databases1 + WHERE EndPosition < LEN(@Databases) + 1 + ), + Databases2 (DatabaseItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN DatabaseItem LIKE '-%' THEN RIGHT(DatabaseItem,LEN(DatabaseItem) - 1) ELSE DatabaseItem END AS DatabaseItem, + StartPosition, + CASE WHEN DatabaseItem LIKE '-%' THEN 0 ELSE 1 END AS Selected + FROM Databases1 + ), + Databases3 (DatabaseItem, DatabaseType, AvailabilityGroup, StartPosition, Selected) AS + ( + SELECT CASE WHEN DatabaseItem IN('ALL_DATABASES','SYSTEM_DATABASES','USER_DATABASES','AVAILABILITY_GROUP_DATABASES') THEN '%' ELSE DatabaseItem END AS DatabaseItem, + CASE WHEN DatabaseItem = 'SYSTEM_DATABASES' THEN 'S' WHEN DatabaseItem = 'USER_DATABASES' THEN 'U' ELSE NULL END AS DatabaseType, + CASE WHEN DatabaseItem = 'AVAILABILITY_GROUP_DATABASES' THEN 1 ELSE NULL END AvailabilityGroup, + StartPosition, + Selected + FROM Databases2 + ), + Databases4 (DatabaseName, DatabaseType, AvailabilityGroup, StartPosition, Selected) AS + ( + SELECT CASE WHEN LEFT(DatabaseItem,1) = '[' AND RIGHT(DatabaseItem,1) = ']' THEN PARSENAME(DatabaseItem,1) ELSE DatabaseItem END AS DatabaseItem, + DatabaseType, + AvailabilityGroup, + StartPosition, + Selected + FROM Databases3 + ) + INSERT INTO @SelectedDatabases (DatabaseName, DatabaseType, AvailabilityGroup, StartPosition, Selected) + SELECT DatabaseName, + DatabaseType, + AvailabilityGroup, + StartPosition, + Selected + FROM Databases4 + OPTION (MAXRECURSION 0); + + IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + BEGIN + INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName, Selected) + SELECT [name] AS AvailabilityGroupName, + 0 AS Selected + FROM [sys].[availability_groups]; + + INSERT INTO @tmpDatabasesAvailabilityGroups (DatabaseName, AvailabilityGroupName) + SELECT [databases].[name], + [availability_groups].[name] + FROM [sys].[databases] [databases] + INNER JOIN [sys].[availability_replicas] [availability_replicas] ON [databases].[replica_id] = [availability_replicas].[replica_id] + INNER JOIN [sys].[availability_groups] [availability_groups] ON [availability_replicas].[group_id] = [availability_groups].[group_id]; + END; + + INSERT INTO @tmpDatabases (DatabaseName, DatabaseType, AvailabilityGroup, [Snapshot], [Order], Selected, Completed) + SELECT [name] AS DatabaseName, + CASE WHEN [name] IN('master','msdb','model') OR [is_distributor] = 1 THEN 'S' ELSE 'U' END AS DatabaseType, + NULL AS AvailabilityGroup, + CASE WHEN [source_database_id] IS NOT NULL THEN 1 ELSE 0 END AS [Snapshot], + 0 AS [Order], + 0 AS Selected, + 0 AS Completed + FROM [sys].[databases] + ORDER BY [name] ASC; + + UPDATE tmpDatabases + SET AvailabilityGroup = CASE WHEN EXISTS (SELECT * FROM @tmpDatabasesAvailabilityGroups WHERE DatabaseName = tmpDatabases.DatabaseName) THEN 1 ELSE 0 END + FROM @tmpDatabases tmpDatabases; + + UPDATE tmpDatabases + SET tmpDatabases.Selected = SelectedDatabases.Selected + FROM @tmpDatabases tmpDatabases + INNER JOIN @SelectedDatabases SelectedDatabases + ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + AND (tmpDatabases.DatabaseType = SelectedDatabases.DatabaseType OR SelectedDatabases.DatabaseType IS NULL) + AND (tmpDatabases.AvailabilityGroup = SelectedDatabases.AvailabilityGroup OR SelectedDatabases.AvailabilityGroup IS NULL) + AND NOT ((tmpDatabases.DatabaseName = 'tempdb' OR tmpDatabases.[Snapshot] = 1) AND tmpDatabases.DatabaseName <> SelectedDatabases.DatabaseName) + WHERE SelectedDatabases.Selected = 1; + + UPDATE tmpDatabases + SET tmpDatabases.Selected = SelectedDatabases.Selected + FROM @tmpDatabases tmpDatabases + INNER JOIN @SelectedDatabases SelectedDatabases + ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + AND (tmpDatabases.DatabaseType = SelectedDatabases.DatabaseType OR SelectedDatabases.DatabaseType IS NULL) + AND (tmpDatabases.AvailabilityGroup = SelectedDatabases.AvailabilityGroup OR SelectedDatabases.AvailabilityGroup IS NULL) + AND NOT ((tmpDatabases.DatabaseName = 'tempdb' OR tmpDatabases.[Snapshot] = 1) AND tmpDatabases.DatabaseName <> SelectedDatabases.DatabaseName) + WHERE SelectedDatabases.Selected = 0; + + UPDATE tmpDatabases + SET tmpDatabases.StartPosition = SelectedDatabases2.StartPosition + FROM @tmpDatabases tmpDatabases + INNER JOIN (SELECT tmpDatabases.DatabaseName, MIN(SelectedDatabases.StartPosition) AS StartPosition + FROM @tmpDatabases tmpDatabases + INNER JOIN @SelectedDatabases SelectedDatabases + ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + AND (tmpDatabases.DatabaseType = SelectedDatabases.DatabaseType OR SelectedDatabases.DatabaseType IS NULL) + AND (tmpDatabases.AvailabilityGroup = SelectedDatabases.AvailabilityGroup OR SelectedDatabases.AvailabilityGroup IS NULL) + WHERE SelectedDatabases.Selected = 1 + GROUP BY tmpDatabases.DatabaseName) SelectedDatabases2 + ON tmpDatabases.DatabaseName = SelectedDatabases2.DatabaseName; + + IF @Databases IS NOT NULL AND (NOT EXISTS(SELECT * FROM @SelectedDatabases) OR EXISTS(SELECT * FROM @SelectedDatabases WHERE DatabaseName IS NULL OR DATALENGTH(DatabaseName) = 0)) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Databases is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Select availability groups //-- + ---------------------------------------------------------------------------------------------------- + + IF @AvailabilityGroups IS NOT NULL AND @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + BEGIN + + SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, CHAR(10), ''); + SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, CHAR(13), ''); + + WHILE CHARINDEX(@StringDelimiter + ' ', @AvailabilityGroups) > 0 SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, @StringDelimiter + ' ', @StringDelimiter); + WHILE CHARINDEX(' ' + @StringDelimiter, @AvailabilityGroups) > 0 SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, ' ' + @StringDelimiter, @StringDelimiter); + + SET @AvailabilityGroups = LTRIM(RTRIM(@AvailabilityGroups)); + + WITH AvailabilityGroups1 (StartPosition, EndPosition, AvailabilityGroupItem) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @AvailabilityGroups, 1), 0), LEN(@AvailabilityGroups) + 1) AS EndPosition, + SUBSTRING(@AvailabilityGroups, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @AvailabilityGroups, 1), 0), LEN(@AvailabilityGroups) + 1) - 1) AS AvailabilityGroupItem + WHERE @AvailabilityGroups IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @AvailabilityGroups, EndPosition + 1), 0), LEN(@AvailabilityGroups) + 1) AS EndPosition, + SUBSTRING(@AvailabilityGroups, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @AvailabilityGroups, EndPosition + 1), 0), LEN(@AvailabilityGroups) + 1) - EndPosition - 1) AS AvailabilityGroupItem + FROM AvailabilityGroups1 + WHERE EndPosition < LEN(@AvailabilityGroups) + 1 + ), + AvailabilityGroups2 (AvailabilityGroupItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN AvailabilityGroupItem LIKE '-%' THEN RIGHT(AvailabilityGroupItem,LEN(AvailabilityGroupItem) - 1) ELSE AvailabilityGroupItem END AS AvailabilityGroupItem, + StartPosition, + CASE WHEN AvailabilityGroupItem LIKE '-%' THEN 0 ELSE 1 END AS Selected + FROM AvailabilityGroups1 + ), + AvailabilityGroups3 (AvailabilityGroupItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN AvailabilityGroupItem = 'ALL_AVAILABILITY_GROUPS' THEN '%' ELSE AvailabilityGroupItem END AS AvailabilityGroupItem, + StartPosition, + Selected + FROM AvailabilityGroups2 + ), + AvailabilityGroups4 (AvailabilityGroupName, StartPosition, Selected) AS + ( + SELECT CASE WHEN LEFT(AvailabilityGroupItem,1) = '[' AND RIGHT(AvailabilityGroupItem,1) = ']' THEN PARSENAME(AvailabilityGroupItem,1) ELSE AvailabilityGroupItem END AS AvailabilityGroupItem, + StartPosition, + Selected + FROM AvailabilityGroups3 + ) + INSERT INTO @SelectedAvailabilityGroups (AvailabilityGroupName, StartPosition, Selected) + SELECT AvailabilityGroupName, StartPosition, Selected + FROM AvailabilityGroups4 + OPTION (MAXRECURSION 0); + + UPDATE tmpAvailabilityGroups + SET tmpAvailabilityGroups.Selected = SelectedAvailabilityGroups.Selected + FROM @tmpAvailabilityGroups tmpAvailabilityGroups + INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + WHERE SelectedAvailabilityGroups.Selected = 1; + + UPDATE tmpAvailabilityGroups + SET tmpAvailabilityGroups.Selected = SelectedAvailabilityGroups.Selected + FROM @tmpAvailabilityGroups tmpAvailabilityGroups + INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + WHERE SelectedAvailabilityGroups.Selected = 0; + + UPDATE tmpAvailabilityGroups + SET tmpAvailabilityGroups.StartPosition = SelectedAvailabilityGroups2.StartPosition + FROM @tmpAvailabilityGroups tmpAvailabilityGroups + INNER JOIN (SELECT tmpAvailabilityGroups.AvailabilityGroupName, MIN(SelectedAvailabilityGroups.StartPosition) AS StartPosition + FROM @tmpAvailabilityGroups tmpAvailabilityGroups + INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + WHERE SelectedAvailabilityGroups.Selected = 1 + GROUP BY tmpAvailabilityGroups.AvailabilityGroupName) SelectedAvailabilityGroups2 + ON tmpAvailabilityGroups.AvailabilityGroupName = SelectedAvailabilityGroups2.AvailabilityGroupName; + + UPDATE tmpDatabases + SET tmpDatabases.StartPosition = tmpAvailabilityGroups.StartPosition, + tmpDatabases.Selected = 1 + FROM @tmpDatabases tmpDatabases + INNER JOIN @tmpDatabasesAvailabilityGroups tmpDatabasesAvailabilityGroups ON tmpDatabases.DatabaseName = tmpDatabasesAvailabilityGroups.DatabaseName + INNER JOIN @tmpAvailabilityGroups tmpAvailabilityGroups ON tmpDatabasesAvailabilityGroups.AvailabilityGroupName = tmpAvailabilityGroups.AvailabilityGroupName + WHERE tmpAvailabilityGroups.Selected = 1; + + END; + + IF @AvailabilityGroups IS NOT NULL AND (NOT EXISTS(SELECT * FROM @SelectedAvailabilityGroups) OR EXISTS(SELECT * FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName IS NULL OR AvailabilityGroupName = '') OR @Version < 11 OR SERVERPROPERTY('IsHadrEnabled') = 0) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AvailabilityGroups is not supported.', 16, 1; + END; + + IF (@Databases IS NULL AND @AvailabilityGroups IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'You need to specify one of the parameters @Databases and @AvailabilityGroups.', 16, 2; + END; + + IF (@Databases IS NOT NULL AND @AvailabilityGroups IS NOT NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'You can only specify one of the parameters @Databases and @AvailabilityGroups.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + --// Select filegroups //-- + ---------------------------------------------------------------------------------------------------- + + SET @FileGroups = REPLACE(@FileGroups, CHAR(10), ''); + SET @FileGroups = REPLACE(@FileGroups, CHAR(13), ''); + + WHILE CHARINDEX(@StringDelimiter + ' ', @FileGroups) > 0 SET @FileGroups = REPLACE(@FileGroups, @StringDelimiter + ' ', @StringDelimiter); + WHILE CHARINDEX(' ' + @StringDelimiter, @FileGroups) > 0 SET @FileGroups = REPLACE(@FileGroups, ' ' + @StringDelimiter, @StringDelimiter); + + SET @FileGroups = LTRIM(RTRIM(@FileGroups)); + + WITH FileGroups1 (StartPosition, EndPosition, FileGroupItem) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FileGroups, 1), 0), LEN(@FileGroups) + 1) AS EndPosition, + SUBSTRING(@FileGroups, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FileGroups, 1), 0), LEN(@FileGroups) + 1) - 1) AS FileGroupItem + WHERE @FileGroups IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FileGroups, EndPosition + 1), 0), LEN(@FileGroups) + 1) AS EndPosition, + SUBSTRING(@FileGroups, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FileGroups, EndPosition + 1), 0), LEN(@FileGroups) + 1) - EndPosition - 1) AS FileGroupItem + FROM FileGroups1 + WHERE EndPosition < LEN(@FileGroups) + 1 + ), + FileGroups2 (FileGroupItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN FileGroupItem LIKE '-%' THEN RIGHT(FileGroupItem,LEN(FileGroupItem) - 1) ELSE FileGroupItem END AS FileGroupItem, + StartPosition, + CASE WHEN FileGroupItem LIKE '-%' THEN 0 ELSE 1 END AS Selected + FROM FileGroups1 + ), + FileGroups3 (FileGroupItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN FileGroupItem = 'ALL_FILEGROUPS' THEN '%.%' ELSE FileGroupItem END AS FileGroupItem, + StartPosition, + Selected + FROM FileGroups2 + ), + FileGroups4 (DatabaseName, FileGroupName, StartPosition, Selected) AS + ( + SELECT CASE WHEN PARSENAME(FileGroupItem,4) IS NULL AND PARSENAME(FileGroupItem,3) IS NULL THEN PARSENAME(FileGroupItem,2) ELSE NULL END AS DatabaseName, + CASE WHEN PARSENAME(FileGroupItem,4) IS NULL AND PARSENAME(FileGroupItem,3) IS NULL THEN PARSENAME(FileGroupItem,1) ELSE NULL END AS FileGroupName, + StartPosition, + Selected + FROM FileGroups3 + ) + INSERT INTO @SelectedFileGroups (DatabaseName, FileGroupName, StartPosition, Selected) + SELECT DatabaseName, FileGroupName, StartPosition, Selected + FROM FileGroups4 + OPTION (MAXRECURSION 0); + + ---------------------------------------------------------------------------------------------------- + --// Select objects //-- + ---------------------------------------------------------------------------------------------------- + + SET @Objects = REPLACE(@Objects, CHAR(10), ''); + SET @Objects = REPLACE(@Objects, CHAR(13), ''); + + WHILE CHARINDEX(@StringDelimiter + ' ', @Objects) > 0 SET @Objects = REPLACE(@Objects, @StringDelimiter + ' ', @StringDelimiter); + WHILE CHARINDEX(' ' + @StringDelimiter, @Objects) > 0 SET @Objects = REPLACE(@Objects, ' ' + @StringDelimiter, @StringDelimiter); + + SET @Objects = LTRIM(RTRIM(@Objects)); + + WITH Objects1 (StartPosition, EndPosition, ObjectItem) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Objects, 1), 0), LEN(@Objects) + 1) AS EndPosition, + SUBSTRING(@Objects, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Objects, 1), 0), LEN(@Objects) + 1) - 1) AS ObjectItem + WHERE @Objects IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Objects, EndPosition + 1), 0), LEN(@Objects) + 1) AS EndPosition, + SUBSTRING(@Objects, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Objects, EndPosition + 1), 0), LEN(@Objects) + 1) - EndPosition - 1) AS ObjectItem + FROM Objects1 + WHERE EndPosition < LEN(@Objects) + 1 + ), + Objects2 (ObjectItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN ObjectItem LIKE '-%' THEN RIGHT(ObjectItem,LEN(ObjectItem) - 1) ELSE ObjectItem END AS ObjectItem, + StartPosition, + CASE WHEN ObjectItem LIKE '-%' THEN 0 ELSE 1 END AS Selected + FROM Objects1 + ), + Objects3 (ObjectItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN ObjectItem = 'ALL_OBJECTS' THEN '%.%.%' ELSE ObjectItem END AS ObjectItem, + StartPosition, + Selected + FROM Objects2 + ), + Objects4 (DatabaseName, SchemaName, ObjectName, StartPosition, Selected) AS + ( + SELECT CASE WHEN PARSENAME(ObjectItem,4) IS NULL THEN PARSENAME(ObjectItem,3) ELSE NULL END AS DatabaseName, + CASE WHEN PARSENAME(ObjectItem,4) IS NULL THEN PARSENAME(ObjectItem,2) ELSE NULL END AS SchemaName, + CASE WHEN PARSENAME(ObjectItem,4) IS NULL THEN PARSENAME(ObjectItem,1) ELSE NULL END AS ObjectName, + StartPosition, + Selected + FROM Objects3 + ) + INSERT INTO @SelectedObjects (DatabaseName, SchemaName, ObjectName, StartPosition, Selected) + SELECT DatabaseName, SchemaName, ObjectName, StartPosition, Selected + FROM Objects4 + OPTION (MAXRECURSION 0); + + ---------------------------------------------------------------------------------------------------- + --// Select check commands //-- + ---------------------------------------------------------------------------------------------------- + + SET @CheckCommands = REPLACE(@CheckCommands, @StringDelimiter + ' ', @StringDelimiter); + + WITH CheckCommands (StartPosition, EndPosition, CheckCommand) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @CheckCommands, 1), 0), LEN(@CheckCommands) + 1) AS EndPosition, + SUBSTRING(@CheckCommands, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @CheckCommands, 1), 0), LEN(@CheckCommands) + 1) - 1) AS CheckCommand + WHERE @CheckCommands IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @CheckCommands, EndPosition + 1), 0), LEN(@CheckCommands) + 1) AS EndPosition, + SUBSTRING(@CheckCommands, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @CheckCommands, EndPosition + 1), 0), LEN(@CheckCommands) + 1) - EndPosition - 1) AS CheckCommand + FROM CheckCommands + WHERE EndPosition < LEN(@CheckCommands) + 1 + ) + INSERT INTO @SelectedCheckCommands (CheckCommand) + SELECT CheckCommand + FROM CheckCommands + OPTION (MAXRECURSION 0); + + ---------------------------------------------------------------------------------------------------- + --// Check input parameters //-- + ---------------------------------------------------------------------------------------------------- + + IF EXISTS (SELECT * FROM @SelectedCheckCommands WHERE CheckCommand NOT IN('CHECKDB','CHECKFILEGROUP','CHECKALLOC','CHECKTABLE','CHECKCATALOG')) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CheckCommands is not supported.', 16, 1; + END; + + IF EXISTS (SELECT * FROM @SelectedCheckCommands GROUP BY CheckCommand HAVING COUNT(*) > 1) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CheckCommands is not supported.', 16, 2; + END; + + IF NOT EXISTS (SELECT * FROM @SelectedCheckCommands) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CheckCommands is not supported.' , 16, 3; + END; + + IF EXISTS (SELECT * FROM @SelectedCheckCommands WHERE CheckCommand IN('CHECKDB')) AND EXISTS (SELECT CheckCommand FROM @SelectedCheckCommands WHERE CheckCommand IN('CHECKFILEGROUP','CHECKALLOC','CHECKTABLE','CHECKCATALOG')) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CheckCommands is not supported.', 16, 4; + END; + + IF EXISTS (SELECT * FROM @SelectedCheckCommands WHERE CheckCommand IN('CHECKFILEGROUP')) AND EXISTS (SELECT CheckCommand FROM @SelectedCheckCommands WHERE CheckCommand IN('CHECKALLOC','CHECKTABLE')) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @CheckCommands is not supported.', 16, 5; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @PhysicalOnly NOT IN ('Y','N') OR @PhysicalOnly IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @PhysicalOnly is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DataPurity NOT IN ('Y','N') OR @DataPurity IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DataPurity is not supported.', 16, 1; + END; + + IF @PhysicalOnly = 'Y' AND @DataPurity = 'Y' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The parameters @PhysicalOnly and @DataPurity cannot be used together.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @NoIndex NOT IN ('Y','N') OR @NoIndex IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NoIndex is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @ExtendedLogicalChecks NOT IN ('Y','N') OR @ExtendedLogicalChecks IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ExtendedLogicalChecks is not supported.', 16, 1; + END; + + IF @PhysicalOnly = 'Y' AND @ExtendedLogicalChecks = 'Y' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The parameters @PhysicalOnly and @ExtendedLogicalChecks cannot be used together.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @NoInformationalMessages NOT IN ('Y','N') OR @NoInformationalMessages IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @NoInformationalMessages is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @TabLock NOT IN ('Y','N') OR @TabLock IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @TabLock is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS(SELECT * FROM @SelectedFileGroups WHERE DatabaseName IS NULL OR FileGroupName IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FileGroups is not supported.', 16, 1; + END; + + IF @FileGroups IS NOT NULL AND NOT EXISTS(SELECT * FROM @SelectedFileGroups) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FileGroups is not supported.', 16, 2; + END; + + IF @FileGroups IS NOT NULL AND NOT EXISTS (SELECT * FROM @SelectedCheckCommands WHERE CheckCommand = 'CHECKFILEGROUP') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FileGroups is not supported.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS(SELECT * FROM @SelectedObjects WHERE DatabaseName IS NULL OR SchemaName IS NULL OR ObjectName IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Objects is not supported.', 16, 1; + END; + + IF (@Objects IS NOT NULL AND NOT EXISTS(SELECT * FROM @SelectedObjects)) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Objects is not supported.', 16, 2; + END; + + IF (@Objects IS NOT NULL AND NOT EXISTS (SELECT * FROM @SelectedCheckCommands WHERE CheckCommand = 'CHECKTABLE')) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Objects is not supported.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @MaxDOP < 0 OR @MaxDOP > 64 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MaxDOP is not supported.', 16, 1; + END; + + IF @MaxDOP IS NOT NULL AND NOT (@Version >= 12.050000 OR SERVERPROPERTY('EngineEdition') IN (5, 8)) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MaxDOP is not supported. MAXDOP is not available in this version of SQL Server.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @AvailabilityGroupReplicas NOT IN('ALL','PRIMARY','SECONDARY','PREFERRED_BACKUP_REPLICA') OR @AvailabilityGroupReplicas IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AvailabilityGroupReplicas is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Updateability NOT IN('READ_ONLY','READ_WRITE','ALL') OR @Updateability IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Updateability is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @TimeLimit < 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @TimeLimit is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @LockTimeout < 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @LockTimeout is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @LockMessageSeverity NOT IN(10, 16) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @LockMessageSeverity is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) > 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @StringDelimiter is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DatabaseOrder NOT IN('DATABASE_NAME_ASC','DATABASE_NAME_DESC','DATABASE_SIZE_ASC','DATABASE_SIZE_DESC','DATABASE_LAST_GOOD_CHECK_ASC','DATABASE_LAST_GOOD_CHECK_DESC','REPLICA_LAST_GOOD_CHECK_ASC','REPLICA_LAST_GOOD_CHECK_DESC') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabaseOrder is not supported.', 16, 1; + END; + + IF @DatabaseOrder IN('DATABASE_LAST_GOOD_CHECK_ASC','DATABASE_LAST_GOOD_CHECK_DESC') AND NOT ((@Version >= 12.06024 AND @Version < 13) OR (@Version >= 13.05026 AND @Version < 14) OR @Version >= 14.0302916 OR SERVERPROPERTY('EngineEdition') = 8) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabaseOrder is not supported. DATABASEPROPERTYEX(''DatabaseName'', ''LastGoodCheckDbTime'') is not available in this version of SQL Server.', 16, 2; + END; + + IF @DatabaseOrder IN('REPLICA_LAST_GOOD_CHECK_ASC','REPLICA_LAST_GOOD_CHECK_DESC') AND @LogToTable = 'N' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabaseOrder is not supported. You need to provide the parameter @LogToTable = ''Y''.', 16, 3; + END; + + IF @DatabaseOrder IN('DATABASE_LAST_GOOD_CHECK_ASC','DATABASE_LAST_GOOD_CHECK_DESC','REPLICA_LAST_GOOD_CHECK_ASC','REPLICA_LAST_GOOD_CHECK_DESC') AND @CheckCommands <> 'CHECKDB' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabaseOrder is not supported. You need to provide the parameter @CheckCommands = ''CHECKDB''.', 16, 4; + END; + + IF @DatabaseOrder IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 5 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabaseOrder is not supported. This parameter is not supported in Azure SQL Database.', 16, 5; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DatabasesInParallel NOT IN('Y','N') OR @DatabasesInParallel IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabasesInParallel is not supported.', 16, 1; + END; + + IF @DatabasesInParallel = 'Y' AND SERVERPROPERTY('EngineEdition') = 5 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabasesInParallel is not supported. This parameter is not supported in Azure SQL Database.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @LogToTable NOT IN('Y','N') OR @LogToTable IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @LogToTable is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Execute NOT IN('Y','N') OR @Execute IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Execute is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS(SELECT * FROM @Errors) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The documentation is available at https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Check that selected databases and availability groups exist //-- + ---------------------------------------------------------------------------------------------------- + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + ', ' + FROM @SelectedDatabases + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases); + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The following databases in the @Databases parameter do not exist: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 10, 1; + END; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + ', ' + FROM @SelectedFileGroups + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases); + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The following databases in the @FileGroups parameter do not exist: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 10, 1; + END; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + ', ' + FROM @SelectedObjects + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases); + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The following databases in the @Objects parameter do not exist: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 10, 1; + END; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(AvailabilityGroupName) + ', ' + FROM @SelectedAvailabilityGroups + WHERE AvailabilityGroupName NOT LIKE '%[%]%' + AND AvailabilityGroupName NOT IN (SELECT AvailabilityGroupName FROM @tmpAvailabilityGroups); + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The following availability groups do not exist: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 10, 1; + END; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + ', ' + FROM @SelectedFileGroups + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName IN (SELECT DatabaseName FROM @tmpDatabases) + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases WHERE Selected = 1); + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The following databases have been selected in the @FileGroups parameter, but not in the @Databases or @AvailabilityGroups parameters: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 10, 1; + END; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + ', ' + FROM @SelectedObjects + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName IN (SELECT DatabaseName FROM @tmpDatabases) + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases WHERE Selected = 1); + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The following databases have been selected in the @Objects parameter, but not in the @Databases or @AvailabilityGroups parameters: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 10, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Check @@SERVERNAME //-- + ---------------------------------------------------------------------------------------------------- + + IF UPPER(@@SERVERNAME) <> UPPER(CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(MAX))) AND SERVERPROPERTY('IsHadrEnabled') = 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The @@SERVERNAME does not match SERVERPROPERTY(''ServerName''). See ' + CASE WHEN SERVERPROPERTY('IsClustered') = 0 THEN 'https://docs.microsoft.com/en-us/sql/database-engine/install-windows/rename-a-computer-that-hosts-a-stand-alone-instance-of-sql-server' WHEN SERVERPROPERTY('IsClustered') = 1 THEN 'https://docs.microsoft.com/en-us/sql/sql-server/failover-clusters/install/rename-a-sql-server-failover-cluster-instance' END + '.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Raise errors //-- + ---------------------------------------------------------------------------------------------------- + + DECLARE ErrorCursor CURSOR FAST_FORWARD FOR SELECT [Message], [severity], [State] FROM @Errors ORDER BY [ID] ASC; + + OPEN ErrorCursor; + + FETCH ErrorCursor INTO @CurrentMessage, @CurrentSeverity, @CurrentState; + + WHILE @@FETCH_STATUS = 0 + BEGIN + RAISERROR('%s', @CurrentSeverity, @CurrentState, @CurrentMessage) WITH NOWAIT; + RAISERROR(@EmptyLine, 10, 1) WITH NOWAIT; + + FETCH NEXT FROM ErrorCursor INTO @CurrentMessage, @CurrentSeverity, @CurrentState; + END; + + CLOSE ErrorCursor; + + DEALLOCATE ErrorCursor; + + IF EXISTS (SELECT * FROM @Errors WHERE [severity] >= 16) + BEGIN + SET @ReturnCode = 50000; + GOTO Logging; + END; + + ---------------------------------------------------------------------------------------------------- + --// Update database order //-- + ---------------------------------------------------------------------------------------------------- + + IF @DatabaseOrder IN('DATABASE_SIZE_ASC','DATABASE_SIZE_DESC') + BEGIN + UPDATE tmpDatabases + SET DatabaseSize = (SELECT SUM(CAST(SIZE AS BIGINT)) FROM [sys].[master_files] WHERE [type] = 0 AND [database_id] = DB_ID(tmpDatabases.DatabaseName)) + FROM @tmpDatabases tmpDatabases; + END; + + IF @DatabaseOrder IN('DATABASE_LAST_GOOD_CHECK_ASC','DATABASE_LAST_GOOD_CHECK_DESC') + BEGIN + UPDATE tmpDatabases + SET LastGoodCheckDbTime = NULLIF(CAST(DATABASEPROPERTYEX (DatabaseName,'LastGoodCheckDbTime') AS DATETIME2),'1900-01-01 00:00:00.000') + FROM @tmpDatabases tmpDatabases; + END; + + IF @DatabaseOrder IN('REPLICA_LAST_GOOD_CHECK_ASC','REPLICA_LAST_GOOD_CHECK_DESC') + BEGIN + UPDATE tmpDatabases + SET LastCommandTime = MaxStartTime + FROM @tmpDatabases tmpDatabases + INNER JOIN (SELECT DatabaseName, MAX([starttime]) AS MaxStartTime + FROM [dbo].CommandLog + WHERE CommandType = 'DBCC_CHECKDB' + AND ErrorNumber = 0 + GROUP BY DatabaseName) CommandLog + ON tmpDatabases.DatabaseName = CommandLog.DatabaseName COLLATE DATABASE_DEFAULT; + END; + + IF @DatabaseOrder IS NULL + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY StartPosition ASC, DatabaseName ASC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_NAME_ASC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY DatabaseName ASC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_NAME_DESC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY DatabaseName DESC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_SIZE_ASC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY DatabaseSize ASC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_SIZE_DESC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY DatabaseSize DESC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_LAST_GOOD_CHECK_ASC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY LastGoodCheckDbTime ASC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_LAST_GOOD_CHECK_DESC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY LastGoodCheckDbTime DESC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'REPLICA_LAST_GOOD_CHECK_ASC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY LastCommandTime ASC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'REPLICA_LAST_GOOD_CHECK_DESC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY LastCommandTime DESC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + + ---------------------------------------------------------------------------------------------------- + --// Update the queue //-- + ---------------------------------------------------------------------------------------------------- + + IF @DatabasesInParallel = 'Y' + BEGIN + + BEGIN TRY + + SELECT @QueueID = QueueID + FROM [dbo].[Queue] + WHERE SchemaName = @SchemaName + AND ObjectName = @ObjectName + AND [Parameters] = @Parameters; + + IF @QueueID IS NULL + BEGIN + BEGIN TRANSACTION; + + SELECT @QueueID = QueueID + FROM [dbo].[Queue] WITH (UPDLOCK, HOLDLOCK) + WHERE SchemaName = @SchemaName + AND ObjectName = @ObjectName + AND [Parameters] = @Parameters; + + IF @QueueID IS NULL + BEGIN + INSERT INTO [dbo].[Queue] (SchemaName, ObjectName, [Parameters]) + SELECT @SchemaName, @ObjectName, @Parameters; + + SET @QueueID = SCOPE_IDENTITY(); + END; + + COMMIT TRANSACTION; + END; + + BEGIN TRANSACTION; + + UPDATE [Queue] + SET QueueStartTime = SYSDATETIME(), + SessionID = @@SPID, + RequestID = (SELECT [request_id] FROM [sys].[dm_exec_requests] WHERE [session_id] = @@SPID), + RequestStartTime = (SELECT [start_time] FROM [sys].[dm_exec_requests] WHERE [session_id] = @@SPID) + FROM [dbo].[Queue] [Queue] + WHERE QueueID = @QueueID + AND NOT EXISTS (SELECT * + FROM [sys].[dm_exec_requests] + WHERE [session_id] = [Queue].SessionID + AND [request_id] = [Queue].RequestID + AND [start_time] = [Queue].RequestStartTime) + AND NOT EXISTS (SELECT * + FROM [dbo].QueueDatabase QueueDatabase + INNER JOIN [sys].[dm_exec_requests] ON QueueDatabase.SessionID = [session_id] AND QueueDatabase.RequestID = [request_id] AND QueueDatabase.RequestStartTime = [start_time] + WHERE QueueDatabase.QueueID = @QueueID); + + IF @@ROWCOUNT = 1 + BEGIN + INSERT INTO [dbo].QueueDatabase (QueueID, DatabaseName) + SELECT @QueueID AS QueueID, + DatabaseName + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + AND NOT EXISTS (SELECT * FROM [dbo].QueueDatabase WHERE DatabaseName = tmpDatabases.DatabaseName AND QueueID = @QueueID); + + DELETE QueueDatabase + FROM [dbo].QueueDatabase QueueDatabase + WHERE QueueID = @QueueID + AND NOT EXISTS (SELECT * FROM @tmpDatabases tmpDatabases WHERE DatabaseName = QueueDatabase.DatabaseName AND Selected = 1); + + UPDATE QueueDatabase + SET DatabaseOrder = tmpDatabases.[Order] + FROM [dbo].QueueDatabase QueueDatabase + INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName = tmpDatabases.DatabaseName + WHERE QueueID = @QueueID; + END; + + COMMIT TRANSACTION; + + SELECT @QueueStartTime = QueueStartTime + FROM [dbo].[Queue] + WHERE QueueID = @QueueID; + + END TRY + + BEGIN CATCH + IF XACT_STATE() <> 0 + BEGIN + ROLLBACK TRANSACTION; + END; + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS NVARCHAR) + ', ' + ISNULL(ERROR_MESSAGE(),''); + RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + SET @ReturnCode = ERROR_NUMBER(); + GOTO Logging; + END CATCH; + + END; + + ---------------------------------------------------------------------------------------------------- + --// Execute commands //-- + ---------------------------------------------------------------------------------------------------- + + WHILE (1 = 1) + BEGIN + + IF @DatabasesInParallel = 'Y' + BEGIN + UPDATE QueueDatabase + SET DatabaseStartTime = NULL, + SessionID = NULL, + RequestID = NULL, + RequestStartTime = NULL + FROM [dbo].QueueDatabase QueueDatabase + WHERE QueueID = @QueueID + AND DatabaseStartTime IS NOT NULL + AND DatabaseEndTime IS NULL + AND NOT EXISTS (SELECT * FROM [sys].[dm_exec_requests] WHERE [session_id] = QueueDatabase.SessionID AND [request_id] = QueueDatabase.RequestID AND [start_time] = QueueDatabase.RequestStartTime); + + UPDATE QueueDatabase + SET DatabaseStartTime = SYSDATETIME(), + DatabaseEndTime = NULL, + SessionID = @@SPID, + RequestID = (SELECT [request_id] FROM [sys].[dm_exec_requests] WHERE [session_id] = @@SPID), + RequestStartTime = (SELECT [start_time] FROM [sys].[dm_exec_requests] WHERE [session_id] = @@SPID), + @CurrentDatabaseName = DatabaseName + FROM (SELECT TOP 1 DatabaseStartTime, + DatabaseEndTime, + SessionID, + RequestID, + RequestStartTime, + DatabaseName + FROM [dbo].QueueDatabase + WHERE QueueID = @QueueID + AND (DatabaseStartTime < @QueueStartTime OR DatabaseStartTime IS NULL) + AND NOT (DatabaseStartTime IS NOT NULL AND DatabaseEndTime IS NULL) + ORDER BY DatabaseOrder ASC + ) QueueDatabase; + END; + ELSE + BEGIN + SELECT TOP 1 @CurrentDBID = [Id], + @CurrentDatabaseName = DatabaseName + FROM @tmpDatabases + WHERE Selected = 1 + AND Completed = 0 + ORDER BY [Order] ASC; + END; + + IF @@ROWCOUNT = 0 + BEGIN + BREAK; + END; + + SET @CurrentDatabase_sp_executesql = QUOTENAME(@CurrentDatabaseName) + '.sys.sp_executesql'; + + BEGIN + SET @DatabaseMessage = 'Date and time: ' + CONVERT(NVARCHAR,SYSDATETIME(),120); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Database: ' + QUOTENAME(@CurrentDatabaseName); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + SELECT @CurrentUserAccess = [user_access_desc], + @CurrentIsReadOnly = [is_read_only], + @CurrentDatabaseState = [state_desc], + @CurrentInStandby = [is_in_standby], + @CurrentRecoveryModel = [recovery_model_desc] + FROM [sys].[databases] + WHERE [name] = @CurrentDatabaseName; + + BEGIN + SET @DatabaseMessage = 'State: ' + @CurrentDatabaseState; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Standby: ' + CASE WHEN @CurrentInStandby = 1 THEN 'Yes' ELSE 'No' END; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Updateability: ' + CASE WHEN @CurrentIsReadOnly = 1 THEN 'READ_ONLY' WHEN @CurrentIsReadOnly = 0 THEN 'READ_WRITE' END; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'User access: ' + @CurrentUserAccess; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Recovery model: ' + @CurrentRecoveryModel; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + BEGIN + SELECT @CurrentReplicaID = [databases].[replica_id] + FROM [sys].[databases] [databases] + INNER JOIN [sys].[availability_replicas] [availability_replicas] ON [databases].[replica_id] = [availability_replicas].[replica_id] + WHERE [databases].[name] = @CurrentDatabaseName; + + SELECT @CurrentAvailabilityGroupID = [group_id], + @CurrentSecondaryRoleAllowConnections = [secondary_role_allow_connections_desc] + FROM [sys].[availability_replicas] + WHERE [replica_id] = @CurrentReplicaID; + + SELECT @CurrentAvailabilityGroupRole = [role_desc] + FROM [sys].[dm_hadr_availability_replica_states] + WHERE [replica_id] = @CurrentReplicaID; + + SELECT @CurrentAvailabilityGroup = [name], + @CurrentAvailabilityGroupBackupPreference = UPPER([automated_backup_preference_desc]) + FROM [sys].[availability_groups] + WHERE [group_id] = @CurrentAvailabilityGroupID; + END; + + IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 AND @CurrentAvailabilityGroup IS NOT NULL AND @AvailabilityGroupReplicas = 'PREFERRED_BACKUP_REPLICA' + BEGIN + SELECT @CurrentIsPreferredBackupReplica = [sys].fn_hadr_backup_is_preferred_replica(@CurrentDatabaseName); + END; + + IF SERVERPROPERTY('EngineEdition') <> 5 + BEGIN + SELECT @CurrentDatabaseMirroringRole = UPPER([mirroring_role_desc]) + FROM [sys].[database_mirroring] [database_mirroring] + INNER JOIN [sys].[databases] [databases] ON [database_mirroring].[database_id] = [databases].[database_id] + WHERE [databases].[name] = @CurrentDatabaseName; + END; + + IF @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Availability group: ' + ISNULL(@CurrentAvailabilityGroup,'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Availability group role: ' + ISNULL(@CurrentAvailabilityGroupRole,'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + IF @CurrentAvailabilityGroupRole = 'SECONDARY' + BEGIN + SET @DatabaseMessage = 'Readable Secondary: ' + ISNULL(@CurrentSecondaryRoleAllowConnections,'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + IF @AvailabilityGroupReplicas = 'PREFERRED_BACKUP_REPLICA' + BEGIN + SET @DatabaseMessage = 'Availability group backup preference: ' + ISNULL(@CurrentAvailabilityGroupBackupPreference,'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Is preferred backup replica: ' + CASE WHEN @CurrentIsPreferredBackupReplica = 1 THEN 'Yes' WHEN @CurrentIsPreferredBackupReplica = 0 THEN 'No' ELSE 'N/A' END; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + END; + + IF @CurrentDatabaseMirroringRole IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Database mirroring role: ' + @CurrentDatabaseMirroringRole; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + IF @CurrentDatabaseState IN('ONLINE','EMERGENCY') + AND NOT (@CurrentUserAccess = 'SINGLE_USER') + AND (@CurrentAvailabilityGroupRole = 'PRIMARY' OR @CurrentAvailabilityGroupRole IS NULL OR SERVERPROPERTY('EngineEdition') = 3) + AND ((@AvailabilityGroupReplicas = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY') OR (@AvailabilityGroupReplicas = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY') OR (@AvailabilityGroupReplicas = 'PREFERRED_BACKUP_REPLICA' AND @CurrentIsPreferredBackupReplica = 1) OR @AvailabilityGroupReplicas = 'ALL' OR @CurrentAvailabilityGroupRole IS NULL) + AND NOT (@CurrentIsReadOnly = 1 AND @Updateability = 'READ_WRITE') + AND NOT (@CurrentIsReadOnly = 0 AND @Updateability = 'READ_ONLY') + AND NOT (@AmazonRDS = 1 AND @CurrentDatabaseName = 'rdsadmin') + BEGIN + + -- Check database + IF EXISTS(SELECT * FROM @SelectedCheckCommands WHERE CheckCommand = 'CHECKDB') AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SET @CurrentDatabaseContext = CASE WHEN SERVERPROPERTY('EngineEdition') = 5 THEN @CurrentDatabaseName ELSE 'master' END; + + SET @CurrentCommandType = 'DBCC_CHECKDB'; + + SET @CurrentCommand = ''; + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS NVARCHAR) + '; '; + SET @CurrentCommand += 'DBCC CHECKDB (' + QUOTENAME(@CurrentDatabaseName); + IF @NoIndex = 'Y' SET @CurrentCommand += ', NOINDEX'; + SET @CurrentCommand += ') WITH ALL_ERRORMSGS'; + IF @DataPurity = 'Y' SET @CurrentCommand += ', DATA_PURITY'; + IF @PhysicalOnly = 'Y' SET @CurrentCommand += ', PHYSICAL_ONLY'; + IF @ExtendedLogicalChecks = 'Y' SET @CurrentCommand += ', EXTENDED_LOGICAL_CHECKS'; + IF @NoInformationalMessages = 'Y' SET @CurrentCommand += ', NO_INFOMSGS'; + IF @TabLock = 'Y' SET @CurrentCommand += ', TABLOCK'; + IF @MaxDOP IS NOT NULL SET @CurrentCommand += ', MAXDOP = ' + CAST(@MaxDOP AS NVARCHAR); + + EXECUTE @CurrentCommandOutput = [dbo].CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @LogToTable = @LogToTable, @Execute = @Execute; + SET @Error = @@ERROR; + IF @Error <> 0 SET @CurrentCommandOutput = @Error; + IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput; + END; + + -- Check filegroups + IF EXISTS(SELECT * FROM @SelectedCheckCommands WHERE CheckCommand = 'CHECKFILEGROUP') + AND (@CurrentAvailabilityGroupRole = 'PRIMARY' OR (@CurrentAvailabilityGroupRole = 'SECONDARY' AND @CurrentSecondaryRoleAllowConnections = 'ALL') OR @CurrentAvailabilityGroupRole IS NULL) + AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT data_space_id AS FileGroupID, name AS FileGroupName, 0 AS [Order], 0 AS Selected, 0 AS Completed FROM sys.filegroups filegroups WHERE [type] <> ''FX'' ORDER BY CASE WHEN filegroups.name = ''PRIMARY'' THEN 1 ELSE 0 END DESC, filegroups.name ASC'; + + INSERT INTO @tmpFileGroups (FileGroupID, FileGroupName, [Order], Selected, Completed) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand; + SET @Error = @@ERROR; + IF @Error <> 0 SET @ReturnCode = @Error; + + IF @FileGroups IS NULL + BEGIN + UPDATE tmpFileGroups + SET tmpFileGroups.Selected = 1 + FROM @tmpFileGroups tmpFileGroups; + END; + ELSE + BEGIN + UPDATE tmpFileGroups + SET tmpFileGroups.Selected = SelectedFileGroups.Selected + FROM @tmpFileGroups tmpFileGroups + INNER JOIN @SelectedFileGroups SelectedFileGroups + ON @CurrentDatabaseName LIKE REPLACE(SelectedFileGroups.DatabaseName,'_','[_]') AND tmpFileGroups.FileGroupName LIKE REPLACE(SelectedFileGroups.FileGroupName,'_','[_]') + WHERE SelectedFileGroups.Selected = 1; + + UPDATE tmpFileGroups + SET tmpFileGroups.Selected = SelectedFileGroups.Selected + FROM @tmpFileGroups tmpFileGroups + INNER JOIN @SelectedFileGroups SelectedFileGroups + ON @CurrentDatabaseName LIKE REPLACE(SelectedFileGroups.DatabaseName,'_','[_]') AND tmpFileGroups.FileGroupName LIKE REPLACE(SelectedFileGroups.FileGroupName,'_','[_]') + WHERE SelectedFileGroups.Selected = 0; + + UPDATE tmpFileGroups + SET tmpFileGroups.StartPosition = SelectedFileGroups2.StartPosition + FROM @tmpFileGroups tmpFileGroups + INNER JOIN (SELECT tmpFileGroups.FileGroupName, MIN(SelectedFileGroups.StartPosition) AS StartPosition + FROM @tmpFileGroups tmpFileGroups + INNER JOIN @SelectedFileGroups SelectedFileGroups + ON @CurrentDatabaseName LIKE REPLACE(SelectedFileGroups.DatabaseName,'_','[_]') AND tmpFileGroups.FileGroupName LIKE REPLACE(SelectedFileGroups.FileGroupName,'_','[_]') + WHERE SelectedFileGroups.Selected = 1 + GROUP BY tmpFileGroups.FileGroupName) SelectedFileGroups2 + ON tmpFileGroups.FileGroupName = SelectedFileGroups2.FileGroupName; + END; + + WITH tmpFileGroups AS ( + SELECT FileGroupName, [Order], ROW_NUMBER() OVER (ORDER BY StartPosition ASC, FileGroupName ASC) AS RowNumber + FROM @tmpFileGroups tmpFileGroups + WHERE Selected = 1 + ) + UPDATE tmpFileGroups + SET [Order] = RowNumber; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + '.' + QUOTENAME(FileGroupName) + ', ' + FROM @SelectedFileGroups SelectedFileGroups + WHERE DatabaseName = @CurrentDatabaseName + AND FileGroupName NOT LIKE '%[%]%' + AND NOT EXISTS (SELECT * FROM @tmpFileGroups WHERE FileGroupName = SelectedFileGroups.FileGroupName); + IF @@ROWCOUNT > 0 + BEGIN + SET @ErrorMessage = 'The following file groups do not exist: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.'; + RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT; + SET @Error = @@ERROR; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + END; + + WHILE (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SELECT TOP 1 @CurrentFGID = [Id], + @CurrentFileGroupID = FileGroupID, + @CurrentFileGroupName = FileGroupName + FROM @tmpFileGroups + WHERE Selected = 1 + AND Completed = 0 + ORDER BY [Order] ASC; + + IF @@ROWCOUNT = 0 + BEGIN + BREAK; + END; + + -- Does the filegroup exist? + SET @CurrentCommand = ''; + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS NVARCHAR) + '; '; + SET @CurrentCommand += 'IF EXISTS(SELECT * FROM sys.filegroups filegroups WHERE [type] <> ''FX'' AND filegroups.data_space_id = @ParamFileGroupID AND filegroups.[name] = @ParamFileGroupName) BEGIN SET @ParamFileGroupExists = 1 END'; + + BEGIN TRY + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamFileGroupID int, @ParamFileGroupName sysname, @ParamFileGroupExists bit OUTPUT', @ParamFileGroupID = @CurrentFileGroupID, @ParamFileGroupName = @CurrentFileGroupName, @ParamFileGroupExists = @CurrentFileGroupExists OUTPUT; + + IF @CurrentFileGroupExists IS NULL SET @CurrentFileGroupExists = 0; + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS NVARCHAR) + ', ' + ISNULL(ERROR_MESSAGE(),'') + CASE WHEN ERROR_NUMBER() = 1222 THEN ', ' + ' The file group ' + QUOTENAME(@CurrentFileGroupName) + ' in the database ' + QUOTENAME(@CurrentDatabaseName) + ' is locked. It could not be checked if the filegroup exists.' ELSE '' END; + SET @Severity = CASE WHEN ERROR_NUMBER() IN(1205,1222) THEN @LockMessageSeverity ELSE 16 END; + RAISERROR('%s',@Severity,1,@ErrorMessage) WITH NOWAIT; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + IF NOT (ERROR_NUMBER() IN(1205,1222) AND @LockMessageSeverity = 10) + BEGIN + SET @ReturnCode = ERROR_NUMBER(); + END; + END CATCH; + + IF @CurrentFileGroupExists = 1 + BEGIN + SET @CurrentDatabaseContext = @CurrentDatabaseName; + + SET @CurrentCommandType = 'DBCC_CHECKFILEGROUP'; + + SET @CurrentCommand = ''; + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS NVARCHAR) + '; '; + SET @CurrentCommand += 'DBCC CHECKFILEGROUP (' + QUOTENAME(@CurrentFileGroupName); + IF @NoIndex = 'Y' SET @CurrentCommand += ', NOINDEX'; + SET @CurrentCommand += ') WITH ALL_ERRORMSGS'; + IF @PhysicalOnly = 'Y' SET @CurrentCommand += ', PHYSICAL_ONLY'; + IF @NoInformationalMessages = 'Y' SET @CurrentCommand += ', NO_INFOMSGS'; + IF @TabLock = 'Y' SET @CurrentCommand += ', TABLOCK'; + IF @MaxDOP IS NOT NULL SET @CurrentCommand += ', MAXDOP = ' + CAST(@MaxDOP AS NVARCHAR); + + EXECUTE @CurrentCommandOutput = [dbo].CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @LogToTable = @LogToTable, @Execute = @Execute; + SET @Error = @@ERROR; + IF @Error <> 0 SET @CurrentCommandOutput = @Error; + IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput; + END; + + UPDATE @tmpFileGroups + SET Completed = 1 + WHERE Selected = 1 + AND Completed = 0 + AND [Id] = @CurrentFGID; + + SET @CurrentFGID = NULL; + SET @CurrentFileGroupID = NULL; + SET @CurrentFileGroupName = NULL; + SET @CurrentFileGroupExists = NULL; + + SET @CurrentDatabaseContext = NULL; + SET @CurrentCommand = NULL; + SET @CurrentCommandOutput = NULL; + SET @CurrentCommandType = NULL; + END; + END; + + -- Check disk space allocation structures + IF EXISTS(SELECT * FROM @SelectedCheckCommands WHERE CheckCommand = 'CHECKALLOC') AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SET @CurrentDatabaseContext = CASE WHEN SERVERPROPERTY('EngineEdition') = 5 THEN @CurrentDatabaseName ELSE 'master' END; + + SET @CurrentCommandType = 'DBCC_CHECKALLOC'; + + SET @CurrentCommand = ''; + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS NVARCHAR) + '; '; + SET @CurrentCommand += 'DBCC CHECKALLOC (' + QUOTENAME(@CurrentDatabaseName); + SET @CurrentCommand += ') WITH ALL_ERRORMSGS'; + IF @NoInformationalMessages = 'Y' SET @CurrentCommand += ', NO_INFOMSGS'; + IF @TabLock = 'Y' SET @CurrentCommand += ', TABLOCK'; + + EXECUTE @CurrentCommandOutput = [dbo].CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @LogToTable = @LogToTable, @Execute = @Execute; + SET @Error = @@ERROR; + IF @Error <> 0 SET @CurrentCommandOutput = @Error; + IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput; + END; + + -- Check objects + IF EXISTS(SELECT * FROM @SelectedCheckCommands WHERE CheckCommand = 'CHECKTABLE') + AND (@CurrentAvailabilityGroupRole = 'PRIMARY' OR (@CurrentAvailabilityGroupRole = 'SECONDARY' AND @CurrentSecondaryRoleAllowConnections = 'ALL') OR @CurrentAvailabilityGroupRole IS NULL) + AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT schemas.[schema_id] AS SchemaID, schemas.[name] AS SchemaName, objects.[object_id] AS ObjectID, objects.[name] AS ObjectName, RTRIM(objects.[type]) AS ObjectType, 0 AS [Order], 0 AS Selected, 0 AS Completed FROM sys.objects objects INNER JOIN sys.schemas schemas ON objects.schema_id = schemas.schema_id LEFT OUTER JOIN sys.tables tables ON objects.object_id = tables.object_id WHERE objects.[type] IN(''U'',''V'') AND EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.object_id = objects.object_id)' + CASE WHEN @Version >= 12 THEN ' AND (tables.is_memory_optimized = 0 OR is_memory_optimized IS NULL)' ELSE '' END + ' ORDER BY schemas.name ASC, objects.name ASC'; + + INSERT INTO @tmpObjects (SchemaID, SchemaName, [objectid], ObjectName, ObjectType, [Order], Selected, Completed) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand; + SET @Error = @@ERROR; + IF @Error <> 0 SET @ReturnCode = @Error; + + IF @Objects IS NULL + BEGIN + UPDATE tmpObjects + SET tmpObjects.Selected = 1 + FROM @tmpObjects tmpObjects; + END; + ELSE + BEGIN + UPDATE tmpObjects + SET tmpObjects.Selected = SelectedObjects.Selected + FROM @tmpObjects tmpObjects + INNER JOIN @SelectedObjects SelectedObjects + ON @CurrentDatabaseName LIKE REPLACE(SelectedObjects.DatabaseName,'_','[_]') AND tmpObjects.SchemaName LIKE REPLACE(SelectedObjects.SchemaName,'_','[_]') AND tmpObjects.ObjectName LIKE REPLACE(SelectedObjects.ObjectName,'_','[_]') + WHERE SelectedObjects.Selected = 1; + + UPDATE tmpObjects + SET tmpObjects.Selected = SelectedObjects.Selected + FROM @tmpObjects tmpObjects + INNER JOIN @SelectedObjects SelectedObjects + ON @CurrentDatabaseName LIKE REPLACE(SelectedObjects.DatabaseName,'_','[_]') AND tmpObjects.SchemaName LIKE REPLACE(SelectedObjects.SchemaName,'_','[_]') AND tmpObjects.ObjectName LIKE REPLACE(SelectedObjects.ObjectName,'_','[_]') + WHERE SelectedObjects.Selected = 0; + + UPDATE tmpObjects + SET tmpObjects.StartPosition = SelectedObjects2.StartPosition + FROM @tmpObjects tmpObjects + INNER JOIN (SELECT tmpObjects.SchemaName, tmpObjects.ObjectName, MIN(SelectedObjects.StartPosition) AS StartPosition + FROM @tmpObjects tmpObjects + INNER JOIN @SelectedObjects SelectedObjects + ON @CurrentDatabaseName LIKE REPLACE(SelectedObjects.DatabaseName,'_','[_]') AND tmpObjects.SchemaName LIKE REPLACE(SelectedObjects.SchemaName,'_','[_]') AND tmpObjects.ObjectName LIKE REPLACE(SelectedObjects.ObjectName,'_','[_]') + WHERE SelectedObjects.Selected = 1 + GROUP BY tmpObjects.SchemaName, tmpObjects.ObjectName) SelectedObjects2 + ON tmpObjects.SchemaName = SelectedObjects2.SchemaName AND tmpObjects.ObjectName = SelectedObjects2.ObjectName; + END; + + WITH tmpObjects AS ( + SELECT SchemaName, ObjectName, [Order], ROW_NUMBER() OVER (ORDER BY StartPosition ASC, SchemaName ASC, ObjectName ASC) AS RowNumber + FROM @tmpObjects tmpObjects + WHERE Selected = 1 + ) + UPDATE tmpObjects + SET [Order] = RowNumber; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + '.' + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName) + ', ' + FROM @SelectedObjects SelectedObjects + WHERE DatabaseName = @CurrentDatabaseName + AND SchemaName NOT LIKE '%[%]%' + AND ObjectName NOT LIKE '%[%]%' + AND NOT EXISTS (SELECT * FROM @tmpObjects WHERE SchemaName = SelectedObjects.SchemaName AND ObjectName = SelectedObjects.ObjectName); + IF @@ROWCOUNT > 0 + BEGIN + SET @ErrorMessage = 'The following objects do not exist: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.'; + RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT; + SET @Error = @@ERROR; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + END; + + WHILE (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SELECT TOP 1 @CurrentOID = [Id], + @CurrentSchemaID = SchemaID, + @CurrentSchemaName = SchemaName, + @CurrentObjectID = [objectid], + @CurrentObjectName = ObjectName, + @CurrentObjectType = ObjectType + FROM @tmpObjects + WHERE Selected = 1 + AND Completed = 0 + ORDER BY [Order] ASC; + + IF @@ROWCOUNT = 0 + BEGIN + BREAK; + END; + + -- Does the object exist? + SET @CurrentCommand = ''; + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS NVARCHAR) + '; '; + SET @CurrentCommand += 'IF EXISTS(SELECT * FROM sys.objects objects INNER JOIN sys.schemas schemas ON objects.schema_id = schemas.schema_id LEFT OUTER JOIN sys.tables tables ON objects.object_id = tables.object_id WHERE objects.[type] IN(''U'',''V'') AND EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.object_id = objects.object_id)' + CASE WHEN @Version >= 12 THEN ' AND (tables.is_memory_optimized = 0 OR is_memory_optimized IS NULL)' ELSE '' END + ' AND schemas.[schema_id] = @ParamSchemaID AND schemas.[name] = @ParamSchemaName AND objects.[object_id] = @ParamObjectID AND objects.[name] = @ParamObjectName AND objects.[type] = @ParamObjectType) BEGIN SET @ParamObjectExists = 1 END'; + + BEGIN TRY + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamSchemaID int, @ParamSchemaName sysname, @ParamObjectID int, @ParamObjectName sysname, @ParamObjectType sysname, @ParamObjectExists bit OUTPUT', @ParamSchemaID = @CurrentSchemaID, @ParamSchemaName = @CurrentSchemaName, @ParamObjectID = @CurrentObjectID, @ParamObjectName = @CurrentObjectName, @ParamObjectType = @CurrentObjectType, @ParamObjectExists = @CurrentObjectExists OUTPUT; + + IF @CurrentObjectExists IS NULL SET @CurrentObjectExists = 0; + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS NVARCHAR) + ', ' + ISNULL(ERROR_MESSAGE(),'') + CASE WHEN ERROR_NUMBER() = 1222 THEN ', ' + 'The object ' + QUOTENAME(@CurrentDatabaseName) + '.' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName) + ' is locked. It could not be checked if the object exists.' ELSE '' END; + SET @Severity = CASE WHEN ERROR_NUMBER() IN(1205,1222) THEN @LockMessageSeverity ELSE 16 END; + RAISERROR('%s',@Severity,1,@ErrorMessage) WITH NOWAIT; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + IF NOT (ERROR_NUMBER() IN(1205,1222) AND @LockMessageSeverity = 10) + BEGIN + SET @ReturnCode = ERROR_NUMBER(); + END; + END CATCH; + + IF @CurrentObjectExists = 1 + BEGIN + SET @CurrentDatabaseContext = @CurrentDatabaseName; + + SET @CurrentCommandType = 'DBCC_CHECKTABLE'; + + SET @CurrentCommand = ''; + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS NVARCHAR) + '; '; + SET @CurrentCommand += 'DBCC CHECKTABLE (' + QUOTENAME(QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName),''''); + IF @NoIndex = 'Y' SET @CurrentCommand += ', NOINDEX'; + SET @CurrentCommand += ') WITH ALL_ERRORMSGS'; + IF @DataPurity = 'Y' SET @CurrentCommand += ', DATA_PURITY'; + IF @PhysicalOnly = 'Y' SET @CurrentCommand += ', PHYSICAL_ONLY'; + IF @ExtendedLogicalChecks = 'Y' SET @CurrentCommand += ', EXTENDED_LOGICAL_CHECKS'; + IF @NoInformationalMessages = 'Y' SET @CurrentCommand += ', NO_INFOMSGS'; + IF @TabLock = 'Y' SET @CurrentCommand += ', TABLOCK'; + IF @MaxDOP IS NOT NULL SET @CurrentCommand += ', MAXDOP = ' + CAST(@MaxDOP AS NVARCHAR); + + EXECUTE @CurrentCommandOutput = [dbo].CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @SchemaName = @CurrentSchemaName, @ObjectName = @CurrentObjectName, @ObjectType = @CurrentObjectType, @LogToTable = @LogToTable, @Execute = @Execute; + SET @Error = @@ERROR; + IF @Error <> 0 SET @CurrentCommandOutput = @Error; + IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput; + END; + + UPDATE @tmpObjects + SET Completed = 1 + WHERE Selected = 1 + AND Completed = 0 + AND [Id] = @CurrentOID; + + SET @CurrentOID = NULL; + SET @CurrentSchemaID = NULL; + SET @CurrentSchemaName = NULL; + SET @CurrentObjectID = NULL; + SET @CurrentObjectName = NULL; + SET @CurrentObjectType = NULL; + SET @CurrentObjectExists = NULL; + + SET @CurrentDatabaseContext = NULL; + SET @CurrentCommand = NULL; + SET @CurrentCommandOutput = NULL; + SET @CurrentCommandType = NULL; + END; + END; + + -- Check catalog + IF EXISTS(SELECT * FROM @SelectedCheckCommands WHERE CheckCommand = 'CHECKCATALOG') AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SET @CurrentDatabaseContext = CASE WHEN SERVERPROPERTY('EngineEdition') = 5 THEN @CurrentDatabaseName ELSE 'master' END; + + SET @CurrentCommandType = 'DBCC_CHECKCATALOG'; + + SET @CurrentCommand = ''; + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS NVARCHAR) + '; '; + SET @CurrentCommand += 'DBCC CHECKCATALOG (' + QUOTENAME(@CurrentDatabaseName); + SET @CurrentCommand += ')'; + IF @NoInformationalMessages = 'Y' SET @CurrentCommand += ' WITH NO_INFOMSGS'; + + EXECUTE @CurrentCommandOutput = [dbo].CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @LogToTable = @LogToTable, @Execute = @Execute; + SET @Error = @@ERROR; + IF @Error <> 0 SET @CurrentCommandOutput = @Error; + IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput; + END; + + END; + + IF @CurrentDatabaseState = 'SUSPECT' + BEGIN + SET @ErrorMessage = 'The database ' + QUOTENAME(@CurrentDatabaseName) + ' is in a SUSPECT state.'; + RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT; + SET @Error = @@ERROR; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + END; + + -- Update that the database is completed + IF @DatabasesInParallel = 'Y' + BEGIN + UPDATE [dbo].QueueDatabase + SET DatabaseEndTime = SYSDATETIME() + WHERE QueueID = @QueueID + AND DatabaseName = @CurrentDatabaseName; + END; + ELSE + BEGIN + UPDATE @tmpDatabases + SET Completed = 1 + WHERE Selected = 1 + AND Completed = 0 + AND [Id] = @CurrentDBID; + END; + + -- Clear variables + SET @CurrentDBID = NULL; + SET @CurrentDatabaseName = NULL; + + SET @CurrentDatabase_sp_executesql = NULL; + + SET @CurrentUserAccess = NULL; + SET @CurrentIsReadOnly = NULL; + SET @CurrentDatabaseState = NULL; + SET @CurrentInStandby = NULL; + SET @CurrentRecoveryModel = NULL; + + SET @CurrentReplicaID = NULL; + SET @CurrentAvailabilityGroupID = NULL; + SET @CurrentAvailabilityGroup = NULL; + SET @CurrentAvailabilityGroupRole = NULL; + SET @CurrentAvailabilityGroupBackupPreference = NULL; + SET @CurrentSecondaryRoleAllowConnections = NULL; + SET @CurrentIsPreferredBackupReplica = NULL; + SET @CurrentDatabaseMirroringRole = NULL; + + SET @CurrentDatabaseContext = NULL; + SET @CurrentCommand = NULL; + SET @CurrentCommandOutput = NULL; + SET @CurrentCommandType = NULL; + + DELETE FROM @tmpFileGroups; + DELETE FROM @tmpObjects; + + END; + + ---------------------------------------------------------------------------------------------------- + --// Log completing information //-- + ---------------------------------------------------------------------------------------------------- + + Logging:; + SET @EndMessage = 'Date and time: ' + CONVERT(NVARCHAR,SYSDATETIME(),120); + RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT; + + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + IF @ReturnCode <> 0 + BEGIN + RETURN @ReturnCode; + END; + + ---------------------------------------------------------------------------------------------------- + +END; + +GO +SET ANSI_NULLS ON; +GO +SET QUOTED_IDENTIFIER ON; +GO +IF NOT EXISTS (SELECT * FROM [sys].[objects] WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[IndexOptimize]') AND TYPE IN (N'P', N'PC')) +BEGIN +EXECUTE [dbo].sp_executesql @statement = N'CREATE PROCEDURE [dbo].[IndexOptimize] AS'; +END; +GO + +ALTER PROCEDURE [dbo].[IndexOptimize] + +@Databases NVARCHAR(MAX) = NULL, +@FragmentationLow NVARCHAR(MAX) = NULL, +@FragmentationMedium NVARCHAR(MAX) = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationHigh NVARCHAR(MAX) = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationLevel1 INT = 5, +@FragmentationLevel2 INT = 30, +@MinNumberOfPages INT = 1000, +@MaxNumberOfPages INT = NULL, +@SortInTempdb NVARCHAR(MAX) = 'N', +@MaxDOP INT = NULL, +@FillFactor INT = NULL, +@PadIndex NVARCHAR(MAX) = NULL, +@LOBCompaction NVARCHAR(MAX) = 'Y', +@UpdateStatistics NVARCHAR(MAX) = NULL, +@OnlyModifiedStatistics NVARCHAR(MAX) = 'N', +@StatisticsModificationLevel INT = NULL, +@StatisticsSample INT = NULL, +@StatisticsResample NVARCHAR(MAX) = 'N', +@PartitionLevel NVARCHAR(MAX) = 'Y', +@MSShippedObjects NVARCHAR(MAX) = 'N', +@Indexes NVARCHAR(MAX) = NULL, +@TimeLimit INT = NULL, +@Delay INT = NULL, +@WaitAtLowPriorityMaxDuration INT = NULL, +@WaitAtLowPriorityAbortAfterWait NVARCHAR(MAX) = NULL, +@Resumable NVARCHAR(MAX) = 'N', +@AvailabilityGroups NVARCHAR(MAX) = NULL, +@LockTimeout INT = NULL, +@LockMessageSeverity INT = 16, +@StringDelimiter NVARCHAR(MAX) = ',', +@DatabaseOrder NVARCHAR(MAX) = NULL, +@DatabasesInParallel NVARCHAR(MAX) = 'N', +@ExecuteAsUser NVARCHAR(MAX) = NULL, +@LogToTable NVARCHAR(MAX) = 'N', +@Execute NVARCHAR(MAX) = 'Y' + +AS + +BEGIN + + ---------------------------------------------------------------------------------------------------- + --// Source: https://ola.hallengren.com //-- + --// License: https://ola.hallengren.com/license.html //-- + --// GitHub: https://github.com/olahallengren/sql-server-maintenance-solution //-- + --// Version: 2025-07-22 16:49:16 //-- + ---------------------------------------------------------------------------------------------------- + + SET NOCOUNT ON; + + SET ARITHABORT ON; + + SET NUMERIC_ROUNDABORT OFF; + + DECLARE @StartMessage NVARCHAR(MAX); + DECLARE @EndMessage NVARCHAR(MAX); + DECLARE @DatabaseMessage NVARCHAR(MAX); + DECLARE @ErrorMessage NVARCHAR(MAX); + DECLARE @Severity INT; + + DECLARE @StartTime DATETIME2 = SYSDATETIME(); + DECLARE @SchemaName NVARCHAR(MAX) = OBJECT_SCHEMA_NAME(@@PROCID); + DECLARE @ObjectName NVARCHAR(MAX) = OBJECT_NAME(@@PROCID); + DECLARE @VersionTimestamp NVARCHAR(MAX) = SUBSTRING(OBJECT_DEFINITION(@@PROCID),CHARINDEX('--// Version: ',OBJECT_DEFINITION(@@PROCID)) + LEN('--// Version: ') + 1, 19); + DECLARE @Parameters NVARCHAR(MAX); + + DECLARE @HostPlatform NVARCHAR(MAX); + + DECLARE @PartitionLevelStatistics BIT; + + DECLARE @QueueID INT; + DECLARE @QueueStartTime DATETIME2; + + DECLARE @CurrentDBID INT; + DECLARE @CurrentDatabaseName NVARCHAR(MAX); + + DECLARE @CurrentDatabase_sp_executesql NVARCHAR(MAX); + + DECLARE @CurrentExecuteAsUserExists BIT; + DECLARE @CurrentUserAccess NVARCHAR(MAX); + DECLARE @CurrentIsReadOnly BIT; + DECLARE @CurrentDatabaseState NVARCHAR(MAX); + DECLARE @CurrentInStandby BIT; + DECLARE @CurrentRecoveryModel NVARCHAR(MAX); + + DECLARE @CurrentReplicaID UNIQUEIDENTIFIER; + DECLARE @CurrentAvailabilityGroupID UNIQUEIDENTIFIER; + DECLARE @CurrentAvailabilityGroup NVARCHAR(MAX); + DECLARE @CurrentAvailabilityGroupRole NVARCHAR(MAX); + DECLARE @CurrentDatabaseMirroringRole NVARCHAR(MAX); + + DECLARE @CurrentDatabaseContext NVARCHAR(MAX); + DECLARE @CurrentCommand NVARCHAR(MAX); + DECLARE @CurrentCommandOutput INT; + DECLARE @CurrentCommandType NVARCHAR(MAX); + DECLARE @CurrentComment NVARCHAR(MAX); + DECLARE @CurrentExtendedInfo XML; + + DECLARE @Errors TABLE ([Id] INT IDENTITY PRIMARY KEY, + [Message] NVARCHAR(MAX) NOT NULL, + [severity] INT NOT NULL, + [State] INT); + + DECLARE @CurrentMessage NVARCHAR(MAX); + DECLARE @CurrentSeverity INT; + DECLARE @CurrentState INT; + + DECLARE @CurrentIxID INT; + DECLARE @CurrentIxOrder INT; + DECLARE @CurrentSchemaID INT; + DECLARE @CurrentSchemaName NVARCHAR(MAX); + DECLARE @CurrentObjectID INT; + DECLARE @CurrentObjectName NVARCHAR(MAX); + DECLARE @CurrentObjectType NVARCHAR(MAX); + DECLARE @CurrentIsMemoryOptimized BIT; + DECLARE @CurrentIndexID INT; + DECLARE @CurrentIndexName NVARCHAR(MAX); + DECLARE @CurrentIndexType INT; + DECLARE @CurrentStatisticsID INT; + DECLARE @CurrentStatisticsName NVARCHAR(MAX); + DECLARE @CurrentPartitionID BIGINT; + DECLARE @CurrentPartitionNumber INT; + DECLARE @CurrentPartitionCount INT; + DECLARE @CurrentIsPartition BIT; + DECLARE @CurrentIndexExists BIT; + DECLARE @CurrentStatisticsExists BIT; + DECLARE @CurrentIsImageText BIT; + DECLARE @CurrentIsNewLOB BIT; + DECLARE @CurrentIsFileStream BIT; + DECLARE @CurrentHasClusteredColumnstore BIT; + DECLARE @CurrentHasNonClusteredColumnstore BIT; + DECLARE @CurrentIsComputed BIT; + DECLARE @CurrentIsClusteredIndexComputed BIT; + DECLARE @CurrentIsTimestamp BIT; + DECLARE @CurrentAllowPageLocks BIT; + DECLARE @CurrentHasFilter BIT; + DECLARE @CurrentNoRecompute BIT; + DECLARE @CurrentIsIncremental BIT; + DECLARE @CurrentRowCount BIGINT; + DECLARE @CurrentModificationCounter BIGINT; + DECLARE @CurrentOnReadOnlyFileGroup BIT; + DECLARE @CurrentResumableIndexOperation BIT; + DECLARE @CurrentFragmentationLevel FLOAT; + DECLARE @CurrentPageCount BIGINT; + DECLARE @CurrentFragmentationGroup NVARCHAR(MAX); + DECLARE @CurrentAction NVARCHAR(MAX); + DECLARE @CurrentMaxDOP INT; + DECLARE @CurrentUpdateStatistics NVARCHAR(MAX); + DECLARE @CurrentStatisticsSample INT; + DECLARE @CurrentStatisticsResample NVARCHAR(MAX); + DECLARE @CurrentDelay DATETIME; + + DECLARE @tmpDatabases TABLE ([Id] INT IDENTITY, + DatabaseName NVARCHAR(MAX), + DatabaseType NVARCHAR(MAX), + AvailabilityGroup BIT, + StartPosition INT, + DatabaseSize BIGINT, + [Order] INT, + Selected BIT, + Completed BIT, + PRIMARY KEY(Selected, Completed, [Order], [Id])); + + DECLARE @tmpAvailabilityGroups TABLE ([Id] INT IDENTITY PRIMARY KEY, + AvailabilityGroupName NVARCHAR(MAX), + StartPosition INT, + Selected BIT); + + DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName NVARCHAR(MAX), + AvailabilityGroupName NVARCHAR(MAX)); + + DECLARE @tmpIndexesStatistics TABLE ([Id] INT IDENTITY, + SchemaID INT, + SchemaName NVARCHAR(MAX), + [objectid] INT, + ObjectName NVARCHAR(MAX), + ObjectType NVARCHAR(MAX), + IsMemoryOptimized BIT, + [indexid] INT, + IndexName NVARCHAR(MAX), + IndexType INT, + AllowPageLocks BIT, + HasFilter BIT, + IsImageText BIT, + IsNewLOB BIT, + IsFileStream BIT, + HasClusteredColumnstore BIT, + HasNonClusteredColumnstore BIT, + [iscomputed] BIT, + IsClusteredIndexComputed BIT, + IsTimestamp BIT, + OnReadOnlyFileGroup BIT, + ResumableIndexOperation BIT, + StatisticsID INT, + StatisticsName NVARCHAR(MAX), + [NoRecompute] BIT, + IsIncremental BIT, + PartitionID BIGINT, + PartitionNumber INT, + PartitionCount INT, + StartPosition INT, + [Order] INT, + Selected BIT, + Completed BIT, + PRIMARY KEY(Selected, Completed, [Order], [Id])); + + DECLARE @SelectedDatabases TABLE (DatabaseName NVARCHAR(MAX), + DatabaseType NVARCHAR(MAX), + AvailabilityGroup NVARCHAR(MAX), + StartPosition INT, + Selected BIT); + + DECLARE @SelectedAvailabilityGroups TABLE (AvailabilityGroupName NVARCHAR(MAX), + StartPosition INT, + Selected BIT); + + DECLARE @SelectedIndexes TABLE (DatabaseName NVARCHAR(MAX), + SchemaName NVARCHAR(MAX), + ObjectName NVARCHAR(MAX), + IndexName NVARCHAR(MAX), + StartPosition INT, + Selected BIT); + + DECLARE @Actions TABLE ([Action] NVARCHAR(MAX)); + + INSERT INTO @Actions([Action]) VALUES('INDEX_REBUILD_ONLINE'); + INSERT INTO @Actions([Action]) VALUES('INDEX_REBUILD_OFFLINE'); + INSERT INTO @Actions([Action]) VALUES('INDEX_REORGANIZE'); + + DECLARE @ActionsPreferred TABLE (FragmentationGroup NVARCHAR(MAX), + [Priority] INT, + [Action] NVARCHAR(MAX)); + + DECLARE @CurrentActionsAllowed TABLE ([Action] NVARCHAR(MAX)); + + DECLARE @CurrentAlterIndexWithClauseArguments TABLE ([Id] INT IDENTITY, + Argument NVARCHAR(MAX), + Added BIT DEFAULT 0); + + DECLARE @CurrentAlterIndexArgumentID INT; + DECLARE @CurrentAlterIndexArgument NVARCHAR(MAX); + DECLARE @CurrentAlterIndexWithClause NVARCHAR(MAX); + + DECLARE @CurrentUpdateStatisticsWithClauseArguments TABLE ([Id] INT IDENTITY, + Argument NVARCHAR(MAX), + Added BIT DEFAULT 0); + + DECLARE @CurrentUpdateStatisticsArgumentID INT; + DECLARE @CurrentUpdateStatisticsArgument NVARCHAR(MAX); + DECLARE @CurrentUpdateStatisticsWithClause NVARCHAR(MAX); + + DECLARE @Error INT = 0; + DECLARE @ReturnCode INT = 0; + + DECLARE @EmptyLine NVARCHAR(MAX) = CHAR(9); + + DECLARE @Version NUMERIC(18,10) = CAST(LEFT(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)),CHARINDEX('.',CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX))) - 1) + '.' + REPLACE(RIGHT(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)), LEN(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX))) - CHARINDEX('.',CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)))),'.','') AS NUMERIC(18,10)); + + IF @Version >= 14 + BEGIN + SELECT @HostPlatform = [host_platform] + FROM [sys].[dm_os_host_info]; + END; + ELSE + BEGIN + SET @HostPlatform = 'Windows'; + END; + + DECLARE @AmazonRDS BIT = CASE WHEN SERVERPROPERTY('EngineEdition') IN (5, 8) THEN 0 WHEN EXISTS (SELECT * FROM [sys].[databases] WHERE [name] = 'rdsadmin') AND SUSER_SNAME(0x01) = 'rdsa' THEN 1 ELSE 0 END; + + ---------------------------------------------------------------------------------------------------- + --// Log initial information //-- + ---------------------------------------------------------------------------------------------------- + + SET @Parameters = '@Databases = ' + ISNULL('''' + REPLACE(@Databases,'''','''''') + '''','NULL'); + SET @Parameters += ', @FragmentationLow = ' + ISNULL('''' + REPLACE(@FragmentationLow,'''','''''') + '''','NULL'); + SET @Parameters += ', @FragmentationMedium = ' + ISNULL('''' + REPLACE(@FragmentationMedium,'''','''''') + '''','NULL'); + SET @Parameters += ', @FragmentationHigh = ' + ISNULL('''' + REPLACE(@FragmentationHigh,'''','''''') + '''','NULL'); + SET @Parameters += ', @FragmentationLevel1 = ' + ISNULL(CAST(@FragmentationLevel1 AS NVARCHAR),'NULL'); + SET @Parameters += ', @FragmentationLevel2 = ' + ISNULL(CAST(@FragmentationLevel2 AS NVARCHAR),'NULL'); + SET @Parameters += ', @MinNumberOfPages = ' + ISNULL(CAST(@MinNumberOfPages AS NVARCHAR),'NULL'); + SET @Parameters += ', @MaxNumberOfPages = ' + ISNULL(CAST(@MaxNumberOfPages AS NVARCHAR),'NULL'); + SET @Parameters += ', @SortInTempdb = ' + ISNULL('''' + REPLACE(@SortInTempdb,'''','''''') + '''','NULL'); + SET @Parameters += ', @MaxDOP = ' + ISNULL(CAST(@MaxDOP AS NVARCHAR),'NULL'); + SET @Parameters += ', @FillFactor = ' + ISNULL(CAST(@FillFactor AS NVARCHAR),'NULL'); + SET @Parameters += ', @PadIndex = ' + ISNULL('''' + REPLACE(@PadIndex,'''','''''') + '''','NULL'); + SET @Parameters += ', @LOBCompaction = ' + ISNULL('''' + REPLACE(@LOBCompaction,'''','''''') + '''','NULL'); + SET @Parameters += ', @UpdateStatistics = ' + ISNULL('''' + REPLACE(@UpdateStatistics,'''','''''') + '''','NULL'); + SET @Parameters += ', @OnlyModifiedStatistics = ' + ISNULL('''' + REPLACE(@OnlyModifiedStatistics,'''','''''') + '''','NULL'); + SET @Parameters += ', @StatisticsModificationLevel = ' + ISNULL(CAST(@StatisticsModificationLevel AS NVARCHAR),'NULL'); + SET @Parameters += ', @StatisticsSample = ' + ISNULL(CAST(@StatisticsSample AS NVARCHAR),'NULL'); + SET @Parameters += ', @StatisticsResample = ' + ISNULL('''' + REPLACE(@StatisticsResample,'''','''''') + '''','NULL'); + SET @Parameters += ', @PartitionLevel = ' + ISNULL('''' + REPLACE(@PartitionLevel,'''','''''') + '''','NULL'); + SET @Parameters += ', @MSShippedObjects = ' + ISNULL('''' + REPLACE(@MSShippedObjects,'''','''''') + '''','NULL'); + SET @Parameters += ', @Indexes = ' + ISNULL('''' + REPLACE(@Indexes,'''','''''') + '''','NULL'); + SET @Parameters += ', @TimeLimit = ' + ISNULL(CAST(@TimeLimit AS NVARCHAR),'NULL'); + SET @Parameters += ', @Delay = ' + ISNULL(CAST(@Delay AS NVARCHAR),'NULL'); + SET @Parameters += ', @WaitAtLowPriorityMaxDuration = ' + ISNULL(CAST(@WaitAtLowPriorityMaxDuration AS NVARCHAR),'NULL'); + SET @Parameters += ', @WaitAtLowPriorityAbortAfterWait = ' + ISNULL('''' + REPLACE(@WaitAtLowPriorityAbortAfterWait,'''','''''') + '''','NULL'); + SET @Parameters += ', @Resumable = ' + ISNULL('''' + REPLACE(@Resumable,'''','''''') + '''','NULL'); + SET @Parameters += ', @AvailabilityGroups = ' + ISNULL('''' + REPLACE(@AvailabilityGroups,'''','''''') + '''','NULL'); + SET @Parameters += ', @LockTimeout = ' + ISNULL(CAST(@LockTimeout AS NVARCHAR),'NULL'); + SET @Parameters += ', @LockMessageSeverity = ' + ISNULL(CAST(@LockMessageSeverity AS NVARCHAR),'NULL'); + SET @Parameters += ', @StringDelimiter = ' + ISNULL('''' + REPLACE(@StringDelimiter,'''','''''') + '''','NULL'); + SET @Parameters += ', @DatabaseOrder = ' + ISNULL('''' + REPLACE(@DatabaseOrder,'''','''''') + '''','NULL'); + SET @Parameters += ', @DatabasesInParallel = ' + ISNULL('''' + REPLACE(@DatabasesInParallel,'''','''''') + '''','NULL'); + SET @Parameters += ', @ExecuteAsUser = ' + ISNULL('''' + REPLACE(@ExecuteAsUser,'''','''''') + '''','NULL'); + SET @Parameters += ', @LogToTable = ' + ISNULL('''' + REPLACE(@LogToTable,'''','''''') + '''','NULL'); + SET @Parameters += ', @Execute = ' + ISNULL('''' + REPLACE(@Execute,'''','''''') + '''','NULL'); + + SET @StartMessage = 'Date and time: ' + CONVERT(NVARCHAR,@StartTime,120); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Server: ' + CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(MAX)); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Version: ' + CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Edition: ' + CAST(SERVERPROPERTY('Edition') AS NVARCHAR(MAX)); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Platform: ' + @HostPlatform; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Procedure: ' + QUOTENAME(DB_NAME()) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName); + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Parameters: ' + @Parameters; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Version: ' + @VersionTimestamp; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + SET @StartMessage = 'Source: https://ola.hallengren.com'; + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT; + + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + ---------------------------------------------------------------------------------------------------- + --// Check core requirements //-- + ---------------------------------------------------------------------------------------------------- + + IF NOT (SELECT [compatibility_level] FROM [sys].[databases] WHERE [name] = DB_NAME()) >= 90 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The database ' + QUOTENAME(DB_NAME()) + ' has to be in compatibility level 90 or higher.', 16, 1; + END; + + IF NOT (SELECT [uses_ansi_nulls] FROM [sys].[sql_modules] WHERE [object_id] = @@PROCID) = 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'ANSI_NULLS has to be set to ON for the stored procedure.', 16, 1; + END; + + IF NOT (SELECT [uses_quoted_identifier] FROM [sys].[sql_modules] WHERE [object_id] = @@PROCID) = 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'QUOTED_IDENTIFIER has to be set to ON for the stored procedure.', 16, 1; + END; + + IF NOT EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'P' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'CommandExecute') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The stored procedure CommandExecute is missing. Download https://ola.hallengren.com/scripts/CommandExecute.sql.', 16, 1; + END; + + IF EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'P' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'CommandExecute' AND OBJECT_DEFINITION([objects].[object_id]) NOT LIKE '%@DatabaseContext%') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The stored procedure CommandExecute needs to be updated. Download https://ola.hallengren.com/scripts/CommandExecute.sql.', 16, 1; + END; + + IF @LogToTable = 'Y' AND NOT EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'U' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'CommandLog') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The table CommandLog is missing. Download https://ola.hallengren.com/scripts/CommandLog.sql.', 16, 1; + END; + + IF @DatabasesInParallel = 'Y' AND NOT EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'U' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'Queue') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The table Queue is missing. Download https://ola.hallengren.com/scripts/Queue.sql.', 16, 1; + END; + + IF @DatabasesInParallel = 'Y' AND NOT EXISTS (SELECT * FROM [sys].[objects] [objects] INNER JOIN [sys].[schemas] [schemas] ON [objects].[schema_id] = [schemas].[schema_id] WHERE [objects].[type] = 'U' AND [schemas].[name] = 'dbo' AND [objects].[name] = 'QueueDatabase') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The table QueueDatabase is missing. Download https://ola.hallengren.com/scripts/QueueDatabase.sql.', 16, 1; + END; + + IF @@TRANCOUNT <> 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The transaction count is not 0.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Select databases //-- + ---------------------------------------------------------------------------------------------------- + + SET @Databases = REPLACE(@Databases, CHAR(10), ''); + SET @Databases = REPLACE(@Databases, CHAR(13), ''); + + WHILE CHARINDEX(@StringDelimiter + ' ', @Databases) > 0 SET @Databases = REPLACE(@Databases, @StringDelimiter + ' ', @StringDelimiter); + WHILE CHARINDEX(' ' + @StringDelimiter, @Databases) > 0 SET @Databases = REPLACE(@Databases, ' ' + @StringDelimiter, @StringDelimiter); + + SET @Databases = LTRIM(RTRIM(@Databases)); + + WITH Databases1 (StartPosition, EndPosition, DatabaseItem) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Databases, 1), 0), LEN(@Databases) + 1) AS EndPosition, + SUBSTRING(@Databases, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Databases, 1), 0), LEN(@Databases) + 1) - 1) AS DatabaseItem + WHERE @Databases IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Databases, EndPosition + 1), 0), LEN(@Databases) + 1) AS EndPosition, + SUBSTRING(@Databases, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Databases, EndPosition + 1), 0), LEN(@Databases) + 1) - EndPosition - 1) AS DatabaseItem + FROM Databases1 + WHERE EndPosition < LEN(@Databases) + 1 + ), + Databases2 (DatabaseItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN DatabaseItem LIKE '-%' THEN RIGHT(DatabaseItem,LEN(DatabaseItem) - 1) ELSE DatabaseItem END AS DatabaseItem, + StartPosition, + CASE WHEN DatabaseItem LIKE '-%' THEN 0 ELSE 1 END AS Selected + FROM Databases1 + ), + Databases3 (DatabaseItem, DatabaseType, AvailabilityGroup, StartPosition, Selected) AS + ( + SELECT CASE WHEN DatabaseItem IN('ALL_DATABASES','SYSTEM_DATABASES','USER_DATABASES','AVAILABILITY_GROUP_DATABASES') THEN '%' ELSE DatabaseItem END AS DatabaseItem, + CASE WHEN DatabaseItem = 'SYSTEM_DATABASES' THEN 'S' WHEN DatabaseItem = 'USER_DATABASES' THEN 'U' ELSE NULL END AS DatabaseType, + CASE WHEN DatabaseItem = 'AVAILABILITY_GROUP_DATABASES' THEN 1 ELSE NULL END AvailabilityGroup, + StartPosition, + Selected + FROM Databases2 + ), + Databases4 (DatabaseName, DatabaseType, AvailabilityGroup, StartPosition, Selected) AS + ( + SELECT CASE WHEN LEFT(DatabaseItem,1) = '[' AND RIGHT(DatabaseItem,1) = ']' THEN PARSENAME(DatabaseItem,1) ELSE DatabaseItem END AS DatabaseItem, + DatabaseType, + AvailabilityGroup, + StartPosition, + Selected + FROM Databases3 + ) + INSERT INTO @SelectedDatabases (DatabaseName, DatabaseType, AvailabilityGroup, StartPosition, Selected) + SELECT DatabaseName, + DatabaseType, + AvailabilityGroup, + StartPosition, + Selected + FROM Databases4 + OPTION (MAXRECURSION 0); + + IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + BEGIN + INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName, Selected) + SELECT [name] AS AvailabilityGroupName, + 0 AS Selected + FROM [sys].[availability_groups]; + + INSERT INTO @tmpDatabasesAvailabilityGroups (DatabaseName, AvailabilityGroupName) + SELECT [databases].[name], + [availability_groups].[name] + FROM [sys].[databases] [databases] + INNER JOIN [sys].[availability_replicas] [availability_replicas] ON [databases].[replica_id] = [availability_replicas].[replica_id] + INNER JOIN [sys].[availability_groups] [availability_groups] ON [availability_replicas].[group_id] = [availability_groups].[group_id]; + END; + + INSERT INTO @tmpDatabases (DatabaseName, DatabaseType, AvailabilityGroup, [Order], Selected, Completed) + SELECT [name] AS DatabaseName, + CASE WHEN [name] IN('master','msdb','model') OR [is_distributor] = 1 THEN 'S' ELSE 'U' END AS DatabaseType, + NULL AS AvailabilityGroup, + 0 AS [Order], + 0 AS Selected, + 0 AS Completed + FROM [sys].[databases] + WHERE [name] <> 'tempdb' + AND [source_database_id] IS NULL + ORDER BY [name] ASC; + + UPDATE tmpDatabases + SET AvailabilityGroup = CASE WHEN EXISTS (SELECT * FROM @tmpDatabasesAvailabilityGroups WHERE DatabaseName = tmpDatabases.DatabaseName) THEN 1 ELSE 0 END + FROM @tmpDatabases tmpDatabases; + + UPDATE tmpDatabases + SET tmpDatabases.Selected = SelectedDatabases.Selected + FROM @tmpDatabases tmpDatabases + INNER JOIN @SelectedDatabases SelectedDatabases + ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + AND (tmpDatabases.DatabaseType = SelectedDatabases.DatabaseType OR SelectedDatabases.DatabaseType IS NULL) + AND (tmpDatabases.AvailabilityGroup = SelectedDatabases.AvailabilityGroup OR SelectedDatabases.AvailabilityGroup IS NULL) + WHERE SelectedDatabases.Selected = 1; + + UPDATE tmpDatabases + SET tmpDatabases.Selected = SelectedDatabases.Selected + FROM @tmpDatabases tmpDatabases + INNER JOIN @SelectedDatabases SelectedDatabases + ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + AND (tmpDatabases.DatabaseType = SelectedDatabases.DatabaseType OR SelectedDatabases.DatabaseType IS NULL) + AND (tmpDatabases.AvailabilityGroup = SelectedDatabases.AvailabilityGroup OR SelectedDatabases.AvailabilityGroup IS NULL) + WHERE SelectedDatabases.Selected = 0; + + UPDATE tmpDatabases + SET tmpDatabases.StartPosition = SelectedDatabases2.StartPosition + FROM @tmpDatabases tmpDatabases + INNER JOIN (SELECT tmpDatabases.DatabaseName, MIN(SelectedDatabases.StartPosition) AS StartPosition + FROM @tmpDatabases tmpDatabases + INNER JOIN @SelectedDatabases SelectedDatabases + ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + AND (tmpDatabases.DatabaseType = SelectedDatabases.DatabaseType OR SelectedDatabases.DatabaseType IS NULL) + AND (tmpDatabases.AvailabilityGroup = SelectedDatabases.AvailabilityGroup OR SelectedDatabases.AvailabilityGroup IS NULL) + WHERE SelectedDatabases.Selected = 1 + GROUP BY tmpDatabases.DatabaseName) SelectedDatabases2 + ON tmpDatabases.DatabaseName = SelectedDatabases2.DatabaseName; + + IF @Databases IS NOT NULL AND (NOT EXISTS(SELECT * FROM @SelectedDatabases) OR EXISTS(SELECT * FROM @SelectedDatabases WHERE DatabaseName IS NULL OR DATALENGTH(DatabaseName) = 0)) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Databases is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Select availability groups //-- + ---------------------------------------------------------------------------------------------------- + + IF @AvailabilityGroups IS NOT NULL AND @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + BEGIN + + SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, CHAR(10), ''); + SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, CHAR(13), ''); + + WHILE CHARINDEX(@StringDelimiter + ' ', @AvailabilityGroups) > 0 SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, @StringDelimiter + ' ', @StringDelimiter); + WHILE CHARINDEX(' ' + @StringDelimiter, @AvailabilityGroups) > 0 SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, ' ' + @StringDelimiter, @StringDelimiter); + + SET @AvailabilityGroups = LTRIM(RTRIM(@AvailabilityGroups)); + + WITH AvailabilityGroups1 (StartPosition, EndPosition, AvailabilityGroupItem) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @AvailabilityGroups, 1), 0), LEN(@AvailabilityGroups) + 1) AS EndPosition, + SUBSTRING(@AvailabilityGroups, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @AvailabilityGroups, 1), 0), LEN(@AvailabilityGroups) + 1) - 1) AS AvailabilityGroupItem + WHERE @AvailabilityGroups IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @AvailabilityGroups, EndPosition + 1), 0), LEN(@AvailabilityGroups) + 1) AS EndPosition, + SUBSTRING(@AvailabilityGroups, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @AvailabilityGroups, EndPosition + 1), 0), LEN(@AvailabilityGroups) + 1) - EndPosition - 1) AS AvailabilityGroupItem + FROM AvailabilityGroups1 + WHERE EndPosition < LEN(@AvailabilityGroups) + 1 + ), + AvailabilityGroups2 (AvailabilityGroupItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN AvailabilityGroupItem LIKE '-%' THEN RIGHT(AvailabilityGroupItem,LEN(AvailabilityGroupItem) - 1) ELSE AvailabilityGroupItem END AS AvailabilityGroupItem, + StartPosition, + CASE WHEN AvailabilityGroupItem LIKE '-%' THEN 0 ELSE 1 END AS Selected + FROM AvailabilityGroups1 + ), + AvailabilityGroups3 (AvailabilityGroupItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN AvailabilityGroupItem = 'ALL_AVAILABILITY_GROUPS' THEN '%' ELSE AvailabilityGroupItem END AS AvailabilityGroupItem, + StartPosition, + Selected + FROM AvailabilityGroups2 + ), + AvailabilityGroups4 (AvailabilityGroupName, StartPosition, Selected) AS + ( + SELECT CASE WHEN LEFT(AvailabilityGroupItem,1) = '[' AND RIGHT(AvailabilityGroupItem,1) = ']' THEN PARSENAME(AvailabilityGroupItem,1) ELSE AvailabilityGroupItem END AS AvailabilityGroupItem, + StartPosition, + Selected + FROM AvailabilityGroups3 + ) + INSERT INTO @SelectedAvailabilityGroups (AvailabilityGroupName, StartPosition, Selected) + SELECT AvailabilityGroupName, StartPosition, Selected + FROM AvailabilityGroups4 + OPTION (MAXRECURSION 0); + + UPDATE tmpAvailabilityGroups + SET tmpAvailabilityGroups.Selected = SelectedAvailabilityGroups.Selected + FROM @tmpAvailabilityGroups tmpAvailabilityGroups + INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + WHERE SelectedAvailabilityGroups.Selected = 1; + + UPDATE tmpAvailabilityGroups + SET tmpAvailabilityGroups.Selected = SelectedAvailabilityGroups.Selected + FROM @tmpAvailabilityGroups tmpAvailabilityGroups + INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + WHERE SelectedAvailabilityGroups.Selected = 0; + + UPDATE tmpAvailabilityGroups + SET tmpAvailabilityGroups.StartPosition = SelectedAvailabilityGroups2.StartPosition + FROM @tmpAvailabilityGroups tmpAvailabilityGroups + INNER JOIN (SELECT tmpAvailabilityGroups.AvailabilityGroupName, MIN(SelectedAvailabilityGroups.StartPosition) AS StartPosition + FROM @tmpAvailabilityGroups tmpAvailabilityGroups + INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + WHERE SelectedAvailabilityGroups.Selected = 1 + GROUP BY tmpAvailabilityGroups.AvailabilityGroupName) SelectedAvailabilityGroups2 + ON tmpAvailabilityGroups.AvailabilityGroupName = SelectedAvailabilityGroups2.AvailabilityGroupName; + + UPDATE tmpDatabases + SET tmpDatabases.StartPosition = tmpAvailabilityGroups.StartPosition, + tmpDatabases.Selected = 1 + FROM @tmpDatabases tmpDatabases + INNER JOIN @tmpDatabasesAvailabilityGroups tmpDatabasesAvailabilityGroups ON tmpDatabases.DatabaseName = tmpDatabasesAvailabilityGroups.DatabaseName + INNER JOIN @tmpAvailabilityGroups tmpAvailabilityGroups ON tmpDatabasesAvailabilityGroups.AvailabilityGroupName = tmpAvailabilityGroups.AvailabilityGroupName + WHERE tmpAvailabilityGroups.Selected = 1; + + END; + + IF @AvailabilityGroups IS NOT NULL AND (NOT EXISTS(SELECT * FROM @SelectedAvailabilityGroups) OR EXISTS(SELECT * FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName IS NULL OR AvailabilityGroupName = '') OR @Version < 11 OR SERVERPROPERTY('IsHadrEnabled') = 0) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @AvailabilityGroups is not supported.', 16, 1; + END; + + IF (@Databases IS NULL AND @AvailabilityGroups IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'You need to specify one of the parameters @Databases and @AvailabilityGroups.', 16, 2; + END; + + IF (@Databases IS NOT NULL AND @AvailabilityGroups IS NOT NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'You can only specify one of the parameters @Databases and @AvailabilityGroups.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + --// Select indexes //-- + ---------------------------------------------------------------------------------------------------- + + SET @Indexes = REPLACE(@Indexes, CHAR(10), ''); + SET @Indexes = REPLACE(@Indexes, CHAR(13), ''); + + WHILE CHARINDEX(@StringDelimiter + ' ', @Indexes) > 0 SET @Indexes = REPLACE(@Indexes, @StringDelimiter + ' ', @StringDelimiter); + WHILE CHARINDEX(' ' + @StringDelimiter, @Indexes) > 0 SET @Indexes = REPLACE(@Indexes, ' ' + @StringDelimiter, @StringDelimiter); + + SET @Indexes = LTRIM(RTRIM(@Indexes)); + + WITH Indexes1 (StartPosition, EndPosition, IndexItem) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Indexes, 1), 0), LEN(@Indexes) + 1) AS EndPosition, + SUBSTRING(@Indexes, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Indexes, 1), 0), LEN(@Indexes) + 1) - 1) AS IndexItem + WHERE @Indexes IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Indexes, EndPosition + 1), 0), LEN(@Indexes) + 1) AS EndPosition, + SUBSTRING(@Indexes, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @Indexes, EndPosition + 1), 0), LEN(@Indexes) + 1) - EndPosition - 1) AS IndexItem + FROM Indexes1 + WHERE EndPosition < LEN(@Indexes) + 1 + ), + Indexes2 (IndexItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN IndexItem LIKE '-%' THEN RIGHT(IndexItem,LEN(IndexItem) - 1) ELSE IndexItem END AS IndexItem, + StartPosition, + CASE WHEN IndexItem LIKE '-%' THEN 0 ELSE 1 END AS Selected + FROM Indexes1 + ), + Indexes3 (IndexItem, StartPosition, Selected) AS + ( + SELECT CASE WHEN IndexItem = 'ALL_INDEXES' THEN '%.%.%.%' ELSE IndexItem END AS IndexItem, + StartPosition, + Selected + FROM Indexes2 + ), + Indexes4 (DatabaseName, SchemaName, ObjectName, IndexName, StartPosition, Selected) AS + ( + SELECT CASE WHEN PARSENAME(IndexItem,4) IS NULL THEN PARSENAME(IndexItem,3) ELSE PARSENAME(IndexItem,4) END AS DatabaseName, + CASE WHEN PARSENAME(IndexItem,4) IS NULL THEN PARSENAME(IndexItem,2) ELSE PARSENAME(IndexItem,3) END AS SchemaName, + CASE WHEN PARSENAME(IndexItem,4) IS NULL THEN PARSENAME(IndexItem,1) ELSE PARSENAME(IndexItem,2) END AS ObjectName, + CASE WHEN PARSENAME(IndexItem,4) IS NULL THEN '%' ELSE PARSENAME(IndexItem,1) END AS IndexName, + StartPosition, + Selected + FROM Indexes3 + ) + INSERT INTO @SelectedIndexes (DatabaseName, SchemaName, ObjectName, IndexName, StartPosition, Selected) + SELECT DatabaseName, SchemaName, ObjectName, IndexName, StartPosition, Selected + FROM Indexes4 + OPTION (MAXRECURSION 0); + + ---------------------------------------------------------------------------------------------------- + --// Select actions //-- + ---------------------------------------------------------------------------------------------------- + + SET @FragmentationLow = REPLACE(@FragmentationLow, @StringDelimiter + ' ', @StringDelimiter); + + WITH FragmentationLow (StartPosition, EndPosition, [Action]) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FragmentationLow, 1), 0), LEN(@FragmentationLow) + 1) AS EndPosition, + SUBSTRING(@FragmentationLow, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FragmentationLow, 1), 0), LEN(@FragmentationLow) + 1) - 1) AS [Action] + WHERE @FragmentationLow IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FragmentationLow, EndPosition + 1), 0), LEN(@FragmentationLow) + 1) AS EndPosition, + SUBSTRING(@FragmentationLow, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FragmentationLow, EndPosition + 1), 0), LEN(@FragmentationLow) + 1) - EndPosition - 1) AS [Action] + FROM FragmentationLow + WHERE EndPosition < LEN(@FragmentationLow) + 1 + ) + INSERT INTO @ActionsPreferred(FragmentationGroup, [Priority], [Action]) + SELECT 'Low' AS FragmentationGroup, + ROW_NUMBER() OVER(ORDER BY StartPosition ASC) AS [Priority], + [Action] + FROM FragmentationLow + OPTION (MAXRECURSION 0); + + SET @FragmentationMedium = REPLACE(@FragmentationMedium, @StringDelimiter + ' ', @StringDelimiter); + + WITH FragmentationMedium (StartPosition, EndPosition, [Action]) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FragmentationMedium, 1), 0), LEN(@FragmentationMedium) + 1) AS EndPosition, + SUBSTRING(@FragmentationMedium, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FragmentationMedium, 1), 0), LEN(@FragmentationMedium) + 1) - 1) AS [Action] + WHERE @FragmentationMedium IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FragmentationMedium, EndPosition + 1), 0), LEN(@FragmentationMedium) + 1) AS EndPosition, + SUBSTRING(@FragmentationMedium, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FragmentationMedium, EndPosition + 1), 0), LEN(@FragmentationMedium) + 1) - EndPosition - 1) AS [Action] + FROM FragmentationMedium + WHERE EndPosition < LEN(@FragmentationMedium) + 1 + ) + INSERT INTO @ActionsPreferred(FragmentationGroup, [Priority], [Action]) + SELECT 'Medium' AS FragmentationGroup, + ROW_NUMBER() OVER(ORDER BY StartPosition ASC) AS [Priority], + [Action] + FROM FragmentationMedium + OPTION (MAXRECURSION 0); + + SET @FragmentationHigh = REPLACE(@FragmentationHigh, @StringDelimiter + ' ', @StringDelimiter); + + WITH FragmentationHigh (StartPosition, EndPosition, [Action]) AS + ( + SELECT 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FragmentationHigh, 1), 0), LEN(@FragmentationHigh) + 1) AS EndPosition, + SUBSTRING(@FragmentationHigh, 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FragmentationHigh, 1), 0), LEN(@FragmentationHigh) + 1) - 1) AS [Action] + WHERE @FragmentationHigh IS NOT NULL + UNION ALL + SELECT CAST(EndPosition AS INT) + 1 AS StartPosition, + ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FragmentationHigh, EndPosition + 1), 0), LEN(@FragmentationHigh) + 1) AS EndPosition, + SUBSTRING(@FragmentationHigh, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(@StringDelimiter, @FragmentationHigh, EndPosition + 1), 0), LEN(@FragmentationHigh) + 1) - EndPosition - 1) AS [Action] + FROM FragmentationHigh + WHERE EndPosition < LEN(@FragmentationHigh) + 1 + ) + INSERT INTO @ActionsPreferred(FragmentationGroup, [Priority], [Action]) + SELECT 'High' AS FragmentationGroup, + ROW_NUMBER() OVER(ORDER BY StartPosition ASC) AS [Priority], + [Action] + FROM FragmentationHigh + OPTION (MAXRECURSION 0); + + ---------------------------------------------------------------------------------------------------- + --// Check input parameters //-- + ---------------------------------------------------------------------------------------------------- + + IF EXISTS (SELECT [Action] FROM @ActionsPreferred WHERE FragmentationGroup = 'Low' AND [Action] NOT IN(SELECT * FROM @Actions)) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FragmentationLow is not supported.', 16, 1; + END; + + IF EXISTS (SELECT * FROM @ActionsPreferred WHERE FragmentationGroup = 'Low' GROUP BY [Action] HAVING COUNT(*) > 1) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FragmentationLow is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS (SELECT [Action] FROM @ActionsPreferred WHERE FragmentationGroup = 'Medium' AND [Action] NOT IN(SELECT * FROM @Actions)) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FragmentationMedium is not supported.', 16, 1; + END; + + IF EXISTS (SELECT * FROM @ActionsPreferred WHERE FragmentationGroup = 'Medium' GROUP BY [Action] HAVING COUNT(*) > 1) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FragmentationMedium is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS (SELECT [Action] FROM @ActionsPreferred WHERE FragmentationGroup = 'High' AND [Action] NOT IN(SELECT * FROM @Actions)) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FragmentationHigh is not supported.', 16, 1; + END; + + IF EXISTS (SELECT * FROM @ActionsPreferred WHERE FragmentationGroup = 'High' GROUP BY [Action] HAVING COUNT(*) > 1) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FragmentationHigh is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @FragmentationLevel1 <= 0 OR @FragmentationLevel1 >= 100 OR @FragmentationLevel1 IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FragmentationLevel1 is not supported.', 16, 1; + END; + + IF @FragmentationLevel1 >= @FragmentationLevel2 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FragmentationLevel1 is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @FragmentationLevel2 <= 0 OR @FragmentationLevel2 >= 100 OR @FragmentationLevel2 IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FragmentationLevel2 is not supported.', 16, 1; + END; + + IF @FragmentationLevel2 <= @FragmentationLevel1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FragmentationLevel2 is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @MinNumberOfPages < 0 OR @MinNumberOfPages IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MinNumberOfPages is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @MaxNumberOfPages < 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MaxNumberOfPages is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @SortInTempdb NOT IN('Y','N') OR @SortInTempdb IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @SortInTempdb is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @MaxDOP < 0 OR @MaxDOP > 64 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MaxDOP is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @FillFactor <= 0 OR @FillFactor > 100 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @FillFactor is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @PadIndex NOT IN('Y','N') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @PadIndex is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @LOBCompaction NOT IN('Y','N') OR @LOBCompaction IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @LOBCompaction is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @UpdateStatistics NOT IN('ALL','COLUMNS','INDEX') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @UpdateStatistics is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @OnlyModifiedStatistics NOT IN('Y','N') OR @OnlyModifiedStatistics IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @OnlyModifiedStatistics is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @StatisticsModificationLevel <= 0 OR @StatisticsModificationLevel > 100 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @StatisticsModificationLevel is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @OnlyModifiedStatistics = 'Y' AND @StatisticsModificationLevel IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'You can only specify one of the parameters @OnlyModifiedStatistics and @StatisticsModificationLevel.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @StatisticsSample <= 0 OR @StatisticsSample > 100 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @StatisticsSample is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @StatisticsResample NOT IN('Y','N') OR @StatisticsResample IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @StatisticsResample is not supported.', 16, 1; + END; + + IF @StatisticsResample = 'Y' AND @StatisticsSample IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @StatisticsResample is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @PartitionLevel NOT IN('Y','N') OR @PartitionLevel IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @PartitionLevel is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @MSShippedObjects NOT IN('Y','N') OR @MSShippedObjects IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @MSShippedObjects is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS(SELECT * FROM @SelectedIndexes WHERE DatabaseName IS NULL OR SchemaName IS NULL OR ObjectName IS NULL OR IndexName IS NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Indexes is not supported.', 16, 1; + END; + + IF @Indexes IS NOT NULL AND NOT EXISTS(SELECT * FROM @SelectedIndexes) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Indexes is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @TimeLimit < 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @TimeLimit is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Delay < 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Delay is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @WaitAtLowPriorityMaxDuration < 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @WaitAtLowPriorityMaxDuration is not supported.', 16, 1; + END; + + IF @WaitAtLowPriorityMaxDuration IS NOT NULL AND @Version < 12 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @WaitAtLowPriorityMaxDuration is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @WaitAtLowPriorityAbortAfterWait NOT IN('NONE','SELF','BLOCKERS') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @WaitAtLowPriorityAbortAfterWait is not supported.', 16, 1; + END; + + IF @WaitAtLowPriorityAbortAfterWait IS NOT NULL AND @Version < 12 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @WaitAtLowPriorityAbortAfterWait is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF (@WaitAtLowPriorityAbortAfterWait IS NOT NULL AND @WaitAtLowPriorityMaxDuration IS NULL) OR (@WaitAtLowPriorityAbortAfterWait IS NULL AND @WaitAtLowPriorityMaxDuration IS NOT NULL) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The parameters @WaitAtLowPriorityMaxDuration and @WaitAtLowPriorityAbortAfterWait can only be used together.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Resumable NOT IN('Y','N') OR @Resumable IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Resumable is not supported.', 16, 1; + END; + + IF @Resumable = 'Y' AND NOT (@Version >= 14 OR SERVERPROPERTY('EngineEdition') IN (5, 8)) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Resumable is not supported.', 16, 2; + END; + + IF @Resumable = 'Y' AND @SortInTempdb = 'Y' + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'You can only specify one of the parameters @Resumable and @SortInTempdb.', 16, 3; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @LockTimeout < 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @LockTimeout is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @LockMessageSeverity NOT IN(10, 16) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @LockMessageSeverity is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) > 1 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @StringDelimiter is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DatabaseOrder NOT IN('DATABASE_NAME_ASC','DATABASE_NAME_DESC','DATABASE_SIZE_ASC','DATABASE_SIZE_DESC') + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabaseOrder is not supported.', 16, 1; + END; + + IF @DatabaseOrder IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 5 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabaseOrder is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @DatabasesInParallel NOT IN('Y','N') OR @DatabasesInParallel IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabasesInParallel is not supported.', 16, 1; + END; + + IF @DatabasesInParallel = 'Y' AND SERVERPROPERTY('EngineEdition') = 5 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @DatabasesInParallel is not supported.', 16, 2; + END; + + ---------------------------------------------------------------------------------------------------- + + IF LEN(@ExecuteAsUser) > 128 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @ExecuteAsUser is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @LogToTable NOT IN('Y','N') OR @LogToTable IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @LogToTable is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF @Execute NOT IN('Y','N') OR @Execute IS NULL + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The value for the parameter @Execute is not supported.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS(SELECT * FROM @Errors) + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The documentation is available at https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Check that selected databases and availability groups exist //-- + ---------------------------------------------------------------------------------------------------- + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + ', ' + FROM @SelectedDatabases + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases); + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The following databases in the @Databases parameter do not exist: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 10, 1; + END; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + ', ' + FROM @SelectedIndexes + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases); + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The following databases in the @Indexes parameter do not exist: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 10, 1; + END; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(AvailabilityGroupName) + ', ' + FROM @SelectedAvailabilityGroups + WHERE AvailabilityGroupName NOT LIKE '%[%]%' + AND AvailabilityGroupName NOT IN (SELECT AvailabilityGroupName FROM @tmpAvailabilityGroups); + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The following availability groups do not exist: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 10, 1; + END; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + ', ' + FROM @SelectedIndexes + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName IN (SELECT DatabaseName FROM @tmpDatabases) + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases WHERE Selected = 1); + IF @@ROWCOUNT > 0 + BEGIN + INSERT INTO @Errors ([Message], [severity], [State]) + SELECT 'The following databases have been selected in the @Indexes parameter, but not in the @Databases or @AvailabilityGroups parameters: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.', 10, 1; + END; + + ---------------------------------------------------------------------------------------------------- + --// Raise errors //-- + ---------------------------------------------------------------------------------------------------- + + DECLARE ErrorCursor CURSOR FAST_FORWARD FOR SELECT [Message], [severity], [State] FROM @Errors ORDER BY [ID] ASC; + + OPEN ErrorCursor; + + FETCH ErrorCursor INTO @CurrentMessage, @CurrentSeverity, @CurrentState; + + WHILE @@FETCH_STATUS = 0 + BEGIN + RAISERROR('%s', @CurrentSeverity, @CurrentState, @CurrentMessage) WITH NOWAIT; + RAISERROR(@EmptyLine, 10, 1) WITH NOWAIT; + + FETCH NEXT FROM ErrorCursor INTO @CurrentMessage, @CurrentSeverity, @CurrentState; + END; + + CLOSE ErrorCursor; + + DEALLOCATE ErrorCursor; + + IF EXISTS (SELECT * FROM @Errors WHERE [severity] >= 16) + BEGIN + SET @ReturnCode = 50000; + GOTO Logging; + END; + + ---------------------------------------------------------------------------------------------------- + --// Should statistics be updated on the partition level? //-- + ---------------------------------------------------------------------------------------------------- + + SET @PartitionLevelStatistics = CASE WHEN @PartitionLevel = 'Y' AND ((@Version >= 12.05 AND @Version < 13) OR @Version >= 13.04422 OR SERVERPROPERTY('EngineEdition') IN (5,8)) THEN 1 ELSE 0 END; + + ---------------------------------------------------------------------------------------------------- + --// Update database order //-- + ---------------------------------------------------------------------------------------------------- + + IF @DatabaseOrder IN('DATABASE_SIZE_ASC','DATABASE_SIZE_DESC') + BEGIN + UPDATE tmpDatabases + SET DatabaseSize = (SELECT SUM(CAST(SIZE AS BIGINT)) FROM [sys].[master_files] WHERE [type] = 0 AND [database_id] = DB_ID(tmpDatabases.DatabaseName)) + FROM @tmpDatabases tmpDatabases; + END; + + IF @DatabaseOrder IS NULL + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY StartPosition ASC, DatabaseName ASC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_NAME_ASC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY DatabaseName ASC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_NAME_DESC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY DatabaseName DESC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_SIZE_ASC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY DatabaseSize ASC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + ELSE + IF @DatabaseOrder = 'DATABASE_SIZE_DESC' + BEGIN + WITH tmpDatabases AS ( + SELECT DatabaseName, [Order], ROW_NUMBER() OVER (ORDER BY DatabaseSize DESC) AS RowNumber + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + ) + UPDATE tmpDatabases + SET [Order] = RowNumber; + END; + + ---------------------------------------------------------------------------------------------------- + --// Update the queue //-- + ---------------------------------------------------------------------------------------------------- + + IF @DatabasesInParallel = 'Y' + BEGIN + + BEGIN TRY + + SELECT @QueueID = QueueID + FROM [dbo].[Queue] + WHERE SchemaName = @SchemaName + AND ObjectName = @ObjectName + AND [Parameters] = @Parameters; + + IF @QueueID IS NULL + BEGIN + BEGIN TRANSACTION; + + SELECT @QueueID = QueueID + FROM [dbo].[Queue] WITH (UPDLOCK, HOLDLOCK) + WHERE SchemaName = @SchemaName + AND ObjectName = @ObjectName + AND [Parameters] = @Parameters; + + IF @QueueID IS NULL + BEGIN + INSERT INTO [dbo].[Queue] (SchemaName, ObjectName, [Parameters]) + SELECT @SchemaName, @ObjectName, @Parameters; + + SET @QueueID = SCOPE_IDENTITY(); + END; + + COMMIT TRANSACTION; + END; + + BEGIN TRANSACTION; + + UPDATE [Queue] + SET QueueStartTime = SYSDATETIME(), + SessionID = @@SPID, + RequestID = (SELECT [request_id] FROM [sys].[dm_exec_requests] WHERE [session_id] = @@SPID), + RequestStartTime = (SELECT [start_time] FROM [sys].[dm_exec_requests] WHERE [session_id] = @@SPID) + FROM [dbo].[Queue] [Queue] + WHERE QueueID = @QueueID + AND NOT EXISTS (SELECT * + FROM [sys].[dm_exec_requests] + WHERE [session_id] = [Queue].SessionID + AND [request_id] = [Queue].RequestID + AND [start_time] = [Queue].RequestStartTime) + AND NOT EXISTS (SELECT * + FROM [dbo].QueueDatabase QueueDatabase + INNER JOIN [sys].[dm_exec_requests] ON QueueDatabase.SessionID = [session_id] AND QueueDatabase.RequestID = [request_id] AND QueueDatabase.RequestStartTime = [start_time] + WHERE QueueDatabase.QueueID = @QueueID); + + IF @@ROWCOUNT = 1 + BEGIN + INSERT INTO [dbo].QueueDatabase (QueueID, DatabaseName) + SELECT @QueueID AS QueueID, + DatabaseName + FROM @tmpDatabases tmpDatabases + WHERE Selected = 1 + AND NOT EXISTS (SELECT * FROM [dbo].QueueDatabase WHERE DatabaseName = tmpDatabases.DatabaseName AND QueueID = @QueueID); + + DELETE QueueDatabase + FROM [dbo].QueueDatabase QueueDatabase + WHERE QueueID = @QueueID + AND NOT EXISTS (SELECT * FROM @tmpDatabases tmpDatabases WHERE DatabaseName = QueueDatabase.DatabaseName AND Selected = 1); + + UPDATE QueueDatabase + SET DatabaseOrder = tmpDatabases.[Order] + FROM [dbo].QueueDatabase QueueDatabase + INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName = tmpDatabases.DatabaseName + WHERE QueueID = @QueueID; + END; + + COMMIT TRANSACTION; + + SELECT @QueueStartTime = QueueStartTime + FROM [dbo].[Queue] + WHERE QueueID = @QueueID; + + END TRY + + BEGIN CATCH + IF XACT_STATE() <> 0 + BEGIN + ROLLBACK TRANSACTION; + END; + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS NVARCHAR) + ', ' + ISNULL(ERROR_MESSAGE(),''); + RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + SET @ReturnCode = ERROR_NUMBER(); + GOTO Logging; + END CATCH; + + END; + + ---------------------------------------------------------------------------------------------------- + --// Execute commands //-- + ---------------------------------------------------------------------------------------------------- + + WHILE (1 = 1) + BEGIN + + IF @DatabasesInParallel = 'Y' + BEGIN + UPDATE QueueDatabase + SET DatabaseStartTime = NULL, + SessionID = NULL, + RequestID = NULL, + RequestStartTime = NULL + FROM [dbo].QueueDatabase QueueDatabase + WHERE QueueID = @QueueID + AND DatabaseStartTime IS NOT NULL + AND DatabaseEndTime IS NULL + AND NOT EXISTS (SELECT * FROM [sys].[dm_exec_requests] WHERE [session_id] = QueueDatabase.SessionID AND [request_id] = QueueDatabase.RequestID AND [start_time] = QueueDatabase.RequestStartTime); + + UPDATE QueueDatabase + SET DatabaseStartTime = SYSDATETIME(), + DatabaseEndTime = NULL, + SessionID = @@SPID, + RequestID = (SELECT [request_id] FROM [sys].[dm_exec_requests] WHERE [session_id] = @@SPID), + RequestStartTime = (SELECT [start_time] FROM [sys].[dm_exec_requests] WHERE [session_id] = @@SPID), + @CurrentDatabaseName = DatabaseName + FROM (SELECT TOP 1 DatabaseStartTime, + DatabaseEndTime, + SessionID, + RequestID, + RequestStartTime, + DatabaseName + FROM [dbo].QueueDatabase + WHERE QueueID = @QueueID + AND (DatabaseStartTime < @QueueStartTime OR DatabaseStartTime IS NULL) + AND NOT (DatabaseStartTime IS NOT NULL AND DatabaseEndTime IS NULL) + ORDER BY DatabaseOrder ASC + ) QueueDatabase; + END; + ELSE + BEGIN + SELECT TOP 1 @CurrentDBID = [Id], + @CurrentDatabaseName = DatabaseName + FROM @tmpDatabases + WHERE Selected = 1 + AND Completed = 0 + ORDER BY [Order] ASC; + END; + + IF @@ROWCOUNT = 0 + BEGIN + BREAK; + END; + + SET @CurrentDatabase_sp_executesql = QUOTENAME(@CurrentDatabaseName) + '.sys.sp_executesql'; + + BEGIN + SET @DatabaseMessage = 'Date and time: ' + CONVERT(NVARCHAR,SYSDATETIME(),120); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Database: ' + QUOTENAME(@CurrentDatabaseName); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + SELECT @CurrentUserAccess = [user_access_desc], + @CurrentIsReadOnly = [is_read_only], + @CurrentDatabaseState = [state_desc], + @CurrentInStandby = [is_in_standby], + @CurrentRecoveryModel = [recovery_model_desc] + FROM [sys].[databases] + WHERE [name] = @CurrentDatabaseName; + + BEGIN + SET @DatabaseMessage = 'State: ' + @CurrentDatabaseState; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Standby: ' + CASE WHEN @CurrentInStandby = 1 THEN 'Yes' ELSE 'No' END; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Updateability: ' + CASE WHEN @CurrentIsReadOnly = 1 THEN 'READ_ONLY' WHEN @CurrentIsReadOnly = 0 THEN 'READ_WRITE' END; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'User access: ' + @CurrentUserAccess; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Recovery model: ' + @CurrentRecoveryModel; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + BEGIN + SELECT @CurrentReplicaID = [databases].[replica_id] + FROM [sys].[databases] [databases] + INNER JOIN [sys].[availability_replicas] [availability_replicas] ON [databases].[replica_id] = [availability_replicas].[replica_id] + WHERE [databases].[name] = @CurrentDatabaseName; + + SELECT @CurrentAvailabilityGroupID = [group_id] + FROM [sys].[availability_replicas] + WHERE [replica_id] = @CurrentReplicaID; + + SELECT @CurrentAvailabilityGroupRole = [role_desc] + FROM [sys].[dm_hadr_availability_replica_states] + WHERE [replica_id] = @CurrentReplicaID; + + SELECT @CurrentAvailabilityGroup = [name] + FROM [sys].[availability_groups] + WHERE [group_id] = @CurrentAvailabilityGroupID; + END; + + IF SERVERPROPERTY('EngineEdition') <> 5 + BEGIN + SELECT @CurrentDatabaseMirroringRole = UPPER([mirroring_role_desc]) + FROM [sys].[database_mirroring] [database_mirroring] + INNER JOIN [sys].[databases] [databases] ON [database_mirroring].[database_id] = [databases].[database_id] + WHERE [databases].[name] = @CurrentDatabaseName; + END; + + IF @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Availability group: ' + ISNULL(@CurrentAvailabilityGroup,'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + + SET @DatabaseMessage = 'Availability group role: ' + ISNULL(@CurrentAvailabilityGroupRole,'N/A'); + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + IF @CurrentDatabaseMirroringRole IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Database mirroring role: ' + @CurrentDatabaseMirroringRole; + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT; + END; + + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + IF @ExecuteAsUser IS NOT NULL + AND @CurrentDatabaseState = 'ONLINE' + AND NOT (@CurrentUserAccess = 'SINGLE_USER') + AND NOT (@CurrentAvailabilityGroup IS NOT NULL AND (@CurrentAvailabilityGroupRole <> 'PRIMARY' OR @CurrentAvailabilityGroupRole IS NULL)) + AND NOT (@AmazonRDS = 1 AND @CurrentDatabaseName = 'rdsadmin') + BEGIN + SET @CurrentCommand = ''; + SET @CurrentCommand += 'IF EXISTS(SELECT * FROM sys.database_principals database_principals WHERE database_principals.[name] = @ParamExecuteAsUser) BEGIN SET @ParamExecuteAsUserExists = 1 END ELSE BEGIN SET @ParamExecuteAsUserExists = 0 END'; + + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamExecuteAsUser sysname, @ParamExecuteAsUserExists bit OUTPUT', @ParamExecuteAsUser = @ExecuteAsUser, @ParamExecuteAsUserExists = @CurrentExecuteAsUserExists OUTPUT; + END; + + IF @CurrentExecuteAsUserExists = 0 + BEGIN + SET @DatabaseMessage = 'The user ' + QUOTENAME(@ExecuteAsUser) + ' does not exist in the database ' + QUOTENAME(@CurrentDatabaseName) + '.'; + RAISERROR('%s',16,1,@DatabaseMessage) WITH NOWAIT; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + END; + + IF @CurrentDatabaseState = 'ONLINE' + AND NOT (@CurrentUserAccess = 'SINGLE_USER') + AND NOT (@CurrentAvailabilityGroup IS NOT NULL AND (@CurrentAvailabilityGroupRole <> 'PRIMARY' OR @CurrentAvailabilityGroupRole IS NULL)) + AND NOT (@AmazonRDS = 1 AND @CurrentDatabaseName = 'rdsadmin') + AND NOT (@CurrentIsReadOnly = 1) + AND (@CurrentExecuteAsUserExists = 1 OR @CurrentExecuteAsUserExists IS NULL) + BEGIN + + -- Select indexes in the current database + IF (EXISTS(SELECT * FROM @ActionsPreferred) OR @UpdateStatistics IS NOT NULL) AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, IndexID, IndexName, IndexType, AllowPageLocks, HasFilter, IsImageText, IsNewLOB, IsFileStream, HasClusteredColumnstore, HasNonClusteredColumnstore, IsComputed, IsClusteredIndexComputed, IsTimestamp, OnReadOnlyFileGroup, ResumableIndexOperation, StatisticsID, StatisticsName, NoRecompute, IsIncremental, PartitionID, PartitionNumber, PartitionCount, [Order], Selected, Completed' + + ' FROM ('; + + IF EXISTS(SELECT * FROM @ActionsPreferred) OR @UpdateStatistics IN('ALL','INDEX') + BEGIN + SET @CurrentCommand = @CurrentCommand + 'SELECT schemas.[schema_id] AS SchemaID' + + ', schemas.[name] AS SchemaName' + + ', objects.[object_id] AS ObjectID' + + ', objects.[name] AS ObjectName' + + ', RTRIM(objects.[type]) AS ObjectType' + + ', ' + CASE WHEN @Version >= 12 THEN 'tables.is_memory_optimized' ELSE '0' END + ' AS IsMemoryOptimized' + + ', indexes.index_id AS IndexID' + + ', indexes.[name] AS IndexName' + + ', indexes.[type] AS IndexType' + + ', indexes.allow_page_locks AS AllowPageLocks' + + ', indexes.has_filter AS HasFilter' + + + ', CASE WHEN indexes.[type] = 1 AND EXISTS(SELECT * FROM sys.columns columns INNER JOIN sys.types types ON columns.system_type_id = types.user_type_id WHERE columns.[object_id] = objects.object_id AND types.name IN(''image'',''text'',''ntext'')) THEN 1 ELSE 0 END AS IsImageText' + + + ', CASE WHEN indexes.[type] = 1 AND EXISTS(SELECT * FROM sys.columns columns INNER JOIN sys.types types ON columns.system_type_id = types.user_type_id OR (columns.user_type_id = types.user_type_id AND types.is_assembly_type = 1) WHERE columns.[object_id] = objects.object_id AND (types.name IN(''xml'') OR (types.name IN(''varchar'',''nvarchar'',''varbinary'') AND columns.max_length = -1) OR (types.is_assembly_type = 1 AND columns.max_length = -1))) THEN 1' + + ' WHEN indexes.[type] = 2 AND EXISTS(SELECT * FROM sys.index_columns index_columns INNER JOIN sys.columns columns ON index_columns.[object_id] = columns.[object_id] AND index_columns.column_id = columns.column_id INNER JOIN sys.types types ON columns.system_type_id = types.user_type_id OR (columns.user_type_id = types.user_type_id AND types.is_assembly_type = 1) WHERE index_columns.[object_id] = objects.object_id AND index_columns.index_id = indexes.index_id AND (types.[name] IN(''xml'') OR (types.[name] IN(''varchar'',''nvarchar'',''varbinary'') AND columns.max_length = -1) OR (types.is_assembly_type = 1 AND columns.max_length = -1))) THEN 1 ELSE 0 END AS IsNewLOB' + + + ', CASE WHEN indexes.[type] = 1 AND EXISTS(SELECT * FROM sys.columns columns WHERE columns.[object_id] = objects.object_id AND columns.is_filestream = 1) THEN 1 ELSE 0 END AS IsFileStream' + + + ', CASE WHEN EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = objects.object_id AND [type] = 5) THEN 1 ELSE 0 END AS HasClusteredColumnstore' + + + ', CASE WHEN EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = objects.object_id AND [type] = 6) THEN 1 ELSE 0 END AS HasNonClusteredColumnstore' + + + ', CASE WHEN EXISTS(SELECT * FROM sys.index_columns index_columns INNER JOIN sys.columns columns ON index_columns.object_id = columns.object_id AND index_columns.column_id = columns.column_id WHERE (index_columns.key_ordinal > 0 OR index_columns.partition_ordinal > 0) AND columns.is_computed = 1 AND index_columns.object_id = indexes.object_id AND index_columns.index_id = indexes.index_id) THEN 1 ELSE 0 END AS IsComputed' + + + ', CASE WHEN EXISTS(SELECT * FROM sys.index_columns index_columns INNER JOIN sys.columns columns ON index_columns.object_id = columns.object_id AND index_columns.column_id = columns.column_id INNER JOIN sys.indexes indexes2 ON index_columns.object_id = indexes2.object_id AND index_columns.index_id = indexes2.index_id WHERE (index_columns.key_ordinal > 0 OR index_columns.partition_ordinal > 0) AND columns.is_computed = 1 AND indexes2.[type] = 1 AND index_columns.object_id = indexes.object_id) THEN 1 ELSE 0 END AS IsClusteredIndexComputed' + + + ', CASE WHEN EXISTS(SELECT * FROM sys.index_columns index_columns INNER JOIN sys.columns columns ON index_columns.[object_id] = columns.[object_id] AND index_columns.column_id = columns.column_id INNER JOIN sys.types types ON columns.system_type_id = types.system_type_id WHERE index_columns.[object_id] = objects.object_id AND index_columns.index_id = indexes.index_id AND types.[name] = ''timestamp'') THEN 1 ELSE 0 END AS IsTimestamp' + + + ', CASE WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.destination_data_spaces destination_data_spaces ON indexes.data_space_id = destination_data_spaces.partition_scheme_id INNER JOIN sys.filegroups filegroups ON destination_data_spaces.data_space_id = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND indexes2.[object_id] = indexes.[object_id] AND indexes2.[index_id] = indexes.index_id' + CASE WHEN @PartitionLevel = 'Y' THEN ' AND destination_data_spaces.destination_id = partitions.partition_number' ELSE '' END + ') THEN 1' + + ' WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.filegroups filegroups ON indexes.data_space_id = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND indexes.[object_id] = indexes2.[object_id] AND indexes.[index_id] = indexes2.index_id) THEN 1' + + ' WHEN indexes.[type] = 1 AND EXISTS (SELECT * FROM sys.tables tables INNER JOIN sys.filegroups filegroups ON tables.lob_data_space_id = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND tables.[object_id] = objects.[object_id]) THEN 1 ELSE 0 END AS OnReadOnlyFileGroup' + + + ', ' + CASE WHEN @Version >= 14 OR SERVERPROPERTY('EngineEdition') IN (5, 8) THEN 'CASE WHEN EXISTS(SELECT * FROM sys.index_resumable_operations index_resumable_operations WHERE state_desc = ''PAUSED'' AND index_resumable_operations.object_id = indexes.object_id AND index_resumable_operations.index_id = indexes.index_id' + CASE WHEN @PartitionLevel = 'Y' THEN ' AND (index_resumable_operations.partition_number = partitions.partition_number OR index_resumable_operations.partition_number IS NULL)' ELSE '' END + ') THEN 1 ELSE 0 END' ELSE '0' END + ' AS ResumableIndexOperation' + + + ', stats.stats_id AS StatisticsID' + + ', stats.name AS StatisticsName' + + ', stats.no_recompute AS NoRecompute' + + ', ' + CASE WHEN @Version >= 12 THEN 'stats.is_incremental' ELSE '0' END + ' AS IsIncremental' + + ', ' + CASE WHEN @PartitionLevel = 'Y' THEN 'partitions.partition_id AS PartitionID' WHEN @PartitionLevel = 'N' THEN 'NULL AS PartitionID' END + + ', ' + CASE WHEN @PartitionLevel = 'Y' THEN 'partitions.partition_number AS PartitionNumber' WHEN @PartitionLevel = 'N' THEN 'NULL AS PartitionNumber' END + + ', ' + CASE WHEN @PartitionLevel = 'Y' THEN 'IndexPartitions.partition_count AS PartitionCount' WHEN @PartitionLevel = 'N' THEN 'NULL AS PartitionCount' END + + ', 0 AS [Order]' + + ', 0 AS Selected' + + ', 0 AS Completed' + + ' FROM sys.indexes indexes' + + ' INNER JOIN sys.objects objects ON indexes.[object_id] = objects.[object_id]' + + ' INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id]' + + ' LEFT OUTER JOIN sys.tables tables ON objects.[object_id] = tables.[object_id]' + + ' LEFT OUTER JOIN sys.stats stats ON indexes.[object_id] = stats.[object_id] AND indexes.[index_id] = stats.[stats_id]'; + IF @PartitionLevel = 'Y' + BEGIN + SET @CurrentCommand = @CurrentCommand + ' LEFT OUTER JOIN sys.partitions partitions ON indexes.[object_id] = partitions.[object_id] AND indexes.index_id = partitions.index_id' + + ' LEFT OUTER JOIN (SELECT partitions.[object_id], partitions.index_id, COUNT(DISTINCT partitions.partition_number) AS partition_count FROM sys.partitions partitions GROUP BY partitions.[object_id], partitions.index_id) IndexPartitions ON partitions.[object_id] = IndexPartitions.[object_id] AND partitions.[index_id] = IndexPartitions.[index_id]'; + END; + + SET @CurrentCommand = @CurrentCommand + ' WHERE objects.[type] IN(''U'',''V'')' + + CASE WHEN @MSShippedObjects = 'N' THEN ' AND objects.is_ms_shipped = 0' ELSE '' END + + ' AND indexes.[type] IN(1,2,3,4,5,6,7)' + + ' AND indexes.is_disabled = 0 AND indexes.is_hypothetical = 0'; + END; + + IF (EXISTS(SELECT * FROM @ActionsPreferred) AND @UpdateStatistics = 'COLUMNS') OR @UpdateStatistics = 'ALL' + BEGIN + SET @CurrentCommand = @CurrentCommand + ' UNION '; + END; + + IF @UpdateStatistics IN('ALL','COLUMNS') + BEGIN + SET @CurrentCommand = @CurrentCommand + 'SELECT schemas.[schema_id] AS SchemaID' + + ', schemas.[name] AS SchemaName' + + ', objects.[object_id] AS ObjectID' + + ', objects.[name] AS ObjectName' + + ', RTRIM(objects.[type]) AS ObjectType' + + ', ' + CASE WHEN @Version >= 12 THEN 'tables.is_memory_optimized' ELSE '0' END + ' AS IsMemoryOptimized' + + ', NULL AS IndexID, NULL AS IndexName' + + ', NULL AS IndexType' + + ', NULL AS AllowPageLocks' + + ', NULL AS HasFilter' + + ', NULL AS IsImageText' + + ', NULL AS IsNewLOB' + + ', NULL AS IsFileStream' + + ', NULL AS HasClusteredColumnstore' + + ', NULL AS HasNonClusteredColumnstore' + + ', NULL AS IsComputed' + + ', NULL AS IsClusteredIndexComputed' + + ', NULL AS IsTimestamp' + + ', NULL AS OnReadOnlyFileGroup' + + ', NULL AS ResumableIndexOperation' + + ', stats.stats_id AS StatisticsID' + + ', stats.name AS StatisticsName' + + ', stats.no_recompute AS NoRecompute' + + ', ' + CASE WHEN @Version >= 12 THEN 'stats.is_incremental' ELSE '0' END + ' AS IsIncremental' + + ', NULL AS PartitionID' + + ', ' + CASE WHEN @PartitionLevelStatistics = 1 THEN 'dm_db_incremental_stats_properties.partition_number' ELSE 'NULL' END + ' AS PartitionNumber' + + ', NULL AS PartitionCount' + + ', 0 AS [Order]' + + ', 0 AS Selected' + + ', 0 AS Completed' + + ' FROM sys.stats stats' + + ' INNER JOIN sys.objects objects ON stats.[object_id] = objects.[object_id]' + + ' INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id]' + + ' LEFT OUTER JOIN sys.tables tables ON objects.[object_id] = tables.[object_id]'; + + IF @PartitionLevelStatistics = 1 + BEGIN + SET @CurrentCommand = @CurrentCommand + ' OUTER APPLY sys.dm_db_incremental_stats_properties(stats.object_id, stats.stats_id) dm_db_incremental_stats_properties'; + END; + + SET @CurrentCommand = @CurrentCommand + ' WHERE objects.[type] IN(''U'',''V'')' + + CASE WHEN @Version >= 12 THEN ' AND (tables.is_memory_optimized = 0 OR tables.is_memory_optimized IS NULL)' ELSE '' END + + CASE WHEN @MSShippedObjects = 'N' THEN ' AND objects.is_ms_shipped = 0' ELSE '' END + + ' AND NOT EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = stats.[object_id] AND indexes.index_id = stats.stats_id)' + + ' AND NOT EXISTS(SELECT * FROM sys.indexes indexes2 WHERE indexes2.[object_id] = stats.[object_id] AND indexes2.type = 1 AND indexes2.is_disabled = 1)'; + + IF @Version >= 12 + BEGIN + SET @CurrentCommand = @CurrentCommand + ' UNION '; + + SET @CurrentCommand = @CurrentCommand + 'SELECT schemas.[schema_id] AS SchemaID' + + ', schemas.[name] AS SchemaName' + + ', objects.[object_id] AS ObjectID' + + ', objects.[name] AS ObjectName' + + ', RTRIM(objects.[type]) AS ObjectType' + + ', tables.is_memory_optimized AS IsMemoryOptimized' + + ', NULL AS IndexID, NULL AS IndexName' + + ', NULL AS IndexType' + + ', NULL AS AllowPageLocks' + + ', NULL AS HasFilter' + + ', NULL AS IsImageText' + + ', NULL AS IsNewLOB' + + ', NULL AS IsFileStream' + + ', NULL AS HasClusteredColumnstore' + + ', NULL AS HasNonClusteredColumnstore' + + ', NULL AS IsComputed' + + ', NULL AS IsClusteredIndexComputed' + + ', NULL AS IsTimestamp' + + ', NULL AS OnReadOnlyFileGroup' + + ', NULL AS ResumableIndexOperation' + + ', stats.stats_id AS StatisticsID' + + ', stats.name AS StatisticsName' + + ', stats.no_recompute AS NoRecompute' + + ', ' + CASE WHEN @Version >= 12 THEN 'stats.is_incremental' ELSE '0' END + ' AS IsIncremental' + + ', NULL AS PartitionID' + + ', NULL AS PartitionNumber' + + ', NULL AS PartitionCount' + + ', 0 AS [Order]' + + ', 0 AS Selected' + + ', 0 AS Completed' + + ' FROM sys.stats stats' + + ' INNER JOIN sys.objects objects ON stats.[object_id] = objects.[object_id]' + + ' INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id]' + + ' INNER JOIN sys.tables tables ON objects.[object_id] = tables.[object_id]'; + + SET @CurrentCommand = @CurrentCommand + ' WHERE objects.[type] = ''U''' + + ' AND tables.is_memory_optimized = 1' + + CASE WHEN @MSShippedObjects = 'N' THEN ' AND objects.is_ms_shipped = 0' ELSE '' END + + ' AND NOT EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = stats.[object_id] AND indexes.index_id = stats.stats_id)'; + END; + END; + + SET @CurrentCommand = @CurrentCommand + ') IndexesStatistics'; + + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, [objectid], ObjectName, ObjectType, IsMemoryOptimized, [indexid], IndexName, IndexType, AllowPageLocks, HasFilter, IsImageText, IsNewLOB, IsFileStream, HasClusteredColumnstore, HasNonClusteredColumnstore, [iscomputed], IsClusteredIndexComputed, IsTimestamp, OnReadOnlyFileGroup, ResumableIndexOperation, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, PartitionID, PartitionNumber, PartitionCount, [Order], Selected, Completed) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand; + SET @Error = @@ERROR; + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error; + END; + END; + + IF @Indexes IS NULL + BEGIN + UPDATE tmpIndexesStatistics + SET tmpIndexesStatistics.Selected = 1 + FROM @tmpIndexesStatistics tmpIndexesStatistics; + END; + ELSE + BEGIN + UPDATE tmpIndexesStatistics + SET tmpIndexesStatistics.Selected = SelectedIndexes.Selected + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN @SelectedIndexes SelectedIndexes + ON @CurrentDatabaseName LIKE REPLACE(SelectedIndexes.DatabaseName,'_','[_]') AND tmpIndexesStatistics.SchemaName LIKE REPLACE(SelectedIndexes.SchemaName,'_','[_]') AND tmpIndexesStatistics.ObjectName LIKE REPLACE(SelectedIndexes.ObjectName,'_','[_]') AND COALESCE(tmpIndexesStatistics.IndexName,tmpIndexesStatistics.StatisticsName) LIKE REPLACE(SelectedIndexes.IndexName,'_','[_]') + WHERE SelectedIndexes.Selected = 1; + + UPDATE tmpIndexesStatistics + SET tmpIndexesStatistics.Selected = SelectedIndexes.Selected + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN @SelectedIndexes SelectedIndexes + ON @CurrentDatabaseName LIKE REPLACE(SelectedIndexes.DatabaseName,'_','[_]') AND tmpIndexesStatistics.SchemaName LIKE REPLACE(SelectedIndexes.SchemaName,'_','[_]') AND tmpIndexesStatistics.ObjectName LIKE REPLACE(SelectedIndexes.ObjectName,'_','[_]') AND COALESCE(tmpIndexesStatistics.IndexName,tmpIndexesStatistics.StatisticsName) LIKE REPLACE(SelectedIndexes.IndexName,'_','[_]') + WHERE SelectedIndexes.Selected = 0; + + UPDATE tmpIndexesStatistics + SET tmpIndexesStatistics.StartPosition = SelectedIndexes2.StartPosition + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN (SELECT tmpIndexesStatistics.SchemaName, tmpIndexesStatistics.ObjectName, tmpIndexesStatistics.IndexName, tmpIndexesStatistics.StatisticsName, MIN(SelectedIndexes.StartPosition) AS StartPosition + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN @SelectedIndexes SelectedIndexes + ON @CurrentDatabaseName LIKE REPLACE(SelectedIndexes.DatabaseName,'_','[_]') AND tmpIndexesStatistics.SchemaName LIKE REPLACE(SelectedIndexes.SchemaName,'_','[_]') AND tmpIndexesStatistics.ObjectName LIKE REPLACE(SelectedIndexes.ObjectName,'_','[_]') AND COALESCE(tmpIndexesStatistics.IndexName,tmpIndexesStatistics.StatisticsName) LIKE REPLACE(SelectedIndexes.IndexName,'_','[_]') + WHERE SelectedIndexes.Selected = 1 + GROUP BY tmpIndexesStatistics.SchemaName, tmpIndexesStatistics.ObjectName, tmpIndexesStatistics.IndexName, tmpIndexesStatistics.StatisticsName) SelectedIndexes2 + ON tmpIndexesStatistics.SchemaName = SelectedIndexes2.SchemaName + AND tmpIndexesStatistics.ObjectName = SelectedIndexes2.ObjectName + AND (tmpIndexesStatistics.IndexName = SelectedIndexes2.IndexName OR tmpIndexesStatistics.IndexName IS NULL) + AND (tmpIndexesStatistics.StatisticsName = SelectedIndexes2.StatisticsName OR tmpIndexesStatistics.StatisticsName IS NULL); + END; + + WITH tmpIndexesStatistics AS ( + SELECT SchemaName, ObjectName, [Order], ROW_NUMBER() OVER (ORDER BY ISNULL(ResumableIndexOperation,0) DESC, StartPosition ASC, SchemaName ASC, ObjectName ASC, CASE WHEN IndexType IS NULL THEN 1 ELSE 0 END ASC, IndexType ASC, IndexName ASC, StatisticsName ASC, PartitionNumber ASC) AS RowNumber + FROM @tmpIndexesStatistics tmpIndexesStatistics + WHERE Selected = 1 + ) + UPDATE tmpIndexesStatistics + SET [Order] = RowNumber; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + '.' + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName) + ', ' + FROM @SelectedIndexes SelectedIndexes + WHERE DatabaseName = @CurrentDatabaseName + AND SchemaName NOT LIKE '%[%]%' + AND ObjectName NOT LIKE '%[%]%' + AND IndexName LIKE '%[%]%' + AND NOT EXISTS (SELECT * FROM @tmpIndexesStatistics WHERE SchemaName = SelectedIndexes.SchemaName AND ObjectName = SelectedIndexes.ObjectName); + IF @@ROWCOUNT > 0 + BEGIN + SET @ErrorMessage = 'The following objects in the @Indexes parameter do not exist: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.'; + RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT; + SET @Error = @@ERROR; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + END; + + SET @ErrorMessage = ''; + SELECT @ErrorMessage = @ErrorMessage + QUOTENAME(DatabaseName) + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName) + '.' + QUOTENAME(IndexName) + ', ' + FROM @SelectedIndexes SelectedIndexes + WHERE DatabaseName = @CurrentDatabaseName + AND SchemaName NOT LIKE '%[%]%' + AND ObjectName NOT LIKE '%[%]%' + AND IndexName NOT LIKE '%[%]%' + AND NOT EXISTS (SELECT * FROM @tmpIndexesStatistics WHERE SchemaName = SelectedIndexes.SchemaName AND ObjectName = SelectedIndexes.ObjectName AND IndexName = SelectedIndexes.IndexName); + IF @@ROWCOUNT > 0 + BEGIN + SET @ErrorMessage = 'The following indexes in the @Indexes parameter do not exist: ' + LEFT(@ErrorMessage,LEN(@ErrorMessage)-1) + '.'; + RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT; + SET @Error = @@ERROR; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + END; + + WHILE (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SELECT TOP 1 @CurrentIxID = [Id], + @CurrentIxOrder = [Order], + @CurrentSchemaID = SchemaID, + @CurrentSchemaName = SchemaName, + @CurrentObjectID = [objectid], + @CurrentObjectName = ObjectName, + @CurrentObjectType = ObjectType, + @CurrentIsMemoryOptimized = IsMemoryOptimized, + @CurrentIndexID = [indexid], + @CurrentIndexName = IndexName, + @CurrentIndexType = IndexType, + @CurrentAllowPageLocks = AllowPageLocks, + @CurrentHasFilter = HasFilter, + @CurrentIsImageText = IsImageText, + @CurrentIsNewLOB = IsNewLOB, + @CurrentIsFileStream = IsFileStream, + @CurrentHasClusteredColumnstore = HasClusteredColumnstore, + @CurrentHasNonClusteredColumnstore = HasNonClusteredColumnstore, + @CurrentIsComputed = [iscomputed], + @CurrentIsClusteredIndexComputed = IsClusteredIndexComputed, + @CurrentIsTimestamp = IsTimestamp, + @CurrentOnReadOnlyFileGroup = OnReadOnlyFileGroup, + @CurrentResumableIndexOperation = ResumableIndexOperation, + @CurrentStatisticsID = StatisticsID, + @CurrentStatisticsName = StatisticsName, + @CurrentNoRecompute = [NoRecompute], + @CurrentIsIncremental = IsIncremental, + @CurrentPartitionID = PartitionID, + @CurrentPartitionNumber = PartitionNumber, + @CurrentPartitionCount = PartitionCount + FROM @tmpIndexesStatistics + WHERE Selected = 1 + AND Completed = 0 + ORDER BY [Order] ASC; + + IF @@ROWCOUNT = 0 + BEGIN + BREAK; + END; + + -- Is the index a partition? + IF @CurrentPartitionNumber IS NULL OR @CurrentPartitionCount = 1 BEGIN SET @CurrentIsPartition = 0; END; ELSE BEGIN SET @CurrentIsPartition = 1; END; + + -- Does the index exist? + IF @CurrentIndexID IS NOT NULL AND EXISTS(SELECT * FROM @ActionsPreferred) + BEGIN + SET @CurrentCommand = ''; + + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS NVARCHAR) + '; '; + + IF @CurrentIsPartition = 0 SET @CurrentCommand += 'IF EXISTS(SELECT * FROM sys.indexes indexes INNER JOIN sys.objects objects ON indexes.[object_id] = objects.[object_id] INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id] WHERE objects.[type] IN(''U'',''V'') AND indexes.[type] IN(1,2,3,4,5,6,7) AND indexes.is_disabled = 0 AND indexes.is_hypothetical = 0 AND schemas.[schema_id] = @ParamSchemaID AND schemas.[name] = @ParamSchemaName AND objects.[object_id] = @ParamObjectID AND objects.[name] = @ParamObjectName AND objects.[type] = @ParamObjectType AND indexes.index_id = @ParamIndexID AND indexes.[name] = @ParamIndexName AND indexes.[type] = @ParamIndexType) BEGIN SET @ParamIndexExists = 1 END'; + IF @CurrentIsPartition = 1 SET @CurrentCommand += 'IF EXISTS(SELECT * FROM sys.indexes indexes INNER JOIN sys.objects objects ON indexes.[object_id] = objects.[object_id] INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id] INNER JOIN sys.partitions partitions ON indexes.[object_id] = partitions.[object_id] AND indexes.index_id = partitions.index_id WHERE objects.[type] IN(''U'',''V'') AND indexes.[type] IN(1,2,3,4,5,6,7) AND indexes.is_disabled = 0 AND indexes.is_hypothetical = 0 AND schemas.[schema_id] = @ParamSchemaID AND schemas.[name] = @ParamSchemaName AND objects.[object_id] = @ParamObjectID AND objects.[name] = @ParamObjectName AND objects.[type] = @ParamObjectType AND indexes.index_id = @ParamIndexID AND indexes.[name] = @ParamIndexName AND indexes.[type] = @ParamIndexType AND partitions.partition_id = @ParamPartitionID AND partitions.partition_number = @ParamPartitionNumber) BEGIN SET @ParamIndexExists = 1 END'; + + BEGIN TRY + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamSchemaID int, @ParamSchemaName sysname, @ParamObjectID int, @ParamObjectName sysname, @ParamObjectType sysname, @ParamIndexID int, @ParamIndexName sysname, @ParamIndexType int, @ParamPartitionID bigint, @ParamPartitionNumber int, @ParamIndexExists bit OUTPUT', @ParamSchemaID = @CurrentSchemaID, @ParamSchemaName = @CurrentSchemaName, @ParamObjectID = @CurrentObjectID, @ParamObjectName = @CurrentObjectName, @ParamObjectType = @CurrentObjectType, @ParamIndexID = @CurrentIndexID, @ParamIndexName = @CurrentIndexName, @ParamIndexType = @CurrentIndexType, @ParamPartitionID = @CurrentPartitionID, @ParamPartitionNumber = @CurrentPartitionNumber, @ParamIndexExists = @CurrentIndexExists OUTPUT; + + IF @CurrentIndexExists IS NULL + BEGIN + SET @CurrentIndexExists = 0; + GOTO NoAction; + END; + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS NVARCHAR) + ', ' + ISNULL(ERROR_MESSAGE(),'') + CASE WHEN ERROR_NUMBER() = 1222 THEN ' The index ' + QUOTENAME(@CurrentIndexName) + ' on the object ' + QUOTENAME(@CurrentDatabaseName) + '.' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName) + ' is locked. It could not be checked if the index exists.' ELSE '' END; + SET @Severity = CASE WHEN ERROR_NUMBER() IN(1205,1222) THEN @LockMessageSeverity ELSE 16 END; + RAISERROR('%s',@Severity,1,@ErrorMessage) WITH NOWAIT; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + IF NOT (ERROR_NUMBER() IN(1205,1222) AND @LockMessageSeverity = 10) + BEGIN + SET @ReturnCode = ERROR_NUMBER(); + END; + + GOTO NoAction; + END CATCH; + END; + + -- Does the statistics exist? + IF @CurrentStatisticsID IS NOT NULL AND @UpdateStatistics IS NOT NULL + BEGIN + SET @CurrentCommand = ''; + + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS NVARCHAR) + '; '; + + SET @CurrentCommand += 'IF EXISTS(SELECT * FROM sys.stats stats INNER JOIN sys.objects objects ON stats.[object_id] = objects.[object_id] INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id] WHERE objects.[type] IN(''U'',''V'')' + CASE WHEN @MSShippedObjects = 'N' THEN ' AND objects.is_ms_shipped = 0' ELSE '' END + ' AND schemas.[schema_id] = @ParamSchemaID AND schemas.[name] = @ParamSchemaName AND objects.[object_id] = @ParamObjectID AND objects.[name] = @ParamObjectName AND objects.[type] = @ParamObjectType AND stats.stats_id = @ParamStatisticsID AND stats.[name] = @ParamStatisticsName) BEGIN SET @ParamStatisticsExists = 1 END'; + + BEGIN TRY + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamSchemaID int, @ParamSchemaName sysname, @ParamObjectID int, @ParamObjectName sysname, @ParamObjectType sysname, @ParamStatisticsID int, @ParamStatisticsName sysname, @ParamStatisticsExists bit OUTPUT', @ParamSchemaID = @CurrentSchemaID, @ParamSchemaName = @CurrentSchemaName, @ParamObjectID = @CurrentObjectID, @ParamObjectName = @CurrentObjectName, @ParamObjectType = @CurrentObjectType, @ParamStatisticsID = @CurrentStatisticsID, @ParamStatisticsName = @CurrentStatisticsName, @ParamStatisticsExists = @CurrentStatisticsExists OUTPUT; + + IF @CurrentStatisticsExists IS NULL + BEGIN + SET @CurrentStatisticsExists = 0; + GOTO NoAction; + END; + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS NVARCHAR) + ', ' + ISNULL(ERROR_MESSAGE(),'') + CASE WHEN ERROR_NUMBER() = 1222 THEN ' The statistics ' + QUOTENAME(@CurrentStatisticsName) + ' on the object ' + QUOTENAME(@CurrentDatabaseName) + '.' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName) + ' is locked. It could not be checked if the statistics exists.' ELSE '' END; + SET @Severity = CASE WHEN ERROR_NUMBER() IN(1205,1222) THEN @LockMessageSeverity ELSE 16 END; + RAISERROR('%s',@Severity,1,@ErrorMessage) WITH NOWAIT; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + IF NOT (ERROR_NUMBER() IN(1205,1222) AND @LockMessageSeverity = 10) + BEGIN + SET @ReturnCode = ERROR_NUMBER(); + END; + + GOTO NoAction; + END CATCH; + END; + + -- Has the data in the statistics been modified since the statistics was last updated? + IF @CurrentStatisticsID IS NOT NULL AND @UpdateStatistics IS NOT NULL + BEGIN + SET @CurrentCommand = ''; + + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS NVARCHAR) + '; '; + + IF @PartitionLevelStatistics = 1 AND @CurrentIsIncremental = 1 + BEGIN + SET @CurrentCommand += 'SELECT @ParamRowCount = [rows], @ParamModificationCounter = modification_counter FROM sys.dm_db_incremental_stats_properties (@ParamObjectID, @ParamStatisticsID) WHERE partition_number = @ParamPartitionNumber'; + END; + ELSE + IF (@Version >= 10.504000 AND @Version < 11) OR @Version >= 11.03000 + BEGIN + SET @CurrentCommand += 'SELECT @ParamRowCount = [rows], @ParamModificationCounter = modification_counter FROM sys.dm_db_stats_properties (@ParamObjectID, @ParamStatisticsID)'; + END; + ELSE + BEGIN + SET @CurrentCommand += 'SELECT @ParamRowCount = rowcnt, @ParamModificationCounter = rowmodctr FROM sys.sysindexes sysindexes WHERE sysindexes.[id] = @ParamObjectID AND sysindexes.[indid] = @ParamStatisticsID'; + END; + + BEGIN TRY + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamObjectID int, @ParamStatisticsID int, @ParamPartitionNumber int, @ParamRowCount bigint OUTPUT, @ParamModificationCounter bigint OUTPUT', @ParamObjectID = @CurrentObjectID, @ParamStatisticsID = @CurrentStatisticsID, @ParamPartitionNumber = @CurrentPartitionNumber, @ParamRowCount = @CurrentRowCount OUTPUT, @ParamModificationCounter = @CurrentModificationCounter OUTPUT; + + IF @CurrentRowCount IS NULL SET @CurrentRowCount = 0; + IF @CurrentModificationCounter IS NULL SET @CurrentModificationCounter = 0; + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS NVARCHAR) + ', ' + ISNULL(ERROR_MESSAGE(),'') + CASE WHEN ERROR_NUMBER() = 1222 THEN ' The statistics ' + QUOTENAME(@CurrentStatisticsName) + ' on the object ' + QUOTENAME(@CurrentDatabaseName) + '.' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName) + ' is locked. The rows and modification_counter could not be checked.' ELSE '' END; + SET @Severity = CASE WHEN ERROR_NUMBER() IN(1205,1222) THEN @LockMessageSeverity ELSE 16 END; + RAISERROR('%s',@Severity,1,@ErrorMessage) WITH NOWAIT; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + IF NOT (ERROR_NUMBER() IN(1205,1222) AND @LockMessageSeverity = 10) + BEGIN + SET @ReturnCode = ERROR_NUMBER(); + END; + + GOTO NoAction; + END CATCH; + END; + + -- Is the index fragmented? + IF @CurrentIndexID IS NOT NULL + AND @CurrentOnReadOnlyFileGroup = 0 + AND EXISTS(SELECT * FROM @ActionsPreferred) + AND (EXISTS(SELECT [Priority], [Action], COUNT(*) FROM @ActionsPreferred GROUP BY [Priority], [Action] HAVING COUNT(*) <> 3) OR @MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) + AND NOT (SERVERPROPERTY('EngineEdition') = 8 AND @CurrentDatabaseName IN ('master', 'model')) + BEGIN + SET @CurrentCommand = ''; + + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS NVARCHAR) + '; '; + + SET @CurrentCommand += 'SELECT @ParamFragmentationLevel = MAX(avg_fragmentation_in_percent), @ParamPageCount = SUM(page_count) FROM sys.dm_db_index_physical_stats(DB_ID(@ParamDatabaseName), @ParamObjectID, @ParamIndexID, @ParamPartitionNumber, ''LIMITED'') WHERE alloc_unit_type_desc = ''IN_ROW_DATA'' AND index_level = 0'; + + BEGIN TRY + EXECUTE sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseName nvarchar(max), @ParamObjectID int, @ParamIndexID int, @ParamPartitionNumber int, @ParamFragmentationLevel float OUTPUT, @ParamPageCount bigint OUTPUT', @ParamDatabaseName = @CurrentDatabaseName, @ParamObjectID = @CurrentObjectID, @ParamIndexID = @CurrentIndexID, @ParamPartitionNumber = @CurrentPartitionNumber, @ParamFragmentationLevel = @CurrentFragmentationLevel OUTPUT, @ParamPageCount = @CurrentPageCount OUTPUT; + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS NVARCHAR) + ', ' + ISNULL(ERROR_MESSAGE(),'') + CASE WHEN ERROR_NUMBER() = 1222 THEN ' The index ' + QUOTENAME(@CurrentIndexName) + ' on the object ' + QUOTENAME(@CurrentDatabaseName) + '.' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName) + ' is locked. The page_count and avg_fragmentation_in_percent could not be checked.' ELSE '' END; + SET @Severity = CASE WHEN ERROR_NUMBER() IN(1205,1222) THEN @LockMessageSeverity ELSE 16 END; + RAISERROR('%s',@Severity,1,@ErrorMessage) WITH NOWAIT; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + IF NOT (ERROR_NUMBER() IN(1205,1222) AND @LockMessageSeverity = 10) + BEGIN + SET @ReturnCode = ERROR_NUMBER(); + END; + + GOTO NoAction; + END CATCH; + END; + + -- Select fragmentation group + IF @CurrentIndexID IS NOT NULL AND @CurrentOnReadOnlyFileGroup = 0 AND EXISTS(SELECT * FROM @ActionsPreferred) + BEGIN + SET @CurrentFragmentationGroup = CASE + WHEN @CurrentFragmentationLevel >= @FragmentationLevel2 THEN 'High' + WHEN @CurrentFragmentationLevel >= @FragmentationLevel1 AND @CurrentFragmentationLevel < @FragmentationLevel2 THEN 'Medium' + WHEN @CurrentFragmentationLevel < @FragmentationLevel1 THEN 'Low' + END; + END; + + -- Which actions are allowed? + IF @CurrentIndexID IS NOT NULL AND EXISTS(SELECT * FROM @ActionsPreferred) + BEGIN + IF NOT (@CurrentOnReadOnlyFileGroup = 1) + AND NOT (@CurrentIsMemoryOptimized = 1) + AND NOT (@CurrentAllowPageLocks = 0) + BEGIN + INSERT INTO @CurrentActionsAllowed ([Action]) + VALUES ('INDEX_REORGANIZE'); + END; + IF NOT (@CurrentOnReadOnlyFileGroup = 1) + AND NOT (@CurrentIsMemoryOptimized = 1) + BEGIN + INSERT INTO @CurrentActionsAllowed ([Action]) + VALUES ('INDEX_REBUILD_OFFLINE'); + END; + IF SERVERPROPERTY('EngineEdition') IN (3, 5, 8) + AND NOT (@CurrentOnReadOnlyFileGroup = 1) + AND NOT (@CurrentIsMemoryOptimized = 1) + AND NOT (@CurrentIsPartition = 1 AND @Version < 12) + AND NOT (@CurrentIndexType = 1 AND @CurrentIsImageText = 1) + AND NOT (@CurrentIndexType = 1 AND @CurrentIsFileStream = 1) + AND NOT (@CurrentIndexType = 1 AND @CurrentIsNewLOB = 1 AND @Version < 11) + AND NOT (@CurrentIndexType = 2 AND @CurrentIsNewLOB = 1 AND @Version < 11) + AND NOT (@CurrentIndexType = 3) + AND NOT (@CurrentIndexType = 4) + AND NOT (@CurrentIndexType = 5 AND @Version < 15) + AND NOT (@CurrentIndexType = 6 AND @Version < 14) + AND NOT (@CurrentIndexType = 1 AND @CurrentHasNonClusteredColumnstore = 1 AND @Version < 13) + AND NOT (@CurrentIndexType = 2 AND @CurrentHasClusteredColumnstore = 1 AND @Version < 15) + AND NOT (@CurrentIndexType = 2 AND @CurrentHasNonClusteredColumnstore = 1 AND @Version < 13) + BEGIN + INSERT INTO @CurrentActionsAllowed ([Action]) + VALUES ('INDEX_REBUILD_ONLINE'); + END; + END; + + -- Decide action + IF @CurrentIndexID IS NOT NULL + AND EXISTS(SELECT * FROM @ActionsPreferred) + AND (@CurrentPageCount >= @MinNumberOfPages OR @MinNumberOfPages = 0) + AND (@CurrentPageCount <= @MaxNumberOfPages OR @MaxNumberOfPages IS NULL) + AND @CurrentResumableIndexOperation = 0 + BEGIN + IF EXISTS(SELECT [Priority], [Action], COUNT(*) FROM @ActionsPreferred GROUP BY [Priority], [Action] HAVING COUNT(*) <> 3) + BEGIN + SELECT @CurrentAction = [Action] + FROM @ActionsPreferred + WHERE FragmentationGroup = @CurrentFragmentationGroup + AND [Priority] = (SELECT MIN([Priority]) + FROM @ActionsPreferred + WHERE FragmentationGroup = @CurrentFragmentationGroup + AND [Action] IN (SELECT [Action] FROM @CurrentActionsAllowed)); + END; + ELSE + BEGIN + SELECT @CurrentAction = [Action] + FROM @ActionsPreferred + WHERE [Priority] = (SELECT MIN([Priority]) + FROM @ActionsPreferred + WHERE [Action] IN (SELECT [Action] FROM @CurrentActionsAllowed)); + END; + END; + + IF @CurrentResumableIndexOperation = 1 + BEGIN + SET @CurrentAction = 'INDEX_REBUILD_ONLINE'; + END; + + -- Workaround for limitation in SQL Server, http://support.microsoft.com/kb/2292737 + IF @CurrentIndexID IS NOT NULL + BEGIN + SET @CurrentMaxDOP = @MaxDOP; + + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentAllowPageLocks = 0 + BEGIN + SET @CurrentMaxDOP = 1; + END; + END; + + -- Update statistics? + IF @CurrentStatisticsID IS NOT NULL + AND ((@UpdateStatistics = 'ALL' AND (@CurrentIndexType IN (1,2,3,4,7) OR @CurrentIndexID IS NULL)) OR (@UpdateStatistics = 'INDEX' AND @CurrentIndexID IS NOT NULL AND @CurrentIndexType IN (1,2,3,4,7)) OR (@UpdateStatistics = 'COLUMNS' AND @CurrentIndexID IS NULL)) + AND ((@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) OR (@OnlyModifiedStatistics = 'Y' AND @CurrentModificationCounter > 0) OR ((@CurrentModificationCounter * 1. / NULLIF(@CurrentRowCount,0)) * 100 >= @StatisticsModificationLevel) OR (@StatisticsModificationLevel IS NOT NULL AND @CurrentModificationCounter > 0 AND (@CurrentModificationCounter >= SQRT(@CurrentRowCount * 1000))) OR (@CurrentIsMemoryOptimized = 1 AND NOT (@Version >= 13 OR SERVERPROPERTY('EngineEdition') IN (5,8)))) + AND ((@CurrentIsPartition = 0 AND (@CurrentAction NOT IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') OR @CurrentAction IS NULL)) OR (@CurrentIsPartition = 1 AND (@CurrentPartitionNumber = @CurrentPartitionCount OR (@PartitionLevelStatistics = 1 AND @CurrentIsIncremental = 1)))) + BEGIN + SET @CurrentUpdateStatistics = 'Y'; + END; + ELSE + BEGIN + SET @CurrentUpdateStatistics = 'N'; + END; + + SET @CurrentStatisticsSample = @StatisticsSample; + SET @CurrentStatisticsResample = @StatisticsResample; + + -- Memory-optimized tables only supports FULLSCAN and RESAMPLE in SQL Server 2014 + IF @CurrentIsMemoryOptimized = 1 AND NOT (@Version >= 13 OR SERVERPROPERTY('EngineEdition') IN (5,8)) AND (@CurrentStatisticsSample <> 100 OR @CurrentStatisticsSample IS NULL) + BEGIN + SET @CurrentStatisticsSample = NULL; + SET @CurrentStatisticsResample = 'Y'; + END; + + -- Incremental statistics only supports RESAMPLE + IF @PartitionLevelStatistics = 1 AND @CurrentIsIncremental = 1 + BEGIN + SET @CurrentStatisticsSample = NULL; + SET @CurrentStatisticsResample = 'Y'; + END; + + -- Create index comment + IF @CurrentIndexID IS NOT NULL + BEGIN + SET @CurrentComment = 'ObjectType: ' + CASE WHEN @CurrentObjectType = 'U' THEN 'Table' WHEN @CurrentObjectType = 'V' THEN 'View' ELSE 'N/A' END + ', '; + SET @CurrentComment += 'IndexType: ' + CASE WHEN @CurrentIndexType = 1 THEN 'Clustered' WHEN @CurrentIndexType = 2 THEN 'NonClustered' WHEN @CurrentIndexType = 3 THEN 'XML' WHEN @CurrentIndexType = 4 THEN 'Spatial' WHEN @CurrentIndexType = 5 THEN 'Clustered Columnstore' WHEN @CurrentIndexType = 6 THEN 'NonClustered Columnstore' WHEN @CurrentIndexType = 7 THEN 'NonClustered Hash' ELSE 'N/A' END + ', '; + SET @CurrentComment += 'ImageText: ' + CASE WHEN @CurrentIsImageText = 1 THEN 'Yes' WHEN @CurrentIsImageText = 0 THEN 'No' ELSE 'N/A' END + ', '; + SET @CurrentComment += 'NewLOB: ' + CASE WHEN @CurrentIsNewLOB = 1 THEN 'Yes' WHEN @CurrentIsNewLOB = 0 THEN 'No' ELSE 'N/A' END + ', '; + SET @CurrentComment += 'FileStream: ' + CASE WHEN @CurrentIsFileStream = 1 THEN 'Yes' WHEN @CurrentIsFileStream = 0 THEN 'No' ELSE 'N/A' END + ', '; + IF @Version >= 12 AND @CurrentIndexType NOT IN(5, 6) SET @CurrentComment += 'HasClusteredColumnstore: ' + CASE WHEN @CurrentHasClusteredColumnstore = 1 THEN 'Yes' WHEN @CurrentHasClusteredColumnstore = 0 THEN 'No' ELSE 'N/A' END + ', '; + IF @Version >= 11 AND @CurrentIndexType NOT IN(5, 6) SET @CurrentComment += 'HasNonClusteredColumnstore: ' + CASE WHEN @CurrentHasNonClusteredColumnstore = 1 THEN 'Yes' WHEN @CurrentHasNonClusteredColumnstore = 0 THEN 'No' ELSE 'N/A' END + ', '; + IF @Version >= 14 AND @Resumable = 'Y' SET @CurrentComment += 'Computed: ' + CASE WHEN @CurrentIsComputed = 1 THEN 'Yes' WHEN @CurrentIsComputed = 0 THEN 'No' ELSE 'N/A' END + ', '; + IF @Version >= 14 AND @Resumable = 'Y' AND @CurrentIndexType = 2 SET @CurrentComment += 'ClusteredIndexComputed: ' + CASE WHEN @CurrentIsClusteredIndexComputed = 1 THEN 'Yes' WHEN @CurrentIsClusteredIndexComputed = 0 THEN 'No' ELSE 'N/A' END + ', '; + IF @Version >= 14 AND @Resumable = 'Y' SET @CurrentComment += 'Timestamp: ' + CASE WHEN @CurrentIsTimestamp = 1 THEN 'Yes' WHEN @CurrentIsTimestamp = 0 THEN 'No' ELSE 'N/A' END + ', '; + IF @Version >= 14 AND @Resumable = 'Y' SET @CurrentComment += 'HasFilter: ' + CASE WHEN @CurrentHasFilter = 1 THEN 'Yes' WHEN @CurrentHasFilter = 0 THEN 'No' ELSE 'N/A' END + ', '; + SET @CurrentComment += 'AllowPageLocks: ' + CASE WHEN @CurrentAllowPageLocks = 1 THEN 'Yes' WHEN @CurrentAllowPageLocks = 0 THEN 'No' ELSE 'N/A' END + ', '; + SET @CurrentComment += 'PageCount: ' + ISNULL(CAST(@CurrentPageCount AS NVARCHAR),'N/A') + ', '; + SET @CurrentComment += 'Fragmentation: ' + ISNULL(CAST(@CurrentFragmentationLevel AS NVARCHAR),'N/A'); + END; + + IF @CurrentIndexID IS NOT NULL AND (@CurrentPageCount IS NOT NULL OR @CurrentFragmentationLevel IS NOT NULL) + BEGIN + SET @CurrentExtendedInfo = (SELECT * + FROM (SELECT CAST(@CurrentPageCount AS NVARCHAR) AS [PageCount], + CAST(@CurrentFragmentationLevel AS NVARCHAR) AS Fragmentation + ) ExtendedInfo FOR XML RAW('ExtendedInfo'), ELEMENTS); + END; + + IF @CurrentIndexID IS NOT NULL AND @CurrentAction IS NOT NULL AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SET @CurrentDatabaseContext = @CurrentDatabaseName; + + SET @CurrentCommandType = 'ALTER_INDEX'; + + SET @CurrentCommand = ''; + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS NVARCHAR) + '; '; + SET @CurrentCommand += 'ALTER INDEX ' + QUOTENAME(@CurrentIndexName) + ' ON ' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName); + IF @CurrentResumableIndexOperation = 1 SET @CurrentCommand += ' RESUME'; + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @CurrentResumableIndexOperation = 0 SET @CurrentCommand += ' REBUILD'; + IF @CurrentAction IN('INDEX_REORGANIZE') AND @CurrentResumableIndexOperation = 0 SET @CurrentCommand += ' REORGANIZE'; + IF @CurrentIsPartition = 1 AND @CurrentResumableIndexOperation = 0 SET @CurrentCommand += ' PARTITION = ' + CAST(@CurrentPartitionNumber AS NVARCHAR); + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @SortInTempdb = 'Y' AND @CurrentIndexType IN(1,2,3,4) AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + SELECT 'SORT_IN_TEMPDB = ON'; + END; + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @SortInTempdb = 'N' AND @CurrentIndexType IN(1,2,3,4) AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + SELECT 'SORT_IN_TEMPDB = OFF'; + END; + + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND (@CurrentIsPartition = 0 OR @Version >= 12) AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + SELECT 'ONLINE = ON' + CASE WHEN @WaitAtLowPriorityMaxDuration IS NOT NULL THEN ' (WAIT_AT_LOW_PRIORITY (MAX_DURATION = ' + CAST(@WaitAtLowPriorityMaxDuration AS NVARCHAR) + ', ABORT_AFTER_WAIT = ' + UPPER(@WaitAtLowPriorityAbortAfterWait) + '))' ELSE '' END; + END; + + IF @CurrentAction = 'INDEX_REBUILD_OFFLINE' AND (@CurrentIsPartition = 0 OR @Version >= 12) AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + SELECT 'ONLINE = OFF'; + END; + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @CurrentMaxDOP IS NOT NULL + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + SELECT 'MAXDOP = ' + CAST(@CurrentMaxDOP AS NVARCHAR); + END; + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @FillFactor IS NOT NULL AND @CurrentIsPartition = 0 AND @CurrentIndexType IN(1,2,3,4) AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + SELECT 'FILLFACTOR = ' + CAST(@FillFactor AS NVARCHAR); + END; + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @PadIndex = 'Y' AND @CurrentIsPartition = 0 AND @CurrentIndexType IN(1,2,3,4) AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + SELECT 'PAD_INDEX = ON'; + END; + + IF (@Version >= 14 OR SERVERPROPERTY('EngineEdition') IN (5,8)) AND @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + SELECT CASE WHEN @Resumable = 'Y' AND @CurrentIndexType IN(1,2) AND @CurrentIsComputed = 0 AND @CurrentIsClusteredIndexComputed = 0 AND @CurrentIsTimestamp = 0 AND @CurrentHasFilter = 0 THEN 'RESUMABLE = ON' ELSE 'RESUMABLE = OFF' END; + END; + + IF (@Version >= 14 OR SERVERPROPERTY('EngineEdition') IN (5,8)) AND @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @Resumable = 'Y' AND @CurrentIndexType IN(1,2) AND @CurrentIsComputed = 0 AND @CurrentIsClusteredIndexComputed = 0 AND @CurrentIsTimestamp = 0 AND @CurrentHasFilter = 0 AND @TimeLimit IS NOT NULL + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + SELECT 'MAX_DURATION = ' + CAST(DATEDIFF(MINUTE,SYSDATETIME(),DATEADD(SECOND,@TimeLimit,@StartTime)) AS NVARCHAR(MAX)); + END; + + IF @CurrentAction IN('INDEX_REORGANIZE') AND @LOBCompaction = 'Y' + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + SELECT 'LOB_COMPACTION = ON'; + END; + + IF @CurrentAction IN('INDEX_REORGANIZE') AND @LOBCompaction = 'N' + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + SELECT 'LOB_COMPACTION = OFF'; + END; + + IF EXISTS (SELECT * FROM @CurrentAlterIndexWithClauseArguments) + BEGIN + SET @CurrentAlterIndexWithClause = ' WITH ('; + + WHILE (1 = 1) + BEGIN + SELECT TOP 1 @CurrentAlterIndexArgumentID = [Id], + @CurrentAlterIndexArgument = Argument + FROM @CurrentAlterIndexWithClauseArguments + WHERE Added = 0 + ORDER BY [Id] ASC; + + IF @@ROWCOUNT = 0 + BEGIN + BREAK; + END; + + SET @CurrentAlterIndexWithClause += @CurrentAlterIndexArgument + ', '; + + UPDATE @CurrentAlterIndexWithClauseArguments + SET Added = 1 + WHERE [ID] = @CurrentAlterIndexArgumentID; + END; + + SET @CurrentAlterIndexWithClause = RTRIM(@CurrentAlterIndexWithClause); + + SET @CurrentAlterIndexWithClause = LEFT(@CurrentAlterIndexWithClause,LEN(@CurrentAlterIndexWithClause) - 1); + + SET @CurrentAlterIndexWithClause = @CurrentAlterIndexWithClause + ')'; + END; + + IF @CurrentAlterIndexWithClause IS NOT NULL SET @CurrentCommand += @CurrentAlterIndexWithClause; + + EXECUTE @CurrentCommandOutput = [dbo].CommandExecute @DatabaseContext = @CurrentDatabaseName, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 2, @Comment = @CurrentComment, @DatabaseName = @CurrentDatabaseName, @SchemaName = @CurrentSchemaName, @ObjectName = @CurrentObjectName, @ObjectType = @CurrentObjectType, @IndexName = @CurrentIndexName, @IndexType = @CurrentIndexType, @PartitionNumber = @CurrentPartitionNumber, @ExtendedInfo = @CurrentExtendedInfo, @LockMessageSeverity = @LockMessageSeverity, @ExecuteAsUser = @ExecuteAsUser, @LogToTable = @LogToTable, @Execute = @Execute; + SET @Error = @@ERROR; + IF @Error <> 0 SET @CurrentCommandOutput = @Error; + IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput; + + IF @Delay > 0 + BEGIN + SET @CurrentDelay = DATEADD(ss,@Delay,'1900-01-01'); + WAITFOR DELAY @CurrentDelay; + END; + END; + + SET @CurrentMaxDOP = @MaxDOP; + + -- Create statistics comment + IF @CurrentStatisticsID IS NOT NULL + BEGIN + SET @CurrentComment = 'ObjectType: ' + CASE WHEN @CurrentObjectType = 'U' THEN 'Table' WHEN @CurrentObjectType = 'V' THEN 'View' ELSE 'N/A' END + ', '; + SET @CurrentComment += 'IndexType: ' + CASE WHEN @CurrentIndexID IS NOT NULL THEN 'Index' ELSE 'Column' END + ', '; + IF @CurrentIndexID IS NOT NULL SET @CurrentComment += 'IndexType: ' + CASE WHEN @CurrentIndexType = 1 THEN 'Clustered' WHEN @CurrentIndexType = 2 THEN 'NonClustered' WHEN @CurrentIndexType = 3 THEN 'XML' WHEN @CurrentIndexType = 4 THEN 'Spatial' WHEN @CurrentIndexType = 5 THEN 'Clustered Columnstore' WHEN @CurrentIndexType = 6 THEN 'NonClustered Columnstore' WHEN @CurrentIndexType = 7 THEN 'NonClustered Hash' ELSE 'N/A' END + ', '; + SET @CurrentComment += 'Incremental: ' + CASE WHEN @CurrentIsIncremental = 1 THEN 'Y' WHEN @CurrentIsIncremental = 0 THEN 'N' ELSE 'N/A' END + ', '; + SET @CurrentComment += 'RowCount: ' + ISNULL(CAST(@CurrentRowCount AS NVARCHAR),'N/A') + ', '; + SET @CurrentComment += 'ModificationCounter: ' + ISNULL(CAST(@CurrentModificationCounter AS NVARCHAR),'N/A'); + END; + + IF @CurrentStatisticsID IS NOT NULL AND (@CurrentRowCount IS NOT NULL OR @CurrentModificationCounter IS NOT NULL) + BEGIN + SET @CurrentExtendedInfo = (SELECT * + FROM (SELECT CAST(@CurrentRowCount AS NVARCHAR) AS [RowCount], + CAST(@CurrentModificationCounter AS NVARCHAR) AS ModificationCounter + ) ExtendedInfo FOR XML RAW('ExtendedInfo'), ELEMENTS); + END; + + IF @CurrentStatisticsID IS NOT NULL AND @CurrentUpdateStatistics = 'Y' AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SET @CurrentDatabaseContext = @CurrentDatabaseName; + + SET @CurrentCommandType = 'UPDATE_STATISTICS'; + + SET @CurrentCommand = ''; + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS NVARCHAR) + '; '; + SET @CurrentCommand += 'UPDATE STATISTICS ' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName) + ' ' + QUOTENAME(@CurrentStatisticsName); + + IF @CurrentMaxDOP IS NOT NULL AND ((@Version >= 12.06024 AND @Version < 13) OR (@Version >= 13.05026 AND @Version < 14) OR @Version >= 14.030154) + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + SELECT 'MAXDOP = ' + CAST(@CurrentMaxDOP AS NVARCHAR); + END; + + IF @CurrentStatisticsSample = 100 + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + SELECT 'FULLSCAN'; + END; + + IF @CurrentStatisticsSample IS NOT NULL AND @CurrentStatisticsSample <> 100 + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + SELECT 'SAMPLE ' + CAST(@CurrentStatisticsSample AS NVARCHAR) + ' PERCENT'; + END; + + IF @CurrentNoRecompute = 1 + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + SELECT 'NORECOMPUTE'; + END; + + IF @CurrentStatisticsResample = 'Y' + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + SELECT 'RESAMPLE'; + END; + + IF EXISTS (SELECT * FROM @CurrentUpdateStatisticsWithClauseArguments) + BEGIN + SET @CurrentUpdateStatisticsWithClause = ' WITH'; + + WHILE (1 = 1) + BEGIN + SELECT TOP 1 @CurrentUpdateStatisticsArgumentID = [Id], + @CurrentUpdateStatisticsArgument = Argument + FROM @CurrentUpdateStatisticsWithClauseArguments + WHERE Added = 0 + ORDER BY [Id] ASC; + + IF @@ROWCOUNT = 0 + BEGIN + BREAK; + END; + + SET @CurrentUpdateStatisticsWithClause = @CurrentUpdateStatisticsWithClause + ' ' + @CurrentUpdateStatisticsArgument + ','; + + UPDATE @CurrentUpdateStatisticsWithClauseArguments + SET Added = 1 + WHERE [ID] = @CurrentUpdateStatisticsArgumentID; + END; + + SET @CurrentUpdateStatisticsWithClause = LEFT(@CurrentUpdateStatisticsWithClause,LEN(@CurrentUpdateStatisticsWithClause) - 1); + END; + + IF @CurrentUpdateStatisticsWithClause IS NOT NULL SET @CurrentCommand += @CurrentUpdateStatisticsWithClause; + + IF @PartitionLevelStatistics = 1 AND @CurrentIsIncremental = 1 AND @CurrentPartitionNumber IS NOT NULL SET @CurrentCommand += ' ON PARTITIONS(' + CAST(@CurrentPartitionNumber AS NVARCHAR(MAX)) + ')'; + + EXECUTE @CurrentCommandOutput = [dbo].CommandExecute @DatabaseContext = @CurrentDatabaseName, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 2, @Comment = @CurrentComment, @DatabaseName = @CurrentDatabaseName, @SchemaName = @CurrentSchemaName, @ObjectName = @CurrentObjectName, @ObjectType = @CurrentObjectType, @IndexName = @CurrentIndexName, @IndexType = @CurrentIndexType, @StatisticsName = @CurrentStatisticsName, @ExtendedInfo = @CurrentExtendedInfo, @LockMessageSeverity = @LockMessageSeverity, @ExecuteAsUser = @ExecuteAsUser, @LogToTable = @LogToTable, @Execute = @Execute; + SET @Error = @@ERROR; + IF @Error <> 0 SET @CurrentCommandOutput = @Error; + IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput; + END; + + NoAction:; + + -- Update that the index or statistics is completed + UPDATE @tmpIndexesStatistics + SET Completed = 1 + WHERE Selected = 1 + AND Completed = 0 + AND [Order] = @CurrentIxOrder + AND [Id] = @CurrentIxID; + + -- Clear variables + SET @CurrentDatabaseContext = NULL; + + SET @CurrentCommand = NULL; + SET @CurrentCommandOutput = NULL; + SET @CurrentCommandType = NULL; + SET @CurrentComment = NULL; + SET @CurrentExtendedInfo = NULL; + + SET @CurrentIxID = NULL; + SET @CurrentIxOrder = NULL; + SET @CurrentSchemaID = NULL; + SET @CurrentSchemaName = NULL; + SET @CurrentObjectID = NULL; + SET @CurrentObjectName = NULL; + SET @CurrentObjectType = NULL; + SET @CurrentIsMemoryOptimized = NULL; + SET @CurrentIndexID = NULL; + SET @CurrentIndexName = NULL; + SET @CurrentIndexType = NULL; + SET @CurrentStatisticsID = NULL; + SET @CurrentStatisticsName = NULL; + SET @CurrentPartitionID = NULL; + SET @CurrentPartitionNumber = NULL; + SET @CurrentPartitionCount = NULL; + SET @CurrentIsPartition = NULL; + SET @CurrentIndexExists = NULL; + SET @CurrentStatisticsExists = NULL; + SET @CurrentIsImageText = NULL; + SET @CurrentIsNewLOB = NULL; + SET @CurrentIsFileStream = NULL; + SET @CurrentHasClusteredColumnstore = NULL; + SET @CurrentHasNonClusteredColumnstore = NULL; + SET @CurrentIsComputed = NULL; + SET @CurrentIsClusteredIndexComputed = NULL; + SET @CurrentIsTimestamp = NULL; + SET @CurrentAllowPageLocks = NULL; + SET @CurrentHasFilter = NULL; + SET @CurrentNoRecompute = NULL; + SET @CurrentIsIncremental = NULL; + SET @CurrentRowCount = NULL; + SET @CurrentModificationCounter = NULL; + SET @CurrentOnReadOnlyFileGroup = NULL; + SET @CurrentResumableIndexOperation = NULL; + SET @CurrentFragmentationLevel = NULL; + SET @CurrentPageCount = NULL; + SET @CurrentFragmentationGroup = NULL; + SET @CurrentAction = NULL; + SET @CurrentMaxDOP = NULL; + SET @CurrentUpdateStatistics = NULL; + SET @CurrentStatisticsSample = NULL; + SET @CurrentStatisticsResample = NULL; + SET @CurrentAlterIndexArgumentID = NULL; + SET @CurrentAlterIndexArgument = NULL; + SET @CurrentAlterIndexWithClause = NULL; + SET @CurrentUpdateStatisticsArgumentID = NULL; + SET @CurrentUpdateStatisticsArgument = NULL; + SET @CurrentUpdateStatisticsWithClause = NULL; + + DELETE FROM @CurrentActionsAllowed; + DELETE FROM @CurrentAlterIndexWithClauseArguments; + DELETE FROM @CurrentUpdateStatisticsWithClauseArguments; + + END; + + END; + + IF @CurrentDatabaseState = 'SUSPECT' + BEGIN + SET @ErrorMessage = 'The database ' + QUOTENAME(@CurrentDatabaseName) + ' is in a SUSPECT state.'; + RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT; + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + SET @Error = @@ERROR; + END; + + -- Update that the database is completed + IF @DatabasesInParallel = 'Y' + BEGIN + UPDATE [dbo].QueueDatabase + SET DatabaseEndTime = SYSDATETIME() + WHERE QueueID = @QueueID + AND DatabaseName = @CurrentDatabaseName; + END; + ELSE + BEGIN + UPDATE @tmpDatabases + SET Completed = 1 + WHERE Selected = 1 + AND Completed = 0 + AND [Id] = @CurrentDBID; + END; + + -- Clear variables + SET @CurrentDBID = NULL; + SET @CurrentDatabaseName = NULL; + + SET @CurrentDatabase_sp_executesql = NULL; + + SET @CurrentExecuteAsUserExists = NULL; + SET @CurrentUserAccess = NULL; + SET @CurrentIsReadOnly = NULL; + SET @CurrentDatabaseState = NULL; + SET @CurrentInStandby = NULL; + SET @CurrentRecoveryModel = NULL; + + SET @CurrentReplicaID = NULL; + SET @CurrentAvailabilityGroupID = NULL; + SET @CurrentAvailabilityGroup = NULL; + SET @CurrentAvailabilityGroupRole = NULL; + SET @CurrentDatabaseMirroringRole = NULL; + + SET @CurrentCommand = NULL; + + DELETE FROM @tmpIndexesStatistics; + + END; + + ---------------------------------------------------------------------------------------------------- + --// Log completing information //-- + ---------------------------------------------------------------------------------------------------- + + Logging:; + SET @EndMessage = 'Date and time: ' + CONVERT(NVARCHAR,SYSDATETIME(),120); + RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT; + + RAISERROR(@EmptyLine,10,1) WITH NOWAIT; + + IF @ReturnCode <> 0 + BEGIN + RETURN @ReturnCode; + END; + + ---------------------------------------------------------------------------------------------------- + +END; + +GO +IF (SELECT [Value] FROM #Config WHERE [name] = 'CreateJobs') = 'Y' + AND SERVERPROPERTY('EngineEdition') NOT IN(4, 5) + AND (IS_SRVROLEMEMBER('sysadmin') = 1 OR (EXISTS (SELECT * FROM [sys].[databases] WHERE [name] = 'rdsadmin') AND SUSER_SNAME(0x01) = 'rdsa')) + AND (SELECT [compatibility_level] FROM [sys].[databases] WHERE [name] = DB_NAME()) >= 90 + AND NOT (EXISTS (SELECT * FROM #Config WHERE [name] = 'BackupDirectory' AND [Value] IS NOT NULL) AND EXISTS (SELECT * FROM #Config WHERE [name] = 'BackupURL' AND [Value] IS NOT NULL)) + AND NOT (EXISTS (SELECT * FROM #Config WHERE [name] = 'BackupURL' AND [Value] IS NOT NULL) AND EXISTS (SELECT * FROM #Config WHERE [name] = 'CleanupTime' AND [Value] IS NOT NULL)) +BEGIN + + DECLARE @BackupDirectory NVARCHAR(MAX); + DECLARE @BackupURL NVARCHAR(MAX); + DECLARE @CleanupTime INT; + DECLARE @OutputFileDirectory NVARCHAR(MAX); + DECLARE @LogToTable NVARCHAR(MAX); + DECLARE @DatabaseName NVARCHAR(MAX); + + DECLARE @HostPlatform NVARCHAR(MAX); + DECLARE @DirectorySeparator NVARCHAR(MAX); + DECLARE @LogDirectory NVARCHAR(MAX); + + DECLARE @TokenServer NVARCHAR(MAX); + DECLARE @TokenJobID NVARCHAR(MAX); + DECLARE @TokenJobName NVARCHAR(MAX); + DECLARE @TokenStepID NVARCHAR(MAX); + DECLARE @TokenStepName NVARCHAR(MAX); + DECLARE @TokenDate NVARCHAR(MAX); + DECLARE @TokenTime NVARCHAR(MAX); + DECLARE @TokenLogDirectory NVARCHAR(MAX); + + DECLARE @JobDescription NVARCHAR(MAX); + DECLARE @JobCategory NVARCHAR(MAX); + DECLARE @JobOwner NVARCHAR(MAX); + + DECLARE @Jobs TABLE ([JobId] INT IDENTITY, + [Name] NVARCHAR(MAX), + CommandTSQL NVARCHAR(MAX), + CommandCmdExec NVARCHAR(MAX), + DatabaseName VARCHAR(MAX), + OutputFileNamePart01 NVARCHAR(MAX), + OutputFileNamePart02 NVARCHAR(MAX), + Selected BIT DEFAULT 0, + Completed BIT DEFAULT 0); + + DECLARE @CurrentJobID INT; + DECLARE @CurrentJobName NVARCHAR(MAX); + DECLARE @CurrentCommandTSQL NVARCHAR(MAX); + DECLARE @CurrentCommandCmdExec NVARCHAR(MAX); + DECLARE @CurrentDatabaseName NVARCHAR(MAX); + DECLARE @CurrentOutputFileNamePart01 NVARCHAR(MAX); + DECLARE @CurrentOutputFileNamePart02 NVARCHAR(MAX); + + DECLARE @CurrentJobStepCommand NVARCHAR(MAX); + DECLARE @CurrentJobStepSubSystem NVARCHAR(MAX); + DECLARE @CurrentJobStepDatabaseName NVARCHAR(MAX); + DECLARE @CurrentOutputFileName NVARCHAR(MAX); + + DECLARE @Version NUMERIC(18,10) = CAST(LEFT(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)),CHARINDEX('.',CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX))) - 1) + '.' + REPLACE(RIGHT(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)), LEN(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX))) - CHARINDEX('.',CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)))),'.','') AS NUMERIC(18,10)); + + DECLARE @AmazonRDS BIT = CASE WHEN SERVERPROPERTY('EngineEdition') IN (5, 8) THEN 0 WHEN EXISTS (SELECT * FROM [sys].[databases] WHERE [name] = 'rdsadmin') AND SUSER_SNAME(0x01) = 'rdsa' THEN 1 ELSE 0 END; + + IF @Version >= 14 + BEGIN + SELECT @HostPlatform = [host_platform] + FROM [sys].[dm_os_host_info]; + END; + ELSE + BEGIN + SET @HostPlatform = 'Windows'; + END; + + SELECT @DirectorySeparator = CASE + WHEN @HostPlatform = 'Windows' THEN '\' + WHEN @HostPlatform = 'Linux' THEN '/' + END; + + SET @TokenServer = '$' + '(ESCAPE_SQUOTE(SRVR))'; + SET @TokenJobID = '$' + '(ESCAPE_SQUOTE(JOBID))'; + SET @TokenStepID = '$' + '(ESCAPE_SQUOTE(STEPID))'; + SET @TokenDate = '$' + '(ESCAPE_SQUOTE(DATE))'; + SET @TokenTime = '$' + '(ESCAPE_SQUOTE(TIME))'; + + IF @Version >= 13 + BEGIN + SET @TokenJobName = '$' + '(ESCAPE_SQUOTE(JOBNAME))'; + SET @TokenStepName = '$' + '(ESCAPE_SQUOTE(STEPNAME))'; + END; + + IF @Version >= 12 AND @HostPlatform = 'Windows' + BEGIN + SET @TokenLogDirectory = '$' + '(ESCAPE_SQUOTE(SQLLOGDIR))'; + END; + + SELECT @BackupDirectory = VALUE + FROM #Config + WHERE [Name] = 'BackupDirectory'; + + SELECT @BackupURL = VALUE + FROM #Config + WHERE [Name] = 'BackupURL'; + + SELECT @CleanupTime = VALUE + FROM #Config + WHERE [Name] = 'CleanupTime'; + + SELECT @OutputFileDirectory = VALUE + FROM #Config + WHERE [Name] = 'OutputFileDirectory'; + + SELECT @LogToTable = VALUE + FROM #Config + WHERE [Name] = 'LogToTable'; + + SELECT @DatabaseName = VALUE + FROM #Config + WHERE [Name] = 'DatabaseName'; + + IF @Version >= 11 + BEGIN + SELECT @LogDirectory = [path] + FROM [sys].[dm_os_server_diagnostics_log_configurations]; + END; + ELSE + BEGIN + SELECT @LogDirectory = LEFT(CAST(SERVERPROPERTY('ErrorLogFileName') AS NVARCHAR(MAX)),LEN(CAST(SERVERPROPERTY('ErrorLogFileName') AS NVARCHAR(MAX))) - CHARINDEX('\',REVERSE(CAST(SERVERPROPERTY('ErrorLogFileName') AS NVARCHAR(MAX))))); + END; + + IF @OutputFileDirectory IS NOT NULL AND RIGHT(@OutputFileDirectory,1) = @DirectorySeparator + BEGIN + SET @OutputFileDirectory = LEFT(@OutputFileDirectory, LEN(@OutputFileDirectory) - 1); + END; + + IF @LogDirectory IS NOT NULL AND RIGHT(@LogDirectory,1) = @DirectorySeparator + BEGIN + SET @LogDirectory = LEFT(@LogDirectory, LEN(@LogDirectory) - 1); + END; + + SET @JobDescription = 'Source: https://ola.hallengren.com'; + SET @JobCategory = 'Database Maintenance'; + + IF @AmazonRDS = 0 + BEGIN + SET @JobOwner = SUSER_SNAME(0x01); + END; + + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01, OutputFileNamePart02) + SELECT 'DatabaseBackup - SYSTEM_DATABASES - FULL', + 'EXECUTE [dbo].[DatabaseBackup]' + CHAR(13) + CHAR(10) + '@Databases = ''SYSTEM_DATABASES'',' + CHAR(13) + CHAR(10) + CASE WHEN @BackupURL IS NOT NULL THEN '@URL = N''' + REPLACE(@BackupURL,'''','''''') + '''' ELSE '@Directory = ' + ISNULL('N''' + REPLACE(@BackupDirectory,'''','''''') + '''','NULL') END + ',' + CHAR(13) + CHAR(10) + '@BackupType = ''FULL'',' + CHAR(13) + CHAR(10) + '@Verify = ''Y'',' + CHAR(13) + CHAR(10) + '@CleanupTime = ' + ISNULL(CAST(@CleanupTime AS NVARCHAR),'NULL') + ',' + CHAR(13) + CHAR(10) + '@Checksum = ''Y'',' + CHAR(13) + CHAR(10) + '@LogToTable = ''' + @LogToTable + '''', + @DatabaseName, + 'DatabaseBackup', + 'FULL'; + + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01, OutputFileNamePart02) + SELECT 'DatabaseBackup - USER_DATABASES - DIFF', + 'EXECUTE [dbo].[DatabaseBackup]' + CHAR(13) + CHAR(10) + '@Databases = ''USER_DATABASES'',' + CHAR(13) + CHAR(10) + CASE WHEN @BackupURL IS NOT NULL THEN '@URL = N''' + REPLACE(@BackupURL,'''','''''') + '''' ELSE '@Directory = ' + ISNULL('N''' + REPLACE(@BackupDirectory,'''','''''') + '''','NULL') END + ',' + CHAR(13) + CHAR(10) + '@BackupType = ''DIFF'',' + CHAR(13) + CHAR(10) + '@Verify = ''Y'',' + CHAR(13) + CHAR(10) + '@CleanupTime = ' + ISNULL(CAST(@CleanupTime AS NVARCHAR),'NULL') + ',' + CHAR(13) + CHAR(10) + '@Checksum = ''Y'',' + CHAR(13) + CHAR(10) + '@LogToTable = ''' + @LogToTable + '''', + @DatabaseName, + 'DatabaseBackup', + 'DIFF'; + + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01, OutputFileNamePart02) + SELECT 'DatabaseBackup - USER_DATABASES - FULL', + 'EXECUTE [dbo].[DatabaseBackup]' + CHAR(13) + CHAR(10) + '@Databases = ''USER_DATABASES'',' + CHAR(13) + CHAR(10) + CASE WHEN @BackupURL IS NOT NULL THEN '@URL = N''' + REPLACE(@BackupURL,'''','''''') + '''' ELSE '@Directory = ' + ISNULL('N''' + REPLACE(@BackupDirectory,'''','''''') + '''','NULL') END + ',' + CHAR(13) + CHAR(10) + '@BackupType = ''FULL'',' + CHAR(13) + CHAR(10) + '@Verify = ''Y'',' + CHAR(13) + CHAR(10) + '@CleanupTime = ' + ISNULL(CAST(@CleanupTime AS NVARCHAR),'NULL') + ',' + CHAR(13) + CHAR(10) + '@Checksum = ''Y'',' + CHAR(13) + CHAR(10) + '@LogToTable = ''' + @LogToTable + '''', + @DatabaseName, + 'DatabaseBackup', + 'FULL'; + + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01, OutputFileNamePart02) + SELECT 'DatabaseBackup - USER_DATABASES - LOG', + 'EXECUTE [dbo].[DatabaseBackup]' + CHAR(13) + CHAR(10) + '@Databases = ''USER_DATABASES'',' + CHAR(13) + CHAR(10) + CASE WHEN @BackupURL IS NOT NULL THEN '@URL = N''' + REPLACE(@BackupURL,'''','''''') + '''' ELSE '@Directory = ' + ISNULL('N''' + REPLACE(@BackupDirectory,'''','''''') + '''','NULL') END + ',' + CHAR(13) + CHAR(10) + '@BackupType = ''LOG'',' + CHAR(13) + CHAR(10) + '@Verify = ''Y'',' + CHAR(13) + CHAR(10) + '@CleanupTime = ' + ISNULL(CAST(@CleanupTime AS NVARCHAR),'NULL') + ',' + CHAR(13) + CHAR(10) + '@Checksum = ''Y'',' + CHAR(13) + CHAR(10) + '@LogToTable = ''' + @LogToTable + '''', + @DatabaseName, + 'DatabaseBackup', + 'LOG'; + + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01) + SELECT 'DatabaseIntegrityCheck - SYSTEM_DATABASES', + 'EXECUTE [dbo].[DatabaseIntegrityCheck]' + CHAR(13) + CHAR(10) + '@Databases = ''SYSTEM_DATABASES'',' + CHAR(13) + CHAR(10) + '@LogToTable = ''' + @LogToTable + '''', + @DatabaseName, + 'DatabaseIntegrityCheck'; + + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01) + SELECT 'DatabaseIntegrityCheck - USER_DATABASES', + 'EXECUTE [dbo].[DatabaseIntegrityCheck]' + CHAR(13) + CHAR(10) + '@Databases = ''USER_DATABASES'',' + CHAR(13) + CHAR(10) + '@LogToTable = ''' + @LogToTable + '''', + @DatabaseName, + 'DatabaseIntegrityCheck'; + + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01) + SELECT 'IndexOptimize - USER_DATABASES', + 'EXECUTE [dbo].[IndexOptimize]' + CHAR(13) + CHAR(10) + '@Databases = ''USER_DATABASES'',' + CHAR(13) + CHAR(10) + '@LogToTable = ''' + @LogToTable + '''', + @DatabaseName, + 'IndexOptimize'; + + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01) + SELECT 'sp_delete_backuphistory', + 'DECLARE @CleanupDate datetime' + CHAR(13) + CHAR(10) + 'SET @CleanupDate = DATEADD(dd,-30,GETDATE())' + CHAR(13) + CHAR(10) + 'EXECUTE dbo.sp_delete_backuphistory @oldest_date = @CleanupDate', + 'msdb', + 'sp_delete_backuphistory'; + + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01) + SELECT 'sp_purge_jobhistory', + 'DECLARE @CleanupDate datetime' + CHAR(13) + CHAR(10) + 'SET @CleanupDate = DATEADD(dd,-30,GETDATE())' + CHAR(13) + CHAR(10) + 'EXECUTE dbo.sp_purge_jobhistory @oldest_date = @CleanupDate', + 'msdb', + 'sp_purge_jobhistory'; + + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01) + SELECT 'CommandLog Cleanup', + 'DELETE FROM [dbo].[CommandLog]' + CHAR(13) + CHAR(10) + 'WHERE StartTime < DATEADD(dd,-30,GETDATE())', + @DatabaseName, + 'CommandLogCleanup'; + + INSERT INTO @Jobs ([Name], CommandCmdExec, OutputFileNamePart01) + SELECT 'Output File Cleanup', + 'cmd /q /c "For /F "tokens=1 delims=" %v In (''ForFiles /P "' + COALESCE(@OutputFileDirectory,@TokenLogDirectory,@LogDirectory) + '" /m *_*_*_*.txt /d -30 2^>^&1'') do if EXIST "' + COALESCE(@OutputFileDirectory,@TokenLogDirectory,@LogDirectory) + '"\%v echo del "' + COALESCE(@OutputFileDirectory,@TokenLogDirectory,@LogDirectory) + '"\%v& del "' + COALESCE(@OutputFileDirectory,@TokenLogDirectory,@LogDirectory) + '"\%v"', + 'OutputFileCleanup'; + + IF @AmazonRDS = 1 + BEGIN + UPDATE @Jobs + SET Selected = 1 + WHERE [Name] IN('DatabaseIntegrityCheck - USER_DATABASES','IndexOptimize - USER_DATABASES','CommandLog Cleanup'); + END; + ELSE IF SERVERPROPERTY('EngineEdition') = 8 + BEGIN + UPDATE @Jobs + SET Selected = 1 + WHERE [Name] IN('DatabaseIntegrityCheck - SYSTEM_DATABASES','DatabaseIntegrityCheck - USER_DATABASES','IndexOptimize - USER_DATABASES','CommandLog Cleanup','sp_delete_backuphistory','sp_purge_jobhistory'); + END; + ELSE IF @HostPlatform = 'Windows' + BEGIN + UPDATE @Jobs + SET Selected = 1; + END; + ELSE IF @HostPlatform = 'Linux' + BEGIN + UPDATE @Jobs + SET Selected = 1 + WHERE CommandTSQL IS NOT NULL; + END; + + WHILE EXISTS (SELECT * FROM @Jobs WHERE Completed = 0 AND Selected = 1) + BEGIN + SELECT @CurrentJobID = [JobId], + @CurrentJobName = [Name], + @CurrentCommandTSQL = CommandTSQL, + @CurrentCommandCmdExec = CommandCmdExec, + @CurrentDatabaseName = DatabaseName, + @CurrentOutputFileNamePart01 = OutputFileNamePart01, + @CurrentOutputFileNamePart02 = OutputFileNamePart02 + FROM @Jobs + WHERE Completed = 0 + AND Selected = 1 + ORDER BY [JobId] ASC; + + IF @CurrentCommandTSQL IS NOT NULL AND @AmazonRDS = 1 + BEGIN + SET @CurrentJobStepSubSystem = 'TSQL'; + SET @CurrentJobStepCommand = @CurrentCommandTSQL; + SET @CurrentJobStepDatabaseName = @CurrentDatabaseName; + END; + ELSE IF @CurrentCommandTSQL IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 8 + BEGIN + SET @CurrentJobStepSubSystem = 'TSQL'; + SET @CurrentJobStepCommand = @CurrentCommandTSQL; + SET @CurrentJobStepDatabaseName = @CurrentDatabaseName; + END; + ELSE IF @CurrentCommandTSQL IS NOT NULL AND @HostPlatform = 'Linux' + BEGIN + SET @CurrentJobStepSubSystem = 'TSQL'; + SET @CurrentJobStepCommand = @CurrentCommandTSQL; + SET @CurrentJobStepDatabaseName = @CurrentDatabaseName; + END; + ELSE IF @CurrentCommandTSQL IS NOT NULL AND @HostPlatform = 'Windows' AND @Version >= 11 + BEGIN + SET @CurrentJobStepSubSystem = 'TSQL'; + SET @CurrentJobStepCommand = @CurrentCommandTSQL; + SET @CurrentJobStepDatabaseName = @CurrentDatabaseName; + END; + ELSE IF @CurrentCommandTSQL IS NOT NULL AND @HostPlatform = 'Windows' AND @Version < 11 + BEGIN + SET @CurrentJobStepSubSystem = 'CMDEXEC'; + SET @CurrentJobStepCommand = 'sqlcmd -E -S ' + @TokenServer + ' -d ' + @CurrentDatabaseName + ' -Q "' + REPLACE(@CurrentCommandTSQL,(CHAR(13) + CHAR(10)),' ') + '" -b'; + SET @CurrentJobStepDatabaseName = NULL; + END; + ELSE IF @CurrentCommandCmdExec IS NOT NULL AND @HostPlatform = 'Windows' + BEGIN + SET @CurrentJobStepSubSystem = 'CMDEXEC'; + SET @CurrentJobStepCommand = @CurrentCommandCmdExec; + SET @CurrentJobStepDatabaseName = NULL; + END; + + IF @AmazonRDS = 0 AND SERVERPROPERTY('EngineEdition') <> 8 + BEGIN + SET @CurrentOutputFileName = COALESCE(@OutputFileDirectory,@TokenLogDirectory,@LogDirectory) + @DirectorySeparator + ISNULL(CASE WHEN @TokenJobName IS NULL THEN @CurrentOutputFileNamePart01 END + '_','') + ISNULL(CASE WHEN @TokenJobName IS NULL THEN @CurrentOutputFileNamePart02 END + '_','') + ISNULL(@TokenJobName,@TokenJobID) + '_' + @TokenStepID + '_' + @TokenDate + '_' + @TokenTime + '.txt'; + IF LEN(@CurrentOutputFileName) > 200 SET @CurrentOutputFileName = COALESCE(@OutputFileDirectory,@TokenLogDirectory,@LogDirectory) + @DirectorySeparator + ISNULL(CASE WHEN @TokenJobName IS NULL THEN @CurrentOutputFileNamePart01 END + '_','') + ISNULL(@TokenJobName,@TokenJobID) + '_' + @TokenStepID + '_' + @TokenDate + '_' + @TokenTime + '.txt'; + IF LEN(@CurrentOutputFileName) > 200 SET @CurrentOutputFileName = COALESCE(@OutputFileDirectory,@TokenLogDirectory,@LogDirectory) + @DirectorySeparator + ISNULL(@TokenJobName,@TokenJobID) + '_' + @TokenStepID + '_' + @TokenDate + '_' + @TokenTime + '.txt'; + IF LEN(@CurrentOutputFileName) > 200 SET @CurrentOutputFileName = NULL; + END; + + IF @CurrentJobStepSubSystem IS NOT NULL AND @CurrentJobStepCommand IS NOT NULL AND NOT EXISTS (SELECT * FROM msdb.[dbo].[sysjobs] WHERE [name] = @CurrentJobName) + BEGIN + EXECUTE msdb.[dbo].sp_add_job @job_name = @CurrentJobName, @description = @JobDescription, @category_name = @JobCategory, @owner_login_name = @JobOwner; + EXECUTE msdb.[dbo].sp_add_jobstep @job_name = @CurrentJobName, @step_name = @CurrentJobName, @subsystem = @CurrentJobStepSubSystem, @command = @CurrentJobStepCommand, @output_file_name = @CurrentOutputFileName, @database_name = @CurrentJobStepDatabaseName; + EXECUTE msdb.[dbo].sp_add_jobserver @job_name = @CurrentJobName; + END; + + UPDATE Jobs + SET Completed = 1 + FROM @Jobs Jobs + WHERE [JobId] = @CurrentJobID; + + SET @CurrentJobID = NULL; + SET @CurrentJobName = NULL; + SET @CurrentCommandTSQL = NULL; + SET @CurrentCommandCmdExec = NULL; + SET @CurrentDatabaseName = NULL; + SET @CurrentOutputFileNamePart01 = NULL; + SET @CurrentOutputFileNamePart02 = NULL; + SET @CurrentJobStepCommand = NULL; + SET @CurrentJobStepSubSystem = NULL; + SET @CurrentJobStepDatabaseName = NULL; + SET @CurrentOutputFileName = NULL; + + END; + +END; +GO + +DECLARE @job_id UNIQUEIDENTIFIER; +DECLARE @step_id INT; +DECLARE @command NVARCHAR(MAX); +DECLARE @AmazonRDS BIT = CASE WHEN SERVERPROPERTY('EngineEdition') IN (5, 8) THEN 0 WHEN EXISTS (SELECT * FROM [sys].[databases] WHERE [name] = 'rdsadmin') AND SUSER_SNAME(0x01) = 'rdsa' THEN 1 ELSE 0 END; + +IF @AmazonRDS = 0 +BEGIN + + DECLARE JobCursor CURSOR FAST_FORWARD FOR SELECT [job_id], [step_id], [command] FROM msdb.[dbo].[sysjobsteps] WHERE [command] LIKE '%DatabaseBackup%@CheckSum%' COLLATE SQL_Latin1_General_CP1_CS_AS; + + OPEN JobCursor; + + FETCH JobCursor INTO @job_id, @step_id, @command; + + WHILE @@FETCH_STATUS = 0 + BEGIN + SET @command = REPLACE(@command, '@CheckSum', '@Checksum'); + + EXECUTE msdb.[dbo].sp_update_jobstep @job_id = @job_id, @step_id = @step_id, @command = @command; + + FETCH NEXT FROM JobCursor INTO @job_id, @step_id, @command; + END; + + CLOSE JobCursor; + + DEALLOCATE JobCursor; +END; +GO +--#endregion SQL Server Maintenance Solution + +USE msdb; +GO + + +--#region deleting maintenance plans +DECLARE @maint_plans TABLE([plan_id] UNIQUEIDENTIFIER NULL, [subplan_id] UNIQUEIDENTIFIER NULL, [task_detail_id] UNIQUEIDENTIFIER NULL); +DECLARE @q NVARCHAR(MAX)=''; +INSERT INTO @maint_plans ([plan_id], + [subplan_id], + [task_detail_id]) +SELECT p.[id], sp.[subplan_id], pl.[task_detail_id] +FROM [sysmaintplan_plans] p + LEFT JOIN [sysmaintplan_subplans] sp ON sp.[plan_id] =p.[id] + LEFT JOIN [sysmaintplan_log] pl ON pl.[plan_id] = p.[id] +WHERE p.[name]='MaintenanceTasks' +AND p.[owner] IN( + N'E-MEDIAT\uapeh', + N'CENTRALINFRA\ucibebildm_adm' +); + +SELECT @q=@q+'delete from [sysmaintplan_log] where [task_detail_id] ='''+TRY_CAST([task_detail_id] AS NVARCHAR(MAX))+'''; +' +FROM @maint_plans +WHERE [task_detail_id] IS NOT NULL; + +SELECT @q=@q+'delete from [sysmaintplan_subplans] where [subplan_id] ='''+TRY_CAST([subplan_id] AS NVARCHAR(MAX))+'''; +' +FROM @maint_plans +WHERE [subplan_id] IS NOT NULL; + +SELECT @q=@q+'delete from [sysmaintplan_plans] where [id] ='''+TRY_CAST([plan_id] AS NVARCHAR(MAX))+'''; +' +FROM @maint_plans +WHERE [plan_id] IS NOT NULL; + +--PRINT @q; +EXECUTE sp_executesql @q,N''; +PRINT 'Cleaned up maintenance plans owned by Paul or Mehdi'; + +--#endregion deleting maintenance plans + +--#region Drop old jobs +IF EXISTS (SELECT 1 FROM [msdb].[dbo].[sysjobs] j WHERE [j].[name]='MaintenanceTasks.CheckDB') +BEGIN + EXECUTE [msdb].[dbo].[sp_delete_job] @job_name = 'MaintenanceTasks.CheckDB', @delete_unused_schedule = 1; + PRINT 'dropped job MaintenanceTasks.CheckDB'; +END; + +IF EXISTS (SELECT 1 FROM [msdb].[dbo].[sysjobs] j WHERE [j].[name]='MaintenanceTasks.Subplan_1') +BEGIN + EXECUTE [msdb].[dbo].[sp_delete_job] @job_name = 'MaintenanceTasks.Subplan_1', @delete_unused_schedule = 1; + PRINT 'dropped job MaintenanceTasks.Subplan_1'; +END; + +IF EXISTS (SELECT 1 FROM [msdb].[dbo].[sysjobs] j WHERE [j].[name]='MaintenanceTasks.IndexStatsCleanup') +BEGIN + EXECUTE [msdb].[dbo].[sp_delete_job] @job_name = 'MaintenanceTasks.IndexStatsCleanup', @delete_unused_schedule = 1; + PRINT 'dropped job MaintenanceTasks.IndexStatsCleanup'; +END; + +IF EXISTS (SELECT 1 FROM [msdb].[dbo].[sysjobs] j WHERE [j].[name]='MaintenanceTasks.IndexRebuild') +BEGIN + EXECUTE [msdb].[dbo].[sp_delete_job] @job_name = 'MaintenanceTasks.IndexRebuild', @delete_unused_schedule = 1; + PRINT 'dropped job MaintenanceTasks.IndexRebuild'; +END; +--#endregion Drop old jobs + +DECLARE @ReturnCode INT; +SELECT @ReturnCode = 0; +DECLARE @jobId BINARY(16); +PRINT 'creating missing jobs'; + +--#region integrity system +IF NOT EXISTS(SELECT 1 FROM [sysjobs] j WHERE [name] ='DatabaseIntegrityCheck - SYSTEM_DATABASES') +BEGIN + PRINT 'deploying DatabaseIntegrityCheck - SYSTEM_DATABASES'; + /****** Object: Job [DatabaseIntegrityCheck - SYSTEM_DATABASES] Script Date: 12.08.2025 14:10:01 ******/ + BEGIN TRANSACTION; + /****** Object: JobCategory [Database Maintenance] Script Date: 12.08.2025 14:10:01 ******/ + IF NOT EXISTS (SELECT [name] FROM msdb.[dbo].[syscategories] WHERE [name]=N'Database Maintenance' AND [category_class]=1) + BEGIN + EXECUTE @ReturnCode = msdb.[dbo].sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'Database Maintenance'; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + END; + + EXECUTE @ReturnCode = msdb.[dbo].sp_add_job @job_name=N'DatabaseIntegrityCheck - SYSTEM_DATABASES', + @enabled=1, + @notify_level_eventlog=2, + @notify_level_email=0, + @notify_level_netsend=0, + @notify_level_page=0, + @delete_level=0, + @description=N'Source: https://ola.hallengren.com', + @category_name=N'Database Maintenance', + @owner_login_name=N'sa', @job_id = @jobId OUTPUT; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + /****** Object: Step [DatabaseIntegrityCheck - SYSTEM_DATABASES] Script Date: 12.08.2025 14:10:01 ******/ + EXECUTE @ReturnCode = msdb.[dbo].sp_add_jobstep @job_id=@jobId, @step_name=N'DatabaseIntegrityCheck - SYSTEM_DATABASES', + @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'EXECUTE [dbo].[DatabaseIntegrityCheck] + @Databases = ''SYSTEM_DATABASES'', + @LogToTable = ''Y''', + @database_name=N'master', + @output_file_name=N'$(ESCAPE_SQUOTE(SQLLOGDIR))\$(ESCAPE_SQUOTE(JOBNAME))_$(ESCAPE_SQUOTE(STEPID))_$(ESCAPE_SQUOTE(DATE))_$(ESCAPE_SQUOTE(TIME)).txt', + @flags=0; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + EXECUTE @ReturnCode = msdb.[dbo].sp_update_job @job_id = @jobId, @start_step_id = 1; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + EXECUTE @ReturnCode = msdb.[dbo].sp_add_jobschedule @job_id=@jobId, @name=N'maintenance_dbIntegrity_system', + @enabled=1, + @freq_type=8, + @freq_interval=64, + @freq_subday_type=1, + @freq_subday_interval=0, + @freq_relative_interval=0, + @freq_recurrence_factor=1, + @active_start_date=20250812, + @active_end_date=99991231, + @active_start_time=100000, + @active_end_time=235959, + @schedule_uid=N'9dd773d6-47bb-4b9b-8d50-b1060cbfe444'; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + EXECUTE @ReturnCode = msdb.[dbo].sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + COMMIT TRANSACTION; + --GOTO EndSave +END; +--#endregion integrity system + +SET @jobId=NULL; + +--#region integrity users +IF NOT EXISTS(SELECT 1 FROM [sysjobs] j WHERE [name] ='DatabaseIntegrityCheck - USER_DATABASES') +BEGIN + PRINT 'deploying DatabaseIntegrityCheck - USER_DATABASES'; + /****** Object: Job [DatabaseIntegrityCheck - USER_DATABASES] Script Date: 12.08.2025 14:11:12 ******/ + BEGIN TRANSACTION; + /****** Object: JobCategory [Database Maintenance] Script Date: 12.08.2025 14:11:12 ******/ + IF NOT EXISTS (SELECT [name] FROM msdb.[dbo].[syscategories] WHERE [name]=N'Database Maintenance' AND [category_class]=1) + BEGIN + EXECUTE @ReturnCode = msdb.[dbo].sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'Database Maintenance'; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + END; + + EXECUTE @ReturnCode = msdb.[dbo].sp_add_job @job_name=N'DatabaseIntegrityCheck - USER_DATABASES', + @enabled=1, + @notify_level_eventlog=2, + @notify_level_email=0, + @notify_level_netsend=0, + @notify_level_page=0, + @delete_level=0, + @description=N'Source: https://ola.hallengren.com', + @category_name=N'Database Maintenance', + @owner_login_name=N'sa', @job_id = @jobId OUTPUT; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + /****** Object: Step [DatabaseIntegrityCheck - USER_DATABASES] Script Date: 12.08.2025 14:11:12 ******/ + EXECUTE @ReturnCode = msdb.[dbo].sp_add_jobstep @job_id=@jobId, @step_name=N'DatabaseIntegrityCheck - USER_DATABASES', + @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'EXECUTE [dbo].[DatabaseIntegrityCheck] + @Databases = ''USER_DATABASES'', + @LogToTable = ''Y''', + @database_name=N'master', + @output_file_name=N'$(ESCAPE_SQUOTE(SQLLOGDIR))\$(ESCAPE_SQUOTE(JOBNAME))_$(ESCAPE_SQUOTE(STEPID))_$(ESCAPE_SQUOTE(DATE))_$(ESCAPE_SQUOTE(TIME)).txt', + @flags=0; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + EXECUTE @ReturnCode = msdb.[dbo].sp_update_job @job_id = @jobId, @start_step_id = 1; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + EXECUTE @ReturnCode = msdb.[dbo].sp_add_jobschedule @job_id=@jobId, @name=N'maintenance_dbIntegrity_userDbs', + @enabled=1, + @freq_type=8, + @freq_interval=64, + @freq_subday_type=1, + @freq_subday_interval=0, + @freq_relative_interval=0, + @freq_recurrence_factor=1, + @active_start_date=20250812, + @active_end_date=99991231, + @active_start_time=110000, + @active_end_time=235959, + @schedule_uid=N'51482738-7705-4cbb-afbf-2cf188016bc2'; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + EXECUTE @ReturnCode = msdb.[dbo].sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + COMMIT TRANSACTION; + --GOTO EndSave +END; +--#endregion integrity users + +SET @jobId=NULL; + +--#region index optimize +IF NOT EXISTS(SELECT 1 FROM [sysjobs] j WHERE [name] ='IndexOptimize - USER_DATABASES') +BEGIN + PRINT 'deploying IndexOptimize - USER_DATABASES'; + /****** Object: Job [IndexOptimize - USER_DATABASES] Script Date: 12.08.2025 14:17:02 ******/ + BEGIN TRANSACTION; + /****** Object: JobCategory [Database Maintenance] Script Date: 12.08.2025 14:17:02 ******/ + IF NOT EXISTS (SELECT [name] FROM msdb.[dbo].[syscategories] WHERE [name]=N'Database Maintenance' AND [category_class]=1) + BEGIN + EXECUTE @ReturnCode = msdb.[dbo].sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'Database Maintenance'; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + + END; + + EXECUTE @ReturnCode = msdb.[dbo].sp_add_job @job_name=N'IndexOptimize - USER_DATABASES', + @enabled=1, + @notify_level_eventlog=2, + @notify_level_email=0, + @notify_level_netsend=0, + @notify_level_page=0, + @delete_level=0, + @description=N'Source: https://ola.hallengren.com', + @category_name=N'Database Maintenance', + @owner_login_name=N'sa', @job_id = @jobId OUTPUT; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + /****** Object: Step [IndexOptimize - USER_DATABASES] Script Date: 12.08.2025 14:17:03 ******/ + EXECUTE @ReturnCode = msdb.[dbo].sp_add_jobstep @job_id=@jobId, @step_name=N'IndexOptimize - USER_DATABASES', + @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'EXECUTE [dbo].[IndexOptimize] + @Databases = ''USER_DATABASES'', + @LogToTable = ''Y''', + @database_name=N'master', + @output_file_name=N'$(ESCAPE_SQUOTE(SQLLOGDIR))\$(ESCAPE_SQUOTE(JOBNAME))_$(ESCAPE_SQUOTE(STEPID))_$(ESCAPE_SQUOTE(DATE))_$(ESCAPE_SQUOTE(TIME)).txt', + @flags=0; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + EXECUTE @ReturnCode = msdb.[dbo].sp_update_job @job_id = @jobId, @start_step_id = 1; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + EXECUTE @ReturnCode = msdb.[dbo].sp_add_jobschedule @job_id=@jobId, @name=N'maintenance_indexOptimise_userDbs', + @enabled=1, + @freq_type=8, + @freq_interval=1, + @freq_subday_type=1, + @freq_subday_interval=0, + @freq_relative_interval=0, + @freq_recurrence_factor=1, + @active_start_date=20250812, + @active_end_date=99991231, + @active_start_time=10000, + @active_end_time=235959, + @schedule_uid=N'934e9df7-5ee4-48df-9c04-0d9a9fd53ccb'; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + EXECUTE @ReturnCode = msdb.[dbo].sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + COMMIT TRANSACTION; + --GOTO EndSave + +END; +--#endregion index optimize + +SET @jobId=NULL; + +--#region statistics refresh +IF NOT EXISTS(SELECT 1 FROM [sysjobs] j WHERE [name] ='IndexOptimize - statistics update USER_DATABASES') +BEGIN + PRINT 'deploying IndexOptimize - statistics update USER_DATABASES'; + /****** Object: Job [IndexOptimize - statistics update USER_DATABASES] Script Date: 12.08.2025 14:20:16 ******/ + BEGIN TRANSACTION; + /****** Object: JobCategory [Database Maintenance] Script Date: 12.08.2025 14:20:16 ******/ + IF NOT EXISTS (SELECT [name] FROM msdb.[dbo].[syscategories] WHERE [name]=N'Database Maintenance' AND [category_class]=1) + BEGIN + EXECUTE @ReturnCode = msdb.[dbo].sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'Database Maintenance'; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + END; + + EXECUTE @ReturnCode = msdb.[dbo].sp_add_job @job_name=N'IndexOptimize - statistics update USER_DATABASES', + @enabled=1, + @notify_level_eventlog=0, + @notify_level_email=0, + @notify_level_netsend=0, + @notify_level_page=0, + @delete_level=0, + @description=N'Source: https://ola.hallengren.com', + @category_name=N'Database Maintenance', + @owner_login_name=N'sa', @job_id = @jobId OUTPUT; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + /****** Object: Step [IndexOptimize - statistics update USER_DATABASES] Script Date: 12.08.2025 14:20:16 ******/ + EXECUTE @ReturnCode = msdb.[dbo].sp_add_jobstep @job_id=@jobId, @step_name=N'IndexOptimize - statistics update USER_DATABASES', + @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'EXECUTE [dbo].[IndexOptimize] + @Databases = ''USER_DATABASES'' , + @FragmentationLow = NULL , + @FragmentationMedium = NULL , + @FragmentationHigh = NULL , + @UpdateStatistics = ''ALL'' , + @OnlyModifiedStatistics = N''Y'' , + @LogToTable = N''Y'';', + @database_name=N'master', + @flags=0; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + EXECUTE @ReturnCode = msdb.[dbo].sp_update_job @job_id = @jobId, @start_step_id = 1; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + EXECUTE @ReturnCode = msdb.[dbo].sp_add_jobschedule @job_id=@jobId, @name=N'maintenance_inexOptimize_statistics', + @enabled=1, + @freq_type=8, + @freq_interval=64, + @freq_subday_type=1, + @freq_subday_interval=0, + @freq_relative_interval=0, + @freq_recurrence_factor=1, + @active_start_date=20250812, + @active_end_date=99991231, + @active_start_time=10000, + @active_end_time=235959, + @schedule_uid=N'cbb27a89-d485-4e1c-b4fe-402db58bc131'; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + EXECUTE @ReturnCode = msdb.[dbo].sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'; + IF (@@ERROR <> 0 OR @ReturnCode <> 0) RAISERROR('Error executing command',15,15); + COMMIT TRANSACTION; + --GOTO EndSave + +END; +--#endregion statistics refresh + + + +IF (@@TRANCOUNT > 0) +BEGIN + --ROLLBACK TRANSACTION; + COMMIT TRANSACTION +END \ No newline at end of file