diff --git a/.gitattributes b/.gitattributes index 5bdace7f..1e9fe84c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,3 @@ -*.sql linguist-language=TSQL +*.sql linguist-language=TSQL -text +.github export-ignore +.gitattributes export-ignore diff --git a/.github/workflows/check-scripts.yml b/.github/workflows/check-scripts.yml new file mode 100644 index 00000000..08ea7ccf --- /dev/null +++ b/.github/workflows/check-scripts.yml @@ -0,0 +1,174 @@ +name: Check scripts + +# Verifies that the scripts in the repository are consistent with each other before a pull request can be merged. + +on: + pull_request: + branches: + - main + push: + branches: + - main # confirms that main itself is consistent after a merge + workflow_dispatch: # adds a manual "Run workflow" button, handy for testing + +jobs: + checksums: + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v5 + + - name: Check that every script is listed in SHA256SUMS.txt + run: | + ls *.sql | sort > /tmp/scripts.txt + awk '{print $2}' SHA256SUMS.txt | sort > /tmp/listed.txt + if ! diff /tmp/scripts.txt /tmp/listed.txt; then + echo "The scripts in the repository and the files listed in SHA256SUMS.txt do not match." + echo "Lines starting with < are scripts that are not listed; lines starting with > are listed files that do not exist." + exit 1 + fi + echo "All scripts are listed." + + - name: Check that the checksums are current + run: sha256sum -c SHA256SUMS.txt + + versions: + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v5 + + - name: Check that every script has the same version timestamp + run: | + set -u + + # Every script in a release is built at the same time and stamped with that time. + # The installation scripts contain one stamp per stored procedure they include, so this also detects an installation script built from an outdated procedure. + PATTERN='Version: [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}' + + echo "Version timestamps found in each script:" + for f in *.sql; do + STAMPS=$(grep -hoE "$PATTERN" "$f" | sed 's/^Version: //' | sort -u | tr '\n' ' ') + printf ' %-42s %s\n' "$f" "${STAMPS:-(none)}" + done + + ALL=$(grep -hoE "$PATTERN" *.sql | sed 's/^Version: //' | sort -u) + COUNT=$(printf '%s\n' "$ALL" | grep -c . || true) + + echo + if [ "$COUNT" -eq 0 ]; then + echo "No version timestamps were found. The scripts are expected to carry one." + exit 1 + fi + if [ "$COUNT" -ne 1 ]; then + echo "The scripts do not all have the same version timestamp:" + printf '%s\n' "$ALL" | sed 's/^/ /' + exit 1 + fi + echo "All scripts have the same version timestamp: $ALL" + + timestamp: + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 # the base commit is needed to read the previous version timestamp + + - name: Check that the version timestamp moves forward + if: github.event_name == 'pull_request' + run: | + set -u + + # Every release is built from scratch and stamped with the time of the build, so the timestamp always moves forward. + # A timestamp that has gone backwards means the scripts were copied from an older build folder. + BASE='${{ github.event.pull_request.base.sha }}' + PATTERN='Version: [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}' + + OLD=$(git show "$BASE:DatabaseBackup.sql" | grep -m1 -oE "$PATTERN" | sed 's/^Version: //') + NEW=$(grep -m1 -oE "$PATTERN" DatabaseBackup.sql | sed 's/^Version: //') + + if [ -z "$OLD" ] || [ -z "$NEW" ]; then + echo "A version timestamp could not be read from DatabaseBackup.sql." + exit 1 + fi + + if [ "$NEW" = "$OLD" ]; then + if git diff --quiet "$BASE" -- '*.sql'; then + echo "The scripts have not changed. Version timestamp $NEW." + exit 0 + fi + echo "The scripts have changed but the version timestamp is still $NEW." + echo "Every change to the scripts should come from a build." + exit 1 + fi + + if [ "$NEW" \< "$OLD" ]; then + echo "The version timestamp has gone backwards: $OLD -> $NEW." + echo "This usually means the scripts were copied from an older build folder." + exit 1 + fi + + echo "The version timestamp has moved forward: $OLD -> $NEW" + + installers: + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v5 + + - name: Check that the installation scripts contain the same code as the separate scripts + run: | + python3 - << 'EOF' + import sys + + BOM = b"\xef\xbb\xbf" + + # Which separate scripts each installation script is built from, in order. + INSTALLERS = { + "MaintenanceSolution.sql": [ + "CommandLog.sql", "CommandExecute.sql", "DatabaseBackup.sql", + "DatabaseIntegrityCheck.sql", "IndexOptimize.sql"], + "MaintenanceSolutionAzureSQLDatabase.sql": [ + "CommandLog.sql", "CommandExecute.sql", + "DatabaseIntegrityCheck.sql", "IndexOptimize.sql"], + } + + def read(path): + data = open(path, "rb").read() + return data[len(BOM):] if data.startswith(BOM) else data + + def body(path): + # The separate scripts have one line terminator appended after they have been merged into the installation scripts, so the merged copy is the file without it. + return read(path).rstrip(b"\r\n") + + failures = 0 + for installer, objects in INSTALLERS.items(): + data = read(installer) + print("%s (%d bytes)" % (installer, len(data))) + position = None + for name in objects: + part = body(name) + found = data.find(part) + if found < 0: + print(" FAIL %s: the code is not in the installation script" % name) + failures += 1 + break + if position is not None and found != position: + print(" FAIL %s: starts at byte %d, but the previous script ends at byte %d" + % (name, found, position)) + failures += 1 + break + print(" OK %-28s %8d bytes at byte %d" % (name, len(part), found)) + position = found + len(part) + 2 # one line terminator between the scripts + else: + # The header and the footer are not in the repository, so the bytes before the first script and after the last one are not checked. + print(" (%d bytes before the first script, %d bytes after the last)" + % (data.find(body(objects[0])), len(data) - position)) + print() + + if failures: + print("Rebuild the installation scripts so that every file comes from the same build.") + sys.exit(1) + print("The installation scripts contain the same code as the separate scripts.") + EOF diff --git a/.github/workflows/create-tag.yml b/.github/workflows/create-tag.yml new file mode 100644 index 00000000..645b34fa --- /dev/null +++ b/.github/workflows/create-tag.yml @@ -0,0 +1,56 @@ +name: Create tag from version header + +# Runs every time you push/merge a commit to main. It reads the version timestamp out of the script header and creates a matching, immutable Git tag. + +on: + push: + branches: + - main + paths: + - MaintenanceSolution.sql # only run when this file actually changes + +permissions: + contents: write # allow the workflow to create and push a tag + +jobs: + tag: + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 # full history so existing tags are visible + + - name: Read version from header and create tag + run: | + set -euo pipefail + + # 1. Pull the "YYYY-MM-DD HH:MM:SS" timestamp out of the header. + VERSION=$(grep -m1 -oP '(?<=--// Version: )\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}' MaintenanceSolution.sql || true) + + if [ -z "$VERSION" ]; then + echo "No version line found in MaintenanceSolution.sql - nothing to tag." + exit 0 + fi + + # 2. Turn it into a valid tag name. + # Tags cannot contain spaces or colons, so strip the separators: + # "2026-07-16 23:38:29" -> "20260716_233829" + TAG=$(echo "$VERSION" | sed 's/[-:]//g; s/ /_/') + echo "Version in header : $VERSION" + echo "Tag name : $TAG" + + # 3. If that tag already exists, do nothing (safe to re-run). + if git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then + echo "Tag $TAG already exists - skipping." + exit 0 + fi + + # 4. Create an annotated tag on this commit and push it. + # Annotated tags carry a tagger and date, so the runner needs a git identity. + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git tag -a "$TAG" -m "Version: $VERSION" + git push origin "refs/tags/$TAG" + echo "Created tag $TAG" diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml new file mode 100644 index 00000000..4e154f1c --- /dev/null +++ b/.github/workflows/deploy-website.yml @@ -0,0 +1,86 @@ +name: Deploy to website + +# Uploads the released files to the website over FTPS whenever a release is merged to main. + +on: + push: + branches: + - main + paths: + - MaintenanceSolution.sql # a release always updates this file + workflow_dispatch: # adds a manual "Run workflow" button, handy for testing + +env: + REMOTE_DIR: "/public_html/scripts" # scripts folder on the server + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v5 + + - name: Install lftp (FTPS client) + run: sudo apt-get update -qq && sudo apt-get install -y -qq lftp + + - name: Upload files over FTPS + env: + FTP_SERVER: ${{ secrets.FTP_SERVER }} + FTP_USERNAME: ${{ secrets.FTP_USERNAME }} + LFTP_PASSWORD: ${{ secrets.FTP_PASSWORD }} + run: | + set -u + + # Upload order: components first, then the installers, SHA256SUMS.txt last + # (the checksums go live only after the files they describe are in place). + FILES="CommandLog.sql Queue.sql QueueDatabase.sql CommandExecute.sql DatabaseBackup.sql DatabaseIntegrityCheck.sql IndexOptimize.sql MaintenanceSolutionAzureSQLDatabase.sql MaintenanceSolution.sql SHA256SUMS.txt" + + # Explicit FTPS (AUTH TLS) on port 21 + SETTINGS="set ftp:ssl-force true; set ftp:ssl-protect-data true; set ssl:verify-certificate true; set net:max-retries 2; set net:timeout 60" + + # The password is read from the LFTP_PASSWORD environment variable, keeping it off the command line and out of the process table. + run_lftp () { + lftp -p 21 --env-password -u "$FTP_USERNAME" -e "$SETTINGS; $1; bye" "$FTP_SERVER" + } + + # Two-phase upload. + # Phase 1 uploads ALL files under temporary names. + # Phase 2 renames the temp files into place. + UPLOAD="" + for f in $FILES; do + UPLOAD="$UPLOAD put $f -o $REMOTE_DIR/$f.tmp;" + done + for f in $FILES; do + UPLOAD="$UPLOAD mv $REMOTE_DIR/$f.tmp $REMOTE_DIR/$f;" + done + + # Verification: list the files on the server and compare each size, byte for byte, against the file in the repository. + verify () { + LISTING=$(run_lftp "cls -s --block-size=1 $REMOTE_DIR/*.sql $REMOTE_DIR/SHA256SUMS.txt") || return 1 + for f in $FILES; do + LOCAL=$(stat -c %s "$f") + REMOTE=$(printf '%s\n' "$LISTING" | grep "/$f\$" | awk '{print $1}') + if [ "$LOCAL" != "$REMOTE" ]; then + echo "VERIFY FAILED: $f is $LOCAL bytes in the repo but ${REMOTE:-missing} on the server" + return 1 + fi + echo "Verified $f ($LOCAL bytes)" + done + return 0 + } + + # Try the whole upload-and-verify up to 3 times + for attempt in 1 2 3; do + echo "=== Upload attempt $attempt of 3 ===" + if run_lftp "$UPLOAD" && verify; then + echo "Deploy succeeded and verified." + exit 0 + fi + if [ "$attempt" -lt 3 ]; then + echo "Attempt $attempt failed - waiting 120 seconds before retrying." + sleep 120 + fi + done + + echo "All attempts failed. The website may be behind or partially updated; re-run this workflow when the host is reachable to make it consistent." + exit 1 diff --git a/CommandExecute.sql b/CommandExecute.sql index 6cea42f2..494eb3b6 100644 --- a/CommandExecute.sql +++ b/CommandExecute.sql @@ -22,6 +22,8 @@ ALTER PROCEDURE [dbo].[CommandExecute] @IndexType int = NULL, @StatisticsName nvarchar(max) = NULL, @PartitionNumber int = NULL, +@EncryptionKey nvarchar(max) = NULL, +@EncryptionKeyPlaceholder nvarchar(max) = NULL, @ExtendedInfo xml = NULL, @LockMessageSeverity int = 16, @ExecuteAsUser nvarchar(max) = NULL, @@ -36,7 +38,7 @@ BEGIN --// Source: https://ola.hallengren.com //-- --// License: https://ola.hallengren.com/license.html //-- --// GitHub: https://github.com/olahallengren/sql-server-maintenance-solution //-- - --// Version: 2026-05-18 20:15:56 //-- + --// Version: 2026-08-23 14:46:45 //-- ---------------------------------------------------------------------------------------------------- SET NOCOUNT ON @@ -50,7 +52,7 @@ BEGIN DECLARE @Errors TABLE (ID int IDENTITY PRIMARY KEY, [Message] nvarchar(max) NOT NULL, Severity int NOT NULL, - [State] int) + [State] int NOT NULL) DECLARE @CurrentMessage nvarchar(max) DECLARE @CurrentSeverity int @@ -70,32 +72,28 @@ BEGIN DECLARE @RevertCommand nvarchar(max) + DECLARE @CommandMasked 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 + VALUES('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 + VALUES('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 + VALUES('The table CommandLog is missing. Download https://ola.hallengren.com/scripts/CommandLog.sql.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -105,56 +103,62 @@ BEGIN 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 + VALUES('The value for the parameter @DatabaseContext is not supported. Specify the name of an existing database.', 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 + VALUES('The value for the parameter @Command is not supported. The value cannot be NULL or empty.', 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 + VALUES('The value for the parameter @CommandType is not supported. The value cannot be NULL or empty, and the maximum length is 60 characters.', 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 + VALUES('The value for the parameter @Mode is not supported. Supported values are 1 and 2.', 16, 1) + END + + IF (@EncryptionKey IS NULL AND @EncryptionKeyPlaceholder IS NOT NULL) OR (@EncryptionKey IS NOT NULL AND @EncryptionKeyPlaceholder IS NULL) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameters @EncryptionKey and @EncryptionKeyPlaceholder must be specified together.', 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 + VALUES('The value for the parameter @LockMessageSeverity is not supported. Supported values are 10 and 16.', 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 + VALUES('The value for the parameter @ExecuteAsUser is not supported. The maximum length is 128 characters.', 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 + VALUES('The value for the parameter @LogToTable is not supported. Supported values are ''Y'' and ''N''.', 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 + VALUES('The value for the parameter @Execute is not supported. Supported values are ''Y'' and ''N''.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Raise errors //-- ---------------------------------------------------------------------------------------------------- - DECLARE ErrorCursor CURSOR FAST_FORWARD FOR SELECT [Message], Severity, [State] FROM @Errors ORDER BY [ID] ASC + DECLARE ErrorCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Message], Severity, [State] FROM @Errors ORDER BY [ID] ASC OPEN ErrorCursor @@ -189,19 +193,27 @@ BEGIN SET @RevertCommand = 'REVERT' END + ---------------------------------------------------------------------------------------------------- + --// Mask encryption key //-- + ---------------------------------------------------------------------------------------------------- + + SET @CommandMasked = CASE WHEN @EncryptionKeyPlaceholder IS NULL THEN @Command ELSE REPLACE(@Command,@EncryptionKeyPlaceholder,'********') END + + SET @Command = CASE WHEN @EncryptionKeyPlaceholder IS NULL THEN @Command ELSE REPLACE(@Command,@EncryptionKeyPlaceholder,REPLACE(ISNULL(@EncryptionKey,''),'''','''''')) END + ---------------------------------------------------------------------------------------------------- --// Log initial information //-- ---------------------------------------------------------------------------------------------------- SET @StartTime = SYSDATETIME() - SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar,@StartTime,120) + SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar(max),@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 + SET @StartMessage = 'Command: ' + @CommandMasked RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT IF @Comment IS NOT NULL @@ -213,10 +225,10 @@ BEGIN 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 + VALUES (@DatabaseName, @SchemaName, @ObjectName, @ObjectType, @IndexName, @IndexType, @StatisticsName, @PartitionNumber, @ExtendedInfo, @CommandType, @CommandMasked, @StartTime) - SET @ID = SCOPE_IDENTITY() + SET @ID = SCOPE_IDENTITY() + END ---------------------------------------------------------------------------------------------------- --// Execute command //-- @@ -238,7 +250,7 @@ BEGIN SET @Error = ERROR_NUMBER() SET @ErrorMessageOriginal = ERROR_MESSAGE() - SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar) + ', ' + ISNULL(ERROR_MESSAGE(),'') + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + ISNULL(ERROR_MESSAGE(),'') SET @Severity = CASE WHEN ERROR_NUMBER() IN(1205, 1222, 5245) THEN @LockMessageSeverity ELSE ERROR_SEVERITY() END RAISERROR('%s',@Severity,1,@ErrorMessage) WITH NOWAIT @@ -263,10 +275,10 @@ BEGIN 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) + SET @EndMessage = 'Duration: ' + CASE WHEN (DATEDIFF(SECOND,@StartTime,@EndTime) / (24 * 3600)) > 0 THEN CAST((DATEDIFF(SECOND,@StartTime,@EndTime) / (24 * 3600)) AS nvarchar(max)) + '.' ELSE '' END + CONVERT(nvarchar(max),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) + SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar(max),@EndTime,120) RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT diff --git a/DatabaseBackup.sql b/DatabaseBackup.sql index 698f334d..dc6fafef 100644 --- a/DatabaseBackup.sql +++ b/DatabaseBackup.sql @@ -50,14 +50,15 @@ ALTER PROCEDURE [dbo].[DatabaseBackup] @AvailabilityGroups nvarchar(max) = NULL, @Updateability nvarchar(max) = 'ALL', @AdaptiveCompression nvarchar(max) = NULL, -@ModificationLevel int = NULL, +@MinModificationLevel int = NULL, @MinDatabaseSizeForDifferentialBackup int = NULL, -@LogSizeSinceLastLogBackup int = NULL, -@TimeSinceLastLogBackup int = NULL, +@MinLogSizeSinceLastLogBackup int = NULL, +@MinTimeSinceLastLogBackup int = NULL, @DataDomainBoostHost nvarchar(max) = NULL, @DataDomainBoostUser nvarchar(max) = NULL, @DataDomainBoostDevicePath nvarchar(max) = NULL, @DataDomainBoostLockboxPath nvarchar(max) = NULL, +@DataDomainBoostNoOutputTable nvarchar(max) = 'N', @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, @@ -72,11 +73,13 @@ ALTER PROCEDURE [dbo].[DatabaseBackup] @Format nvarchar(max) = 'N', @ObjectLevelRecoveryMap nvarchar(max) = 'N', @ExcludeLogShippedFromLogBackup nvarchar(max) = 'Y', +@ExcludeSeedingFromLogBackup nvarchar(max) = 'N', @DirectoryCheck nvarchar(max) = 'Y', @BackupOptions nvarchar(max) = NULL, @Stats int = NULL, @ExpireDate datetime = NULL, @RetainDays int = NULL, +@AllowNonCopyOnlyBackupsOnForwarder nvarchar(max) = 'N', @StringDelimiter nvarchar(max) = ',', @DatabaseOrder nvarchar(max) = NULL, @DatabasesInParallel nvarchar(max) = 'N', @@ -91,7 +94,7 @@ BEGIN --// Source: https://ola.hallengren.com //-- --// License: https://ola.hallengren.com/license.html //-- --// GitHub: https://github.com/olahallengren/sql-server-maintenance-solution //-- - --// Version: 2026-05-18 20:15:56 //-- + --// Version: 2026-08-23 14:46:45 //-- ---------------------------------------------------------------------------------------------------- SET NOCOUNT ON @@ -105,9 +108,25 @@ BEGIN 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 @Parameters TABLE (ID int IDENTITY PRIMARY KEY, + [Name] nvarchar(max) NOT NULL, + ValueNvarchar nvarchar(max), + ValueInt int, + ValueDatetime datetime2) + + DECLARE @ParametersString nvarchar(max) + DECLARE @CurrentParameterName nvarchar(max) + DECLARE @CurrentParameterValueNvarchar nvarchar(max) + DECLARE @CurrentParameterValueInt int + DECLARE @CurrentParameterValueDatetime datetime2 + DECLARE @CurrentParameterDelimiter nvarchar(max) + DECLARE @CurrentParameterMessage nvarchar(max) DECLARE @HostPlatform nvarchar(max) + DECLARE @ContainedAvailabilityGroupID uniqueidentifier + DECLARE @ContainedAvailabilityGroupListenerConnection bit + DECLARE @IsSysadmin bit = IS_SRVROLEMEMBER('sysadmin') DECLARE @DirectorySeparator nvarchar(max) DECLARE @Updated bit @@ -144,11 +163,12 @@ BEGIN DECLARE @CurrentDifferentialBaseLSN numeric(25,0) DECLARE @CurrentDifferentialBaseIsSnapshot bit DECLARE @CurrentLogLSN numeric(25,0) + DECLARE @BackupInProgress bit DECLARE @CurrentLatestBackup datetime2 DECLARE @CurrentDatabaseNameFS nvarchar(max) DECLARE @CurrentDirectoryStructure nvarchar(max) DECLARE @CurrentDatabaseFileName nvarchar(max) - DECLARE @CurrentMaxFilePathLength nvarchar(max) + DECLARE @CurrentMaxFilePathLength int DECLARE @CurrentFileName nvarchar(max) DECLARE @CurrentDirectoryID int DECLARE @CurrentDirectoryPath nvarchar(4000) @@ -156,16 +176,21 @@ BEGIN DECLARE @CurrentDate datetime2 DECLARE @CurrentDateUTC datetime2 DECLARE @CurrentCleanupDate datetime2 - DECLARE @CurrentReplicaID uniqueidentifier + DECLARE @CurrentAvailabilityGroupReplicaID 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 @CurrentSecondaryRoleAllowConnections nvarchar(max) + DECLARE @CurrentDistributedAvailabilityGroup nvarchar(max) + DECLARE @CurrentDistributedAvailabilityGroupReplicaID uniqueidentifier + DECLARE @CurrentDistributedAvailabilityGroupRole nvarchar(max) DECLARE @CurrentIsPreferredBackupReplica bit DECLARE @CurrentDatabaseMirroringRole nvarchar(max) DECLARE @CurrentLogShippingRole nvarchar(max) + DECLARE @CurrentIsSeeding bit DECLARE @CurrentBackupOperationSupportedOnSecondaryReplicas bit DECLARE @CurrentBackupSetID int @@ -183,7 +208,7 @@ BEGIN DECLARE @Errors TABLE (ID int IDENTITY PRIMARY KEY, [Message] nvarchar(max) NOT NULL, Severity int NOT NULL, - [State] int) + [State] int NOT NULL) DECLARE @CurrentMessage nvarchar(max) DECLARE @CurrentSeverity int @@ -203,29 +228,29 @@ BEGIN ParentDirectoryExists bit) DECLARE @tmpDatabases TABLE (ID int IDENTITY, - DatabaseName nvarchar(max), - DatabaseNameFS nvarchar(max), - DatabaseType nvarchar(max), + DatabaseName nvarchar(128), + DatabaseNameFS nvarchar(128), + DatabaseType nvarchar(1), AvailabilityGroup bit, StartPosition int, DatabaseSize bigint, LogSizeSinceLastLogBackup float, - [Order] int, - Selected bit, - Completed bit, - PRIMARY KEY(Selected, Completed, [Order], ID)) + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) DECLARE @tmpAvailabilityGroups TABLE (ID int IDENTITY PRIMARY KEY, - AvailabilityGroupName nvarchar(max), + AvailabilityGroupName nvarchar(128), StartPosition int, - Selected bit) + Selected bit DEFAULT 0) - DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(max), - AvailabilityGroupName nvarchar(max)) + DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(128), + AvailabilityGroupName nvarchar(128)) DECLARE @SelectedDatabases TABLE (DatabaseName nvarchar(max), - DatabaseType nvarchar(max), - AvailabilityGroup nvarchar(max), + DatabaseType nvarchar(1), + AvailabilityGroup bit, StartPosition int, Selected bit) @@ -256,129 +281,176 @@ BEGIN Mirror bit, DirectoryNumber int) - DECLARE @CurrentFiles TABLE ([Type] nvarchar(max), + DECLARE @CurrentFiles TABLE ([Type] nvarchar(4), FilePath nvarchar(max), Mirror bit) - DECLARE @CurrentCleanupDates TABLE (CleanupDate datetime2, + DECLARE @CurrentCleanupDates TABLE ([Type] nvarchar(max), + CleanupDate datetime2, Mirror bit) + DECLARE @EncryptionKeyMasked nvarchar(max) = CASE WHEN @EncryptionKey IS NULL THEN NULL ELSE '********' END + + DECLARE @EncryptionKeyPlaceholder nvarchar(max) = CASE WHEN @EncryptionKey IS NULL THEN NULL ELSE CAST(NEWID() AS nvarchar(max)) END + 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)) + DECLARE @ProductVersion nvarchar(max) = CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + DECLARE @ProductMajorVersion nvarchar(max) = CAST(SERVERPROPERTY('ProductMajorVersion') AS nvarchar(max)) + DECLARE @ProductMinorVersion nvarchar(max) = CAST(SERVERPROPERTY('ProductMinorVersion') AS nvarchar(max)) + DECLARE @ProductUpdateType nvarchar(max) = CAST(SERVERPROPERTY('ProductUpdateType') AS nvarchar(max)) + DECLARE @EngineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int) + DECLARE @EditionID bigint = CAST(SERVERPROPERTY('EditionID') AS bigint) + DECLARE @Edition nvarchar(max) = CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + DECLARE @IsHadrEnabled bit = CAST(SERVERPROPERTY('IsHadrEnabled') AS bit) + DECLARE @IsClustered bit = CAST(SERVERPROPERTY('IsClustered') AS bit) + DECLARE @ServerName nvarchar(max) = CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) + DECLARE @InstanceName nvarchar(max) = CAST(SERVERPROPERTY('InstanceName') AS nvarchar(max)) + DECLARE @MachineName nvarchar(max) = CAST(SERVERPROPERTY('MachineName') AS nvarchar(max)) + DECLARE @InstanceDefaultBackupPath nvarchar(max) = CAST(SERVERPROPERTY('InstanceDefaultBackupPath') AS nvarchar(max)) + + DECLARE @Version numeric(18,10) = CAST(PARSENAME(@ProductVersion,4) + '.' + PARSENAME(@ProductVersion,3) + PARSENAME(@ProductVersion,2) AS numeric(18,10)) - IF @Version >= 14 + IF @EngineEdition = 8 AND @ProductVersion = '12.0.2000.8' AND @ProductUpdateType = 'CU' BEGIN - SELECT @HostPlatform = host_platform - FROM sys.dm_os_host_info + SET @Version = 16.01000 END - ELSE + + SELECT @HostPlatform = host_platform + FROM sys.dm_os_host_info + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) BEGIN - SET @HostPlatform = 'Windows' + SET @CurrentCommand = 'SELECT @ParamContainedAvailabilityGroupID = contained_availability_group_id FROM sys.dm_exec_sessions WHERE session_id = @@SPID' + + EXECUTE sp_executesql @stmt = @CurrentCommand, @params = N'@ParamContainedAvailabilityGroupID uniqueidentifier OUTPUT', @ParamContainedAvailabilityGroupID = @ContainedAvailabilityGroupID OUTPUT END - DECLARE @AmazonRDS bit = CASE WHEN EXISTS (SELECT * FROM sys.databases WHERE [name] = 'rdsadmin') AND SUSER_SNAME(0x01) = 'rdsa' THEN 1 ELSE 0 END + SET @ContainedAvailabilityGroupListenerConnection = CASE WHEN @ContainedAvailabilityGroupID IS NOT NULL THEN 1 ELSE 0 END + + DECLARE @AmazonRDS bit = CASE WHEN @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 += ', @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) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Databases', @Databases) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Directory', @Directory) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@BackupType', @BackupType) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Verify', @Verify) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@CleanupTime', @CleanupTime) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@CleanupMode', @CleanupMode) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Compress', @Compress) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@CompressionAlgorithm', @CompressionAlgorithm) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@CompressionLevel', @CompressionLevel) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@CopyOnly', @CopyOnly) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ChangeBackupType', @ChangeBackupType) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@BackupSoftware', @BackupSoftware) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Checksum', @Checksum) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@BlockSize', @BlockSize) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@BufferCount', @BufferCount) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MaxTransferSize', @MaxTransferSize) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@NumberOfFiles', @NumberOfFiles) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MinBackupSizeForMultipleFiles', @MinBackupSizeForMultipleFiles) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MaxFileSize', @MaxFileSize) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@CompressionLevelNumeric', @CompressionLevelNumeric) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Description', @Description) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@BackupSetName', @BackupSetName) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@Threads', @Threads) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@Throttle', @Throttle) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Encrypt', @Encrypt) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@EncryptionAlgorithm', @EncryptionAlgorithm) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ServerCertificate', @ServerCertificate) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ServerAsymmetricKey', @ServerAsymmetricKey) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@EncryptionKey', @EncryptionKeyMasked) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ReadWriteFileGroups', @ReadWriteFileGroups) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@OverrideBackupPreference', @OverrideBackupPreference) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@NoRecovery', @NoRecovery) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@URL', @URL) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Credential', @Credential) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@MirrorDirectory', @MirrorDirectory) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MirrorCleanupTime', @MirrorCleanupTime) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@MirrorCleanupMode', @MirrorCleanupMode) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@MirrorURL', @MirrorURL) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroups', @AvailabilityGroups) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Updateability', @Updateability) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AdaptiveCompression', @AdaptiveCompression) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MinModificationLevel', @MinModificationLevel) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MinDatabaseSizeForDifferentialBackup', @MinDatabaseSizeForDifferentialBackup) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MinLogSizeSinceLastLogBackup', @MinLogSizeSinceLastLogBackup) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MinTimeSinceLastLogBackup', @MinTimeSinceLastLogBackup) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataDomainBoostHost', @DataDomainBoostHost) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataDomainBoostUser', @DataDomainBoostUser) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataDomainBoostDevicePath', @DataDomainBoostDevicePath) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataDomainBoostLockboxPath', @DataDomainBoostLockboxPath) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataDomainBoostNoOutputTable', @DataDomainBoostNoOutputTable) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DirectoryStructure', @DirectoryStructure) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroupDirectoryStructure', @AvailabilityGroupDirectoryStructure) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DirectoryStructureCase', @DirectoryStructureCase) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FileName', @FileName) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroupFileName', @AvailabilityGroupFileName) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FileNameCase', @FileNameCase) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@TokenTimezone', @TokenTimezone) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FileExtensionFull', @FileExtensionFull) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FileExtensionDiff', @FileExtensionDiff) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FileExtensionLog', @FileExtensionLog) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Init', @Init) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Format', @Format) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ObjectLevelRecoveryMap', @ObjectLevelRecoveryMap) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ExcludeLogShippedFromLogBackup', @ExcludeLogShippedFromLogBackup) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ExcludeSeedingFromLogBackup', @ExcludeSeedingFromLogBackup) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DirectoryCheck', @DirectoryCheck) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@BackupOptions', @BackupOptions) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@Stats', @Stats) + INSERT INTO @Parameters ([Name], ValueDatetime) VALUES('@ExpireDate', @ExpireDate) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@RetainDays', @RetainDays) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AllowNonCopyOnlyBackupsOnForwarder', @AllowNonCopyOnlyBackupsOnForwarder) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StringDelimiter', @StringDelimiter) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabaseOrder', @DatabaseOrder) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabasesInParallel', @DatabasesInParallel) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@LogToTable', @LogToTable) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Execute', @Execute) + + SELECT @ParametersString = STRING_AGG(CAST([Name] + ' = ' + CASE WHEN ValueNvarchar IS NOT NULL THEN '''' + REPLACE(ValueNvarchar,'''','''''') + '''' WHEN ValueInt IS NOT NULL THEN CAST(ValueInt AS nvarchar(max)) WHEN ValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), ValueDatetime, 21) + '''' ELSE 'NULL' END AS nvarchar(max)), ', ') WITHIN GROUP (ORDER BY [ID] ASC) + FROM @Parameters + + SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar(max),@StartTime,120) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Server: ' + CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) + SET @StartMessage = 'Server: ' + @ServerName RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Version: ' + CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + SET @StartMessage = 'Version: ' + @ProductVersion RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Edition: ' + CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + SET @StartMessage = 'Edition: ' + @Edition RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Platform: ' + @HostPlatform + IF @EngineEdition = 8 + BEGIN + SET @StartMessage = 'Update type: ' + @ProductUpdateType + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + SET @StartMessage = 'Platform: ' + ISNULL(@HostPlatform, 'N/A') + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) + BEGIN + SET @StartMessage = 'Contained availability group connection: ' + CASE WHEN @ContainedAvailabilityGroupListenerConnection = 1 THEN 'Yes' WHEN @ContainedAvailabilityGroupListenerConnection = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + SET @StartMessage = 'Is sysadmin: ' + CASE WHEN @IsSysadmin = 1 THEN 'Yes' WHEN @IsSysadmin = 0 THEN 'No' ELSE 'N/A' END RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Procedure: ' + QUOTENAME(DB_NAME()) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + SET @StartMessage = 'Database: ' + QUOTENAME(DB_NAME()) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Parameters: ' + @Parameters + SET @StartMessage = 'Procedure: ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT SET @StartMessage = 'Version: ' + @VersionTimestamp @@ -386,6 +458,32 @@ BEGIN SET @StartMessage = 'Source: https://ola.hallengren.com' RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + + SET @StartMessage = 'Command:' + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'EXECUTE ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + DECLARE ParameterCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Name], ValueNvarchar, ValueInt, ValueDatetime, CASE WHEN [ID] = MAX([ID]) OVER() THEN '' ELSE ',' END FROM @Parameters ORDER BY [ID] ASC + + OPEN ParameterCursor + + FETCH ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + + WHILE @@FETCH_STATUS = 0 + BEGIN + SET @CurrentParameterMessage = @CurrentParameterName + ' = ' + CASE WHEN @CurrentParameterValueNvarchar IS NOT NULL THEN '''' + REPLACE(@CurrentParameterValueNvarchar,'''','''''') + '''' WHEN @CurrentParameterValueInt IS NOT NULL THEN CAST(@CurrentParameterValueInt AS nvarchar(max)) WHEN @CurrentParameterValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), @CurrentParameterValueDatetime, 21) + '''' ELSE 'NULL' END + @CurrentParameterDelimiter + + RAISERROR('%s',10,1,@CurrentParameterMessage) WITH NOWAIT + + FETCH NEXT FROM ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + END + + CLOSE ParameterCursor + + DEALLOCATE ParameterCursor RAISERROR(@EmptyLine,10,1) WITH NOWAIT @@ -393,64 +491,58 @@ BEGIN --// 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 + VALUES('ANSI_NULLS has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-backup.html.', 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 + VALUES('QUOTED_IDENTIFIER has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-backup.html.', 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 + VALUES('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%') + 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 '%@EncryptionKeyPlaceholder%') 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 + VALUES('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 + VALUES('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 + VALUES('The table Queue is missing. It is required when @DatabasesInParallel = ''Y''. 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 + VALUES('The table QueueDatabase is missing. It is required when @DatabasesInParallel = ''Y''. 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 + VALUES('The stored procedure cannot be executed inside a transaction. The transaction count (@@TRANCOUNT) has to be 0. See https://ola.hallengren.com/sql-server-backup.html.', 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 + VALUES('The stored procedure DatabaseBackup is not supported on Amazon RDS. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -512,11 +604,10 @@ BEGIN FROM Databases4 OPTION (MAXRECURSION 0) - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN - INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName, Selected) - SELECT name AS AvailabilityGroupName, - 0 AS Selected + INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName) + SELECT name AS AvailabilityGroupName FROM sys.availability_groups INSERT INTO @tmpDatabasesAvailabilityGroups (DatabaseName, AvailabilityGroupName) @@ -527,14 +618,11 @@ BEGIN 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) + INSERT INTO @tmpDatabases (DatabaseName, DatabaseNameFS, DatabaseType, AvailabilityGroup) 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 + NULL AS AvailabilityGroup FROM sys.databases WHERE [name] <> 'tempdb' AND source_database_id IS NULL @@ -548,7 +636,7 @@ BEGIN SET tmpDatabases.Selected = SelectedDatabases.Selected FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -557,7 +645,7 @@ BEGIN SET tmpDatabases.Selected = SelectedDatabases.Selected FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -568,7 +656,7 @@ BEGIN INNER JOIN (SELECT tmpDatabases.DatabaseName, MIN(SelectedDatabases.StartPosition) AS StartPosition FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -578,14 +666,14 @@ BEGIN 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 + VALUES('The value for the parameter @Databases is not supported. The value could not be parsed into a list of databases. See https://ola.hallengren.com/sql-server-backup.html#Databases.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Select availability groups //-- ---------------------------------------------------------------------------------------------------- - IF @AvailabilityGroups IS NOT NULL AND @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 1 BEGIN SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, CHAR(10), '') @@ -639,14 +727,14 @@ BEGIN SET tmpAvailabilityGroups.Selected = SelectedAvailabilityGroups.Selected FROM @tmpAvailabilityGroups tmpAvailabilityGroups INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups - ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(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,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'[','[[]'),'_','[_]') WHERE SelectedAvailabilityGroups.Selected = 0 UPDATE tmpAvailabilityGroups @@ -655,7 +743,7 @@ BEGIN INNER JOIN (SELECT tmpAvailabilityGroups.AvailabilityGroupName, MIN(SelectedAvailabilityGroups.StartPosition) AS StartPosition FROM @tmpAvailabilityGroups tmpAvailabilityGroups INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups - ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'[','[[]'),'_','[_]') WHERE SelectedAvailabilityGroups.Selected = 1 GROUP BY tmpAvailabilityGroups.AvailabilityGroupName) SelectedAvailabilityGroups2 ON tmpAvailabilityGroups.AvailabilityGroupName = SelectedAvailabilityGroups2.AvailabilityGroupName @@ -670,29 +758,35 @@ BEGIN 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) + IF @AvailabilityGroups IS NOT NULL AND (NOT EXISTS(SELECT * FROM @SelectedAvailabilityGroups) OR EXISTS(SELECT * FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName IS NULL OR AvailabilityGroupName = '')) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroups is not supported.', 16, 1 + VALUES('The value for the parameter @AvailabilityGroups is not supported. The value could not be parsed into a list of availability groups. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroups.', 16, 1) + END + + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 0 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @AvailabilityGroups can only be used when availability groups are enabled on the instance. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroups.', 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 + VALUES('You need to specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) 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 + VALUES('You can only specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Check database names //-- ---------------------------------------------------------------------------------------------------- - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @tmpDatabases WHERE Selected = 1 @@ -701,10 +795,10 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The names of the following databases are not supported: ' + @ErrorMessage + '.', 16, 1 + VALUES('The names of the following databases are not supported: ' + @ErrorMessage + '. A database name has to contain at least one character that can be used in file names. See https://ola.hallengren.com/sql-server-backup.html#Databases.', 16, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') WITHIN GROUP (ORDER BY DatabaseName ASC) 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) @@ -714,7 +808,7 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The names of the following databases are not unique in the file system: ' + @ErrorMessage + '.', 16, 1 + VALUES('The names of the following databases are not unique in the file system: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-backup.html#Databases.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -723,9 +817,9 @@ BEGIN IF @Directory IS NULL AND @URL IS NULL AND (@BackupSoftware <> 'DATA_DOMAIN_BOOST' OR @BackupSoftware IS NULL) BEGIN - IF @Version >= 15 + IF @Version >= 15 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous') BEGIN - SET @DefaultDirectory = CAST(SERVERPROPERTY('InstanceDefaultBackupPath') AS nvarchar(max)) + SET @DefaultDirectory = @InstanceDefaultBackupPath END ELSE BEGIN @@ -739,7 +833,7 @@ BEGIN ELSE BEGIN INSERT INTO @Directories (ID, DirectoryPath, Mirror, Completed) - SELECT 1, @DefaultDirectory, 0, 0 + VALUES(1, @DefaultDirectory, 0, 0) END END @@ -779,8 +873,8 @@ BEGIN 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,' ,',',') + WHILE CHARINDEX(@StringDelimiter + ' ', @MirrorDirectory) > 0 SET @MirrorDirectory = REPLACE(@MirrorDirectory, @StringDelimiter + ' ', @StringDelimiter) + WHILE CHARINDEX(' ' + @StringDelimiter, @MirrorDirectory) > 0 SET @MirrorDirectory = REPLACE(@MirrorDirectory, ' ' + @StringDelimiter, @StringDelimiter) SET @MirrorDirectory = LTRIM(RTRIM(@MirrorDirectory)); @@ -812,43 +906,73 @@ BEGIN 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 + VALUES('The value for the parameter @Directory is not supported. Specify a local path (e.g. D:\Backup), a UNC path (e.g. \\Server\Share), a path starting with / on Linux, or NUL, without leading or trailing spaces. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 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 + VALUES('The same directory has been specified multiple times in the parameters @Directory and @MirrorDirectory. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) 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 + VALUES('The number of directories for the parameters @Directory and @MirrorDirectory has to be the same. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) + END + + IF @Directory IS NOT NULL AND @EngineEdition = 8 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @Directory is not supported on Azure SQL Managed Instance. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) END - IF (@Directory IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 8) OR (@Directory IS NOT NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST') + IF @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 + VALUES('The parameter @Directory is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) 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 + VALUES('The value for the parameter @Directory is not supported. Backup to NUL cannot be combined with other directories. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) 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 + VALUES('Mirrored backup is not supported when backing up to NUL. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END IF EXISTS (SELECT * FROM @Directories WHERE Mirror = 0 AND DirectoryPath = 'NUL') AND @BackupSoftware IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'Backup to NUL is only supported with SQL Server native backups.', 16, 7 + VALUES('Backup to NUL is only supported with SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) + END + + IF (SELECT COUNT(*) FROM @Directories WHERE Mirror = 0) > 64 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Directory is not supported. The maximum number of directories is 64. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) + END + + IF (SELECT COUNT(*) FROM @Directories WHERE Mirror = 0) > 32 AND @BackupSoftware = 'SQLBACKUP' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Directory is not supported. The maximum number of directories with Redgate SQL Backup Pro is 32. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) + END + + IF (SELECT COUNT(*) FROM @Directories WHERE Mirror = 0) > 32 AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) AND @BackupSoftware IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Directory is not supported. The maximum number of directories when performing mirrored SQL Server native backups is 32. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) + END + + IF (SELECT COUNT(*) FROM @Directories WHERE Mirror = 0) > 32 AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) AND @BackupSoftware = 'LITESPEED' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Directory is not supported. The maximum number of directories when performing mirrored backups with LiteSpeed for SQL Server is 32. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -856,37 +980,37 @@ BEGIN 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 + VALUES('The value for the parameter @MirrorDirectory is not supported. Specify a local path (e.g. D:\Backup), a UNC path (e.g. \\Server\Share), or a path starting with / on Linux, without leading or trailing spaces. See https://ola.hallengren.com/sql-server-backup.html#MirrorDirectory.', 16, 1) END - IF EXISTS (SELECT * FROM @Directories GROUP BY DirectoryPath HAVING COUNT(*) <> 1) + IF @BackupSoftware = 'SQLBACKUP' 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, 2 + VALUES('The value for the parameter @MirrorDirectory is not supported. Redgate SQL Backup Pro supports only one mirror directory. See https://ola.hallengren.com/sql-server-backup.html#MirrorDirectory.', 16, 1) END - IF @BackupSoftware IN('SQLBACKUP','SQLSAFE') AND (SELECT COUNT(*) FROM @Directories WHERE Mirror = 1) > 1 + IF @BackupSoftware = '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 + VALUES('The value for the parameter @MirrorDirectory is not supported. Idera SQL Safe Backup supports only one mirror directory. See https://ola.hallengren.com/sql-server-backup.html#MirrorDirectory.', 16, 1) END - IF @MirrorDirectory IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 8 + IF @MirrorDirectory IS NOT NULL AND @EngineEdition = 8 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MirrorDirectory is not supported.', 16, 5 + VALUES('The parameter @MirrorDirectory is not supported on Azure SQL Managed Instance. See https://ola.hallengren.com/sql-server-backup.html#MirrorDirectory.', 16, 1) 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 + VALUES('The value for the parameter @MirrorDirectory is not supported. Mirrored backup is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#MirrorDirectory.', 16, 1) END - IF (@BackupSoftware IS NULL AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) AND SERVERPROPERTY('EngineEdition') <> 3) + IF (@BackupSoftware IS NULL AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) AND @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 + VALUES('The value for the parameter @MirrorDirectory is not supported. Mirrored backup to disk is not supported in this edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#MirrorDirectory.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -916,7 +1040,7 @@ BEGIN 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 + VALUES('The directory ' + @CurrentRootDirectoryPath + ' does not exist. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END UPDATE @Directories @@ -997,39 +1121,65 @@ BEGIN 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 + VALUES('The value for the parameter @URL is not supported. The URL has to start with https:// (Azure Blob Storage) or s3:// (S3-compatible storage). See https://ola.hallengren.com/sql-server-backup.html#URL.', 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 + VALUES('The same URL has been specified multiple times in the parameters @URL and @MirrorURL. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) 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 + VALUES('The number of URLs for the parameters @URL and @MirrorURL has to be the same. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END - ---------------------------------------------------------------------------------------------------- + IF EXISTS(SELECT * FROM @URLs WHERE DirectoryPath LIKE 's3://%/%') AND NOT (@Version >= 16 AND @EngineEdition IN(2, 3)) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Backup to S3-compatible storage is not supported in this version and edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#URL.', 16, 1) + END - IF EXISTS(SELECT * FROM @URLs WHERE Mirror = 1 AND NOT (DirectoryPath LIKE 'https://%/%' OR DirectoryPath LIKE 's3://%/%')) + IF EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 's3://%/%') + AND EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 'https://%/%') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 1 + VALUES('Striped backups across S3-compatible storage and Azure Blob Storage are not supported. See https://ola.hallengren.com/sql-server-backup.html#URL.', 16, 1) END - IF EXISTS (SELECT * FROM @URLs GROUP BY DirectoryPath HAVING COUNT(*) <> 1) + IF (SELECT COUNT(*) FROM @URLs WHERE Mirror = 0) > 64 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 2 + VALUES('The value for the parameter @URL is not supported. The maximum number of URLs is 64. See https://ola.hallengren.com/sql-server-backup.html#URL.', 16, 1) 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 + IF (SELECT COUNT(*) FROM @URLs WHERE Mirror = 0) > 32 AND EXISTS(SELECT * FROM @URLs WHERE Mirror = 1) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 3 + VALUES('The value for the parameter @URL is not supported. The maximum number of URLs when performing mirrored backups is 32. See https://ola.hallengren.com/sql-server-backup.html#URL.', 16, 1) + END + + IF @URL IS NOT NULL AND @Credential IS NOT NULL AND (SELECT COUNT(*) FROM @URLs WHERE Mirror = 0) > 1 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @URL is not supported. Backup striping to URL with page blobs is not supported. See https://ola.hallengren.com/sql-server-backup.html#URL.', 16, 1) + 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]) + VALUES('The value for the parameter @MirrorURL is not supported. The URL has to start with https:// (Azure Blob Storage) or s3:// (S3-compatible storage). See https://ola.hallengren.com/sql-server-backup.html#MirrorURL.', 16, 1) + END + + IF (EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 'https://%/%') AND EXISTS(SELECT * FROM @URLs WHERE Mirror = 1 AND DirectoryPath LIKE 's3://%/%')) + OR (EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 's3://%/%') AND EXISTS(SELECT * FROM @URLs WHERE Mirror = 1 AND DirectoryPath LIKE 'https://%/%')) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Mirrored backups across S3-compatible storage and Azure Blob Storage are not supported. See https://ola.hallengren.com/sql-server-backup.html#MirrorURL.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1110,7 +1260,7 @@ BEGIN --// Get default compression algorithm //-- ---------------------------------------------------------------------------------------------------- - IF @CompressionAlgorithm IS NULL AND @BackupSoftware IS NULL AND @Version >= 16 + IF @CompressionAlgorithm IS NULL AND @BackupSoftware IS NULL AND (@Version >= 16 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) 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' @@ -1121,7 +1271,7 @@ BEGIN --// Get default compression level //-- ---------------------------------------------------------------------------------------------------- - IF @CompressionLevel IS NULL AND @BackupSoftware IS NULL AND @Version >= 17 + IF @CompressionLevel IS NULL AND @BackupSoftware IS NULL AND (@Version >= 17 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) BEGIN SET @CompressionLevel = 'LOW' END @@ -1133,15 +1283,15 @@ BEGIN 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 + VALUES('The value for the parameter @BackupType is not supported. Supported values are FULL, DIFF and LOG. See https://ola.hallengren.com/sql-server-backup.html#BackupType.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF SERVERPROPERTY('EngineEdition') = 8 AND NOT (@BackupType = 'FULL' AND @CopyOnly = 'Y') + IF @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 + VALUES('Azure SQL Managed Instance only supports COPY_ONLY full backups. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1149,25 +1299,25 @@ BEGIN 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 + VALUES('The value for the parameter @Verify is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Verify.', 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 + VALUES('Verify is not supported for encrypted backups with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#Verify.', 16, 1) 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 + VALUES('Verify is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#Verify.', 16, 1) 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 + VALUES('Verify is not supported when backing up to NUL. See https://ola.hallengren.com/sql-server-backup.html#Verify.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1175,37 +1325,37 @@ BEGIN IF @CleanupTime < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @CleanupTime is not supported.', 16, 1 + VALUES('The value for the parameter @CleanupTime is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-backup.html#CleanupTime.', 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 + VALUES('The value for the parameter @CleanupTime is not supported. Cleanup is not supported when backing up to URL. See https://ola.hallengren.com/sql-server-backup.html#CleanupTime.', 16, 1) 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 + VALUES('The value for the parameter @CleanupTime is not supported. Cleanup is not supported when backing up to NUL. See https://ola.hallengren.com/sql-server-backup.html#CleanupTime.', 16, 1) 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))) + IF @CleanupTime IS NOT NULL AND ((@DirectoryStructure NOT LIKE '%{DatabaseName}%' OR @DirectoryStructure IS NULL) OR (@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 + VALUES('The value for the parameter @CleanupTime is not supported. Cleanup is not supported if the token {DatabaseName} is not part of the directory structure. See https://ola.hallengren.com/sql-server-backup.html#CleanupTime.', 16, 1) 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 + IF @CleanupTime IS NOT NULL AND ((@DirectoryStructure NOT LIKE '%{BackupType}%' OR @DirectoryStructure IS NULL) OR (@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 + VALUES('The value for the parameter @CleanupTime is not supported. Cleanup is not supported if the token {BackupType} is not part of the directory structure and the file extensions are not unique. See https://ola.hallengren.com/sql-server-backup.html#CleanupTime.', 16, 1) 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))) + IF @CleanupTime IS NOT NULL AND @CopyOnly = 'Y' AND ((@DirectoryStructure NOT LIKE '%{CopyOnly}%' OR @DirectoryStructure IS NULL) OR (@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 + VALUES('The value for the parameter @CleanupTime is not supported. Cleanup is not supported if the token {CopyOnly} is not part of the directory structure. See https://ola.hallengren.com/sql-server-backup.html#CleanupTime.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1213,7 +1363,7 @@ BEGIN 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 + VALUES('The value for the parameter @CleanupMode is not supported. Supported values are BEFORE_BACKUP and AFTER_BACKUP. See https://ola.hallengren.com/sql-server-backup.html#CleanupMode.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1221,27 +1371,38 @@ BEGIN 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 + VALUES('The value for the parameter @Compress is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Compress.', 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, -1785266663)))) + AND NOT (@EngineEdition IN (3, 8) OR @EditionID IN (-1534726760, -1785266663)) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Backup compression is not supported in this edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#Compress.', 16, 1) + END + + IF @Compress = 'N' AND @BackupSoftware = 'LITESPEED' AND (@CompressionLevelNumeric IS NULL OR @CompressionLevelNumeric >= 1) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Setting @Compress to ''N'' with LiteSpeed for SQL Server requires @CompressionLevelNumeric = 0. See https://ola.hallengren.com/sql-server-backup.html#Compress.', 16, 1) + END + + IF @Compress = 'N' AND @BackupSoftware = 'SQLBACKUP' AND (@CompressionLevelNumeric IS NULL OR @CompressionLevelNumeric >= 1) 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 + VALUES('Setting @Compress to ''N'' with Redgate SQL Backup Pro requires @CompressionLevelNumeric = 0. See https://ola.hallengren.com/sql-server-backup.html#Compress.', 16, 1) END - IF @Compress = 'N' AND @BackupSoftware IN ('LITESPEED','SQLBACKUP','SQLSAFE') AND (@CompressionLevelNumeric IS NULL OR @CompressionLevelNumeric >= 1) + IF @Compress = 'N' AND @BackupSoftware = '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 + VALUES('Setting @Compress to ''N'' with Idera SQL Safe Backup requires @CompressionLevelNumeric = 0. See https://ola.hallengren.com/sql-server-backup.html#Compress.', 16, 1) 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 + VALUES('Setting @Compress to ''Y'' cannot be combined with @CompressionLevelNumeric = 0. See https://ola.hallengren.com/sql-server-backup.html#Compress.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1249,31 +1410,31 @@ BEGIN 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 + VALUES('The value for the parameter @CompressionAlgorithm is not supported. Supported values are MS_XPRESS, QAT_DEFLATE and ZSTD. See https://ola.hallengren.com/sql-server-backup.html#CompressionAlgorithm.', 16, 1) END - IF @CompressionAlgorithm IS NOT NULL AND NOT (@Version >= 16) + IF @CompressionAlgorithm IS NOT NULL AND NOT (@Version >= 16 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) 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 + VALUES('The parameter @CompressionAlgorithm is not supported in this version and edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#CompressionAlgorithm.', 16, 1) END - IF @CompressionAlgorithm = 'QAT_DEFLATE' AND NOT (SERVERPROPERTY('EngineEdition') IN(2, 3)) + IF @CompressionAlgorithm = 'QAT_DEFLATE' AND NOT (@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 + VALUES('Setting @CompressionAlgorithm to QAT_DEFLATE is not supported in this edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#CompressionAlgorithm.', 16, 1) END - IF @CompressionAlgorithm = 'ZSTD' AND NOT (@Version >= 17) + IF @CompressionAlgorithm = 'ZSTD' AND NOT (@Version >= 17 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) 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 + VALUES('Setting @CompressionAlgorithm to ZSTD is not supported in this version and edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#CompressionAlgorithm.', 16, 1) 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 + VALUES('The parameter @CompressionAlgorithm is only supported with SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html#CompressionAlgorithm.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1281,19 +1442,19 @@ BEGIN 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 + VALUES('The parameter @CompressionLevel is only supported with SQL Server native backups. For third-party backup software, use the parameter @CompressionLevelNumeric. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevel.', 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 + VALUES('The value for the parameter @CompressionLevel is not supported. Supported values are LOW, MEDIUM and HIGH. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevel.', 16, 1) END - IF @CompressionLevel IS NOT NULL AND NOT (@Version >= 17) + IF @CompressionLevel IS NOT NULL AND NOT (@Version >= 17 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) 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 + VALUES('The parameter @CompressionLevel is not supported in this version and edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevel.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1301,7 +1462,13 @@ BEGIN 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 + VALUES('The value for the parameter @CopyOnly is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#CopyOnly.', 16, 1) + END + + IF @CopyOnly = 'Y' AND @BackupType = 'DIFF' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Differential copy-only backups are not supported. See https://ola.hallengren.com/sql-server-backup.html#CopyOnly.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1309,7 +1476,13 @@ BEGIN 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 + VALUES('The value for the parameter @ChangeBackupType is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#ChangeBackupType.', 16, 1) + END + + IF @ChangeBackupType = 'Y' AND NOT @BackupType IN ('DIFF', 'LOG') + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Setting @ChangeBackupType to ''Y'' is only supported with differential and log backups. See https://ola.hallengren.com/sql-server-backup.html#ChangeBackupType.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1317,37 +1490,37 @@ BEGIN 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 + VALUES('The value for the parameter @BackupSoftware is not supported. Supported values are LITESPEED, SQLBACKUP, SQLSAFE and DATA_DOMAIN_BOOST. See https://ola.hallengren.com/sql-server-backup.html#BackupSoftware.', 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 + VALUES('The value for the parameter @BackupSoftware is not supported. Only native backups are supported on Linux. See https://ola.hallengren.com/sql-server-backup.html#BackupSoftware.', 16, 1) 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 + VALUES('LiteSpeed for SQL Server is not installed. Download https://www.quest.com/products/litespeed-for-sql-server/.', 16, 1) 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 + VALUES('Redgate SQL Backup Pro is not installed. Download https://www.red-gate.com/products/sql-backup/.', 16, 1) 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 + VALUES('Idera SQL Safe Backup is not installed. Download https://www.idera.com/products/sql-safe-backup/.', 16, 1) 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 + VALUES('Data Domain Boost is not installed. Download https://www.dell.com/en-us/shop/storage-servers-and-networking-for-business/sf/powerprotect-data-domain.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1355,7 +1528,7 @@ BEGIN 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 + VALUES('The value for the parameter @Checksum is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Checksum.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1363,83 +1536,107 @@ BEGIN 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 + VALUES('The value for the parameter @BlockSize is not supported. Supported values are 512, 1024, 2048, 4096, 8192, 16384, 32768 and 65536. See https://ola.hallengren.com/sql-server-backup.html#BlockSize.', 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 + VALUES('The parameter @BlockSize is not supported with Redgate SQL Backup Pro. See https://ola.hallengren.com/sql-server-backup.html#BlockSize.', 16, 1) 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 + VALUES('The parameter @BlockSize is not supported with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#BlockSize.', 16, 1) 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 + VALUES('BLOCKSIZE is not supported when backing up to URL with page blobs. See https://learn.microsoft.com/en-us/sql/relational-databases/backup-restore/sql-server-backup-to-url.', 16, 1) 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 + VALUES('The parameter @BlockSize is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#BlockSize.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @BufferCount <= 0 OR @BufferCount > 2147483647 + IF @BufferCount <= 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @BufferCount is not supported.', 16, 1 + VALUES('The value for the parameter @BufferCount is not supported. The value has to be greater than 0. See https://ola.hallengren.com/sql-server-backup.html#BufferCount.', 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 + VALUES('The parameter @BufferCount is not supported with Redgate SQL Backup Pro. See https://ola.hallengren.com/sql-server-backup.html#BufferCount.', 16, 1) 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 + VALUES('The parameter @BufferCount is not supported with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#BufferCount.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @MaxTransferSize < 65536 OR @MaxTransferSize > 20971520 + IF @BackupSoftware IS NULL AND @URL IS NULL AND (@MaxTransferSize < 65536 OR @MaxTransferSize > 4194304) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MaxTransferSize is not supported.', 16, 1 + VALUES('The value for the parameter @MaxTransferSize is not supported. The value has to be between 65536 and 4194304 when performing SQL Server native backups to disk. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) END - IF @MaxTransferSize > 1048576 AND @BackupSoftware = 'SQLBACKUP' + IF @BackupSoftware IS NULL AND @URL IS NOT NULL AND @Credential IS NULL AND NOT EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 's3://%/%') AND (@Version >= 16 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) AND (@MaxTransferSize < 65536 OR @MaxTransferSize > 20971520) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MaxTransferSize is not supported.', 16, 2 + VALUES('The value for the parameter @MaxTransferSize is not supported. The value has to be between 65536 and 20971520 when backing up to URL with block blobs on this version of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) END - IF @MaxTransferSize IS NOT NULL AND @BackupSoftware = 'SQLSAFE' + IF @BackupSoftware IS NULL AND @URL IS NOT NULL AND @Credential IS NULL AND NOT EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 's3://%/%') AND NOT (@Version >= 16 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) AND (@MaxTransferSize < 65536 OR @MaxTransferSize > 4194304) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MaxTransferSize is not supported.', 16, 3 + VALUES('The value for the parameter @MaxTransferSize is not supported. The value has to be between 65536 and 4194304 when backing up to URL with block blobs on this version of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) END - IF @MaxTransferSize IS NOT NULL AND @URL IS NOT NULL AND @Credential IS NOT NULL + IF @BackupSoftware IS NULL AND @URL IS NOT NULL AND @Credential IS NULL AND EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 's3://%/%') AND (@MaxTransferSize < 5242880 OR @MaxTransferSize > 20971520) 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 + VALUES('The value for the parameter @MaxTransferSize is not supported. The value has to be between 5242880 and 20971520 when backing up to S3-compatible storage. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) END - IF @MaxTransferSize IS NOT NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + IF @URL IS NOT NULL AND @Credential IS NOT NULL AND @MaxTransferSize IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MaxTransferSize is not supported.', 16, 5 + VALUES('MAXTRANSFERSIZE is not supported when backing up to URL with page blobs. See https://learn.microsoft.com/en-us/sql/relational-databases/backup-restore/sql-server-backup-to-url.', 16, 1) + END + + IF @BackupSoftware = 'SQLBACKUP' AND (@MaxTransferSize < 65536 OR @MaxTransferSize > 1048576) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MaxTransferSize is not supported. The value has to be between 65536 and 1048576 when backing up using Redgate SQL Backup Pro. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) + END + + IF @BackupSoftware = 'LITESPEED' AND (@MaxTransferSize < 65536 OR @MaxTransferSize > 4194304) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MaxTransferSize is not supported. The value has to be between 65536 and 4194304 when backing up using LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) + END + + IF @BackupSoftware = 'SQLSAFE' AND @MaxTransferSize IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @MaxTransferSize is not supported with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) + END + + IF @BackupSoftware = 'DATA_DOMAIN_BOOST' AND @MaxTransferSize IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @MaxTransferSize is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1447,87 +1644,93 @@ BEGIN 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The value has to be between 1 and 64. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The maximum number of files with Redgate SQL Backup Pro is 32. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The number of files has to be greater than or equal to the number of directories. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The number of files has to be evenly divisible by the number of directories. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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 + VALUES('Backup striping to URL with page blobs is not supported. See https://learn.microsoft.com/en-us/sql/relational-databases/backup-restore/sql-server-backup-to-url.', 16, 1) END - IF @NumberOfFiles > 1 AND @BackupSoftware IN('SQLBACKUP','SQLSAFE') AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) + IF @NumberOfFiles > 1 AND @BackupSoftware = 'SQLBACKUP' 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. Mirrored backups with multiple files are not supported with Redgate SQL Backup Pro. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) + END + + IF @NumberOfFiles > 1 AND @BackupSoftware = 'SQLSAFE' AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @NumberOfFiles is not supported. Mirrored backups with multiple files are not supported with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The maximum number of files with Data Domain Boost is 32. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The number of files has to be greater than or equal to the number of URLs. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The number of files has to be evenly divisible by the number of URLs. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) END - IF @NumberOfFiles > 32 AND @URL LIKE 's3%' AND @MirrorURL LIKE 's3%' + IF @BackupSoftware IS NULL AND @NumberOfFiles > 32 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. The maximum number of files when performing mirrored backups to S3 storage is 32.', 16, 10 + VALUES('The value for the parameter @NumberOfFiles is not supported. The maximum number of files when performing mirrored SQL Server native backups to disk is 32. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) END - ---------------------------------------------------------------------------------------------------- - - IF @MinBackupSizeForMultipleFiles <= 0 + IF @NumberOfFiles > 32 AND EXISTS(SELECT * FROM @URLs WHERE Mirror = 1) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MinBackupSizeForMultipleFiles is not supported.', 16, 1 + VALUES('The value for the parameter @NumberOfFiles is not supported. The maximum number of files when performing mirrored backups to URL is 32. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) END - IF @MinBackupSizeForMultipleFiles IS NOT NULL AND @NumberOfFiles IS NULL + IF @BackupSoftware = 'LITESPEED' AND @NumberOfFiles > 32 AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The maximum number of files when performing mirrored backups with LiteSpeed for SQL Server is 32. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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') + ---------------------------------------------------------------------------------------------------- + + IF @MinBackupSizeForMultipleFiles <= 0 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 + VALUES('The value for the parameter @MinBackupSizeForMultipleFiles is not supported. The value has to be greater than 0. See https://ola.hallengren.com/sql-server-backup.html#MinBackupSizeForMultipleFiles.', 16, 1) 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') + 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. The column sys.dm_db_log_stats.log_since_last_log_backup_mb is not available in this version of SQL Server.', 16, 4 + VALUES('The parameter @MinBackupSizeForMultipleFiles can only be used together with @NumberOfFiles. See https://ola.hallengren.com/sql-server-backup.html#MinBackupSizeForMultipleFiles.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1535,25 +1738,13 @@ BEGIN IF @MaxFileSize <= 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MaxFileSize is not supported.', 16, 1 + VALUES('The value for the parameter @MaxFileSize is not supported. The value has to be greater than 0. See https://ola.hallengren.com/sql-server-backup.html#MaxFileSize.', 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 + VALUES('The parameters @MaxFileSize and @NumberOfFiles cannot be used together. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1561,31 +1752,31 @@ BEGIN 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 + VALUES('The parameter @CompressionLevelNumeric is only supported with third-party backup software. For SQL Server native backups, use the parameter @CompressionLevel. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevelNumeric.', 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 + VALUES('The value for the parameter @CompressionLevelNumeric is not supported. With LiteSpeed for SQL Server, the value has to be between 0 and 8. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevelNumeric.', 16, 1) 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 + VALUES('The value for the parameter @CompressionLevelNumeric is not supported. With Redgate SQL Backup Pro, the value has to be between 0 and 4. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevelNumeric.', 16, 1) 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 + VALUES('The value for the parameter @CompressionLevelNumeric is not supported. With Idera SQL Safe Backup, the value has to be between 1 and 4. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevelNumeric.', 16, 1) 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 + VALUES('The parameter @CompressionLevelNumeric is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevelNumeric.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1593,19 +1784,25 @@ BEGIN IF LEN(@Description) > 255 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Description is not supported.', 16, 1 + VALUES('The value for the parameter @Description is not supported. The maximum length is 255 characters. See https://ola.hallengren.com/sql-server-backup.html#Description.', 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 + VALUES('The value for the parameter @Description is not supported. The maximum length with LiteSpeed for SQL Server is 128 characters. See https://ola.hallengren.com/sql-server-backup.html#Description.', 16, 1) 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 + VALUES('The value for the parameter @Description is not supported. The maximum length with Data Domain Boost is 254 characters. See https://ola.hallengren.com/sql-server-backup.html#Description.', 16, 1) + END + + IF @BackupSoftware = 'DATA_DOMAIN_BOOST' AND @Description LIKE '%"%' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Description is not supported. Double quotes (") are not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#Description.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1613,7 +1810,13 @@ BEGIN IF LEN(@BackupSetName) > 128 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @BackupSetName is not supported.', 16, 1 + VALUES('The value for the parameter @BackupSetName is not supported. The maximum length is 128 characters. See https://ola.hallengren.com/sql-server-backup.html#BackupSetName.', 16, 1) + END + + IF @BackupSoftware = 'DATA_DOMAIN_BOOST' AND @BackupSetName LIKE '%"%' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @BackupSetName is not supported. Double quotes (") are not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#BackupSetName.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1621,25 +1824,25 @@ BEGIN 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 + VALUES('The parameter @Threads is only supported with LiteSpeed for SQL Server, Redgate SQL Backup Pro and Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#Threads.', 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 + VALUES('The value for the parameter @Threads is not supported. With LiteSpeed for SQL Server, the value has to be between 1 and 32. See https://ola.hallengren.com/sql-server-backup.html#Threads.', 16, 1) 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 + VALUES('The value for the parameter @Threads is not supported. With Redgate SQL Backup Pro, the value has to be between 2 and 32. See https://ola.hallengren.com/sql-server-backup.html#Threads.', 16, 1) 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 + VALUES('The value for the parameter @Threads is not supported. With Idera SQL Safe Backup, the value has to be between 1 and 64. See https://ola.hallengren.com/sql-server-backup.html#Threads.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1647,13 +1850,13 @@ BEGIN 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 + VALUES('The value for the parameter @Throttle is not supported. The value has to be between 1 and 100. See https://ola.hallengren.com/sql-server-backup.html#Throttle.', 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 + VALUES('The parameter @Throttle is only supported with LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#Throttle.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1661,19 +1864,19 @@ BEGIN 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 + VALUES('The value for the parameter @Encrypt is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Encrypt.', 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, -1785266663))) + IF @Encrypt = 'Y' AND @BackupSoftware IS NULL AND NOT (@EngineEdition IN(3, 8) OR @EditionID IN(-1534726760, -1785266663)) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Encrypt is not supported.', 16, 2 + VALUES('Backup encryption is not supported in this edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#Encrypt.', 16, 1) 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 + VALUES('The value for the parameter @Encrypt is not supported. Encrypted backups are not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#Encrypt.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1681,32 +1884,31 @@ BEGIN 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 + VALUES('The value for the parameter @EncryptionAlgorithm is not supported. Supported values for SQL Server native backups are AES_128, AES_192, AES_256 and TRIPLE_DES_3KEY. See https://ola.hallengren.com/sql-server-backup.html#EncryptionAlgorithm.', 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 + VALUES('The value for the parameter @EncryptionAlgorithm is not supported. Supported values with LiteSpeed for SQL Server are RC2_40, RC2_56, RC2_112, RC2_128, TRIPLE_DES_3KEY, RC4_128, AES_128, AES_192 and AES_256. See https://ola.hallengren.com/sql-server-backup.html#EncryptionAlgorithm.', 16, 1) 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 + VALUES('The value for the parameter @EncryptionAlgorithm is not supported. Supported values with Redgate SQL Backup Pro are AES_128 and AES_256. See https://ola.hallengren.com/sql-server-backup.html#EncryptionAlgorithm.', 16, 1) 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 + VALUES('The value for the parameter @EncryptionAlgorithm is not supported. Supported values with Idera SQL Safe Backup are AES_128 and AES_256. See https://ola.hallengren.com/sql-server-backup.html#EncryptionAlgorithm.', 16, 1) 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 + VALUES('The parameter @EncryptionAlgorithm is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#EncryptionAlgorithm.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1714,25 +1916,25 @@ BEGIN 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 + VALUES('The parameter @ServerCertificate can only be used together with @Encrypt = ''Y'' and SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html#ServerCertificate.', 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 + VALUES('You need to specify one of the parameters @ServerCertificate and @ServerAsymmetricKey when performing encrypted SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) 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 + VALUES('You can only specify one of the parameters @ServerCertificate and @ServerAsymmetricKey. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) 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 + VALUES('The value for the parameter @ServerCertificate is not supported. The certificate does not exist in the master database. See https://ola.hallengren.com/sql-server-backup.html#ServerCertificate.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1740,51 +1942,51 @@ BEGIN 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 + VALUES('The parameter @ServerAsymmetricKey can only be used together with @Encrypt = ''Y'' and SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html#ServerAsymmetricKey.', 16, 1) END - IF @BackupSoftware IS NULL AND @Encrypt = 'Y' AND @ServerAsymmetricKey IS NULL AND @ServerCertificate IS NULL + 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, 2 + VALUES('The value for the parameter @ServerAsymmetricKey is not supported. The asymmetric key does not exist in the master database. See https://ola.hallengren.com/sql-server-backup.html#ServerAsymmetricKey.', 16, 1) END - IF @BackupSoftware IS NULL AND @Encrypt = 'Y' AND @ServerAsymmetricKey IS NOT NULL AND @ServerCertificate IS NOT NULL + ---------------------------------------------------------------------------------------------------- + + IF @EncryptionKey IS NOT NULL AND @BackupSoftware IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ServerAsymmetricKey is not supported.', 16, 3 + VALUES('The parameter @EncryptionKey is only supported with third-party backup software. For SQL Server native backups, use @ServerCertificate or @ServerAsymmetricKey. See https://ola.hallengren.com/sql-server-backup.html#EncryptionKey.', 16, 1) END - IF @ServerAsymmetricKey IS NOT NULL AND NOT EXISTS(SELECT * FROM master.sys.asymmetric_keys WHERE name = @ServerAsymmetricKey) + IF @EncryptionKey IS NOT NULL AND @Encrypt = 'N' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ServerAsymmetricKey is not supported.', 16, 4 + VALUES('The parameter @EncryptionKey can only be used together with @Encrypt = ''Y''. See https://ola.hallengren.com/sql-server-backup.html#EncryptionKey.', 16, 1) END - ---------------------------------------------------------------------------------------------------- - - IF @EncryptionKey IS NOT NULL AND @BackupSoftware IS NULL + IF @EncryptionKey IS NULL AND @Encrypt = 'Y' AND @BackupSoftware = 'LITESPEED' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @EncryptionKey is not supported.', 16, 1 + VALUES('You need to specify @EncryptionKey when performing encrypted backups with LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#EncryptionKey.', 16, 1) END - IF @EncryptionKey IS NOT NULL AND @Encrypt = 'N' + IF @EncryptionKey IS NULL AND @Encrypt = 'Y' AND @BackupSoftware = 'SQLBACKUP' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @EncryptionKey is not supported.', 16, 2 + VALUES('You need to specify @EncryptionKey when performing encrypted backups with Redgate SQL Backup Pro. See https://ola.hallengren.com/sql-server-backup.html#EncryptionKey.', 16, 1) END - IF @EncryptionKey IS NULL AND @Encrypt = 'Y' AND @BackupSoftware IN('LITESPEED','SQLBACKUP','SQLSAFE') + IF @EncryptionKey IS NULL AND @Encrypt = 'Y' AND @BackupSoftware = 'SQLSAFE' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @EncryptionKey is not supported.', 16, 3 + VALUES('You need to specify @EncryptionKey when performing encrypted backups with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#EncryptionKey.', 16, 1) 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 + VALUES('The parameter @EncryptionKey is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#EncryptionKey.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1792,13 +1994,13 @@ BEGIN 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 + VALUES('The value for the parameter @ReadWriteFileGroups is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#ReadWriteFileGroups.', 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 + VALUES('Setting @ReadWriteFileGroups to ''Y'' is not supported for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#ReadWriteFileGroups.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1806,7 +2008,7 @@ BEGIN 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 + VALUES('The value for the parameter @OverrideBackupPreference is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#OverrideBackupPreference.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1814,19 +2016,19 @@ BEGIN 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 + VALUES('The value for the parameter @NoRecovery is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#NoRecovery.', 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 + VALUES('Setting @NoRecovery to ''Y'' is only supported for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#NoRecovery.', 16, 1) 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 + VALUES('Setting @NoRecovery to ''Y'' is not supported with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#NoRecovery.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1834,51 +2036,39 @@ BEGIN 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 + VALUES('The parameters @URL and @Directory cannot be used together. See https://ola.hallengren.com/sql-server-backup.html.', 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 + VALUES('The parameters @URL and @MirrorDirectory cannot be used together. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) 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 + VALUES('Backup to URL is only supported with SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html#URL.', 16, 1) 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 + VALUES('The parameter @Credential can only be used together with @URL. See https://ola.hallengren.com/sql-server-backup.html#Credential.', 16, 1) 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 + VALUES('When backing up to URL, you need to specify @Credential or create a credential with the identity SHARED ACCESS SIGNATURE, MANAGED IDENTITY or S3 ACCESS KEY. See https://ola.hallengren.com/sql-server-backup.html#Credential.', 16, 1) 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 + VALUES('The value for the parameter @Credential is not supported. The credential does not exist. See https://ola.hallengren.com/sql-server-backup.html#Credential.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1886,13 +2076,13 @@ BEGIN IF @MirrorCleanupTime < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MirrorCleanupTime is not supported.', 16, 1 + VALUES('The value for the parameter @MirrorCleanupTime is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-backup.html#MirrorCleanupTime.', 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 + VALUES('The parameter @MirrorCleanupTime can only be used together with @MirrorDirectory. See https://ola.hallengren.com/sql-server-backup.html#MirrorCleanupTime.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1900,7 +2090,7 @@ BEGIN 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 + VALUES('The value for the parameter @MirrorCleanupMode is not supported. Supported values are BEFORE_BACKUP and AFTER_BACKUP. See https://ola.hallengren.com/sql-server-backup.html#MirrorCleanupMode.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1908,31 +2098,37 @@ BEGIN 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 + VALUES('The parameters @MirrorURL and @Directory cannot be used together. See https://ola.hallengren.com/sql-server-backup.html.', 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 + VALUES('The parameters @MirrorURL and @MirrorDirectory cannot be used together. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END - IF @MirrorURL IS NOT NULL AND @Version < 11.03339 + 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, 3 + VALUES('Mirrored backup to URL is only supported with SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html#MirrorURL.', 16, 1) END - IF @MirrorURL IS NOT NULL AND @BackupSoftware IS NOT NULL + 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, 4 + VALUES('The parameter @MirrorURL can only be used together with @URL. See https://ola.hallengren.com/sql-server-backup.html#MirrorURL.', 16, 1) END - IF @MirrorURL IS NOT NULL AND @URL IS NULL + IF @MirrorURL IS NOT NULL AND @Credential IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MirrorURL is not supported. Mirrored backup to URL with page blobs is not supported. See https://ola.hallengren.com/sql-server-backup.html#MirrorURL.', 16, 1) + END + + IF @MirrorURL IS NOT NULL AND @BackupSoftware IS NULL AND @EngineEdition NOT IN (3, 8) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 5 + VALUES('The value for the parameter @MirrorURL is not supported. Mirrored backup to URL is not supported in this edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#MirrorURL.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1940,7 +2136,7 @@ BEGIN 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 + VALUES('The value for the parameter @Updateability is not supported. Supported values are ALL, READ_ONLY and READ_WRITE. See https://ola.hallengren.com/sql-server-backup.html#Updateability.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1948,39 +2144,33 @@ BEGIN 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 + VALUES('The value for the parameter @AdaptiveCompression is not supported. Supported values are SIZE and SPEED. See https://ola.hallengren.com/sql-server-backup.html#AdaptiveCompression.', 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 + VALUES('The parameter @AdaptiveCompression is only supported with LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#AdaptiveCompression.', 16, 1) 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 + IF @MinModificationLevel <= 0 OR @MinModificationLevel > 100 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ModificationLevel is not supported.', 16, 2 + VALUES('The value for the parameter @MinModificationLevel is not supported. The value has to be greater than 0 and less than or equal to 100. See https://ola.hallengren.com/sql-server-backup.html#MinModificationLevel.', 16, 1) END - IF @ModificationLevel IS NOT NULL AND @ChangeBackupType = 'N' + IF @MinModificationLevel 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 + VALUES('The parameter @MinModificationLevel can only be used together with @ChangeBackupType = ''Y''. See https://ola.hallengren.com/sql-server-backup.html#MinModificationLevel.', 16, 1) END - IF @ModificationLevel IS NOT NULL AND @BackupType <> 'DIFF' + IF @MinModificationLevel IS NOT NULL AND @BackupType NOT IN('DIFF','LOG') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The parameter @ModificationLevel can only be used for differential backups.', 16, 4 + VALUES('The parameter @MinModificationLevel can only be used for differential and transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#MinModificationLevel.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1988,49 +2178,37 @@ BEGIN IF @MinDatabaseSizeForDifferentialBackup <= 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MinDatabaseSizeForDifferentialBackup is not supported.', 16, 1 + VALUES('The value for the parameter @MinDatabaseSizeForDifferentialBackup is not supported. The value has to be greater than 0. See https://ola.hallengren.com/sql-server-backup.html#MinDatabaseSizeForDifferentialBackup.', 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 + VALUES('The parameter @MinDatabaseSizeForDifferentialBackup can only be used for differential backups. See https://ola.hallengren.com/sql-server-backup.html#MinDatabaseSizeForDifferentialBackup.', 16, 1) 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' + IF @MinLogSizeSinceLastLogBackup 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 + VALUES('The parameter @MinLogSizeSinceLastLogBackup can only be used for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#MinLogSizeSinceLastLogBackup.', 16, 1) 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') + IF @MinTimeSinceLastLogBackup IS NOT NULL AND @BackupType <> 'LOG' 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 + VALUES('The parameter @MinTimeSinceLastLogBackup can only be used for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#MinTimeSinceLastLogBackup.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF (@TimeSinceLastLogBackup IS NOT NULL AND @LogSizeSinceLastLogBackup IS NULL) OR (@TimeSinceLastLogBackup IS NULL AND @LogSizeSinceLastLogBackup IS NOT NULL) + IF (@MinTimeSinceLastLogBackup IS NOT NULL AND @MinLogSizeSinceLastLogBackup IS NULL) OR (@MinTimeSinceLastLogBackup IS NULL AND @MinLogSizeSinceLastLogBackup IS NOT NULL) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The parameters @TimeSinceLastLogBackup and @LogSizeSinceLastLogBackup can only be used together.', 16, 1 + VALUES('The parameters @MinTimeSinceLastLogBackup and @MinLogSizeSinceLastLogBackup can only be used together. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2038,13 +2216,19 @@ BEGIN 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 + VALUES('The parameter @DataDomainBoostHost can only be used together with @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostHost.', 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 + VALUES('You need to specify @DataDomainBoostHost when @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostHost.', 16, 1) + END + + IF @DataDomainBoostHost LIKE '%"%' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DataDomainBoostHost is not supported. Double quotes (") are not supported. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostHost.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2052,13 +2236,19 @@ BEGIN 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 + VALUES('The parameter @DataDomainBoostUser can only be used together with @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostUser.', 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 + VALUES('You need to specify @DataDomainBoostUser when @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostUser.', 16, 1) + END + + IF @DataDomainBoostUser LIKE '%"%' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DataDomainBoostUser is not supported. Double quotes (") are not supported. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostUser.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2066,13 +2256,19 @@ BEGIN 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 + VALUES('The parameter @DataDomainBoostDevicePath can only be used together with @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostDevicePath.', 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 + VALUES('You need to specify @DataDomainBoostDevicePath when @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostDevicePath.', 16, 1) + END + + IF @DataDomainBoostDevicePath LIKE '%"%' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DataDomainBoostDevicePath is not supported. Double quotes (") are not supported. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostDevicePath.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2080,7 +2276,27 @@ BEGIN 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 + VALUES('The parameter @DataDomainBoostLockboxPath can only be used together with @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostLockboxPath.', 16, 1) + END + + IF @DataDomainBoostLockboxPath LIKE '%"%' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DataDomainBoostLockboxPath is not supported. Double quotes (") are not supported. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostLockboxPath.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @DataDomainBoostNoOutputTable NOT IN('Y','N') OR @DataDomainBoostNoOutputTable IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DataDomainBoostNoOutputTable is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostNoOutputTable.', 16, 1) + END + + IF @DataDomainBoostNoOutputTable = 'Y' AND (@BackupSoftware <> 'DATA_DOMAIN_BOOST' OR @BackupSoftware IS NULL) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @DataDomainBoostNoOutputTable can only be used together with @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostNoOutputTable.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2088,7 +2304,7 @@ BEGIN IF @DirectoryStructure = '' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DirectoryStructure is not supported.', 16, 1 + VALUES('The value for the parameter @DirectoryStructure is not supported. The value cannot be an empty string. See https://ola.hallengren.com/sql-server-backup.html#DirectoryStructure.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2096,7 +2312,7 @@ BEGIN IF @AvailabilityGroupDirectoryStructure = '' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroupDirectoryStructure is not supported.', 16, 1 + VALUES('The value for the parameter @AvailabilityGroupDirectoryStructure is not supported. The value cannot be an empty string. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupDirectoryStructure.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2104,7 +2320,7 @@ BEGIN 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 + VALUES('The value for the parameter @DirectoryStructureCase is not supported. Supported values are LOWER and UPPER. See https://ola.hallengren.com/sql-server-backup.html#DirectoryStructureCase.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2112,81 +2328,81 @@ BEGIN 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 + VALUES('The value for the parameter @FileName is not supported. The value cannot be NULL or empty. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 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 + VALUES('The value for the parameter @FileName is not supported. The file name has to end with .{FileExtension}. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 16, 1) 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 + VALUES('The value for the parameter @FileName is not supported. The token {FileNumber} is required when @NumberOfFiles is greater than 1. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 16, 1) END IF @FileName LIKE '%{DirectorySeparator}%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @FileName is not supported.', 16, 4 + VALUES('The value for the parameter @FileName is not supported. The token {DirectorySeparator} cannot be used in the file name. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 16, 1) END IF @FileName LIKE '%/%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @FileName is not supported.', 16, 5 + VALUES('The value for the parameter @FileName is not supported. The character / cannot be used in the file name. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 16, 1) END IF @FileName LIKE '%\%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @FileName is not supported.', 16, 6 + VALUES('The value for the parameter @FileName is not supported. The character \ cannot be used in the file name. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF (SERVERPROPERTY('IsHadrEnabled') = 1 AND @AvailabilityGroupFileName IS NULL) + IF (@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 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The value cannot be NULL when the server is part of an availability group. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) END IF @AvailabilityGroupFileName = '' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 2 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The value cannot be an empty string. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) 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 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The file name has to end with .{FileExtension}. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) 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 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The token {FileNumber} is required when @NumberOfFiles is greater than 1. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) END IF @AvailabilityGroupFileName LIKE '%{DirectorySeparator}%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 5 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The token {DirectorySeparator} cannot be used in the file name. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) END IF @AvailabilityGroupFileName LIKE '%/%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 6 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The character / cannot be used in the file name. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) END IF @AvailabilityGroupFileName LIKE '%\%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 7 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The character \ cannot be used in the file name. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2194,15 +2410,15 @@ BEGIN 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 + VALUES('The value for the parameter @FileNameCase is not supported. Supported values are LOWER and UPPER. See https://ola.hallengren.com/sql-server-backup.html#FileNameCase.', 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 '%}%') + 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(@DirectoryStructure,'{DirectorySeparator}',''),'{ServerName}',''),'{InstanceName}',''),'{ServiceName}',''),'{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 + VALUES('The parameter @DirectoryStructure contains one or more tokens that are not supported. See https://ola.hallengren.com/sql-server-backup.html#DirectoryStructure.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2210,23 +2426,23 @@ BEGIN 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 + VALUES('The parameter @AvailabilityGroupDirectoryStructure contains one or more tokens that are not supported. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupDirectoryStructure.', 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 '%}%') + 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(@FileName,'{ServerName}',''),'{InstanceName}',''),'{ServiceName}',''),'{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 + VALUES('The parameter @FileName contains one or more tokens that are not supported. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 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 '%}%') + 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(@AvailabilityGroupFileName,'{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 + VALUES('The parameter @AvailabilityGroupFileName contains one or more tokens that are not supported. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2234,7 +2450,7 @@ BEGIN 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 + VALUES('The value for the parameter @TokenTimezone is not supported. Supported values are LOCAL and UTC. See https://ola.hallengren.com/sql-server-backup.html#TokenTimezone.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2242,7 +2458,7 @@ BEGIN IF @FileExtensionFull LIKE '%.%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @FileExtensionFull is not supported.', 16, 1 + VALUES('The value for the parameter @FileExtensionFull is not supported. Specify the file extension without a leading period. See https://ola.hallengren.com/sql-server-backup.html#FileExtensionFull.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2250,7 +2466,7 @@ BEGIN IF @FileExtensionDiff LIKE '%.%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @FileExtensionDiff is not supported.', 16, 1 + VALUES('The value for the parameter @FileExtensionDiff is not supported. Specify the file extension without a leading period. See https://ola.hallengren.com/sql-server-backup.html#FileExtensionDiff.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2258,7 +2474,7 @@ BEGIN IF @FileExtensionLog LIKE '%.%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @FileExtensionLog is not supported.', 16, 1 + VALUES('The value for the parameter @FileExtensionLog is not supported. Specify the file extension without a leading period. See https://ola.hallengren.com/sql-server-backup.html#FileExtensionLog.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2266,19 +2482,25 @@ BEGIN 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 + VALUES('The value for the parameter @Init is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Init.', 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 + VALUES('Setting @Init to ''Y'' is not supported for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#Init.', 16, 1) 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 + VALUES('Setting @Init to ''Y'' is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#Init.', 16, 1) + END + + IF @Init = 'Y' AND @URL IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Setting @Init to ''Y'' is not supported with backup to URL. See https://ola.hallengren.com/sql-server-backup.html#Init.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2286,19 +2508,19 @@ BEGIN 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 + VALUES('The value for the parameter @Format is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Format.', 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 + VALUES('Setting @Format to ''Y'' is not supported for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#Format.', 16, 1) 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 + VALUES('Setting @Format to ''Y'' is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#Format.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2306,33 +2528,41 @@ BEGIN 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 + VALUES('The value for the parameter @ObjectLevelRecoveryMap is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#ObjectLevelRecoveryMap.', 16, 1) END - IF @ObjectLevelRecoveryMap = 'Y' AND @BackupSoftware IS NULL + IF @ObjectLevelRecoveryMap = 'Y' AND (@BackupSoftware NOT IN('LITESPEED') OR @BackupSoftware IS NULL) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ObjectLevelRecovery is not supported.', 16, 2 + VALUES('Setting @ObjectLevelRecoveryMap to ''Y'' is only supported with LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#ObjectLevelRecoveryMap.', 16, 1) END - IF @ObjectLevelRecoveryMap = 'Y' AND @BackupSoftware <> 'LITESPEED' + IF @ObjectLevelRecoveryMap = 'Y' AND @BackupType = 'LOG' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ObjectLevelRecovery is not supported.', 16, 3 + VALUES('Setting @ObjectLevelRecoveryMap to ''Y'' is not supported for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#ObjectLevelRecoveryMap.', 16, 1) END - IF @ObjectLevelRecoveryMap = 'Y' AND @BackupType = 'LOG' + ---------------------------------------------------------------------------------------------------- + + IF @ExcludeLogShippedFromLogBackup NOT IN('Y','N') OR @ExcludeLogShippedFromLogBackup IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ObjectLevelRecovery is not supported.', 16, 4 + VALUES('The value for the parameter @ExcludeLogShippedFromLogBackup is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#ExcludeLogShippedFromLogBackup.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @ExcludeLogShippedFromLogBackup NOT IN('Y','N') OR @ExcludeLogShippedFromLogBackup IS NULL + IF @ExcludeSeedingFromLogBackup NOT IN('Y','N') OR @ExcludeSeedingFromLogBackup IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @ExcludeSeedingFromLogBackup is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#ExcludeSeedingFromLogBackup.', 16, 1) + END + + IF @ExcludeSeedingFromLogBackup = 'Y' AND @BackupType <> 'LOG' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ExcludeLogShippedFromLogBackup is not supported.', 16, 1 + VALUES('The parameter @ExcludeSeedingFromLogBackup can only be used for log backups. See https://ola.hallengren.com/sql-server-backup.html#ExcludeSeedingFromLogBackup.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2340,15 +2570,15 @@ BEGIN 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 + VALUES('The value for the parameter @DirectoryCheck is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#DirectoryCheck.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @BackupOptions IS NOT NULL AND @URL IS NULL + IF @BackupOptions IS NOT NULL AND NOT EXISTS(SELECT * FROM @URLs WHERE DirectoryPath LIKE 's3://%/%') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @BackupOptions is not supported.', 16, 1 + VALUES('The parameter @BackupOptions can only be used when backing up to S3-compatible storage. See https://ola.hallengren.com/sql-server-backup.html#BackupOptions.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2356,7 +2586,7 @@ BEGIN 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 + VALUES('The value for the parameter @Stats is not supported. The value has to be between 1 and 100. See https://ola.hallengren.com/sql-server-backup.html#Stats.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2364,7 +2594,7 @@ BEGIN 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 + VALUES('The parameter @ExpireDate is only supported with SQL Server native backups and LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#ExpireDate.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2372,41 +2602,49 @@ BEGIN IF @RetainDays < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @RetainDays is not supported.', 16, 1 + VALUES('The value for the parameter @RetainDays is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-backup.html#RetainDays.', 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 + VALUES('The parameter @RetainDays is only supported with SQL Server native backups and LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#RetainDays.', 16, 1) + END + + IF @ExpireDate IS NOT NULL AND @RetainDays IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameters @ExpireDate and @RetainDays cannot be used together. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) > 1 + IF @AllowNonCopyOnlyBackupsOnForwarder NOT IN('Y','N') OR @AllowNonCopyOnlyBackupsOnForwarder IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @StringDelimiter is not supported.', 16, 1 + VALUES('The value for the parameter @AllowNonCopyOnlyBackupsOnForwarder is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#AllowNonCopyOnlyBackupsOnForwarder.', 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') + IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) <> 1 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DatabaseOrder is not supported.', 16, 1 + VALUES('The value for the parameter @StringDelimiter is not supported. The value has to be exactly one character. See https://ola.hallengren.com/sql-server-backup.html#StringDelimiter.', 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') + ---------------------------------------------------------------------------------------------------- + + 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. The column sys.dm_db_log_stats.log_since_last_log_backup_mb is not available in this version of SQL Server.', 16, 2 + VALUES('The value for the parameter @DatabaseOrder is not supported. Supported values are DATABASE_NAME_ASC, DATABASE_NAME_DESC, DATABASE_SIZE_ASC, DATABASE_SIZE_DESC, LOG_SIZE_SINCE_LAST_LOG_BACKUP_ASC and LOG_SIZE_SINCE_LAST_LOG_BACKUP_DESC. See https://ola.hallengren.com/sql-server-backup.html#DatabaseOrder.', 16, 1) END - IF @DatabaseOrder IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 5 + IF @DatabaseOrder IS NOT NULL AND @EngineEdition = 5 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DatabaseOrder is not supported.', 16, 3 + VALUES('The parameter @DatabaseOrder is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-backup.html#DatabaseOrder.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2414,13 +2652,13 @@ BEGIN 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 + VALUES('The value for the parameter @DatabasesInParallel is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#DatabasesInParallel.', 16, 1) END - IF @DatabasesInParallel = 'Y' AND SERVERPROPERTY('EngineEdition') = 5 + IF @DatabasesInParallel = 'Y' AND @EngineEdition = 5 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DatabasesInParallel is not supported.', 16, 2 + VALUES('The parameter @DatabasesInParallel is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-backup.html#DatabasesInParallel.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2428,7 +2666,7 @@ BEGIN 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 + VALUES('The value for the parameter @LogToTable is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#LogToTable.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2436,22 +2674,15 @@ BEGIN 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 + VALUES('The value for the parameter @Execute is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Execute.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Check that selected databases and availability groups exist //-- ---------------------------------------------------------------------------------------------------- - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedDatabases WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) @@ -2459,10 +2690,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-backup.html#Databases.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(AvailabilityGroupName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(AvailabilityGroupName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY AvailabilityGroupName ASC) FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName NOT LIKE '%[%]%' AND AvailabilityGroupName NOT IN (SELECT AvailabilityGroupName FROM @tmpAvailabilityGroups) @@ -2470,24 +2702,24 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following availability groups do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following availability groups do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroups.', 10, 1) END ---------------------------------------------------------------------------------------------------- --// Check @@SERVERNAME //-- ---------------------------------------------------------------------------------------------------- - IF UPPER(@@SERVERNAME) <> UPPER(CAST(SERVERPROPERTY('ServerName') AS nvarchar(max))) AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF UPPER(@@SERVERNAME) <> UPPER(@ServerName) AND @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 + VALUES('The @@SERVERNAME does not match SERVERPROPERTY(''ServerName''). See ' + CASE WHEN @IsClustered = 0 THEN 'https://learn.microsoft.com/en-us/sql/database-engine/install-windows/rename-a-computer-that-hosts-a-stand-alone-instance-of-sql-server' WHEN @IsClustered = 1 THEN 'https://learn.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 + DECLARE ErrorCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Message], Severity, [State] FROM @Errors ORDER BY [ID] ASC OPEN ErrorCursor @@ -2515,7 +2747,7 @@ BEGIN --// Check Availability Group cluster name //-- ---------------------------------------------------------------------------------------------------- - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN SELECT @Cluster = NULLIF(cluster_name,'') FROM sys.dm_hadr_cluster @@ -2629,7 +2861,7 @@ BEGIN FROM dbo.[Queue] WHERE SchemaName = @SchemaName AND ObjectName = @ObjectName - AND [Parameters] = @Parameters + AND [Parameters] = @ParametersString IF @QueueID IS NULL BEGIN @@ -2639,12 +2871,12 @@ BEGIN FROM dbo.[Queue] WITH (UPDLOCK, HOLDLOCK) WHERE SchemaName = @SchemaName AND ObjectName = @ObjectName - AND [Parameters] = @Parameters + AND [Parameters] = @ParametersString IF @QueueID IS NULL BEGIN INSERT INTO dbo.[Queue] (SchemaName, ObjectName, [Parameters]) - SELECT @SchemaName, @ObjectName, @Parameters + VALUES(@SchemaName, @ObjectName, @ParametersString) SET @QueueID = SCOPE_IDENTITY() END @@ -2678,17 +2910,17 @@ BEGIN DatabaseName FROM @tmpDatabases tmpDatabases WHERE Selected = 1 - AND NOT EXISTS (SELECT * FROM dbo.QueueDatabase WHERE DatabaseName = tmpDatabases.DatabaseName AND QueueID = @QueueID) + AND NOT EXISTS (SELECT * FROM dbo.QueueDatabase WHERE DatabaseName COLLATE DATABASE_DEFAULT = 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) + AND NOT EXISTS (SELECT * FROM @tmpDatabases tmpDatabases WHERE DatabaseName = QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT AND Selected = 1) UPDATE QueueDatabase SET DatabaseOrder = tmpDatabases.[Order] FROM dbo.QueueDatabase QueueDatabase - INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName = tmpDatabases.DatabaseName + INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT = tmpDatabases.DatabaseName WHERE QueueID = @QueueID END @@ -2705,7 +2937,7 @@ BEGIN BEGIN ROLLBACK TRANSACTION END - SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar) + ', ' + ISNULL(ERROR_MESSAGE(),'') + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + ISNULL(ERROR_MESSAGE(),'') RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT SET @ReturnCode = ERROR_NUMBER() @@ -2741,7 +2973,7 @@ BEGIN 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) + @CurrentDatabaseNameFS = (SELECT DatabaseNameFS FROM @tmpDatabases WHERE DatabaseName = QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT) FROM (SELECT TOP 1 DatabaseStartTime, DatabaseEndTime, SessionID, @@ -2774,7 +3006,7 @@ BEGIN SET @CurrentDatabase_sp_executesql = QUOTENAME(@CurrentDatabaseName) + '.sys.sp_executesql' BEGIN - SET @DatabaseMessage = 'Date and time: ' + CONVERT(nvarchar,SYSDATETIME(),120) + SET @DatabaseMessage = 'Date and time: ' + CONVERT(nvarchar(max),SYSDATETIME(),120) RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT SET @DatabaseMessage = 'Database: ' + QUOTENAME(@CurrentDatabaseName) @@ -2813,41 +3045,84 @@ BEGIN 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 + WHEN @MaxTransferSize IS NULL AND @Compress = 'Y' AND @CurrentIsEncrypted = 1 AND @BackupSoftware IS NULL AND (@Version < 15.04043 AND NOT (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) AND @Credential IS NULL THEN 65537 END - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN - SELECT @CurrentReplicaID = databases.replica_id + SELECT @CurrentAvailabilityGroupReplicaID = 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 + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + + IF @CurrentAvailabilityGroupReplicaID IS NULL AND @CurrentAvailabilityGroupID IS NULL AND @Version >= 16 + BEGIN + SET @CurrentAvailabilityGroupID = @ContainedAvailabilityGroupID + + SELECT @CurrentAvailabilityGroupReplicaID = replica_id + FROM sys.dm_hadr_availability_replica_states + WHERE group_id = @CurrentAvailabilityGroupID + AND is_local = 1 + END + + SELECT @CurrentSecondaryRoleAllowConnections = secondary_role_allow_connections_desc + FROM sys.availability_replicas + WHERE replica_id = @CurrentAvailabilityGroupReplicaID SELECT @CurrentAvailabilityGroupRole = role_desc FROM sys.dm_hadr_availability_replica_states - WHERE replica_id = @CurrentReplicaID + WHERE replica_id = @CurrentAvailabilityGroupReplicaID SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc FROM sys.dm_hadr_database_replica_states - WHERE replica_id = @CurrentReplicaID + WHERE replica_id = @CurrentAvailabilityGroupReplicaID AND database_id = DB_ID(@CurrentDatabaseName) + IF @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState IS NULL AND @ContainedAvailabilityGroupListenerConnection = 1 + BEGIN + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND DB_NAME(database_id) = @CurrentDatabaseName + END + 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 + IF @IsHadrEnabled = 1 AND @CurrentAvailabilityGroup IS NOT NULL BEGIN SELECT @CurrentIsPreferredBackupReplica = sys.fn_hadr_backup_is_preferred_replica(@CurrentDatabaseName) END + IF @IsHadrEnabled = 1 AND @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SELECT @CurrentIsSeeding = CASE WHEN EXISTS (SELECT * FROM sys.dm_hadr_physical_seeding_stats dm_hadr_physical_seeding_stats WHERE dm_hadr_physical_seeding_stats.local_database_name = @CurrentDatabaseName AND dm_hadr_physical_seeding_stats.role_desc IN('Source','Forwarder') AND dm_hadr_physical_seeding_stats.end_time_utc IS NULL) THEN 1 ELSE 0 END + END + + IF @IsHadrEnabled = 1 AND @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SELECT @CurrentDistributedAvailabilityGroup = availability_groups.[name], + @CurrentDistributedAvailabilityGroupReplicaID = availability_replicas.replica_id + FROM sys.availability_groups availability_groups + INNER JOIN sys.availability_replicas availability_replicas ON availability_groups.group_id = availability_replicas.group_id + INNER JOIN sys.availability_groups availability_groups_local ON availability_replicas.replica_server_name = availability_groups_local.[name] + WHERE availability_groups.is_distributed = 1 + AND availability_groups_local.group_id = @CurrentAvailabilityGroupID + + SELECT @CurrentDistributedAvailabilityGroupRole = dm_hadr_availability_replica_states.role_desc + FROM sys.dm_hadr_availability_replica_states dm_hadr_availability_replica_states + WHERE dm_hadr_availability_replica_states.replica_id = @CurrentDistributedAvailabilityGroupReplicaID + END + SELECT @CurrentDifferentialBaseLSN = differential_base_lsn FROM sys.master_files WHERE database_id = DB_ID(@CurrentDatabaseName) @@ -2861,35 +3136,41 @@ BEGIN 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') + SET @CurrentBackupType = @BackupType + + IF (@Version >= 16.04265 AND @Version < 17) OR @Version >= 17.04065 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous') + BEGIN + SET @BackupInProgress = CASE WHEN EXISTS(SELECT * FROM sys.dm_exec_requests WHERE database_id = DB_ID(@CurrentDatabaseName) AND command = 'BACKUP DATABASE') THEN 1 ELSE 0 END + END + + IF @CurrentDatabaseState = 'ONLINE' + AND NOT @CurrentUserAccess = 'SINGLE_USER' + AND NOT @CurrentInStandby = 1 + AND NOT (@CurrentAvailabilityGroup IS NOT NULL AND (@CurrentAvailabilityGroupRole NOT IN('PRIMARY','SECONDARY') OR @CurrentAvailabilityGroupRole IS NULL)) 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) + AND (@CurrentDistributedAvailabilityGroupRole = 'PRIMARY' OR @CurrentDistributedAvailabilityGroupRole IS NULL) + AND (@BackupType IN('DIFF','FULL') OR (@ChangeBackupType = 'Y' AND @CurrentBackupType = 'LOG' AND @CurrentRecoveryModel IN('FULL','BULK_LOGGED') AND @CurrentLogLSN IS NULL AND NOT (@CurrentDatabaseName = 'master' AND @ContainedAvailabilityGroupListenerConnection = 0))) AND (@BackupInProgress = 0 OR @BackupInProgress IS 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' + IF @CurrentBackupType = 'LOG' AND @CurrentRecoveryModel IN('FULL','BULK_LOGGED') AND @CurrentLogLSN IS NULL AND NOT (@CurrentDatabaseName = 'master' AND @ContainedAvailabilityGroupListenerConnection = 0) AND (@BackupInProgress = 0 OR @BackupInProgress IS NULL) 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)) + IF @CurrentBackupType = 'DIFF' AND ((@CurrentDatabaseName = 'master' AND @ContainedAvailabilityGroupListenerConnection = 0) OR @CurrentDifferentialBaseLSN IS NULL OR (@CurrentModifiedExtentPageCount * 1. / NULLIF(@CurrentAllocatedExtentPageCount, 0) * 100 >= @MinModificationLevel) 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, + SELECT @CurrentLastLogBackup = NULLIF(log_backup_time,'1900-01-01'), @CurrentLogSizeSinceLastLogBackup = log_since_last_log_backup_mb FROM sys.dm_db_log_stats (DB_ID(@CurrentDatabaseName)) END @@ -2898,7 +3179,7 @@ BEGIN BEGIN SELECT @CurrentDifferentialBaseIsSnapshot = is_snapshot FROM msdb.dbo.backupset - WHERE database_name = @CurrentDatabaseName + WHERE [database_name] = @CurrentDatabaseName AND [type] = 'D' AND checkpoint_lsn = @CurrentDifferentialBaseLSN END @@ -2917,7 +3198,12 @@ BEGIN 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, + MaxNumberOfFiles = CASE WHEN @BackupSoftware IN('SQLBACKUP','SQLSAFE') AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) THEN 1 + WHEN @BackupSoftware = 'LITESPEED' AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) THEN 32 + WHEN @BackupSoftware IN('SQLBACKUP','DATA_DOMAIN_BOOST') THEN 32 + WHEN @BackupSoftware IS NULL AND (EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) OR EXISTS(SELECT * FROM @URLs WHERE Mirror = 1)) 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 @@ -2957,6 +3243,7 @@ BEGIN OR (@CurrentBackupType = 'DIFF' AND @CopyOnly = 'N' AND @Version >= 17) OR (@CurrentBackupType = 'FULL' AND @CopyOnly = 'Y') OR (@CurrentBackupType = 'LOG' AND @CopyOnly = 'N')) + AND @EngineEdition = 3 BEGIN SET @CurrentBackupOperationSupportedOnSecondaryReplicas = 1 END @@ -2980,6 +3267,12 @@ BEGIN SET @DatabaseMessage = 'Availability group database replica synchronization health: ' + ISNULL(@CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth,'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 + SET @DatabaseMessage = 'Availability group backup preference: ' + ISNULL(@CurrentAvailabilityGroupBackupPreference,'N/A') RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT @@ -2991,6 +3284,24 @@ 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 + + SET @DatabaseMessage = 'Is seeding: ' + CASE WHEN @CurrentIsSeeding = 1 THEN 'Yes' WHEN @CurrentIsSeeding = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + END + + IF @CurrentDistributedAvailabilityGroup IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Distributed availability group: ' + ISNULL(@CurrentDistributedAvailabilityGroup,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Distributed availability group role: ' + ISNULL(@CurrentDistributedAvailabilityGroupRole,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Replica role in distributed availability group: ' + CASE WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Global primary' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Forwarder' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in secondary availability group' + WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in primary availability group' ELSE 'N/A' END + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT END IF @CurrentDatabaseMirroringRole IS NOT NULL @@ -3005,7 +3316,7 @@ BEGIN RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT END - SET @DatabaseMessage = 'Differential base LSN: ' + ISNULL(CAST(@CurrentDifferentialBaseLSN AS nvarchar),'N/A') + SET @DatabaseMessage = 'Differential base LSN: ' + ISNULL(CAST(@CurrentDifferentialBaseLSN AS nvarchar(max)),'N/A') RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT IF @CurrentBackupType = 'DIFF' OR @CurrentDifferentialBaseIsSnapshot IS NOT NULL @@ -3014,24 +3325,27 @@ BEGIN RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT END - SET @DatabaseMessage = 'Last log backup LSN: ' + ISNULL(CAST(@CurrentLogLSN AS nvarchar),'N/A') + SET @DatabaseMessage = 'Last log backup LSN: ' + ISNULL(CAST(@CurrentLogLSN AS nvarchar(max)),'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') + SET @DatabaseMessage = 'Full or differential backup in progress: ' + CASE WHEN @BackupInProgress = 1 THEN 'Yes' WHEN @BackupInProgress = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + IF @CurrentBackupType IN('DIFF','FULL') BEGIN - SET @DatabaseMessage = 'Allocated extent page count: ' + ISNULL(CAST(@CurrentAllocatedExtentPageCount AS nvarchar) + ' (' + CAST(@CurrentAllocatedExtentPageCount * 1. * 8 / 1024 AS nvarchar) + ' MB)','N/A') + SET @DatabaseMessage = 'Allocated extent page count: ' + ISNULL(CAST(@CurrentAllocatedExtentPageCount AS nvarchar(max)) + ' (' + CAST(@CurrentAllocatedExtentPageCount * 1. * 8 / 1024 AS nvarchar(max)) + ' 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') + SET @DatabaseMessage = 'Modified extent page count: ' + ISNULL(CAST(@CurrentModifiedExtentPageCount AS nvarchar(max)) + ' (' + CAST(@CurrentModifiedExtentPageCount * 1. * 8 / 1024 AS nvarchar(max)) + ' 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') + IF @CurrentBackupType = 'LOG' BEGIN - SET @DatabaseMessage = 'Last log backup: ' + ISNULL(CONVERT(nvarchar(19),NULLIF(@CurrentLastLogBackup,'1900-01-01'),120),'N/A') + SET @DatabaseMessage = 'Last log backup: ' + ISNULL(CONVERT(nvarchar(19),@CurrentLastLogBackup,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') + SET @DatabaseMessage = 'Log size since last log backup (MB): ' + ISNULL(CAST(@CurrentLogSizeSinceLastLogBackup AS nvarchar(max)),'N/A') RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT END @@ -3043,13 +3357,17 @@ BEGIN 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 (@CurrentBackupType IN('DIFF','LOG') AND (@CurrentDatabaseName = 'master' AND @ContainedAvailabilityGroupListenerConnection = 0)) + AND NOT (@CurrentAvailabilityGroup IS NOT NULL AND (@CurrentAvailabilityGroupRole NOT IN('PRIMARY','SECONDARY') OR @CurrentAvailabilityGroupRole IS NULL)) 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 (@CurrentDistributedAvailabilityGroup IS NOT NULL AND @CurrentBackupOperationSupportedOnSecondaryReplicas = 0 AND (@CurrentDistributedAvailabilityGroupRole <> 'PRIMARY' OR @CurrentDistributedAvailabilityGroupRole IS NULL)) + AND NOT (@CurrentDistributedAvailabilityGroup IS NOT NULL AND (@CurrentDistributedAvailabilityGroupRole <> 'PRIMARY' OR @CurrentDistributedAvailabilityGroupRole IS NULL) AND @AllowNonCopyOnlyBackupsOnForwarder = 'N' AND NOT (@CurrentBackupType = 'FULL' AND @CopyOnly = 'Y')) AND NOT ((@CurrentLogShippingRole = 'PRIMARY' AND @CurrentLogShippingRole IS NOT NULL) AND @CurrentBackupType = 'LOG' AND @ExcludeLogShippedFromLogBackup = 'Y') + AND NOT (@CurrentAvailabilityGroup IS NOT NULL AND @CurrentBackupType = 'LOG' AND @ExcludeSeedingFromLogBackup = 'Y' AND @CurrentIsSeeding = 1) 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 @MinLogSizeSinceLastLogBackup IS NOT NULL AND @MinTimeSinceLastLogBackup IS NOT NULL AND NOT(@CurrentLogSizeSinceLastLogBackup >= @MinLogSizeSinceLastLogBackup OR @CurrentLogSizeSinceLastLogBackup IS NULL OR DATEDIFF(SECOND,@CurrentLastLogBackup,SYSDATETIME()) >= @MinTimeSinceLastLogBackup 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 @@ -3067,13 +3385,13 @@ BEGIN SET @CurrentDate = SYSDATETIME() SET @CurrentDateUTC = SYSUTCDATETIME() - INSERT INTO @CurrentCleanupDates (CleanupDate) - SELECT @CurrentDate + INSERT INTO @CurrentCleanupDates ([Type], CleanupDate) + VALUES('CurrentTime', @CurrentDate) IF @CurrentBackupType = 'LOG' BEGIN - INSERT INTO @CurrentCleanupDates (CleanupDate) - SELECT @CurrentLatestBackup + INSERT INTO @CurrentCleanupDates ([Type], CleanupDate) + VALUES('LatestBackupTime', @CurrentLatestBackup) END SELECT @CurrentDirectoryStructure = CASE @@ -3087,13 +3405,12 @@ BEGIN 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 @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 @Description IS NULL OR LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@Description,'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|',''))) = '' SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Description}','') + IF @BackupSetName IS NULL OR LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@BackupSetName,'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|',''))) = '' 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' + IF @Directory IS NULL AND @MirrorDirectory IS NULL AND @URL IS NULL AND @DefaultDirectory LIKE '%' + '.' + REPLACE(@@SERVICENAME,'_','[_]') + @DirectorySeparator + 'MSSQL' + @DirectorySeparator + 'Backup' BEGIN SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{ServerName}','') SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{InstanceName}','') @@ -3239,8 +3556,8 @@ BEGIN -- 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,'{ServerName}',CASE WHEN @EngineEdition = 8 AND CHARINDEX('.',@ServerName) > 0 THEN LEFT(@ServerName,CHARINDEX('.',@ServerName) - 1) WHEN @EngineEdition = 8 AND CHARINDEX('.',@ServerName) = 0 THEN @ServerName ELSE @MachineName END) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{InstanceName}',ISNULL(@InstanceName,'')) SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{ServiceName}',ISNULL(@@SERVICENAME,'')) SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{ClusterName}',ISNULL(@Cluster,'')) SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{AvailabilityGroupName}',ISNULL(@CurrentAvailabilityGroup,'')) @@ -3250,18 +3567,18 @@ BEGIN 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))) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Year}',CAST(DATEPART(YEAR,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max))) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Month}',RIGHT('0' + CAST(DATEPART(MONTH,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Day}',RIGHT('0' + CAST(DATEPART(DAY,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Week}',RIGHT('0' + CAST(DATEPART(WEEK,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Weekday}',FORMAT(CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END,'dddd','en-US')) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Hour}',RIGHT('0' + CAST(DATEPART(HOUR,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Minute}',RIGHT('0' + CAST(DATEPART(MINUTE,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Second}',RIGHT('0' + CAST(DATEPART(SECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Millisecond}',RIGHT('00' + CAST(DATEPART(MILLISECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),3)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Microsecond}',RIGHT('00000' + CAST(DATEPART(MICROSECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),6)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{MajorVersion}',@ProductMajorVersion) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{MinorVersion}',@ProductMinorVersion) END IF @DirectoryStructureCase IS NOT NULL @@ -3297,11 +3614,10 @@ BEGIN 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 @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 @Description IS NULL OR LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@Description,'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|',''))) = '' SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Description}','') + IF @BackupSetName IS NULL OR LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@BackupSetName,'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|',''))) = '' SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{BackupSetName}','') IF @CurrentNumberOfFiles = 1 SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{FileNumber}','') IF @CurrentNumberOfFiles = 1 SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{NumberOfFiles}','') @@ -3404,8 +3720,8 @@ BEGIN 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,'{ServerName}',CASE WHEN @EngineEdition = 8 AND CHARINDEX('.',@ServerName) > 0 THEN LEFT(@ServerName,CHARINDEX('.',@ServerName) - 1) WHEN @EngineEdition = 8 AND CHARINDEX('.',@ServerName) = 0 THEN @ServerName ELSE @MachineName END) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{InstanceName}',ISNULL(@InstanceName,'')) SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{ServiceName}',ISNULL(@@SERVICENAME,'')) SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{ClusterName}',ISNULL(@Cluster,'')) SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{AvailabilityGroupName}',ISNULL(@CurrentAvailabilityGroup,'')) @@ -3414,26 +3730,26 @@ BEGIN 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,'{Year}',CAST(DATEPART(YEAR,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max))) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Month}',RIGHT('0' + CAST(DATEPART(MONTH,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Day}',RIGHT('0' + CAST(DATEPART(DAY,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Week}',RIGHT('0' + CAST(DATEPART(WEEK,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Weekday}',FORMAT(CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END,'dddd','en-US')) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Hour}',RIGHT('0' + CAST(DATEPART(HOUR,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Minute}',RIGHT('0' + CAST(DATEPART(MINUTE,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Second}',RIGHT('0' + CAST(DATEPART(SECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Millisecond}',RIGHT('00' + CAST(DATEPART(MILLISECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),3)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Microsecond}',RIGHT('00000' + CAST(DATEPART(MICROSECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),6)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{NumberOfFiles}',CAST(@CurrentNumberOfFiles AS nvarchar(max))) 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))) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{MajorVersion}',@ProductMajorVersion) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{MinorVersion}',@ProductMinorVersion) 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)) + + LEN(REPLACE(REPLACE(@CurrentDatabaseFileName,'{DatabaseName}',@CurrentDatabaseNameFS), '{FileNumber}', CASE WHEN @CurrentNumberOfFiles >= 1 AND @CurrentNumberOfFiles <= 9 THEN '1' WHEN @CurrentNumberOfFiles >= 10 THEN '01' ELSE '' END)) -- The maximum length of a backup device is 259 characters IF @CurrentMaxFilePathLength > 259 @@ -3465,7 +3781,7 @@ BEGIN 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) + SET @CurrentFileName = REPLACE(@CurrentDatabaseFileName, '{FileNumber}', CASE WHEN @CurrentNumberOfFiles >= 1 AND @CurrentNumberOfFiles <= 9 THEN CAST(@CurrentFileNumber AS nvarchar(max)) WHEN @CurrentNumberOfFiles >= 10 THEN RIGHT('0' + CAST(@CurrentFileNumber AS nvarchar(max)),2) ELSE '' END) IF @CurrentDirectoryPath = 'NUL' BEGIN @@ -3477,7 +3793,7 @@ BEGIN END INSERT INTO @CurrentFiles ([Type], FilePath, Mirror) - SELECT 'DISK', @CurrentFilePath, 0 + VALUES('DISK', @CurrentFilePath, 0) SET @CurrentDirectoryPath = NULL SET @CurrentFileName = NULL @@ -3485,7 +3801,7 @@ BEGIN END INSERT INTO @CurrentBackupSet (Mirror, VerifyCompleted) - SELECT 0, 0 + VALUES(0, 0) END IF EXISTS (SELECT * FROM @CurrentDirectories WHERE Mirror = 1) @@ -3502,12 +3818,12 @@ BEGIN 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 @CurrentFileName = REPLACE(@CurrentDatabaseFileName, '{FileNumber}', CASE WHEN @CurrentNumberOfFiles >= 1 AND @CurrentNumberOfFiles <= 9 THEN CAST(@CurrentFileNumber AS nvarchar(max)) WHEN @CurrentNumberOfFiles >= 10 THEN RIGHT('0' + CAST(@CurrentFileNumber AS nvarchar(max)),2) ELSE '' END) SET @CurrentFilePath = @CurrentDirectoryPath + @DirectorySeparator + @CurrentFileName INSERT INTO @CurrentFiles ([Type], FilePath, Mirror) - SELECT 'DISK', @CurrentFilePath, 1 + VALUES('DISK', @CurrentFilePath, 1) SET @CurrentDirectoryPath = NULL SET @CurrentFileName = NULL @@ -3515,7 +3831,7 @@ BEGIN END INSERT INTO @CurrentBackupSet (Mirror, VerifyCompleted) - SELECT 1, 0 + VALUES(1, 0) END IF EXISTS (SELECT * FROM @CurrentURLs WHERE Mirror = 0) @@ -3532,12 +3848,12 @@ BEGIN 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 @CurrentFileName = REPLACE(@CurrentDatabaseFileName, '{FileNumber}', CASE WHEN @CurrentNumberOfFiles >= 1 AND @CurrentNumberOfFiles <= 9 THEN CAST(@CurrentFileNumber AS nvarchar(max)) WHEN @CurrentNumberOfFiles >= 10 THEN RIGHT('0' + CAST(@CurrentFileNumber AS nvarchar(max)),2) ELSE '' END) SET @CurrentFilePath = @CurrentDirectoryPath + @DirectorySeparator + @CurrentFileName INSERT INTO @CurrentFiles ([Type], FilePath, Mirror) - SELECT 'URL', @CurrentFilePath, 0 + VALUES('URL', @CurrentFilePath, 0) SET @CurrentDirectoryPath = NULL SET @CurrentFileName = NULL @@ -3545,7 +3861,7 @@ BEGIN END INSERT INTO @CurrentBackupSet (Mirror, VerifyCompleted) - SELECT 0, 0 + VALUES(0, 0) END IF EXISTS (SELECT * FROM @CurrentURLs WHERE Mirror = 1) @@ -3558,16 +3874,16 @@ BEGIN 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) + WHERE @CurrentFileNumber >= (DirectoryNumber - 1) * (SELECT @CurrentNumberOfFiles / COUNT(*) FROM @CurrentURLs WHERE Mirror = 1) + 1 + AND @CurrentFileNumber <= DirectoryNumber * (SELECT @CurrentNumberOfFiles / COUNT(*) FROM @CurrentURLs 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 @CurrentFileName = REPLACE(@CurrentDatabaseFileName, '{FileNumber}', CASE WHEN @CurrentNumberOfFiles >= 1 AND @CurrentNumberOfFiles <= 9 THEN CAST(@CurrentFileNumber AS nvarchar(max)) WHEN @CurrentNumberOfFiles >= 10 THEN RIGHT('0' + CAST(@CurrentFileNumber AS nvarchar(max)),2) ELSE '' END) SET @CurrentFilePath = @CurrentDirectoryPath + @DirectorySeparator + @CurrentFileName INSERT INTO @CurrentFiles ([Type], FilePath, Mirror) - SELECT 'URL', @CurrentFilePath, 1 + VALUES('URL', @CurrentFilePath, 1) SET @CurrentDirectoryPath = NULL SET @CurrentFileName = NULL @@ -3575,7 +3891,7 @@ BEGIN END INSERT INTO @CurrentBackupSet (Mirror, VerifyCompleted) - SELECT 1, 0 + VALUES(1, 0) END -- Create directory @@ -3611,7 +3927,7 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_create_subdir N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + ''' IF @ReturnCode <> 0 OR @ReturnCode IS NULL 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 @@ -3645,8 +3961,8 @@ BEGIN IF @CleanupMode = 'BEFORE_BACKUP' BEGIN - INSERT INTO @CurrentCleanupDates (CleanupDate, Mirror) - SELECT DATEADD(hh,-(@CleanupTime),SYSDATETIME()), 0 + INSERT INTO @CurrentCleanupDates ([Type], CleanupDate, Mirror) + VALUES('CleanupTime', DATEADD(hh,-(@CleanupTime),SYSDATETIME()), 0) IF NOT EXISTS(SELECT * FROM @CurrentCleanupDates WHERE (Mirror = 0 OR Mirror IS NULL) AND CleanupDate IS NULL) BEGIN @@ -3661,8 +3977,8 @@ BEGIN IF @MirrorCleanupMode = 'BEFORE_BACKUP' BEGIN - INSERT INTO @CurrentCleanupDates (CleanupDate, Mirror) - SELECT DATEADD(hh,-(@MirrorCleanupTime),SYSDATETIME()), 1 + INSERT INTO @CurrentCleanupDates ([Type], CleanupDate, Mirror) + VALUES('MirrorCleanupTime', DATEADD(hh,-(@MirrorCleanupTime),SYSDATETIME()), 1) IF NOT EXISTS(SELECT * FROM @CurrentCleanupDates WHERE (Mirror = 1 OR Mirror IS NULL) AND CleanupDate IS NULL) BEGIN @@ -3702,7 +4018,7 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_delete_file 0, N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + ''', ''' + REPLACE(@CurrentFileExtension,'''','''''') + ''', ''' + CONVERT(nvarchar(19),@CurrentCleanupDate,126) + ''' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error deleting files.'', 16, 1)' END IF @BackupSoftware = 'LITESPEED' @@ -3711,7 +4027,7 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_slssqlmaint N''-MAINTDEL -DELFOLDER "' + REPLACE(@CurrentDirectoryPath,'''','''''') + '" -DELEXTENSION "' + REPLACE(@CurrentFileExtension,'''','''''') + '" -DELUNIT "' + CAST(DATEDIFF(mi,@CurrentCleanupDate,SYSDATETIME()) + 1 AS nvarchar(max)) + '" -DELUNITTYPE "minutes" -DELUSEAGE'' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error deleting LiteSpeed backup files.'', 16, 1)' END IF @BackupSoftware = 'SQLBACKUP' @@ -3720,7 +4036,7 @@ BEGIN 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)' + 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(max)) + 'h'', ' + ISNULL('''' + @EncryptionKeyPlaceholder + '''','NULL') + ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error deleting SQLBackup backup files.'', 16, 1)' END IF @BackupSoftware = 'SQLSAFE' @@ -3729,10 +4045,10 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_ss_delete @filename = N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + '\*.' + REPLACE(@CurrentFileExtension,'''','''''') + ''', @age = ''' + CAST(DATEDIFF(mi,@CurrentCleanupDate,SYSDATETIME()) + 1 AS nvarchar(max)) + 'Minutes'' IF @ReturnCode <> 0 OR @ReturnCode IS NULL 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 + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @EncryptionKey = @EncryptionKey, @EncryptionKeyPlaceholder = @EncryptionKeyPlaceholder, @LogToTable = @LogToTable, @Execute = @Execute SET @Error = @@ERROR IF @Error <> 0 SET @CurrentCommandOutput = @Error IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput @@ -3754,7 +4070,15 @@ BEGIN 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)) + IF @BackupSoftware = 'DATA_DOMAIN_BOOST' AND @CurrentDatabaseName LIKE '%"%' + BEGIN + SET @ErrorMessage = 'The name of the database ' + QUOTENAME(@CurrentDatabaseName) + ' is not supported. Double quotes (") are not supported with Data Domain Boost.' + RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT + SET @Error = @@ERROR + SET @ReturnCode = @Error + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + END + ELSE 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 @@ -3794,10 +4118,7 @@ BEGIN 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 + SET @CurrentCommand += CASE WHEN @Compress = 'Y' AND (@CurrentIsEncrypted = 0 OR (@CurrentIsEncrypted = 1 AND (@CurrentMaxTransferSize >= 65537 OR (@Version >= 15.04043 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous'))))) THEN ', COMPRESSION' ELSE ', NO_COMPRESSION' END IF @Compress = 'Y' AND @CompressionAlgorithm IS NOT NULL BEGIN @@ -3815,20 +4136,20 @@ BEGIN 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 @BlockSize IS NOT NULL SET @CurrentCommand += ', BLOCKSIZE = ' + CAST(@BlockSize AS nvarchar(max)) + IF @BufferCount IS NOT NULL SET @CurrentCommand += ', BUFFERCOUNT = ' + CAST(@BufferCount AS nvarchar(max)) + IF @CurrentMaxTransferSize IS NOT NULL SET @CurrentCommand += ', MAXTRANSFERSIZE = ' + CAST(@CurrentMaxTransferSize AS nvarchar(max)) 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 @Stats IS NOT NULL SET @CurrentCommand += ', STATS = ' + CAST(@Stats AS nvarchar(max)) 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) + IF @ExpireDate IS NOT NULL SET @CurrentCommand += ', EXPIREDATE = ''' + CONVERT(nvarchar(max), @ExpireDate, 21) + '''' + IF @RetainDays IS NOT NULL SET @CurrentCommand += ', RETAINDAYS = ' + CAST(@RetainDays AS nvarchar(max)) END IF @BackupSoftware = 'LITESPEED' @@ -3864,21 +4185,21 @@ BEGIN 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) + IF @BlockSize IS NOT NULL SET @CurrentCommand += ', BLOCKSIZE = ' + CAST(@BlockSize AS nvarchar(max)) 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 @CompressionLevelNumeric IS NOT NULL SET @CurrentCommand += ', @compressionlevel = ' + CAST(@CompressionLevelNumeric AS nvarchar(max)) 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 @BufferCount IS NOT NULL SET @CurrentCommand += ', @buffercount = ' + CAST(@BufferCount AS nvarchar(max)) + IF @CurrentMaxTransferSize IS NOT NULL SET @CurrentCommand += ', @maxtransfersize = ' + CAST(@CurrentMaxTransferSize AS nvarchar(max)) + IF @Threads IS NOT NULL SET @CurrentCommand += ', @threads = ' + CAST(@Threads AS nvarchar(max)) 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 @Throttle IS NOT NULL SET @CurrentCommand += ', @throttle = ' + CAST(@Throttle AS nvarchar(max)) 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 @ExpireDate IS NOT NULL SET @CurrentCommand += ', @expiration = ''' + CONVERT(nvarchar(max), @ExpireDate, 21) + '''' + IF @RetainDays IS NOT NULL SET @CurrentCommand += ', @retaindays = ' + CAST(@RetainDays AS nvarchar(max)) IF @EncryptionAlgorithm IS NOT NULL SET @CurrentCommand += ', @cryptlevel = ' + CASE WHEN @EncryptionAlgorithm = 'RC2_40' THEN '0' @@ -3892,8 +4213,8 @@ BEGIN 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)' + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', @encryptionkey = N''' + @EncryptionKeyPlaceholder + '''' + SET @CurrentCommand += ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error performing LiteSpeed backup.'', 16, 1)' END IF @BackupSoftware = 'SQLBACKUP' @@ -3930,9 +4251,9 @@ BEGIN 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 @CompressionLevelNumeric IS NOT NULL SET @CurrentCommand += ', COMPRESSION = ' + CAST(@CompressionLevelNumeric AS nvarchar(max)) + IF @Threads IS NOT NULL SET @CurrentCommand += ', THREADCOUNT = ' + CAST(@Threads AS nvarchar(max)) + IF @CurrentMaxTransferSize IS NOT NULL SET @CurrentCommand += ', MAXTRANSFERSIZE = ' + CAST(@CurrentMaxTransferSize AS nvarchar(max)) IF @Description IS NOT NULL SET @CurrentCommand += ', DESCRIPTION = N''' + REPLACE(@Description,'''','''''') + '''' IF @EncryptionAlgorithm IS NOT NULL SET @CurrentCommand += ', KEYSIZE = ' + CASE @@ -3940,8 +4261,8 @@ BEGIN 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)' + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', PASSWORD = N''' + @EncryptionKeyPlaceholder + '''' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.sqlbackup N''-SQL "' + REPLACE(@CurrentCommand,'''','''''') + '"''' + ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error performing SQLBackup backup.'', 16, 1)' END IF @BackupSoftware = 'SQLSAFE' @@ -3974,8 +4295,8 @@ BEGIN 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 @CompressionLevelNumeric IS NOT NULL SET @CurrentCommand += ', @compressionlevel = ' + CAST(@CompressionLevelNumeric AS nvarchar(max)) + IF @Threads IS NOT NULL SET @CurrentCommand += ', @threads = ' + CAST(@Threads AS nvarchar(max)) IF @Init = 'Y' SET @CurrentCommand += ', @overwrite = 1' IF @Description IS NOT NULL SET @CurrentCommand += ', @desc = N''' + REPLACE(@Description,'''','''''') + '''' @@ -3984,8 +4305,8 @@ BEGIN 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)' + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', @encryptedbackuppassword = N''' + @EncryptionKeyPlaceholder + '''' + SET @CurrentCommand += ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error performing SQLsafe backup.'', 16, 1)' END IF @BackupSoftware = 'DATA_DOMAIN_BOOST' @@ -3994,9 +4315,13 @@ BEGIN SET @CurrentCommandType = 'emc_run_backup' - SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.emc_run_backup ''' + SET @CurrentCommand = '' + IF @DataDomainBoostNoOutputTable = 'Y' SET @CurrentCommand += 'DECLARE @DataDomainBoostOutput TABLE ([Message] nvarchar(MAX)); ' + SET @CurrentCommand += 'DECLARE @ReturnCode int; ' + IF @DataDomainBoostNoOutputTable = 'Y' SET @CurrentCommand += 'INSERT INTO @DataDomainBoostOutput ([Message]) ' + SET @CurrentCommand += '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 += ' -c ' + CASE WHEN @Cluster IS NOT NULL AND @CurrentAvailabilityGroup IS NOT NULL THEN REPLACE(@Cluster,'''','''''') ELSE REPLACE(@MachineName,'''','''''') END SET @CurrentCommand += ' -l ' + CASE WHEN @CurrentBackupType = 'FULL' THEN 'full' @@ -4006,15 +4331,15 @@ BEGIN 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 @CleanupTime IS NOT NULL SET @CurrentCommand += ' -y +' + CAST(@CleanupTime/24 + CASE WHEN @CleanupTime%24 > 0 THEN 1 ELSE 0 END AS nvarchar(max)) + 'd' IF @Checksum = 'Y' SET @CurrentCommand += ' -k' - SET @CurrentCommand += ' -S ' + CAST(@CurrentNumberOfFiles AS nvarchar) + SET @CurrentCommand += ' -S ' + CAST(@CurrentNumberOfFiles AS nvarchar(max)) IF @Description IS NOT NULL SET @CurrentCommand += ' -b "' + REPLACE(@Description,'''','''''') + '"' - IF @BufferCount IS NOT NULL SET @CurrentCommand += ' -O "BUFFERCOUNT=' + CAST(@BufferCount AS nvarchar) + '"' + IF @BufferCount IS NOT NULL SET @CurrentCommand += ' -O "BUFFERCOUNT=' + CAST(@BufferCount AS nvarchar(max)) + '"' IF @ReadWriteFileGroups = 'Y' AND @CurrentDatabaseName <> 'master' SET @CurrentCommand += ' -O "READ_WRITE_FILEGROUPS"' @@ -4027,16 +4352,16 @@ BEGIN 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) + IF @InstanceName IS NULL SET @CurrentCommand += ' "MSSQL' + IF @InstanceName IS NOT NULL SET @CurrentCommand += ' "MSSQL$' + @InstanceName SET @CurrentCommand += ':' + REPLACE(REPLACE(@CurrentDatabaseName,'''',''''''),'.','\.') + '"' SET @CurrentCommand += '''' - SET @CurrentCommand += ' IF @ReturnCode <> 0 RAISERROR(''Error performing Data Domain Boost backup.'', 16, 1)' + SET @CurrentCommand += ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL 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 + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @EncryptionKey = @EncryptionKey, @EncryptionKeyPlaceholder = @EncryptionKeyPlaceholder, @LogToTable = @LogToTable, @Execute = @Execute SET @Error = @@ERROR IF @Error <> 0 SET @CurrentCommandOutput = @Error IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput @@ -4075,7 +4400,7 @@ BEGIN 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 @Stats IS NOT NULL SET @CurrentCommand += ', STATS = ' + CAST(@Stats AS nvarchar(max)) 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 @@ -4097,9 +4422,9 @@ BEGIN 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,'''','''''') + '''' + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', @encryptionkey = N''' + @EncryptionKeyPlaceholder + '''' - SET @CurrentCommand += ' IF @ReturnCode <> 0 RAISERROR(''Error verifying LiteSpeed backup.'', 16, 1)' + SET @CurrentCommand += ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error verifying LiteSpeed backup.'', 16, 1)' END IF @BackupSoftware = 'SQLBACKUP' @@ -4118,9 +4443,9 @@ BEGIN 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,'''','''''') + '''' + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', PASSWORD = N''' + @EncryptionKeyPlaceholder + '''' - SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.sqlbackup N''-SQL "' + REPLACE(@CurrentCommand,'''','''''') + '"''' + ' IF @ReturnCode <> 0 RAISERROR(''Error verifying SQLBackup backup.'', 16, 1)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.sqlbackup N''-SQL "' + REPLACE(@CurrentCommand,'''','''''') + '"''' + ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error verifying SQLBackup backup.'', 16, 1)' END IF @BackupSoftware = 'SQLSAFE' @@ -4141,10 +4466,10 @@ BEGIN WITHIN GROUP (ORDER BY RowNumber ASC) FROM CurrentFiles - SET @CurrentCommand += ' IF @ReturnCode <> 0 RAISERROR(''Error verifying SQLsafe backup.'', 16, 1)' + SET @CurrentCommand += ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL 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 + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @EncryptionKey = @EncryptionKey, @EncryptionKeyPlaceholder = @EncryptionKeyPlaceholder, @LogToTable = @LogToTable, @Execute = @Execute SET @Error = @@ERROR IF @Error <> 0 SET @CurrentCommandOutput = @Error IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput @@ -4166,8 +4491,8 @@ BEGIN IF @CleanupMode = 'AFTER_BACKUP' BEGIN - INSERT INTO @CurrentCleanupDates (CleanupDate, Mirror) - SELECT DATEADD(hh,-(@CleanupTime),SYSDATETIME()), 0 + INSERT INTO @CurrentCleanupDates ([Type], CleanupDate, Mirror) + VALUES('CleanupTime', DATEADD(hh,-(@CleanupTime),SYSDATETIME()), 0) IF NOT EXISTS(SELECT * FROM @CurrentCleanupDates WHERE (Mirror = 0 OR Mirror IS NULL) AND CleanupDate IS NULL) BEGIN @@ -4182,8 +4507,8 @@ BEGIN IF @MirrorCleanupMode = 'AFTER_BACKUP' BEGIN - INSERT INTO @CurrentCleanupDates (CleanupDate, Mirror) - SELECT DATEADD(hh,-(@MirrorCleanupTime),SYSDATETIME()), 1 + INSERT INTO @CurrentCleanupDates ([Type], CleanupDate, Mirror) + VALUES('MirrorCleanupTime', DATEADD(hh,-(@MirrorCleanupTime),SYSDATETIME()), 1) IF NOT EXISTS(SELECT * FROM @CurrentCleanupDates WHERE (Mirror = 1 OR Mirror IS NULL) AND CleanupDate IS NULL) BEGIN @@ -4224,7 +4549,7 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_delete_file 0, N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + ''', ''' + REPLACE(@CurrentFileExtension,'''','''''') + ''', ''' + CONVERT(nvarchar(19),@CurrentCleanupDate,126) + ''' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error deleting files.'', 16, 1)' END IF @BackupSoftware = 'LITESPEED' @@ -4233,7 +4558,7 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_slssqlmaint N''-MAINTDEL -DELFOLDER "' + REPLACE(@CurrentDirectoryPath,'''','''''') + '" -DELEXTENSION "' + REPLACE(@CurrentFileExtension,'''','''''') + '" -DELUNIT "' + CAST(DATEDIFF(mi,@CurrentCleanupDate,SYSDATETIME()) + 1 AS nvarchar(max)) + '" -DELUNITTYPE "minutes" -DELUSEAGE'' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error deleting LiteSpeed backup files.'', 16, 1)' END IF @BackupSoftware = 'SQLBACKUP' @@ -4242,7 +4567,7 @@ BEGIN 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)' + 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(max)) + 'h'', ' + ISNULL('''' + @EncryptionKeyPlaceholder + '''','NULL') + ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error deleting SQLBackup backup files.'', 16, 1)' END IF @BackupSoftware = 'SQLSAFE' @@ -4251,10 +4576,10 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_ss_delete @filename = N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + '\*.' + REPLACE(@CurrentFileExtension,'''','''''') + ''', @age = ''' + CAST(DATEDIFF(mi,@CurrentCleanupDate,SYSDATETIME()) + 1 AS nvarchar(max)) + 'Minutes'' IF @ReturnCode <> 0 OR @ReturnCode IS NULL 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 + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @EncryptionKey = @EncryptionKey, @EncryptionKeyPlaceholder = @EncryptionKeyPlaceholder, @LogToTable = @LogToTable, @Execute = @Execute SET @Error = @@ERROR IF @Error <> 0 SET @CurrentCommandOutput = @Error IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput @@ -4281,6 +4606,7 @@ BEGIN SET @ErrorMessage = 'The database ' + QUOTENAME(@CurrentDatabaseName) + ' is in a SUSPECT state.' RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT SET @Error = @@ERROR + SET @ReturnCode = @Error RAISERROR(@EmptyLine,10,1) WITH NOWAIT END @@ -4323,6 +4649,7 @@ BEGIN SET @CurrentDifferentialBaseLSN = NULL SET @CurrentDifferentialBaseIsSnapshot = NULL SET @CurrentLogLSN = NULL + SET @BackupInProgress = NULL SET @CurrentLatestBackup = NULL SET @CurrentDatabaseNameFS = NULL SET @CurrentDirectoryStructure = NULL @@ -4331,16 +4658,21 @@ BEGIN SET @CurrentDate = NULL SET @CurrentDateUTC = NULL SET @CurrentCleanupDate = NULL - SET @CurrentReplicaID = NULL + SET @CurrentAvailabilityGroupReplicaID = NULL SET @CurrentAvailabilityGroupID = NULL SET @CurrentAvailabilityGroup = NULL SET @CurrentAvailabilityGroupRole = NULL SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = NULL SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = NULL SET @CurrentAvailabilityGroupBackupPreference = NULL + SET @CurrentSecondaryRoleAllowConnections = NULL SET @CurrentIsPreferredBackupReplica = NULL + SET @CurrentDistributedAvailabilityGroup = NULL + SET @CurrentDistributedAvailabilityGroupReplicaID = NULL + SET @CurrentDistributedAvailabilityGroupRole = NULL SET @CurrentDatabaseMirroringRole = NULL SET @CurrentLogShippingRole = NULL + SET @CurrentIsSeeding = NULL SET @CurrentBackupOperationSupportedOnSecondaryReplicas = NULL SET @CurrentLastLogBackup = NULL SET @CurrentLogSizeSinceLastLogBackup = NULL @@ -4367,7 +4699,7 @@ BEGIN ---------------------------------------------------------------------------------------------------- Logging: - SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar,SYSDATETIME(),120) + SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar(max),SYSDATETIME(),120) RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT diff --git a/DatabaseIntegrityCheck.sql b/DatabaseIntegrityCheck.sql index e15e2244..5a084a93 100644 --- a/DatabaseIntegrityCheck.sql +++ b/DatabaseIntegrityCheck.sql @@ -40,7 +40,7 @@ BEGIN --// Source: https://ola.hallengren.com //-- --// License: https://ola.hallengren.com/license.html //-- --// GitHub: https://github.com/olahallengren/sql-server-maintenance-solution //-- - --// Version: 2026-05-18 20:15:56 //-- + --// Version: 2026-08-23 14:46:45 //-- ---------------------------------------------------------------------------------------------------- SET NOCOUNT ON @@ -55,9 +55,25 @@ BEGIN 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 @Parameters TABLE (ID int IDENTITY PRIMARY KEY, + [Name] nvarchar(max) NOT NULL, + ValueNvarchar nvarchar(max), + ValueInt int, + ValueDatetime datetime2) + + DECLARE @ParametersString nvarchar(max) + DECLARE @CurrentParameterName nvarchar(max) + DECLARE @CurrentParameterValueNvarchar nvarchar(max) + DECLARE @CurrentParameterValueInt int + DECLARE @CurrentParameterValueDatetime datetime2 + DECLARE @CurrentParameterDelimiter nvarchar(max) + DECLARE @CurrentParameterMessage nvarchar(max) DECLARE @HostPlatform nvarchar(max) + DECLARE @ContainedAvailabilityGroupID uniqueidentifier + DECLARE @ContainedAvailabilityGroupListenerConnection bit + DECLARE @IsSysadmin bit = IS_SRVROLEMEMBER('sysadmin') DECLARE @QueueID int DECLARE @QueueStartTime datetime2 @@ -72,14 +88,18 @@ BEGIN DECLARE @CurrentDatabaseState nvarchar(max) DECLARE @CurrentInStandby bit DECLARE @CurrentRecoveryModel nvarchar(max) - - DECLARE @CurrentReplicaID uniqueidentifier + DECLARE @CurrentAvailabilityGroupReplicaID 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 @CurrentSecondaryRoleAllowConnections nvarchar(max) DECLARE @CurrentIsPreferredBackupReplica bit + DECLARE @CurrentDistributedAvailabilityGroup nvarchar(max) + DECLARE @CurrentDistributedAvailabilityGroupReplicaID uniqueidentifier + DECLARE @CurrentDistributedAvailabilityGroupRole nvarchar(max) DECLARE @CurrentDatabaseMirroringRole nvarchar(max) DECLARE @CurrentFGID int @@ -103,58 +123,58 @@ BEGIN DECLARE @Errors TABLE (ID int IDENTITY PRIMARY KEY, [Message] nvarchar(max) NOT NULL, Severity int NOT NULL, - [State] int) + [State] int NOT NULL) DECLARE @CurrentMessage nvarchar(max) DECLARE @CurrentSeverity int DECLARE @CurrentState int DECLARE @tmpDatabases TABLE (ID int IDENTITY, - DatabaseName nvarchar(max), - DatabaseType nvarchar(max), + DatabaseName nvarchar(128), + DatabaseType nvarchar(1), AvailabilityGroup bit, [Snapshot] bit, StartPosition int, LastCommandTime datetime2, DatabaseSize bigint, LastGoodCheckDbTime datetime2, - [Order] int, - Selected bit, - Completed bit, - PRIMARY KEY(Selected, Completed, [Order], ID)) + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) DECLARE @tmpAvailabilityGroups TABLE (ID int IDENTITY PRIMARY KEY, - AvailabilityGroupName nvarchar(max), + AvailabilityGroupName nvarchar(128), StartPosition int, - Selected bit) + Selected bit DEFAULT 0) - DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(max), - AvailabilityGroupName nvarchar(max)) + DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(128), + AvailabilityGroupName nvarchar(128)) DECLARE @tmpFileGroups TABLE (ID int IDENTITY, FileGroupID int, - FileGroupName nvarchar(max), + FileGroupName nvarchar(128), StartPosition int, - [Order] int, - Selected bit, - Completed bit, - PRIMARY KEY(Selected, Completed, [Order], ID)) + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) DECLARE @tmpObjects TABLE (ID int IDENTITY, SchemaID int, - SchemaName nvarchar(max), + SchemaName nvarchar(128), ObjectID int, - ObjectName nvarchar(max), - ObjectType nvarchar(max), + ObjectName nvarchar(128), + ObjectType nvarchar(2), StartPosition int, - [Order] int, - Selected bit, - Completed bit, - PRIMARY KEY(Selected, Completed, [Order], ID)) + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) DECLARE @SelectedDatabases TABLE (DatabaseName nvarchar(max), - DatabaseType nvarchar(max), - AvailabilityGroup nvarchar(max), + DatabaseType nvarchar(1), + AvailabilityGroup bit, StartPosition int, Selected bit) @@ -180,66 +200,108 @@ BEGIN 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)) + DECLARE @ProductVersion nvarchar(max) = CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + DECLARE @ProductUpdateType nvarchar(max) = CAST(SERVERPROPERTY('ProductUpdateType') AS nvarchar(max)) + DECLARE @EngineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int) + DECLARE @Edition nvarchar(max) = CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + DECLARE @IsHadrEnabled bit = CAST(SERVERPROPERTY('IsHadrEnabled') AS bit) + DECLARE @IsClustered bit = CAST(SERVERPROPERTY('IsClustered') AS bit) + DECLARE @ServerName nvarchar(max) = CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) - IF @Version >= 14 + DECLARE @Version numeric(18,10) = CAST(PARSENAME(@ProductVersion,4) + '.' + PARSENAME(@ProductVersion,3) + PARSENAME(@ProductVersion,2) AS numeric(18,10)) + + IF @EngineEdition = 8 AND @ProductVersion = '12.0.2000.8' AND @ProductUpdateType = 'CU' + BEGIN + SET @Version = 16.01000 + END + + IF @EngineEdition <> 5 BEGIN SELECT @HostPlatform = host_platform FROM sys.dm_os_host_info END - ELSE + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) BEGIN - SET @HostPlatform = 'Windows' + SET @CurrentCommand = 'SELECT @ParamContainedAvailabilityGroupID = contained_availability_group_id FROM sys.dm_exec_sessions WHERE session_id = @@SPID' + + EXECUTE sp_executesql @stmt = @CurrentCommand, @params = N'@ParamContainedAvailabilityGroupID uniqueidentifier OUTPUT', @ParamContainedAvailabilityGroupID = @ContainedAvailabilityGroupID OUTPUT 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 + SET @ContainedAvailabilityGroupListenerConnection = CASE WHEN @ContainedAvailabilityGroupID IS NOT NULL THEN 1 ELSE 0 END + + DECLARE @AmazonRDS bit = CASE WHEN @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) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Databases', @Databases) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@CheckCommands', @CheckCommands) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@PhysicalOnly', @PhysicalOnly) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataPurity', @DataPurity) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@NoIndex', @NoIndex) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ExtendedLogicalChecks', @ExtendedLogicalChecks) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@NoInformationalMessages', @NoInformationalMessages) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@TabLock', @TabLock) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FileGroups', @FileGroups) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Objects', @Objects) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MaxDOP', @MaxDOP) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroups', @AvailabilityGroups) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroupReplicas', @AvailabilityGroupReplicas) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Updateability', @Updateability) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@TimeLimit', @TimeLimit) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@LockTimeout', @LockTimeout) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@LockMessageSeverity', @LockMessageSeverity) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StringDelimiter', @StringDelimiter) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabaseOrder', @DatabaseOrder) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabasesInParallel', @DatabasesInParallel) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@LogToTable', @LogToTable) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Execute', @Execute) + + SELECT @ParametersString = STRING_AGG(CAST([Name] + ' = ' + CASE WHEN ValueNvarchar IS NOT NULL THEN '''' + REPLACE(ValueNvarchar,'''','''''') + '''' WHEN ValueInt IS NOT NULL THEN CAST(ValueInt AS nvarchar(max)) WHEN ValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), ValueDatetime, 21) + '''' ELSE 'NULL' END AS nvarchar(max)), ', ') WITHIN GROUP (ORDER BY [ID] ASC) + FROM @Parameters + + SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar(max),@StartTime,120) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Server: ' + CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) + SET @StartMessage = 'Server: ' + @ServerName RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Version: ' + CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + SET @StartMessage = 'Version: ' + @ProductVersion RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Edition: ' + CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + SET @StartMessage = 'Edition: ' + @Edition RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Platform: ' + @HostPlatform - RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + IF @EngineEdition = 8 + BEGIN + SET @StartMessage = 'Update type: ' + @ProductUpdateType + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @EngineEdition <> 5 + BEGIN + SET @StartMessage = 'Platform: ' + ISNULL(@HostPlatform, 'N/A') + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END - SET @StartMessage = 'Procedure: ' + QUOTENAME(DB_NAME()) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) + BEGIN + SET @StartMessage = 'Contained availability group connection: ' + CASE WHEN @ContainedAvailabilityGroupListenerConnection = 1 THEN 'Yes' WHEN @ContainedAvailabilityGroupListenerConnection = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @EngineEdition <> 5 + BEGIN + SET @StartMessage = 'Is sysadmin: ' + CASE WHEN @IsSysadmin = 1 THEN 'Yes' WHEN @IsSysadmin = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + SET @StartMessage = 'Database: ' + QUOTENAME(DB_NAME()) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Parameters: ' + @Parameters + SET @StartMessage = 'Procedure: ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT SET @StartMessage = 'Version: ' + @VersionTimestamp @@ -247,6 +309,32 @@ BEGIN SET @StartMessage = 'Source: https://ola.hallengren.com' RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + + SET @StartMessage = 'Command:' + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'EXECUTE ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + DECLARE ParameterCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Name], ValueNvarchar, ValueInt, ValueDatetime, CASE WHEN [ID] = MAX([ID]) OVER() THEN '' ELSE ',' END FROM @Parameters ORDER BY [ID] ASC + + OPEN ParameterCursor + + FETCH ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + + WHILE @@FETCH_STATUS = 0 + BEGIN + SET @CurrentParameterMessage = @CurrentParameterName + ' = ' + CASE WHEN @CurrentParameterValueNvarchar IS NOT NULL THEN '''' + REPLACE(@CurrentParameterValueNvarchar,'''','''''') + '''' WHEN @CurrentParameterValueInt IS NOT NULL THEN CAST(@CurrentParameterValueInt AS nvarchar(max)) WHEN @CurrentParameterValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), @CurrentParameterValueDatetime, 21) + '''' ELSE 'NULL' END + @CurrentParameterDelimiter + + RAISERROR('%s',10,1,@CurrentParameterMessage) WITH NOWAIT + + FETCH NEXT FROM ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + END + + CLOSE ParameterCursor + + DEALLOCATE ParameterCursor RAISERROR(@EmptyLine,10,1) WITH NOWAIT @@ -254,58 +342,52 @@ BEGIN --// 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 + VALUES('ANSI_NULLS has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-integrity-check.html.', 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 + VALUES('QUOTED_IDENTIFIER has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-integrity-check.html.', 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 + VALUES('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 + VALUES('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 + VALUES('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 + VALUES('The table Queue is missing. It is required when @DatabasesInParallel = ''Y''. 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 + VALUES('The table QueueDatabase is missing. It is required when @DatabasesInParallel = ''Y''. 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 + VALUES('The stored procedure cannot be executed inside a transaction. The transaction count (@@TRANCOUNT) has to be 0. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -367,11 +449,10 @@ BEGIN FROM Databases4 OPTION (MAXRECURSION 0) - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN - INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName, Selected) - SELECT name AS AvailabilityGroupName, - 0 AS Selected + INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName) + SELECT name AS AvailabilityGroupName FROM sys.availability_groups INSERT INTO @tmpDatabasesAvailabilityGroups (DatabaseName, AvailabilityGroupName) @@ -382,14 +463,11 @@ BEGIN 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) + INSERT INTO @tmpDatabases (DatabaseName, DatabaseType, AvailabilityGroup, [Snapshot]) 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 + CASE WHEN source_database_id IS NOT NULL THEN 1 ELSE 0 END AS [Snapshot] FROM sys.databases ORDER BY [name] ASC @@ -401,7 +479,7 @@ BEGIN SET tmpDatabases.Selected = SelectedDatabases.Selected FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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) @@ -411,7 +489,7 @@ BEGIN SET tmpDatabases.Selected = SelectedDatabases.Selected FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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) @@ -423,7 +501,7 @@ BEGIN INNER JOIN (SELECT tmpDatabases.DatabaseName, MIN(SelectedDatabases.StartPosition) AS StartPosition FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -433,14 +511,14 @@ BEGIN 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 + VALUES('The value for the parameter @Databases is not supported. The value could not be parsed into a list of databases. See https://ola.hallengren.com/sql-server-integrity-check.html#Databases.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Select availability groups //-- ---------------------------------------------------------------------------------------------------- - IF @AvailabilityGroups IS NOT NULL AND @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 1 BEGIN SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, CHAR(10), '') @@ -494,14 +572,14 @@ BEGIN SET tmpAvailabilityGroups.Selected = SelectedAvailabilityGroups.Selected FROM @tmpAvailabilityGroups tmpAvailabilityGroups INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups - ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(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,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'[','[[]'),'_','[_]') WHERE SelectedAvailabilityGroups.Selected = 0 UPDATE tmpAvailabilityGroups @@ -510,7 +588,7 @@ BEGIN INNER JOIN (SELECT tmpAvailabilityGroups.AvailabilityGroupName, MIN(SelectedAvailabilityGroups.StartPosition) AS StartPosition FROM @tmpAvailabilityGroups tmpAvailabilityGroups INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups - ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'[','[[]'),'_','[_]') WHERE SelectedAvailabilityGroups.Selected = 1 GROUP BY tmpAvailabilityGroups.AvailabilityGroupName) SelectedAvailabilityGroups2 ON tmpAvailabilityGroups.AvailabilityGroupName = SelectedAvailabilityGroups2.AvailabilityGroupName @@ -525,22 +603,28 @@ BEGIN 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) + IF @AvailabilityGroups IS NOT NULL AND (NOT EXISTS(SELECT * FROM @SelectedAvailabilityGroups) OR EXISTS(SELECT * FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName IS NULL OR AvailabilityGroupName = '')) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroups is not supported.', 16, 1 + VALUES('The value for the parameter @AvailabilityGroups is not supported. The value could not be parsed into a list of availability groups. See https://ola.hallengren.com/sql-server-integrity-check.html#AvailabilityGroups.', 16, 1) + END + + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 0 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @AvailabilityGroups can only be used when availability groups are enabled on the instance. See https://ola.hallengren.com/sql-server-integrity-check.html#AvailabilityGroups.', 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 + VALUES('You need to specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) 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 + VALUES('You can only specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -679,31 +763,31 @@ BEGIN 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 + VALUES('The value for the parameter @CheckCommands is not supported. Supported values are CHECKDB, CHECKFILEGROUP, CHECKALLOC, CHECKTABLE and CHECKCATALOG. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 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 + VALUES('The value for the parameter @CheckCommands is not supported. The same check command has been specified more than once. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 16, 1) 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 + VALUES('The value for the parameter @CheckCommands is not supported. The value cannot be NULL or empty. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 16, 1) 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 + VALUES('The value for the parameter @CheckCommands is not supported. CHECKDB cannot be combined with CHECKFILEGROUP, CHECKALLOC, CHECKTABLE or CHECKCATALOG. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 16, 1) 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 + VALUES('The value for the parameter @CheckCommands is not supported. CHECKFILEGROUP cannot be combined with CHECKALLOC or CHECKTABLE. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -711,7 +795,7 @@ BEGIN 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 + VALUES('The value for the parameter @PhysicalOnly is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#PhysicalOnly.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -719,13 +803,13 @@ BEGIN 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 + VALUES('The value for the parameter @DataPurity is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#DataPurity.', 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 + VALUES('The parameters @PhysicalOnly and @DataPurity cannot be used together. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -733,7 +817,7 @@ BEGIN 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 + VALUES('The value for the parameter @NoIndex is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#NoIndex.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -741,13 +825,13 @@ BEGIN 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 + VALUES('The value for the parameter @ExtendedLogicalChecks is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#ExtendedLogicalChecks.', 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 + VALUES('The parameters @PhysicalOnly and @ExtendedLogicalChecks cannot be used together. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -755,7 +839,7 @@ BEGIN 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 + VALUES('The value for the parameter @NoInformationalMessages is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#NoInformationalMessages.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -763,7 +847,7 @@ BEGIN 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 + VALUES('The value for the parameter @TabLock is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#TabLock.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -771,19 +855,19 @@ BEGIN 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 + VALUES('The value for the parameter @FileGroups is not supported. The value contains one or more items that could not be parsed. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 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 + VALUES('The value for the parameter @FileGroups is not supported. The value could not be parsed into a list of filegroups. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 16, 1) 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 + VALUES('The parameter @FileGroups can only be used together with @CheckCommands = ''CHECKFILEGROUP''. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -791,19 +875,19 @@ BEGIN 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 + VALUES('The value for the parameter @Objects is not supported. The value contains one or more items that could not be parsed. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 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 + VALUES('The value for the parameter @Objects is not supported. The value could not be parsed into a list of objects. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 16, 1) 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 + VALUES('The parameter @Objects can only be used together with @CheckCommands = ''CHECKTABLE''. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -811,13 +895,7 @@ BEGIN 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 + VALUES('The value for the parameter @MaxDOP is not supported. The value has to be between 0 and 64. See https://ola.hallengren.com/sql-server-integrity-check.html#MaxDOP.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -825,7 +903,7 @@ BEGIN 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 + VALUES('The value for the parameter @AvailabilityGroupReplicas is not supported. Supported values are ALL, PRIMARY, SECONDARY and PREFERRED_BACKUP_REPLICA. See https://ola.hallengren.com/sql-server-integrity-check.html#AvailabilityGroupReplicas.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -833,7 +911,7 @@ BEGIN 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 + VALUES('The value for the parameter @Updateability is not supported. Supported values are ALL, READ_ONLY and READ_WRITE. See https://ola.hallengren.com/sql-server-integrity-check.html#Updateability.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -841,31 +919,31 @@ BEGIN IF @TimeLimit < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @TimeLimit is not supported.', 16, 1 + VALUES('The value for the parameter @TimeLimit is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-integrity-check.html#TimeLimit.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @LockTimeout < 0 + IF @LockTimeout < 0 OR @LockTimeout > 86400 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @LockTimeout is not supported.', 16, 1 + VALUES('The value for the parameter @LockTimeout is not supported. The value has to be between 0 and 86400. See https://ola.hallengren.com/sql-server-integrity-check.html#LockTimeout.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @LockMessageSeverity NOT IN(10, 16) + 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 + VALUES('The value for the parameter @LockMessageSeverity is not supported. Supported values are 10 and 16. See https://ola.hallengren.com/sql-server-integrity-check.html#LockMessageSeverity.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) > 1 + 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 + VALUES('The value for the parameter @StringDelimiter is not supported. The value has to be exactly one character. See https://ola.hallengren.com/sql-server-integrity-check.html#StringDelimiter.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -873,31 +951,31 @@ BEGIN 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 + VALUES('The value for the parameter @DatabaseOrder is not supported. Supported values are 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 and REPLICA_LAST_GOOD_CHECK_DESC. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 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) + IF @DatabaseOrder IN('DATABASE_LAST_GOOD_CHECK_ASC','DATABASE_LAST_GOOD_CHECK_DESC') AND NOT (@Version >= 14.03029 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) 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 + VALUES('The value for the parameter @DatabaseOrder is not supported. DATABASEPROPERTYEX(''DatabaseName'', ''LastGoodCheckDbTime'') is not available in this version of SQL Server. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 16, 1) 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 + VALUES('The value for the parameter @DatabaseOrder is not supported. You need to provide the parameter @LogToTable = ''Y''. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 16, 1) 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 + VALUES('The value for the parameter @DatabaseOrder is not supported. You need to provide the parameter @CheckCommands = ''CHECKDB''. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 16, 1) END - IF @DatabaseOrder IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 5 + IF @DatabaseOrder IS NOT NULL AND @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 + VALUES('The parameter @DatabaseOrder is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -905,13 +983,13 @@ BEGIN 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 + VALUES('The value for the parameter @DatabasesInParallel is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabasesInParallel.', 16, 1) END - IF @DatabasesInParallel = 'Y' AND SERVERPROPERTY('EngineEdition') = 5 + IF @DatabasesInParallel = 'Y' AND @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 + VALUES('The parameter @DatabasesInParallel is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabasesInParallel.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -919,7 +997,7 @@ BEGIN 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 + VALUES('The value for the parameter @LogToTable is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#LogToTable.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -927,22 +1005,15 @@ BEGIN 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 + VALUES('The value for the parameter @Execute is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#Execute.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Check that selected databases and availability groups exist //-- ---------------------------------------------------------------------------------------------------- - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedDatabases WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) @@ -950,10 +1021,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#Databases.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedFileGroups WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) @@ -961,10 +1033,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following databases in the @FileGroups parameter do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases in the @FileGroups parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedObjects WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) @@ -972,10 +1045,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following databases in the @Objects parameter do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases in the @Objects parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(AvailabilityGroupName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(AvailabilityGroupName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY AvailabilityGroupName ASC) FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName NOT LIKE '%[%]%' AND AvailabilityGroupName NOT IN (SELECT AvailabilityGroupName FROM @tmpAvailabilityGroups) @@ -983,10 +1057,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following availability groups do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following availability groups do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#AvailabilityGroups.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedFileGroups WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName IN (SELECT DatabaseName FROM @tmpDatabases) @@ -995,10 +1070,11 @@ BEGIN IF @ErrorMessage IS NOT NULL 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: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases have been selected in the @FileGroups parameter, but not in the @Databases or @AvailabilityGroups parameters: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedObjects WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName IN (SELECT DatabaseName FROM @tmpDatabases) @@ -1007,24 +1083,24 @@ BEGIN IF @ErrorMessage IS NOT NULL 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: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases have been selected in the @Objects parameter, but not in the @Databases or @AvailabilityGroups parameters: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 10, 1) END ---------------------------------------------------------------------------------------------------- --// Check @@SERVERNAME //-- ---------------------------------------------------------------------------------------------------- - IF UPPER(@@SERVERNAME) <> UPPER(CAST(SERVERPROPERTY('ServerName') AS nvarchar(max))) AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF UPPER(@@SERVERNAME) <> UPPER(@ServerName) AND @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 + VALUES('The @@SERVERNAME does not match SERVERPROPERTY(''ServerName''). See ' + CASE WHEN @IsClustered = 0 THEN 'https://learn.microsoft.com/en-us/sql/database-engine/install-windows/rename-a-computer-that-hosts-a-stand-alone-instance-of-sql-server' WHEN @IsClustered = 1 THEN 'https://learn.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 + DECLARE ErrorCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Message], Severity, [State] FROM @Errors ORDER BY [ID] ASC OPEN ErrorCursor @@ -1191,7 +1267,7 @@ BEGIN FROM dbo.[Queue] WHERE SchemaName = @SchemaName AND ObjectName = @ObjectName - AND [Parameters] = @Parameters + AND [Parameters] = @ParametersString IF @QueueID IS NULL BEGIN @@ -1201,12 +1277,12 @@ BEGIN FROM dbo.[Queue] WITH (UPDLOCK, HOLDLOCK) WHERE SchemaName = @SchemaName AND ObjectName = @ObjectName - AND [Parameters] = @Parameters + AND [Parameters] = @ParametersString IF @QueueID IS NULL BEGIN INSERT INTO dbo.[Queue] (SchemaName, ObjectName, [Parameters]) - SELECT @SchemaName, @ObjectName, @Parameters + VALUES(@SchemaName, @ObjectName, @ParametersString) SET @QueueID = SCOPE_IDENTITY() END @@ -1240,17 +1316,17 @@ BEGIN DatabaseName FROM @tmpDatabases tmpDatabases WHERE Selected = 1 - AND NOT EXISTS (SELECT * FROM dbo.QueueDatabase WHERE DatabaseName = tmpDatabases.DatabaseName AND QueueID = @QueueID) + AND NOT EXISTS (SELECT * FROM dbo.QueueDatabase WHERE DatabaseName COLLATE DATABASE_DEFAULT = 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) + AND NOT EXISTS (SELECT * FROM @tmpDatabases tmpDatabases WHERE DatabaseName = QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT AND Selected = 1) UPDATE QueueDatabase SET DatabaseOrder = tmpDatabases.[Order] FROM dbo.QueueDatabase QueueDatabase - INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName = tmpDatabases.DatabaseName + INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT = tmpDatabases.DatabaseName WHERE QueueID = @QueueID END @@ -1267,7 +1343,7 @@ BEGIN BEGIN ROLLBACK TRANSACTION END - SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar) + ', ' + ISNULL(ERROR_MESSAGE(),'') + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + ISNULL(ERROR_MESSAGE(),'') RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT SET @ReturnCode = ERROR_NUMBER() @@ -1281,7 +1357,7 @@ BEGIN ---------------------------------------------------------------------------------------------------- WHILE (1 = 1) - BEGIN + BEGIN -- Start of database loop IF @DatabasesInParallel = 'Y' BEGIN @@ -1328,13 +1404,13 @@ BEGIN IF @@ROWCOUNT = 0 BEGIN - BREAK + BREAK END SET @CurrentDatabase_sp_executesql = QUOTENAME(@CurrentDatabaseName) + '.sys.sp_executesql' BEGIN - SET @DatabaseMessage = 'Date and time: ' + CONVERT(nvarchar,SYSDATETIME(),120) + SET @DatabaseMessage = 'Date and time: ' + CONVERT(nvarchar(max),SYSDATETIME(),120) RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT SET @DatabaseMessage = 'Database: ' + QUOTENAME(@CurrentDatabaseName) @@ -1366,21 +1442,49 @@ BEGIN RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT END - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN - SELECT @CurrentReplicaID = databases.replica_id + SELECT @CurrentAvailabilityGroupReplicaID = 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 + SELECT @CurrentAvailabilityGroupID = group_id FROM sys.availability_replicas - WHERE replica_id = @CurrentReplicaID + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + + IF @CurrentAvailabilityGroupReplicaID IS NULL AND @CurrentAvailabilityGroupID IS NULL AND @Version >= 16 + BEGIN + SET @CurrentAvailabilityGroupID = @ContainedAvailabilityGroupID + + SELECT @CurrentAvailabilityGroupReplicaID = replica_id + FROM sys.dm_hadr_availability_replica_states + WHERE group_id = @CurrentAvailabilityGroupID + AND is_local = 1 + END + + SELECT @CurrentSecondaryRoleAllowConnections = secondary_role_allow_connections_desc + FROM sys.availability_replicas + WHERE replica_id = @CurrentAvailabilityGroupReplicaID SELECT @CurrentAvailabilityGroupRole = role_desc FROM sys.dm_hadr_availability_replica_states - WHERE replica_id = @CurrentReplicaID + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND database_id = DB_ID(@CurrentDatabaseName) + + IF @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState IS NULL AND @ContainedAvailabilityGroupListenerConnection = 1 + BEGIN + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND DB_NAME(database_id) = @CurrentDatabaseName + END SELECT @CurrentAvailabilityGroup = [name], @CurrentAvailabilityGroupBackupPreference = UPPER(automated_backup_preference_desc) @@ -1388,12 +1492,27 @@ BEGIN WHERE group_id = @CurrentAvailabilityGroupID END - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 AND @CurrentAvailabilityGroup IS NOT NULL AND @AvailabilityGroupReplicas = 'PREFERRED_BACKUP_REPLICA' + IF @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 + IF @IsHadrEnabled = 1 AND @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SELECT @CurrentDistributedAvailabilityGroup = availability_groups.[name], + @CurrentDistributedAvailabilityGroupReplicaID = availability_replicas.replica_id + FROM sys.availability_groups availability_groups + INNER JOIN sys.availability_replicas availability_replicas ON availability_groups.group_id = availability_replicas.group_id + INNER JOIN sys.availability_groups availability_groups_local ON availability_replicas.replica_server_name = availability_groups_local.[name] + WHERE availability_groups.is_distributed = 1 + AND availability_groups_local.group_id = @CurrentAvailabilityGroupID + + SELECT @CurrentDistributedAvailabilityGroupRole = dm_hadr_availability_replica_states.role_desc + FROM sys.dm_hadr_availability_replica_states dm_hadr_availability_replica_states + WHERE dm_hadr_availability_replica_states.replica_id = @CurrentDistributedAvailabilityGroupReplicaID + END + + IF @EngineEdition <> 5 BEGIN SELECT @CurrentDatabaseMirroringRole = UPPER(mirroring_role_desc) FROM sys.database_mirroring database_mirroring @@ -1409,6 +1528,12 @@ BEGIN 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 + IF @CurrentAvailabilityGroupRole = 'SECONDARY' BEGIN SET @DatabaseMessage = 'Readable Secondary: ' + ISNULL(@CurrentSecondaryRoleAllowConnections,'N/A') @@ -1425,6 +1550,21 @@ BEGIN END END + IF @CurrentDistributedAvailabilityGroup IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Distributed availability group: ' + ISNULL(@CurrentDistributedAvailabilityGroup,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Distributed availability group role: ' + ISNULL(@CurrentDistributedAvailabilityGroupRole,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Replica role in distributed availability group: ' + CASE WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Global primary' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Forwarder' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in secondary availability group' + WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in primary availability group' ELSE 'N/A' END + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + END + IF @CurrentDatabaseMirroringRole IS NOT NULL BEGIN SET @DatabaseMessage = 'Database mirroring role: ' + @CurrentDatabaseMirroringRole @@ -1435,7 +1575,7 @@ BEGIN IF @CurrentDatabaseState IN('ONLINE','EMERGENCY') AND NOT (@CurrentUserAccess = 'SINGLE_USER') - AND (@CurrentAvailabilityGroupRole = 'PRIMARY' OR @CurrentAvailabilityGroupRole IS NULL OR SERVERPROPERTY('EngineEdition') = 3) + AND (@CurrentAvailabilityGroupRole = 'PRIMARY' OR (@CurrentAvailabilityGroupRole = 'SECONDARY' AND @EngineEdition = 3) OR @CurrentAvailabilityGroup IS NULL) 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') @@ -1445,12 +1585,12 @@ 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 @CurrentDatabaseContext = CASE WHEN @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) + '; ' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' SET @CurrentCommand += 'DBCC CHECKDB (' + QUOTENAME(@CurrentDatabaseName) IF @NoIndex = 'Y' SET @CurrentCommand += ', NOINDEX' SET @CurrentCommand += ') WITH ALL_ERRORMSGS' @@ -1459,7 +1599,7 @@ BEGIN 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) + IF @MaxDOP IS NOT NULL SET @CurrentCommand += ', MAXDOP = ' + CAST(@MaxDOP AS nvarchar(max)) EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @LogToTable = @LogToTable, @Execute = @Execute SET @Error = @@ERROR @@ -1472,9 +1612,9 @@ BEGIN 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' + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT data_space_id AS FileGroupID, name AS FileGroupName 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) + INSERT INTO @tmpFileGroups (FileGroupID, FileGroupName) EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand SET @Error = @@ERROR IF @Error <> 0 SET @ReturnCode = @Error @@ -1491,14 +1631,14 @@ BEGIN 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,'_','[_]') + ON @CurrentDatabaseName LIKE REPLACE(REPLACE(SelectedFileGroups.DatabaseName,'[','[[]'),'_','[_]') AND tmpFileGroups.FileGroupName LIKE REPLACE(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,'_','[_]') + ON @CurrentDatabaseName LIKE REPLACE(REPLACE(SelectedFileGroups.DatabaseName,'[','[[]'),'_','[_]') AND tmpFileGroups.FileGroupName LIKE REPLACE(REPLACE(SelectedFileGroups.FileGroupName,'[','[[]'),'_','[_]') WHERE SelectedFileGroups.Selected = 0 UPDATE tmpFileGroups @@ -1507,7 +1647,7 @@ BEGIN 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,'_','[_]') + ON @CurrentDatabaseName LIKE REPLACE(REPLACE(SelectedFileGroups.DatabaseName,'[','[[]'),'_','[_]') AND tmpFileGroups.FileGroupName LIKE REPLACE(REPLACE(SelectedFileGroups.FileGroupName,'[','[[]'),'_','[_]') WHERE SelectedFileGroups.Selected = 1 GROUP BY tmpFileGroups.FileGroupName) SelectedFileGroups2 ON tmpFileGroups.FileGroupName = SelectedFileGroups2.FileGroupName @@ -1521,7 +1661,8 @@ BEGIN UPDATE tmpFileGroups SET [Order] = RowNumber - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName) + '.' + QUOTENAME(FileGroupName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)) + '.' + QUOTENAME(FileGroupName), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC, FileGroupName ASC) FROM @SelectedFileGroups SelectedFileGroups WHERE DatabaseName = @CurrentDatabaseName AND FileGroupName NOT LIKE '%[%]%' @@ -1531,7 +1672,6 @@ BEGIN BEGIN SET @ErrorMessage = 'The following file groups do not exist: ' + @ErrorMessage + '.' RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT - SET @Error = @@ERROR RAISERROR(@EmptyLine,10,1) WITH NOWAIT END @@ -1552,7 +1692,7 @@ BEGIN -- Does the filegroup exist? SET @CurrentCommand = '' - IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar) + '; ' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' 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 @@ -1561,7 +1701,7 @@ BEGIN 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 @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 @@ -1579,14 +1719,14 @@ BEGIN SET @CurrentCommandType = 'DBCC_CHECKFILEGROUP' SET @CurrentCommand = '' - IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar) + '; ' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' 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) + IF @MaxDOP IS NOT NULL SET @CurrentCommand += ', MAXDOP = ' + CAST(@MaxDOP AS nvarchar(max)) EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @LogToTable = @LogToTable, @Execute = @Execute SET @Error = @@ERROR @@ -1615,13 +1755,14 @@ BEGIN -- 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 @CurrentDatabaseContext = CASE WHEN @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) + '; ' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' SET @CurrentCommand += 'DBCC CHECKALLOC (' + QUOTENAME(@CurrentDatabaseName) + IF @NoIndex = 'Y' SET @CurrentCommand += ', NOINDEX' SET @CurrentCommand += ') WITH ALL_ERRORMSGS' IF @NoInformationalMessages = 'Y' SET @CurrentCommand += ', NO_INFOMSGS' IF @TabLock = 'Y' SET @CurrentCommand += ', TABLOCK' @@ -1637,9 +1778,9 @@ BEGIN 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' + 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 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) AND (tables.is_memory_optimized = 0 OR is_memory_optimized IS NULL) ORDER BY schemas.name ASC, objects.name ASC' - INSERT INTO @tmpObjects (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, [Order], Selected, Completed) + INSERT INTO @tmpObjects (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType) EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand SET @Error = @@ERROR IF @Error <> 0 SET @ReturnCode = @Error @@ -1656,14 +1797,14 @@ BEGIN 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,'_','[_]') + ON @CurrentDatabaseName LIKE REPLACE(REPLACE(SelectedObjects.DatabaseName,'[','[[]'),'_','[_]') AND tmpObjects.SchemaName LIKE REPLACE(REPLACE(SelectedObjects.SchemaName,'[','[[]'),'_','[_]') AND tmpObjects.ObjectName LIKE REPLACE(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,'_','[_]') + ON @CurrentDatabaseName LIKE REPLACE(REPLACE(SelectedObjects.DatabaseName,'[','[[]'),'_','[_]') AND tmpObjects.SchemaName LIKE REPLACE(REPLACE(SelectedObjects.SchemaName,'[','[[]'),'_','[_]') AND tmpObjects.ObjectName LIKE REPLACE(REPLACE(SelectedObjects.ObjectName,'[','[[]'),'_','[_]') WHERE SelectedObjects.Selected = 0 UPDATE tmpObjects @@ -1672,7 +1813,7 @@ BEGIN 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,'_','[_]') + ON @CurrentDatabaseName LIKE REPLACE(REPLACE(SelectedObjects.DatabaseName,'[','[[]'),'_','[_]') AND tmpObjects.SchemaName LIKE REPLACE(REPLACE(SelectedObjects.SchemaName,'[','[[]'),'_','[_]') AND tmpObjects.ObjectName LIKE REPLACE(REPLACE(SelectedObjects.ObjectName,'[','[[]'),'_','[_]') WHERE SelectedObjects.Selected = 1 GROUP BY tmpObjects.SchemaName, tmpObjects.ObjectName) SelectedObjects2 ON tmpObjects.SchemaName = SelectedObjects2.SchemaName AND tmpObjects.ObjectName = SelectedObjects2.ObjectName @@ -1686,7 +1827,8 @@ BEGIN UPDATE tmpObjects SET [Order] = RowNumber - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName) + '.' + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)) + '.' + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC, SchemaName ASC, ObjectName ASC) FROM @SelectedObjects SelectedObjects WHERE DatabaseName = @CurrentDatabaseName AND SchemaName NOT LIKE '%[%]%' @@ -1697,7 +1839,6 @@ BEGIN BEGIN SET @ErrorMessage = 'The following objects do not exist: ' + @ErrorMessage + '.' RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT - SET @Error = @@ERROR RAISERROR(@EmptyLine,10,1) WITH NOWAIT END @@ -1721,8 +1862,8 @@ BEGIN -- 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' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + 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) AND (tables.is_memory_optimized = 0 OR is_memory_optimized IS NULL) 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 @@ -1730,7 +1871,7 @@ BEGIN 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 @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 @@ -1748,8 +1889,8 @@ BEGIN 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 (N' + QUOTENAME(QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName),'''') + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + SET @CurrentCommand += 'DBCC CHECKTABLE (N''' + REPLACE(QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName),'''','''''') + '''' IF @NoIndex = 'Y' SET @CurrentCommand += ', NOINDEX' SET @CurrentCommand += ') WITH ALL_ERRORMSGS' IF @DataPurity = 'Y' SET @CurrentCommand += ', DATA_PURITY' @@ -1757,7 +1898,7 @@ BEGIN 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) + IF @MaxDOP IS NOT NULL SET @CurrentCommand += ', MAXDOP = ' + CAST(@MaxDOP AS nvarchar(max)) 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 @@ -1787,14 +1928,14 @@ BEGIN END -- Check catalog - IF EXISTS(SELECT * FROM @SelectedCheckCommands WHERE CheckCommand = 'CHECKCATALOG') AND @CurrentAvailabilityGroupRole = 'PRIMARY' AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + IF EXISTS(SELECT * FROM @SelectedCheckCommands WHERE CheckCommand = 'CHECKCATALOG') AND (@CurrentAvailabilityGroupRole = 'PRIMARY' OR @CurrentAvailabilityGroupRole IS NULL) AND (@CurrentDistributedAvailabilityGroupRole = 'PRIMARY' OR @CurrentDistributedAvailabilityGroupRole IS NULL) AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) BEGIN - SET @CurrentDatabaseContext = CASE WHEN SERVERPROPERTY('EngineEdition') = 5 THEN @CurrentDatabaseName ELSE 'master' END + SET @CurrentDatabaseContext = CASE WHEN @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) + '; ' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' SET @CurrentCommand += 'DBCC CHECKCATALOG (' + QUOTENAME(@CurrentDatabaseName) SET @CurrentCommand += ')' IF @NoInformationalMessages = 'Y' SET @CurrentCommand += ' WITH NO_INFOMSGS' @@ -1812,6 +1953,7 @@ BEGIN SET @ErrorMessage = 'The database ' + QUOTENAME(@CurrentDatabaseName) + ' is in a SUSPECT state.' RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT SET @Error = @@ERROR + SET @ReturnCode = @Error RAISERROR(@EmptyLine,10,1) WITH NOWAIT END @@ -1843,14 +1985,18 @@ BEGIN SET @CurrentDatabaseState = NULL SET @CurrentInStandby = NULL SET @CurrentRecoveryModel = NULL - - SET @CurrentReplicaID = NULL + SET @CurrentAvailabilityGroupReplicaID = NULL SET @CurrentAvailabilityGroupID = NULL SET @CurrentAvailabilityGroup = NULL SET @CurrentAvailabilityGroupRole = NULL + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = NULL + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = NULL SET @CurrentAvailabilityGroupBackupPreference = NULL SET @CurrentSecondaryRoleAllowConnections = NULL SET @CurrentIsPreferredBackupReplica = NULL + SET @CurrentDistributedAvailabilityGroup = NULL + SET @CurrentDistributedAvailabilityGroupReplicaID = NULL + SET @CurrentDistributedAvailabilityGroupRole = NULL SET @CurrentDatabaseMirroringRole = NULL SET @CurrentDatabaseContext = NULL @@ -1861,14 +2007,14 @@ BEGIN DELETE FROM @tmpFileGroups DELETE FROM @tmpObjects - END + END -- End of database loop ---------------------------------------------------------------------------------------------------- --// Log completing information //-- ---------------------------------------------------------------------------------------------------- Logging: - SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar,SYSDATETIME(),120) + SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar(max),SYSDATETIME(),120) RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT diff --git a/IndexOptimize.sql b/IndexOptimize.sql index 04a9428c..0311b5cf 100644 --- a/IndexOptimize.sql +++ b/IndexOptimize.sql @@ -9,7 +9,6 @@ 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', @@ -22,20 +21,22 @@ ALTER PROCEDURE [dbo].[IndexOptimize] @MaxDOP int = NULL, @FillFactor int = NULL, @PadIndex nvarchar(max) = NULL, +@DataCompression nvarchar(max) = NULL, +@WaitAtLowPriorityMaxDuration int = NULL, +@WaitAtLowPriorityAbortAfterWait nvarchar(max) = NULL, +@Resumable nvarchar(max) = 'N', @LOBCompaction nvarchar(max) = 'Y', @UpdateStatistics nvarchar(max) = NULL, @OnlyModifiedStatistics nvarchar(max) = 'N', @StatisticsModificationLevel int = NULL, @StatisticsSample int = NULL, +@StatisticsPersistSample nvarchar(max) = 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, @@ -44,6 +45,7 @@ ALTER PROCEDURE [dbo].[IndexOptimize] @DatabasesInParallel nvarchar(max) = 'N', @ExecuteAsUser nvarchar(max) = NULL, @LogToTable nvarchar(max) = 'N', +@MinIndexUsageDays int = NULL, @Execute nvarchar(max) = 'Y' AS @@ -54,7 +56,7 @@ BEGIN --// Source: https://ola.hallengren.com //-- --// License: https://ola.hallengren.com/license.html //-- --// GitHub: https://github.com/olahallengren/sql-server-maintenance-solution //-- - --// Version: 2026-05-18 20:15:56 //-- + --// Version: 2026-08-23 14:46:45 //-- ---------------------------------------------------------------------------------------------------- SET NOCOUNT ON @@ -73,11 +75,25 @@ BEGIN 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 @Parameters TABLE (ID int IDENTITY PRIMARY KEY, + [Name] nvarchar(max) NOT NULL, + ValueNvarchar nvarchar(max), + ValueInt int, + ValueDatetime datetime2) - DECLARE @PartitionLevelStatistics bit + DECLARE @ParametersString nvarchar(max) + DECLARE @CurrentParameterName nvarchar(max) + DECLARE @CurrentParameterValueNvarchar nvarchar(max) + DECLARE @CurrentParameterValueInt int + DECLARE @CurrentParameterValueDatetime datetime2 + DECLARE @CurrentParameterDelimiter nvarchar(max) + DECLARE @CurrentParameterMessage nvarchar(max) + + DECLARE @HostPlatform nvarchar(max) + DECLARE @ContainedAvailabilityGroupID uniqueidentifier + DECLARE @ContainedAvailabilityGroupListenerConnection bit + DECLARE @IsSysadmin bit = IS_SRVROLEMEMBER('sysadmin') DECLARE @QueueID int DECLARE @QueueStartTime datetime2 @@ -93,11 +109,16 @@ BEGIN DECLARE @CurrentDatabaseState nvarchar(max) DECLARE @CurrentInStandby bit DECLARE @CurrentRecoveryModel nvarchar(max) - - DECLARE @CurrentReplicaID uniqueidentifier + DECLARE @CurrentDatabaseHasReadOnlyFileGroup bit + DECLARE @CurrentAvailabilityGroupReplicaID uniqueidentifier DECLARE @CurrentAvailabilityGroupID uniqueidentifier DECLARE @CurrentAvailabilityGroup nvarchar(max) DECLARE @CurrentAvailabilityGroupRole nvarchar(max) + DECLARE @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState nvarchar(max) + DECLARE @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth nvarchar(max) + DECLARE @CurrentDistributedAvailabilityGroup nvarchar(max) + DECLARE @CurrentDistributedAvailabilityGroupReplicaID uniqueidentifier + DECLARE @CurrentDistributedAvailabilityGroupRole nvarchar(max) DECLARE @CurrentDatabaseMirroringRole nvarchar(max) DECLARE @CurrentDatabaseContext nvarchar(max) @@ -110,7 +131,7 @@ BEGIN DECLARE @Errors TABLE (ID int IDENTITY PRIMARY KEY, [Message] nvarchar(max) NOT NULL, Severity int NOT NULL, - [State] int) + [State] int NOT NULL) DECLARE @CurrentMessage nvarchar(max) DECLARE @CurrentSeverity int @@ -131,15 +152,17 @@ BEGIN DECLARE @CurrentStatisticsName nvarchar(max) DECLARE @CurrentPartitionID bigint DECLARE @CurrentPartitionNumber int - DECLARE @CurrentPartitionCount int + DECLARE @CurrentInRowDataPageCount bigint + DECLARE @CurrentAlterIndexCompleted bit + DECLARE @CurrentUpdateStatisticsCompleted bit DECLARE @CurrentIsPartition bit + DECLARE @CurrentIsLastPartition bit DECLARE @CurrentIndexExists bit DECLARE @CurrentStatisticsExists bit DECLARE @CurrentIsImageText bit - DECLARE @CurrentIsNewLOB bit DECLARE @CurrentIsFileStream bit DECLARE @CurrentHasClusteredColumnstore bit - DECLARE @CurrentHasNonClusteredColumnstore bit + DECLARE @CurrentIsColumnstoreOrdered bit DECLARE @CurrentIsComputed bit DECLARE @CurrentIsClusteredIndexComputed bit DECLARE @CurrentIsTimestamp bit @@ -147,6 +170,7 @@ BEGIN DECLARE @CurrentHasFilter bit DECLARE @CurrentNoRecompute bit DECLARE @CurrentIsIncremental bit + DECLARE @CurrentObjectHasRows bit DECLARE @CurrentRowCount bigint DECLARE @CurrentModificationCounter bigint DECLARE @CurrentOnReadOnlyFileGroup bit @@ -158,66 +182,136 @@ BEGIN DECLARE @CurrentMaxDOP int DECLARE @CurrentUpdateStatistics nvarchar(max) DECLARE @CurrentStatisticsSample int + DECLARE @CurrentStatisticsPersistSample nvarchar(max) DECLARE @CurrentStatisticsResample nvarchar(max) DECLARE @CurrentDelay datetime DECLARE @tmpDatabases TABLE (ID int IDENTITY, - DatabaseName nvarchar(max), - DatabaseType nvarchar(max), + DatabaseName nvarchar(128), + DatabaseType nvarchar(1), AvailabilityGroup bit, StartPosition int, DatabaseSize bigint, - [Order] int, - Selected bit, - Completed bit, - PRIMARY KEY(Selected, Completed, [Order], ID)) + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) DECLARE @tmpAvailabilityGroups TABLE (ID int IDENTITY PRIMARY KEY, - AvailabilityGroupName nvarchar(max), + AvailabilityGroupName nvarchar(128), StartPosition int, - Selected bit) + Selected bit DEFAULT 0) - DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(max), - AvailabilityGroupName nvarchar(max)) + DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(128), + AvailabilityGroupName nvarchar(128)) DECLARE @tmpIndexesStatistics TABLE (ID int IDENTITY, SchemaID int, - SchemaName nvarchar(max), + SchemaName nvarchar(128), ObjectID int, - ObjectName nvarchar(max), - ObjectType nvarchar(max), + ObjectName nvarchar(128), + ObjectType nvarchar(2), IsMemoryOptimized bit, IndexID int, - IndexName nvarchar(max), + IndexName nvarchar(128), IndexType int, AllowPageLocks bit, HasFilter bit, IsImageText bit, - IsNewLOB bit, IsFileStream bit, HasClusteredColumnstore bit, - HasNonClusteredColumnstore bit, + IsColumnstoreOrdered bit, IsComputed bit, IsClusteredIndexComputed bit, IsTimestamp bit, OnReadOnlyFileGroup bit, ResumableIndexOperation bit, StatisticsID int, - StatisticsName nvarchar(max), + StatisticsName nvarchar(128), [NoRecompute] bit, IsIncremental bit, PartitionID bigint, PartitionNumber int, - PartitionCount int, + IsPartition bit, + IsLastPartition bit, + InRowDataPageCount bigint, StartPosition int, - [Order] int, - Selected bit, - Completed bit, - PRIMARY KEY(Selected, Completed, [Order], ID)) + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + AlterIndexCompleted bit DEFAULT 0, + UpdateStatisticsCompleted bit DEFAULT 0, + Completed AS CAST(CASE WHEN AlterIndexCompleted = 1 AND UpdateStatisticsCompleted = 1 THEN 1 ELSE 0 END AS bit) PERSISTED, + PRIMARY KEY (Selected, Completed, [Order], ID), + INDEX IX_ObjectID_IndexID_PartitionNumber NONCLUSTERED (ObjectID, IndexID, PartitionNumber), + INDEX IX_ObjectID_StatisticsID_PartitionNumber NONCLUSTERED (ObjectID, StatisticsID, PartitionNumber)) + + DROP TABLE IF EXISTS #SelectedIndexes + + CREATE TABLE #SelectedIndexes (DatabaseName nvarchar(max) COLLATE DATABASE_DEFAULT, + SchemaName nvarchar(max) COLLATE DATABASE_DEFAULT, + ObjectName nvarchar(max) COLLATE DATABASE_DEFAULT, + IndexName nvarchar(max) COLLATE DATABASE_DEFAULT, + StartPosition int, + Selected bit) + + DROP TABLE IF EXISTS #Objects + + CREATE TABLE #Objects (ObjectID int NOT NULL, + SchemaID int, + SchemaName nvarchar(128) COLLATE DATABASE_DEFAULT, + ObjectName nvarchar(128) COLLATE DATABASE_DEFAULT, + ObjectType nvarchar(2) COLLATE DATABASE_DEFAULT, + IsMemoryOptimized bit, + HasClusteredColumnstore bit, + IsClusteredIndexComputed bit, + IsClusteredIndexDisabled bit, + PRIMARY KEY (ObjectID)) + + DROP TABLE IF EXISTS #Indexes + + CREATE TABLE #Indexes (ObjectID int NOT NULL, + IndexID int NOT NULL, + IndexName nvarchar(128) COLLATE DATABASE_DEFAULT, + IndexType int, + DataSpaceID int, + IsPartitioned bit, + AllowPageLocks bit, + HasFilter bit, + IsImageText bit, + IsFileStream bit, + IsColumnstoreOrdered bit, + IsComputed bit, + IsTimestamp bit, + PRIMARY KEY (ObjectID, IndexID)) + + DROP TABLE IF EXISTS #Stats + + CREATE TABLE #Stats (ObjectID int NOT NULL, + StatisticsID int NOT NULL, + StatisticsName nvarchar(128) COLLATE DATABASE_DEFAULT, + [NoRecompute] bit, + IsIncremental bit, + IsIndex bit, + PRIMARY KEY (ObjectID, StatisticsID)) + + DROP TABLE IF EXISTS #ExistingObjects + + CREATE TABLE #ExistingObjects (SchemaName nvarchar(max) COLLATE DATABASE_DEFAULT, + ObjectName nvarchar(max) COLLATE DATABASE_DEFAULT) + + DROP TABLE IF EXISTS #ExistingIndexes + + CREATE TABLE #ExistingIndexes (SchemaName nvarchar(max) COLLATE DATABASE_DEFAULT, + ObjectName nvarchar(max) COLLATE DATABASE_DEFAULT, + IndexName nvarchar(max) COLLATE DATABASE_DEFAULT) + + DECLARE @tmpResumableOperations TABLE (ObjectID int NOT NULL, + IndexID int NOT NULL, + PartitionNumber int) DECLARE @SelectedDatabases TABLE (DatabaseName nvarchar(max), - DatabaseType nvarchar(max), - AvailabilityGroup nvarchar(max), + DatabaseType nvarchar(1), + AvailabilityGroup bit, StartPosition int, Selected bit) @@ -232,6 +326,20 @@ BEGIN StartPosition int, Selected bit) + DECLARE @PhysicalStats TABLE (ObjectID int, + IndexID int, + PartitionNumber int, + FragmentationLevel float, + PageCount bigint, + PRIMARY KEY (ObjectID, IndexID, PartitionNumber)) + + DECLARE @IncrementalStatsProperties TABLE (ObjectID int, + StatisticsID int, + PartitionNumber int, + [Rows] bigint, + ModificationCounter bigint, + PRIMARY KEY (ObjectID, StatisticsID, PartitionNumber)) + DECLARE @Actions TABLE ([Action] nvarchar(max)) INSERT INTO @Actions([Action]) VALUES('INDEX_REBUILD_ONLINE') @@ -255,19 +363,45 @@ BEGIN DECLARE @EmptyLine nvarchar(max) = CHAR(9) + + DECLARE @CurrentStatsResetEstimate datetime2 + DECLARE @CurrentHoursSinceReset int + DECLARE @CurrentStatsTrusted bit + DECLARE @RequiredTrustHours int = @MinIndexUsageDays * 24 + 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 @ProductVersion nvarchar(max) = CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + DECLARE @ProductUpdateType nvarchar(max) = CAST(SERVERPROPERTY('ProductUpdateType') AS nvarchar(max)) + DECLARE @EngineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int) + DECLARE @Edition nvarchar(max) = CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + DECLARE @IsHadrEnabled bit = CAST(SERVERPROPERTY('IsHadrEnabled') AS bit) + DECLARE @ServerName nvarchar(max) = CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) + + DECLARE @Collation nvarchar(128) = CAST(DATABASEPROPERTYEX(DB_NAME(),'Collation') AS nvarchar(128)) + + DECLARE @Version numeric(18,10) = CAST(PARSENAME(@ProductVersion,4) + '.' + PARSENAME(@ProductVersion,3) + PARSENAME(@ProductVersion,2) AS numeric(18,10)) - IF @Version >= 14 + IF @EngineEdition = 8 AND @ProductVersion = '12.0.2000.8' AND @ProductUpdateType = 'CU' + BEGIN + SET @Version = 16.01000 + END + + IF @EngineEdition <> 5 BEGIN SELECT @HostPlatform = host_platform FROM sys.dm_os_host_info END - ELSE + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) BEGIN - SET @HostPlatform = 'Windows' + SET @CurrentCommand = 'SELECT @ParamContainedAvailabilityGroupID = contained_availability_group_id FROM sys.dm_exec_sessions WHERE session_id = @@SPID' + + EXECUTE sp_executesql @stmt = @CurrentCommand, @params = N'@ParamContainedAvailabilityGroupID uniqueidentifier OUTPUT', @ParamContainedAvailabilityGroupID = @ContainedAvailabilityGroupID OUTPUT 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 + SET @ContainedAvailabilityGroupListenerConnection = CASE WHEN @ContainedAvailabilityGroupID IS NOT NULL THEN 1 ELSE 0 END + + DECLARE @AmazonRDS bit = CASE WHEN @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 //-- @@ -307,27 +441,91 @@ BEGIN SET @Parameters += ', @DatabasesInParallel = ' + ISNULL('''' + REPLACE(@DatabasesInParallel,'''','''''') + '''','NULL') SET @Parameters += ', @ExecuteAsUser = ' + ISNULL('''' + REPLACE(@ExecuteAsUser,'''','''''') + '''','NULL') SET @Parameters += ', @LogToTable = ' + ISNULL('''' + REPLACE(@LogToTable,'''','''''') + '''','NULL') + SET @Parameters += ', @MinIndexUsageDays = ' + ISNULL(CAST(@MinIndexUsageDays AS nvarchar),'NULL') SET @Parameters += ', @Execute = ' + ISNULL('''' + REPLACE(@Execute,'''','''''') + '''','NULL') SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar,@StartTime,120) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Databases', @Databases) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FragmentationLow', @FragmentationLow) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FragmentationMedium', @FragmentationMedium) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FragmentationHigh', @FragmentationHigh) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@FragmentationLevel1', @FragmentationLevel1) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@FragmentationLevel2', @FragmentationLevel2) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MinNumberOfPages', @MinNumberOfPages) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MaxNumberOfPages', @MaxNumberOfPages) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@SortInTempdb', @SortInTempdb) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MaxDOP', @MaxDOP) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@FillFactor', @FillFactor) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@PadIndex', @PadIndex) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataCompression', @DataCompression) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@WaitAtLowPriorityMaxDuration', @WaitAtLowPriorityMaxDuration) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@WaitAtLowPriorityAbortAfterWait', @WaitAtLowPriorityAbortAfterWait) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Resumable', @Resumable) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@LOBCompaction', @LOBCompaction) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@UpdateStatistics', @UpdateStatistics) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@OnlyModifiedStatistics', @OnlyModifiedStatistics) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@StatisticsModificationLevel', @StatisticsModificationLevel) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@StatisticsSample', @StatisticsSample) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StatisticsPersistSample', @StatisticsPersistSample) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StatisticsResample', @StatisticsResample) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@PartitionLevel', @PartitionLevel) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@MSShippedObjects', @MSShippedObjects) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Indexes', @Indexes) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@TimeLimit', @TimeLimit) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@Delay', @Delay) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroups', @AvailabilityGroups) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@LockTimeout', @LockTimeout) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@LockMessageSeverity', @LockMessageSeverity) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StringDelimiter', @StringDelimiter) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabaseOrder', @DatabaseOrder) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabasesInParallel', @DatabasesInParallel) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ExecuteAsUser', @ExecuteAsUser) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@LogToTable', @LogToTable) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Execute', @Execute) + + SELECT @ParametersString = STRING_AGG(CAST([Name] + ' = ' + CASE WHEN ValueNvarchar IS NOT NULL THEN '''' + REPLACE(ValueNvarchar,'''','''''') + '''' WHEN ValueInt IS NOT NULL THEN CAST(ValueInt AS nvarchar(max)) WHEN ValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), ValueDatetime, 21) + '''' ELSE 'NULL' END AS nvarchar(max)), ', ') WITHIN GROUP (ORDER BY [ID] ASC) + FROM @Parameters + + SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar(max),@StartTime,120) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Server: ' + CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) + SET @StartMessage = 'Server: ' + @ServerName RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Version: ' + CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + SET @StartMessage = 'Version: ' + @ProductVersion RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Edition: ' + CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + SET @StartMessage = 'Edition: ' + @Edition RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Platform: ' + @HostPlatform - RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + IF @EngineEdition = 8 + BEGIN + SET @StartMessage = 'Update type: ' + @ProductUpdateType + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @EngineEdition <> 5 + BEGIN + SET @StartMessage = 'Platform: ' + ISNULL(@HostPlatform, 'N/A') + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) + BEGIN + SET @StartMessage = 'Contained availability group connection: ' + CASE WHEN @ContainedAvailabilityGroupListenerConnection = 1 THEN 'Yes' WHEN @ContainedAvailabilityGroupListenerConnection = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END - SET @StartMessage = 'Procedure: ' + QUOTENAME(DB_NAME()) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + IF @EngineEdition <> 5 + BEGIN + SET @StartMessage = 'Is sysadmin: ' + CASE WHEN @IsSysadmin = 1 THEN 'Yes' WHEN @IsSysadmin = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + SET @StartMessage = 'Database: ' + QUOTENAME(DB_NAME()) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Parameters: ' + @Parameters + SET @StartMessage = 'Procedure: ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT SET @StartMessage = 'Version: ' + @VersionTimestamp @@ -335,6 +533,32 @@ BEGIN SET @StartMessage = 'Source: https://ola.hallengren.com' RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + + SET @StartMessage = 'Command:' + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'EXECUTE ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + DECLARE ParameterCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Name], ValueNvarchar, ValueInt, ValueDatetime, CASE WHEN [ID] = MAX([ID]) OVER() THEN '' ELSE ',' END FROM @Parameters ORDER BY [ID] ASC + + OPEN ParameterCursor + + FETCH ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + + WHILE @@FETCH_STATUS = 0 + BEGIN + SET @CurrentParameterMessage = @CurrentParameterName + ' = ' + CASE WHEN @CurrentParameterValueNvarchar IS NOT NULL THEN '''' + REPLACE(@CurrentParameterValueNvarchar,'''','''''') + '''' WHEN @CurrentParameterValueInt IS NOT NULL THEN CAST(@CurrentParameterValueInt AS nvarchar(max)) WHEN @CurrentParameterValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), @CurrentParameterValueDatetime, 21) + '''' ELSE 'NULL' END + @CurrentParameterDelimiter + + RAISERROR('%s',10,1,@CurrentParameterMessage) WITH NOWAIT + + FETCH NEXT FROM ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + END + + CLOSE ParameterCursor + + DEALLOCATE ParameterCursor RAISERROR(@EmptyLine,10,1) WITH NOWAIT @@ -342,58 +566,52 @@ BEGIN --// 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 + VALUES('ANSI_NULLS has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 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 + VALUES('QUOTED_IDENTIFIER has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 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 + VALUES('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 + VALUES('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 + VALUES('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 + VALUES('The table Queue is missing. It is required when @DatabasesInParallel = ''Y''. 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 + VALUES('The table QueueDatabase is missing. It is required when @DatabasesInParallel = ''Y''. 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 + VALUES('The stored procedure cannot be executed inside a transaction. The transaction count (@@TRANCOUNT) has to be 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -455,11 +673,10 @@ BEGIN FROM Databases4 OPTION (MAXRECURSION 0) - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN - INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName, Selected) - SELECT name AS AvailabilityGroupName, - 0 AS Selected + INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName) + SELECT name AS AvailabilityGroupName FROM sys.availability_groups INSERT INTO @tmpDatabasesAvailabilityGroups (DatabaseName, AvailabilityGroupName) @@ -470,13 +687,10 @@ BEGIN 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) + INSERT INTO @tmpDatabases (DatabaseName, DatabaseType, AvailabilityGroup) 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 + NULL AS AvailabilityGroup FROM sys.databases WHERE [name] <> 'tempdb' AND source_database_id IS NULL @@ -490,7 +704,7 @@ BEGIN SET tmpDatabases.Selected = SelectedDatabases.Selected FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -499,7 +713,7 @@ BEGIN SET tmpDatabases.Selected = SelectedDatabases.Selected FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -510,7 +724,7 @@ BEGIN INNER JOIN (SELECT tmpDatabases.DatabaseName, MIN(SelectedDatabases.StartPosition) AS StartPosition FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -520,14 +734,14 @@ BEGIN 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 + VALUES('The value for the parameter @Databases is not supported. The value could not be parsed into a list of databases. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Databases.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Select availability groups //-- ---------------------------------------------------------------------------------------------------- - IF @AvailabilityGroups IS NOT NULL AND @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 1 BEGIN SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, CHAR(10), '') @@ -581,14 +795,14 @@ BEGIN SET tmpAvailabilityGroups.Selected = SelectedAvailabilityGroups.Selected FROM @tmpAvailabilityGroups tmpAvailabilityGroups INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups - ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(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,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'[','[[]'),'_','[_]') WHERE SelectedAvailabilityGroups.Selected = 0 UPDATE tmpAvailabilityGroups @@ -597,7 +811,7 @@ BEGIN INNER JOIN (SELECT tmpAvailabilityGroups.AvailabilityGroupName, MIN(SelectedAvailabilityGroups.StartPosition) AS StartPosition FROM @tmpAvailabilityGroups tmpAvailabilityGroups INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups - ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'[','[[]'),'_','[_]') WHERE SelectedAvailabilityGroups.Selected = 1 GROUP BY tmpAvailabilityGroups.AvailabilityGroupName) SelectedAvailabilityGroups2 ON tmpAvailabilityGroups.AvailabilityGroupName = SelectedAvailabilityGroups2.AvailabilityGroupName @@ -612,22 +826,28 @@ BEGIN 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) + IF @AvailabilityGroups IS NOT NULL AND (NOT EXISTS(SELECT * FROM @SelectedAvailabilityGroups) OR EXISTS(SELECT * FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName IS NULL OR AvailabilityGroupName = '')) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @AvailabilityGroups is not supported. The value could not be parsed into a list of availability groups. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#AvailabilityGroups.', 16, 1) + END + + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroups is not supported.', 16, 1 + VALUES('The parameter @AvailabilityGroups can only be used when availability groups are enabled on the instance. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#AvailabilityGroups.', 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 + VALUES('You need to specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) 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 + VALUES('You can only specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -684,6 +904,10 @@ BEGIN FROM Indexes4 OPTION (MAXRECURSION 0) + INSERT INTO #SelectedIndexes (DatabaseName, SchemaName, ObjectName, IndexName, StartPosition, Selected) + SELECT DatabaseName, SchemaName, ObjectName, IndexName, StartPosition, Selected + FROM @SelectedIndexes + ---------------------------------------------------------------------------------------------------- --// Select actions //-- ---------------------------------------------------------------------------------------------------- @@ -761,13 +985,13 @@ BEGIN 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 + VALUES('The value for the parameter @FragmentationLow is not supported. Supported values are INDEX_REBUILD_ONLINE, INDEX_REBUILD_OFFLINE and INDEX_REORGANIZE. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLow.', 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 + VALUES('The value for the parameter @FragmentationLow is not supported. The same action has been specified more than once. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLow.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -775,13 +999,13 @@ BEGIN 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 + VALUES('The value for the parameter @FragmentationMedium is not supported. Supported values are INDEX_REBUILD_ONLINE, INDEX_REBUILD_OFFLINE and INDEX_REORGANIZE. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationMedium.', 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 + VALUES('The value for the parameter @FragmentationMedium is not supported. The same action has been specified more than once. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationMedium.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -789,13 +1013,13 @@ BEGIN 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 + VALUES('The value for the parameter @FragmentationHigh is not supported. Supported values are INDEX_REBUILD_ONLINE, INDEX_REBUILD_OFFLINE and INDEX_REORGANIZE. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationHigh.', 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 + VALUES('The value for the parameter @FragmentationHigh is not supported. The same action has been specified more than once. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationHigh.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -803,27 +1027,24 @@ BEGIN 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 + VALUES('The value for the parameter @FragmentationLevel1 is not supported. The value has to be greater than 0 and less than 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLevel1.', 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 + VALUES('The value for the parameter @FragmentationLevel2 is not supported. The value has to be greater than 0 and less than 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLevel2.', 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 + VALUES('The value for the parameter @FragmentationLevel2 has to be greater than the value for @FragmentationLevel1. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLevel2.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -831,7 +1052,7 @@ BEGIN 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 + VALUES('The value for the parameter @MinNumberOfPages is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MinNumberOfPages.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -839,7 +1060,15 @@ BEGIN IF @MaxNumberOfPages < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MaxNumberOfPages is not supported.', 16, 1 + VALUES('The value for the parameter @MaxNumberOfPages is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MaxNumberOfPages.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @MinNumberOfPages > @MaxNumberOfPages + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MaxNumberOfPages has to be greater than or equal to the value for @MinNumberOfPages. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MaxNumberOfPages.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -847,7 +1076,7 @@ BEGIN 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 + VALUES('The value for the parameter @SortInTempdb is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#SortInTempdb.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -855,7 +1084,7 @@ BEGIN 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 + VALUES('The value for the parameter @MaxDOP is not supported. The value has to be between 0 and 64. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MaxDOP.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -863,7 +1092,7 @@ BEGIN 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 + VALUES('The value for the parameter @FillFactor is not supported. The value has to be between 1 and 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FillFactor.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -871,262 +1100,285 @@ BEGIN 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 + VALUES('The value for the parameter @PadIndex is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#PadIndex.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @LOBCompaction NOT IN('Y','N') OR @LOBCompaction IS NULL + IF @DataCompression NOT IN('NONE', 'PAGE', 'ROW') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @LOBCompaction is not supported.', 16, 1 + VALUES('The value for the parameter @DataCompression is not supported. Supported values are NONE, PAGE and ROW. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DataCompression.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @UpdateStatistics NOT IN('ALL','COLUMNS','INDEX') + IF @WaitAtLowPriorityMaxDuration < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @UpdateStatistics is not supported.', 16, 1 + VALUES('The value for the parameter @WaitAtLowPriorityMaxDuration is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#WaitAtLowPriorityMaxDuration.', 16, 1) END - ---------------------------------------------------------------------------------------------------- - - IF @OnlyModifiedStatistics NOT IN('Y','N') OR @OnlyModifiedStatistics IS NULL + IF @WaitAtLowPriorityAbortAfterWait = 'SELF' AND @WaitAtLowPriorityMaxDuration = 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @OnlyModifiedStatistics is not supported.', 16, 1 + VALUES('The value for the parameter @WaitAtLowPriorityMaxDuration is not supported. The value has to be greater than 0 when @WaitAtLowPriorityAbortAfterWait = ''SELF''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#WaitAtLowPriorityMaxDuration.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @StatisticsModificationLevel <= 0 OR @StatisticsModificationLevel > 100 + IF @WaitAtLowPriorityAbortAfterWait NOT IN('NONE','SELF','BLOCKERS') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @StatisticsModificationLevel is not supported.', 16, 1 + VALUES('The value for the parameter @WaitAtLowPriorityAbortAfterWait is not supported. Supported values are NONE, SELF and BLOCKERS. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#WaitAtLowPriorityAbortAfterWait.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @OnlyModifiedStatistics = 'Y' AND @StatisticsModificationLevel IS NOT NULL + 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 'You can only specify one of the parameters @OnlyModifiedStatistics and @StatisticsModificationLevel.', 16, 1 + VALUES('The parameters @WaitAtLowPriorityMaxDuration and @WaitAtLowPriorityAbortAfterWait can only be used together. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @StatisticsSample <= 0 OR @StatisticsSample > 100 + IF @Resumable NOT IN('Y','N') OR @Resumable IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Resumable is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Resumable.', 16, 1) + END + + IF @Resumable = 'Y' AND @SortInTempdb = 'Y' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @StatisticsSample is not supported.', 16, 1 + VALUES('You can only specify one of the parameters @Resumable and @SortInTempdb. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @StatisticsResample NOT IN('Y','N') OR @StatisticsResample IS NULL + IF @LOBCompaction NOT IN('Y','N') OR @LOBCompaction IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @StatisticsResample is not supported.', 16, 1 + VALUES('The value for the parameter @LOBCompaction is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#LOBCompaction.', 16, 1) END - IF @StatisticsResample = 'Y' AND @StatisticsSample IS NOT NULL + ---------------------------------------------------------------------------------------------------- + + IF @UpdateStatistics NOT IN('ALL','COLUMNS','INDEX') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @StatisticsResample is not supported.', 16, 2 + VALUES('The value for the parameter @UpdateStatistics is not supported. Supported values are ALL, COLUMNS and INDEX. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#UpdateStatistics.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @PartitionLevel NOT IN('Y','N') OR @PartitionLevel IS NULL + IF @OnlyModifiedStatistics NOT IN('Y','N') OR @OnlyModifiedStatistics IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @PartitionLevel is not supported.', 16, 1 + VALUES('The value for the parameter @OnlyModifiedStatistics is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#OnlyModifiedStatistics.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @MSShippedObjects NOT IN('Y','N') OR @MSShippedObjects IS NULL + IF @StatisticsModificationLevel <= 0 OR @StatisticsModificationLevel > 100 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MSShippedObjects is not supported.', 16, 1 + VALUES('The value for the parameter @StatisticsModificationLevel is not supported. The value has to be greater than 0 and less than or equal to 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsModificationLevel.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF EXISTS(SELECT * FROM @SelectedIndexes WHERE DatabaseName IS NULL OR SchemaName IS NULL OR ObjectName IS NULL OR IndexName IS NULL) + IF @OnlyModifiedStatistics = 'Y' AND @StatisticsModificationLevel IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Indexes is not supported.', 16, 1 + VALUES('You can only specify one of the parameters @OnlyModifiedStatistics and @StatisticsModificationLevel. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) END - IF @Indexes IS NOT NULL AND NOT EXISTS(SELECT * FROM @SelectedIndexes) + ---------------------------------------------------------------------------------------------------- + + IF @StatisticsSample <= 0 OR @StatisticsSample > 100 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Indexes is not supported.', 16, 2 + VALUES('The value for the parameter @StatisticsSample is not supported. The value has to be between 1 and 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsSample.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @TimeLimit < 0 + IF @MinIndexUsageDays < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @TimeLimit is not supported.', 16, 1 + SELECT 'The value for the parameter @MinIndexUsageDays is not supported.', 16, 1 END - ---------------------------------------------------------------------------------------------------- IF @Delay < 0 + IF @StatisticsPersistSample NOT IN('Y','N') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Delay is not supported.', 16, 1 + VALUES('The value for the parameter @StatisticsPersistSample is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsPersistSample.', 16, 1) END - ---------------------------------------------------------------------------------------------------- + IF @StatisticsPersistSample IS NOT NULL AND @StatisticsSample IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @StatisticsPersistSample can only be used together with @StatisticsSample. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsPersistSample.', 16, 1) + END - IF @WaitAtLowPriorityMaxDuration < 0 + IF @StatisticsPersistSample IS NOT NULL AND @StatisticsResample = 'Y' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @WaitAtLowPriorityMaxDuration is not supported.', 16, 1 + VALUES('The parameters @StatisticsPersistSample and @StatisticsResample cannot be used together. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) END - IF @WaitAtLowPriorityMaxDuration IS NOT NULL AND @Version < 12 + IF @StatisticsPersistSample IS NOT NULL AND NOT (@Version >= 14.03006 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @WaitAtLowPriorityMaxDuration is not supported.', 16, 2 + VALUES('The value for the parameter @StatisticsPersistSample is not supported. PERSIST_SAMPLE_PERCENT is not supported in this version of SQL Server. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsPersistSample.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @WaitAtLowPriorityAbortAfterWait NOT IN('NONE','SELF','BLOCKERS') + IF @StatisticsResample NOT IN('Y','N') OR @StatisticsResample IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @WaitAtLowPriorityAbortAfterWait is not supported.', 16, 1 + VALUES('The value for the parameter @StatisticsResample is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsResample.', 16, 1) END - IF @WaitAtLowPriorityAbortAfterWait IS NOT NULL AND @Version < 12 + IF @StatisticsResample = 'Y' AND @StatisticsSample IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @WaitAtLowPriorityAbortAfterWait is not supported.', 16, 2 + VALUES('Setting @StatisticsResample to ''Y'' cannot be combined with @StatisticsSample. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsResample.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF (@WaitAtLowPriorityAbortAfterWait IS NOT NULL AND @WaitAtLowPriorityMaxDuration IS NULL) OR (@WaitAtLowPriorityAbortAfterWait IS NULL AND @WaitAtLowPriorityMaxDuration IS NOT NULL) + IF @PartitionLevel NOT IN('Y','N') OR @PartitionLevel IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The parameters @WaitAtLowPriorityMaxDuration and @WaitAtLowPriorityAbortAfterWait can only be used together.', 16, 1 + VALUES('The value for the parameter @PartitionLevel is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#PartitionLevel.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @Resumable NOT IN('Y','N') OR @Resumable IS NULL + IF @MSShippedObjects NOT IN('Y','N') OR @MSShippedObjects IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Resumable is not supported.', 16, 1 + VALUES('The value for the parameter @MSShippedObjects is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MSShippedObjects.', 16, 1) END - IF @Resumable = 'Y' AND NOT (@Version >= 14 OR SERVERPROPERTY('EngineEdition') IN (5, 8)) + ---------------------------------------------------------------------------------------------------- + + 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 @Resumable is not supported.', 16, 2 + VALUES('The value for the parameter @Indexes is not supported. The value contains one or more items that could not be parsed. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Indexes.', 16, 1) END - IF @Resumable = 'Y' AND @SortInTempdb = 'Y' + IF @Indexes IS NOT NULL AND NOT EXISTS(SELECT * FROM @SelectedIndexes) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'You can only specify one of the parameters @Resumable and @SortInTempdb.', 16, 3 + VALUES('The value for the parameter @Indexes is not supported. The value could not be parsed into a list of indexes. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Indexes.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @LockTimeout < 0 + IF @TimeLimit < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @LockTimeout is not supported.', 16, 1 + VALUES('The value for the parameter @TimeLimit is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#TimeLimit.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @LockMessageSeverity NOT IN(10, 16) + IF @Delay < 0 OR @Delay >= 86400 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @LockMessageSeverity is not supported.', 16, 1 + VALUES('The value for the parameter @Delay is not supported. The value has to be greater than or equal to 0 and less than 86400. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Delay.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) > 1 + IF @LockTimeout < 0 OR @LockTimeout > 86400 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @StringDelimiter is not supported.', 16, 1 + VALUES('The value for the parameter @LockTimeout is not supported. The value has to be between 0 and 86400. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#LockTimeout.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @DatabaseOrder NOT IN('DATABASE_NAME_ASC','DATABASE_NAME_DESC','DATABASE_SIZE_ASC','DATABASE_SIZE_DESC') + IF @LockMessageSeverity NOT IN(10, 16) OR @LockMessageSeverity IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DatabaseOrder is not supported.', 16, 1 + VALUES('The value for the parameter @LockMessageSeverity is not supported. Supported values are 10 and 16. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#LockMessageSeverity.', 16, 1) END - IF @DatabaseOrder IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 5 + ---------------------------------------------------------------------------------------------------- + + IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) <> 1 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DatabaseOrder is not supported.', 16, 2 + VALUES('The value for the parameter @StringDelimiter is not supported. The value has to be exactly one character. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StringDelimiter.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @DatabasesInParallel NOT IN('Y','N') OR @DatabasesInParallel IS NULL + 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 @DatabasesInParallel is not supported.', 16, 1 + VALUES('The value for the parameter @DatabaseOrder is not supported. Supported values are DATABASE_NAME_ASC, DATABASE_NAME_DESC, DATABASE_SIZE_ASC and DATABASE_SIZE_DESC. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DatabaseOrder.', 16, 1) END - IF @DatabasesInParallel = 'Y' AND SERVERPROPERTY('EngineEdition') = 5 + IF @DatabaseOrder IS NOT NULL AND @EngineEdition = 5 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DatabasesInParallel is not supported.', 16, 2 + VALUES('The parameter @DatabaseOrder is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DatabaseOrder.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF LEN(@ExecuteAsUser) > 128 + IF @DatabasesInParallel NOT IN('Y','N') OR @DatabasesInParallel IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DatabasesInParallel is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DatabasesInParallel.', 16, 1) + END + + IF @DatabasesInParallel = 'Y' AND @EngineEdition = 5 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ExecuteAsUser is not supported.', 16, 1 + VALUES('The parameter @DatabasesInParallel is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DatabasesInParallel.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @LogToTable NOT IN('Y','N') OR @LogToTable IS NULL + IF LEN(@ExecuteAsUser) > 128 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @LogToTable is not supported.', 16, 1 + VALUES('The value for the parameter @ExecuteAsUser is not supported. The maximum length is 128 characters. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#ExecuteAsUser.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @Execute NOT IN('Y','N') OR @Execute IS NULL + IF @LogToTable NOT IN('Y','N') OR @LogToTable IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Execute is not supported.', 16, 1 + VALUES('The value for the parameter @LogToTable is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#LogToTable.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF EXISTS(SELECT * FROM @Errors) + IF @Execute NOT IN('Y','N') OR @Execute IS NULL 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 + VALUES('The value for the parameter @Execute is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Execute.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Check that selected databases and availability groups exist //-- ---------------------------------------------------------------------------------------------------- - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedDatabases WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) @@ -1134,10 +1386,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Databases.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedIndexes WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) @@ -1145,10 +1398,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following databases in the @Indexes parameter do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases in the @Indexes parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Indexes.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(AvailabilityGroupName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(AvailabilityGroupName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY AvailabilityGroupName ASC) FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName NOT LIKE '%[%]%' AND AvailabilityGroupName NOT IN (SELECT AvailabilityGroupName FROM @tmpAvailabilityGroups) @@ -1156,10 +1410,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following availability groups do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following availability groups do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#AvailabilityGroups.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedIndexes WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName IN (SELECT DatabaseName FROM @tmpDatabases) @@ -1168,14 +1423,14 @@ BEGIN IF @ErrorMessage IS NOT NULL 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: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases have been selected in the @Indexes parameter, but not in the @Databases or @AvailabilityGroups parameters: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Indexes.', 10, 1) END ---------------------------------------------------------------------------------------------------- --// Raise errors //-- ---------------------------------------------------------------------------------------------------- - DECLARE ErrorCursor CURSOR FAST_FORWARD FOR SELECT [Message], Severity, [State] FROM @Errors ORDER BY [ID] ASC + DECLARE ErrorCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Message], Severity, [State] FROM @Errors ORDER BY [ID] ASC OPEN ErrorCursor @@ -1199,12 +1454,6 @@ BEGIN 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 //-- ---------------------------------------------------------------------------------------------------- @@ -1284,7 +1533,7 @@ BEGIN FROM dbo.[Queue] WHERE SchemaName = @SchemaName AND ObjectName = @ObjectName - AND [Parameters] = @Parameters + AND [Parameters] = @ParametersString IF @QueueID IS NULL BEGIN @@ -1294,12 +1543,12 @@ BEGIN FROM dbo.[Queue] WITH (UPDLOCK, HOLDLOCK) WHERE SchemaName = @SchemaName AND ObjectName = @ObjectName - AND [Parameters] = @Parameters + AND [Parameters] = @ParametersString IF @QueueID IS NULL BEGIN INSERT INTO dbo.[Queue] (SchemaName, ObjectName, [Parameters]) - SELECT @SchemaName, @ObjectName, @Parameters + VALUES(@SchemaName, @ObjectName, @ParametersString) SET @QueueID = SCOPE_IDENTITY() END @@ -1333,17 +1582,17 @@ BEGIN DatabaseName FROM @tmpDatabases tmpDatabases WHERE Selected = 1 - AND NOT EXISTS (SELECT * FROM dbo.QueueDatabase WHERE DatabaseName = tmpDatabases.DatabaseName AND QueueID = @QueueID) + AND NOT EXISTS (SELECT * FROM dbo.QueueDatabase WHERE DatabaseName COLLATE DATABASE_DEFAULT = 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) + AND NOT EXISTS (SELECT * FROM @tmpDatabases tmpDatabases WHERE DatabaseName = QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT AND Selected = 1) UPDATE QueueDatabase SET DatabaseOrder = tmpDatabases.[Order] FROM dbo.QueueDatabase QueueDatabase - INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName = tmpDatabases.DatabaseName + INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT = tmpDatabases.DatabaseName WHERE QueueID = @QueueID END @@ -1360,7 +1609,7 @@ BEGIN BEGIN ROLLBACK TRANSACTION END - SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar) + ', ' + ISNULL(ERROR_MESSAGE(),'') + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + ISNULL(ERROR_MESSAGE(),'') RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT SET @ReturnCode = ERROR_NUMBER() @@ -1374,7 +1623,7 @@ BEGIN ---------------------------------------------------------------------------------------------------- WHILE (1 = 1) - BEGIN + BEGIN -- Start of database loop IF @DatabasesInParallel = 'Y' BEGIN @@ -1427,7 +1676,7 @@ BEGIN SET @CurrentDatabase_sp_executesql = QUOTENAME(@CurrentDatabaseName) + '.sys.sp_executesql' BEGIN - SET @DatabaseMessage = 'Date and time: ' + CONVERT(nvarchar,SYSDATETIME(),120) + SET @DatabaseMessage = 'Date and time: ' + CONVERT(nvarchar(max),SYSDATETIME(),120) RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT SET @DatabaseMessage = 'Database: ' + QUOTENAME(@CurrentDatabaseName) @@ -1456,30 +1705,71 @@ BEGIN RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT SET @DatabaseMessage = 'Recovery model: ' + @CurrentRecoveryModel + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT END - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN - SELECT @CurrentReplicaID = databases.replica_id + SELECT @CurrentAvailabilityGroupReplicaID = 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 + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + + IF @CurrentAvailabilityGroupReplicaID IS NULL AND @CurrentAvailabilityGroupID IS NULL AND @Version >= 16 + BEGIN + SET @CurrentAvailabilityGroupID = @ContainedAvailabilityGroupID + + SELECT @CurrentAvailabilityGroupReplicaID = replica_id + FROM sys.dm_hadr_availability_replica_states + WHERE group_id = @CurrentAvailabilityGroupID + AND is_local = 1 + END SELECT @CurrentAvailabilityGroupRole = role_desc FROM sys.dm_hadr_availability_replica_states - WHERE replica_id = @CurrentReplicaID + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND database_id = DB_ID(@CurrentDatabaseName) + + IF @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState IS NULL AND @ContainedAvailabilityGroupListenerConnection = 1 + BEGIN + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND DB_NAME(database_id) = @CurrentDatabaseName + END SELECT @CurrentAvailabilityGroup = [name] FROM sys.availability_groups WHERE group_id = @CurrentAvailabilityGroupID END - IF SERVERPROPERTY('EngineEdition') <> 5 + IF @IsHadrEnabled = 1 AND @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SELECT @CurrentDistributedAvailabilityGroup = availability_groups.[name], + @CurrentDistributedAvailabilityGroupReplicaID = availability_replicas.replica_id + FROM sys.availability_groups availability_groups + INNER JOIN sys.availability_replicas availability_replicas ON availability_groups.group_id = availability_replicas.group_id + INNER JOIN sys.availability_groups availability_groups_local ON availability_replicas.replica_server_name = availability_groups_local.[name] + WHERE availability_groups.is_distributed = 1 + AND availability_groups_local.group_id = @CurrentAvailabilityGroupID + + SELECT @CurrentDistributedAvailabilityGroupRole = dm_hadr_availability_replica_states.role_desc + FROM sys.dm_hadr_availability_replica_states dm_hadr_availability_replica_states + WHERE dm_hadr_availability_replica_states.replica_id = @CurrentDistributedAvailabilityGroupReplicaID + END + + IF @EngineEdition <> 5 BEGIN SELECT @CurrentDatabaseMirroringRole = UPPER(mirroring_role_desc) FROM sys.database_mirroring database_mirroring @@ -1494,15 +1784,80 @@ BEGIN 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 END - IF @CurrentDatabaseMirroringRole IS NOT NULL + IF @CurrentDistributedAvailabilityGroup IS NOT NULL BEGIN - SET @DatabaseMessage = 'Database mirroring role: ' + @CurrentDatabaseMirroringRole + SET @DatabaseMessage = 'Distributed availability group: ' + ISNULL(@CurrentDistributedAvailabilityGroup,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Distributed availability group role: ' + ISNULL(@CurrentDistributedAvailabilityGroupRole,'N/A') RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT - END - RAISERROR(@EmptyLine,10,1) WITH NOWAIT + SET @DatabaseMessage = 'Replica role in distributed availability group: ' + CASE WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Global primary' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Forwarder' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in secondary availability group' + WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in primary availability group' ELSE 'N/A' END + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + END + -- Reset internal trust state for this database. + SET @CurrentStatsResetEstimate = NULL + SET @CurrentHoursSinceReset = NULL + SET @CurrentStatsTrusted = NULL + + -- Reset internal trust state for this database. + SET @CurrentStatsResetEstimate = NULL + SET @CurrentHoursSinceReset = NULL + SET @CurrentStatsTrusted = NULL + + -- Estimate when sys.dm_db_index_usage_stats was last reset for this DB. + -- Take the LATEST of: + -- 1) instance start time -- a restart wipes the DMV + -- 2) database create_date -- catches attach / restore / new DBs + -- 3) earliest activity in the DMV -- a lower bound; usage must be at least this old + -- Skip the computation entirely on contexts where the DMV would be misleading or unavailable: + -- - DB not ONLINE + -- - AG secondary (or DB whose AG role cannot be determined) + -- - Amazon RDS rdsadmin + IF @MinIndexUsageDays IS NOT NULL + AND @CurrentDatabaseState = 'ONLINE' + AND NOT (@CurrentAvailabilityGroup IS NOT NULL AND (@CurrentAvailabilityGroupRole <> 'PRIMARY' OR @CurrentAvailabilityGroupRole IS NULL)) + AND NOT (@AmazonRDS = 1 AND @CurrentDatabaseName = 'rdsadmin') + BEGIN + SELECT @CurrentStatsResetEstimate = + (SELECT MAX(t) FROM (VALUES + ((SELECT sqlserver_start_time FROM sys.dm_os_sys_info)), + ((SELECT create_date FROM sys.databases WHERE [name] = @CurrentDatabaseName)), + ((SELECT MIN(x) FROM (VALUES + ((SELECT MIN(last_user_seek) FROM sys.dm_db_index_usage_stats WHERE database_id = DB_ID(@CurrentDatabaseName))), + ((SELECT MIN(last_user_scan) FROM sys.dm_db_index_usage_stats WHERE database_id = DB_ID(@CurrentDatabaseName))), + ((SELECT MIN(last_user_lookup) FROM sys.dm_db_index_usage_stats WHERE database_id = DB_ID(@CurrentDatabaseName))), + ((SELECT MIN(last_user_update) FROM sys.dm_db_index_usage_stats WHERE database_id = DB_ID(@CurrentDatabaseName))) + ) AS u(x))) + ) AS r(t)) + + SET @CurrentHoursSinceReset = DATEDIFF(HOUR, @CurrentStatsResetEstimate, SYSDATETIME()) + SET @CurrentStatsTrusted = CASE WHEN @CurrentHoursSinceReset >= @RequiredTrustHours + THEN 1 ELSE 0 END + + SET @DatabaseMessage = 'Usage stats reset estimate: ' + + ISNULL(CONVERT(nvarchar(30), @CurrentStatsResetEstimate, 121),'N/A') + + ' (DMV age: ' + ISNULL(CAST(@CurrentHoursSinceReset AS nvarchar), 'N/A') + ' h' + + ', required: ' + CAST(@RequiredTrustHours AS nvarchar) + ' h / ' + + CAST(@MinIndexUsageDays AS nvarchar) + ' d) - Trusted: ' + + CASE WHEN @CurrentStatsTrusted = 1 + THEN 'Yes' + ELSE 'NO - Insufficient history in sys.dm_db_index_usage_stats (likely restart, failover, detach/attach, or recent index DDL). Unused-index skip disabled for this database; fragmentation-based maintenance will still run.' + END + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + END IF @ExecuteAsUser IS NOT NULL AND @CurrentDatabaseState = 'ONLINE' @@ -1520,221 +1875,412 @@ BEGIN BEGIN SET @DatabaseMessage = 'The user ' + QUOTENAME(@ExecuteAsUser) + ' does not exist in the database ' + QUOTENAME(@CurrentDatabaseName) + '.' RAISERROR('%s',16,1,@DatabaseMessage) WITH NOWAIT + SET @Error = @@ERROR + SET @ReturnCode = @Error 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 (@CurrentDistributedAvailabilityGroup IS NOT NULL AND (@CurrentDistributedAvailabilityGroupRole <> 'PRIMARY' OR @CurrentDistributedAvailabilityGroupRole 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 + -- Select objects + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT objects.[object_id] AS ObjectID' + + ', objects.[schema_id] AS SchemaID' + + ', schemas.[name] AS SchemaName' + + ', objects.[name] AS ObjectName' + + ', RTRIM(objects.[type]) AS ObjectType' + + ', ISNULL(tables.is_memory_optimized, 0) AS IsMemoryOptimized' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') THEN 'CASE WHEN EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = objects.object_id AND [type] = 5) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS HasClusteredColumnstore' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') AND @Resumable = 'Y' THEN '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 = objects.object_id) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsClusteredIndexComputed' + + ', CASE WHEN EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = objects.object_id AND [type] = 1 AND is_disabled = 1) THEN 1 ELSE 0 END AS IsClusteredIndexDisabled' + + ' 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 (tables.is_external = 0 OR tables.is_external IS NULL)' + + CASE WHEN @MSShippedObjects = 'N' THEN ' AND objects.is_ms_shipped = 0' ELSE '' END + IF @Indexes IS NOT NULL AND EXISTS(SELECT * FROM @SelectedIndexes WHERE Selected = 1) AND NOT EXISTS(SELECT * FROM @SelectedIndexes WHERE Selected = 1 AND DatabaseName = '%' AND SchemaName = '%' AND ObjectName = '%') + BEGIN + SET @CurrentCommand += ' AND EXISTS(SELECT * FROM #SelectedIndexes SelectedIndexes WHERE @ParamDatabaseName LIKE REPLACE(REPLACE(SelectedIndexes.DatabaseName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND schemas.[name] LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND objects.[name] LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND SelectedIndexes.Selected = 1)' + END + IF @Indexes IS NOT NULL AND EXISTS(SELECT * FROM @SelectedIndexes WHERE Selected = 0 AND IndexName = '%') + BEGIN + SET @CurrentCommand += ' AND NOT EXISTS(SELECT * FROM #SelectedIndexes SelectedIndexes WHERE @ParamDatabaseName LIKE REPLACE(REPLACE(SelectedIndexes.DatabaseName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND schemas.[name] LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND objects.[name] LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND SelectedIndexes.IndexName = ''%'' AND SelectedIndexes.Selected = 0)' + END + + INSERT INTO #Objects (ObjectID, SchemaID, SchemaName, ObjectName, ObjectType, IsMemoryOptimized, HasClusteredColumnstore, IsClusteredIndexComputed, IsClusteredIndexDisabled) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseName nvarchar(max)', @ParamDatabaseName = @CurrentDatabaseName + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select indexes + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT indexes.[object_id] AS ObjectID' + + ', indexes.index_id AS IndexID' + + ', indexes.[name] AS IndexName' + + ', indexes.[type] AS IndexType' + + ', indexes.data_space_id AS DataSpaceID' + + ', CASE WHEN EXISTS (SELECT * FROM sys.partition_schemes partition_schemes WHERE partition_schemes.data_space_id = indexes.data_space_id) THEN 1 ELSE 0 END AS IsPartitioned' + + ', indexes.allow_page_locks AS AllowPageLocks' + + ', indexes.has_filter AS HasFilter' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') THEN '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] = indexes.object_id AND types.name IN(''image'',''text'',''ntext'')) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsImageText' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') THEN 'CASE WHEN indexes.[type] = 1 AND EXISTS(SELECT * FROM sys.columns columns WHERE columns.[object_id] = indexes.object_id AND columns.is_filestream = 1) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsFileStream' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') AND (@Version >= 16 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) THEN 'CASE WHEN EXISTS(SELECT * FROM sys.index_columns index_columns WHERE index_columns.[object_id] = indexes.[object_id] AND index_columns.index_id = indexes.index_id AND index_columns.column_store_order_ordinal = 1) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsColumnstoreOrdered' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') AND @Resumable = 'Y' THEN '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 OR index_columns.is_included_column = 1) 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' ELSE 'NULL' END + ' AS IsComputed' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') AND @Resumable = 'Y' THEN '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.key_ordinal > 0 OR index_columns.partition_ordinal > 0) AND index_columns.[object_id] = indexes.[object_id] AND index_columns.index_id = indexes.index_id AND types.[name] = ''timestamp'') THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsTimestamp' + + ' FROM sys.indexes indexes' + + ' INNER JOIN #Objects Objects ON indexes.[object_id] = Objects.ObjectID' + + ' AND indexes.[type] IN(1,2,3,4,5,6,7)' + + ' AND indexes.is_disabled = 0' + + ' AND indexes.is_hypothetical = 0' + + INSERT INTO #Indexes (ObjectID, IndexID, IndexName, IndexType, DataSpaceID, IsPartitioned, AllowPageLocks, HasFilter, IsImageText, IsFileStream, IsColumnstoreOrdered, IsComputed, IsTimestamp) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select statistics 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 (' + + ' SELECT stats.[object_id] AS ObjectID' + + ', stats.stats_id AS StatisticsID' + + ', stats.name AS StatisticsName' + + ', stats.no_recompute AS NoRecompute' + + ', stats.is_incremental AS IsIncremental' + + ', CASE WHEN EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = stats.[object_id] AND indexes.index_id = stats.stats_id) THEN 1 ELSE 0 END AS IsIndex' + + ' FROM sys.stats stats' + + ' INNER JOIN #Objects Objects ON stats.[object_id] = Objects.ObjectID' + + INSERT INTO #Stats (ObjectID, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, IsIndex) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select paused resumable index operations + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT index_resumable_operations.object_id AS ObjectID' + + ', index_resumable_operations.index_id AS IndexID' + + ', ' + CASE WHEN @PartitionLevel = 'Y' THEN 'index_resumable_operations.partition_number AS PartitionNumber' WHEN @PartitionLevel = 'N' THEN 'NULL AS PartitionNumber' END + + ' FROM sys.index_resumable_operations index_resumable_operations' + + ' WHERE index_resumable_operations.state_desc = ''PAUSED''' + + INSERT INTO @tmpResumableOperations (ObjectID, IndexID, PartitionNumber) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END 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 'ISNULL(tables.is_memory_optimized, 0)' 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 OR index_columns.is_included_column = 1) 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.key_ordinal > 0 OR index_columns.partition_ordinal > 0) AND 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]' + -- Check if there are read-only filegroups in the database + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT @ParamDatabaseHasReadOnlyFileGroup = CASE WHEN EXISTS(SELECT * FROM sys.filegroups filegroups WHERE filegroups.is_read_only = 1) THEN 1 ELSE 0 END' + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseHasReadOnlyFileGroup bit OUTPUT', @ParamDatabaseHasReadOnlyFileGroup = @CurrentDatabaseHasReadOnlyFileGroup OUTPUT + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select clustered, nonclustered and hash indexes + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Indexes.IndexID AS IndexID' + + ', Indexes.IndexName AS IndexName' + + ', Indexes.IndexType AS IndexType' + + ', Indexes.AllowPageLocks AS AllowPageLocks' + + ', Indexes.HasFilter AS HasFilter' + + ', Indexes.IsImageText AS IsImageText' + + ', Indexes.IsFileStream AS IsFileStream' + + ', Objects.HasClusteredColumnstore AS HasClusteredColumnstore' + + ', Indexes.IsColumnstoreOrdered AS IsColumnstoreOrdered' + + ', Indexes.IsComputed AS IsComputed' + + ', Objects.IsClusteredIndexComputed AS IsClusteredIndexComputed' + + ', Indexes.IsTimestamp AS IsTimestamp' + + CASE WHEN @CurrentDatabaseHasReadOnlyFileGroup = 1 THEN ', CASE WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.destination_data_spaces destination_data_spaces ON Indexes.DataSpaceID = 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.ObjectID AND indexes2.[index_id] = Indexes.IndexID' + 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.DataSpaceID = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND Indexes.ObjectID = indexes2.[object_id] AND Indexes.IndexID = indexes2.index_id) THEN 1' + + ' WHEN Indexes.IndexType = 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.ObjectID) THEN 1 ELSE 0 END AS OnReadOnlyFileGroup' ELSE ', 0 AS OnReadOnlyFileGroup' END + + ', 0 AS ResumableIndexOperation' + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ', Stats.StatisticsID AS StatisticsID' ELSE ', NULL AS StatisticsID' END + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ', Stats.StatisticsName AS StatisticsName' ELSE ', NULL AS StatisticsName' END + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ', Stats.[NoRecompute] AS NoRecompute' ELSE ', NULL AS NoRecompute' END + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ', Stats.IsIncremental AS IsIncremental' ELSE ', NULL AS IsIncremental' END + + ', ' + 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 'Indexes.IsPartitioned AS IsPartition' WHEN @PartitionLevel = 'N' THEN '0 AS IsPartition' END + + ', ' + CASE WHEN (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) THEN 'dm_db_partition_stats.in_row_data_page_count AS InRowDataPageCount' ELSE 'NULL AS InRowDataPageCount' END + + ' FROM #Indexes Indexes' + + ' INNER JOIN #Objects Objects ON Indexes.ObjectID = Objects.ObjectID' + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ' INNER JOIN #Stats Stats ON Indexes.ObjectID = Stats.ObjectID AND Indexes.IndexID = Stats.StatisticsID' ELSE '' END 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]' + SET @CurrentCommand += ' INNER JOIN sys.partitions partitions ON Indexes.ObjectID = partitions.[object_id] AND Indexes.IndexID = partitions.index_id' + END + IF @PartitionLevel = 'Y' AND (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) + BEGIN + SET @CurrentCommand += ' INNER JOIN sys.dm_db_partition_stats dm_db_partition_stats ON partitions.object_id = dm_db_partition_stats.object_id AND partitions.index_id = dm_db_partition_stats.index_id AND partitions.partition_number = dm_db_partition_stats.partition_number' + END + IF @PartitionLevel = 'N' AND (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) + BEGIN + SET @CurrentCommand += ' INNER JOIN (SELECT object_id, index_id, SUM(in_row_data_page_count) AS in_row_data_page_count FROM sys.dm_db_partition_stats GROUP BY object_id, index_id) dm_db_partition_stats ON Indexes.ObjectID = dm_db_partition_stats.object_id AND Indexes.IndexID = dm_db_partition_stats.index_id' END + SET @CurrentCommand += ' WHERE Objects.ObjectType IN(''U'',''V'')' + + ' AND Indexes.IndexType IN(1,2,7)' + + CASE WHEN (@UpdateStatistics = 'COLUMNS' OR @UpdateStatistics IS NULL) AND @MinNumberOfPages > 0 THEN ' AND dm_db_partition_stats.in_row_data_page_count >= @ParamMinNumberOfPages' ELSE '' END + + CASE WHEN (@UpdateStatistics = 'COLUMNS' OR @UpdateStatistics IS NULL) AND @MaxNumberOfPages IS NOT NULL THEN ' AND dm_db_partition_stats.in_row_data_page_count <= @ParamMaxNumberOfPages' ELSE '' 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 + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, IndexID, IndexName, IndexType, AllowPageLocks, HasFilter, IsImageText, IsFileStream, HasClusteredColumnstore, IsColumnstoreOrdered, IsComputed, IsClusteredIndexComputed, IsTimestamp, OnReadOnlyFileGroup, ResumableIndexOperation, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, PartitionID, PartitionNumber, IsPartition, InRowDataPageCount) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamMinNumberOfPages int, @ParamMaxNumberOfPages int', @ParamMinNumberOfPages = @MinNumberOfPages, @ParamMaxNumberOfPages = @MaxNumberOfPages + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select XML and spatial indexes + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Indexes.IndexID AS IndexID' + + ', Indexes.IndexName AS IndexName' + + ', Indexes.IndexType AS IndexType' + + ', Indexes.AllowPageLocks AS AllowPageLocks' + + ', Indexes.HasFilter AS HasFilter' + + ', Indexes.IsImageText AS IsImageText' + + ', Indexes.IsFileStream AS IsFileStream' + + ', Objects.HasClusteredColumnstore AS HasClusteredColumnstore' + + ', Indexes.IsColumnstoreOrdered AS IsColumnstoreOrdered' + + ', Indexes.IsComputed AS IsComputed' + + ', Objects.IsClusteredIndexComputed AS IsClusteredIndexComputed' + + ', Indexes.IsTimestamp AS IsTimestamp' + + CASE WHEN @CurrentDatabaseHasReadOnlyFileGroup = 1 THEN ', CASE WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.destination_data_spaces destination_data_spaces ON Indexes.DataSpaceID = 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.ObjectID AND indexes2.[index_id] = Indexes.IndexID) THEN 1' + + ' WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.filegroups filegroups ON Indexes.DataSpaceID = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND Indexes.ObjectID = indexes2.[object_id] AND Indexes.IndexID = indexes2.index_id) THEN 1 ELSE 0 END AS OnReadOnlyFileGroup' ELSE ', 0 AS OnReadOnlyFileGroup' END + + ', 0 AS ResumableIndexOperation' + + ', 0 AS IsPartition' + + ' FROM #Indexes Indexes' + + ' INNER JOIN #Objects Objects ON Indexes.ObjectID = Objects.ObjectID' + + ' WHERE Objects.ObjectType = ''U''' + + ' AND Indexes.IndexType IN(3,4)' + + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, IndexID, IndexName, IndexType, AllowPageLocks, HasFilter, IsImageText, IsFileStream, HasClusteredColumnstore, IsColumnstoreOrdered, IsComputed, IsClusteredIndexComputed, IsTimestamp, OnReadOnlyFileGroup, ResumableIndexOperation, IsPartition) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select columnstore indexes + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Indexes.IndexID AS IndexID' + + ', Indexes.IndexName AS IndexName' + + ', Indexes.IndexType AS IndexType' + + ', Indexes.AllowPageLocks AS AllowPageLocks' + + ', Indexes.HasFilter AS HasFilter' + + ', Indexes.IsImageText AS IsImageText' + + ', Indexes.IsFileStream AS IsFileStream' + + ', Objects.HasClusteredColumnstore AS HasClusteredColumnstore' + + ', Indexes.IsColumnstoreOrdered AS IsColumnstoreOrdered' + + ', Indexes.IsComputed AS IsComputed' + + ', Objects.IsClusteredIndexComputed AS IsClusteredIndexComputed' + + ', Indexes.IsTimestamp AS IsTimestamp' + + CASE WHEN @CurrentDatabaseHasReadOnlyFileGroup = 1 THEN ', CASE WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.destination_data_spaces destination_data_spaces ON Indexes.DataSpaceID = 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.ObjectID AND indexes2.[index_id] = Indexes.IndexID' + 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.DataSpaceID = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND Indexes.ObjectID = indexes2.[object_id] AND Indexes.IndexID = indexes2.index_id) THEN 1' + + ' WHEN Indexes.IndexType = 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.ObjectID) THEN 1 ELSE 0 END AS OnReadOnlyFileGroup' ELSE ', 0 AS OnReadOnlyFileGroup' END + + ', 0 AS ResumableIndexOperation' + + ', NULL AS StatisticsID' + + ', NULL AS StatisticsName' + + ', NULL AS NoRecompute' + + ', NULL 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 'Indexes.IsPartitioned AS IsPartition' WHEN @PartitionLevel = 'N' THEN '0 AS IsPartition' END + + ', ' + CASE WHEN (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) THEN 'dm_db_partition_stats.in_row_data_page_count AS InRowDataPageCount' ELSE 'NULL AS InRowDataPageCount' END + + ' FROM #Indexes Indexes' + + ' INNER JOIN #Objects Objects ON Indexes.ObjectID = Objects.ObjectID' + IF @PartitionLevel = 'Y' + BEGIN + SET @CurrentCommand += ' INNER JOIN sys.partitions partitions ON Indexes.ObjectID = partitions.[object_id] AND Indexes.IndexID = partitions.index_id' + END + IF @PartitionLevel = 'Y' AND (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) + BEGIN + SET @CurrentCommand += ' INNER JOIN sys.dm_db_partition_stats dm_db_partition_stats ON partitions.object_id = dm_db_partition_stats.object_id AND partitions.index_id = dm_db_partition_stats.index_id AND partitions.partition_number = dm_db_partition_stats.partition_number' + END + IF @PartitionLevel = 'N' AND (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) + BEGIN + SET @CurrentCommand += ' INNER JOIN (SELECT object_id, index_id, SUM(in_row_data_page_count) AS in_row_data_page_count FROM sys.dm_db_partition_stats GROUP BY object_id, index_id) dm_db_partition_stats ON Indexes.ObjectID = dm_db_partition_stats.object_id AND Indexes.IndexID = dm_db_partition_stats.index_id' + END + SET @CurrentCommand += ' WHERE Objects.ObjectType = ''U''' + + ' AND Indexes.IndexType IN(5,6)' + + CASE WHEN (@UpdateStatistics = 'COLUMNS' OR @UpdateStatistics IS NULL) AND @MinNumberOfPages > 0 THEN ' AND dm_db_partition_stats.in_row_data_page_count >= @ParamMinNumberOfPages' ELSE '' END + + CASE WHEN (@UpdateStatistics = 'COLUMNS' OR @UpdateStatistics IS NULL) AND @MaxNumberOfPages IS NOT NULL THEN ' AND dm_db_partition_stats.in_row_data_page_count <= @ParamMaxNumberOfPages' ELSE '' END + + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, IndexID, IndexName, IndexType, AllowPageLocks, HasFilter, IsImageText, IsFileStream, HasClusteredColumnstore, IsColumnstoreOrdered, IsComputed, IsClusteredIndexComputed, IsTimestamp, OnReadOnlyFileGroup, ResumableIndexOperation, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, PartitionID, PartitionNumber, IsPartition, InRowDataPageCount) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamMinNumberOfPages int, @ParamMaxNumberOfPages int', @ParamMinNumberOfPages = @MinNumberOfPages, @ParamMaxNumberOfPages = @MaxNumberOfPages + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + 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 'ISNULL(tables.is_memory_optimized, 0)' 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 + -- Select non-incremental column level statistics + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Stats.StatisticsID AS StatisticsID' + + ', Stats.StatisticsName AS StatisticsName' + + ', Stats.[NoRecompute] AS NoRecompute' + + ', Stats.IsIncremental AS IsIncremental' + + ', NULL AS PartitionNumber' + + ', 0 AS IsPartition' + + ' FROM #Stats Stats' + + ' INNER JOIN #Objects Objects ON Stats.ObjectID = Objects.ObjectID' + + ' WHERE Stats.IsIndex = 0' + + ' AND Stats.IsIncremental = 0' + + ' AND Objects.IsClusteredIndexDisabled = 0' + + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, PartitionNumber, IsPartition) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 BEGIN - SET @CurrentCommand = @CurrentCommand + ' OUTER APPLY sys.dm_db_incremental_stats_properties(stats.object_id, stats.stats_id) dm_db_incremental_stats_properties' + SET @ReturnCode = @Error 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)' + -- Select incremental column level statistics + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Stats.StatisticsID AS StatisticsID' + + ', Stats.StatisticsName AS StatisticsName' + + ', Stats.[NoRecompute] AS NoRecompute' + + ', Stats.IsIncremental AS IsIncremental' + + ', ' + CASE WHEN @PartitionLevel = 'Y' THEN 'partitions.partition_number' ELSE 'NULL' END + ' AS PartitionNumber' + + ', ' + CASE WHEN @PartitionLevel = 'Y' THEN '1' ELSE '0' END + ' AS IsPartition' + + ' FROM #Stats Stats' + + ' INNER JOIN #Objects Objects ON Stats.ObjectID = Objects.ObjectID' + IF @PartitionLevel = 'Y' + BEGIN + SET @CurrentCommand += ' INNER JOIN sys.partitions partitions ON partitions.[object_id] = Stats.ObjectID AND partitions.index_id IN (0, 1)' + END + SET @CurrentCommand += ' WHERE Objects.IsMemoryOptimized = 0' + + ' AND Stats.IsIndex = 0' + + ' AND Stats.IsIncremental = 1' + + ' AND Objects.IsClusteredIndexDisabled = 0' - IF @Version >= 12 + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, PartitionNumber, IsPartition) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 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)' + SET @ReturnCode = @Error 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 + IF @PartitionLevel = 'Y' BEGIN - SET @ReturnCode = @Error + UPDATE tmpIndexesStatistics + SET tmpIndexesStatistics.IsLastPartition = CASE WHEN tmpIndexesStatistics.PartitionNumber = LastPartitionNumbers.LastPartitionNumber THEN 1 ELSE 0 END + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN (SELECT ObjectID, IndexID, MAX(PartitionNumber) AS LastPartitionNumber FROM @tmpIndexesStatistics WHERE IndexID IS NOT NULL AND PartitionNumber IS NOT NULL GROUP BY ObjectID, IndexID) LastPartitionNumbers ON tmpIndexesStatistics.ObjectID = LastPartitionNumbers.ObjectID AND tmpIndexesStatistics.IndexID = LastPartitionNumbers.IndexID + OPTION (RECOMPILE) 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 + SET tmpIndexesStatistics.ResumableIndexOperation = 1 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 + INNER JOIN @tmpResumableOperations tmpResumableOperations ON tmpIndexesStatistics.ObjectID = tmpResumableOperations.ObjectID AND tmpIndexesStatistics.IndexID = tmpResumableOperations.IndexID AND (tmpIndexesStatistics.PartitionNumber = tmpResumableOperations.PartitionNumber OR tmpResumableOperations.PartitionNumber IS NULL) + OPTION (RECOMPILE) - UPDATE tmpIndexesStatistics + 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(REPLACE(SelectedIndexes.DatabaseName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.SchemaName LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.ObjectName LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,'[','[[]'),'_','[_]') AND COALESCE(tmpIndexesStatistics.IndexName,tmpIndexesStatistics.StatisticsName) LIKE REPLACE(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(REPLACE(SelectedIndexes.DatabaseName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.SchemaName LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.ObjectName LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,'[','[[]'),'_','[_]') AND COALESCE(tmpIndexesStatistics.IndexName,tmpIndexesStatistics.StatisticsName) LIKE REPLACE(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(REPLACE(SelectedIndexes.DatabaseName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.SchemaName LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.ObjectName LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,'[','[[]'),'_','[_]') AND COALESCE(tmpIndexesStatistics.IndexName,tmpIndexesStatistics.StatisticsName) LIKE REPLACE(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 tmpIndexesStatistics.StartPosition = SelectedIndexes2.StartPosition FROM @tmpIndexesStatistics tmpIndexesStatistics INNER JOIN (SELECT tmpIndexesStatistics.SchemaName, tmpIndexesStatistics.ObjectName, tmpIndexesStatistics.IndexName, tmpIndexesStatistics.StatisticsName, MIN(SelectedIndexes.StartPosition) AS StartPosition @@ -1749,6 +2295,30 @@ BEGIN AND (tmpIndexesStatistics.StatisticsName = SelectedIndexes2.StatisticsName OR tmpIndexesStatistics.StatisticsName IS NULL) END; + + -- Skip indexes that look unused, but ONLY when the DMV is old enough to trust. + -- If the DMV is too young (@CurrentStatsTrusted = 0), do nothing here: + -- we fall back to the existing fragmentation-based behavior rather than + -- making decisions on data we cannot trust. + IF @MinIndexUsageDays IS NOT NULL AND @CurrentStatsTrusted = 1 + BEGIN + UPDATE tis + SET tis.Selected = 0 + FROM @tmpIndexesStatistics tis + WHERE tis.IndexID IS NOT NULL + AND tis.IndexID > 1 -- never deselect the clustered index / heap entry + AND NOT EXISTS ( + SELECT 1 + FROM sys.dm_db_index_usage_stats us + WHERE us.database_id = DB_ID(@CurrentDatabaseName) + AND us.object_id = tis.ObjectID + AND us.index_id = tis.IndexID + AND (us.user_seeks + us.user_scans + us.user_lookups) > 0 + ) + 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 @@ -1772,21 +2342,85 @@ BEGIN SET @Error = @@ERROR RAISERROR(@EmptyLine,10,1) WITH NOWAIT END + SET [Order] = RowNumber - SELECT @ErrorMessage = STRING_AGG(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) + -- Update that alter index is completed for rows that have no index, if no index actions have been selected, for rows on read-only filegroups, or based on the page counts + UPDATE @tmpIndexesStatistics + SET AlterIndexCompleted = 1 + WHERE IndexID IS NULL + OR NOT EXISTS (SELECT * FROM @ActionsPreferred) + OR OnReadOnlyFileGroup = 1 + OR NOT (((InRowDataPageCount >= @MinNumberOfPages OR @MinNumberOfPages = 0) AND (InRowDataPageCount <= @MaxNumberOfPages OR @MaxNumberOfPages IS NULL)) OR InRowDataPageCount IS NULL) - IF @ErrorMessage IS NOT NULL - BEGIN - SET @ErrorMessage = 'The following indexes in the @Indexes parameter do not exist: ' + @ErrorMessage + '.' - RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT - SET @Error = @@ERROR - RAISERROR(@EmptyLine,10,1) WITH NOWAIT + -- Update that update statistics is completed for rows that have no statistics + UPDATE @tmpIndexesStatistics + SET UpdateStatisticsCompleted = 1 + WHERE StatisticsID IS NULL + OR (IndexID IS NOT NULL AND @PartitionLevel = 'Y' AND IsIncremental = 0 AND IsLastPartition = 0) + + SET @CurrentCommand = 'SELECT schemas.[name] AS SchemaName, objects.[name] AS ObjectName' + + ' FROM sys.objects objects' + + ' INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id]' + + ' WHERE objects.[type] IN(''U'',''V'')' + + ' AND EXISTS(SELECT * FROM #SelectedIndexes SelectedIndexes' + + ' WHERE SelectedIndexes.DatabaseName = @ParamDatabaseName' + + ' AND SelectedIndexes.SchemaName NOT LIKE ''%[%]%''' + + ' AND SelectedIndexes.ObjectName NOT LIKE ''%[%]%''' + + ' AND schemas.[name] = SelectedIndexes.SchemaName COLLATE ' + @Collation + + ' AND objects.[name] = SelectedIndexes.ObjectName COLLATE ' + @Collation + ')' + + INSERT INTO #ExistingObjects (SchemaName, ObjectName) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseName nvarchar(max)', @ParamDatabaseName = @CurrentDatabaseName + + SET @CurrentCommand = 'SELECT schemas.[name] AS SchemaName, objects.[name] AS ObjectName, [Names].[name] AS IndexName' + + ' FROM sys.objects objects' + + ' INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id]' + + ' CROSS APPLY (SELECT indexes.[name] FROM sys.indexes indexes WHERE indexes.[object_id] = objects.[object_id] AND indexes.[type] <> 0' + + ' UNION SELECT stats.[name] FROM sys.stats stats WHERE stats.[object_id] = objects.[object_id]) [Names]' + + ' WHERE objects.[type] IN(''U'',''V'')' + + ' AND EXISTS(SELECT * FROM #SelectedIndexes SelectedIndexes' + + ' WHERE SelectedIndexes.DatabaseName = @ParamDatabaseName' + + ' AND SelectedIndexes.SchemaName NOT LIKE ''%[%]%''' + + ' AND SelectedIndexes.ObjectName NOT LIKE ''%[%]%''' + + ' AND SelectedIndexes.IndexName NOT LIKE ''%[%]%''' + + ' AND schemas.[name] = SelectedIndexes.SchemaName COLLATE ' + @Collation + + ' AND objects.[name] = SelectedIndexes.ObjectName COLLATE ' + @Collation + + ' AND [Names].[name] = SelectedIndexes.IndexName COLLATE ' + @Collation + ')' + + INSERT INTO #ExistingIndexes (SchemaName, ObjectName, IndexName) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseName nvarchar(max)', @ParamDatabaseName = @CurrentDatabaseName + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)) + '.' + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC, SchemaName ASC, ObjectName ASC) + FROM @SelectedIndexes SelectedIndexes + WHERE DatabaseName = @CurrentDatabaseName + AND SchemaName NOT LIKE '%[%]%' + AND ObjectName NOT LIKE '%[%]%' + AND IndexName LIKE '%[%]%' + AND NOT EXISTS (SELECT * FROM #ExistingObjects WHERE SchemaName = SelectedIndexes.SchemaName AND ObjectName = SelectedIndexes.ObjectName) + + IF @ErrorMessage IS NOT NULL + BEGIN + SET @ErrorMessage = 'The following objects in the @Indexes parameter do not exist: ' + @ErrorMessage + '.' + RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + END + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)) + '.' + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName) + '.' + QUOTENAME(IndexName), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC, SchemaName ASC, ObjectName ASC, IndexName ASC) + FROM @SelectedIndexes SelectedIndexes + WHERE DatabaseName = @CurrentDatabaseName + AND SchemaName NOT LIKE '%[%]%' + AND ObjectName NOT LIKE '%[%]%' + AND IndexName NOT LIKE '%[%]%' + AND NOT EXISTS (SELECT * FROM #ExistingIndexes WHERE SchemaName = SelectedIndexes.SchemaName AND ObjectName = SelectedIndexes.ObjectName AND IndexName = SelectedIndexes.IndexName) + + IF @ErrorMessage IS NOT NULL + BEGIN + SET @ErrorMessage = 'The following indexes in the @Indexes parameter do not exist: ' + @ErrorMessage + '.' + RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + END END WHILE (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) @@ -1805,10 +2439,9 @@ BEGIN @CurrentAllowPageLocks = AllowPageLocks, @CurrentHasFilter = HasFilter, @CurrentIsImageText = IsImageText, - @CurrentIsNewLOB = IsNewLOB, @CurrentIsFileStream = IsFileStream, @CurrentHasClusteredColumnstore = HasClusteredColumnstore, - @CurrentHasNonClusteredColumnstore = HasNonClusteredColumnstore, + @CurrentIsColumnstoreOrdered = IsColumnstoreOrdered, @CurrentIsComputed = IsComputed, @CurrentIsClusteredIndexComputed = IsClusteredIndexComputed, @CurrentIsTimestamp = IsTimestamp, @@ -1820,7 +2453,11 @@ BEGIN @CurrentIsIncremental = IsIncremental, @CurrentPartitionID = PartitionID, @CurrentPartitionNumber = PartitionNumber, - @CurrentPartitionCount = PartitionCount + @CurrentIsPartition = IsPartition, + @CurrentIsLastPartition = IsLastPartition, + @CurrentInRowDataPageCount = InRowDataPageCount, + @CurrentAlterIndexCompleted = AlterIndexCompleted, + @CurrentUpdateStatisticsCompleted = UpdateStatisticsCompleted FROM @tmpIndexesStatistics WHERE Selected = 1 AND Completed = 0 @@ -1831,15 +2468,14 @@ 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) + IF @CurrentAlterIndexCompleted = 0 AND @CurrentIndexID IS NOT NULL AND EXISTS(SELECT * FROM @ActionsPreferred) AND @CurrentOnReadOnlyFileGroup = 0 BEGIN + SET @CurrentMaxDOP = @MaxDOP + + -- Does the index exist? SET @CurrentCommand = '' - IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar) + '; ' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' 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' @@ -1854,7 +2490,7 @@ BEGIN 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 @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 @@ -1866,101 +2502,301 @@ BEGIN GOTO NoAction END CATCH - END - -- Does the statistics exist? - IF @CurrentStatisticsID IS NOT NULL AND @UpdateStatistics IS NOT NULL - BEGIN - SET @CurrentCommand = '' + -- Is the index fragmented? + IF EXISTS(SELECT [Priority], [Action], COUNT(*) FROM @ActionsPreferred GROUP BY [Priority], [Action] HAVING COUNT(*) <> 3) OR @MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL + BEGIN + IF NOT EXISTS (SELECT * FROM @PhysicalStats WHERE ObjectID = @CurrentObjectID AND IndexID = @CurrentIndexID) + BEGIN + SET @CurrentCommand = '' + + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + + IF @CurrentIndexType IN(5, 6) + BEGIN + SET @CurrentCommand += 'SELECT object_id, index_id, partition_number, MAX(avg_fragmentation_in_percent), SUM(page_count) FROM sys.dm_db_index_physical_stats(DB_ID(@ParamDatabaseName), @ParamObjectID, @ParamIndexID, NULL, ''LIMITED'') WHERE alloc_unit_type_desc = ''IN_ROW_DATA'' AND index_level = 0 GROUP BY object_id, index_id, partition_number' + END + ELSE + BEGIN + SET @CurrentCommand += 'SELECT object_id, index_id, partition_number, avg_fragmentation_in_percent, page_count FROM sys.dm_db_index_physical_stats(DB_ID(@ParamDatabaseName), @ParamObjectID, @ParamIndexID, NULL, ''LIMITED'') WHERE alloc_unit_type_desc = ''IN_ROW_DATA'' AND index_level = 0' + END + + BEGIN TRY + INSERT INTO @PhysicalStats (ObjectID, IndexID, PartitionNumber, FragmentationLevel, PageCount) + EXECUTE sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseName nvarchar(max), @ParamObjectID int, @ParamIndexID int', @ParamDatabaseName = @CurrentDatabaseName, @ParamObjectID = @CurrentObjectID, @ParamIndexID = @CurrentIndexID + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 + + UPDATE @tmpIndexesStatistics + SET AlterIndexCompleted = 1 + WHERE ObjectID = @CurrentObjectID + AND IndexID = @CurrentIndexID + AND AlterIndexCompleted = 0 + + GOTO NoAction + END CATCH + END - IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar) + '; ' + IF @CurrentPartitionNumber IS NULL + BEGIN + SELECT @CurrentFragmentationLevel = MAX(FragmentationLevel), + @CurrentPageCount = SUM(PageCount) + FROM @PhysicalStats + WHERE ObjectID = @CurrentObjectID + AND IndexID = @CurrentIndexID + END + ELSE + BEGIN + SELECT @CurrentFragmentationLevel = FragmentationLevel, + @CurrentPageCount = PageCount + FROM @PhysicalStats + WHERE ObjectID = @CurrentObjectID + AND IndexID = @CurrentIndexID + AND PartitionNumber = @CurrentPartitionNumber + END + END - 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' + -- Select fragmentation group + SET @CurrentFragmentationGroup = CASE + WHEN @CurrentFragmentationLevel >= @FragmentationLevel2 THEN 'High' + WHEN @CurrentFragmentationLevel >= @FragmentationLevel1 AND @CurrentFragmentationLevel < @FragmentationLevel2 THEN 'Medium' + WHEN @CurrentFragmentationLevel < @FragmentationLevel1 THEN 'Low' + 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 + -- Which actions are allowed? + IF NOT (@CurrentIsMemoryOptimized = 1) + AND NOT (@CurrentAllowPageLocks = 0) + BEGIN + INSERT INTO @CurrentActionsAllowed ([Action]) + VALUES ('INDEX_REORGANIZE') + END + IF NOT (@CurrentIsMemoryOptimized = 1) + BEGIN + INSERT INTO @CurrentActionsAllowed ([Action]) + VALUES ('INDEX_REBUILD_OFFLINE') + END + IF @EngineEdition IN (3, 5, 8) + AND NOT (@CurrentIsMemoryOptimized = 1) + AND NOT (@CurrentIndexType = 1 AND @CurrentIsImageText = 1 AND @CurrentIsImageText IS NOT NULL) + AND NOT (@CurrentIndexType = 1 AND @CurrentIsFileStream = 1 AND @CurrentIsFileStream IS NOT NULL) + AND NOT (@CurrentIndexType = 3) + AND NOT (@CurrentIndexType = 4) + AND NOT (@CurrentIndexType = 5 AND NOT (@Version >= 15 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous'))) + AND NOT (@CurrentIndexType = 2 AND @CurrentHasClusteredColumnstore = 1 AND @CurrentHasClusteredColumnstore IS NOT NULL AND NOT (@Version >= 15 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous'))) + AND NOT (@CurrentIndexType = 5 AND @CurrentIsColumnstoreOrdered = 1 AND @CurrentIsColumnstoreOrdered IS NOT NULL AND NOT (@Version >= 17 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous'))) + BEGIN + INSERT INTO @CurrentActionsAllowed ([Action]) + VALUES ('INDEX_REBUILD_ONLINE') + END - IF @CurrentStatisticsExists IS NULL + -- Decide action + IF (@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 - SET @CurrentStatisticsExists = 0 - GOTO NoAction + 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 - 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) + ELSE BEGIN - SET @ReturnCode = ERROR_NUMBER() + SELECT @CurrentAction = [Action] + FROM @ActionsPreferred + WHERE [Priority] = (SELECT MIN([Priority]) + FROM @ActionsPreferred + WHERE [Action] IN (SELECT [Action] FROM @CurrentActionsAllowed)) END + 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 @CurrentResumableIndexOperation = 1 + BEGIN + SET @CurrentAction = 'INDEX_REBUILD_ONLINE' + END - IF @PartitionLevelStatistics = 1 AND @CurrentIsIncremental = 1 + -- Workaround for limitation in SQL Server, http://support.microsoft.com/kb/2292737 + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentIndexType IN (1, 2) AND @CurrentAllowPageLocks = 0 BEGIN - SET @CurrentCommand += 'SELECT @ParamRowCount = [rows], @ParamModificationCounter = modification_counter FROM sys.dm_db_incremental_stats_properties (@ParamObjectID, @ParamStatisticsID) WHERE partition_number = @ParamPartitionNumber' + SET @CurrentMaxDOP = 1 END - ELSE - IF (@Version >= 10.504000 AND @Version < 11) OR @Version >= 11.03000 + + -- Create index comment + IF @CurrentAction IS NOT NULL BEGIN - SET @CurrentCommand += 'SELECT @ParamRowCount = [rows], @ParamModificationCounter = modification_counter FROM sys.dm_db_stats_properties (@ParamObjectID, @ParamStatisticsID)' + 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 + ', ' + IF @CurrentIsImageText IS NOT NULL SET @CurrentComment += 'ImageText: ' + CASE WHEN @CurrentIsImageText = 1 THEN 'Yes' WHEN @CurrentIsImageText = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentIsFileStream IS NOT NULL SET @CurrentComment += 'FileStream: ' + CASE WHEN @CurrentIsFileStream = 1 THEN 'Yes' WHEN @CurrentIsFileStream = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentHasClusteredColumnstore IS NOT NULL 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 @CurrentIsColumnstoreOrdered IS NOT NULL AND @CurrentIndexType = 5 SET @CurrentComment += 'IsColumnstoreOrdered: ' + CASE WHEN @CurrentIsColumnstoreOrdered = 1 THEN 'Yes' WHEN @CurrentIsColumnstoreOrdered = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentIsComputed IS NOT NULL SET @CurrentComment += 'Computed: ' + CASE WHEN @CurrentIsComputed = 1 THEN 'Yes' WHEN @CurrentIsComputed = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentIsClusteredIndexComputed IS NOT NULL AND @CurrentIndexType = 2 SET @CurrentComment += 'ClusteredIndexComputed: ' + CASE WHEN @CurrentIsClusteredIndexComputed = 1 THEN 'Yes' WHEN @CurrentIsClusteredIndexComputed = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentIsTimestamp IS NOT NULL SET @CurrentComment += 'Timestamp: ' + CASE WHEN @CurrentIsTimestamp = 1 THEN 'Yes' WHEN @CurrentIsTimestamp = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @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(max)),'N/A') + ', ' + SET @CurrentComment += 'Fragmentation: ' + ISNULL(CAST(@CurrentFragmentationLevel AS nvarchar(max)),'N/A') END - ELSE + + IF @CurrentAction IS NOT NULL AND (@CurrentPageCount IS NOT NULL OR @CurrentFragmentationLevel IS NOT NULL) BEGIN - SET @CurrentCommand += 'SELECT @ParamRowCount = rowcnt, @ParamModificationCounter = rowmodctr FROM sys.sysindexes sysindexes WHERE sysindexes.[id] = @ParamObjectID AND sysindexes.[indid] = @ParamStatisticsID' + SET @CurrentExtendedInfo = (SELECT * + FROM (SELECT CAST(@CurrentPageCount AS nvarchar(max)) AS [PageCount], + CAST(@CurrentFragmentationLevel AS nvarchar(max)) AS Fragmentation + ) ExtendedInfo FOR XML RAW('ExtendedInfo'), ELEMENTS) 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 @CurrentAction IS NOT NULL AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SET @CurrentDatabaseContext = @CurrentDatabaseName - 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 + SET @CurrentCommandType = 'ALTER_INDEX' - IF NOT (ERROR_NUMBER() IN(1205,1222) AND @LockMessageSeverity = 10) + SET @CurrentCommand = '' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + 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(max)) + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @SortInTempdb = 'Y' AND @CurrentIndexType IN(1,2,3,4) AND @CurrentResumableIndexOperation = 0 BEGIN - SET @ReturnCode = ERROR_NUMBER() + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('SORT_IN_TEMPDB = ON') END - GOTO NoAction - END CATCH + 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) + VALUES('SORT_IN_TEMPDB = OFF') + END + + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('ONLINE = ON' + CASE WHEN @WaitAtLowPriorityMaxDuration IS NOT NULL THEN ' (WAIT_AT_LOW_PRIORITY (MAX_DURATION = ' + CAST(@WaitAtLowPriorityMaxDuration AS nvarchar(max)) + ', ABORT_AFTER_WAIT = ' + UPPER(@WaitAtLowPriorityAbortAfterWait) + '))' ELSE '' END) + END + + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentResumableIndexOperation = 1 AND @WaitAtLowPriorityMaxDuration IS NOT NULL + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('WAIT_AT_LOW_PRIORITY (MAX_DURATION = ' + CAST(@WaitAtLowPriorityMaxDuration AS nvarchar(max)) + ', ABORT_AFTER_WAIT = ' + UPPER(@WaitAtLowPriorityAbortAfterWait) + ')') + END + + IF @CurrentAction = 'INDEX_REBUILD_OFFLINE' AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('ONLINE = OFF') + END + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @CurrentMaxDOP IS NOT NULL + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('MAXDOP = ' + CAST(@CurrentMaxDOP AS nvarchar(max))) + 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) + VALUES('FILLFACTOR = ' + CAST(@FillFactor AS nvarchar(max))) + END + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @PadIndex IS NOT NULL AND @CurrentIsPartition = 0 AND @CurrentIndexType IN(1,2,3,4) AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('PAD_INDEX = ' + CASE WHEN @PadIndex = 'Y' THEN 'ON' WHEN @PadIndex = 'N' THEN 'OFF' END) + END + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @DataCompression IS NOT NULL AND @CurrentIndexType IN(1,2,4) AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('DATA_COMPRESSION = ' + @DataCompression) + END + + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES(CASE WHEN @Resumable = 'Y' AND @CurrentIndexType IN(1,2) AND (@CurrentIsComputed = 0 OR @CurrentIsComputed IS NULL) AND (@CurrentIsClusteredIndexComputed = 0 OR @CurrentIsClusteredIndexComputed IS NULL) AND (@CurrentIsTimestamp = 0 OR @CurrentIsTimestamp IS NULL) AND @CurrentHasFilter = 0 AND (@CurrentHasClusteredColumnstore = 0 OR @CurrentHasClusteredColumnstore IS NULL) THEN 'RESUMABLE = ON' ELSE 'RESUMABLE = OFF' END) + END + + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND ((@Resumable = 'Y' AND @CurrentIndexType IN(1,2) AND (@CurrentIsComputed = 0 OR @CurrentIsComputed IS NULL) AND (@CurrentIsClusteredIndexComputed = 0 OR @CurrentIsClusteredIndexComputed IS NULL) AND (@CurrentIsTimestamp = 0 OR @CurrentIsTimestamp IS NULL) AND @CurrentHasFilter = 0 AND (@CurrentHasClusteredColumnstore = 0 OR @CurrentHasClusteredColumnstore IS NULL)) OR @CurrentResumableIndexOperation = 1) AND @TimeLimit IS NOT NULL + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('MAX_DURATION = ' + CAST(CASE WHEN DATEDIFF(MINUTE,SYSDATETIME(),DATEADD(SECOND,@TimeLimit,@StartTime)) < 1 THEN 1 WHEN DATEDIFF(MINUTE,SYSDATETIME(),DATEADD(SECOND,@TimeLimit,@StartTime)) > 10080 THEN 10080 ELSE DATEDIFF(MINUTE,SYSDATETIME(),DATEADD(SECOND,@TimeLimit,@StartTime)) END AS nvarchar(max))) + END + + IF @CurrentAction IN('INDEX_REORGANIZE') AND @LOBCompaction = 'Y' + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('LOB_COMPACTION = ON') + END + + IF @CurrentAction IN('INDEX_REORGANIZE') AND @LOBCompaction = 'N' + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('LOB_COMPACTION = OFF') + END + + IF EXISTS (SELECT * FROM @CurrentAlterIndexWithClauseArguments) + BEGIN + SELECT @CurrentCommand += ' WITH (' + STRING_AGG(Argument, ', ') WITHIN GROUP (ORDER BY ID ASC) + ')' + FROM @CurrentAlterIndexWithClauseArguments + END + + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @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 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')) + -- Should the statistics be updated? + IF @CurrentUpdateStatisticsCompleted = 0 + AND @CurrentStatisticsID IS NOT NULL + AND ((@UpdateStatistics = 'ALL' AND (@CurrentIndexType IN (1,2,7) OR @CurrentIndexID IS NULL)) OR (@UpdateStatistics = 'INDEX' AND @CurrentIndexID IS NOT NULL AND @CurrentIndexType IN (1,2,7)) OR (@UpdateStatistics = 'COLUMNS' AND @CurrentIndexID IS NULL)) + AND ((@CurrentIsPartition = 0 AND (@CurrentAction NOT IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') OR @CurrentAction IS NULL)) OR (@CurrentIsPartition = 1 AND (@CurrentIsLastPartition = 1 OR (@PartitionLevel = 'Y' AND @CurrentIsIncremental = 1)))) BEGIN + SET @CurrentMaxDOP = @MaxDOP + + -- Does the statistics exist? SET @CurrentCommand = '' - IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar) + '; ' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' - 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' + 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 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 + 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 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 @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 @@ -1972,338 +2808,271 @@ BEGIN 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) + -- Check non-incremental statistics properties + IF NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) AND NOT (@PartitionLevel = 'Y' AND @CurrentIsIncremental = 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 + SET @CurrentCommand = '' - -- 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 @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' - 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 @CurrentCommand += 'SELECT @ParamRowCount = [rows], @ParamModificationCounter = modification_counter FROM sys.dm_db_stats_properties (@ParamObjectID, @ParamStatisticsID)' - SET @CurrentStatisticsSample = @StatisticsSample - SET @CurrentStatisticsResample = @StatisticsResample + BEGIN TRY + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamObjectID int, @ParamStatisticsID int, @ParamRowCount bigint OUTPUT, @ParamModificationCounter bigint OUTPUT', @ParamObjectID = @CurrentObjectID, @ParamStatisticsID = @CurrentStatisticsID, @ParamRowCount = @CurrentRowCount OUTPUT, @ParamModificationCounter = @CurrentModificationCounter OUTPUT + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 - -- 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 + IF NOT (ERROR_NUMBER() IN(1205,1222) AND @LockMessageSeverity = 10) + BEGIN + SET @ReturnCode = ERROR_NUMBER() + END - -- Incremental statistics only supports RESAMPLE - IF @PartitionLevelStatistics = 1 AND @CurrentIsIncremental = 1 - BEGIN - SET @CurrentStatisticsSample = NULL - SET @CurrentStatisticsResample = 'Y' - END + GOTO NoAction + END CATCH + 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 + -- Check incremental statistics properties + IF NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) AND @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 + AND NOT EXISTS (SELECT * FROM @IncrementalStatsProperties WHERE ObjectID = @CurrentObjectID AND StatisticsID = @CurrentStatisticsID) + BEGIN + SET @CurrentCommand = '' - 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 @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' - IF @CurrentIndexID IS NOT NULL AND @CurrentAction IS NOT NULL AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) - BEGIN - SET @CurrentDatabaseContext = @CurrentDatabaseName + BEGIN + SET @CurrentCommand += 'SELECT object_id, stats_id, partition_number, [rows], modification_counter FROM sys.dm_db_incremental_stats_properties (@ParamObjectID, @ParamStatisticsID)' + END - SET @CurrentCommandType = 'ALTER_INDEX' + BEGIN TRY + INSERT INTO @IncrementalStatsProperties (ObjectID, StatisticsID, PartitionNumber, [Rows], ModificationCounter) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamObjectID int, @ParamStatisticsID int', @ParamObjectID = @CurrentObjectID, @ParamStatisticsID = @CurrentStatisticsID + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 + + UPDATE @tmpIndexesStatistics + SET UpdateStatisticsCompleted = 1 + WHERE ObjectID = @CurrentObjectID + AND StatisticsID = @CurrentStatisticsID + AND UpdateStatisticsCompleted = 0 - 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' + GOTO NoAction + END CATCH END - IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @SortInTempdb = 'N' AND @CurrentIndexType IN(1,2,3,4) AND @CurrentResumableIndexOperation = 0 + IF NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) AND @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'SORT_IN_TEMPDB = OFF' + SELECT @CurrentRowCount = [Rows], + @CurrentModificationCounter = [ModificationCounter] + FROM @IncrementalStatsProperties + WHERE ObjectID = @CurrentObjectID + AND StatisticsID = @CurrentStatisticsID + AND PartitionNumber = @CurrentPartitionNumber END - IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND (@CurrentIsPartition = 0 OR @Version >= 12) AND @CurrentResumableIndexOperation = 0 + -- Check partition statistics + IF NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) AND @CurrentModificationCounter IS NULL 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 + SET @CurrentCommand = '' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' - IF @CurrentAction = 'INDEX_REBUILD_OFFLINE' AND (@CurrentIsPartition = 0 OR @Version >= 12) AND @CurrentResumableIndexOperation = 0 - BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'ONLINE = OFF' - END + IF @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 + BEGIN + SET @CurrentCommand += 'SELECT @ParamObjectHasRows = CASE WHEN EXISTS (SELECT * FROM sys.dm_db_partition_stats WHERE [object_id] = @ParamObjectID AND index_id IN (0,1) AND partition_number = @ParamPartitionNumber AND row_count > 0) THEN 1 ELSE 0 END' + END + ELSE + BEGIN + SET @CurrentCommand += 'SELECT @ParamObjectHasRows = CASE WHEN EXISTS (SELECT * FROM sys.dm_db_partition_stats WHERE [object_id] = @ParamObjectID AND index_id IN (0,1) AND row_count > 0) THEN 1 ELSE 0 END' + 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) + BEGIN TRY + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamObjectID int, @ParamPartitionNumber int, @ParamObjectHasRows bit OUTPUT', @ParamObjectID = @CurrentObjectID, @ParamPartitionNumber = @CurrentPartitionNumber, @ParamObjectHasRows = @CurrentObjectHasRows OUTPUT + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + ISNULL(ERROR_MESSAGE(),'') + CASE WHEN ERROR_NUMBER() = 1222 THEN ' The object ' + QUOTENAME(@CurrentDatabaseName) + '.' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName) + ' is locked. The row count 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 - 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 + -- Update statistics? + IF ((@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 ((@CurrentIndexType IN (1,2) OR @CurrentIndexID IS NULL) AND @CurrentModificationCounter IS NULL AND @CurrentObjectHasRows = 1)) BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'FILLFACTOR = ' + CAST(@FillFactor AS nvarchar) + SET @CurrentUpdateStatistics = 'Y' 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 + ELSE BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'PAD_INDEX = ON' + SET @CurrentUpdateStatistics = 'N' 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 + SET @CurrentStatisticsSample = @StatisticsSample + SET @CurrentStatisticsPersistSample = @StatisticsPersistSample + SET @CurrentStatisticsResample = @StatisticsResample - 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 + -- Incremental statistics only supports RESAMPLE + IF @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'MAX_DURATION = ' + CAST(DATEDIFF(MINUTE,SYSDATETIME(),DATEADD(SECOND,@TimeLimit,@StartTime)) AS nvarchar(max)) + SET @CurrentStatisticsSample = NULL + SET @CurrentStatisticsPersistSample = NULL + SET @CurrentStatisticsResample = 'Y' END - IF @CurrentAction IN('INDEX_REORGANIZE') AND @LOBCompaction = 'Y' + -- Create statistics comment + IF @CurrentUpdateStatistics = 'Y' BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'LOB_COMPACTION = ON' + SET @CurrentComment = 'ObjectType: ' + CASE WHEN @CurrentObjectType = 'U' THEN 'Table' WHEN @CurrentObjectType = 'V' THEN 'View' ELSE 'N/A' END + ', ' + SET @CurrentComment += 'StatisticsType: ' + 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 'Yes' WHEN @CurrentIsIncremental = 0 THEN 'No' ELSE 'N/A' END + ', ' + SET @CurrentComment += 'RowCount: ' + ISNULL(CAST(@CurrentRowCount AS nvarchar(max)),'N/A') + ', ' + SET @CurrentComment += 'ModificationCounter: ' + ISNULL(CAST(@CurrentModificationCounter AS nvarchar(max)),'N/A') END - IF @CurrentAction IN('INDEX_REORGANIZE') AND @LOBCompaction = 'N' + IF @CurrentUpdateStatistics = 'Y' AND (@CurrentRowCount IS NOT NULL OR @CurrentModificationCounter IS NOT NULL) BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'LOB_COMPACTION = OFF' + SET @CurrentExtendedInfo = (SELECT * + FROM (SELECT CAST(@CurrentRowCount AS nvarchar(max)) AS [RowCount], + CAST(@CurrentModificationCounter AS nvarchar(max)) AS ModificationCounter + ) ExtendedInfo FOR XML RAW('ExtendedInfo'), ELEMENTS) END - - IF EXISTS (SELECT * FROM @CurrentAlterIndexWithClauseArguments) + ELSE BEGIN - SELECT @CurrentCommand += ' WITH (' + STRING_AGG(Argument, ', ') WITHIN GROUP (ORDER BY ID ASC) + ')' - FROM @CurrentAlterIndexWithClauseArguments + SET @CurrentExtendedInfo = NULL END - 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 + IF @CurrentUpdateStatistics = 'Y' AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) BEGIN - SET @CurrentDelay = DATEADD(ss,@Delay,'1900-01-01') - WAITFOR DELAY @CurrentDelay - END - END + SET @CurrentDatabaseContext = @CurrentDatabaseName - SET @CurrentMaxDOP = @MaxDOP + SET @CurrentCommandType = 'UPDATE_STATISTICS' - -- 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 + SET @CurrentCommand = '' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + SET @CurrentCommand += 'UPDATE STATISTICS ' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName) + ' ' + QUOTENAME(@CurrentStatisticsName) - 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 @CurrentMaxDOP IS NOT NULL AND (@Version >= 14.03015 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('MAXDOP = ' + CAST(@CurrentMaxDOP AS nvarchar(max))) + END - IF @CurrentStatisticsID IS NOT NULL AND @CurrentUpdateStatistics = 'Y' AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) - BEGIN - SET @CurrentDatabaseContext = @CurrentDatabaseName + IF @CurrentStatisticsSample = 100 + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('FULLSCAN') + END - SET @CurrentCommandType = 'UPDATE_STATISTICS' + IF @CurrentStatisticsSample IS NOT NULL AND @CurrentStatisticsSample <> 100 + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('SAMPLE ' + CAST(@CurrentStatisticsSample AS nvarchar(max)) + ' PERCENT') + END - 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 @CurrentStatisticsPersistSample = 'Y' + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('PERSIST_SAMPLE_PERCENT = ON') + END - IF @CurrentMaxDOP IS NOT NULL AND ((@Version >= 12.06024 AND @Version < 13) OR (@Version >= 13.05026 AND @Version < 14) OR @Version >= 14.030154 OR SERVERPROPERTY('EngineEdition') IN (5, 8)) - BEGIN - INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) - SELECT 'MAXDOP = ' + CAST(@CurrentMaxDOP AS nvarchar) - END + IF @CurrentStatisticsPersistSample = 'N' + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('PERSIST_SAMPLE_PERCENT = OFF') + END - IF @CurrentStatisticsSample = 100 - BEGIN - INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) - SELECT 'FULLSCAN' - END + IF @CurrentNoRecompute = 1 + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('NORECOMPUTE') + END - IF @CurrentStatisticsSample IS NOT NULL AND @CurrentStatisticsSample <> 100 - BEGIN - INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) - SELECT 'SAMPLE ' + CAST(@CurrentStatisticsSample AS nvarchar) + ' PERCENT' - END + IF @CurrentStatisticsResample = 'Y' + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('RESAMPLE') + END - IF @CurrentNoRecompute = 1 - BEGIN - INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) - SELECT 'NORECOMPUTE' - END + IF EXISTS (SELECT * FROM @CurrentUpdateStatisticsWithClauseArguments) + BEGIN + SELECT @CurrentCommand += ' WITH ' + STRING_AGG(Argument, ', ') WITHIN GROUP (ORDER BY ID ASC) + FROM @CurrentUpdateStatisticsWithClauseArguments + END - IF @CurrentStatisticsResample = 'Y' - BEGIN - INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) - SELECT 'RESAMPLE' - END + IF @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 AND @CurrentPartitionNumber IS NOT NULL SET @CurrentCommand += ' ON PARTITIONS(' + CAST(@CurrentPartitionNumber AS nvarchar(max)) + ')' - IF EXISTS (SELECT * FROM @CurrentUpdateStatisticsWithClauseArguments) - BEGIN - SELECT @CurrentCommand += ' WITH ' + STRING_AGG(Argument, ', ') WITHIN GROUP (ORDER BY ID ASC) - FROM @CurrentUpdateStatisticsWithClauseArguments + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 2, @Comment = @CurrentComment, @DatabaseName = @CurrentDatabaseName, @SchemaName = @CurrentSchemaName, @ObjectName = @CurrentObjectName, @ObjectType = @CurrentObjectType, @IndexName = @CurrentIndexName, @IndexType = @CurrentIndexType, @StatisticsName = @CurrentStatisticsName, @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 END - - 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 + SET AlterIndexCompleted = 1, + UpdateStatisticsCompleted = 1 WHERE Selected = 1 AND Completed = 0 AND [Order] = @CurrentIxOrder AND ID = @CurrentIxID + -- Update that index operations on remaining partitions are completed where no action is needed + IF @CurrentAlterIndexCompleted = 0 AND @CurrentIndexID IS NOT NULL AND @PartitionLevel = 'Y' AND @CurrentIsPartition = 1 AND (SELECT COUNT(DISTINCT FragmentationGroup) FROM @ActionsPreferred) < 3 + BEGIN + UPDATE tmpIndexesStatistics + SET AlterIndexCompleted = 1 + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN @PhysicalStats PhysicalStats ON tmpIndexesStatistics.ObjectID = PhysicalStats.ObjectID AND tmpIndexesStatistics.IndexID = PhysicalStats.IndexID AND tmpIndexesStatistics.PartitionNumber = PhysicalStats.PartitionNumber + WHERE tmpIndexesStatistics.ObjectID = @CurrentObjectID + AND tmpIndexesStatistics.IndexID = @CurrentIndexID + AND tmpIndexesStatistics.AlterIndexCompleted = 0 + AND tmpIndexesStatistics.ResumableIndexOperation = 0 + AND NOT EXISTS (SELECT * + FROM @ActionsPreferred ActionsPreferred + WHERE ActionsPreferred.FragmentationGroup = CASE + WHEN PhysicalStats.FragmentationLevel >= @FragmentationLevel2 THEN 'High' + WHEN PhysicalStats.FragmentationLevel >= @FragmentationLevel1 AND PhysicalStats.FragmentationLevel < @FragmentationLevel2 THEN 'Medium' + WHEN PhysicalStats.FragmentationLevel < @FragmentationLevel1 THEN 'Low' + END) + END + + -- Update that statistics on remaining partitions are completed where no update is needed + IF @CurrentUpdateStatisticsCompleted = 0 AND @CurrentStatisticsID IS NOT NULL AND @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 AND NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) + BEGIN + UPDATE tmpIndexesStatistics + SET UpdateStatisticsCompleted = 1 + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN @IncrementalStatsProperties IncrementalStatsProperties ON tmpIndexesStatistics.ObjectID = IncrementalStatsProperties.ObjectID AND tmpIndexesStatistics.StatisticsID = IncrementalStatsProperties.StatisticsID AND tmpIndexesStatistics.PartitionNumber = IncrementalStatsProperties.PartitionNumber + WHERE tmpIndexesStatistics.ObjectID = @CurrentObjectID + AND tmpIndexesStatistics.StatisticsID = @CurrentStatisticsID + AND tmpIndexesStatistics.Selected = 1 + AND tmpIndexesStatistics.Completed = 0 + AND IncrementalStatsProperties.ModificationCounter IS NOT NULL + AND ((@OnlyModifiedStatistics = 'Y' AND NOT (IncrementalStatsProperties.ModificationCounter > 0)) + OR (@StatisticsModificationLevel IS NOT NULL AND NOT ((IncrementalStatsProperties.ModificationCounter * 1. / NULLIF(IncrementalStatsProperties.[Rows],0)) * 100 >= @StatisticsModificationLevel OR (IncrementalStatsProperties.ModificationCounter > 0 AND IncrementalStatsProperties.ModificationCounter >= SQRT(IncrementalStatsProperties.[Rows] * 1000))))) + END + -- Clear variables SET @CurrentDatabaseContext = NULL @@ -2328,15 +3097,17 @@ BEGIN SET @CurrentStatisticsName = NULL SET @CurrentPartitionID = NULL SET @CurrentPartitionNumber = NULL - SET @CurrentPartitionCount = NULL + SET @CurrentInRowDataPageCount = NULL + SET @CurrentAlterIndexCompleted = NULL + SET @CurrentUpdateStatisticsCompleted = NULL SET @CurrentIsPartition = NULL + SET @CurrentIsLastPartition = NULL SET @CurrentIndexExists = NULL SET @CurrentStatisticsExists = NULL SET @CurrentIsImageText = NULL - SET @CurrentIsNewLOB = NULL SET @CurrentIsFileStream = NULL SET @CurrentHasClusteredColumnstore = NULL - SET @CurrentHasNonClusteredColumnstore = NULL + SET @CurrentIsColumnstoreOrdered = NULL SET @CurrentIsComputed = NULL SET @CurrentIsClusteredIndexComputed = NULL SET @CurrentIsTimestamp = NULL @@ -2344,6 +3115,7 @@ BEGIN SET @CurrentHasFilter = NULL SET @CurrentNoRecompute = NULL SET @CurrentIsIncremental = NULL + SET @CurrentObjectHasRows = NULL SET @CurrentRowCount = NULL SET @CurrentModificationCounter = NULL SET @CurrentOnReadOnlyFileGroup = NULL @@ -2355,6 +3127,7 @@ BEGIN SET @CurrentMaxDOP = NULL SET @CurrentUpdateStatistics = NULL SET @CurrentStatisticsSample = NULL + SET @CurrentStatisticsPersistSample = NULL SET @CurrentStatisticsResample = NULL DELETE FROM @CurrentActionsAllowed @@ -2369,8 +3142,9 @@ BEGIN 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 + SET @ReturnCode = @Error + RAISERROR(@EmptyLine,10,1) WITH NOWAIT END -- Update that the database is completed @@ -2391,6 +3165,10 @@ BEGIN END -- Clear variables + SET @CurrentStatsResetEstimate = NULL + SET @CurrentHoursSinceReset = NULL + SET @CurrentStatsTrusted = NULL + SET @CurrentDBID = NULL SET @CurrentDatabaseName = NULL @@ -2402,25 +3180,40 @@ BEGIN SET @CurrentDatabaseState = NULL SET @CurrentInStandby = NULL SET @CurrentRecoveryModel = NULL - - SET @CurrentReplicaID = NULL + SET @CurrentDatabaseHasReadOnlyFileGroup = NULL + SET @CurrentAvailabilityGroupReplicaID = NULL SET @CurrentAvailabilityGroupID = NULL SET @CurrentAvailabilityGroup = NULL SET @CurrentAvailabilityGroupRole = NULL + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = NULL + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = NULL + SET @CurrentDistributedAvailabilityGroup = NULL + SET @CurrentDistributedAvailabilityGroupReplicaID = NULL + SET @CurrentDistributedAvailabilityGroupRole = NULL + SET @CurrentDatabaseMirroringRole = NULL SET @CurrentCommand = NULL DELETE FROM @tmpIndexesStatistics - END + TRUNCATE TABLE #Objects + TRUNCATE TABLE #Indexes + TRUNCATE TABLE #Stats + TRUNCATE TABLE #ExistingObjects + TRUNCATE TABLE #ExistingIndexes + DELETE FROM @tmpResumableOperations + DELETE FROM @PhysicalStats + DELETE FROM @IncrementalStatsProperties + + END -- End of database loop ---------------------------------------------------------------------------------------------------- --// Log completing information //-- ---------------------------------------------------------------------------------------------------- Logging: - SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar,SYSDATETIME(),120) + SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar(max),SYSDATETIME(),120) RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT diff --git a/MaintenanceSolution.sql b/MaintenanceSolution.sql index 6fc95474..a03abe0f 100644 --- a/MaintenanceSolution.sql +++ b/MaintenanceSolution.sql @@ -1,6 +1,6 @@ /* -SQL Server Maintenance Solution - SQL Server 2017, SQL Server 2019, SQL Server 2022, and SQL Server 2025 +SQL Server Maintenance Solution - SQL Server 2017, SQL Server 2019, SQL Server 2022, SQL Server 2025, and Azure SQL Managed Instance Backup: https://ola.hallengren.com/sql-server-backup.html Integrity Check: https://ola.hallengren.com/sql-server-integrity-check.html @@ -10,7 +10,7 @@ License: https://ola.hallengren.com/license.html GitHub: https://github.com/olahallengren/sql-server-maintenance-solution -Version: 2026-05-18 20:15:56 +Version: 2026-08-23 14:46:45 You can contact me by e-mail at ola@hallengren.com. @@ -38,12 +38,6 @@ BEGIN 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.' @@ -123,6 +117,8 @@ ALTER PROCEDURE [dbo].[CommandExecute] @IndexType int = NULL, @StatisticsName nvarchar(max) = NULL, @PartitionNumber int = NULL, +@EncryptionKey nvarchar(max) = NULL, +@EncryptionKeyPlaceholder nvarchar(max) = NULL, @ExtendedInfo xml = NULL, @LockMessageSeverity int = 16, @ExecuteAsUser nvarchar(max) = NULL, @@ -137,7 +133,7 @@ BEGIN --// Source: https://ola.hallengren.com //-- --// License: https://ola.hallengren.com/license.html //-- --// GitHub: https://github.com/olahallengren/sql-server-maintenance-solution //-- - --// Version: 2026-05-18 20:15:56 //-- + --// Version: 2026-08-23 14:46:45 //-- ---------------------------------------------------------------------------------------------------- SET NOCOUNT ON @@ -151,7 +147,7 @@ BEGIN DECLARE @Errors TABLE (ID int IDENTITY PRIMARY KEY, [Message] nvarchar(max) NOT NULL, Severity int NOT NULL, - [State] int) + [State] int NOT NULL) DECLARE @CurrentMessage nvarchar(max) DECLARE @CurrentSeverity int @@ -171,32 +167,28 @@ BEGIN DECLARE @RevertCommand nvarchar(max) + DECLARE @CommandMasked 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 + VALUES('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 + VALUES('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 + VALUES('The table CommandLog is missing. Download https://ola.hallengren.com/scripts/CommandLog.sql.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -206,56 +198,62 @@ BEGIN 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 + VALUES('The value for the parameter @DatabaseContext is not supported. Specify the name of an existing database.', 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 + VALUES('The value for the parameter @Command is not supported. The value cannot be NULL or empty.', 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 + VALUES('The value for the parameter @CommandType is not supported. The value cannot be NULL or empty, and the maximum length is 60 characters.', 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 + VALUES('The value for the parameter @Mode is not supported. Supported values are 1 and 2.', 16, 1) + END + + IF (@EncryptionKey IS NULL AND @EncryptionKeyPlaceholder IS NOT NULL) OR (@EncryptionKey IS NOT NULL AND @EncryptionKeyPlaceholder IS NULL) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameters @EncryptionKey and @EncryptionKeyPlaceholder must be specified together.', 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 + VALUES('The value for the parameter @LockMessageSeverity is not supported. Supported values are 10 and 16.', 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 + VALUES('The value for the parameter @ExecuteAsUser is not supported. The maximum length is 128 characters.', 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 + VALUES('The value for the parameter @LogToTable is not supported. Supported values are ''Y'' and ''N''.', 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 + VALUES('The value for the parameter @Execute is not supported. Supported values are ''Y'' and ''N''.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Raise errors //-- ---------------------------------------------------------------------------------------------------- - DECLARE ErrorCursor CURSOR FAST_FORWARD FOR SELECT [Message], Severity, [State] FROM @Errors ORDER BY [ID] ASC + DECLARE ErrorCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Message], Severity, [State] FROM @Errors ORDER BY [ID] ASC OPEN ErrorCursor @@ -290,19 +288,27 @@ BEGIN SET @RevertCommand = 'REVERT' END + ---------------------------------------------------------------------------------------------------- + --// Mask encryption key //-- + ---------------------------------------------------------------------------------------------------- + + SET @CommandMasked = CASE WHEN @EncryptionKeyPlaceholder IS NULL THEN @Command ELSE REPLACE(@Command,@EncryptionKeyPlaceholder,'********') END + + SET @Command = CASE WHEN @EncryptionKeyPlaceholder IS NULL THEN @Command ELSE REPLACE(@Command,@EncryptionKeyPlaceholder,REPLACE(ISNULL(@EncryptionKey,''),'''','''''')) END + ---------------------------------------------------------------------------------------------------- --// Log initial information //-- ---------------------------------------------------------------------------------------------------- SET @StartTime = SYSDATETIME() - SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar,@StartTime,120) + SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar(max),@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 + SET @StartMessage = 'Command: ' + @CommandMasked RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT IF @Comment IS NOT NULL @@ -314,10 +320,10 @@ BEGIN 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 + VALUES (@DatabaseName, @SchemaName, @ObjectName, @ObjectType, @IndexName, @IndexType, @StatisticsName, @PartitionNumber, @ExtendedInfo, @CommandType, @CommandMasked, @StartTime) - SET @ID = SCOPE_IDENTITY() + SET @ID = SCOPE_IDENTITY() + END ---------------------------------------------------------------------------------------------------- --// Execute command //-- @@ -339,7 +345,7 @@ BEGIN SET @Error = ERROR_NUMBER() SET @ErrorMessageOriginal = ERROR_MESSAGE() - SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar) + ', ' + ISNULL(ERROR_MESSAGE(),'') + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + ISNULL(ERROR_MESSAGE(),'') SET @Severity = CASE WHEN ERROR_NUMBER() IN(1205, 1222, 5245) THEN @LockMessageSeverity ELSE ERROR_SEVERITY() END RAISERROR('%s',@Severity,1,@ErrorMessage) WITH NOWAIT @@ -364,10 +370,10 @@ BEGIN 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) + SET @EndMessage = 'Duration: ' + CASE WHEN (DATEDIFF(SECOND,@StartTime,@EndTime) / (24 * 3600)) > 0 THEN CAST((DATEDIFF(SECOND,@StartTime,@EndTime) / (24 * 3600)) AS nvarchar(max)) + '.' ELSE '' END + CONVERT(nvarchar(max),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) + SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar(max),@EndTime,120) RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT @@ -443,14 +449,15 @@ ALTER PROCEDURE [dbo].[DatabaseBackup] @AvailabilityGroups nvarchar(max) = NULL, @Updateability nvarchar(max) = 'ALL', @AdaptiveCompression nvarchar(max) = NULL, -@ModificationLevel int = NULL, +@MinModificationLevel int = NULL, @MinDatabaseSizeForDifferentialBackup int = NULL, -@LogSizeSinceLastLogBackup int = NULL, -@TimeSinceLastLogBackup int = NULL, +@MinLogSizeSinceLastLogBackup int = NULL, +@MinTimeSinceLastLogBackup int = NULL, @DataDomainBoostHost nvarchar(max) = NULL, @DataDomainBoostUser nvarchar(max) = NULL, @DataDomainBoostDevicePath nvarchar(max) = NULL, @DataDomainBoostLockboxPath nvarchar(max) = NULL, +@DataDomainBoostNoOutputTable nvarchar(max) = 'N', @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, @@ -465,11 +472,13 @@ ALTER PROCEDURE [dbo].[DatabaseBackup] @Format nvarchar(max) = 'N', @ObjectLevelRecoveryMap nvarchar(max) = 'N', @ExcludeLogShippedFromLogBackup nvarchar(max) = 'Y', +@ExcludeSeedingFromLogBackup nvarchar(max) = 'N', @DirectoryCheck nvarchar(max) = 'Y', @BackupOptions nvarchar(max) = NULL, @Stats int = NULL, @ExpireDate datetime = NULL, @RetainDays int = NULL, +@AllowNonCopyOnlyBackupsOnForwarder nvarchar(max) = 'N', @StringDelimiter nvarchar(max) = ',', @DatabaseOrder nvarchar(max) = NULL, @DatabasesInParallel nvarchar(max) = 'N', @@ -484,7 +493,7 @@ BEGIN --// Source: https://ola.hallengren.com //-- --// License: https://ola.hallengren.com/license.html //-- --// GitHub: https://github.com/olahallengren/sql-server-maintenance-solution //-- - --// Version: 2026-05-18 20:15:56 //-- + --// Version: 2026-08-23 14:46:45 //-- ---------------------------------------------------------------------------------------------------- SET NOCOUNT ON @@ -498,9 +507,25 @@ BEGIN 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 @Parameters TABLE (ID int IDENTITY PRIMARY KEY, + [Name] nvarchar(max) NOT NULL, + ValueNvarchar nvarchar(max), + ValueInt int, + ValueDatetime datetime2) + + DECLARE @ParametersString nvarchar(max) + DECLARE @CurrentParameterName nvarchar(max) + DECLARE @CurrentParameterValueNvarchar nvarchar(max) + DECLARE @CurrentParameterValueInt int + DECLARE @CurrentParameterValueDatetime datetime2 + DECLARE @CurrentParameterDelimiter nvarchar(max) + DECLARE @CurrentParameterMessage nvarchar(max) DECLARE @HostPlatform nvarchar(max) + DECLARE @ContainedAvailabilityGroupID uniqueidentifier + DECLARE @ContainedAvailabilityGroupListenerConnection bit + DECLARE @IsSysadmin bit = IS_SRVROLEMEMBER('sysadmin') DECLARE @DirectorySeparator nvarchar(max) DECLARE @Updated bit @@ -537,11 +562,12 @@ BEGIN DECLARE @CurrentDifferentialBaseLSN numeric(25,0) DECLARE @CurrentDifferentialBaseIsSnapshot bit DECLARE @CurrentLogLSN numeric(25,0) + DECLARE @BackupInProgress bit DECLARE @CurrentLatestBackup datetime2 DECLARE @CurrentDatabaseNameFS nvarchar(max) DECLARE @CurrentDirectoryStructure nvarchar(max) DECLARE @CurrentDatabaseFileName nvarchar(max) - DECLARE @CurrentMaxFilePathLength nvarchar(max) + DECLARE @CurrentMaxFilePathLength int DECLARE @CurrentFileName nvarchar(max) DECLARE @CurrentDirectoryID int DECLARE @CurrentDirectoryPath nvarchar(4000) @@ -549,16 +575,21 @@ BEGIN DECLARE @CurrentDate datetime2 DECLARE @CurrentDateUTC datetime2 DECLARE @CurrentCleanupDate datetime2 - DECLARE @CurrentReplicaID uniqueidentifier + DECLARE @CurrentAvailabilityGroupReplicaID 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 @CurrentSecondaryRoleAllowConnections nvarchar(max) + DECLARE @CurrentDistributedAvailabilityGroup nvarchar(max) + DECLARE @CurrentDistributedAvailabilityGroupReplicaID uniqueidentifier + DECLARE @CurrentDistributedAvailabilityGroupRole nvarchar(max) DECLARE @CurrentIsPreferredBackupReplica bit DECLARE @CurrentDatabaseMirroringRole nvarchar(max) DECLARE @CurrentLogShippingRole nvarchar(max) + DECLARE @CurrentIsSeeding bit DECLARE @CurrentBackupOperationSupportedOnSecondaryReplicas bit DECLARE @CurrentBackupSetID int @@ -576,7 +607,7 @@ BEGIN DECLARE @Errors TABLE (ID int IDENTITY PRIMARY KEY, [Message] nvarchar(max) NOT NULL, Severity int NOT NULL, - [State] int) + [State] int NOT NULL) DECLARE @CurrentMessage nvarchar(max) DECLARE @CurrentSeverity int @@ -596,29 +627,29 @@ BEGIN ParentDirectoryExists bit) DECLARE @tmpDatabases TABLE (ID int IDENTITY, - DatabaseName nvarchar(max), - DatabaseNameFS nvarchar(max), - DatabaseType nvarchar(max), + DatabaseName nvarchar(128), + DatabaseNameFS nvarchar(128), + DatabaseType nvarchar(1), AvailabilityGroup bit, StartPosition int, DatabaseSize bigint, LogSizeSinceLastLogBackup float, - [Order] int, - Selected bit, - Completed bit, - PRIMARY KEY(Selected, Completed, [Order], ID)) + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) DECLARE @tmpAvailabilityGroups TABLE (ID int IDENTITY PRIMARY KEY, - AvailabilityGroupName nvarchar(max), + AvailabilityGroupName nvarchar(128), StartPosition int, - Selected bit) + Selected bit DEFAULT 0) - DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(max), - AvailabilityGroupName nvarchar(max)) + DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(128), + AvailabilityGroupName nvarchar(128)) DECLARE @SelectedDatabases TABLE (DatabaseName nvarchar(max), - DatabaseType nvarchar(max), - AvailabilityGroup nvarchar(max), + DatabaseType nvarchar(1), + AvailabilityGroup bit, StartPosition int, Selected bit) @@ -649,129 +680,176 @@ BEGIN Mirror bit, DirectoryNumber int) - DECLARE @CurrentFiles TABLE ([Type] nvarchar(max), + DECLARE @CurrentFiles TABLE ([Type] nvarchar(4), FilePath nvarchar(max), Mirror bit) - DECLARE @CurrentCleanupDates TABLE (CleanupDate datetime2, + DECLARE @CurrentCleanupDates TABLE ([Type] nvarchar(max), + CleanupDate datetime2, Mirror bit) + DECLARE @EncryptionKeyMasked nvarchar(max) = CASE WHEN @EncryptionKey IS NULL THEN NULL ELSE '********' END + + DECLARE @EncryptionKeyPlaceholder nvarchar(max) = CASE WHEN @EncryptionKey IS NULL THEN NULL ELSE CAST(NEWID() AS nvarchar(max)) END + 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)) + DECLARE @ProductVersion nvarchar(max) = CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + DECLARE @ProductMajorVersion nvarchar(max) = CAST(SERVERPROPERTY('ProductMajorVersion') AS nvarchar(max)) + DECLARE @ProductMinorVersion nvarchar(max) = CAST(SERVERPROPERTY('ProductMinorVersion') AS nvarchar(max)) + DECLARE @ProductUpdateType nvarchar(max) = CAST(SERVERPROPERTY('ProductUpdateType') AS nvarchar(max)) + DECLARE @EngineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int) + DECLARE @EditionID bigint = CAST(SERVERPROPERTY('EditionID') AS bigint) + DECLARE @Edition nvarchar(max) = CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + DECLARE @IsHadrEnabled bit = CAST(SERVERPROPERTY('IsHadrEnabled') AS bit) + DECLARE @IsClustered bit = CAST(SERVERPROPERTY('IsClustered') AS bit) + DECLARE @ServerName nvarchar(max) = CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) + DECLARE @InstanceName nvarchar(max) = CAST(SERVERPROPERTY('InstanceName') AS nvarchar(max)) + DECLARE @MachineName nvarchar(max) = CAST(SERVERPROPERTY('MachineName') AS nvarchar(max)) + DECLARE @InstanceDefaultBackupPath nvarchar(max) = CAST(SERVERPROPERTY('InstanceDefaultBackupPath') AS nvarchar(max)) - IF @Version >= 14 + DECLARE @Version numeric(18,10) = CAST(PARSENAME(@ProductVersion,4) + '.' + PARSENAME(@ProductVersion,3) + PARSENAME(@ProductVersion,2) AS numeric(18,10)) + + IF @EngineEdition = 8 AND @ProductVersion = '12.0.2000.8' AND @ProductUpdateType = 'CU' BEGIN - SELECT @HostPlatform = host_platform - FROM sys.dm_os_host_info + SET @Version = 16.01000 END - ELSE + + SELECT @HostPlatform = host_platform + FROM sys.dm_os_host_info + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) BEGIN - SET @HostPlatform = 'Windows' + SET @CurrentCommand = 'SELECT @ParamContainedAvailabilityGroupID = contained_availability_group_id FROM sys.dm_exec_sessions WHERE session_id = @@SPID' + + EXECUTE sp_executesql @stmt = @CurrentCommand, @params = N'@ParamContainedAvailabilityGroupID uniqueidentifier OUTPUT', @ParamContainedAvailabilityGroupID = @ContainedAvailabilityGroupID OUTPUT END - DECLARE @AmazonRDS bit = CASE WHEN EXISTS (SELECT * FROM sys.databases WHERE [name] = 'rdsadmin') AND SUSER_SNAME(0x01) = 'rdsa' THEN 1 ELSE 0 END + SET @ContainedAvailabilityGroupListenerConnection = CASE WHEN @ContainedAvailabilityGroupID IS NOT NULL THEN 1 ELSE 0 END + + DECLARE @AmazonRDS bit = CASE WHEN @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 += ', @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) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Databases', @Databases) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Directory', @Directory) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@BackupType', @BackupType) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Verify', @Verify) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@CleanupTime', @CleanupTime) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@CleanupMode', @CleanupMode) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Compress', @Compress) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@CompressionAlgorithm', @CompressionAlgorithm) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@CompressionLevel', @CompressionLevel) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@CopyOnly', @CopyOnly) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ChangeBackupType', @ChangeBackupType) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@BackupSoftware', @BackupSoftware) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Checksum', @Checksum) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@BlockSize', @BlockSize) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@BufferCount', @BufferCount) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MaxTransferSize', @MaxTransferSize) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@NumberOfFiles', @NumberOfFiles) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MinBackupSizeForMultipleFiles', @MinBackupSizeForMultipleFiles) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MaxFileSize', @MaxFileSize) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@CompressionLevelNumeric', @CompressionLevelNumeric) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Description', @Description) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@BackupSetName', @BackupSetName) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@Threads', @Threads) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@Throttle', @Throttle) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Encrypt', @Encrypt) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@EncryptionAlgorithm', @EncryptionAlgorithm) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ServerCertificate', @ServerCertificate) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ServerAsymmetricKey', @ServerAsymmetricKey) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@EncryptionKey', @EncryptionKeyMasked) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ReadWriteFileGroups', @ReadWriteFileGroups) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@OverrideBackupPreference', @OverrideBackupPreference) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@NoRecovery', @NoRecovery) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@URL', @URL) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Credential', @Credential) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@MirrorDirectory', @MirrorDirectory) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MirrorCleanupTime', @MirrorCleanupTime) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@MirrorCleanupMode', @MirrorCleanupMode) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@MirrorURL', @MirrorURL) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroups', @AvailabilityGroups) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Updateability', @Updateability) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AdaptiveCompression', @AdaptiveCompression) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MinModificationLevel', @MinModificationLevel) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MinDatabaseSizeForDifferentialBackup', @MinDatabaseSizeForDifferentialBackup) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MinLogSizeSinceLastLogBackup', @MinLogSizeSinceLastLogBackup) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MinTimeSinceLastLogBackup', @MinTimeSinceLastLogBackup) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataDomainBoostHost', @DataDomainBoostHost) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataDomainBoostUser', @DataDomainBoostUser) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataDomainBoostDevicePath', @DataDomainBoostDevicePath) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataDomainBoostLockboxPath', @DataDomainBoostLockboxPath) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataDomainBoostNoOutputTable', @DataDomainBoostNoOutputTable) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DirectoryStructure', @DirectoryStructure) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroupDirectoryStructure', @AvailabilityGroupDirectoryStructure) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DirectoryStructureCase', @DirectoryStructureCase) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FileName', @FileName) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroupFileName', @AvailabilityGroupFileName) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FileNameCase', @FileNameCase) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@TokenTimezone', @TokenTimezone) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FileExtensionFull', @FileExtensionFull) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FileExtensionDiff', @FileExtensionDiff) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FileExtensionLog', @FileExtensionLog) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Init', @Init) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Format', @Format) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ObjectLevelRecoveryMap', @ObjectLevelRecoveryMap) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ExcludeLogShippedFromLogBackup', @ExcludeLogShippedFromLogBackup) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ExcludeSeedingFromLogBackup', @ExcludeSeedingFromLogBackup) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DirectoryCheck', @DirectoryCheck) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@BackupOptions', @BackupOptions) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@Stats', @Stats) + INSERT INTO @Parameters ([Name], ValueDatetime) VALUES('@ExpireDate', @ExpireDate) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@RetainDays', @RetainDays) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AllowNonCopyOnlyBackupsOnForwarder', @AllowNonCopyOnlyBackupsOnForwarder) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StringDelimiter', @StringDelimiter) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabaseOrder', @DatabaseOrder) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabasesInParallel', @DatabasesInParallel) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@LogToTable', @LogToTable) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Execute', @Execute) + + SELECT @ParametersString = STRING_AGG(CAST([Name] + ' = ' + CASE WHEN ValueNvarchar IS NOT NULL THEN '''' + REPLACE(ValueNvarchar,'''','''''') + '''' WHEN ValueInt IS NOT NULL THEN CAST(ValueInt AS nvarchar(max)) WHEN ValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), ValueDatetime, 21) + '''' ELSE 'NULL' END AS nvarchar(max)), ', ') WITHIN GROUP (ORDER BY [ID] ASC) + FROM @Parameters + + SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar(max),@StartTime,120) + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'Server: ' + @ServerName RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Server: ' + CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) + SET @StartMessage = 'Version: ' + @ProductVersion RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Version: ' + CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + SET @StartMessage = 'Edition: ' + @Edition RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Edition: ' + CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + IF @EngineEdition = 8 + BEGIN + SET @StartMessage = 'Update type: ' + @ProductUpdateType + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + SET @StartMessage = 'Platform: ' + ISNULL(@HostPlatform, 'N/A') RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Platform: ' + @HostPlatform + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) + BEGIN + SET @StartMessage = 'Contained availability group connection: ' + CASE WHEN @ContainedAvailabilityGroupListenerConnection = 1 THEN 'Yes' WHEN @ContainedAvailabilityGroupListenerConnection = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + SET @StartMessage = 'Is sysadmin: ' + CASE WHEN @IsSysadmin = 1 THEN 'Yes' WHEN @IsSysadmin = 0 THEN 'No' ELSE 'N/A' END RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Procedure: ' + QUOTENAME(DB_NAME()) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + SET @StartMessage = 'Database: ' + QUOTENAME(DB_NAME()) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Parameters: ' + @Parameters + SET @StartMessage = 'Procedure: ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT SET @StartMessage = 'Version: ' + @VersionTimestamp @@ -779,6 +857,32 @@ BEGIN SET @StartMessage = 'Source: https://ola.hallengren.com' RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + + SET @StartMessage = 'Command:' + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'EXECUTE ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + DECLARE ParameterCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Name], ValueNvarchar, ValueInt, ValueDatetime, CASE WHEN [ID] = MAX([ID]) OVER() THEN '' ELSE ',' END FROM @Parameters ORDER BY [ID] ASC + + OPEN ParameterCursor + + FETCH ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + + WHILE @@FETCH_STATUS = 0 + BEGIN + SET @CurrentParameterMessage = @CurrentParameterName + ' = ' + CASE WHEN @CurrentParameterValueNvarchar IS NOT NULL THEN '''' + REPLACE(@CurrentParameterValueNvarchar,'''','''''') + '''' WHEN @CurrentParameterValueInt IS NOT NULL THEN CAST(@CurrentParameterValueInt AS nvarchar(max)) WHEN @CurrentParameterValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), @CurrentParameterValueDatetime, 21) + '''' ELSE 'NULL' END + @CurrentParameterDelimiter + + RAISERROR('%s',10,1,@CurrentParameterMessage) WITH NOWAIT + + FETCH NEXT FROM ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + END + + CLOSE ParameterCursor + + DEALLOCATE ParameterCursor RAISERROR(@EmptyLine,10,1) WITH NOWAIT @@ -786,64 +890,58 @@ BEGIN --// 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 + VALUES('ANSI_NULLS has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-backup.html.', 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 + VALUES('QUOTED_IDENTIFIER has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-backup.html.', 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 + VALUES('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%') + 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 '%@EncryptionKeyPlaceholder%') 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 + VALUES('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 + VALUES('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 + VALUES('The table Queue is missing. It is required when @DatabasesInParallel = ''Y''. 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 + VALUES('The table QueueDatabase is missing. It is required when @DatabasesInParallel = ''Y''. 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 + VALUES('The stored procedure cannot be executed inside a transaction. The transaction count (@@TRANCOUNT) has to be 0. See https://ola.hallengren.com/sql-server-backup.html.', 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 + VALUES('The stored procedure DatabaseBackup is not supported on Amazon RDS. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -905,11 +1003,10 @@ BEGIN FROM Databases4 OPTION (MAXRECURSION 0) - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN - INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName, Selected) - SELECT name AS AvailabilityGroupName, - 0 AS Selected + INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName) + SELECT name AS AvailabilityGroupName FROM sys.availability_groups INSERT INTO @tmpDatabasesAvailabilityGroups (DatabaseName, AvailabilityGroupName) @@ -920,14 +1017,11 @@ BEGIN 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) + INSERT INTO @tmpDatabases (DatabaseName, DatabaseNameFS, DatabaseType, AvailabilityGroup) 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 + NULL AS AvailabilityGroup FROM sys.databases WHERE [name] <> 'tempdb' AND source_database_id IS NULL @@ -941,7 +1035,7 @@ BEGIN SET tmpDatabases.Selected = SelectedDatabases.Selected FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -950,7 +1044,7 @@ BEGIN SET tmpDatabases.Selected = SelectedDatabases.Selected FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -961,7 +1055,7 @@ BEGIN INNER JOIN (SELECT tmpDatabases.DatabaseName, MIN(SelectedDatabases.StartPosition) AS StartPosition FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -971,14 +1065,14 @@ BEGIN 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 + VALUES('The value for the parameter @Databases is not supported. The value could not be parsed into a list of databases. See https://ola.hallengren.com/sql-server-backup.html#Databases.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Select availability groups //-- ---------------------------------------------------------------------------------------------------- - IF @AvailabilityGroups IS NOT NULL AND @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 1 BEGIN SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, CHAR(10), '') @@ -1032,14 +1126,14 @@ BEGIN SET tmpAvailabilityGroups.Selected = SelectedAvailabilityGroups.Selected FROM @tmpAvailabilityGroups tmpAvailabilityGroups INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups - ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(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,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'[','[[]'),'_','[_]') WHERE SelectedAvailabilityGroups.Selected = 0 UPDATE tmpAvailabilityGroups @@ -1048,7 +1142,7 @@ BEGIN INNER JOIN (SELECT tmpAvailabilityGroups.AvailabilityGroupName, MIN(SelectedAvailabilityGroups.StartPosition) AS StartPosition FROM @tmpAvailabilityGroups tmpAvailabilityGroups INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups - ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'[','[[]'),'_','[_]') WHERE SelectedAvailabilityGroups.Selected = 1 GROUP BY tmpAvailabilityGroups.AvailabilityGroupName) SelectedAvailabilityGroups2 ON tmpAvailabilityGroups.AvailabilityGroupName = SelectedAvailabilityGroups2.AvailabilityGroupName @@ -1063,29 +1157,35 @@ BEGIN 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) + IF @AvailabilityGroups IS NOT NULL AND (NOT EXISTS(SELECT * FROM @SelectedAvailabilityGroups) OR EXISTS(SELECT * FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName IS NULL OR AvailabilityGroupName = '')) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @AvailabilityGroups is not supported. The value could not be parsed into a list of availability groups. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroups.', 16, 1) + END + + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroups is not supported.', 16, 1 + VALUES('The parameter @AvailabilityGroups can only be used when availability groups are enabled on the instance. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroups.', 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 + VALUES('You need to specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) 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 + VALUES('You can only specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Check database names //-- ---------------------------------------------------------------------------------------------------- - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @tmpDatabases WHERE Selected = 1 @@ -1094,10 +1194,10 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The names of the following databases are not supported: ' + @ErrorMessage + '.', 16, 1 + VALUES('The names of the following databases are not supported: ' + @ErrorMessage + '. A database name has to contain at least one character that can be used in file names. See https://ola.hallengren.com/sql-server-backup.html#Databases.', 16, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') WITHIN GROUP (ORDER BY DatabaseName ASC) 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) @@ -1107,7 +1207,7 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The names of the following databases are not unique in the file system: ' + @ErrorMessage + '.', 16, 1 + VALUES('The names of the following databases are not unique in the file system: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-backup.html#Databases.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1116,9 +1216,9 @@ BEGIN IF @Directory IS NULL AND @URL IS NULL AND (@BackupSoftware <> 'DATA_DOMAIN_BOOST' OR @BackupSoftware IS NULL) BEGIN - IF @Version >= 15 + IF @Version >= 15 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous') BEGIN - SET @DefaultDirectory = CAST(SERVERPROPERTY('InstanceDefaultBackupPath') AS nvarchar(max)) + SET @DefaultDirectory = @InstanceDefaultBackupPath END ELSE BEGIN @@ -1132,7 +1232,7 @@ BEGIN ELSE BEGIN INSERT INTO @Directories (ID, DirectoryPath, Mirror, Completed) - SELECT 1, @DefaultDirectory, 0, 0 + VALUES(1, @DefaultDirectory, 0, 0) END END @@ -1172,8 +1272,8 @@ BEGIN 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,' ,',',') + WHILE CHARINDEX(@StringDelimiter + ' ', @MirrorDirectory) > 0 SET @MirrorDirectory = REPLACE(@MirrorDirectory, @StringDelimiter + ' ', @StringDelimiter) + WHILE CHARINDEX(' ' + @StringDelimiter, @MirrorDirectory) > 0 SET @MirrorDirectory = REPLACE(@MirrorDirectory, ' ' + @StringDelimiter, @StringDelimiter) SET @MirrorDirectory = LTRIM(RTRIM(@MirrorDirectory)); @@ -1205,43 +1305,73 @@ BEGIN 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 + VALUES('The value for the parameter @Directory is not supported. Specify a local path (e.g. D:\Backup), a UNC path (e.g. \\Server\Share), a path starting with / on Linux, or NUL, without leading or trailing spaces. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 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 + VALUES('The same directory has been specified multiple times in the parameters @Directory and @MirrorDirectory. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) 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 + VALUES('The number of directories for the parameters @Directory and @MirrorDirectory has to be the same. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) + END + + IF @Directory IS NOT NULL AND @EngineEdition = 8 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @Directory is not supported on Azure SQL Managed Instance. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) END - IF (@Directory IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 8) OR (@Directory IS NOT NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST') + IF @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 + VALUES('The parameter @Directory is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) 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 + VALUES('The value for the parameter @Directory is not supported. Backup to NUL cannot be combined with other directories. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) 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 + VALUES('Mirrored backup is not supported when backing up to NUL. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END IF EXISTS (SELECT * FROM @Directories WHERE Mirror = 0 AND DirectoryPath = 'NUL') AND @BackupSoftware IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'Backup to NUL is only supported with SQL Server native backups.', 16, 7 + VALUES('Backup to NUL is only supported with SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) + END + + IF (SELECT COUNT(*) FROM @Directories WHERE Mirror = 0) > 64 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Directory is not supported. The maximum number of directories is 64. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) + END + + IF (SELECT COUNT(*) FROM @Directories WHERE Mirror = 0) > 32 AND @BackupSoftware = 'SQLBACKUP' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Directory is not supported. The maximum number of directories with Redgate SQL Backup Pro is 32. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) + END + + IF (SELECT COUNT(*) FROM @Directories WHERE Mirror = 0) > 32 AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) AND @BackupSoftware IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Directory is not supported. The maximum number of directories when performing mirrored SQL Server native backups is 32. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) + END + + IF (SELECT COUNT(*) FROM @Directories WHERE Mirror = 0) > 32 AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) AND @BackupSoftware = 'LITESPEED' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Directory is not supported. The maximum number of directories when performing mirrored backups with LiteSpeed for SQL Server is 32. See https://ola.hallengren.com/sql-server-backup.html#Directory.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1249,37 +1379,37 @@ BEGIN 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 + VALUES('The value for the parameter @MirrorDirectory is not supported. Specify a local path (e.g. D:\Backup), a UNC path (e.g. \\Server\Share), or a path starting with / on Linux, without leading or trailing spaces. See https://ola.hallengren.com/sql-server-backup.html#MirrorDirectory.', 16, 1) END - IF EXISTS (SELECT * FROM @Directories GROUP BY DirectoryPath HAVING COUNT(*) <> 1) + IF @BackupSoftware = 'SQLBACKUP' 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, 2 + VALUES('The value for the parameter @MirrorDirectory is not supported. Redgate SQL Backup Pro supports only one mirror directory. See https://ola.hallengren.com/sql-server-backup.html#MirrorDirectory.', 16, 1) END - IF @BackupSoftware IN('SQLBACKUP','SQLSAFE') AND (SELECT COUNT(*) FROM @Directories WHERE Mirror = 1) > 1 + IF @BackupSoftware = '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 + VALUES('The value for the parameter @MirrorDirectory is not supported. Idera SQL Safe Backup supports only one mirror directory. See https://ola.hallengren.com/sql-server-backup.html#MirrorDirectory.', 16, 1) END - IF @MirrorDirectory IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 8 + IF @MirrorDirectory IS NOT NULL AND @EngineEdition = 8 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MirrorDirectory is not supported.', 16, 5 + VALUES('The parameter @MirrorDirectory is not supported on Azure SQL Managed Instance. See https://ola.hallengren.com/sql-server-backup.html#MirrorDirectory.', 16, 1) 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 + VALUES('The value for the parameter @MirrorDirectory is not supported. Mirrored backup is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#MirrorDirectory.', 16, 1) END - IF (@BackupSoftware IS NULL AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) AND SERVERPROPERTY('EngineEdition') <> 3) + IF (@BackupSoftware IS NULL AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) AND @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 + VALUES('The value for the parameter @MirrorDirectory is not supported. Mirrored backup to disk is not supported in this edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#MirrorDirectory.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1309,7 +1439,7 @@ BEGIN 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 + VALUES('The directory ' + @CurrentRootDirectoryPath + ' does not exist. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END UPDATE @Directories @@ -1390,39 +1520,65 @@ BEGIN 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 + VALUES('The value for the parameter @URL is not supported. The URL has to start with https:// (Azure Blob Storage) or s3:// (S3-compatible storage). See https://ola.hallengren.com/sql-server-backup.html#URL.', 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 + VALUES('The same URL has been specified multiple times in the parameters @URL and @MirrorURL. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) 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 + VALUES('The number of URLs for the parameters @URL and @MirrorURL has to be the same. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END - ---------------------------------------------------------------------------------------------------- + IF EXISTS(SELECT * FROM @URLs WHERE DirectoryPath LIKE 's3://%/%') AND NOT (@Version >= 16 AND @EngineEdition IN(2, 3)) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Backup to S3-compatible storage is not supported in this version and edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#URL.', 16, 1) + END - IF EXISTS(SELECT * FROM @URLs WHERE Mirror = 1 AND NOT (DirectoryPath LIKE 'https://%/%' OR DirectoryPath LIKE 's3://%/%')) + IF EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 's3://%/%') + AND EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 'https://%/%') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 1 + VALUES('Striped backups across S3-compatible storage and Azure Blob Storage are not supported. See https://ola.hallengren.com/sql-server-backup.html#URL.', 16, 1) END - IF EXISTS (SELECT * FROM @URLs GROUP BY DirectoryPath HAVING COUNT(*) <> 1) + IF (SELECT COUNT(*) FROM @URLs WHERE Mirror = 0) > 64 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 2 + VALUES('The value for the parameter @URL is not supported. The maximum number of URLs is 64. See https://ola.hallengren.com/sql-server-backup.html#URL.', 16, 1) 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 + IF (SELECT COUNT(*) FROM @URLs WHERE Mirror = 0) > 32 AND EXISTS(SELECT * FROM @URLs WHERE Mirror = 1) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @URL is not supported. The maximum number of URLs when performing mirrored backups is 32. See https://ola.hallengren.com/sql-server-backup.html#URL.', 16, 1) + END + + IF @URL IS NOT NULL AND @Credential IS NOT NULL AND (SELECT COUNT(*) FROM @URLs WHERE Mirror = 0) > 1 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @URL is not supported. Backup striping to URL with page blobs is not supported. See https://ola.hallengren.com/sql-server-backup.html#URL.', 16, 1) + 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]) + VALUES('The value for the parameter @MirrorURL is not supported. The URL has to start with https:// (Azure Blob Storage) or s3:// (S3-compatible storage). See https://ola.hallengren.com/sql-server-backup.html#MirrorURL.', 16, 1) + END + + IF (EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 'https://%/%') AND EXISTS(SELECT * FROM @URLs WHERE Mirror = 1 AND DirectoryPath LIKE 's3://%/%')) + OR (EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 's3://%/%') AND EXISTS(SELECT * FROM @URLs WHERE Mirror = 1 AND DirectoryPath LIKE 'https://%/%')) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 3 + VALUES('Mirrored backups across S3-compatible storage and Azure Blob Storage are not supported. See https://ola.hallengren.com/sql-server-backup.html#MirrorURL.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1503,7 +1659,7 @@ BEGIN --// Get default compression algorithm //-- ---------------------------------------------------------------------------------------------------- - IF @CompressionAlgorithm IS NULL AND @BackupSoftware IS NULL AND @Version >= 16 + IF @CompressionAlgorithm IS NULL AND @BackupSoftware IS NULL AND (@Version >= 16 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) 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' @@ -1514,7 +1670,7 @@ BEGIN --// Get default compression level //-- ---------------------------------------------------------------------------------------------------- - IF @CompressionLevel IS NULL AND @BackupSoftware IS NULL AND @Version >= 17 + IF @CompressionLevel IS NULL AND @BackupSoftware IS NULL AND (@Version >= 17 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) BEGIN SET @CompressionLevel = 'LOW' END @@ -1526,15 +1682,15 @@ BEGIN 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 + VALUES('The value for the parameter @BackupType is not supported. Supported values are FULL, DIFF and LOG. See https://ola.hallengren.com/sql-server-backup.html#BackupType.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF SERVERPROPERTY('EngineEdition') = 8 AND NOT (@BackupType = 'FULL' AND @CopyOnly = 'Y') + IF @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 + VALUES('Azure SQL Managed Instance only supports COPY_ONLY full backups. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1542,25 +1698,25 @@ BEGIN 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 + VALUES('The value for the parameter @Verify is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Verify.', 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 + VALUES('Verify is not supported for encrypted backups with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#Verify.', 16, 1) 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 + VALUES('Verify is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#Verify.', 16, 1) 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 + VALUES('Verify is not supported when backing up to NUL. See https://ola.hallengren.com/sql-server-backup.html#Verify.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1568,37 +1724,37 @@ BEGIN IF @CleanupTime < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @CleanupTime is not supported.', 16, 1 + VALUES('The value for the parameter @CleanupTime is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-backup.html#CleanupTime.', 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 + VALUES('The value for the parameter @CleanupTime is not supported. Cleanup is not supported when backing up to URL. See https://ola.hallengren.com/sql-server-backup.html#CleanupTime.', 16, 1) 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 + VALUES('The value for the parameter @CleanupTime is not supported. Cleanup is not supported when backing up to NUL. See https://ola.hallengren.com/sql-server-backup.html#CleanupTime.', 16, 1) 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))) + IF @CleanupTime IS NOT NULL AND ((@DirectoryStructure NOT LIKE '%{DatabaseName}%' OR @DirectoryStructure IS NULL) OR (@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 + VALUES('The value for the parameter @CleanupTime is not supported. Cleanup is not supported if the token {DatabaseName} is not part of the directory structure. See https://ola.hallengren.com/sql-server-backup.html#CleanupTime.', 16, 1) 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 + IF @CleanupTime IS NOT NULL AND ((@DirectoryStructure NOT LIKE '%{BackupType}%' OR @DirectoryStructure IS NULL) OR (@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 + VALUES('The value for the parameter @CleanupTime is not supported. Cleanup is not supported if the token {BackupType} is not part of the directory structure and the file extensions are not unique. See https://ola.hallengren.com/sql-server-backup.html#CleanupTime.', 16, 1) 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))) + IF @CleanupTime IS NOT NULL AND @CopyOnly = 'Y' AND ((@DirectoryStructure NOT LIKE '%{CopyOnly}%' OR @DirectoryStructure IS NULL) OR (@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 + VALUES('The value for the parameter @CleanupTime is not supported. Cleanup is not supported if the token {CopyOnly} is not part of the directory structure. See https://ola.hallengren.com/sql-server-backup.html#CleanupTime.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1606,7 +1762,7 @@ BEGIN 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 + VALUES('The value for the parameter @CleanupMode is not supported. Supported values are BEFORE_BACKUP and AFTER_BACKUP. See https://ola.hallengren.com/sql-server-backup.html#CleanupMode.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1614,27 +1770,38 @@ BEGIN 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 + VALUES('The value for the parameter @Compress is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Compress.', 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, -1785266663)))) + AND NOT (@EngineEdition IN (3, 8) OR @EditionID IN (-1534726760, -1785266663)) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Backup compression is not supported in this edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#Compress.', 16, 1) + END + + IF @Compress = 'N' AND @BackupSoftware = 'LITESPEED' AND (@CompressionLevelNumeric IS NULL OR @CompressionLevelNumeric >= 1) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Setting @Compress to ''N'' with LiteSpeed for SQL Server requires @CompressionLevelNumeric = 0. See https://ola.hallengren.com/sql-server-backup.html#Compress.', 16, 1) + END + + IF @Compress = 'N' AND @BackupSoftware = 'SQLBACKUP' AND (@CompressionLevelNumeric IS NULL OR @CompressionLevelNumeric >= 1) 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 + VALUES('Setting @Compress to ''N'' with Redgate SQL Backup Pro requires @CompressionLevelNumeric = 0. See https://ola.hallengren.com/sql-server-backup.html#Compress.', 16, 1) END - IF @Compress = 'N' AND @BackupSoftware IN ('LITESPEED','SQLBACKUP','SQLSAFE') AND (@CompressionLevelNumeric IS NULL OR @CompressionLevelNumeric >= 1) + IF @Compress = 'N' AND @BackupSoftware = '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 + VALUES('Setting @Compress to ''N'' with Idera SQL Safe Backup requires @CompressionLevelNumeric = 0. See https://ola.hallengren.com/sql-server-backup.html#Compress.', 16, 1) 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 + VALUES('Setting @Compress to ''Y'' cannot be combined with @CompressionLevelNumeric = 0. See https://ola.hallengren.com/sql-server-backup.html#Compress.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1642,31 +1809,31 @@ BEGIN 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 + VALUES('The value for the parameter @CompressionAlgorithm is not supported. Supported values are MS_XPRESS, QAT_DEFLATE and ZSTD. See https://ola.hallengren.com/sql-server-backup.html#CompressionAlgorithm.', 16, 1) END - IF @CompressionAlgorithm IS NOT NULL AND NOT (@Version >= 16) + IF @CompressionAlgorithm IS NOT NULL AND NOT (@Version >= 16 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) 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 + VALUES('The parameter @CompressionAlgorithm is not supported in this version and edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#CompressionAlgorithm.', 16, 1) END - IF @CompressionAlgorithm = 'QAT_DEFLATE' AND NOT (SERVERPROPERTY('EngineEdition') IN(2, 3)) + IF @CompressionAlgorithm = 'QAT_DEFLATE' AND NOT (@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 + VALUES('Setting @CompressionAlgorithm to QAT_DEFLATE is not supported in this edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#CompressionAlgorithm.', 16, 1) END - IF @CompressionAlgorithm = 'ZSTD' AND NOT (@Version >= 17) + IF @CompressionAlgorithm = 'ZSTD' AND NOT (@Version >= 17 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) 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 + VALUES('Setting @CompressionAlgorithm to ZSTD is not supported in this version and edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#CompressionAlgorithm.', 16, 1) 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 + VALUES('The parameter @CompressionAlgorithm is only supported with SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html#CompressionAlgorithm.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1674,19 +1841,19 @@ BEGIN 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 + VALUES('The parameter @CompressionLevel is only supported with SQL Server native backups. For third-party backup software, use the parameter @CompressionLevelNumeric. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevel.', 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 + VALUES('The value for the parameter @CompressionLevel is not supported. Supported values are LOW, MEDIUM and HIGH. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevel.', 16, 1) END - IF @CompressionLevel IS NOT NULL AND NOT (@Version >= 17) + IF @CompressionLevel IS NOT NULL AND NOT (@Version >= 17 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) 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 + VALUES('The parameter @CompressionLevel is not supported in this version and edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevel.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1694,7 +1861,13 @@ BEGIN 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 + VALUES('The value for the parameter @CopyOnly is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#CopyOnly.', 16, 1) + END + + IF @CopyOnly = 'Y' AND @BackupType = 'DIFF' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Differential copy-only backups are not supported. See https://ola.hallengren.com/sql-server-backup.html#CopyOnly.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1702,7 +1875,13 @@ BEGIN 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 + VALUES('The value for the parameter @ChangeBackupType is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#ChangeBackupType.', 16, 1) + END + + IF @ChangeBackupType = 'Y' AND NOT @BackupType IN ('DIFF', 'LOG') + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Setting @ChangeBackupType to ''Y'' is only supported with differential and log backups. See https://ola.hallengren.com/sql-server-backup.html#ChangeBackupType.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1710,37 +1889,37 @@ BEGIN 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 + VALUES('The value for the parameter @BackupSoftware is not supported. Supported values are LITESPEED, SQLBACKUP, SQLSAFE and DATA_DOMAIN_BOOST. See https://ola.hallengren.com/sql-server-backup.html#BackupSoftware.', 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 + VALUES('The value for the parameter @BackupSoftware is not supported. Only native backups are supported on Linux. See https://ola.hallengren.com/sql-server-backup.html#BackupSoftware.', 16, 1) 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 + VALUES('LiteSpeed for SQL Server is not installed. Download https://www.quest.com/products/litespeed-for-sql-server/.', 16, 1) 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 + VALUES('Redgate SQL Backup Pro is not installed. Download https://www.red-gate.com/products/sql-backup/.', 16, 1) 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 + VALUES('Idera SQL Safe Backup is not installed. Download https://www.idera.com/products/sql-safe-backup/.', 16, 1) 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 + VALUES('Data Domain Boost is not installed. Download https://www.dell.com/en-us/shop/storage-servers-and-networking-for-business/sf/powerprotect-data-domain.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1748,7 +1927,7 @@ BEGIN 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 + VALUES('The value for the parameter @Checksum is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Checksum.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1756,83 +1935,107 @@ BEGIN 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 + VALUES('The value for the parameter @BlockSize is not supported. Supported values are 512, 1024, 2048, 4096, 8192, 16384, 32768 and 65536. See https://ola.hallengren.com/sql-server-backup.html#BlockSize.', 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 + VALUES('The parameter @BlockSize is not supported with Redgate SQL Backup Pro. See https://ola.hallengren.com/sql-server-backup.html#BlockSize.', 16, 1) 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 + VALUES('The parameter @BlockSize is not supported with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#BlockSize.', 16, 1) 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 + VALUES('BLOCKSIZE is not supported when backing up to URL with page blobs. See https://learn.microsoft.com/en-us/sql/relational-databases/backup-restore/sql-server-backup-to-url.', 16, 1) 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 + VALUES('The parameter @BlockSize is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#BlockSize.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @BufferCount <= 0 OR @BufferCount > 2147483647 + IF @BufferCount <= 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @BufferCount is not supported.', 16, 1 + VALUES('The value for the parameter @BufferCount is not supported. The value has to be greater than 0. See https://ola.hallengren.com/sql-server-backup.html#BufferCount.', 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 + VALUES('The parameter @BufferCount is not supported with Redgate SQL Backup Pro. See https://ola.hallengren.com/sql-server-backup.html#BufferCount.', 16, 1) 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 + VALUES('The parameter @BufferCount is not supported with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#BufferCount.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @MaxTransferSize < 65536 OR @MaxTransferSize > 20971520 + IF @BackupSoftware IS NULL AND @URL IS NULL AND (@MaxTransferSize < 65536 OR @MaxTransferSize > 4194304) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MaxTransferSize is not supported. The value has to be between 65536 and 4194304 when performing SQL Server native backups to disk. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) + END + + IF @BackupSoftware IS NULL AND @URL IS NOT NULL AND @Credential IS NULL AND NOT EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 's3://%/%') AND (@Version >= 16 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) AND (@MaxTransferSize < 65536 OR @MaxTransferSize > 20971520) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MaxTransferSize is not supported. The value has to be between 65536 and 20971520 when backing up to URL with block blobs on this version of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) + END + + IF @BackupSoftware IS NULL AND @URL IS NOT NULL AND @Credential IS NULL AND NOT EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 's3://%/%') AND NOT (@Version >= 16 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) AND (@MaxTransferSize < 65536 OR @MaxTransferSize > 4194304) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MaxTransferSize is not supported. The value has to be between 65536 and 4194304 when backing up to URL with block blobs on this version of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) + END + + IF @BackupSoftware IS NULL AND @URL IS NOT NULL AND @Credential IS NULL AND EXISTS(SELECT * FROM @URLs WHERE Mirror = 0 AND DirectoryPath LIKE 's3://%/%') AND (@MaxTransferSize < 5242880 OR @MaxTransferSize > 20971520) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MaxTransferSize is not supported. The value has to be between 5242880 and 20971520 when backing up to S3-compatible storage. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) + END + + IF @URL IS NOT NULL AND @Credential IS NOT NULL AND @MaxTransferSize IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MaxTransferSize is not supported.', 16, 1 + VALUES('MAXTRANSFERSIZE is not supported when backing up to URL with page blobs. See https://learn.microsoft.com/en-us/sql/relational-databases/backup-restore/sql-server-backup-to-url.', 16, 1) END - IF @MaxTransferSize > 1048576 AND @BackupSoftware = 'SQLBACKUP' + IF @BackupSoftware = 'SQLBACKUP' AND (@MaxTransferSize < 65536 OR @MaxTransferSize > 1048576) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MaxTransferSize is not supported.', 16, 2 + VALUES('The value for the parameter @MaxTransferSize is not supported. The value has to be between 65536 and 1048576 when backing up using Redgate SQL Backup Pro. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) END - IF @MaxTransferSize IS NOT NULL AND @BackupSoftware = 'SQLSAFE' + IF @BackupSoftware = 'LITESPEED' AND (@MaxTransferSize < 65536 OR @MaxTransferSize > 4194304) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MaxTransferSize is not supported.', 16, 3 + VALUES('The value for the parameter @MaxTransferSize is not supported. The value has to be between 65536 and 4194304 when backing up using LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) END - IF @MaxTransferSize IS NOT NULL AND @URL IS NOT NULL AND @Credential IS NOT NULL + IF @BackupSoftware = 'SQLSAFE' AND @MaxTransferSize 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 + VALUES('The parameter @MaxTransferSize is not supported with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) END - IF @MaxTransferSize IS NOT NULL AND @BackupSoftware = 'DATA_DOMAIN_BOOST' + IF @BackupSoftware = 'DATA_DOMAIN_BOOST' AND @MaxTransferSize IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MaxTransferSize is not supported.', 16, 5 + VALUES('The parameter @MaxTransferSize is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#MaxTransferSize.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1840,87 +2043,93 @@ BEGIN 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The value has to be between 1 and 64. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The maximum number of files with Redgate SQL Backup Pro is 32. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The number of files has to be greater than or equal to the number of directories. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The number of files has to be evenly divisible by the number of directories. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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 + VALUES('Backup striping to URL with page blobs is not supported. See https://learn.microsoft.com/en-us/sql/relational-databases/backup-restore/sql-server-backup-to-url.', 16, 1) + END + + IF @NumberOfFiles > 1 AND @BackupSoftware = 'SQLBACKUP' AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @NumberOfFiles is not supported. Mirrored backups with multiple files are not supported with Redgate SQL Backup Pro. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) END - IF @NumberOfFiles > 1 AND @BackupSoftware IN('SQLBACKUP','SQLSAFE') AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) + IF @NumberOfFiles > 1 AND @BackupSoftware = '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 + VALUES('The value for the parameter @NumberOfFiles is not supported. Mirrored backups with multiple files are not supported with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The maximum number of files with Data Domain Boost is 32. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The number of files has to be greater than or equal to the number of URLs. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The number of files has to be evenly divisible by the number of URLs. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) END - IF @NumberOfFiles > 32 AND @URL LIKE 's3%' AND @MirrorURL LIKE 's3%' + IF @BackupSoftware IS NULL AND @NumberOfFiles > 32 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. The maximum number of files when performing mirrored backups to S3 storage is 32.', 16, 10 + VALUES('The value for the parameter @NumberOfFiles is not supported. The maximum number of files when performing mirrored SQL Server native backups to disk is 32. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) END - ---------------------------------------------------------------------------------------------------- - - IF @MinBackupSizeForMultipleFiles <= 0 + IF @NumberOfFiles > 32 AND EXISTS(SELECT * FROM @URLs WHERE Mirror = 1) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MinBackupSizeForMultipleFiles is not supported.', 16, 1 + VALUES('The value for the parameter @NumberOfFiles is not supported. The maximum number of files when performing mirrored backups to URL is 32. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) END - IF @MinBackupSizeForMultipleFiles IS NOT NULL AND @NumberOfFiles IS NULL + IF @BackupSoftware = 'LITESPEED' AND @NumberOfFiles > 32 AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) 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 + VALUES('The value for the parameter @NumberOfFiles is not supported. The maximum number of files when performing mirrored backups with LiteSpeed for SQL Server is 32. See https://ola.hallengren.com/sql-server-backup.html#NumberOfFiles.', 16, 1) 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') + ---------------------------------------------------------------------------------------------------- + + IF @MinBackupSizeForMultipleFiles <= 0 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 + VALUES('The value for the parameter @MinBackupSizeForMultipleFiles is not supported. The value has to be greater than 0. See https://ola.hallengren.com/sql-server-backup.html#MinBackupSizeForMultipleFiles.', 16, 1) 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') + 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. The column sys.dm_db_log_stats.log_since_last_log_backup_mb is not available in this version of SQL Server.', 16, 4 + VALUES('The parameter @MinBackupSizeForMultipleFiles can only be used together with @NumberOfFiles. See https://ola.hallengren.com/sql-server-backup.html#MinBackupSizeForMultipleFiles.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1928,25 +2137,13 @@ BEGIN IF @MaxFileSize <= 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MaxFileSize is not supported.', 16, 1 + VALUES('The value for the parameter @MaxFileSize is not supported. The value has to be greater than 0. See https://ola.hallengren.com/sql-server-backup.html#MaxFileSize.', 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 + VALUES('The parameters @MaxFileSize and @NumberOfFiles cannot be used together. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1954,31 +2151,31 @@ BEGIN 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 + VALUES('The parameter @CompressionLevelNumeric is only supported with third-party backup software. For SQL Server native backups, use the parameter @CompressionLevel. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevelNumeric.', 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 + VALUES('The value for the parameter @CompressionLevelNumeric is not supported. With LiteSpeed for SQL Server, the value has to be between 0 and 8. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevelNumeric.', 16, 1) 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 + VALUES('The value for the parameter @CompressionLevelNumeric is not supported. With Redgate SQL Backup Pro, the value has to be between 0 and 4. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevelNumeric.', 16, 1) 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 + VALUES('The value for the parameter @CompressionLevelNumeric is not supported. With Idera SQL Safe Backup, the value has to be between 1 and 4. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevelNumeric.', 16, 1) 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 + VALUES('The parameter @CompressionLevelNumeric is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#CompressionLevelNumeric.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -1986,19 +2183,25 @@ BEGIN IF LEN(@Description) > 255 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Description is not supported.', 16, 1 + VALUES('The value for the parameter @Description is not supported. The maximum length is 255 characters. See https://ola.hallengren.com/sql-server-backup.html#Description.', 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 + VALUES('The value for the parameter @Description is not supported. The maximum length with LiteSpeed for SQL Server is 128 characters. See https://ola.hallengren.com/sql-server-backup.html#Description.', 16, 1) 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 + VALUES('The value for the parameter @Description is not supported. The maximum length with Data Domain Boost is 254 characters. See https://ola.hallengren.com/sql-server-backup.html#Description.', 16, 1) + END + + IF @BackupSoftware = 'DATA_DOMAIN_BOOST' AND @Description LIKE '%"%' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Description is not supported. Double quotes (") are not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#Description.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2006,7 +2209,13 @@ BEGIN IF LEN(@BackupSetName) > 128 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @BackupSetName is not supported.', 16, 1 + VALUES('The value for the parameter @BackupSetName is not supported. The maximum length is 128 characters. See https://ola.hallengren.com/sql-server-backup.html#BackupSetName.', 16, 1) + END + + IF @BackupSoftware = 'DATA_DOMAIN_BOOST' AND @BackupSetName LIKE '%"%' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @BackupSetName is not supported. Double quotes (") are not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#BackupSetName.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2014,25 +2223,25 @@ BEGIN 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 + VALUES('The parameter @Threads is only supported with LiteSpeed for SQL Server, Redgate SQL Backup Pro and Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#Threads.', 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 + VALUES('The value for the parameter @Threads is not supported. With LiteSpeed for SQL Server, the value has to be between 1 and 32. See https://ola.hallengren.com/sql-server-backup.html#Threads.', 16, 1) 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 + VALUES('The value for the parameter @Threads is not supported. With Redgate SQL Backup Pro, the value has to be between 2 and 32. See https://ola.hallengren.com/sql-server-backup.html#Threads.', 16, 1) 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 + VALUES('The value for the parameter @Threads is not supported. With Idera SQL Safe Backup, the value has to be between 1 and 64. See https://ola.hallengren.com/sql-server-backup.html#Threads.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2040,13 +2249,13 @@ BEGIN 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 + VALUES('The value for the parameter @Throttle is not supported. The value has to be between 1 and 100. See https://ola.hallengren.com/sql-server-backup.html#Throttle.', 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 + VALUES('The parameter @Throttle is only supported with LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#Throttle.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2054,19 +2263,19 @@ BEGIN 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 + VALUES('The value for the parameter @Encrypt is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Encrypt.', 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, -1785266663))) + IF @Encrypt = 'Y' AND @BackupSoftware IS NULL AND NOT (@EngineEdition IN(3, 8) OR @EditionID IN(-1534726760, -1785266663)) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Encrypt is not supported.', 16, 2 + VALUES('Backup encryption is not supported in this edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#Encrypt.', 16, 1) 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 + VALUES('The value for the parameter @Encrypt is not supported. Encrypted backups are not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#Encrypt.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2074,32 +2283,31 @@ BEGIN 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 + VALUES('The value for the parameter @EncryptionAlgorithm is not supported. Supported values for SQL Server native backups are AES_128, AES_192, AES_256 and TRIPLE_DES_3KEY. See https://ola.hallengren.com/sql-server-backup.html#EncryptionAlgorithm.', 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 + VALUES('The value for the parameter @EncryptionAlgorithm is not supported. Supported values with LiteSpeed for SQL Server are RC2_40, RC2_56, RC2_112, RC2_128, TRIPLE_DES_3KEY, RC4_128, AES_128, AES_192 and AES_256. See https://ola.hallengren.com/sql-server-backup.html#EncryptionAlgorithm.', 16, 1) 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 + VALUES('The value for the parameter @EncryptionAlgorithm is not supported. Supported values with Redgate SQL Backup Pro are AES_128 and AES_256. See https://ola.hallengren.com/sql-server-backup.html#EncryptionAlgorithm.', 16, 1) 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 + VALUES('The value for the parameter @EncryptionAlgorithm is not supported. Supported values with Idera SQL Safe Backup are AES_128 and AES_256. See https://ola.hallengren.com/sql-server-backup.html#EncryptionAlgorithm.', 16, 1) 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 + VALUES('The parameter @EncryptionAlgorithm is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#EncryptionAlgorithm.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2107,25 +2315,25 @@ BEGIN 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 + VALUES('The parameter @ServerCertificate can only be used together with @Encrypt = ''Y'' and SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html#ServerCertificate.', 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 + VALUES('You need to specify one of the parameters @ServerCertificate and @ServerAsymmetricKey when performing encrypted SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) 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 + VALUES('You can only specify one of the parameters @ServerCertificate and @ServerAsymmetricKey. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) 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 + VALUES('The value for the parameter @ServerCertificate is not supported. The certificate does not exist in the master database. See https://ola.hallengren.com/sql-server-backup.html#ServerCertificate.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2133,51 +2341,51 @@ BEGIN 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 + VALUES('The parameter @ServerAsymmetricKey can only be used together with @Encrypt = ''Y'' and SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html#ServerAsymmetricKey.', 16, 1) END - IF @BackupSoftware IS NULL AND @Encrypt = 'Y' AND @ServerAsymmetricKey IS NULL AND @ServerCertificate IS NULL + 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, 2 + VALUES('The value for the parameter @ServerAsymmetricKey is not supported. The asymmetric key does not exist in the master database. See https://ola.hallengren.com/sql-server-backup.html#ServerAsymmetricKey.', 16, 1) END - IF @BackupSoftware IS NULL AND @Encrypt = 'Y' AND @ServerAsymmetricKey IS NOT NULL AND @ServerCertificate IS NOT NULL + ---------------------------------------------------------------------------------------------------- + + IF @EncryptionKey IS NOT NULL AND @BackupSoftware IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ServerAsymmetricKey is not supported.', 16, 3 + VALUES('The parameter @EncryptionKey is only supported with third-party backup software. For SQL Server native backups, use @ServerCertificate or @ServerAsymmetricKey. See https://ola.hallengren.com/sql-server-backup.html#EncryptionKey.', 16, 1) END - IF @ServerAsymmetricKey IS NOT NULL AND NOT EXISTS(SELECT * FROM master.sys.asymmetric_keys WHERE name = @ServerAsymmetricKey) + IF @EncryptionKey IS NOT NULL AND @Encrypt = 'N' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ServerAsymmetricKey is not supported.', 16, 4 + VALUES('The parameter @EncryptionKey can only be used together with @Encrypt = ''Y''. See https://ola.hallengren.com/sql-server-backup.html#EncryptionKey.', 16, 1) END - ---------------------------------------------------------------------------------------------------- - - IF @EncryptionKey IS NOT NULL AND @BackupSoftware IS NULL + IF @EncryptionKey IS NULL AND @Encrypt = 'Y' AND @BackupSoftware = 'LITESPEED' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @EncryptionKey is not supported.', 16, 1 + VALUES('You need to specify @EncryptionKey when performing encrypted backups with LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#EncryptionKey.', 16, 1) END - IF @EncryptionKey IS NOT NULL AND @Encrypt = 'N' + IF @EncryptionKey IS NULL AND @Encrypt = 'Y' AND @BackupSoftware = 'SQLBACKUP' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @EncryptionKey is not supported.', 16, 2 + VALUES('You need to specify @EncryptionKey when performing encrypted backups with Redgate SQL Backup Pro. See https://ola.hallengren.com/sql-server-backup.html#EncryptionKey.', 16, 1) END - IF @EncryptionKey IS NULL AND @Encrypt = 'Y' AND @BackupSoftware IN('LITESPEED','SQLBACKUP','SQLSAFE') + IF @EncryptionKey IS NULL AND @Encrypt = 'Y' AND @BackupSoftware = 'SQLSAFE' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @EncryptionKey is not supported.', 16, 3 + VALUES('You need to specify @EncryptionKey when performing encrypted backups with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#EncryptionKey.', 16, 1) 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 + VALUES('The parameter @EncryptionKey is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#EncryptionKey.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2185,13 +2393,13 @@ BEGIN 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 + VALUES('The value for the parameter @ReadWriteFileGroups is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#ReadWriteFileGroups.', 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 + VALUES('Setting @ReadWriteFileGroups to ''Y'' is not supported for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#ReadWriteFileGroups.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2199,7 +2407,7 @@ BEGIN 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 + VALUES('The value for the parameter @OverrideBackupPreference is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#OverrideBackupPreference.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2207,19 +2415,19 @@ BEGIN 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 + VALUES('The value for the parameter @NoRecovery is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#NoRecovery.', 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 + VALUES('Setting @NoRecovery to ''Y'' is only supported for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#NoRecovery.', 16, 1) 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 + VALUES('Setting @NoRecovery to ''Y'' is not supported with Idera SQL Safe Backup. See https://ola.hallengren.com/sql-server-backup.html#NoRecovery.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2227,51 +2435,39 @@ BEGIN 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 + VALUES('The parameters @URL and @Directory cannot be used together. See https://ola.hallengren.com/sql-server-backup.html.', 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 + VALUES('The parameters @URL and @MirrorDirectory cannot be used together. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) 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 + VALUES('Backup to URL is only supported with SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html#URL.', 16, 1) 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 + VALUES('The parameter @Credential can only be used together with @URL. See https://ola.hallengren.com/sql-server-backup.html#Credential.', 16, 1) 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 + VALUES('When backing up to URL, you need to specify @Credential or create a credential with the identity SHARED ACCESS SIGNATURE, MANAGED IDENTITY or S3 ACCESS KEY. See https://ola.hallengren.com/sql-server-backup.html#Credential.', 16, 1) 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 + VALUES('The value for the parameter @Credential is not supported. The credential does not exist. See https://ola.hallengren.com/sql-server-backup.html#Credential.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2279,13 +2475,13 @@ BEGIN IF @MirrorCleanupTime < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MirrorCleanupTime is not supported.', 16, 1 + VALUES('The value for the parameter @MirrorCleanupTime is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-backup.html#MirrorCleanupTime.', 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 + VALUES('The parameter @MirrorCleanupTime can only be used together with @MirrorDirectory. See https://ola.hallengren.com/sql-server-backup.html#MirrorCleanupTime.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2293,7 +2489,7 @@ BEGIN 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 + VALUES('The value for the parameter @MirrorCleanupMode is not supported. Supported values are BEFORE_BACKUP and AFTER_BACKUP. See https://ola.hallengren.com/sql-server-backup.html#MirrorCleanupMode.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2301,31 +2497,37 @@ BEGIN 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 + VALUES('The parameters @MirrorURL and @Directory cannot be used together. See https://ola.hallengren.com/sql-server-backup.html.', 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 + VALUES('The parameters @MirrorURL and @MirrorDirectory cannot be used together. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) + END + + IF @MirrorURL IS NOT NULL AND @BackupSoftware IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Mirrored backup to URL is only supported with SQL Server native backups. See https://ola.hallengren.com/sql-server-backup.html#MirrorURL.', 16, 1) END - IF @MirrorURL IS NOT NULL AND @Version < 11.03339 + 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, 3 + VALUES('The parameter @MirrorURL can only be used together with @URL. See https://ola.hallengren.com/sql-server-backup.html#MirrorURL.', 16, 1) END - IF @MirrorURL IS NOT NULL AND @BackupSoftware IS NOT NULL + IF @MirrorURL IS NOT NULL AND @Credential IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 4 + VALUES('The value for the parameter @MirrorURL is not supported. Mirrored backup to URL with page blobs is not supported. See https://ola.hallengren.com/sql-server-backup.html#MirrorURL.', 16, 1) END - IF @MirrorURL IS NOT NULL AND @URL IS NULL + IF @MirrorURL IS NOT NULL AND @BackupSoftware IS NULL AND @EngineEdition NOT IN (3, 8) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MirrorURL is not supported.', 16, 5 + VALUES('The value for the parameter @MirrorURL is not supported. Mirrored backup to URL is not supported in this edition of SQL Server. See https://ola.hallengren.com/sql-server-backup.html#MirrorURL.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2333,7 +2535,7 @@ BEGIN 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 + VALUES('The value for the parameter @Updateability is not supported. Supported values are ALL, READ_ONLY and READ_WRITE. See https://ola.hallengren.com/sql-server-backup.html#Updateability.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2341,39 +2543,33 @@ BEGIN 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 + VALUES('The value for the parameter @AdaptiveCompression is not supported. Supported values are SIZE and SPEED. See https://ola.hallengren.com/sql-server-backup.html#AdaptiveCompression.', 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 + VALUES('The parameter @AdaptiveCompression is only supported with LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#AdaptiveCompression.', 16, 1) 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 + IF @MinModificationLevel <= 0 OR @MinModificationLevel > 100 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ModificationLevel is not supported.', 16, 2 + VALUES('The value for the parameter @MinModificationLevel is not supported. The value has to be greater than 0 and less than or equal to 100. See https://ola.hallengren.com/sql-server-backup.html#MinModificationLevel.', 16, 1) END - IF @ModificationLevel IS NOT NULL AND @ChangeBackupType = 'N' + IF @MinModificationLevel 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 + VALUES('The parameter @MinModificationLevel can only be used together with @ChangeBackupType = ''Y''. See https://ola.hallengren.com/sql-server-backup.html#MinModificationLevel.', 16, 1) END - IF @ModificationLevel IS NOT NULL AND @BackupType <> 'DIFF' + IF @MinModificationLevel IS NOT NULL AND @BackupType NOT IN('DIFF','LOG') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The parameter @ModificationLevel can only be used for differential backups.', 16, 4 + VALUES('The parameter @MinModificationLevel can only be used for differential and transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#MinModificationLevel.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2381,49 +2577,37 @@ BEGIN IF @MinDatabaseSizeForDifferentialBackup <= 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MinDatabaseSizeForDifferentialBackup is not supported.', 16, 1 + VALUES('The value for the parameter @MinDatabaseSizeForDifferentialBackup is not supported. The value has to be greater than 0. See https://ola.hallengren.com/sql-server-backup.html#MinDatabaseSizeForDifferentialBackup.', 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 + VALUES('The parameter @MinDatabaseSizeForDifferentialBackup can only be used for differential backups. See https://ola.hallengren.com/sql-server-backup.html#MinDatabaseSizeForDifferentialBackup.', 16, 1) 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' + IF @MinLogSizeSinceLastLogBackup 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 + VALUES('The parameter @MinLogSizeSinceLastLogBackup can only be used for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#MinLogSizeSinceLastLogBackup.', 16, 1) 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' + IF @MinTimeSinceLastLogBackup 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 + VALUES('The parameter @MinTimeSinceLastLogBackup can only be used for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#MinTimeSinceLastLogBackup.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF (@TimeSinceLastLogBackup IS NOT NULL AND @LogSizeSinceLastLogBackup IS NULL) OR (@TimeSinceLastLogBackup IS NULL AND @LogSizeSinceLastLogBackup IS NOT NULL) + IF (@MinTimeSinceLastLogBackup IS NOT NULL AND @MinLogSizeSinceLastLogBackup IS NULL) OR (@MinTimeSinceLastLogBackup IS NULL AND @MinLogSizeSinceLastLogBackup IS NOT NULL) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The parameters @TimeSinceLastLogBackup and @LogSizeSinceLastLogBackup can only be used together.', 16, 1 + VALUES('The parameters @MinTimeSinceLastLogBackup and @MinLogSizeSinceLastLogBackup can only be used together. See https://ola.hallengren.com/sql-server-backup.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2431,13 +2615,19 @@ BEGIN 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 + VALUES('The parameter @DataDomainBoostHost can only be used together with @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostHost.', 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 + VALUES('You need to specify @DataDomainBoostHost when @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostHost.', 16, 1) + END + + IF @DataDomainBoostHost LIKE '%"%' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DataDomainBoostHost is not supported. Double quotes (") are not supported. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostHost.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2445,13 +2635,19 @@ BEGIN 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 + VALUES('The parameter @DataDomainBoostUser can only be used together with @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostUser.', 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 + VALUES('You need to specify @DataDomainBoostUser when @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostUser.', 16, 1) + END + + IF @DataDomainBoostUser LIKE '%"%' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DataDomainBoostUser is not supported. Double quotes (") are not supported. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostUser.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2459,13 +2655,19 @@ BEGIN 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 + VALUES('The parameter @DataDomainBoostDevicePath can only be used together with @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostDevicePath.', 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 + VALUES('You need to specify @DataDomainBoostDevicePath when @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostDevicePath.', 16, 1) + END + + IF @DataDomainBoostDevicePath LIKE '%"%' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DataDomainBoostDevicePath is not supported. Double quotes (") are not supported. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostDevicePath.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2473,7 +2675,27 @@ BEGIN 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 + VALUES('The parameter @DataDomainBoostLockboxPath can only be used together with @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostLockboxPath.', 16, 1) + END + + IF @DataDomainBoostLockboxPath LIKE '%"%' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DataDomainBoostLockboxPath is not supported. Double quotes (") are not supported. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostLockboxPath.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @DataDomainBoostNoOutputTable NOT IN('Y','N') OR @DataDomainBoostNoOutputTable IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DataDomainBoostNoOutputTable is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostNoOutputTable.', 16, 1) + END + + IF @DataDomainBoostNoOutputTable = 'Y' AND (@BackupSoftware <> 'DATA_DOMAIN_BOOST' OR @BackupSoftware IS NULL) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @DataDomainBoostNoOutputTable can only be used together with @BackupSoftware = ''DATA_DOMAIN_BOOST''. See https://ola.hallengren.com/sql-server-backup.html#DataDomainBoostNoOutputTable.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2481,7 +2703,7 @@ BEGIN IF @DirectoryStructure = '' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DirectoryStructure is not supported.', 16, 1 + VALUES('The value for the parameter @DirectoryStructure is not supported. The value cannot be an empty string. See https://ola.hallengren.com/sql-server-backup.html#DirectoryStructure.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2489,7 +2711,7 @@ BEGIN IF @AvailabilityGroupDirectoryStructure = '' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroupDirectoryStructure is not supported.', 16, 1 + VALUES('The value for the parameter @AvailabilityGroupDirectoryStructure is not supported. The value cannot be an empty string. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupDirectoryStructure.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2497,7 +2719,7 @@ BEGIN 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 + VALUES('The value for the parameter @DirectoryStructureCase is not supported. Supported values are LOWER and UPPER. See https://ola.hallengren.com/sql-server-backup.html#DirectoryStructureCase.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2505,81 +2727,81 @@ BEGIN 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 + VALUES('The value for the parameter @FileName is not supported. The value cannot be NULL or empty. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 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 + VALUES('The value for the parameter @FileName is not supported. The file name has to end with .{FileExtension}. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 16, 1) 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 + VALUES('The value for the parameter @FileName is not supported. The token {FileNumber} is required when @NumberOfFiles is greater than 1. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 16, 1) END IF @FileName LIKE '%{DirectorySeparator}%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @FileName is not supported.', 16, 4 + VALUES('The value for the parameter @FileName is not supported. The token {DirectorySeparator} cannot be used in the file name. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 16, 1) END IF @FileName LIKE '%/%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @FileName is not supported.', 16, 5 + VALUES('The value for the parameter @FileName is not supported. The character / cannot be used in the file name. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 16, 1) END IF @FileName LIKE '%\%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @FileName is not supported.', 16, 6 + VALUES('The value for the parameter @FileName is not supported. The character \ cannot be used in the file name. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF (SERVERPROPERTY('IsHadrEnabled') = 1 AND @AvailabilityGroupFileName IS NULL) + IF (@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 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The value cannot be NULL when the server is part of an availability group. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) END IF @AvailabilityGroupFileName = '' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 2 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The value cannot be an empty string. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) 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 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The file name has to end with .{FileExtension}. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) 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 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The token {FileNumber} is required when @NumberOfFiles is greater than 1. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) END IF @AvailabilityGroupFileName LIKE '%{DirectorySeparator}%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 5 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The token {DirectorySeparator} cannot be used in the file name. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) END IF @AvailabilityGroupFileName LIKE '%/%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 6 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The character / cannot be used in the file name. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) END IF @AvailabilityGroupFileName LIKE '%\%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroupFileName is not supported.', 16, 7 + VALUES('The value for the parameter @AvailabilityGroupFileName is not supported. The character \ cannot be used in the file name. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2587,15 +2809,15 @@ BEGIN 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 + VALUES('The value for the parameter @FileNameCase is not supported. Supported values are LOWER and UPPER. See https://ola.hallengren.com/sql-server-backup.html#FileNameCase.', 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 '%}%') + 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(@DirectoryStructure,'{DirectorySeparator}',''),'{ServerName}',''),'{InstanceName}',''),'{ServiceName}',''),'{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 + VALUES('The parameter @DirectoryStructure contains one or more tokens that are not supported. See https://ola.hallengren.com/sql-server-backup.html#DirectoryStructure.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2603,23 +2825,23 @@ BEGIN 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 + VALUES('The parameter @AvailabilityGroupDirectoryStructure contains one or more tokens that are not supported. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupDirectoryStructure.', 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 '%}%') + 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(@FileName,'{ServerName}',''),'{InstanceName}',''),'{ServiceName}',''),'{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 + VALUES('The parameter @FileName contains one or more tokens that are not supported. See https://ola.hallengren.com/sql-server-backup.html#FileName.', 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 '%}%') + 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(@AvailabilityGroupFileName,'{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 + VALUES('The parameter @AvailabilityGroupFileName contains one or more tokens that are not supported. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroupFileName.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2627,7 +2849,7 @@ BEGIN 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 + VALUES('The value for the parameter @TokenTimezone is not supported. Supported values are LOCAL and UTC. See https://ola.hallengren.com/sql-server-backup.html#TokenTimezone.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2635,7 +2857,7 @@ BEGIN IF @FileExtensionFull LIKE '%.%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @FileExtensionFull is not supported.', 16, 1 + VALUES('The value for the parameter @FileExtensionFull is not supported. Specify the file extension without a leading period. See https://ola.hallengren.com/sql-server-backup.html#FileExtensionFull.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2643,7 +2865,7 @@ BEGIN IF @FileExtensionDiff LIKE '%.%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @FileExtensionDiff is not supported.', 16, 1 + VALUES('The value for the parameter @FileExtensionDiff is not supported. Specify the file extension without a leading period. See https://ola.hallengren.com/sql-server-backup.html#FileExtensionDiff.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2651,7 +2873,7 @@ BEGIN IF @FileExtensionLog LIKE '%.%' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @FileExtensionLog is not supported.', 16, 1 + VALUES('The value for the parameter @FileExtensionLog is not supported. Specify the file extension without a leading period. See https://ola.hallengren.com/sql-server-backup.html#FileExtensionLog.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2659,19 +2881,25 @@ BEGIN 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 + VALUES('The value for the parameter @Init is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Init.', 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 + VALUES('Setting @Init to ''Y'' is not supported for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#Init.', 16, 1) 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 + VALUES('Setting @Init to ''Y'' is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#Init.', 16, 1) + END + + IF @Init = 'Y' AND @URL IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Setting @Init to ''Y'' is not supported with backup to URL. See https://ola.hallengren.com/sql-server-backup.html#Init.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2679,19 +2907,19 @@ BEGIN 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 + VALUES('The value for the parameter @Format is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Format.', 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 + VALUES('Setting @Format to ''Y'' is not supported for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#Format.', 16, 1) 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 + VALUES('Setting @Format to ''Y'' is not supported with Data Domain Boost. See https://ola.hallengren.com/sql-server-backup.html#Format.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2699,33 +2927,41 @@ BEGIN 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 + VALUES('The value for the parameter @ObjectLevelRecoveryMap is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#ObjectLevelRecoveryMap.', 16, 1) END - IF @ObjectLevelRecoveryMap = 'Y' AND @BackupSoftware IS NULL + IF @ObjectLevelRecoveryMap = 'Y' AND (@BackupSoftware NOT IN('LITESPEED') OR @BackupSoftware IS NULL) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ObjectLevelRecovery is not supported.', 16, 2 + VALUES('Setting @ObjectLevelRecoveryMap to ''Y'' is only supported with LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#ObjectLevelRecoveryMap.', 16, 1) END - IF @ObjectLevelRecoveryMap = 'Y' AND @BackupSoftware <> 'LITESPEED' + IF @ObjectLevelRecoveryMap = 'Y' AND @BackupType = 'LOG' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ObjectLevelRecovery is not supported.', 16, 3 + VALUES('Setting @ObjectLevelRecoveryMap to ''Y'' is not supported for transaction log backups. See https://ola.hallengren.com/sql-server-backup.html#ObjectLevelRecoveryMap.', 16, 1) END - IF @ObjectLevelRecoveryMap = 'Y' AND @BackupType = 'LOG' + ---------------------------------------------------------------------------------------------------- + + IF @ExcludeLogShippedFromLogBackup NOT IN('Y','N') OR @ExcludeLogShippedFromLogBackup IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ObjectLevelRecovery is not supported.', 16, 4 + VALUES('The value for the parameter @ExcludeLogShippedFromLogBackup is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#ExcludeLogShippedFromLogBackup.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @ExcludeLogShippedFromLogBackup NOT IN('Y','N') OR @ExcludeLogShippedFromLogBackup IS NULL + IF @ExcludeSeedingFromLogBackup NOT IN('Y','N') OR @ExcludeSeedingFromLogBackup IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @ExcludeSeedingFromLogBackup is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#ExcludeSeedingFromLogBackup.', 16, 1) + END + + IF @ExcludeSeedingFromLogBackup = 'Y' AND @BackupType <> 'LOG' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ExcludeLogShippedFromLogBackup is not supported.', 16, 1 + VALUES('The parameter @ExcludeSeedingFromLogBackup can only be used for log backups. See https://ola.hallengren.com/sql-server-backup.html#ExcludeSeedingFromLogBackup.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2733,15 +2969,15 @@ BEGIN 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 + VALUES('The value for the parameter @DirectoryCheck is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#DirectoryCheck.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @BackupOptions IS NOT NULL AND @URL IS NULL + IF @BackupOptions IS NOT NULL AND NOT EXISTS(SELECT * FROM @URLs WHERE DirectoryPath LIKE 's3://%/%') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @BackupOptions is not supported.', 16, 1 + VALUES('The parameter @BackupOptions can only be used when backing up to S3-compatible storage. See https://ola.hallengren.com/sql-server-backup.html#BackupOptions.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2749,7 +2985,7 @@ BEGIN 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 + VALUES('The value for the parameter @Stats is not supported. The value has to be between 1 and 100. See https://ola.hallengren.com/sql-server-backup.html#Stats.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2757,7 +2993,7 @@ BEGIN 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 + VALUES('The parameter @ExpireDate is only supported with SQL Server native backups and LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#ExpireDate.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2765,41 +3001,49 @@ BEGIN IF @RetainDays < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @RetainDays is not supported.', 16, 1 + VALUES('The value for the parameter @RetainDays is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-backup.html#RetainDays.', 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 + VALUES('The parameter @RetainDays is only supported with SQL Server native backups and LiteSpeed for SQL Server. See https://ola.hallengren.com/sql-server-backup.html#RetainDays.', 16, 1) END - ---------------------------------------------------------------------------------------------------- - - IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) > 1 + IF @ExpireDate IS NOT NULL AND @RetainDays IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @StringDelimiter is not supported.', 16, 1 + VALUES('The parameters @ExpireDate and @RetainDays cannot be used together. See https://ola.hallengren.com/sql-server-backup.html.', 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') + IF @AllowNonCopyOnlyBackupsOnForwarder NOT IN('Y','N') OR @AllowNonCopyOnlyBackupsOnForwarder IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DatabaseOrder is not supported.', 16, 1 + VALUES('The value for the parameter @AllowNonCopyOnlyBackupsOnForwarder is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#AllowNonCopyOnlyBackupsOnForwarder.', 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') + ---------------------------------------------------------------------------------------------------- + + IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) <> 1 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 + VALUES('The value for the parameter @StringDelimiter is not supported. The value has to be exactly one character. See https://ola.hallengren.com/sql-server-backup.html#StringDelimiter.', 16, 1) END - IF @DatabaseOrder IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 5 + ---------------------------------------------------------------------------------------------------- + + 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]) + VALUES('The value for the parameter @DatabaseOrder is not supported. Supported values are DATABASE_NAME_ASC, DATABASE_NAME_DESC, DATABASE_SIZE_ASC, DATABASE_SIZE_DESC, LOG_SIZE_SINCE_LAST_LOG_BACKUP_ASC and LOG_SIZE_SINCE_LAST_LOG_BACKUP_DESC. See https://ola.hallengren.com/sql-server-backup.html#DatabaseOrder.', 16, 1) + END + + IF @DatabaseOrder IS NOT NULL AND @EngineEdition = 5 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DatabaseOrder is not supported.', 16, 3 + VALUES('The parameter @DatabaseOrder is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-backup.html#DatabaseOrder.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2807,13 +3051,13 @@ BEGIN 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 + VALUES('The value for the parameter @DatabasesInParallel is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#DatabasesInParallel.', 16, 1) END - IF @DatabasesInParallel = 'Y' AND SERVERPROPERTY('EngineEdition') = 5 + IF @DatabasesInParallel = 'Y' AND @EngineEdition = 5 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DatabasesInParallel is not supported.', 16, 2 + VALUES('The parameter @DatabasesInParallel is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-backup.html#DatabasesInParallel.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2821,7 +3065,7 @@ BEGIN 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 + VALUES('The value for the parameter @LogToTable is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#LogToTable.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -2829,22 +3073,15 @@ BEGIN 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 + VALUES('The value for the parameter @Execute is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-backup.html#Execute.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Check that selected databases and availability groups exist //-- ---------------------------------------------------------------------------------------------------- - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedDatabases WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) @@ -2852,10 +3089,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-backup.html#Databases.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(AvailabilityGroupName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(AvailabilityGroupName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY AvailabilityGroupName ASC) FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName NOT LIKE '%[%]%' AND AvailabilityGroupName NOT IN (SELECT AvailabilityGroupName FROM @tmpAvailabilityGroups) @@ -2863,24 +3101,24 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following availability groups do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following availability groups do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-backup.html#AvailabilityGroups.', 10, 1) END ---------------------------------------------------------------------------------------------------- --// Check @@SERVERNAME //-- ---------------------------------------------------------------------------------------------------- - IF UPPER(@@SERVERNAME) <> UPPER(CAST(SERVERPROPERTY('ServerName') AS nvarchar(max))) AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF UPPER(@@SERVERNAME) <> UPPER(@ServerName) AND @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 + VALUES('The @@SERVERNAME does not match SERVERPROPERTY(''ServerName''). See ' + CASE WHEN @IsClustered = 0 THEN 'https://learn.microsoft.com/en-us/sql/database-engine/install-windows/rename-a-computer-that-hosts-a-stand-alone-instance-of-sql-server' WHEN @IsClustered = 1 THEN 'https://learn.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 + DECLARE ErrorCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Message], Severity, [State] FROM @Errors ORDER BY [ID] ASC OPEN ErrorCursor @@ -2908,7 +3146,7 @@ BEGIN --// Check Availability Group cluster name //-- ---------------------------------------------------------------------------------------------------- - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN SELECT @Cluster = NULLIF(cluster_name,'') FROM sys.dm_hadr_cluster @@ -3022,7 +3260,7 @@ BEGIN FROM dbo.[Queue] WHERE SchemaName = @SchemaName AND ObjectName = @ObjectName - AND [Parameters] = @Parameters + AND [Parameters] = @ParametersString IF @QueueID IS NULL BEGIN @@ -3032,12 +3270,12 @@ BEGIN FROM dbo.[Queue] WITH (UPDLOCK, HOLDLOCK) WHERE SchemaName = @SchemaName AND ObjectName = @ObjectName - AND [Parameters] = @Parameters + AND [Parameters] = @ParametersString IF @QueueID IS NULL BEGIN INSERT INTO dbo.[Queue] (SchemaName, ObjectName, [Parameters]) - SELECT @SchemaName, @ObjectName, @Parameters + VALUES(@SchemaName, @ObjectName, @ParametersString) SET @QueueID = SCOPE_IDENTITY() END @@ -3071,17 +3309,17 @@ BEGIN DatabaseName FROM @tmpDatabases tmpDatabases WHERE Selected = 1 - AND NOT EXISTS (SELECT * FROM dbo.QueueDatabase WHERE DatabaseName = tmpDatabases.DatabaseName AND QueueID = @QueueID) + AND NOT EXISTS (SELECT * FROM dbo.QueueDatabase WHERE DatabaseName COLLATE DATABASE_DEFAULT = 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) + AND NOT EXISTS (SELECT * FROM @tmpDatabases tmpDatabases WHERE DatabaseName = QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT AND Selected = 1) UPDATE QueueDatabase SET DatabaseOrder = tmpDatabases.[Order] FROM dbo.QueueDatabase QueueDatabase - INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName = tmpDatabases.DatabaseName + INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT = tmpDatabases.DatabaseName WHERE QueueID = @QueueID END @@ -3098,7 +3336,7 @@ BEGIN BEGIN ROLLBACK TRANSACTION END - SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar) + ', ' + ISNULL(ERROR_MESSAGE(),'') + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + ISNULL(ERROR_MESSAGE(),'') RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT SET @ReturnCode = ERROR_NUMBER() @@ -3134,7 +3372,7 @@ BEGIN 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) + @CurrentDatabaseNameFS = (SELECT DatabaseNameFS FROM @tmpDatabases WHERE DatabaseName = QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT) FROM (SELECT TOP 1 DatabaseStartTime, DatabaseEndTime, SessionID, @@ -3167,7 +3405,7 @@ BEGIN SET @CurrentDatabase_sp_executesql = QUOTENAME(@CurrentDatabaseName) + '.sys.sp_executesql' BEGIN - SET @DatabaseMessage = 'Date and time: ' + CONVERT(nvarchar,SYSDATETIME(),120) + SET @DatabaseMessage = 'Date and time: ' + CONVERT(nvarchar(max),SYSDATETIME(),120) RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT SET @DatabaseMessage = 'Database: ' + QUOTENAME(@CurrentDatabaseName) @@ -3206,41 +3444,84 @@ BEGIN 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 + WHEN @MaxTransferSize IS NULL AND @Compress = 'Y' AND @CurrentIsEncrypted = 1 AND @BackupSoftware IS NULL AND (@Version < 15.04043 AND NOT (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) AND @Credential IS NULL THEN 65537 END - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN - SELECT @CurrentReplicaID = databases.replica_id + SELECT @CurrentAvailabilityGroupReplicaID = 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 + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + + IF @CurrentAvailabilityGroupReplicaID IS NULL AND @CurrentAvailabilityGroupID IS NULL AND @Version >= 16 + BEGIN + SET @CurrentAvailabilityGroupID = @ContainedAvailabilityGroupID + + SELECT @CurrentAvailabilityGroupReplicaID = replica_id + FROM sys.dm_hadr_availability_replica_states + WHERE group_id = @CurrentAvailabilityGroupID + AND is_local = 1 + END + + SELECT @CurrentSecondaryRoleAllowConnections = secondary_role_allow_connections_desc + FROM sys.availability_replicas + WHERE replica_id = @CurrentAvailabilityGroupReplicaID SELECT @CurrentAvailabilityGroupRole = role_desc FROM sys.dm_hadr_availability_replica_states - WHERE replica_id = @CurrentReplicaID + WHERE replica_id = @CurrentAvailabilityGroupReplicaID SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc FROM sys.dm_hadr_database_replica_states - WHERE replica_id = @CurrentReplicaID + WHERE replica_id = @CurrentAvailabilityGroupReplicaID AND database_id = DB_ID(@CurrentDatabaseName) + IF @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState IS NULL AND @ContainedAvailabilityGroupListenerConnection = 1 + BEGIN + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND DB_NAME(database_id) = @CurrentDatabaseName + END + 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 + IF @IsHadrEnabled = 1 AND @CurrentAvailabilityGroup IS NOT NULL BEGIN SELECT @CurrentIsPreferredBackupReplica = sys.fn_hadr_backup_is_preferred_replica(@CurrentDatabaseName) END + IF @IsHadrEnabled = 1 AND @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SELECT @CurrentIsSeeding = CASE WHEN EXISTS (SELECT * FROM sys.dm_hadr_physical_seeding_stats dm_hadr_physical_seeding_stats WHERE dm_hadr_physical_seeding_stats.local_database_name = @CurrentDatabaseName AND dm_hadr_physical_seeding_stats.role_desc IN('Source','Forwarder') AND dm_hadr_physical_seeding_stats.end_time_utc IS NULL) THEN 1 ELSE 0 END + END + + IF @IsHadrEnabled = 1 AND @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SELECT @CurrentDistributedAvailabilityGroup = availability_groups.[name], + @CurrentDistributedAvailabilityGroupReplicaID = availability_replicas.replica_id + FROM sys.availability_groups availability_groups + INNER JOIN sys.availability_replicas availability_replicas ON availability_groups.group_id = availability_replicas.group_id + INNER JOIN sys.availability_groups availability_groups_local ON availability_replicas.replica_server_name = availability_groups_local.[name] + WHERE availability_groups.is_distributed = 1 + AND availability_groups_local.group_id = @CurrentAvailabilityGroupID + + SELECT @CurrentDistributedAvailabilityGroupRole = dm_hadr_availability_replica_states.role_desc + FROM sys.dm_hadr_availability_replica_states dm_hadr_availability_replica_states + WHERE dm_hadr_availability_replica_states.replica_id = @CurrentDistributedAvailabilityGroupReplicaID + END + SELECT @CurrentDifferentialBaseLSN = differential_base_lsn FROM sys.master_files WHERE database_id = DB_ID(@CurrentDatabaseName) @@ -3254,35 +3535,41 @@ BEGIN 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') + SET @CurrentBackupType = @BackupType + + IF (@Version >= 16.04265 AND @Version < 17) OR @Version >= 17.04065 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous') + BEGIN + SET @BackupInProgress = CASE WHEN EXISTS(SELECT * FROM sys.dm_exec_requests WHERE database_id = DB_ID(@CurrentDatabaseName) AND command = 'BACKUP DATABASE') THEN 1 ELSE 0 END + END + + IF @CurrentDatabaseState = 'ONLINE' + AND NOT @CurrentUserAccess = 'SINGLE_USER' + AND NOT @CurrentInStandby = 1 + AND NOT (@CurrentAvailabilityGroup IS NOT NULL AND (@CurrentAvailabilityGroupRole NOT IN('PRIMARY','SECONDARY') OR @CurrentAvailabilityGroupRole IS NULL)) 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) + AND (@CurrentDistributedAvailabilityGroupRole = 'PRIMARY' OR @CurrentDistributedAvailabilityGroupRole IS NULL) + AND (@BackupType IN('DIFF','FULL') OR (@ChangeBackupType = 'Y' AND @CurrentBackupType = 'LOG' AND @CurrentRecoveryModel IN('FULL','BULK_LOGGED') AND @CurrentLogLSN IS NULL AND NOT (@CurrentDatabaseName = 'master' AND @ContainedAvailabilityGroupListenerConnection = 0))) AND (@BackupInProgress = 0 OR @BackupInProgress IS 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' + IF @CurrentBackupType = 'LOG' AND @CurrentRecoveryModel IN('FULL','BULK_LOGGED') AND @CurrentLogLSN IS NULL AND NOT (@CurrentDatabaseName = 'master' AND @ContainedAvailabilityGroupListenerConnection = 0) AND (@BackupInProgress = 0 OR @BackupInProgress IS NULL) 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)) + IF @CurrentBackupType = 'DIFF' AND ((@CurrentDatabaseName = 'master' AND @ContainedAvailabilityGroupListenerConnection = 0) OR @CurrentDifferentialBaseLSN IS NULL OR (@CurrentModifiedExtentPageCount * 1. / NULLIF(@CurrentAllocatedExtentPageCount, 0) * 100 >= @MinModificationLevel) 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, + SELECT @CurrentLastLogBackup = NULLIF(log_backup_time,'1900-01-01'), @CurrentLogSizeSinceLastLogBackup = log_since_last_log_backup_mb FROM sys.dm_db_log_stats (DB_ID(@CurrentDatabaseName)) END @@ -3291,7 +3578,7 @@ BEGIN BEGIN SELECT @CurrentDifferentialBaseIsSnapshot = is_snapshot FROM msdb.dbo.backupset - WHERE database_name = @CurrentDatabaseName + WHERE [database_name] = @CurrentDatabaseName AND [type] = 'D' AND checkpoint_lsn = @CurrentDifferentialBaseLSN END @@ -3310,7 +3597,12 @@ BEGIN 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, + MaxNumberOfFiles = CASE WHEN @BackupSoftware IN('SQLBACKUP','SQLSAFE') AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) THEN 1 + WHEN @BackupSoftware = 'LITESPEED' AND EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) THEN 32 + WHEN @BackupSoftware IN('SQLBACKUP','DATA_DOMAIN_BOOST') THEN 32 + WHEN @BackupSoftware IS NULL AND (EXISTS(SELECT * FROM @Directories WHERE Mirror = 1) OR EXISTS(SELECT * FROM @URLs WHERE Mirror = 1)) 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 @@ -3350,6 +3642,7 @@ BEGIN OR (@CurrentBackupType = 'DIFF' AND @CopyOnly = 'N' AND @Version >= 17) OR (@CurrentBackupType = 'FULL' AND @CopyOnly = 'Y') OR (@CurrentBackupType = 'LOG' AND @CopyOnly = 'N')) + AND @EngineEdition = 3 BEGIN SET @CurrentBackupOperationSupportedOnSecondaryReplicas = 1 END @@ -3373,6 +3666,12 @@ BEGIN SET @DatabaseMessage = 'Availability group database replica synchronization health: ' + ISNULL(@CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth,'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 + SET @DatabaseMessage = 'Availability group backup preference: ' + ISNULL(@CurrentAvailabilityGroupBackupPreference,'N/A') RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT @@ -3384,6 +3683,24 @@ 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 + + SET @DatabaseMessage = 'Is seeding: ' + CASE WHEN @CurrentIsSeeding = 1 THEN 'Yes' WHEN @CurrentIsSeeding = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + END + + IF @CurrentDistributedAvailabilityGroup IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Distributed availability group: ' + ISNULL(@CurrentDistributedAvailabilityGroup,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Distributed availability group role: ' + ISNULL(@CurrentDistributedAvailabilityGroupRole,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Replica role in distributed availability group: ' + CASE WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Global primary' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Forwarder' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in secondary availability group' + WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in primary availability group' ELSE 'N/A' END + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT END IF @CurrentDatabaseMirroringRole IS NOT NULL @@ -3398,7 +3715,7 @@ BEGIN RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT END - SET @DatabaseMessage = 'Differential base LSN: ' + ISNULL(CAST(@CurrentDifferentialBaseLSN AS nvarchar),'N/A') + SET @DatabaseMessage = 'Differential base LSN: ' + ISNULL(CAST(@CurrentDifferentialBaseLSN AS nvarchar(max)),'N/A') RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT IF @CurrentBackupType = 'DIFF' OR @CurrentDifferentialBaseIsSnapshot IS NOT NULL @@ -3407,24 +3724,27 @@ BEGIN RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT END - SET @DatabaseMessage = 'Last log backup LSN: ' + ISNULL(CAST(@CurrentLogLSN AS nvarchar),'N/A') + SET @DatabaseMessage = 'Last log backup LSN: ' + ISNULL(CAST(@CurrentLogLSN AS nvarchar(max)),'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Full or differential backup in progress: ' + CASE WHEN @BackupInProgress = 1 THEN 'Yes' WHEN @BackupInProgress = 0 THEN 'No' ELSE 'N/A' END 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') + IF @CurrentBackupType IN('DIFF','FULL') BEGIN - SET @DatabaseMessage = 'Allocated extent page count: ' + ISNULL(CAST(@CurrentAllocatedExtentPageCount AS nvarchar) + ' (' + CAST(@CurrentAllocatedExtentPageCount * 1. * 8 / 1024 AS nvarchar) + ' MB)','N/A') + SET @DatabaseMessage = 'Allocated extent page count: ' + ISNULL(CAST(@CurrentAllocatedExtentPageCount AS nvarchar(max)) + ' (' + CAST(@CurrentAllocatedExtentPageCount * 1. * 8 / 1024 AS nvarchar(max)) + ' 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') + SET @DatabaseMessage = 'Modified extent page count: ' + ISNULL(CAST(@CurrentModifiedExtentPageCount AS nvarchar(max)) + ' (' + CAST(@CurrentModifiedExtentPageCount * 1. * 8 / 1024 AS nvarchar(max)) + ' 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') + IF @CurrentBackupType = 'LOG' BEGIN - SET @DatabaseMessage = 'Last log backup: ' + ISNULL(CONVERT(nvarchar(19),NULLIF(@CurrentLastLogBackup,'1900-01-01'),120),'N/A') + SET @DatabaseMessage = 'Last log backup: ' + ISNULL(CONVERT(nvarchar(19),@CurrentLastLogBackup,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') + SET @DatabaseMessage = 'Log size since last log backup (MB): ' + ISNULL(CAST(@CurrentLogSizeSinceLastLogBackup AS nvarchar(max)),'N/A') RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT END @@ -3436,13 +3756,17 @@ BEGIN 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 (@CurrentBackupType IN('DIFF','LOG') AND (@CurrentDatabaseName = 'master' AND @ContainedAvailabilityGroupListenerConnection = 0)) + AND NOT (@CurrentAvailabilityGroup IS NOT NULL AND (@CurrentAvailabilityGroupRole NOT IN('PRIMARY','SECONDARY') OR @CurrentAvailabilityGroupRole IS NULL)) 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 (@CurrentDistributedAvailabilityGroup IS NOT NULL AND @CurrentBackupOperationSupportedOnSecondaryReplicas = 0 AND (@CurrentDistributedAvailabilityGroupRole <> 'PRIMARY' OR @CurrentDistributedAvailabilityGroupRole IS NULL)) + AND NOT (@CurrentDistributedAvailabilityGroup IS NOT NULL AND (@CurrentDistributedAvailabilityGroupRole <> 'PRIMARY' OR @CurrentDistributedAvailabilityGroupRole IS NULL) AND @AllowNonCopyOnlyBackupsOnForwarder = 'N' AND NOT (@CurrentBackupType = 'FULL' AND @CopyOnly = 'Y')) AND NOT ((@CurrentLogShippingRole = 'PRIMARY' AND @CurrentLogShippingRole IS NOT NULL) AND @CurrentBackupType = 'LOG' AND @ExcludeLogShippedFromLogBackup = 'Y') + AND NOT (@CurrentAvailabilityGroup IS NOT NULL AND @CurrentBackupType = 'LOG' AND @ExcludeSeedingFromLogBackup = 'Y' AND @CurrentIsSeeding = 1) 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 @MinLogSizeSinceLastLogBackup IS NOT NULL AND @MinTimeSinceLastLogBackup IS NOT NULL AND NOT(@CurrentLogSizeSinceLastLogBackup >= @MinLogSizeSinceLastLogBackup OR @CurrentLogSizeSinceLastLogBackup IS NULL OR DATEDIFF(SECOND,@CurrentLastLogBackup,SYSDATETIME()) >= @MinTimeSinceLastLogBackup 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 @@ -3460,13 +3784,13 @@ BEGIN SET @CurrentDate = SYSDATETIME() SET @CurrentDateUTC = SYSUTCDATETIME() - INSERT INTO @CurrentCleanupDates (CleanupDate) - SELECT @CurrentDate + INSERT INTO @CurrentCleanupDates ([Type], CleanupDate) + VALUES('CurrentTime', @CurrentDate) IF @CurrentBackupType = 'LOG' BEGIN - INSERT INTO @CurrentCleanupDates (CleanupDate) - SELECT @CurrentLatestBackup + INSERT INTO @CurrentCleanupDates ([Type], CleanupDate) + VALUES('LatestBackupTime', @CurrentLatestBackup) END SELECT @CurrentDirectoryStructure = CASE @@ -3480,13 +3804,12 @@ BEGIN 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 @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 @Description IS NULL OR LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@Description,'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|',''))) = '' SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Description}','') + IF @BackupSetName IS NULL OR LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@BackupSetName,'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|',''))) = '' 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' + IF @Directory IS NULL AND @MirrorDirectory IS NULL AND @URL IS NULL AND @DefaultDirectory LIKE '%' + '.' + REPLACE(@@SERVICENAME,'_','[_]') + @DirectorySeparator + 'MSSQL' + @DirectorySeparator + 'Backup' BEGIN SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{ServerName}','') SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{InstanceName}','') @@ -3632,8 +3955,8 @@ BEGIN -- 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,'{ServerName}',CASE WHEN @EngineEdition = 8 AND CHARINDEX('.',@ServerName) > 0 THEN LEFT(@ServerName,CHARINDEX('.',@ServerName) - 1) WHEN @EngineEdition = 8 AND CHARINDEX('.',@ServerName) = 0 THEN @ServerName ELSE @MachineName END) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{InstanceName}',ISNULL(@InstanceName,'')) SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{ServiceName}',ISNULL(@@SERVICENAME,'')) SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{ClusterName}',ISNULL(@Cluster,'')) SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{AvailabilityGroupName}',ISNULL(@CurrentAvailabilityGroup,'')) @@ -3643,18 +3966,18 @@ BEGIN 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))) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Year}',CAST(DATEPART(YEAR,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max))) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Month}',RIGHT('0' + CAST(DATEPART(MONTH,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Day}',RIGHT('0' + CAST(DATEPART(DAY,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Week}',RIGHT('0' + CAST(DATEPART(WEEK,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Weekday}',FORMAT(CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END,'dddd','en-US')) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Hour}',RIGHT('0' + CAST(DATEPART(HOUR,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Minute}',RIGHT('0' + CAST(DATEPART(MINUTE,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Second}',RIGHT('0' + CAST(DATEPART(SECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Millisecond}',RIGHT('00' + CAST(DATEPART(MILLISECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),3)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{Microsecond}',RIGHT('00000' + CAST(DATEPART(MICROSECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),6)) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{MajorVersion}',@ProductMajorVersion) + SET @CurrentDirectoryStructure = REPLACE(@CurrentDirectoryStructure,'{MinorVersion}',@ProductMinorVersion) END IF @DirectoryStructureCase IS NOT NULL @@ -3690,11 +4013,10 @@ BEGIN 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 @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 @Description IS NULL OR LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@Description,'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|',''))) = '' SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Description}','') + IF @BackupSetName IS NULL OR LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@BackupSetName,'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|',''))) = '' SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{BackupSetName}','') IF @CurrentNumberOfFiles = 1 SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{FileNumber}','') IF @CurrentNumberOfFiles = 1 SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{NumberOfFiles}','') @@ -3797,8 +4119,8 @@ BEGIN 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,'{ServerName}',CASE WHEN @EngineEdition = 8 AND CHARINDEX('.',@ServerName) > 0 THEN LEFT(@ServerName,CHARINDEX('.',@ServerName) - 1) WHEN @EngineEdition = 8 AND CHARINDEX('.',@ServerName) = 0 THEN @ServerName ELSE @MachineName END) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{InstanceName}',ISNULL(@InstanceName,'')) SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{ServiceName}',ISNULL(@@SERVICENAME,'')) SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{ClusterName}',ISNULL(@Cluster,'')) SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{AvailabilityGroupName}',ISNULL(@CurrentAvailabilityGroup,'')) @@ -3807,26 +4129,26 @@ BEGIN 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,'{Year}',CAST(DATEPART(YEAR,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max))) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Month}',RIGHT('0' + CAST(DATEPART(MONTH,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Day}',RIGHT('0' + CAST(DATEPART(DAY,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Week}',RIGHT('0' + CAST(DATEPART(WEEK,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Weekday}',FORMAT(CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END,'dddd','en-US')) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Hour}',RIGHT('0' + CAST(DATEPART(HOUR,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Minute}',RIGHT('0' + CAST(DATEPART(MINUTE,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Second}',RIGHT('0' + CAST(DATEPART(SECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),2)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Millisecond}',RIGHT('00' + CAST(DATEPART(MILLISECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),3)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{Microsecond}',RIGHT('00000' + CAST(DATEPART(MICROSECOND,CASE WHEN @TokenTimezone = 'UTC' THEN @CurrentDateUTC ELSE @CurrentDate END) AS nvarchar(max)),6)) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{NumberOfFiles}',CAST(@CurrentNumberOfFiles AS nvarchar(max))) 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))) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{MajorVersion}',@ProductMajorVersion) + SET @CurrentDatabaseFileName = REPLACE(@CurrentDatabaseFileName,'{MinorVersion}',@ProductMinorVersion) 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)) + + LEN(REPLACE(REPLACE(@CurrentDatabaseFileName,'{DatabaseName}',@CurrentDatabaseNameFS), '{FileNumber}', CASE WHEN @CurrentNumberOfFiles >= 1 AND @CurrentNumberOfFiles <= 9 THEN '1' WHEN @CurrentNumberOfFiles >= 10 THEN '01' ELSE '' END)) -- The maximum length of a backup device is 259 characters IF @CurrentMaxFilePathLength > 259 @@ -3858,7 +4180,7 @@ BEGIN 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) + SET @CurrentFileName = REPLACE(@CurrentDatabaseFileName, '{FileNumber}', CASE WHEN @CurrentNumberOfFiles >= 1 AND @CurrentNumberOfFiles <= 9 THEN CAST(@CurrentFileNumber AS nvarchar(max)) WHEN @CurrentNumberOfFiles >= 10 THEN RIGHT('0' + CAST(@CurrentFileNumber AS nvarchar(max)),2) ELSE '' END) IF @CurrentDirectoryPath = 'NUL' BEGIN @@ -3870,7 +4192,7 @@ BEGIN END INSERT INTO @CurrentFiles ([Type], FilePath, Mirror) - SELECT 'DISK', @CurrentFilePath, 0 + VALUES('DISK', @CurrentFilePath, 0) SET @CurrentDirectoryPath = NULL SET @CurrentFileName = NULL @@ -3878,7 +4200,7 @@ BEGIN END INSERT INTO @CurrentBackupSet (Mirror, VerifyCompleted) - SELECT 0, 0 + VALUES(0, 0) END IF EXISTS (SELECT * FROM @CurrentDirectories WHERE Mirror = 1) @@ -3895,12 +4217,12 @@ BEGIN 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 @CurrentFileName = REPLACE(@CurrentDatabaseFileName, '{FileNumber}', CASE WHEN @CurrentNumberOfFiles >= 1 AND @CurrentNumberOfFiles <= 9 THEN CAST(@CurrentFileNumber AS nvarchar(max)) WHEN @CurrentNumberOfFiles >= 10 THEN RIGHT('0' + CAST(@CurrentFileNumber AS nvarchar(max)),2) ELSE '' END) SET @CurrentFilePath = @CurrentDirectoryPath + @DirectorySeparator + @CurrentFileName INSERT INTO @CurrentFiles ([Type], FilePath, Mirror) - SELECT 'DISK', @CurrentFilePath, 1 + VALUES('DISK', @CurrentFilePath, 1) SET @CurrentDirectoryPath = NULL SET @CurrentFileName = NULL @@ -3908,7 +4230,7 @@ BEGIN END INSERT INTO @CurrentBackupSet (Mirror, VerifyCompleted) - SELECT 1, 0 + VALUES(1, 0) END IF EXISTS (SELECT * FROM @CurrentURLs WHERE Mirror = 0) @@ -3925,12 +4247,12 @@ BEGIN 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 @CurrentFileName = REPLACE(@CurrentDatabaseFileName, '{FileNumber}', CASE WHEN @CurrentNumberOfFiles >= 1 AND @CurrentNumberOfFiles <= 9 THEN CAST(@CurrentFileNumber AS nvarchar(max)) WHEN @CurrentNumberOfFiles >= 10 THEN RIGHT('0' + CAST(@CurrentFileNumber AS nvarchar(max)),2) ELSE '' END) SET @CurrentFilePath = @CurrentDirectoryPath + @DirectorySeparator + @CurrentFileName INSERT INTO @CurrentFiles ([Type], FilePath, Mirror) - SELECT 'URL', @CurrentFilePath, 0 + VALUES('URL', @CurrentFilePath, 0) SET @CurrentDirectoryPath = NULL SET @CurrentFileName = NULL @@ -3938,7 +4260,7 @@ BEGIN END INSERT INTO @CurrentBackupSet (Mirror, VerifyCompleted) - SELECT 0, 0 + VALUES(0, 0) END IF EXISTS (SELECT * FROM @CurrentURLs WHERE Mirror = 1) @@ -3951,16 +4273,16 @@ BEGIN 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) + WHERE @CurrentFileNumber >= (DirectoryNumber - 1) * (SELECT @CurrentNumberOfFiles / COUNT(*) FROM @CurrentURLs WHERE Mirror = 1) + 1 + AND @CurrentFileNumber <= DirectoryNumber * (SELECT @CurrentNumberOfFiles / COUNT(*) FROM @CurrentURLs 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 @CurrentFileName = REPLACE(@CurrentDatabaseFileName, '{FileNumber}', CASE WHEN @CurrentNumberOfFiles >= 1 AND @CurrentNumberOfFiles <= 9 THEN CAST(@CurrentFileNumber AS nvarchar(max)) WHEN @CurrentNumberOfFiles >= 10 THEN RIGHT('0' + CAST(@CurrentFileNumber AS nvarchar(max)),2) ELSE '' END) SET @CurrentFilePath = @CurrentDirectoryPath + @DirectorySeparator + @CurrentFileName INSERT INTO @CurrentFiles ([Type], FilePath, Mirror) - SELECT 'URL', @CurrentFilePath, 1 + VALUES('URL', @CurrentFilePath, 1) SET @CurrentDirectoryPath = NULL SET @CurrentFileName = NULL @@ -3968,7 +4290,7 @@ BEGIN END INSERT INTO @CurrentBackupSet (Mirror, VerifyCompleted) - SELECT 1, 0 + VALUES(1, 0) END -- Create directory @@ -4004,7 +4326,7 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_create_subdir N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + ''' IF @ReturnCode <> 0 OR @ReturnCode IS NULL 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 @@ -4038,8 +4360,8 @@ BEGIN IF @CleanupMode = 'BEFORE_BACKUP' BEGIN - INSERT INTO @CurrentCleanupDates (CleanupDate, Mirror) - SELECT DATEADD(hh,-(@CleanupTime),SYSDATETIME()), 0 + INSERT INTO @CurrentCleanupDates ([Type], CleanupDate, Mirror) + VALUES('CleanupTime', DATEADD(hh,-(@CleanupTime),SYSDATETIME()), 0) IF NOT EXISTS(SELECT * FROM @CurrentCleanupDates WHERE (Mirror = 0 OR Mirror IS NULL) AND CleanupDate IS NULL) BEGIN @@ -4054,8 +4376,8 @@ BEGIN IF @MirrorCleanupMode = 'BEFORE_BACKUP' BEGIN - INSERT INTO @CurrentCleanupDates (CleanupDate, Mirror) - SELECT DATEADD(hh,-(@MirrorCleanupTime),SYSDATETIME()), 1 + INSERT INTO @CurrentCleanupDates ([Type], CleanupDate, Mirror) + VALUES('MirrorCleanupTime', DATEADD(hh,-(@MirrorCleanupTime),SYSDATETIME()), 1) IF NOT EXISTS(SELECT * FROM @CurrentCleanupDates WHERE (Mirror = 1 OR Mirror IS NULL) AND CleanupDate IS NULL) BEGIN @@ -4095,7 +4417,7 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_delete_file 0, N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + ''', ''' + REPLACE(@CurrentFileExtension,'''','''''') + ''', ''' + CONVERT(nvarchar(19),@CurrentCleanupDate,126) + ''' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error deleting files.'', 16, 1)' END IF @BackupSoftware = 'LITESPEED' @@ -4104,7 +4426,7 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_slssqlmaint N''-MAINTDEL -DELFOLDER "' + REPLACE(@CurrentDirectoryPath,'''','''''') + '" -DELEXTENSION "' + REPLACE(@CurrentFileExtension,'''','''''') + '" -DELUNIT "' + CAST(DATEDIFF(mi,@CurrentCleanupDate,SYSDATETIME()) + 1 AS nvarchar(max)) + '" -DELUNITTYPE "minutes" -DELUSEAGE'' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error deleting LiteSpeed backup files.'', 16, 1)' END IF @BackupSoftware = 'SQLBACKUP' @@ -4113,7 +4435,7 @@ BEGIN 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)' + 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(max)) + 'h'', ' + ISNULL('''' + @EncryptionKeyPlaceholder + '''','NULL') + ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error deleting SQLBackup backup files.'', 16, 1)' END IF @BackupSoftware = 'SQLSAFE' @@ -4122,10 +4444,10 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_ss_delete @filename = N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + '\*.' + REPLACE(@CurrentFileExtension,'''','''''') + ''', @age = ''' + CAST(DATEDIFF(mi,@CurrentCleanupDate,SYSDATETIME()) + 1 AS nvarchar(max)) + 'Minutes'' IF @ReturnCode <> 0 OR @ReturnCode IS NULL 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 + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @EncryptionKey = @EncryptionKey, @EncryptionKeyPlaceholder = @EncryptionKeyPlaceholder, @LogToTable = @LogToTable, @Execute = @Execute SET @Error = @@ERROR IF @Error <> 0 SET @CurrentCommandOutput = @Error IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput @@ -4147,7 +4469,15 @@ BEGIN 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)) + IF @BackupSoftware = 'DATA_DOMAIN_BOOST' AND @CurrentDatabaseName LIKE '%"%' + BEGIN + SET @ErrorMessage = 'The name of the database ' + QUOTENAME(@CurrentDatabaseName) + ' is not supported. Double quotes (") are not supported with Data Domain Boost.' + RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT + SET @Error = @@ERROR + SET @ReturnCode = @Error + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + END + ELSE 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 @@ -4187,10 +4517,7 @@ BEGIN 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 + SET @CurrentCommand += CASE WHEN @Compress = 'Y' AND (@CurrentIsEncrypted = 0 OR (@CurrentIsEncrypted = 1 AND (@CurrentMaxTransferSize >= 65537 OR (@Version >= 15.04043 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous'))))) THEN ', COMPRESSION' ELSE ', NO_COMPRESSION' END IF @Compress = 'Y' AND @CompressionAlgorithm IS NOT NULL BEGIN @@ -4208,20 +4535,20 @@ BEGIN 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 @BlockSize IS NOT NULL SET @CurrentCommand += ', BLOCKSIZE = ' + CAST(@BlockSize AS nvarchar(max)) + IF @BufferCount IS NOT NULL SET @CurrentCommand += ', BUFFERCOUNT = ' + CAST(@BufferCount AS nvarchar(max)) + IF @CurrentMaxTransferSize IS NOT NULL SET @CurrentCommand += ', MAXTRANSFERSIZE = ' + CAST(@CurrentMaxTransferSize AS nvarchar(max)) 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 @Stats IS NOT NULL SET @CurrentCommand += ', STATS = ' + CAST(@Stats AS nvarchar(max)) 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) + IF @ExpireDate IS NOT NULL SET @CurrentCommand += ', EXPIREDATE = ''' + CONVERT(nvarchar(max), @ExpireDate, 21) + '''' + IF @RetainDays IS NOT NULL SET @CurrentCommand += ', RETAINDAYS = ' + CAST(@RetainDays AS nvarchar(max)) END IF @BackupSoftware = 'LITESPEED' @@ -4257,21 +4584,21 @@ BEGIN 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) + IF @BlockSize IS NOT NULL SET @CurrentCommand += ', BLOCKSIZE = ' + CAST(@BlockSize AS nvarchar(max)) 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 @CompressionLevelNumeric IS NOT NULL SET @CurrentCommand += ', @compressionlevel = ' + CAST(@CompressionLevelNumeric AS nvarchar(max)) 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 @BufferCount IS NOT NULL SET @CurrentCommand += ', @buffercount = ' + CAST(@BufferCount AS nvarchar(max)) + IF @CurrentMaxTransferSize IS NOT NULL SET @CurrentCommand += ', @maxtransfersize = ' + CAST(@CurrentMaxTransferSize AS nvarchar(max)) + IF @Threads IS NOT NULL SET @CurrentCommand += ', @threads = ' + CAST(@Threads AS nvarchar(max)) 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 @Throttle IS NOT NULL SET @CurrentCommand += ', @throttle = ' + CAST(@Throttle AS nvarchar(max)) 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 @ExpireDate IS NOT NULL SET @CurrentCommand += ', @expiration = ''' + CONVERT(nvarchar(max), @ExpireDate, 21) + '''' + IF @RetainDays IS NOT NULL SET @CurrentCommand += ', @retaindays = ' + CAST(@RetainDays AS nvarchar(max)) IF @EncryptionAlgorithm IS NOT NULL SET @CurrentCommand += ', @cryptlevel = ' + CASE WHEN @EncryptionAlgorithm = 'RC2_40' THEN '0' @@ -4285,8 +4612,8 @@ BEGIN 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)' + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', @encryptionkey = N''' + @EncryptionKeyPlaceholder + '''' + SET @CurrentCommand += ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error performing LiteSpeed backup.'', 16, 1)' END IF @BackupSoftware = 'SQLBACKUP' @@ -4323,9 +4650,9 @@ BEGIN 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 @CompressionLevelNumeric IS NOT NULL SET @CurrentCommand += ', COMPRESSION = ' + CAST(@CompressionLevelNumeric AS nvarchar(max)) + IF @Threads IS NOT NULL SET @CurrentCommand += ', THREADCOUNT = ' + CAST(@Threads AS nvarchar(max)) + IF @CurrentMaxTransferSize IS NOT NULL SET @CurrentCommand += ', MAXTRANSFERSIZE = ' + CAST(@CurrentMaxTransferSize AS nvarchar(max)) IF @Description IS NOT NULL SET @CurrentCommand += ', DESCRIPTION = N''' + REPLACE(@Description,'''','''''') + '''' IF @EncryptionAlgorithm IS NOT NULL SET @CurrentCommand += ', KEYSIZE = ' + CASE @@ -4333,8 +4660,8 @@ BEGIN 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)' + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', PASSWORD = N''' + @EncryptionKeyPlaceholder + '''' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.sqlbackup N''-SQL "' + REPLACE(@CurrentCommand,'''','''''') + '"''' + ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error performing SQLBackup backup.'', 16, 1)' END IF @BackupSoftware = 'SQLSAFE' @@ -4367,8 +4694,8 @@ BEGIN 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 @CompressionLevelNumeric IS NOT NULL SET @CurrentCommand += ', @compressionlevel = ' + CAST(@CompressionLevelNumeric AS nvarchar(max)) + IF @Threads IS NOT NULL SET @CurrentCommand += ', @threads = ' + CAST(@Threads AS nvarchar(max)) IF @Init = 'Y' SET @CurrentCommand += ', @overwrite = 1' IF @Description IS NOT NULL SET @CurrentCommand += ', @desc = N''' + REPLACE(@Description,'''','''''') + '''' @@ -4377,8 +4704,8 @@ BEGIN 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)' + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', @encryptedbackuppassword = N''' + @EncryptionKeyPlaceholder + '''' + SET @CurrentCommand += ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error performing SQLsafe backup.'', 16, 1)' END IF @BackupSoftware = 'DATA_DOMAIN_BOOST' @@ -4387,9 +4714,13 @@ BEGIN SET @CurrentCommandType = 'emc_run_backup' - SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.emc_run_backup ''' + SET @CurrentCommand = '' + IF @DataDomainBoostNoOutputTable = 'Y' SET @CurrentCommand += 'DECLARE @DataDomainBoostOutput TABLE ([Message] nvarchar(MAX)); ' + SET @CurrentCommand += 'DECLARE @ReturnCode int; ' + IF @DataDomainBoostNoOutputTable = 'Y' SET @CurrentCommand += 'INSERT INTO @DataDomainBoostOutput ([Message]) ' + SET @CurrentCommand += '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 += ' -c ' + CASE WHEN @Cluster IS NOT NULL AND @CurrentAvailabilityGroup IS NOT NULL THEN REPLACE(@Cluster,'''','''''') ELSE REPLACE(@MachineName,'''','''''') END SET @CurrentCommand += ' -l ' + CASE WHEN @CurrentBackupType = 'FULL' THEN 'full' @@ -4399,15 +4730,15 @@ BEGIN 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 @CleanupTime IS NOT NULL SET @CurrentCommand += ' -y +' + CAST(@CleanupTime/24 + CASE WHEN @CleanupTime%24 > 0 THEN 1 ELSE 0 END AS nvarchar(max)) + 'd' IF @Checksum = 'Y' SET @CurrentCommand += ' -k' - SET @CurrentCommand += ' -S ' + CAST(@CurrentNumberOfFiles AS nvarchar) + SET @CurrentCommand += ' -S ' + CAST(@CurrentNumberOfFiles AS nvarchar(max)) IF @Description IS NOT NULL SET @CurrentCommand += ' -b "' + REPLACE(@Description,'''','''''') + '"' - IF @BufferCount IS NOT NULL SET @CurrentCommand += ' -O "BUFFERCOUNT=' + CAST(@BufferCount AS nvarchar) + '"' + IF @BufferCount IS NOT NULL SET @CurrentCommand += ' -O "BUFFERCOUNT=' + CAST(@BufferCount AS nvarchar(max)) + '"' IF @ReadWriteFileGroups = 'Y' AND @CurrentDatabaseName <> 'master' SET @CurrentCommand += ' -O "READ_WRITE_FILEGROUPS"' @@ -4420,16 +4751,16 @@ BEGIN 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) + IF @InstanceName IS NULL SET @CurrentCommand += ' "MSSQL' + IF @InstanceName IS NOT NULL SET @CurrentCommand += ' "MSSQL$' + @InstanceName SET @CurrentCommand += ':' + REPLACE(REPLACE(@CurrentDatabaseName,'''',''''''),'.','\.') + '"' SET @CurrentCommand += '''' - SET @CurrentCommand += ' IF @ReturnCode <> 0 RAISERROR(''Error performing Data Domain Boost backup.'', 16, 1)' + SET @CurrentCommand += ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL 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 + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @EncryptionKey = @EncryptionKey, @EncryptionKeyPlaceholder = @EncryptionKeyPlaceholder, @LogToTable = @LogToTable, @Execute = @Execute SET @Error = @@ERROR IF @Error <> 0 SET @CurrentCommandOutput = @Error IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput @@ -4468,7 +4799,7 @@ BEGIN 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 @Stats IS NOT NULL SET @CurrentCommand += ', STATS = ' + CAST(@Stats AS nvarchar(max)) 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 @@ -4490,9 +4821,9 @@ BEGIN 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,'''','''''') + '''' + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', @encryptionkey = N''' + @EncryptionKeyPlaceholder + '''' - SET @CurrentCommand += ' IF @ReturnCode <> 0 RAISERROR(''Error verifying LiteSpeed backup.'', 16, 1)' + SET @CurrentCommand += ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error verifying LiteSpeed backup.'', 16, 1)' END IF @BackupSoftware = 'SQLBACKUP' @@ -4511,9 +4842,9 @@ BEGIN 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,'''','''''') + '''' + IF @EncryptionKey IS NOT NULL SET @CurrentCommand += ', PASSWORD = N''' + @EncryptionKeyPlaceholder + '''' - SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.sqlbackup N''-SQL "' + REPLACE(@CurrentCommand,'''','''''') + '"''' + ' IF @ReturnCode <> 0 RAISERROR(''Error verifying SQLBackup backup.'', 16, 1)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.sqlbackup N''-SQL "' + REPLACE(@CurrentCommand,'''','''''') + '"''' + ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error verifying SQLBackup backup.'', 16, 1)' END IF @BackupSoftware = 'SQLSAFE' @@ -4534,10 +4865,10 @@ BEGIN WITHIN GROUP (ORDER BY RowNumber ASC) FROM CurrentFiles - SET @CurrentCommand += ' IF @ReturnCode <> 0 RAISERROR(''Error verifying SQLsafe backup.'', 16, 1)' + SET @CurrentCommand += ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL 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 + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @EncryptionKey = @EncryptionKey, @EncryptionKeyPlaceholder = @EncryptionKeyPlaceholder, @LogToTable = @LogToTable, @Execute = @Execute SET @Error = @@ERROR IF @Error <> 0 SET @CurrentCommandOutput = @Error IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput @@ -4559,8 +4890,8 @@ BEGIN IF @CleanupMode = 'AFTER_BACKUP' BEGIN - INSERT INTO @CurrentCleanupDates (CleanupDate, Mirror) - SELECT DATEADD(hh,-(@CleanupTime),SYSDATETIME()), 0 + INSERT INTO @CurrentCleanupDates ([Type], CleanupDate, Mirror) + VALUES('CleanupTime', DATEADD(hh,-(@CleanupTime),SYSDATETIME()), 0) IF NOT EXISTS(SELECT * FROM @CurrentCleanupDates WHERE (Mirror = 0 OR Mirror IS NULL) AND CleanupDate IS NULL) BEGIN @@ -4575,8 +4906,8 @@ BEGIN IF @MirrorCleanupMode = 'AFTER_BACKUP' BEGIN - INSERT INTO @CurrentCleanupDates (CleanupDate, Mirror) - SELECT DATEADD(hh,-(@MirrorCleanupTime),SYSDATETIME()), 1 + INSERT INTO @CurrentCleanupDates ([Type], CleanupDate, Mirror) + VALUES('MirrorCleanupTime', DATEADD(hh,-(@MirrorCleanupTime),SYSDATETIME()), 1) IF NOT EXISTS(SELECT * FROM @CurrentCleanupDates WHERE (Mirror = 1 OR Mirror IS NULL) AND CleanupDate IS NULL) BEGIN @@ -4617,7 +4948,7 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_delete_file 0, N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + ''', ''' + REPLACE(@CurrentFileExtension,'''','''''') + ''', ''' + CONVERT(nvarchar(19),@CurrentCleanupDate,126) + ''' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error deleting files.'', 16, 1)' END IF @BackupSoftware = 'LITESPEED' @@ -4626,7 +4957,7 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_slssqlmaint N''-MAINTDEL -DELFOLDER "' + REPLACE(@CurrentDirectoryPath,'''','''''') + '" -DELEXTENSION "' + REPLACE(@CurrentFileExtension,'''','''''') + '" -DELUNIT "' + CAST(DATEDIFF(mi,@CurrentCleanupDate,SYSDATETIME()) + 1 AS nvarchar(max)) + '" -DELUNITTYPE "minutes" -DELUSEAGE'' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error deleting LiteSpeed backup files.'', 16, 1)' END IF @BackupSoftware = 'SQLBACKUP' @@ -4635,7 +4966,7 @@ BEGIN 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)' + 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(max)) + 'h'', ' + ISNULL('''' + @EncryptionKeyPlaceholder + '''','NULL') + ' IF @ReturnCode <> 0 OR @ReturnCode IS NULL RAISERROR(''Error deleting SQLBackup backup files.'', 16, 1)' END IF @BackupSoftware = 'SQLSAFE' @@ -4644,10 +4975,10 @@ BEGIN 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)' + SET @CurrentCommand = 'DECLARE @ReturnCode int EXECUTE @ReturnCode = dbo.xp_ss_delete @filename = N''' + REPLACE(@CurrentDirectoryPath,'''','''''') + '\*.' + REPLACE(@CurrentFileExtension,'''','''''') + ''', @age = ''' + CAST(DATEDIFF(mi,@CurrentCleanupDate,SYSDATETIME()) + 1 AS nvarchar(max)) + 'Minutes'' IF @ReturnCode <> 0 OR @ReturnCode IS NULL 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 + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @EncryptionKey = @EncryptionKey, @EncryptionKeyPlaceholder = @EncryptionKeyPlaceholder, @LogToTable = @LogToTable, @Execute = @Execute SET @Error = @@ERROR IF @Error <> 0 SET @CurrentCommandOutput = @Error IF @CurrentCommandOutput <> 0 SET @ReturnCode = @CurrentCommandOutput @@ -4674,6 +5005,7 @@ BEGIN SET @ErrorMessage = 'The database ' + QUOTENAME(@CurrentDatabaseName) + ' is in a SUSPECT state.' RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT SET @Error = @@ERROR + SET @ReturnCode = @Error RAISERROR(@EmptyLine,10,1) WITH NOWAIT END @@ -4716,6 +5048,7 @@ BEGIN SET @CurrentDifferentialBaseLSN = NULL SET @CurrentDifferentialBaseIsSnapshot = NULL SET @CurrentLogLSN = NULL + SET @BackupInProgress = NULL SET @CurrentLatestBackup = NULL SET @CurrentDatabaseNameFS = NULL SET @CurrentDirectoryStructure = NULL @@ -4724,16 +5057,21 @@ BEGIN SET @CurrentDate = NULL SET @CurrentDateUTC = NULL SET @CurrentCleanupDate = NULL - SET @CurrentReplicaID = NULL + SET @CurrentAvailabilityGroupReplicaID = NULL SET @CurrentAvailabilityGroupID = NULL SET @CurrentAvailabilityGroup = NULL SET @CurrentAvailabilityGroupRole = NULL SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = NULL SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = NULL SET @CurrentAvailabilityGroupBackupPreference = NULL + SET @CurrentSecondaryRoleAllowConnections = NULL SET @CurrentIsPreferredBackupReplica = NULL + SET @CurrentDistributedAvailabilityGroup = NULL + SET @CurrentDistributedAvailabilityGroupReplicaID = NULL + SET @CurrentDistributedAvailabilityGroupRole = NULL SET @CurrentDatabaseMirroringRole = NULL SET @CurrentLogShippingRole = NULL + SET @CurrentIsSeeding = NULL SET @CurrentBackupOperationSupportedOnSecondaryReplicas = NULL SET @CurrentLastLogBackup = NULL SET @CurrentLogSizeSinceLastLogBackup = NULL @@ -4760,7 +5098,7 @@ BEGIN ---------------------------------------------------------------------------------------------------- Logging: - SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar,SYSDATETIME(),120) + SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar(max),SYSDATETIME(),120) RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT @@ -4816,7 +5154,7 @@ BEGIN --// Source: https://ola.hallengren.com //-- --// License: https://ola.hallengren.com/license.html //-- --// GitHub: https://github.com/olahallengren/sql-server-maintenance-solution //-- - --// Version: 2026-05-18 20:15:56 //-- + --// Version: 2026-08-23 14:46:45 //-- ---------------------------------------------------------------------------------------------------- SET NOCOUNT ON @@ -4831,9 +5169,25 @@ BEGIN 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 @Parameters TABLE (ID int IDENTITY PRIMARY KEY, + [Name] nvarchar(max) NOT NULL, + ValueNvarchar nvarchar(max), + ValueInt int, + ValueDatetime datetime2) + + DECLARE @ParametersString nvarchar(max) + DECLARE @CurrentParameterName nvarchar(max) + DECLARE @CurrentParameterValueNvarchar nvarchar(max) + DECLARE @CurrentParameterValueInt int + DECLARE @CurrentParameterValueDatetime datetime2 + DECLARE @CurrentParameterDelimiter nvarchar(max) + DECLARE @CurrentParameterMessage nvarchar(max) DECLARE @HostPlatform nvarchar(max) + DECLARE @ContainedAvailabilityGroupID uniqueidentifier + DECLARE @ContainedAvailabilityGroupListenerConnection bit + DECLARE @IsSysadmin bit = IS_SRVROLEMEMBER('sysadmin') DECLARE @QueueID int DECLARE @QueueStartTime datetime2 @@ -4848,14 +5202,18 @@ BEGIN DECLARE @CurrentDatabaseState nvarchar(max) DECLARE @CurrentInStandby bit DECLARE @CurrentRecoveryModel nvarchar(max) - - DECLARE @CurrentReplicaID uniqueidentifier + DECLARE @CurrentAvailabilityGroupReplicaID 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 @CurrentSecondaryRoleAllowConnections nvarchar(max) DECLARE @CurrentIsPreferredBackupReplica bit + DECLARE @CurrentDistributedAvailabilityGroup nvarchar(max) + DECLARE @CurrentDistributedAvailabilityGroupReplicaID uniqueidentifier + DECLARE @CurrentDistributedAvailabilityGroupRole nvarchar(max) DECLARE @CurrentDatabaseMirroringRole nvarchar(max) DECLARE @CurrentFGID int @@ -4879,58 +5237,58 @@ BEGIN DECLARE @Errors TABLE (ID int IDENTITY PRIMARY KEY, [Message] nvarchar(max) NOT NULL, Severity int NOT NULL, - [State] int) + [State] int NOT NULL) DECLARE @CurrentMessage nvarchar(max) DECLARE @CurrentSeverity int DECLARE @CurrentState int DECLARE @tmpDatabases TABLE (ID int IDENTITY, - DatabaseName nvarchar(max), - DatabaseType nvarchar(max), + DatabaseName nvarchar(128), + DatabaseType nvarchar(1), AvailabilityGroup bit, [Snapshot] bit, StartPosition int, LastCommandTime datetime2, DatabaseSize bigint, LastGoodCheckDbTime datetime2, - [Order] int, - Selected bit, - Completed bit, - PRIMARY KEY(Selected, Completed, [Order], ID)) + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) DECLARE @tmpAvailabilityGroups TABLE (ID int IDENTITY PRIMARY KEY, - AvailabilityGroupName nvarchar(max), + AvailabilityGroupName nvarchar(128), StartPosition int, - Selected bit) + Selected bit DEFAULT 0) - DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(max), - AvailabilityGroupName nvarchar(max)) + DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(128), + AvailabilityGroupName nvarchar(128)) DECLARE @tmpFileGroups TABLE (ID int IDENTITY, FileGroupID int, - FileGroupName nvarchar(max), + FileGroupName nvarchar(128), StartPosition int, - [Order] int, - Selected bit, - Completed bit, - PRIMARY KEY(Selected, Completed, [Order], ID)) + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) DECLARE @tmpObjects TABLE (ID int IDENTITY, SchemaID int, - SchemaName nvarchar(max), + SchemaName nvarchar(128), ObjectID int, - ObjectName nvarchar(max), - ObjectType nvarchar(max), + ObjectName nvarchar(128), + ObjectType nvarchar(2), StartPosition int, - [Order] int, - Selected bit, - Completed bit, - PRIMARY KEY(Selected, Completed, [Order], ID)) + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) DECLARE @SelectedDatabases TABLE (DatabaseName nvarchar(max), - DatabaseType nvarchar(max), - AvailabilityGroup nvarchar(max), + DatabaseType nvarchar(1), + AvailabilityGroup bit, StartPosition int, Selected bit) @@ -4956,66 +5314,108 @@ BEGIN 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)) + DECLARE @ProductVersion nvarchar(max) = CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + DECLARE @ProductUpdateType nvarchar(max) = CAST(SERVERPROPERTY('ProductUpdateType') AS nvarchar(max)) + DECLARE @EngineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int) + DECLARE @Edition nvarchar(max) = CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + DECLARE @IsHadrEnabled bit = CAST(SERVERPROPERTY('IsHadrEnabled') AS bit) + DECLARE @IsClustered bit = CAST(SERVERPROPERTY('IsClustered') AS bit) + DECLARE @ServerName nvarchar(max) = CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) - IF @Version >= 14 + DECLARE @Version numeric(18,10) = CAST(PARSENAME(@ProductVersion,4) + '.' + PARSENAME(@ProductVersion,3) + PARSENAME(@ProductVersion,2) AS numeric(18,10)) + + IF @EngineEdition = 8 AND @ProductVersion = '12.0.2000.8' AND @ProductUpdateType = 'CU' + BEGIN + SET @Version = 16.01000 + END + + IF @EngineEdition <> 5 BEGIN SELECT @HostPlatform = host_platform FROM sys.dm_os_host_info END - ELSE + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) BEGIN - SET @HostPlatform = 'Windows' + SET @CurrentCommand = 'SELECT @ParamContainedAvailabilityGroupID = contained_availability_group_id FROM sys.dm_exec_sessions WHERE session_id = @@SPID' + + EXECUTE sp_executesql @stmt = @CurrentCommand, @params = N'@ParamContainedAvailabilityGroupID uniqueidentifier OUTPUT', @ParamContainedAvailabilityGroupID = @ContainedAvailabilityGroupID OUTPUT 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 + SET @ContainedAvailabilityGroupListenerConnection = CASE WHEN @ContainedAvailabilityGroupID IS NOT NULL THEN 1 ELSE 0 END + + DECLARE @AmazonRDS bit = CASE WHEN @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) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Databases', @Databases) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@CheckCommands', @CheckCommands) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@PhysicalOnly', @PhysicalOnly) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataPurity', @DataPurity) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@NoIndex', @NoIndex) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ExtendedLogicalChecks', @ExtendedLogicalChecks) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@NoInformationalMessages', @NoInformationalMessages) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@TabLock', @TabLock) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FileGroups', @FileGroups) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Objects', @Objects) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MaxDOP', @MaxDOP) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroups', @AvailabilityGroups) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroupReplicas', @AvailabilityGroupReplicas) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Updateability', @Updateability) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@TimeLimit', @TimeLimit) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@LockTimeout', @LockTimeout) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@LockMessageSeverity', @LockMessageSeverity) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StringDelimiter', @StringDelimiter) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabaseOrder', @DatabaseOrder) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabasesInParallel', @DatabasesInParallel) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@LogToTable', @LogToTable) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Execute', @Execute) + + SELECT @ParametersString = STRING_AGG(CAST([Name] + ' = ' + CASE WHEN ValueNvarchar IS NOT NULL THEN '''' + REPLACE(ValueNvarchar,'''','''''') + '''' WHEN ValueInt IS NOT NULL THEN CAST(ValueInt AS nvarchar(max)) WHEN ValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), ValueDatetime, 21) + '''' ELSE 'NULL' END AS nvarchar(max)), ', ') WITHIN GROUP (ORDER BY [ID] ASC) + FROM @Parameters + + SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar(max),@StartTime,120) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Server: ' + CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) + SET @StartMessage = 'Server: ' + @ServerName RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Version: ' + CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + SET @StartMessage = 'Version: ' + @ProductVersion RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Edition: ' + CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + SET @StartMessage = 'Edition: ' + @Edition RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Platform: ' + @HostPlatform - RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + IF @EngineEdition = 8 + BEGIN + SET @StartMessage = 'Update type: ' + @ProductUpdateType + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @EngineEdition <> 5 + BEGIN + SET @StartMessage = 'Platform: ' + ISNULL(@HostPlatform, 'N/A') + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) + BEGIN + SET @StartMessage = 'Contained availability group connection: ' + CASE WHEN @ContainedAvailabilityGroupListenerConnection = 1 THEN 'Yes' WHEN @ContainedAvailabilityGroupListenerConnection = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @EngineEdition <> 5 + BEGIN + SET @StartMessage = 'Is sysadmin: ' + CASE WHEN @IsSysadmin = 1 THEN 'Yes' WHEN @IsSysadmin = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END - SET @StartMessage = 'Procedure: ' + QUOTENAME(DB_NAME()) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + SET @StartMessage = 'Database: ' + QUOTENAME(DB_NAME()) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Parameters: ' + @Parameters + SET @StartMessage = 'Procedure: ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT SET @StartMessage = 'Version: ' + @VersionTimestamp @@ -5023,6 +5423,32 @@ BEGIN SET @StartMessage = 'Source: https://ola.hallengren.com' RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + + SET @StartMessage = 'Command:' + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'EXECUTE ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + DECLARE ParameterCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Name], ValueNvarchar, ValueInt, ValueDatetime, CASE WHEN [ID] = MAX([ID]) OVER() THEN '' ELSE ',' END FROM @Parameters ORDER BY [ID] ASC + + OPEN ParameterCursor + + FETCH ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + + WHILE @@FETCH_STATUS = 0 + BEGIN + SET @CurrentParameterMessage = @CurrentParameterName + ' = ' + CASE WHEN @CurrentParameterValueNvarchar IS NOT NULL THEN '''' + REPLACE(@CurrentParameterValueNvarchar,'''','''''') + '''' WHEN @CurrentParameterValueInt IS NOT NULL THEN CAST(@CurrentParameterValueInt AS nvarchar(max)) WHEN @CurrentParameterValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), @CurrentParameterValueDatetime, 21) + '''' ELSE 'NULL' END + @CurrentParameterDelimiter + + RAISERROR('%s',10,1,@CurrentParameterMessage) WITH NOWAIT + + FETCH NEXT FROM ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + END + + CLOSE ParameterCursor + + DEALLOCATE ParameterCursor RAISERROR(@EmptyLine,10,1) WITH NOWAIT @@ -5030,58 +5456,52 @@ BEGIN --// 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 + VALUES('ANSI_NULLS has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-integrity-check.html.', 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 + VALUES('QUOTED_IDENTIFIER has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-integrity-check.html.', 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 + VALUES('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 + VALUES('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 + VALUES('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 + VALUES('The table Queue is missing. It is required when @DatabasesInParallel = ''Y''. 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 + VALUES('The table QueueDatabase is missing. It is required when @DatabasesInParallel = ''Y''. 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 + VALUES('The stored procedure cannot be executed inside a transaction. The transaction count (@@TRANCOUNT) has to be 0. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5143,11 +5563,10 @@ BEGIN FROM Databases4 OPTION (MAXRECURSION 0) - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN - INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName, Selected) - SELECT name AS AvailabilityGroupName, - 0 AS Selected + INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName) + SELECT name AS AvailabilityGroupName FROM sys.availability_groups INSERT INTO @tmpDatabasesAvailabilityGroups (DatabaseName, AvailabilityGroupName) @@ -5158,14 +5577,11 @@ BEGIN 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) + INSERT INTO @tmpDatabases (DatabaseName, DatabaseType, AvailabilityGroup, [Snapshot]) 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 + CASE WHEN source_database_id IS NOT NULL THEN 1 ELSE 0 END AS [Snapshot] FROM sys.databases ORDER BY [name] ASC @@ -5177,7 +5593,7 @@ BEGIN SET tmpDatabases.Selected = SelectedDatabases.Selected FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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) @@ -5187,7 +5603,7 @@ BEGIN SET tmpDatabases.Selected = SelectedDatabases.Selected FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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) @@ -5199,7 +5615,7 @@ BEGIN INNER JOIN (SELECT tmpDatabases.DatabaseName, MIN(SelectedDatabases.StartPosition) AS StartPosition FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -5209,14 +5625,14 @@ BEGIN 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 + VALUES('The value for the parameter @Databases is not supported. The value could not be parsed into a list of databases. See https://ola.hallengren.com/sql-server-integrity-check.html#Databases.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Select availability groups //-- ---------------------------------------------------------------------------------------------------- - IF @AvailabilityGroups IS NOT NULL AND @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 1 BEGIN SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, CHAR(10), '') @@ -5270,14 +5686,14 @@ BEGIN SET tmpAvailabilityGroups.Selected = SelectedAvailabilityGroups.Selected FROM @tmpAvailabilityGroups tmpAvailabilityGroups INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups - ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(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,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'[','[[]'),'_','[_]') WHERE SelectedAvailabilityGroups.Selected = 0 UPDATE tmpAvailabilityGroups @@ -5286,7 +5702,7 @@ BEGIN INNER JOIN (SELECT tmpAvailabilityGroups.AvailabilityGroupName, MIN(SelectedAvailabilityGroups.StartPosition) AS StartPosition FROM @tmpAvailabilityGroups tmpAvailabilityGroups INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups - ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'[','[[]'),'_','[_]') WHERE SelectedAvailabilityGroups.Selected = 1 GROUP BY tmpAvailabilityGroups.AvailabilityGroupName) SelectedAvailabilityGroups2 ON tmpAvailabilityGroups.AvailabilityGroupName = SelectedAvailabilityGroups2.AvailabilityGroupName @@ -5301,22 +5717,28 @@ BEGIN 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) + IF @AvailabilityGroups IS NOT NULL AND (NOT EXISTS(SELECT * FROM @SelectedAvailabilityGroups) OR EXISTS(SELECT * FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName IS NULL OR AvailabilityGroupName = '')) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @AvailabilityGroups is not supported. The value could not be parsed into a list of availability groups. See https://ola.hallengren.com/sql-server-integrity-check.html#AvailabilityGroups.', 16, 1) + END + + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroups is not supported.', 16, 1 + VALUES('The parameter @AvailabilityGroups can only be used when availability groups are enabled on the instance. See https://ola.hallengren.com/sql-server-integrity-check.html#AvailabilityGroups.', 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 + VALUES('You need to specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) 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 + VALUES('You can only specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5455,31 +5877,31 @@ BEGIN 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 + VALUES('The value for the parameter @CheckCommands is not supported. Supported values are CHECKDB, CHECKFILEGROUP, CHECKALLOC, CHECKTABLE and CHECKCATALOG. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 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 + VALUES('The value for the parameter @CheckCommands is not supported. The same check command has been specified more than once. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 16, 1) 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 + VALUES('The value for the parameter @CheckCommands is not supported. The value cannot be NULL or empty. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 16, 1) 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 + VALUES('The value for the parameter @CheckCommands is not supported. CHECKDB cannot be combined with CHECKFILEGROUP, CHECKALLOC, CHECKTABLE or CHECKCATALOG. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 16, 1) 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 + VALUES('The value for the parameter @CheckCommands is not supported. CHECKFILEGROUP cannot be combined with CHECKALLOC or CHECKTABLE. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5487,7 +5909,7 @@ BEGIN 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 + VALUES('The value for the parameter @PhysicalOnly is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#PhysicalOnly.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5495,13 +5917,13 @@ BEGIN 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 + VALUES('The value for the parameter @DataPurity is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#DataPurity.', 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 + VALUES('The parameters @PhysicalOnly and @DataPurity cannot be used together. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5509,7 +5931,7 @@ BEGIN 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 + VALUES('The value for the parameter @NoIndex is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#NoIndex.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5517,13 +5939,13 @@ BEGIN 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 + VALUES('The value for the parameter @ExtendedLogicalChecks is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#ExtendedLogicalChecks.', 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 + VALUES('The parameters @PhysicalOnly and @ExtendedLogicalChecks cannot be used together. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5531,7 +5953,7 @@ BEGIN 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 + VALUES('The value for the parameter @NoInformationalMessages is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#NoInformationalMessages.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5539,7 +5961,7 @@ BEGIN 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 + VALUES('The value for the parameter @TabLock is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#TabLock.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5547,19 +5969,19 @@ BEGIN 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 + VALUES('The value for the parameter @FileGroups is not supported. The value contains one or more items that could not be parsed. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 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 + VALUES('The value for the parameter @FileGroups is not supported. The value could not be parsed into a list of filegroups. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 16, 1) 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 + VALUES('The parameter @FileGroups can only be used together with @CheckCommands = ''CHECKFILEGROUP''. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5567,19 +5989,19 @@ BEGIN 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 + VALUES('The value for the parameter @Objects is not supported. The value contains one or more items that could not be parsed. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 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 + VALUES('The value for the parameter @Objects is not supported. The value could not be parsed into a list of objects. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 16, 1) 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 + VALUES('The parameter @Objects can only be used together with @CheckCommands = ''CHECKTABLE''. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5587,13 +6009,7 @@ BEGIN 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 + VALUES('The value for the parameter @MaxDOP is not supported. The value has to be between 0 and 64. See https://ola.hallengren.com/sql-server-integrity-check.html#MaxDOP.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5601,7 +6017,7 @@ BEGIN 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 + VALUES('The value for the parameter @AvailabilityGroupReplicas is not supported. Supported values are ALL, PRIMARY, SECONDARY and PREFERRED_BACKUP_REPLICA. See https://ola.hallengren.com/sql-server-integrity-check.html#AvailabilityGroupReplicas.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5609,7 +6025,7 @@ BEGIN 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 + VALUES('The value for the parameter @Updateability is not supported. Supported values are ALL, READ_ONLY and READ_WRITE. See https://ola.hallengren.com/sql-server-integrity-check.html#Updateability.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5617,31 +6033,31 @@ BEGIN IF @TimeLimit < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @TimeLimit is not supported.', 16, 1 + VALUES('The value for the parameter @TimeLimit is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-integrity-check.html#TimeLimit.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @LockTimeout < 0 + IF @LockTimeout < 0 OR @LockTimeout > 86400 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @LockTimeout is not supported.', 16, 1 + VALUES('The value for the parameter @LockTimeout is not supported. The value has to be between 0 and 86400. See https://ola.hallengren.com/sql-server-integrity-check.html#LockTimeout.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @LockMessageSeverity NOT IN(10, 16) + 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 + VALUES('The value for the parameter @LockMessageSeverity is not supported. Supported values are 10 and 16. See https://ola.hallengren.com/sql-server-integrity-check.html#LockMessageSeverity.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) > 1 + 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 + VALUES('The value for the parameter @StringDelimiter is not supported. The value has to be exactly one character. See https://ola.hallengren.com/sql-server-integrity-check.html#StringDelimiter.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5649,31 +6065,31 @@ BEGIN 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 + VALUES('The value for the parameter @DatabaseOrder is not supported. Supported values are 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 and REPLICA_LAST_GOOD_CHECK_DESC. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 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) + IF @DatabaseOrder IN('DATABASE_LAST_GOOD_CHECK_ASC','DATABASE_LAST_GOOD_CHECK_DESC') AND NOT (@Version >= 14.03029 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) 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 + VALUES('The value for the parameter @DatabaseOrder is not supported. DATABASEPROPERTYEX(''DatabaseName'', ''LastGoodCheckDbTime'') is not available in this version of SQL Server. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 16, 1) 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 + VALUES('The value for the parameter @DatabaseOrder is not supported. You need to provide the parameter @LogToTable = ''Y''. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 16, 1) 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 + VALUES('The value for the parameter @DatabaseOrder is not supported. You need to provide the parameter @CheckCommands = ''CHECKDB''. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 16, 1) END - IF @DatabaseOrder IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 5 + IF @DatabaseOrder IS NOT NULL AND @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 + VALUES('The parameter @DatabaseOrder is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5681,13 +6097,13 @@ BEGIN 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 + VALUES('The value for the parameter @DatabasesInParallel is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabasesInParallel.', 16, 1) END - IF @DatabasesInParallel = 'Y' AND SERVERPROPERTY('EngineEdition') = 5 + IF @DatabasesInParallel = 'Y' AND @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 + VALUES('The parameter @DatabasesInParallel is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabasesInParallel.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5695,7 +6111,7 @@ BEGIN 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 + VALUES('The value for the parameter @LogToTable is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#LogToTable.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -5703,22 +6119,15 @@ BEGIN 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 + VALUES('The value for the parameter @Execute is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#Execute.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Check that selected databases and availability groups exist //-- ---------------------------------------------------------------------------------------------------- - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedDatabases WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) @@ -5726,10 +6135,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#Databases.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedFileGroups WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) @@ -5737,10 +6147,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following databases in the @FileGroups parameter do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases in the @FileGroups parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedObjects WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) @@ -5748,10 +6159,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following databases in the @Objects parameter do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases in the @Objects parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(AvailabilityGroupName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(AvailabilityGroupName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY AvailabilityGroupName ASC) FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName NOT LIKE '%[%]%' AND AvailabilityGroupName NOT IN (SELECT AvailabilityGroupName FROM @tmpAvailabilityGroups) @@ -5759,10 +6171,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following availability groups do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following availability groups do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#AvailabilityGroups.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedFileGroups WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName IN (SELECT DatabaseName FROM @tmpDatabases) @@ -5771,10 +6184,11 @@ BEGIN IF @ErrorMessage IS NOT NULL 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: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases have been selected in the @FileGroups parameter, but not in the @Databases or @AvailabilityGroups parameters: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedObjects WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName IN (SELECT DatabaseName FROM @tmpDatabases) @@ -5783,24 +6197,24 @@ BEGIN IF @ErrorMessage IS NOT NULL 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: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases have been selected in the @Objects parameter, but not in the @Databases or @AvailabilityGroups parameters: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 10, 1) END ---------------------------------------------------------------------------------------------------- --// Check @@SERVERNAME //-- ---------------------------------------------------------------------------------------------------- - IF UPPER(@@SERVERNAME) <> UPPER(CAST(SERVERPROPERTY('ServerName') AS nvarchar(max))) AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF UPPER(@@SERVERNAME) <> UPPER(@ServerName) AND @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 + VALUES('The @@SERVERNAME does not match SERVERPROPERTY(''ServerName''). See ' + CASE WHEN @IsClustered = 0 THEN 'https://learn.microsoft.com/en-us/sql/database-engine/install-windows/rename-a-computer-that-hosts-a-stand-alone-instance-of-sql-server' WHEN @IsClustered = 1 THEN 'https://learn.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 + DECLARE ErrorCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Message], Severity, [State] FROM @Errors ORDER BY [ID] ASC OPEN ErrorCursor @@ -5967,7 +6381,7 @@ BEGIN FROM dbo.[Queue] WHERE SchemaName = @SchemaName AND ObjectName = @ObjectName - AND [Parameters] = @Parameters + AND [Parameters] = @ParametersString IF @QueueID IS NULL BEGIN @@ -5977,12 +6391,12 @@ BEGIN FROM dbo.[Queue] WITH (UPDLOCK, HOLDLOCK) WHERE SchemaName = @SchemaName AND ObjectName = @ObjectName - AND [Parameters] = @Parameters + AND [Parameters] = @ParametersString IF @QueueID IS NULL BEGIN INSERT INTO dbo.[Queue] (SchemaName, ObjectName, [Parameters]) - SELECT @SchemaName, @ObjectName, @Parameters + VALUES(@SchemaName, @ObjectName, @ParametersString) SET @QueueID = SCOPE_IDENTITY() END @@ -6016,17 +6430,17 @@ BEGIN DatabaseName FROM @tmpDatabases tmpDatabases WHERE Selected = 1 - AND NOT EXISTS (SELECT * FROM dbo.QueueDatabase WHERE DatabaseName = tmpDatabases.DatabaseName AND QueueID = @QueueID) + AND NOT EXISTS (SELECT * FROM dbo.QueueDatabase WHERE DatabaseName COLLATE DATABASE_DEFAULT = 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) + AND NOT EXISTS (SELECT * FROM @tmpDatabases tmpDatabases WHERE DatabaseName = QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT AND Selected = 1) UPDATE QueueDatabase SET DatabaseOrder = tmpDatabases.[Order] FROM dbo.QueueDatabase QueueDatabase - INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName = tmpDatabases.DatabaseName + INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT = tmpDatabases.DatabaseName WHERE QueueID = @QueueID END @@ -6043,7 +6457,7 @@ BEGIN BEGIN ROLLBACK TRANSACTION END - SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar) + ', ' + ISNULL(ERROR_MESSAGE(),'') + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + ISNULL(ERROR_MESSAGE(),'') RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT SET @ReturnCode = ERROR_NUMBER() @@ -6057,7 +6471,7 @@ BEGIN ---------------------------------------------------------------------------------------------------- WHILE (1 = 1) - BEGIN + BEGIN -- Start of database loop IF @DatabasesInParallel = 'Y' BEGIN @@ -6104,13 +6518,13 @@ BEGIN IF @@ROWCOUNT = 0 BEGIN - BREAK + BREAK END SET @CurrentDatabase_sp_executesql = QUOTENAME(@CurrentDatabaseName) + '.sys.sp_executesql' BEGIN - SET @DatabaseMessage = 'Date and time: ' + CONVERT(nvarchar,SYSDATETIME(),120) + SET @DatabaseMessage = 'Date and time: ' + CONVERT(nvarchar(max),SYSDATETIME(),120) RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT SET @DatabaseMessage = 'Database: ' + QUOTENAME(@CurrentDatabaseName) @@ -6142,21 +6556,49 @@ BEGIN RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT END - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN - SELECT @CurrentReplicaID = databases.replica_id + SELECT @CurrentAvailabilityGroupReplicaID = 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 + SELECT @CurrentAvailabilityGroupID = group_id + FROM sys.availability_replicas + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + + IF @CurrentAvailabilityGroupReplicaID IS NULL AND @CurrentAvailabilityGroupID IS NULL AND @Version >= 16 + BEGIN + SET @CurrentAvailabilityGroupID = @ContainedAvailabilityGroupID + + SELECT @CurrentAvailabilityGroupReplicaID = replica_id + FROM sys.dm_hadr_availability_replica_states + WHERE group_id = @CurrentAvailabilityGroupID + AND is_local = 1 + END + + SELECT @CurrentSecondaryRoleAllowConnections = secondary_role_allow_connections_desc FROM sys.availability_replicas - WHERE replica_id = @CurrentReplicaID + WHERE replica_id = @CurrentAvailabilityGroupReplicaID SELECT @CurrentAvailabilityGroupRole = role_desc FROM sys.dm_hadr_availability_replica_states - WHERE replica_id = @CurrentReplicaID + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND database_id = DB_ID(@CurrentDatabaseName) + + IF @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState IS NULL AND @ContainedAvailabilityGroupListenerConnection = 1 + BEGIN + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND DB_NAME(database_id) = @CurrentDatabaseName + END SELECT @CurrentAvailabilityGroup = [name], @CurrentAvailabilityGroupBackupPreference = UPPER(automated_backup_preference_desc) @@ -6164,12 +6606,27 @@ BEGIN WHERE group_id = @CurrentAvailabilityGroupID END - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 AND @CurrentAvailabilityGroup IS NOT NULL AND @AvailabilityGroupReplicas = 'PREFERRED_BACKUP_REPLICA' + IF @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 + IF @IsHadrEnabled = 1 AND @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SELECT @CurrentDistributedAvailabilityGroup = availability_groups.[name], + @CurrentDistributedAvailabilityGroupReplicaID = availability_replicas.replica_id + FROM sys.availability_groups availability_groups + INNER JOIN sys.availability_replicas availability_replicas ON availability_groups.group_id = availability_replicas.group_id + INNER JOIN sys.availability_groups availability_groups_local ON availability_replicas.replica_server_name = availability_groups_local.[name] + WHERE availability_groups.is_distributed = 1 + AND availability_groups_local.group_id = @CurrentAvailabilityGroupID + + SELECT @CurrentDistributedAvailabilityGroupRole = dm_hadr_availability_replica_states.role_desc + FROM sys.dm_hadr_availability_replica_states dm_hadr_availability_replica_states + WHERE dm_hadr_availability_replica_states.replica_id = @CurrentDistributedAvailabilityGroupReplicaID + END + + IF @EngineEdition <> 5 BEGIN SELECT @CurrentDatabaseMirroringRole = UPPER(mirroring_role_desc) FROM sys.database_mirroring database_mirroring @@ -6185,6 +6642,12 @@ BEGIN 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 + IF @CurrentAvailabilityGroupRole = 'SECONDARY' BEGIN SET @DatabaseMessage = 'Readable Secondary: ' + ISNULL(@CurrentSecondaryRoleAllowConnections,'N/A') @@ -6201,6 +6664,21 @@ BEGIN END END + IF @CurrentDistributedAvailabilityGroup IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Distributed availability group: ' + ISNULL(@CurrentDistributedAvailabilityGroup,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Distributed availability group role: ' + ISNULL(@CurrentDistributedAvailabilityGroupRole,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Replica role in distributed availability group: ' + CASE WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Global primary' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Forwarder' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in secondary availability group' + WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in primary availability group' ELSE 'N/A' END + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + END + IF @CurrentDatabaseMirroringRole IS NOT NULL BEGIN SET @DatabaseMessage = 'Database mirroring role: ' + @CurrentDatabaseMirroringRole @@ -6211,7 +6689,7 @@ BEGIN IF @CurrentDatabaseState IN('ONLINE','EMERGENCY') AND NOT (@CurrentUserAccess = 'SINGLE_USER') - AND (@CurrentAvailabilityGroupRole = 'PRIMARY' OR @CurrentAvailabilityGroupRole IS NULL OR SERVERPROPERTY('EngineEdition') = 3) + AND (@CurrentAvailabilityGroupRole = 'PRIMARY' OR (@CurrentAvailabilityGroupRole = 'SECONDARY' AND @EngineEdition = 3) OR @CurrentAvailabilityGroup IS NULL) 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') @@ -6221,12 +6699,12 @@ 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 @CurrentDatabaseContext = CASE WHEN @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) + '; ' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' SET @CurrentCommand += 'DBCC CHECKDB (' + QUOTENAME(@CurrentDatabaseName) IF @NoIndex = 'Y' SET @CurrentCommand += ', NOINDEX' SET @CurrentCommand += ') WITH ALL_ERRORMSGS' @@ -6235,7 +6713,7 @@ BEGIN 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) + IF @MaxDOP IS NOT NULL SET @CurrentCommand += ', MAXDOP = ' + CAST(@MaxDOP AS nvarchar(max)) EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @LogToTable = @LogToTable, @Execute = @Execute SET @Error = @@ERROR @@ -6248,9 +6726,9 @@ BEGIN 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' + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT data_space_id AS FileGroupID, name AS FileGroupName 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) + INSERT INTO @tmpFileGroups (FileGroupID, FileGroupName) EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand SET @Error = @@ERROR IF @Error <> 0 SET @ReturnCode = @Error @@ -6267,14 +6745,14 @@ BEGIN 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,'_','[_]') + ON @CurrentDatabaseName LIKE REPLACE(REPLACE(SelectedFileGroups.DatabaseName,'[','[[]'),'_','[_]') AND tmpFileGroups.FileGroupName LIKE REPLACE(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,'_','[_]') + ON @CurrentDatabaseName LIKE REPLACE(REPLACE(SelectedFileGroups.DatabaseName,'[','[[]'),'_','[_]') AND tmpFileGroups.FileGroupName LIKE REPLACE(REPLACE(SelectedFileGroups.FileGroupName,'[','[[]'),'_','[_]') WHERE SelectedFileGroups.Selected = 0 UPDATE tmpFileGroups @@ -6283,7 +6761,7 @@ BEGIN 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,'_','[_]') + ON @CurrentDatabaseName LIKE REPLACE(REPLACE(SelectedFileGroups.DatabaseName,'[','[[]'),'_','[_]') AND tmpFileGroups.FileGroupName LIKE REPLACE(REPLACE(SelectedFileGroups.FileGroupName,'[','[[]'),'_','[_]') WHERE SelectedFileGroups.Selected = 1 GROUP BY tmpFileGroups.FileGroupName) SelectedFileGroups2 ON tmpFileGroups.FileGroupName = SelectedFileGroups2.FileGroupName @@ -6297,7 +6775,8 @@ BEGIN UPDATE tmpFileGroups SET [Order] = RowNumber - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName) + '.' + QUOTENAME(FileGroupName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)) + '.' + QUOTENAME(FileGroupName), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC, FileGroupName ASC) FROM @SelectedFileGroups SelectedFileGroups WHERE DatabaseName = @CurrentDatabaseName AND FileGroupName NOT LIKE '%[%]%' @@ -6307,7 +6786,6 @@ BEGIN BEGIN SET @ErrorMessage = 'The following file groups do not exist: ' + @ErrorMessage + '.' RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT - SET @Error = @@ERROR RAISERROR(@EmptyLine,10,1) WITH NOWAIT END @@ -6328,7 +6806,7 @@ BEGIN -- Does the filegroup exist? SET @CurrentCommand = '' - IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar) + '; ' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' 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 @@ -6337,7 +6815,7 @@ BEGIN 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 @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 @@ -6355,14 +6833,14 @@ BEGIN SET @CurrentCommandType = 'DBCC_CHECKFILEGROUP' SET @CurrentCommand = '' - IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar) + '; ' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' 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) + IF @MaxDOP IS NOT NULL SET @CurrentCommand += ', MAXDOP = ' + CAST(@MaxDOP AS nvarchar(max)) EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 1, @DatabaseName = @CurrentDatabaseName, @LogToTable = @LogToTable, @Execute = @Execute SET @Error = @@ERROR @@ -6391,13 +6869,14 @@ BEGIN -- 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 @CurrentDatabaseContext = CASE WHEN @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) + '; ' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' SET @CurrentCommand += 'DBCC CHECKALLOC (' + QUOTENAME(@CurrentDatabaseName) + IF @NoIndex = 'Y' SET @CurrentCommand += ', NOINDEX' SET @CurrentCommand += ') WITH ALL_ERRORMSGS' IF @NoInformationalMessages = 'Y' SET @CurrentCommand += ', NO_INFOMSGS' IF @TabLock = 'Y' SET @CurrentCommand += ', TABLOCK' @@ -6413,9 +6892,9 @@ BEGIN 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' + 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 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) AND (tables.is_memory_optimized = 0 OR is_memory_optimized IS NULL) ORDER BY schemas.name ASC, objects.name ASC' - INSERT INTO @tmpObjects (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, [Order], Selected, Completed) + INSERT INTO @tmpObjects (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType) EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand SET @Error = @@ERROR IF @Error <> 0 SET @ReturnCode = @Error @@ -6432,14 +6911,14 @@ BEGIN 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,'_','[_]') + ON @CurrentDatabaseName LIKE REPLACE(REPLACE(SelectedObjects.DatabaseName,'[','[[]'),'_','[_]') AND tmpObjects.SchemaName LIKE REPLACE(REPLACE(SelectedObjects.SchemaName,'[','[[]'),'_','[_]') AND tmpObjects.ObjectName LIKE REPLACE(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,'_','[_]') + ON @CurrentDatabaseName LIKE REPLACE(REPLACE(SelectedObjects.DatabaseName,'[','[[]'),'_','[_]') AND tmpObjects.SchemaName LIKE REPLACE(REPLACE(SelectedObjects.SchemaName,'[','[[]'),'_','[_]') AND tmpObjects.ObjectName LIKE REPLACE(REPLACE(SelectedObjects.ObjectName,'[','[[]'),'_','[_]') WHERE SelectedObjects.Selected = 0 UPDATE tmpObjects @@ -6448,7 +6927,7 @@ BEGIN 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,'_','[_]') + ON @CurrentDatabaseName LIKE REPLACE(REPLACE(SelectedObjects.DatabaseName,'[','[[]'),'_','[_]') AND tmpObjects.SchemaName LIKE REPLACE(REPLACE(SelectedObjects.SchemaName,'[','[[]'),'_','[_]') AND tmpObjects.ObjectName LIKE REPLACE(REPLACE(SelectedObjects.ObjectName,'[','[[]'),'_','[_]') WHERE SelectedObjects.Selected = 1 GROUP BY tmpObjects.SchemaName, tmpObjects.ObjectName) SelectedObjects2 ON tmpObjects.SchemaName = SelectedObjects2.SchemaName AND tmpObjects.ObjectName = SelectedObjects2.ObjectName @@ -6462,7 +6941,8 @@ BEGIN UPDATE tmpObjects SET [Order] = RowNumber - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName) + '.' + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)) + '.' + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC, SchemaName ASC, ObjectName ASC) FROM @SelectedObjects SelectedObjects WHERE DatabaseName = @CurrentDatabaseName AND SchemaName NOT LIKE '%[%]%' @@ -6473,7 +6953,6 @@ BEGIN BEGIN SET @ErrorMessage = 'The following objects do not exist: ' + @ErrorMessage + '.' RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT - SET @Error = @@ERROR RAISERROR(@EmptyLine,10,1) WITH NOWAIT END @@ -6497,8 +6976,8 @@ BEGIN -- 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' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + 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) AND (tables.is_memory_optimized = 0 OR is_memory_optimized IS NULL) 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 @@ -6506,7 +6985,7 @@ BEGIN 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 @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 @@ -6524,8 +7003,8 @@ BEGIN 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 (N' + QUOTENAME(QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName),'''') + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + SET @CurrentCommand += 'DBCC CHECKTABLE (N''' + REPLACE(QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName),'''','''''') + '''' IF @NoIndex = 'Y' SET @CurrentCommand += ', NOINDEX' SET @CurrentCommand += ') WITH ALL_ERRORMSGS' IF @DataPurity = 'Y' SET @CurrentCommand += ', DATA_PURITY' @@ -6533,7 +7012,7 @@ BEGIN 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) + IF @MaxDOP IS NOT NULL SET @CurrentCommand += ', MAXDOP = ' + CAST(@MaxDOP AS nvarchar(max)) 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 @@ -6563,14 +7042,14 @@ BEGIN END -- Check catalog - IF EXISTS(SELECT * FROM @SelectedCheckCommands WHERE CheckCommand = 'CHECKCATALOG') AND @CurrentAvailabilityGroupRole = 'PRIMARY' AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + IF EXISTS(SELECT * FROM @SelectedCheckCommands WHERE CheckCommand = 'CHECKCATALOG') AND (@CurrentAvailabilityGroupRole = 'PRIMARY' OR @CurrentAvailabilityGroupRole IS NULL) AND (@CurrentDistributedAvailabilityGroupRole = 'PRIMARY' OR @CurrentDistributedAvailabilityGroupRole IS NULL) AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) BEGIN - SET @CurrentDatabaseContext = CASE WHEN SERVERPROPERTY('EngineEdition') = 5 THEN @CurrentDatabaseName ELSE 'master' END + SET @CurrentDatabaseContext = CASE WHEN @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) + '; ' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' SET @CurrentCommand += 'DBCC CHECKCATALOG (' + QUOTENAME(@CurrentDatabaseName) SET @CurrentCommand += ')' IF @NoInformationalMessages = 'Y' SET @CurrentCommand += ' WITH NO_INFOMSGS' @@ -6588,6 +7067,7 @@ BEGIN SET @ErrorMessage = 'The database ' + QUOTENAME(@CurrentDatabaseName) + ' is in a SUSPECT state.' RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT SET @Error = @@ERROR + SET @ReturnCode = @Error RAISERROR(@EmptyLine,10,1) WITH NOWAIT END @@ -6619,14 +7099,18 @@ BEGIN SET @CurrentDatabaseState = NULL SET @CurrentInStandby = NULL SET @CurrentRecoveryModel = NULL - - SET @CurrentReplicaID = NULL + SET @CurrentAvailabilityGroupReplicaID = NULL SET @CurrentAvailabilityGroupID = NULL SET @CurrentAvailabilityGroup = NULL SET @CurrentAvailabilityGroupRole = NULL + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = NULL + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = NULL SET @CurrentAvailabilityGroupBackupPreference = NULL SET @CurrentSecondaryRoleAllowConnections = NULL SET @CurrentIsPreferredBackupReplica = NULL + SET @CurrentDistributedAvailabilityGroup = NULL + SET @CurrentDistributedAvailabilityGroupReplicaID = NULL + SET @CurrentDistributedAvailabilityGroupRole = NULL SET @CurrentDatabaseMirroringRole = NULL SET @CurrentDatabaseContext = NULL @@ -6637,14 +7121,14 @@ BEGIN DELETE FROM @tmpFileGroups DELETE FROM @tmpObjects - END + END -- End of database loop ---------------------------------------------------------------------------------------------------- --// Log completing information //-- ---------------------------------------------------------------------------------------------------- Logging: - SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar,SYSDATETIME(),120) + SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar(max),SYSDATETIME(),120) RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT @@ -6683,20 +7167,22 @@ ALTER PROCEDURE [dbo].[IndexOptimize] @MaxDOP int = NULL, @FillFactor int = NULL, @PadIndex nvarchar(max) = NULL, +@DataCompression nvarchar(max) = NULL, +@WaitAtLowPriorityMaxDuration int = NULL, +@WaitAtLowPriorityAbortAfterWait nvarchar(max) = NULL, +@Resumable nvarchar(max) = 'N', @LOBCompaction nvarchar(max) = 'Y', @UpdateStatistics nvarchar(max) = NULL, @OnlyModifiedStatistics nvarchar(max) = 'N', @StatisticsModificationLevel int = NULL, @StatisticsSample int = NULL, +@StatisticsPersistSample nvarchar(max) = 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, @@ -6715,7 +7201,7 @@ BEGIN --// Source: https://ola.hallengren.com //-- --// License: https://ola.hallengren.com/license.html //-- --// GitHub: https://github.com/olahallengren/sql-server-maintenance-solution //-- - --// Version: 2026-05-18 20:15:56 //-- + --// Version: 2026-08-23 14:46:45 //-- ---------------------------------------------------------------------------------------------------- SET NOCOUNT ON @@ -6734,11 +7220,25 @@ BEGIN 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 @Parameters TABLE (ID int IDENTITY PRIMARY KEY, + [Name] nvarchar(max) NOT NULL, + ValueNvarchar nvarchar(max), + ValueInt int, + ValueDatetime datetime2) - DECLARE @PartitionLevelStatistics bit + DECLARE @ParametersString nvarchar(max) + DECLARE @CurrentParameterName nvarchar(max) + DECLARE @CurrentParameterValueNvarchar nvarchar(max) + DECLARE @CurrentParameterValueInt int + DECLARE @CurrentParameterValueDatetime datetime2 + DECLARE @CurrentParameterDelimiter nvarchar(max) + DECLARE @CurrentParameterMessage nvarchar(max) + + DECLARE @HostPlatform nvarchar(max) + DECLARE @ContainedAvailabilityGroupID uniqueidentifier + DECLARE @ContainedAvailabilityGroupListenerConnection bit + DECLARE @IsSysadmin bit = IS_SRVROLEMEMBER('sysadmin') DECLARE @QueueID int DECLARE @QueueStartTime datetime2 @@ -6754,11 +7254,16 @@ BEGIN DECLARE @CurrentDatabaseState nvarchar(max) DECLARE @CurrentInStandby bit DECLARE @CurrentRecoveryModel nvarchar(max) - - DECLARE @CurrentReplicaID uniqueidentifier + DECLARE @CurrentDatabaseHasReadOnlyFileGroup bit + DECLARE @CurrentAvailabilityGroupReplicaID uniqueidentifier DECLARE @CurrentAvailabilityGroupID uniqueidentifier DECLARE @CurrentAvailabilityGroup nvarchar(max) DECLARE @CurrentAvailabilityGroupRole nvarchar(max) + DECLARE @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState nvarchar(max) + DECLARE @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth nvarchar(max) + DECLARE @CurrentDistributedAvailabilityGroup nvarchar(max) + DECLARE @CurrentDistributedAvailabilityGroupReplicaID uniqueidentifier + DECLARE @CurrentDistributedAvailabilityGroupRole nvarchar(max) DECLARE @CurrentDatabaseMirroringRole nvarchar(max) DECLARE @CurrentDatabaseContext nvarchar(max) @@ -6771,7 +7276,7 @@ BEGIN DECLARE @Errors TABLE (ID int IDENTITY PRIMARY KEY, [Message] nvarchar(max) NOT NULL, Severity int NOT NULL, - [State] int) + [State] int NOT NULL) DECLARE @CurrentMessage nvarchar(max) DECLARE @CurrentSeverity int @@ -6792,15 +7297,17 @@ BEGIN DECLARE @CurrentStatisticsName nvarchar(max) DECLARE @CurrentPartitionID bigint DECLARE @CurrentPartitionNumber int - DECLARE @CurrentPartitionCount int + DECLARE @CurrentInRowDataPageCount bigint + DECLARE @CurrentAlterIndexCompleted bit + DECLARE @CurrentUpdateStatisticsCompleted bit DECLARE @CurrentIsPartition bit + DECLARE @CurrentIsLastPartition bit DECLARE @CurrentIndexExists bit DECLARE @CurrentStatisticsExists bit DECLARE @CurrentIsImageText bit - DECLARE @CurrentIsNewLOB bit DECLARE @CurrentIsFileStream bit DECLARE @CurrentHasClusteredColumnstore bit - DECLARE @CurrentHasNonClusteredColumnstore bit + DECLARE @CurrentIsColumnstoreOrdered bit DECLARE @CurrentIsComputed bit DECLARE @CurrentIsClusteredIndexComputed bit DECLARE @CurrentIsTimestamp bit @@ -6808,6 +7315,7 @@ BEGIN DECLARE @CurrentHasFilter bit DECLARE @CurrentNoRecompute bit DECLARE @CurrentIsIncremental bit + DECLARE @CurrentObjectHasRows bit DECLARE @CurrentRowCount bigint DECLARE @CurrentModificationCounter bigint DECLARE @CurrentOnReadOnlyFileGroup bit @@ -6819,66 +7327,136 @@ BEGIN DECLARE @CurrentMaxDOP int DECLARE @CurrentUpdateStatistics nvarchar(max) DECLARE @CurrentStatisticsSample int + DECLARE @CurrentStatisticsPersistSample nvarchar(max) DECLARE @CurrentStatisticsResample nvarchar(max) DECLARE @CurrentDelay datetime DECLARE @tmpDatabases TABLE (ID int IDENTITY, - DatabaseName nvarchar(max), - DatabaseType nvarchar(max), + DatabaseName nvarchar(128), + DatabaseType nvarchar(1), AvailabilityGroup bit, StartPosition int, DatabaseSize bigint, - [Order] int, - Selected bit, - Completed bit, - PRIMARY KEY(Selected, Completed, [Order], ID)) + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) DECLARE @tmpAvailabilityGroups TABLE (ID int IDENTITY PRIMARY KEY, - AvailabilityGroupName nvarchar(max), + AvailabilityGroupName nvarchar(128), StartPosition int, - Selected bit) + Selected bit DEFAULT 0) - DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(max), - AvailabilityGroupName nvarchar(max)) + DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(128), + AvailabilityGroupName nvarchar(128)) DECLARE @tmpIndexesStatistics TABLE (ID int IDENTITY, SchemaID int, - SchemaName nvarchar(max), + SchemaName nvarchar(128), ObjectID int, - ObjectName nvarchar(max), - ObjectType nvarchar(max), + ObjectName nvarchar(128), + ObjectType nvarchar(2), IsMemoryOptimized bit, IndexID int, - IndexName nvarchar(max), + IndexName nvarchar(128), IndexType int, AllowPageLocks bit, HasFilter bit, IsImageText bit, - IsNewLOB bit, IsFileStream bit, HasClusteredColumnstore bit, - HasNonClusteredColumnstore bit, + IsColumnstoreOrdered bit, IsComputed bit, IsClusteredIndexComputed bit, IsTimestamp bit, OnReadOnlyFileGroup bit, ResumableIndexOperation bit, StatisticsID int, - StatisticsName nvarchar(max), + StatisticsName nvarchar(128), [NoRecompute] bit, IsIncremental bit, PartitionID bigint, PartitionNumber int, - PartitionCount int, + IsPartition bit, + IsLastPartition bit, + InRowDataPageCount bigint, StartPosition int, - [Order] int, - Selected bit, - Completed bit, - PRIMARY KEY(Selected, Completed, [Order], ID)) + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + AlterIndexCompleted bit DEFAULT 0, + UpdateStatisticsCompleted bit DEFAULT 0, + Completed AS CAST(CASE WHEN AlterIndexCompleted = 1 AND UpdateStatisticsCompleted = 1 THEN 1 ELSE 0 END AS bit) PERSISTED, + PRIMARY KEY (Selected, Completed, [Order], ID), + INDEX IX_ObjectID_IndexID_PartitionNumber NONCLUSTERED (ObjectID, IndexID, PartitionNumber), + INDEX IX_ObjectID_StatisticsID_PartitionNumber NONCLUSTERED (ObjectID, StatisticsID, PartitionNumber)) + + DROP TABLE IF EXISTS #SelectedIndexes + + CREATE TABLE #SelectedIndexes (DatabaseName nvarchar(max) COLLATE DATABASE_DEFAULT, + SchemaName nvarchar(max) COLLATE DATABASE_DEFAULT, + ObjectName nvarchar(max) COLLATE DATABASE_DEFAULT, + IndexName nvarchar(max) COLLATE DATABASE_DEFAULT, + StartPosition int, + Selected bit) + + DROP TABLE IF EXISTS #Objects + + CREATE TABLE #Objects (ObjectID int NOT NULL, + SchemaID int, + SchemaName nvarchar(128) COLLATE DATABASE_DEFAULT, + ObjectName nvarchar(128) COLLATE DATABASE_DEFAULT, + ObjectType nvarchar(2) COLLATE DATABASE_DEFAULT, + IsMemoryOptimized bit, + HasClusteredColumnstore bit, + IsClusteredIndexComputed bit, + IsClusteredIndexDisabled bit, + PRIMARY KEY (ObjectID)) + + DROP TABLE IF EXISTS #Indexes + + CREATE TABLE #Indexes (ObjectID int NOT NULL, + IndexID int NOT NULL, + IndexName nvarchar(128) COLLATE DATABASE_DEFAULT, + IndexType int, + DataSpaceID int, + IsPartitioned bit, + AllowPageLocks bit, + HasFilter bit, + IsImageText bit, + IsFileStream bit, + IsColumnstoreOrdered bit, + IsComputed bit, + IsTimestamp bit, + PRIMARY KEY (ObjectID, IndexID)) + + DROP TABLE IF EXISTS #Stats + + CREATE TABLE #Stats (ObjectID int NOT NULL, + StatisticsID int NOT NULL, + StatisticsName nvarchar(128) COLLATE DATABASE_DEFAULT, + [NoRecompute] bit, + IsIncremental bit, + IsIndex bit, + PRIMARY KEY (ObjectID, StatisticsID)) + + DROP TABLE IF EXISTS #ExistingObjects + + CREATE TABLE #ExistingObjects (SchemaName nvarchar(max) COLLATE DATABASE_DEFAULT, + ObjectName nvarchar(max) COLLATE DATABASE_DEFAULT) + + DROP TABLE IF EXISTS #ExistingIndexes + + CREATE TABLE #ExistingIndexes (SchemaName nvarchar(max) COLLATE DATABASE_DEFAULT, + ObjectName nvarchar(max) COLLATE DATABASE_DEFAULT, + IndexName nvarchar(max) COLLATE DATABASE_DEFAULT) + + DECLARE @tmpResumableOperations TABLE (ObjectID int NOT NULL, + IndexID int NOT NULL, + PartitionNumber int) DECLARE @SelectedDatabases TABLE (DatabaseName nvarchar(max), - DatabaseType nvarchar(max), - AvailabilityGroup nvarchar(max), + DatabaseType nvarchar(1), + AvailabilityGroup bit, StartPosition int, Selected bit) @@ -6893,6 +7471,20 @@ BEGIN StartPosition int, Selected bit) + DECLARE @PhysicalStats TABLE (ObjectID int, + IndexID int, + PartitionNumber int, + FragmentationLevel float, + PageCount bigint, + PRIMARY KEY (ObjectID, IndexID, PartitionNumber)) + + DECLARE @IncrementalStatsProperties TABLE (ObjectID int, + StatisticsID int, + PartitionNumber int, + [Rows] bigint, + ModificationCounter bigint, + PRIMARY KEY (ObjectID, StatisticsID, PartitionNumber)) + DECLARE @Actions TABLE ([Action] nvarchar(max)) INSERT INTO @Actions([Action]) VALUES('INDEX_REBUILD_ONLINE') @@ -6916,79 +7508,124 @@ BEGIN 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)) + DECLARE @ProductVersion nvarchar(max) = CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + DECLARE @ProductUpdateType nvarchar(max) = CAST(SERVERPROPERTY('ProductUpdateType') AS nvarchar(max)) + DECLARE @EngineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int) + DECLARE @Edition nvarchar(max) = CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + DECLARE @IsHadrEnabled bit = CAST(SERVERPROPERTY('IsHadrEnabled') AS bit) + DECLARE @ServerName nvarchar(max) = CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) + + DECLARE @Collation nvarchar(128) = CAST(DATABASEPROPERTYEX(DB_NAME(),'Collation') AS nvarchar(128)) + + DECLARE @Version numeric(18,10) = CAST(PARSENAME(@ProductVersion,4) + '.' + PARSENAME(@ProductVersion,3) + PARSENAME(@ProductVersion,2) AS numeric(18,10)) + + IF @EngineEdition = 8 AND @ProductVersion = '12.0.2000.8' AND @ProductUpdateType = 'CU' + BEGIN + SET @Version = 16.01000 + END - IF @Version >= 14 + IF @EngineEdition <> 5 BEGIN SELECT @HostPlatform = host_platform FROM sys.dm_os_host_info END - ELSE + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) BEGIN - SET @HostPlatform = 'Windows' + SET @CurrentCommand = 'SELECT @ParamContainedAvailabilityGroupID = contained_availability_group_id FROM sys.dm_exec_sessions WHERE session_id = @@SPID' + + EXECUTE sp_executesql @stmt = @CurrentCommand, @params = N'@ParamContainedAvailabilityGroupID uniqueidentifier OUTPUT', @ParamContainedAvailabilityGroupID = @ContainedAvailabilityGroupID OUTPUT 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 + SET @ContainedAvailabilityGroupListenerConnection = CASE WHEN @ContainedAvailabilityGroupID IS NOT NULL THEN 1 ELSE 0 END + + DECLARE @AmazonRDS bit = CASE WHEN @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) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Databases', @Databases) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FragmentationLow', @FragmentationLow) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FragmentationMedium', @FragmentationMedium) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FragmentationHigh', @FragmentationHigh) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@FragmentationLevel1', @FragmentationLevel1) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@FragmentationLevel2', @FragmentationLevel2) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MinNumberOfPages', @MinNumberOfPages) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MaxNumberOfPages', @MaxNumberOfPages) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@SortInTempdb', @SortInTempdb) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MaxDOP', @MaxDOP) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@FillFactor', @FillFactor) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@PadIndex', @PadIndex) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataCompression', @DataCompression) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@WaitAtLowPriorityMaxDuration', @WaitAtLowPriorityMaxDuration) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@WaitAtLowPriorityAbortAfterWait', @WaitAtLowPriorityAbortAfterWait) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Resumable', @Resumable) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@LOBCompaction', @LOBCompaction) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@UpdateStatistics', @UpdateStatistics) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@OnlyModifiedStatistics', @OnlyModifiedStatistics) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@StatisticsModificationLevel', @StatisticsModificationLevel) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@StatisticsSample', @StatisticsSample) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StatisticsPersistSample', @StatisticsPersistSample) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StatisticsResample', @StatisticsResample) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@PartitionLevel', @PartitionLevel) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@MSShippedObjects', @MSShippedObjects) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Indexes', @Indexes) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@TimeLimit', @TimeLimit) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@Delay', @Delay) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroups', @AvailabilityGroups) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@LockTimeout', @LockTimeout) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@LockMessageSeverity', @LockMessageSeverity) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StringDelimiter', @StringDelimiter) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabaseOrder', @DatabaseOrder) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabasesInParallel', @DatabasesInParallel) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ExecuteAsUser', @ExecuteAsUser) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@LogToTable', @LogToTable) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Execute', @Execute) + + SELECT @ParametersString = STRING_AGG(CAST([Name] + ' = ' + CASE WHEN ValueNvarchar IS NOT NULL THEN '''' + REPLACE(ValueNvarchar,'''','''''') + '''' WHEN ValueInt IS NOT NULL THEN CAST(ValueInt AS nvarchar(max)) WHEN ValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), ValueDatetime, 21) + '''' ELSE 'NULL' END AS nvarchar(max)), ', ') WITHIN GROUP (ORDER BY [ID] ASC) + FROM @Parameters + + SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar(max),@StartTime,120) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Server: ' + CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) + SET @StartMessage = 'Server: ' + @ServerName RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Version: ' + CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + SET @StartMessage = 'Version: ' + @ProductVersion RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Edition: ' + CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + SET @StartMessage = 'Edition: ' + @Edition RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Platform: ' + @HostPlatform - RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + IF @EngineEdition = 8 + BEGIN + SET @StartMessage = 'Update type: ' + @ProductUpdateType + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @EngineEdition <> 5 + BEGIN + SET @StartMessage = 'Platform: ' + ISNULL(@HostPlatform, 'N/A') + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) + BEGIN + SET @StartMessage = 'Contained availability group connection: ' + CASE WHEN @ContainedAvailabilityGroupListenerConnection = 1 THEN 'Yes' WHEN @ContainedAvailabilityGroupListenerConnection = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @EngineEdition <> 5 + BEGIN + SET @StartMessage = 'Is sysadmin: ' + CASE WHEN @IsSysadmin = 1 THEN 'Yes' WHEN @IsSysadmin = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END - SET @StartMessage = 'Procedure: ' + QUOTENAME(DB_NAME()) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + SET @StartMessage = 'Database: ' + QUOTENAME(DB_NAME()) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT - SET @StartMessage = 'Parameters: ' + @Parameters + SET @StartMessage = 'Procedure: ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT SET @StartMessage = 'Version: ' + @VersionTimestamp @@ -6996,6 +7633,32 @@ BEGIN SET @StartMessage = 'Source: https://ola.hallengren.com' RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + + SET @StartMessage = 'Command:' + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'EXECUTE ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + DECLARE ParameterCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Name], ValueNvarchar, ValueInt, ValueDatetime, CASE WHEN [ID] = MAX([ID]) OVER() THEN '' ELSE ',' END FROM @Parameters ORDER BY [ID] ASC + + OPEN ParameterCursor + + FETCH ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + + WHILE @@FETCH_STATUS = 0 + BEGIN + SET @CurrentParameterMessage = @CurrentParameterName + ' = ' + CASE WHEN @CurrentParameterValueNvarchar IS NOT NULL THEN '''' + REPLACE(@CurrentParameterValueNvarchar,'''','''''') + '''' WHEN @CurrentParameterValueInt IS NOT NULL THEN CAST(@CurrentParameterValueInt AS nvarchar(max)) WHEN @CurrentParameterValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), @CurrentParameterValueDatetime, 21) + '''' ELSE 'NULL' END + @CurrentParameterDelimiter + + RAISERROR('%s',10,1,@CurrentParameterMessage) WITH NOWAIT + + FETCH NEXT FROM ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + END + + CLOSE ParameterCursor + + DEALLOCATE ParameterCursor RAISERROR(@EmptyLine,10,1) WITH NOWAIT @@ -7003,58 +7666,52 @@ BEGIN --// 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 + VALUES('ANSI_NULLS has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 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 + VALUES('QUOTED_IDENTIFIER has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 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 + VALUES('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 + VALUES('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 + VALUES('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 + VALUES('The table Queue is missing. It is required when @DatabasesInParallel = ''Y''. 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 + VALUES('The table QueueDatabase is missing. It is required when @DatabasesInParallel = ''Y''. 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 + VALUES('The stored procedure cannot be executed inside a transaction. The transaction count (@@TRANCOUNT) has to be 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -7116,11 +7773,10 @@ BEGIN FROM Databases4 OPTION (MAXRECURSION 0) - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN - INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName, Selected) - SELECT name AS AvailabilityGroupName, - 0 AS Selected + INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName) + SELECT name AS AvailabilityGroupName FROM sys.availability_groups INSERT INTO @tmpDatabasesAvailabilityGroups (DatabaseName, AvailabilityGroupName) @@ -7131,13 +7787,10 @@ BEGIN 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) + INSERT INTO @tmpDatabases (DatabaseName, DatabaseType, AvailabilityGroup) 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 + NULL AS AvailabilityGroup FROM sys.databases WHERE [name] <> 'tempdb' AND source_database_id IS NULL @@ -7151,7 +7804,7 @@ BEGIN SET tmpDatabases.Selected = SelectedDatabases.Selected FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -7160,7 +7813,7 @@ BEGIN SET tmpDatabases.Selected = SelectedDatabases.Selected FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -7171,7 +7824,7 @@ BEGIN INNER JOIN (SELECT tmpDatabases.DatabaseName, MIN(SelectedDatabases.StartPosition) AS StartPosition FROM @tmpDatabases tmpDatabases INNER JOIN @SelectedDatabases SelectedDatabases - ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]') + ON tmpDatabases.DatabaseName LIKE REPLACE(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 @@ -7181,14 +7834,14 @@ BEGIN 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 + VALUES('The value for the parameter @Databases is not supported. The value could not be parsed into a list of databases. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Databases.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Select availability groups //-- ---------------------------------------------------------------------------------------------------- - IF @AvailabilityGroups IS NOT NULL AND @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 1 BEGIN SET @AvailabilityGroups = REPLACE(@AvailabilityGroups, CHAR(10), '') @@ -7242,14 +7895,14 @@ BEGIN SET tmpAvailabilityGroups.Selected = SelectedAvailabilityGroups.Selected FROM @tmpAvailabilityGroups tmpAvailabilityGroups INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups - ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(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,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'[','[[]'),'_','[_]') WHERE SelectedAvailabilityGroups.Selected = 0 UPDATE tmpAvailabilityGroups @@ -7258,7 +7911,7 @@ BEGIN INNER JOIN (SELECT tmpAvailabilityGroups.AvailabilityGroupName, MIN(SelectedAvailabilityGroups.StartPosition) AS StartPosition FROM @tmpAvailabilityGroups tmpAvailabilityGroups INNER JOIN @SelectedAvailabilityGroups SelectedAvailabilityGroups - ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'_','[_]') + ON tmpAvailabilityGroups.AvailabilityGroupName LIKE REPLACE(REPLACE(SelectedAvailabilityGroups.AvailabilityGroupName,'[','[[]'),'_','[_]') WHERE SelectedAvailabilityGroups.Selected = 1 GROUP BY tmpAvailabilityGroups.AvailabilityGroupName) SelectedAvailabilityGroups2 ON tmpAvailabilityGroups.AvailabilityGroupName = SelectedAvailabilityGroups2.AvailabilityGroupName @@ -7273,22 +7926,28 @@ BEGIN 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) + IF @AvailabilityGroups IS NOT NULL AND (NOT EXISTS(SELECT * FROM @SelectedAvailabilityGroups) OR EXISTS(SELECT * FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName IS NULL OR AvailabilityGroupName = '')) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @AvailabilityGroups is not supported.', 16, 1 + VALUES('The value for the parameter @AvailabilityGroups is not supported. The value could not be parsed into a list of availability groups. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#AvailabilityGroups.', 16, 1) + END + + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 0 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @AvailabilityGroups can only be used when availability groups are enabled on the instance. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#AvailabilityGroups.', 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 + VALUES('You need to specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) 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 + VALUES('You can only specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -7345,6 +8004,10 @@ BEGIN FROM Indexes4 OPTION (MAXRECURSION 0) + INSERT INTO #SelectedIndexes (DatabaseName, SchemaName, ObjectName, IndexName, StartPosition, Selected) + SELECT DatabaseName, SchemaName, ObjectName, IndexName, StartPosition, Selected + FROM @SelectedIndexes + ---------------------------------------------------------------------------------------------------- --// Select actions //-- ---------------------------------------------------------------------------------------------------- @@ -7422,13 +8085,13 @@ BEGIN 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 + VALUES('The value for the parameter @FragmentationLow is not supported. Supported values are INDEX_REBUILD_ONLINE, INDEX_REBUILD_OFFLINE and INDEX_REORGANIZE. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLow.', 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 + VALUES('The value for the parameter @FragmentationLow is not supported. The same action has been specified more than once. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLow.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -7436,13 +8099,13 @@ BEGIN 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 + VALUES('The value for the parameter @FragmentationMedium is not supported. Supported values are INDEX_REBUILD_ONLINE, INDEX_REBUILD_OFFLINE and INDEX_REORGANIZE. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationMedium.', 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 + VALUES('The value for the parameter @FragmentationMedium is not supported. The same action has been specified more than once. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationMedium.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -7450,13 +8113,13 @@ BEGIN 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 + VALUES('The value for the parameter @FragmentationHigh is not supported. Supported values are INDEX_REBUILD_ONLINE, INDEX_REBUILD_OFFLINE and INDEX_REORGANIZE. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationHigh.', 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 + VALUES('The value for the parameter @FragmentationHigh is not supported. The same action has been specified more than once. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationHigh.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -7464,27 +8127,24 @@ BEGIN 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 + VALUES('The value for the parameter @FragmentationLevel1 is not supported. The value has to be greater than 0 and less than 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLevel1.', 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 + VALUES('The value for the parameter @FragmentationLevel2 is not supported. The value has to be greater than 0 and less than 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLevel2.', 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 + VALUES('The value for the parameter @FragmentationLevel2 has to be greater than the value for @FragmentationLevel1. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLevel2.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -7492,7 +8152,7 @@ BEGIN 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 + VALUES('The value for the parameter @MinNumberOfPages is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MinNumberOfPages.', 16, 1) END ---------------------------------------------------------------------------------------------------- @@ -7500,294 +8160,317 @@ BEGIN IF @MaxNumberOfPages < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MaxNumberOfPages is not supported.', 16, 1 + VALUES('The value for the parameter @MaxNumberOfPages is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MaxNumberOfPages.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @SortInTempdb NOT IN('Y','N') OR @SortInTempdb IS NULL + IF @MinNumberOfPages > @MaxNumberOfPages BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @SortInTempdb is not supported.', 16, 1 + VALUES('The value for the parameter @MaxNumberOfPages has to be greater than or equal to the value for @MinNumberOfPages. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MaxNumberOfPages.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @MaxDOP < 0 OR @MaxDOP > 64 + IF @SortInTempdb NOT IN('Y','N') OR @SortInTempdb IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MaxDOP is not supported.', 16, 1 + VALUES('The value for the parameter @SortInTempdb is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#SortInTempdb.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @FillFactor <= 0 OR @FillFactor > 100 + IF @MaxDOP < 0 OR @MaxDOP > 64 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @FillFactor is not supported.', 16, 1 + VALUES('The value for the parameter @MaxDOP is not supported. The value has to be between 0 and 64. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MaxDOP.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @PadIndex NOT IN('Y','N') + IF @FillFactor <= 0 OR @FillFactor > 100 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @PadIndex is not supported.', 16, 1 + VALUES('The value for the parameter @FillFactor is not supported. The value has to be between 1 and 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FillFactor.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @LOBCompaction NOT IN('Y','N') OR @LOBCompaction IS NULL + IF @PadIndex NOT IN('Y','N') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @LOBCompaction is not supported.', 16, 1 + VALUES('The value for the parameter @PadIndex is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#PadIndex.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @UpdateStatistics NOT IN('ALL','COLUMNS','INDEX') + IF @DataCompression NOT IN('NONE', 'PAGE', 'ROW') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @UpdateStatistics is not supported.', 16, 1 + VALUES('The value for the parameter @DataCompression is not supported. Supported values are NONE, PAGE and ROW. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DataCompression.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @OnlyModifiedStatistics NOT IN('Y','N') OR @OnlyModifiedStatistics IS NULL + IF @WaitAtLowPriorityMaxDuration < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @OnlyModifiedStatistics is not supported.', 16, 1 + VALUES('The value for the parameter @WaitAtLowPriorityMaxDuration is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#WaitAtLowPriorityMaxDuration.', 16, 1) END - ---------------------------------------------------------------------------------------------------- - - IF @StatisticsModificationLevel <= 0 OR @StatisticsModificationLevel > 100 + IF @WaitAtLowPriorityAbortAfterWait = 'SELF' AND @WaitAtLowPriorityMaxDuration = 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @StatisticsModificationLevel is not supported.', 16, 1 + VALUES('The value for the parameter @WaitAtLowPriorityMaxDuration is not supported. The value has to be greater than 0 when @WaitAtLowPriorityAbortAfterWait = ''SELF''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#WaitAtLowPriorityMaxDuration.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @OnlyModifiedStatistics = 'Y' AND @StatisticsModificationLevel IS NOT NULL + IF @WaitAtLowPriorityAbortAfterWait NOT IN('NONE','SELF','BLOCKERS') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'You can only specify one of the parameters @OnlyModifiedStatistics and @StatisticsModificationLevel.', 16, 1 + VALUES('The value for the parameter @WaitAtLowPriorityAbortAfterWait is not supported. Supported values are NONE, SELF and BLOCKERS. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#WaitAtLowPriorityAbortAfterWait.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @StatisticsSample <= 0 OR @StatisticsSample > 100 + 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 value for the parameter @StatisticsSample is not supported.', 16, 1 + VALUES('The parameters @WaitAtLowPriorityMaxDuration and @WaitAtLowPriorityAbortAfterWait can only be used together. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @StatisticsResample NOT IN('Y','N') OR @StatisticsResample IS NULL + IF @Resumable NOT IN('Y','N') OR @Resumable IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @StatisticsResample is not supported.', 16, 1 + VALUES('The value for the parameter @Resumable is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Resumable.', 16, 1) END - IF @StatisticsResample = 'Y' AND @StatisticsSample IS NOT NULL + IF @Resumable = 'Y' AND @SortInTempdb = 'Y' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @StatisticsResample is not supported.', 16, 2 + VALUES('You can only specify one of the parameters @Resumable and @SortInTempdb. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @PartitionLevel NOT IN('Y','N') OR @PartitionLevel IS NULL + IF @LOBCompaction NOT IN('Y','N') OR @LOBCompaction IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @PartitionLevel is not supported.', 16, 1 + VALUES('The value for the parameter @LOBCompaction is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#LOBCompaction.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @MSShippedObjects NOT IN('Y','N') OR @MSShippedObjects IS NULL + IF @UpdateStatistics NOT IN('ALL','COLUMNS','INDEX') BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @MSShippedObjects is not supported.', 16, 1 + VALUES('The value for the parameter @UpdateStatistics is not supported. Supported values are ALL, COLUMNS and INDEX. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#UpdateStatistics.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF EXISTS(SELECT * FROM @SelectedIndexes WHERE DatabaseName IS NULL OR SchemaName IS NULL OR ObjectName IS NULL OR IndexName IS NULL) + IF @OnlyModifiedStatistics NOT IN('Y','N') OR @OnlyModifiedStatistics IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Indexes is not supported.', 16, 1 + VALUES('The value for the parameter @OnlyModifiedStatistics is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#OnlyModifiedStatistics.', 16, 1) END - IF @Indexes IS NOT NULL AND NOT EXISTS(SELECT * FROM @SelectedIndexes) + ---------------------------------------------------------------------------------------------------- + + IF @StatisticsModificationLevel <= 0 OR @StatisticsModificationLevel > 100 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Indexes is not supported.', 16, 2 + VALUES('The value for the parameter @StatisticsModificationLevel is not supported. The value has to be greater than 0 and less than or equal to 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsModificationLevel.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @TimeLimit < 0 + IF @OnlyModifiedStatistics = 'Y' AND @StatisticsModificationLevel IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @TimeLimit is not supported.', 16, 1 + VALUES('You can only specify one of the parameters @OnlyModifiedStatistics and @StatisticsModificationLevel. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @Delay < 0 + IF @StatisticsSample <= 0 OR @StatisticsSample > 100 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Delay is not supported.', 16, 1 + VALUES('The value for the parameter @StatisticsSample is not supported. The value has to be between 1 and 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsSample.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @WaitAtLowPriorityMaxDuration < 0 + IF @StatisticsPersistSample NOT IN('Y','N') + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @StatisticsPersistSample is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsPersistSample.', 16, 1) + END + + IF @StatisticsPersistSample IS NOT NULL AND @StatisticsSample IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @StatisticsPersistSample can only be used together with @StatisticsSample. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsPersistSample.', 16, 1) + END + + IF @StatisticsPersistSample IS NOT NULL AND @StatisticsResample = 'Y' BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @WaitAtLowPriorityMaxDuration is not supported.', 16, 1 + VALUES('The parameters @StatisticsPersistSample and @StatisticsResample cannot be used together. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) END - IF @WaitAtLowPriorityMaxDuration IS NOT NULL AND @Version < 12 + IF @StatisticsPersistSample IS NOT NULL AND NOT (@Version >= 14.03006 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @WaitAtLowPriorityMaxDuration is not supported.', 16, 2 + VALUES('The value for the parameter @StatisticsPersistSample is not supported. PERSIST_SAMPLE_PERCENT is not supported in this version of SQL Server. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsPersistSample.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @WaitAtLowPriorityAbortAfterWait NOT IN('NONE','SELF','BLOCKERS') + IF @StatisticsResample NOT IN('Y','N') OR @StatisticsResample IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @WaitAtLowPriorityAbortAfterWait is not supported.', 16, 1 + VALUES('The value for the parameter @StatisticsResample is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsResample.', 16, 1) END - IF @WaitAtLowPriorityAbortAfterWait IS NOT NULL AND @Version < 12 + IF @StatisticsResample = 'Y' AND @StatisticsSample IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @WaitAtLowPriorityAbortAfterWait is not supported.', 16, 2 + VALUES('Setting @StatisticsResample to ''Y'' cannot be combined with @StatisticsSample. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsResample.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF (@WaitAtLowPriorityAbortAfterWait IS NOT NULL AND @WaitAtLowPriorityMaxDuration IS NULL) OR (@WaitAtLowPriorityAbortAfterWait IS NULL AND @WaitAtLowPriorityMaxDuration IS NOT NULL) + IF @PartitionLevel NOT IN('Y','N') OR @PartitionLevel IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The parameters @WaitAtLowPriorityMaxDuration and @WaitAtLowPriorityAbortAfterWait can only be used together.', 16, 1 + VALUES('The value for the parameter @PartitionLevel is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#PartitionLevel.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @Resumable NOT IN('Y','N') OR @Resumable IS NULL + IF @MSShippedObjects NOT IN('Y','N') OR @MSShippedObjects IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Resumable is not supported.', 16, 1 + VALUES('The value for the parameter @MSShippedObjects is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MSShippedObjects.', 16, 1) END - IF @Resumable = 'Y' AND NOT (@Version >= 14 OR SERVERPROPERTY('EngineEdition') IN (5, 8)) + ---------------------------------------------------------------------------------------------------- + + 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 @Resumable is not supported.', 16, 2 + VALUES('The value for the parameter @Indexes is not supported. The value contains one or more items that could not be parsed. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Indexes.', 16, 1) END - IF @Resumable = 'Y' AND @SortInTempdb = 'Y' + IF @Indexes IS NOT NULL AND NOT EXISTS(SELECT * FROM @SelectedIndexes) BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'You can only specify one of the parameters @Resumable and @SortInTempdb.', 16, 3 + VALUES('The value for the parameter @Indexes is not supported. The value could not be parsed into a list of indexes. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Indexes.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @LockTimeout < 0 + IF @TimeLimit < 0 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @LockTimeout is not supported.', 16, 1 + VALUES('The value for the parameter @TimeLimit is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#TimeLimit.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @LockMessageSeverity NOT IN(10, 16) + IF @Delay < 0 OR @Delay >= 86400 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @LockMessageSeverity is not supported.', 16, 1 + VALUES('The value for the parameter @Delay is not supported. The value has to be greater than or equal to 0 and less than 86400. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Delay.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) > 1 + IF @LockTimeout < 0 OR @LockTimeout > 86400 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @StringDelimiter is not supported.', 16, 1 + VALUES('The value for the parameter @LockTimeout is not supported. The value has to be between 0 and 86400. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#LockTimeout.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @DatabaseOrder NOT IN('DATABASE_NAME_ASC','DATABASE_NAME_DESC','DATABASE_SIZE_ASC','DATABASE_SIZE_DESC') + IF @LockMessageSeverity NOT IN(10, 16) OR @LockMessageSeverity IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DatabaseOrder is not supported.', 16, 1 + VALUES('The value for the parameter @LockMessageSeverity is not supported. Supported values are 10 and 16. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#LockMessageSeverity.', 16, 1) END - IF @DatabaseOrder IS NOT NULL AND SERVERPROPERTY('EngineEdition') = 5 + ---------------------------------------------------------------------------------------------------- + + IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) <> 1 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DatabaseOrder is not supported.', 16, 2 + VALUES('The value for the parameter @StringDelimiter is not supported. The value has to be exactly one character. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StringDelimiter.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @DatabasesInParallel NOT IN('Y','N') OR @DatabasesInParallel IS NULL + 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 @DatabasesInParallel is not supported.', 16, 1 + VALUES('The value for the parameter @DatabaseOrder is not supported. Supported values are DATABASE_NAME_ASC, DATABASE_NAME_DESC, DATABASE_SIZE_ASC and DATABASE_SIZE_DESC. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DatabaseOrder.', 16, 1) END - IF @DatabasesInParallel = 'Y' AND SERVERPROPERTY('EngineEdition') = 5 + IF @DatabaseOrder IS NOT NULL AND @EngineEdition = 5 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @DatabasesInParallel is not supported.', 16, 2 + VALUES('The parameter @DatabaseOrder is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DatabaseOrder.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF LEN(@ExecuteAsUser) > 128 + IF @DatabasesInParallel NOT IN('Y','N') OR @DatabasesInParallel IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DatabasesInParallel is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DatabasesInParallel.', 16, 1) + END + + IF @DatabasesInParallel = 'Y' AND @EngineEdition = 5 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @ExecuteAsUser is not supported.', 16, 1 + VALUES('The parameter @DatabasesInParallel is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DatabasesInParallel.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @LogToTable NOT IN('Y','N') OR @LogToTable IS NULL + IF LEN(@ExecuteAsUser) > 128 BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @LogToTable is not supported.', 16, 1 + VALUES('The value for the parameter @ExecuteAsUser is not supported. The maximum length is 128 characters. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#ExecuteAsUser.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF @Execute NOT IN('Y','N') OR @Execute IS NULL + IF @LogToTable NOT IN('Y','N') OR @LogToTable IS NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The value for the parameter @Execute is not supported.', 16, 1 + VALUES('The value for the parameter @LogToTable is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#LogToTable.', 16, 1) END ---------------------------------------------------------------------------------------------------- - IF EXISTS(SELECT * FROM @Errors) + IF @Execute NOT IN('Y','N') OR @Execute IS NULL 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 + VALUES('The value for the parameter @Execute is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Execute.', 16, 1) END ---------------------------------------------------------------------------------------------------- --// Check that selected databases and availability groups exist //-- ---------------------------------------------------------------------------------------------------- - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedDatabases WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) @@ -7795,10 +8478,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Databases.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedIndexes WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) @@ -7806,10 +8490,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following databases in the @Indexes parameter do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases in the @Indexes parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Indexes.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(AvailabilityGroupName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(AvailabilityGroupName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY AvailabilityGroupName ASC) FROM @SelectedAvailabilityGroups WHERE AvailabilityGroupName NOT LIKE '%[%]%' AND AvailabilityGroupName NOT IN (SELECT AvailabilityGroupName FROM @tmpAvailabilityGroups) @@ -7817,10 +8502,11 @@ BEGIN IF @ErrorMessage IS NOT NULL BEGIN INSERT INTO @Errors ([Message], Severity, [State]) - SELECT 'The following availability groups do not exist: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following availability groups do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#AvailabilityGroups.', 10, 1) END - SELECT @ErrorMessage = STRING_AGG(QUOTENAME(DatabaseName), ', ') + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) FROM @SelectedIndexes WHERE DatabaseName NOT LIKE '%[%]%' AND DatabaseName IN (SELECT DatabaseName FROM @tmpDatabases) @@ -7829,14 +8515,14 @@ BEGIN IF @ErrorMessage IS NOT NULL 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: ' + @ErrorMessage + '.', 10, 1 + VALUES('The following databases have been selected in the @Indexes parameter, but not in the @Databases or @AvailabilityGroups parameters: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Indexes.', 10, 1) END ---------------------------------------------------------------------------------------------------- --// Raise errors //-- ---------------------------------------------------------------------------------------------------- - DECLARE ErrorCursor CURSOR FAST_FORWARD FOR SELECT [Message], Severity, [State] FROM @Errors ORDER BY [ID] ASC + DECLARE ErrorCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Message], Severity, [State] FROM @Errors ORDER BY [ID] ASC OPEN ErrorCursor @@ -7860,12 +8546,6 @@ BEGIN 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 //-- ---------------------------------------------------------------------------------------------------- @@ -7945,7 +8625,7 @@ BEGIN FROM dbo.[Queue] WHERE SchemaName = @SchemaName AND ObjectName = @ObjectName - AND [Parameters] = @Parameters + AND [Parameters] = @ParametersString IF @QueueID IS NULL BEGIN @@ -7955,12 +8635,12 @@ BEGIN FROM dbo.[Queue] WITH (UPDLOCK, HOLDLOCK) WHERE SchemaName = @SchemaName AND ObjectName = @ObjectName - AND [Parameters] = @Parameters + AND [Parameters] = @ParametersString IF @QueueID IS NULL BEGIN INSERT INTO dbo.[Queue] (SchemaName, ObjectName, [Parameters]) - SELECT @SchemaName, @ObjectName, @Parameters + VALUES(@SchemaName, @ObjectName, @ParametersString) SET @QueueID = SCOPE_IDENTITY() END @@ -7994,17 +8674,17 @@ BEGIN DatabaseName FROM @tmpDatabases tmpDatabases WHERE Selected = 1 - AND NOT EXISTS (SELECT * FROM dbo.QueueDatabase WHERE DatabaseName = tmpDatabases.DatabaseName AND QueueID = @QueueID) + AND NOT EXISTS (SELECT * FROM dbo.QueueDatabase WHERE DatabaseName COLLATE DATABASE_DEFAULT = 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) + AND NOT EXISTS (SELECT * FROM @tmpDatabases tmpDatabases WHERE DatabaseName = QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT AND Selected = 1) UPDATE QueueDatabase SET DatabaseOrder = tmpDatabases.[Order] FROM dbo.QueueDatabase QueueDatabase - INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName = tmpDatabases.DatabaseName + INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT = tmpDatabases.DatabaseName WHERE QueueID = @QueueID END @@ -8021,7 +8701,7 @@ BEGIN BEGIN ROLLBACK TRANSACTION END - SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar) + ', ' + ISNULL(ERROR_MESSAGE(),'') + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + ISNULL(ERROR_MESSAGE(),'') RAISERROR('%s',16,1,@ErrorMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT SET @ReturnCode = ERROR_NUMBER() @@ -8035,7 +8715,7 @@ BEGIN ---------------------------------------------------------------------------------------------------- WHILE (1 = 1) - BEGIN + BEGIN -- Start of database loop IF @DatabasesInParallel = 'Y' BEGIN @@ -8088,7 +8768,7 @@ BEGIN SET @CurrentDatabase_sp_executesql = QUOTENAME(@CurrentDatabaseName) + '.sys.sp_executesql' BEGIN - SET @DatabaseMessage = 'Date and time: ' + CONVERT(nvarchar,SYSDATETIME(),120) + SET @DatabaseMessage = 'Date and time: ' + CONVERT(nvarchar(max),SYSDATETIME(),120) RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT SET @DatabaseMessage = 'Database: ' + QUOTENAME(@CurrentDatabaseName) @@ -8120,27 +8800,67 @@ BEGIN RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT END - IF @Version >= 11 AND SERVERPROPERTY('IsHadrEnabled') = 1 + IF @IsHadrEnabled = 1 BEGIN - SELECT @CurrentReplicaID = databases.replica_id + SELECT @CurrentAvailabilityGroupReplicaID = 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 + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + + IF @CurrentAvailabilityGroupReplicaID IS NULL AND @CurrentAvailabilityGroupID IS NULL AND @Version >= 16 + BEGIN + SET @CurrentAvailabilityGroupID = @ContainedAvailabilityGroupID + + SELECT @CurrentAvailabilityGroupReplicaID = replica_id + FROM sys.dm_hadr_availability_replica_states + WHERE group_id = @CurrentAvailabilityGroupID + AND is_local = 1 + END SELECT @CurrentAvailabilityGroupRole = role_desc FROM sys.dm_hadr_availability_replica_states - WHERE replica_id = @CurrentReplicaID + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND database_id = DB_ID(@CurrentDatabaseName) + + IF @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState IS NULL AND @ContainedAvailabilityGroupListenerConnection = 1 + BEGIN + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND DB_NAME(database_id) = @CurrentDatabaseName + END SELECT @CurrentAvailabilityGroup = [name] FROM sys.availability_groups WHERE group_id = @CurrentAvailabilityGroupID END - IF SERVERPROPERTY('EngineEdition') <> 5 + IF @IsHadrEnabled = 1 AND @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SELECT @CurrentDistributedAvailabilityGroup = availability_groups.[name], + @CurrentDistributedAvailabilityGroupReplicaID = availability_replicas.replica_id + FROM sys.availability_groups availability_groups + INNER JOIN sys.availability_replicas availability_replicas ON availability_groups.group_id = availability_replicas.group_id + INNER JOIN sys.availability_groups availability_groups_local ON availability_replicas.replica_server_name = availability_groups_local.[name] + WHERE availability_groups.is_distributed = 1 + AND availability_groups_local.group_id = @CurrentAvailabilityGroupID + + SELECT @CurrentDistributedAvailabilityGroupRole = dm_hadr_availability_replica_states.role_desc + FROM sys.dm_hadr_availability_replica_states dm_hadr_availability_replica_states + WHERE dm_hadr_availability_replica_states.replica_id = @CurrentDistributedAvailabilityGroupReplicaID + END + + IF @EngineEdition <> 5 BEGIN SELECT @CurrentDatabaseMirroringRole = UPPER(mirroring_role_desc) FROM sys.database_mirroring database_mirroring @@ -8155,6 +8875,27 @@ BEGIN 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 + END + + IF @CurrentDistributedAvailabilityGroup IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Distributed availability group: ' + ISNULL(@CurrentDistributedAvailabilityGroup,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Distributed availability group role: ' + ISNULL(@CurrentDistributedAvailabilityGroupRole,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Replica role in distributed availability group: ' + CASE WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Global primary' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Forwarder' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in secondary availability group' + WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in primary availability group' ELSE 'N/A' END + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT END IF @CurrentDatabaseMirroringRole IS NOT NULL @@ -8181,273 +8922,491 @@ BEGIN BEGIN SET @DatabaseMessage = 'The user ' + QUOTENAME(@ExecuteAsUser) + ' does not exist in the database ' + QUOTENAME(@CurrentDatabaseName) + '.' RAISERROR('%s',16,1,@DatabaseMessage) WITH NOWAIT + SET @Error = @@ERROR + SET @ReturnCode = @Error 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 (@CurrentDistributedAvailabilityGroup IS NOT NULL AND (@CurrentDistributedAvailabilityGroupRole <> 'PRIMARY' OR @CurrentDistributedAvailabilityGroupRole 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 + -- Select objects + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT objects.[object_id] AS ObjectID' + + ', objects.[schema_id] AS SchemaID' + + ', schemas.[name] AS SchemaName' + + ', objects.[name] AS ObjectName' + + ', RTRIM(objects.[type]) AS ObjectType' + + ', ISNULL(tables.is_memory_optimized, 0) AS IsMemoryOptimized' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') THEN 'CASE WHEN EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = objects.object_id AND [type] = 5) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS HasClusteredColumnstore' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') AND @Resumable = 'Y' THEN '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 = objects.object_id) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsClusteredIndexComputed' + + ', CASE WHEN EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = objects.object_id AND [type] = 1 AND is_disabled = 1) THEN 1 ELSE 0 END AS IsClusteredIndexDisabled' + + ' 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 (tables.is_external = 0 OR tables.is_external IS NULL)' + + CASE WHEN @MSShippedObjects = 'N' THEN ' AND objects.is_ms_shipped = 0' ELSE '' END + IF @Indexes IS NOT NULL AND EXISTS(SELECT * FROM @SelectedIndexes WHERE Selected = 1) AND NOT EXISTS(SELECT * FROM @SelectedIndexes WHERE Selected = 1 AND DatabaseName = '%' AND SchemaName = '%' AND ObjectName = '%') + BEGIN + SET @CurrentCommand += ' AND EXISTS(SELECT * FROM #SelectedIndexes SelectedIndexes WHERE @ParamDatabaseName LIKE REPLACE(REPLACE(SelectedIndexes.DatabaseName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND schemas.[name] LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND objects.[name] LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND SelectedIndexes.Selected = 1)' + END + IF @Indexes IS NOT NULL AND EXISTS(SELECT * FROM @SelectedIndexes WHERE Selected = 0 AND IndexName = '%') + BEGIN + SET @CurrentCommand += ' AND NOT EXISTS(SELECT * FROM #SelectedIndexes SelectedIndexes WHERE @ParamDatabaseName LIKE REPLACE(REPLACE(SelectedIndexes.DatabaseName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND schemas.[name] LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND objects.[name] LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND SelectedIndexes.IndexName = ''%'' AND SelectedIndexes.Selected = 0)' + END + + INSERT INTO #Objects (ObjectID, SchemaID, SchemaName, ObjectName, ObjectType, IsMemoryOptimized, HasClusteredColumnstore, IsClusteredIndexComputed, IsClusteredIndexDisabled) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseName nvarchar(max)', @ParamDatabaseName = @CurrentDatabaseName + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select indexes + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT indexes.[object_id] AS ObjectID' + + ', indexes.index_id AS IndexID' + + ', indexes.[name] AS IndexName' + + ', indexes.[type] AS IndexType' + + ', indexes.data_space_id AS DataSpaceID' + + ', CASE WHEN EXISTS (SELECT * FROM sys.partition_schemes partition_schemes WHERE partition_schemes.data_space_id = indexes.data_space_id) THEN 1 ELSE 0 END AS IsPartitioned' + + ', indexes.allow_page_locks AS AllowPageLocks' + + ', indexes.has_filter AS HasFilter' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') THEN '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] = indexes.object_id AND types.name IN(''image'',''text'',''ntext'')) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsImageText' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') THEN 'CASE WHEN indexes.[type] = 1 AND EXISTS(SELECT * FROM sys.columns columns WHERE columns.[object_id] = indexes.object_id AND columns.is_filestream = 1) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsFileStream' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') AND (@Version >= 16 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) THEN 'CASE WHEN EXISTS(SELECT * FROM sys.index_columns index_columns WHERE index_columns.[object_id] = indexes.[object_id] AND index_columns.index_id = indexes.index_id AND index_columns.column_store_order_ordinal = 1) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsColumnstoreOrdered' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') AND @Resumable = 'Y' THEN '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 OR index_columns.is_included_column = 1) 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' ELSE 'NULL' END + ' AS IsComputed' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') AND @Resumable = 'Y' THEN '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.key_ordinal > 0 OR index_columns.partition_ordinal > 0) AND index_columns.[object_id] = indexes.[object_id] AND index_columns.index_id = indexes.index_id AND types.[name] = ''timestamp'') THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsTimestamp' + + ' FROM sys.indexes indexes' + + ' INNER JOIN #Objects Objects ON indexes.[object_id] = Objects.ObjectID' + + ' AND indexes.[type] IN(1,2,3,4,5,6,7)' + + ' AND indexes.is_disabled = 0' + + ' AND indexes.is_hypothetical = 0' + + INSERT INTO #Indexes (ObjectID, IndexID, IndexName, IndexType, DataSpaceID, IsPartitioned, AllowPageLocks, HasFilter, IsImageText, IsFileStream, IsColumnstoreOrdered, IsComputed, IsTimestamp) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select statistics + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT stats.[object_id] AS ObjectID' + + ', stats.stats_id AS StatisticsID' + + ', stats.name AS StatisticsName' + + ', stats.no_recompute AS NoRecompute' + + ', stats.is_incremental AS IsIncremental' + + ', CASE WHEN EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = stats.[object_id] AND indexes.index_id = stats.stats_id) THEN 1 ELSE 0 END AS IsIndex' + + ' FROM sys.stats stats' + + ' INNER JOIN #Objects Objects ON stats.[object_id] = Objects.ObjectID' + + INSERT INTO #Stats (ObjectID, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, IsIndex) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select paused resumable index operations 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 (' + + ' SELECT index_resumable_operations.object_id AS ObjectID' + + ', index_resumable_operations.index_id AS IndexID' + + ', ' + CASE WHEN @PartitionLevel = 'Y' THEN 'index_resumable_operations.partition_number AS PartitionNumber' WHEN @PartitionLevel = 'N' THEN 'NULL AS PartitionNumber' END + + ' FROM sys.index_resumable_operations index_resumable_operations' + + ' WHERE index_resumable_operations.state_desc = ''PAUSED''' + + INSERT INTO @tmpResumableOperations (ObjectID, IndexID, PartitionNumber) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END 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 'ISNULL(tables.is_memory_optimized, 0)' 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 OR index_columns.is_included_column = 1) 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.key_ordinal > 0 OR index_columns.partition_ordinal > 0) AND 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]' + -- Check if there are read-only filegroups in the database + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT @ParamDatabaseHasReadOnlyFileGroup = CASE WHEN EXISTS(SELECT * FROM sys.filegroups filegroups WHERE filegroups.is_read_only = 1) THEN 1 ELSE 0 END' + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseHasReadOnlyFileGroup bit OUTPUT', @ParamDatabaseHasReadOnlyFileGroup = @CurrentDatabaseHasReadOnlyFileGroup OUTPUT + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select clustered, nonclustered and hash indexes + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Indexes.IndexID AS IndexID' + + ', Indexes.IndexName AS IndexName' + + ', Indexes.IndexType AS IndexType' + + ', Indexes.AllowPageLocks AS AllowPageLocks' + + ', Indexes.HasFilter AS HasFilter' + + ', Indexes.IsImageText AS IsImageText' + + ', Indexes.IsFileStream AS IsFileStream' + + ', Objects.HasClusteredColumnstore AS HasClusteredColumnstore' + + ', Indexes.IsColumnstoreOrdered AS IsColumnstoreOrdered' + + ', Indexes.IsComputed AS IsComputed' + + ', Objects.IsClusteredIndexComputed AS IsClusteredIndexComputed' + + ', Indexes.IsTimestamp AS IsTimestamp' + + CASE WHEN @CurrentDatabaseHasReadOnlyFileGroup = 1 THEN ', CASE WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.destination_data_spaces destination_data_spaces ON Indexes.DataSpaceID = 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.ObjectID AND indexes2.[index_id] = Indexes.IndexID' + 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.DataSpaceID = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND Indexes.ObjectID = indexes2.[object_id] AND Indexes.IndexID = indexes2.index_id) THEN 1' + + ' WHEN Indexes.IndexType = 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.ObjectID) THEN 1 ELSE 0 END AS OnReadOnlyFileGroup' ELSE ', 0 AS OnReadOnlyFileGroup' END + + ', 0 AS ResumableIndexOperation' + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ', Stats.StatisticsID AS StatisticsID' ELSE ', NULL AS StatisticsID' END + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ', Stats.StatisticsName AS StatisticsName' ELSE ', NULL AS StatisticsName' END + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ', Stats.[NoRecompute] AS NoRecompute' ELSE ', NULL AS NoRecompute' END + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ', Stats.IsIncremental AS IsIncremental' ELSE ', NULL AS IsIncremental' END + + ', ' + 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 'Indexes.IsPartitioned AS IsPartition' WHEN @PartitionLevel = 'N' THEN '0 AS IsPartition' END + + ', ' + CASE WHEN (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) THEN 'dm_db_partition_stats.in_row_data_page_count AS InRowDataPageCount' ELSE 'NULL AS InRowDataPageCount' END + + ' FROM #Indexes Indexes' + + ' INNER JOIN #Objects Objects ON Indexes.ObjectID = Objects.ObjectID' + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ' INNER JOIN #Stats Stats ON Indexes.ObjectID = Stats.ObjectID AND Indexes.IndexID = Stats.StatisticsID' ELSE '' END 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]' + SET @CurrentCommand += ' INNER JOIN sys.partitions partitions ON Indexes.ObjectID = partitions.[object_id] AND Indexes.IndexID = partitions.index_id' + END + IF @PartitionLevel = 'Y' AND (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) + BEGIN + SET @CurrentCommand += ' INNER JOIN sys.dm_db_partition_stats dm_db_partition_stats ON partitions.object_id = dm_db_partition_stats.object_id AND partitions.index_id = dm_db_partition_stats.index_id AND partitions.partition_number = dm_db_partition_stats.partition_number' + END + IF @PartitionLevel = 'N' AND (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) + BEGIN + SET @CurrentCommand += ' INNER JOIN (SELECT object_id, index_id, SUM(in_row_data_page_count) AS in_row_data_page_count FROM sys.dm_db_partition_stats GROUP BY object_id, index_id) dm_db_partition_stats ON Indexes.ObjectID = dm_db_partition_stats.object_id AND Indexes.IndexID = dm_db_partition_stats.index_id' END + SET @CurrentCommand += ' WHERE Objects.ObjectType IN(''U'',''V'')' + + ' AND Indexes.IndexType IN(1,2,7)' + + CASE WHEN (@UpdateStatistics = 'COLUMNS' OR @UpdateStatistics IS NULL) AND @MinNumberOfPages > 0 THEN ' AND dm_db_partition_stats.in_row_data_page_count >= @ParamMinNumberOfPages' ELSE '' END + + CASE WHEN (@UpdateStatistics = 'COLUMNS' OR @UpdateStatistics IS NULL) AND @MaxNumberOfPages IS NOT NULL THEN ' AND dm_db_partition_stats.in_row_data_page_count <= @ParamMaxNumberOfPages' ELSE '' 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 + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, IndexID, IndexName, IndexType, AllowPageLocks, HasFilter, IsImageText, IsFileStream, HasClusteredColumnstore, IsColumnstoreOrdered, IsComputed, IsClusteredIndexComputed, IsTimestamp, OnReadOnlyFileGroup, ResumableIndexOperation, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, PartitionID, PartitionNumber, IsPartition, InRowDataPageCount) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamMinNumberOfPages int, @ParamMaxNumberOfPages int', @ParamMinNumberOfPages = @MinNumberOfPages, @ParamMaxNumberOfPages = @MaxNumberOfPages + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select XML and spatial indexes + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Indexes.IndexID AS IndexID' + + ', Indexes.IndexName AS IndexName' + + ', Indexes.IndexType AS IndexType' + + ', Indexes.AllowPageLocks AS AllowPageLocks' + + ', Indexes.HasFilter AS HasFilter' + + ', Indexes.IsImageText AS IsImageText' + + ', Indexes.IsFileStream AS IsFileStream' + + ', Objects.HasClusteredColumnstore AS HasClusteredColumnstore' + + ', Indexes.IsColumnstoreOrdered AS IsColumnstoreOrdered' + + ', Indexes.IsComputed AS IsComputed' + + ', Objects.IsClusteredIndexComputed AS IsClusteredIndexComputed' + + ', Indexes.IsTimestamp AS IsTimestamp' + + CASE WHEN @CurrentDatabaseHasReadOnlyFileGroup = 1 THEN ', CASE WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.destination_data_spaces destination_data_spaces ON Indexes.DataSpaceID = 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.ObjectID AND indexes2.[index_id] = Indexes.IndexID) THEN 1' + + ' WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.filegroups filegroups ON Indexes.DataSpaceID = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND Indexes.ObjectID = indexes2.[object_id] AND Indexes.IndexID = indexes2.index_id) THEN 1 ELSE 0 END AS OnReadOnlyFileGroup' ELSE ', 0 AS OnReadOnlyFileGroup' END + + ', 0 AS ResumableIndexOperation' + + ', 0 AS IsPartition' + + ' FROM #Indexes Indexes' + + ' INNER JOIN #Objects Objects ON Indexes.ObjectID = Objects.ObjectID' + + ' WHERE Objects.ObjectType = ''U''' + + ' AND Indexes.IndexType IN(3,4)' + + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, IndexID, IndexName, IndexType, AllowPageLocks, HasFilter, IsImageText, IsFileStream, HasClusteredColumnstore, IsColumnstoreOrdered, IsComputed, IsClusteredIndexComputed, IsTimestamp, OnReadOnlyFileGroup, ResumableIndexOperation, IsPartition) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select columnstore indexes + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Indexes.IndexID AS IndexID' + + ', Indexes.IndexName AS IndexName' + + ', Indexes.IndexType AS IndexType' + + ', Indexes.AllowPageLocks AS AllowPageLocks' + + ', Indexes.HasFilter AS HasFilter' + + ', Indexes.IsImageText AS IsImageText' + + ', Indexes.IsFileStream AS IsFileStream' + + ', Objects.HasClusteredColumnstore AS HasClusteredColumnstore' + + ', Indexes.IsColumnstoreOrdered AS IsColumnstoreOrdered' + + ', Indexes.IsComputed AS IsComputed' + + ', Objects.IsClusteredIndexComputed AS IsClusteredIndexComputed' + + ', Indexes.IsTimestamp AS IsTimestamp' + + CASE WHEN @CurrentDatabaseHasReadOnlyFileGroup = 1 THEN ', CASE WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.destination_data_spaces destination_data_spaces ON Indexes.DataSpaceID = 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.ObjectID AND indexes2.[index_id] = Indexes.IndexID' + 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.DataSpaceID = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND Indexes.ObjectID = indexes2.[object_id] AND Indexes.IndexID = indexes2.index_id) THEN 1' + + ' WHEN Indexes.IndexType = 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.ObjectID) THEN 1 ELSE 0 END AS OnReadOnlyFileGroup' ELSE ', 0 AS OnReadOnlyFileGroup' END + + ', 0 AS ResumableIndexOperation' + + ', NULL AS StatisticsID' + + ', NULL AS StatisticsName' + + ', NULL AS NoRecompute' + + ', NULL 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 'Indexes.IsPartitioned AS IsPartition' WHEN @PartitionLevel = 'N' THEN '0 AS IsPartition' END + + ', ' + CASE WHEN (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) THEN 'dm_db_partition_stats.in_row_data_page_count AS InRowDataPageCount' ELSE 'NULL AS InRowDataPageCount' END + + ' FROM #Indexes Indexes' + + ' INNER JOIN #Objects Objects ON Indexes.ObjectID = Objects.ObjectID' + IF @PartitionLevel = 'Y' + BEGIN + SET @CurrentCommand += ' INNER JOIN sys.partitions partitions ON Indexes.ObjectID = partitions.[object_id] AND Indexes.IndexID = partitions.index_id' + END + IF @PartitionLevel = 'Y' AND (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) + BEGIN + SET @CurrentCommand += ' INNER JOIN sys.dm_db_partition_stats dm_db_partition_stats ON partitions.object_id = dm_db_partition_stats.object_id AND partitions.index_id = dm_db_partition_stats.index_id AND partitions.partition_number = dm_db_partition_stats.partition_number' + END + IF @PartitionLevel = 'N' AND (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) + BEGIN + SET @CurrentCommand += ' INNER JOIN (SELECT object_id, index_id, SUM(in_row_data_page_count) AS in_row_data_page_count FROM sys.dm_db_partition_stats GROUP BY object_id, index_id) dm_db_partition_stats ON Indexes.ObjectID = dm_db_partition_stats.object_id AND Indexes.IndexID = dm_db_partition_stats.index_id' + END + SET @CurrentCommand += ' WHERE Objects.ObjectType = ''U''' + + ' AND Indexes.IndexType IN(5,6)' + + CASE WHEN (@UpdateStatistics = 'COLUMNS' OR @UpdateStatistics IS NULL) AND @MinNumberOfPages > 0 THEN ' AND dm_db_partition_stats.in_row_data_page_count >= @ParamMinNumberOfPages' ELSE '' END + + CASE WHEN (@UpdateStatistics = 'COLUMNS' OR @UpdateStatistics IS NULL) AND @MaxNumberOfPages IS NOT NULL THEN ' AND dm_db_partition_stats.in_row_data_page_count <= @ParamMaxNumberOfPages' ELSE '' END + + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, IndexID, IndexName, IndexType, AllowPageLocks, HasFilter, IsImageText, IsFileStream, HasClusteredColumnstore, IsColumnstoreOrdered, IsComputed, IsClusteredIndexComputed, IsTimestamp, OnReadOnlyFileGroup, ResumableIndexOperation, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, PartitionID, PartitionNumber, IsPartition, InRowDataPageCount) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamMinNumberOfPages int, @ParamMaxNumberOfPages int', @ParamMinNumberOfPages = @MinNumberOfPages, @ParamMaxNumberOfPages = @MaxNumberOfPages + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + 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 'ISNULL(tables.is_memory_optimized, 0)' 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 + -- Select non-incremental column level statistics + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Stats.StatisticsID AS StatisticsID' + + ', Stats.StatisticsName AS StatisticsName' + + ', Stats.[NoRecompute] AS NoRecompute' + + ', Stats.IsIncremental AS IsIncremental' + + ', NULL AS PartitionNumber' + + ', 0 AS IsPartition' + + ' FROM #Stats Stats' + + ' INNER JOIN #Objects Objects ON Stats.ObjectID = Objects.ObjectID' + + ' WHERE Stats.IsIndex = 0' + + ' AND Stats.IsIncremental = 0' + + ' AND Objects.IsClusteredIndexDisabled = 0' + + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, PartitionNumber, IsPartition) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 BEGIN - SET @CurrentCommand = @CurrentCommand + ' OUTER APPLY sys.dm_db_incremental_stats_properties(stats.object_id, stats.stats_id) dm_db_incremental_stats_properties' + SET @ReturnCode = @Error 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)' + -- Select incremental column level statistics + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Stats.StatisticsID AS StatisticsID' + + ', Stats.StatisticsName AS StatisticsName' + + ', Stats.[NoRecompute] AS NoRecompute' + + ', Stats.IsIncremental AS IsIncremental' + + ', ' + CASE WHEN @PartitionLevel = 'Y' THEN 'partitions.partition_number' ELSE 'NULL' END + ' AS PartitionNumber' + + ', ' + CASE WHEN @PartitionLevel = 'Y' THEN '1' ELSE '0' END + ' AS IsPartition' + + ' FROM #Stats Stats' + + ' INNER JOIN #Objects Objects ON Stats.ObjectID = Objects.ObjectID' + IF @PartitionLevel = 'Y' + BEGIN + SET @CurrentCommand += ' INNER JOIN sys.partitions partitions ON partitions.[object_id] = Stats.ObjectID AND partitions.index_id IN (0, 1)' + END + SET @CurrentCommand += ' WHERE Objects.IsMemoryOptimized = 0' + + ' AND Stats.IsIndex = 0' + + ' AND Stats.IsIncremental = 1' + + ' AND Objects.IsClusteredIndexDisabled = 0' - IF @Version >= 12 + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, PartitionNumber, IsPartition) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 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)' + SET @ReturnCode = @Error 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 + IF @PartitionLevel = 'Y' BEGIN - SET @ReturnCode = @Error + UPDATE tmpIndexesStatistics + SET tmpIndexesStatistics.IsLastPartition = CASE WHEN tmpIndexesStatistics.PartitionNumber = LastPartitionNumbers.LastPartitionNumber THEN 1 ELSE 0 END + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN (SELECT ObjectID, IndexID, MAX(PartitionNumber) AS LastPartitionNumber FROM @tmpIndexesStatistics WHERE IndexID IS NOT NULL AND PartitionNumber IS NOT NULL GROUP BY ObjectID, IndexID) LastPartitionNumbers ON tmpIndexesStatistics.ObjectID = LastPartitionNumbers.ObjectID AND tmpIndexesStatistics.IndexID = LastPartitionNumbers.IndexID + OPTION (RECOMPILE) 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 + SET tmpIndexesStatistics.ResumableIndexOperation = 1 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 + INNER JOIN @tmpResumableOperations tmpResumableOperations ON tmpIndexesStatistics.ObjectID = tmpResumableOperations.ObjectID AND tmpIndexesStatistics.IndexID = tmpResumableOperations.IndexID AND (tmpIndexesStatistics.PartitionNumber = tmpResumableOperations.PartitionNumber OR tmpResumableOperations.PartitionNumber IS NULL) + OPTION (RECOMPILE) - 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 + 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(REPLACE(SelectedIndexes.DatabaseName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.SchemaName LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.ObjectName LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,'[','[[]'),'_','[_]') AND COALESCE(tmpIndexesStatistics.IndexName,tmpIndexesStatistics.StatisticsName) LIKE REPLACE(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(REPLACE(SelectedIndexes.DatabaseName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.SchemaName LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.ObjectName LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,'[','[[]'),'_','[_]') AND COALESCE(tmpIndexesStatistics.IndexName,tmpIndexesStatistics.StatisticsName) LIKE REPLACE(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(REPLACE(SelectedIndexes.DatabaseName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.SchemaName LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.ObjectName LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,'[','[[]'),'_','[_]') AND COALESCE(tmpIndexesStatistics.IndexName,tmpIndexesStatistics.StatisticsName) LIKE REPLACE(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; - UPDATE tmpIndexesStatistics - SET tmpIndexesStatistics.StartPosition = SelectedIndexes2.StartPosition + 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 - 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 - - SELECT @ErrorMessage = STRING_AGG(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 @ErrorMessage IS NOT NULL - BEGIN - SET @ErrorMessage = 'The following objects in the @Indexes parameter do not exist: ' + @ErrorMessage + '.' - RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT - SET @Error = @@ERROR - RAISERROR(@EmptyLine,10,1) WITH NOWAIT - END + WHERE Selected = 1 + ) + UPDATE tmpIndexesStatistics + SET [Order] = RowNumber + + -- Update that alter index is completed for rows that have no index, if no index actions have been selected, for rows on read-only filegroups, or based on the page counts + UPDATE @tmpIndexesStatistics + SET AlterIndexCompleted = 1 + WHERE IndexID IS NULL + OR NOT EXISTS (SELECT * FROM @ActionsPreferred) + OR OnReadOnlyFileGroup = 1 + OR NOT (((InRowDataPageCount >= @MinNumberOfPages OR @MinNumberOfPages = 0) AND (InRowDataPageCount <= @MaxNumberOfPages OR @MaxNumberOfPages IS NULL)) OR InRowDataPageCount IS NULL) - SELECT @ErrorMessage = STRING_AGG(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) + -- Update that update statistics is completed for rows that have no statistics + UPDATE @tmpIndexesStatistics + SET UpdateStatisticsCompleted = 1 + WHERE StatisticsID IS NULL + OR (IndexID IS NOT NULL AND @PartitionLevel = 'Y' AND IsIncremental = 0 AND IsLastPartition = 0) + + SET @CurrentCommand = 'SELECT schemas.[name] AS SchemaName, objects.[name] AS ObjectName' + + ' FROM sys.objects objects' + + ' INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id]' + + ' WHERE objects.[type] IN(''U'',''V'')' + + ' AND EXISTS(SELECT * FROM #SelectedIndexes SelectedIndexes' + + ' WHERE SelectedIndexes.DatabaseName = @ParamDatabaseName' + + ' AND SelectedIndexes.SchemaName NOT LIKE ''%[%]%''' + + ' AND SelectedIndexes.ObjectName NOT LIKE ''%[%]%''' + + ' AND schemas.[name] = SelectedIndexes.SchemaName COLLATE ' + @Collation + + ' AND objects.[name] = SelectedIndexes.ObjectName COLLATE ' + @Collation + ')' + + INSERT INTO #ExistingObjects (SchemaName, ObjectName) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseName nvarchar(max)', @ParamDatabaseName = @CurrentDatabaseName + + SET @CurrentCommand = 'SELECT schemas.[name] AS SchemaName, objects.[name] AS ObjectName, [Names].[name] AS IndexName' + + ' FROM sys.objects objects' + + ' INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id]' + + ' CROSS APPLY (SELECT indexes.[name] FROM sys.indexes indexes WHERE indexes.[object_id] = objects.[object_id] AND indexes.[type] <> 0' + + ' UNION SELECT stats.[name] FROM sys.stats stats WHERE stats.[object_id] = objects.[object_id]) [Names]' + + ' WHERE objects.[type] IN(''U'',''V'')' + + ' AND EXISTS(SELECT * FROM #SelectedIndexes SelectedIndexes' + + ' WHERE SelectedIndexes.DatabaseName = @ParamDatabaseName' + + ' AND SelectedIndexes.SchemaName NOT LIKE ''%[%]%''' + + ' AND SelectedIndexes.ObjectName NOT LIKE ''%[%]%''' + + ' AND SelectedIndexes.IndexName NOT LIKE ''%[%]%''' + + ' AND schemas.[name] = SelectedIndexes.SchemaName COLLATE ' + @Collation + + ' AND objects.[name] = SelectedIndexes.ObjectName COLLATE ' + @Collation + + ' AND [Names].[name] = SelectedIndexes.IndexName COLLATE ' + @Collation + ')' + + INSERT INTO #ExistingIndexes (SchemaName, ObjectName, IndexName) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseName nvarchar(max)', @ParamDatabaseName = @CurrentDatabaseName + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)) + '.' + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC, SchemaName ASC, ObjectName ASC) + FROM @SelectedIndexes SelectedIndexes + WHERE DatabaseName = @CurrentDatabaseName + AND SchemaName NOT LIKE '%[%]%' + AND ObjectName NOT LIKE '%[%]%' + AND IndexName LIKE '%[%]%' + AND NOT EXISTS (SELECT * FROM #ExistingObjects WHERE SchemaName = SelectedIndexes.SchemaName AND ObjectName = SelectedIndexes.ObjectName) - IF @ErrorMessage IS NOT NULL - BEGIN - SET @ErrorMessage = 'The following indexes in the @Indexes parameter do not exist: ' + @ErrorMessage + '.' - RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT - SET @Error = @@ERROR - RAISERROR(@EmptyLine,10,1) WITH NOWAIT + IF @ErrorMessage IS NOT NULL + BEGIN + SET @ErrorMessage = 'The following objects in the @Indexes parameter do not exist: ' + @ErrorMessage + '.' + RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + END + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)) + '.' + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName) + '.' + QUOTENAME(IndexName), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC, SchemaName ASC, ObjectName ASC, IndexName ASC) + FROM @SelectedIndexes SelectedIndexes + WHERE DatabaseName = @CurrentDatabaseName + AND SchemaName NOT LIKE '%[%]%' + AND ObjectName NOT LIKE '%[%]%' + AND IndexName NOT LIKE '%[%]%' + AND NOT EXISTS (SELECT * FROM #ExistingIndexes WHERE SchemaName = SelectedIndexes.SchemaName AND ObjectName = SelectedIndexes.ObjectName AND IndexName = SelectedIndexes.IndexName) + + IF @ErrorMessage IS NOT NULL + BEGIN + SET @ErrorMessage = 'The following indexes in the @Indexes parameter do not exist: ' + @ErrorMessage + '.' + RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + END END WHILE (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) @@ -8466,10 +9425,9 @@ BEGIN @CurrentAllowPageLocks = AllowPageLocks, @CurrentHasFilter = HasFilter, @CurrentIsImageText = IsImageText, - @CurrentIsNewLOB = IsNewLOB, @CurrentIsFileStream = IsFileStream, @CurrentHasClusteredColumnstore = HasClusteredColumnstore, - @CurrentHasNonClusteredColumnstore = HasNonClusteredColumnstore, + @CurrentIsColumnstoreOrdered = IsColumnstoreOrdered, @CurrentIsComputed = IsComputed, @CurrentIsClusteredIndexComputed = IsClusteredIndexComputed, @CurrentIsTimestamp = IsTimestamp, @@ -8481,7 +9439,11 @@ BEGIN @CurrentIsIncremental = IsIncremental, @CurrentPartitionID = PartitionID, @CurrentPartitionNumber = PartitionNumber, - @CurrentPartitionCount = PartitionCount + @CurrentIsPartition = IsPartition, + @CurrentIsLastPartition = IsLastPartition, + @CurrentInRowDataPageCount = InRowDataPageCount, + @CurrentAlterIndexCompleted = AlterIndexCompleted, + @CurrentUpdateStatisticsCompleted = UpdateStatisticsCompleted FROM @tmpIndexesStatistics WHERE Selected = 1 AND Completed = 0 @@ -8492,15 +9454,14 @@ 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) + IF @CurrentAlterIndexCompleted = 0 AND @CurrentIndexID IS NOT NULL AND EXISTS(SELECT * FROM @ActionsPreferred) AND @CurrentOnReadOnlyFileGroup = 0 BEGIN + SET @CurrentMaxDOP = @MaxDOP + + -- Does the index exist? SET @CurrentCommand = '' - IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar) + '; ' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' 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' @@ -8515,7 +9476,7 @@ BEGIN 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 @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 @@ -8527,444 +9488,577 @@ BEGIN 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 + -- Is the index fragmented? + IF EXISTS(SELECT [Priority], [Action], COUNT(*) FROM @ActionsPreferred GROUP BY [Priority], [Action] HAVING COUNT(*) <> 3) OR @MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL + BEGIN + IF NOT EXISTS (SELECT * FROM @PhysicalStats WHERE ObjectID = @CurrentObjectID AND IndexID = @CurrentIndexID) BEGIN - SET @CurrentStatisticsExists = 0 - GOTO NoAction + SET @CurrentCommand = '' + + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + + IF @CurrentIndexType IN(5, 6) + BEGIN + SET @CurrentCommand += 'SELECT object_id, index_id, partition_number, MAX(avg_fragmentation_in_percent), SUM(page_count) FROM sys.dm_db_index_physical_stats(DB_ID(@ParamDatabaseName), @ParamObjectID, @ParamIndexID, NULL, ''LIMITED'') WHERE alloc_unit_type_desc = ''IN_ROW_DATA'' AND index_level = 0 GROUP BY object_id, index_id, partition_number' + END + ELSE + BEGIN + SET @CurrentCommand += 'SELECT object_id, index_id, partition_number, avg_fragmentation_in_percent, page_count FROM sys.dm_db_index_physical_stats(DB_ID(@ParamDatabaseName), @ParamObjectID, @ParamIndexID, NULL, ''LIMITED'') WHERE alloc_unit_type_desc = ''IN_ROW_DATA'' AND index_level = 0' + END + + BEGIN TRY + INSERT INTO @PhysicalStats (ObjectID, IndexID, PartitionNumber, FragmentationLevel, PageCount) + EXECUTE sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseName nvarchar(max), @ParamObjectID int, @ParamIndexID int', @ParamDatabaseName = @CurrentDatabaseName, @ParamObjectID = @CurrentObjectID, @ParamIndexID = @CurrentIndexID + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 + + UPDATE @tmpIndexesStatistics + SET AlterIndexCompleted = 1 + WHERE ObjectID = @CurrentObjectID + AND IndexID = @CurrentIndexID + AND AlterIndexCompleted = 0 + + GOTO NoAction + END CATCH 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) + IF @CurrentPartitionNumber IS NULL BEGIN - SET @ReturnCode = ERROR_NUMBER() + SELECT @CurrentFragmentationLevel = MAX(FragmentationLevel), + @CurrentPageCount = SUM(PageCount) + FROM @PhysicalStats + WHERE ObjectID = @CurrentObjectID + AND IndexID = @CurrentIndexID 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) + ELSE BEGIN - SET @ReturnCode = ERROR_NUMBER() + SELECT @CurrentFragmentationLevel = FragmentationLevel, + @CurrentPageCount = PageCount + FROM @PhysicalStats + WHERE ObjectID = @CurrentObjectID + AND IndexID = @CurrentIndexID + AND PartitionNumber = @CurrentPartitionNumber END + END - GOTO NoAction - END CATCH - END - - -- Select fragmentation group - IF @CurrentIndexID IS NOT NULL AND @CurrentOnReadOnlyFileGroup = 0 AND EXISTS(SELECT * FROM @ActionsPreferred) - BEGIN + -- Select fragmentation group 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) + -- Which actions are allowed? + IF NOT (@CurrentIsMemoryOptimized = 1) AND NOT (@CurrentAllowPageLocks = 0) BEGIN INSERT INTO @CurrentActionsAllowed ([Action]) VALUES ('INDEX_REORGANIZE') END - IF NOT (@CurrentOnReadOnlyFileGroup = 1) - AND NOT (@CurrentIsMemoryOptimized = 1) + IF NOT (@CurrentIsMemoryOptimized = 1) BEGIN INSERT INTO @CurrentActionsAllowed ([Action]) VALUES ('INDEX_REBUILD_OFFLINE') END - IF SERVERPROPERTY('EngineEdition') IN (3, 5, 8) - AND NOT (@CurrentOnReadOnlyFileGroup = 1) + IF @EngineEdition IN (3, 5, 8) 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 = 1 AND @CurrentIsImageText = 1 AND @CurrentIsImageText IS NOT NULL) + AND NOT (@CurrentIndexType = 1 AND @CurrentIsFileStream = 1 AND @CurrentIsFileStream IS NOT NULL) 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) + AND NOT (@CurrentIndexType = 5 AND NOT (@Version >= 15 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous'))) + AND NOT (@CurrentIndexType = 2 AND @CurrentHasClusteredColumnstore = 1 AND @CurrentHasClusteredColumnstore IS NOT NULL AND NOT (@Version >= 15 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous'))) + AND NOT (@CurrentIndexType = 5 AND @CurrentIsColumnstoreOrdered = 1 AND @CurrentIsColumnstoreOrdered IS NOT NULL AND NOT (@Version >= 17 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous'))) 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) + -- Decide action + IF (@CurrentPageCount >= @MinNumberOfPages OR @MinNumberOfPages = 0) + AND (@CurrentPageCount <= @MaxNumberOfPages OR @MaxNumberOfPages IS NULL) + AND @CurrentResumableIndexOperation = 0 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)) + 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 - ELSE + + IF @CurrentResumableIndexOperation = 1 BEGIN - SELECT @CurrentAction = [Action] - FROM @ActionsPreferred - WHERE [Priority] = (SELECT MIN([Priority]) - FROM @ActionsPreferred - WHERE [Action] IN (SELECT [Action] FROM @CurrentActionsAllowed)) + SET @CurrentAction = 'INDEX_REBUILD_ONLINE' 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 @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentIndexType IN (1, 2) AND @CurrentAllowPageLocks = 0 + BEGIN + SET @CurrentMaxDOP = 1 + END - -- Workaround for limitation in SQL Server, http://support.microsoft.com/kb/2292737 - IF @CurrentIndexID IS NOT NULL - BEGIN - SET @CurrentMaxDOP = @MaxDOP + -- Create index comment + IF @CurrentAction 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 + ', ' + IF @CurrentIsImageText IS NOT NULL SET @CurrentComment += 'ImageText: ' + CASE WHEN @CurrentIsImageText = 1 THEN 'Yes' WHEN @CurrentIsImageText = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentIsFileStream IS NOT NULL SET @CurrentComment += 'FileStream: ' + CASE WHEN @CurrentIsFileStream = 1 THEN 'Yes' WHEN @CurrentIsFileStream = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentHasClusteredColumnstore IS NOT NULL 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 @CurrentIsColumnstoreOrdered IS NOT NULL AND @CurrentIndexType = 5 SET @CurrentComment += 'IsColumnstoreOrdered: ' + CASE WHEN @CurrentIsColumnstoreOrdered = 1 THEN 'Yes' WHEN @CurrentIsColumnstoreOrdered = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentIsComputed IS NOT NULL SET @CurrentComment += 'Computed: ' + CASE WHEN @CurrentIsComputed = 1 THEN 'Yes' WHEN @CurrentIsComputed = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentIsClusteredIndexComputed IS NOT NULL AND @CurrentIndexType = 2 SET @CurrentComment += 'ClusteredIndexComputed: ' + CASE WHEN @CurrentIsClusteredIndexComputed = 1 THEN 'Yes' WHEN @CurrentIsClusteredIndexComputed = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentIsTimestamp IS NOT NULL SET @CurrentComment += 'Timestamp: ' + CASE WHEN @CurrentIsTimestamp = 1 THEN 'Yes' WHEN @CurrentIsTimestamp = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @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(max)),'N/A') + ', ' + SET @CurrentComment += 'Fragmentation: ' + ISNULL(CAST(@CurrentFragmentationLevel AS nvarchar(max)),'N/A') + END - IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentAllowPageLocks = 0 + IF @CurrentAction IS NOT NULL AND (@CurrentPageCount IS NOT NULL OR @CurrentFragmentationLevel IS NOT NULL) BEGIN - SET @CurrentMaxDOP = 1 + SET @CurrentExtendedInfo = (SELECT * + FROM (SELECT CAST(@CurrentPageCount AS nvarchar(max)) AS [PageCount], + CAST(@CurrentFragmentationLevel AS nvarchar(max)) AS Fragmentation + ) ExtendedInfo FOR XML RAW('ExtendedInfo'), ELEMENTS) 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 + IF @CurrentAction IS NOT NULL AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SET @CurrentDatabaseContext = @CurrentDatabaseName - SET @CurrentStatisticsSample = @StatisticsSample - SET @CurrentStatisticsResample = @StatisticsResample + SET @CurrentCommandType = 'ALTER_INDEX' - -- 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 + SET @CurrentCommand = '' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + 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(max)) + + 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) + VALUES('SORT_IN_TEMPDB = ON') + END - -- Incremental statistics only supports RESAMPLE - IF @PartitionLevelStatistics = 1 AND @CurrentIsIncremental = 1 - BEGIN - SET @CurrentStatisticsSample = NULL - SET @CurrentStatisticsResample = 'Y' - 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) + VALUES('SORT_IN_TEMPDB = OFF') + 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 @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('ONLINE = ON' + CASE WHEN @WaitAtLowPriorityMaxDuration IS NOT NULL THEN ' (WAIT_AT_LOW_PRIORITY (MAX_DURATION = ' + CAST(@WaitAtLowPriorityMaxDuration AS nvarchar(max)) + ', ABORT_AFTER_WAIT = ' + UPPER(@WaitAtLowPriorityAbortAfterWait) + '))' ELSE '' END) + 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) + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentResumableIndexOperation = 1 AND @WaitAtLowPriorityMaxDuration IS NOT NULL + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('WAIT_AT_LOW_PRIORITY (MAX_DURATION = ' + CAST(@WaitAtLowPriorityMaxDuration AS nvarchar(max)) + ', ABORT_AFTER_WAIT = ' + UPPER(@WaitAtLowPriorityAbortAfterWait) + ')') + END + + IF @CurrentAction = 'INDEX_REBUILD_OFFLINE' AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('ONLINE = OFF') + END + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @CurrentMaxDOP IS NOT NULL + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('MAXDOP = ' + CAST(@CurrentMaxDOP AS nvarchar(max))) + 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) + VALUES('FILLFACTOR = ' + CAST(@FillFactor AS nvarchar(max))) + END + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @PadIndex IS NOT NULL AND @CurrentIsPartition = 0 AND @CurrentIndexType IN(1,2,3,4) AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('PAD_INDEX = ' + CASE WHEN @PadIndex = 'Y' THEN 'ON' WHEN @PadIndex = 'N' THEN 'OFF' END) + END + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @DataCompression IS NOT NULL AND @CurrentIndexType IN(1,2,4) AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('DATA_COMPRESSION = ' + @DataCompression) + END + + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES(CASE WHEN @Resumable = 'Y' AND @CurrentIndexType IN(1,2) AND (@CurrentIsComputed = 0 OR @CurrentIsComputed IS NULL) AND (@CurrentIsClusteredIndexComputed = 0 OR @CurrentIsClusteredIndexComputed IS NULL) AND (@CurrentIsTimestamp = 0 OR @CurrentIsTimestamp IS NULL) AND @CurrentHasFilter = 0 AND (@CurrentHasClusteredColumnstore = 0 OR @CurrentHasClusteredColumnstore IS NULL) THEN 'RESUMABLE = ON' ELSE 'RESUMABLE = OFF' END) + END + + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND ((@Resumable = 'Y' AND @CurrentIndexType IN(1,2) AND (@CurrentIsComputed = 0 OR @CurrentIsComputed IS NULL) AND (@CurrentIsClusteredIndexComputed = 0 OR @CurrentIsClusteredIndexComputed IS NULL) AND (@CurrentIsTimestamp = 0 OR @CurrentIsTimestamp IS NULL) AND @CurrentHasFilter = 0 AND (@CurrentHasClusteredColumnstore = 0 OR @CurrentHasClusteredColumnstore IS NULL)) OR @CurrentResumableIndexOperation = 1) AND @TimeLimit IS NOT NULL + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('MAX_DURATION = ' + CAST(CASE WHEN DATEDIFF(MINUTE,SYSDATETIME(),DATEADD(SECOND,@TimeLimit,@StartTime)) < 1 THEN 1 WHEN DATEDIFF(MINUTE,SYSDATETIME(),DATEADD(SECOND,@TimeLimit,@StartTime)) > 10080 THEN 10080 ELSE DATEDIFF(MINUTE,SYSDATETIME(),DATEADD(SECOND,@TimeLimit,@StartTime)) END AS nvarchar(max))) + END + + IF @CurrentAction IN('INDEX_REORGANIZE') AND @LOBCompaction = 'Y' + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('LOB_COMPACTION = ON') + END + + IF @CurrentAction IN('INDEX_REORGANIZE') AND @LOBCompaction = 'N' + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('LOB_COMPACTION = OFF') + END + + IF EXISTS (SELECT * FROM @CurrentAlterIndexWithClauseArguments) + BEGIN + SELECT @CurrentCommand += ' WITH (' + STRING_AGG(Argument, ', ') WITHIN GROUP (ORDER BY ID ASC) + ')' + FROM @CurrentAlterIndexWithClauseArguments + END + + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @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 END - IF @CurrentIndexID IS NOT NULL AND @CurrentAction IS NOT NULL AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + -- Should the statistics be updated? + IF @CurrentUpdateStatisticsCompleted = 0 + AND @CurrentStatisticsID IS NOT NULL + AND ((@UpdateStatistics = 'ALL' AND (@CurrentIndexType IN (1,2,7) OR @CurrentIndexID IS NULL)) OR (@UpdateStatistics = 'INDEX' AND @CurrentIndexID IS NOT NULL AND @CurrentIndexType IN (1,2,7)) OR (@UpdateStatistics = 'COLUMNS' AND @CurrentIndexID IS NULL)) + AND ((@CurrentIsPartition = 0 AND (@CurrentAction NOT IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') OR @CurrentAction IS NULL)) OR (@CurrentIsPartition = 1 AND (@CurrentIsLastPartition = 1 OR (@PartitionLevel = 'Y' AND @CurrentIsIncremental = 1)))) BEGIN - SET @CurrentDatabaseContext = @CurrentDatabaseName - - SET @CurrentCommandType = 'ALTER_INDEX' + SET @CurrentMaxDOP = @MaxDOP + -- Does the statistics exist? 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 @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' - IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND (@CurrentIsPartition = 0 OR @Version >= 12) AND @CurrentResumableIndexOperation = 0 + 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(max)) + ', ' + 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 + + -- Check non-incremental statistics properties + IF NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) AND NOT (@PartitionLevel = 'Y' AND @CurrentIsIncremental = 1) 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 + SET @CurrentCommand = '' + + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + + SET @CurrentCommand += 'SELECT @ParamRowCount = [rows], @ParamModificationCounter = modification_counter FROM sys.dm_db_stats_properties (@ParamObjectID, @ParamStatisticsID)' + + BEGIN TRY + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamObjectID int, @ParamStatisticsID int, @ParamRowCount bigint OUTPUT, @ParamModificationCounter bigint OUTPUT', @ParamObjectID = @CurrentObjectID, @ParamStatisticsID = @CurrentStatisticsID, @ParamRowCount = @CurrentRowCount OUTPUT, @ParamModificationCounter = @CurrentModificationCounter OUTPUT + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 - IF @CurrentAction = 'INDEX_REBUILD_OFFLINE' AND (@CurrentIsPartition = 0 OR @Version >= 12) AND @CurrentResumableIndexOperation = 0 + -- Check incremental statistics properties + IF NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) AND @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 + AND NOT EXISTS (SELECT * FROM @IncrementalStatsProperties WHERE ObjectID = @CurrentObjectID AND StatisticsID = @CurrentStatisticsID) BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'ONLINE = OFF' + SET @CurrentCommand = '' + + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + + BEGIN + SET @CurrentCommand += 'SELECT object_id, stats_id, partition_number, [rows], modification_counter FROM sys.dm_db_incremental_stats_properties (@ParamObjectID, @ParamStatisticsID)' + END + + BEGIN TRY + INSERT INTO @IncrementalStatsProperties (ObjectID, StatisticsID, PartitionNumber, [Rows], ModificationCounter) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamObjectID int, @ParamStatisticsID int', @ParamObjectID = @CurrentObjectID, @ParamStatisticsID = @CurrentStatisticsID + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 + + UPDATE @tmpIndexesStatistics + SET UpdateStatisticsCompleted = 1 + WHERE ObjectID = @CurrentObjectID + AND StatisticsID = @CurrentStatisticsID + AND UpdateStatisticsCompleted = 0 + + GOTO NoAction + END CATCH END - IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @CurrentMaxDOP IS NOT NULL + IF NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) AND @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'MAXDOP = ' + CAST(@CurrentMaxDOP AS nvarchar) + SELECT @CurrentRowCount = [Rows], + @CurrentModificationCounter = [ModificationCounter] + FROM @IncrementalStatsProperties + WHERE ObjectID = @CurrentObjectID + AND StatisticsID = @CurrentStatisticsID + AND PartitionNumber = @CurrentPartitionNumber 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 + -- Check partition statistics + IF NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) AND @CurrentModificationCounter IS NULL BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'FILLFACTOR = ' + CAST(@FillFactor AS nvarchar) + SET @CurrentCommand = '' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + + IF @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 + BEGIN + SET @CurrentCommand += 'SELECT @ParamObjectHasRows = CASE WHEN EXISTS (SELECT * FROM sys.dm_db_partition_stats WHERE [object_id] = @ParamObjectID AND index_id IN (0,1) AND partition_number = @ParamPartitionNumber AND row_count > 0) THEN 1 ELSE 0 END' + END + ELSE + BEGIN + SET @CurrentCommand += 'SELECT @ParamObjectHasRows = CASE WHEN EXISTS (SELECT * FROM sys.dm_db_partition_stats WHERE [object_id] = @ParamObjectID AND index_id IN (0,1) AND row_count > 0) THEN 1 ELSE 0 END' + END + + BEGIN TRY + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamObjectID int, @ParamPartitionNumber int, @ParamObjectHasRows bit OUTPUT', @ParamObjectID = @CurrentObjectID, @ParamPartitionNumber = @CurrentPartitionNumber, @ParamObjectHasRows = @CurrentObjectHasRows OUTPUT + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + ISNULL(ERROR_MESSAGE(),'') + CASE WHEN ERROR_NUMBER() = 1222 THEN ' The object ' + QUOTENAME(@CurrentDatabaseName) + '.' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName) + ' is locked. The row count 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 - 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 + -- Update statistics? + IF ((@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 ((@CurrentIndexType IN (1,2) OR @CurrentIndexID IS NULL) AND @CurrentModificationCounter IS NULL AND @CurrentObjectHasRows = 1)) BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'PAD_INDEX = ON' + SET @CurrentUpdateStatistics = 'Y' END - - IF (@Version >= 14 OR SERVERPROPERTY('EngineEdition') IN (5,8)) AND @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentResumableIndexOperation = 0 + ELSE 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 + SET @CurrentUpdateStatistics = 'N' 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 + SET @CurrentStatisticsSample = @StatisticsSample + SET @CurrentStatisticsPersistSample = @StatisticsPersistSample + SET @CurrentStatisticsResample = @StatisticsResample + + -- Incremental statistics only supports RESAMPLE + IF @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'MAX_DURATION = ' + CAST(DATEDIFF(MINUTE,SYSDATETIME(),DATEADD(SECOND,@TimeLimit,@StartTime)) AS nvarchar(max)) + SET @CurrentStatisticsSample = NULL + SET @CurrentStatisticsPersistSample = NULL + SET @CurrentStatisticsResample = 'Y' END - IF @CurrentAction IN('INDEX_REORGANIZE') AND @LOBCompaction = 'Y' + -- Create statistics comment + IF @CurrentUpdateStatistics = 'Y' BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'LOB_COMPACTION = ON' + SET @CurrentComment = 'ObjectType: ' + CASE WHEN @CurrentObjectType = 'U' THEN 'Table' WHEN @CurrentObjectType = 'V' THEN 'View' ELSE 'N/A' END + ', ' + SET @CurrentComment += 'StatisticsType: ' + 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 'Yes' WHEN @CurrentIsIncremental = 0 THEN 'No' ELSE 'N/A' END + ', ' + SET @CurrentComment += 'RowCount: ' + ISNULL(CAST(@CurrentRowCount AS nvarchar(max)),'N/A') + ', ' + SET @CurrentComment += 'ModificationCounter: ' + ISNULL(CAST(@CurrentModificationCounter AS nvarchar(max)),'N/A') END - IF @CurrentAction IN('INDEX_REORGANIZE') AND @LOBCompaction = 'N' + IF @CurrentUpdateStatistics = 'Y' AND (@CurrentRowCount IS NOT NULL OR @CurrentModificationCounter IS NOT NULL) BEGIN - INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) - SELECT 'LOB_COMPACTION = OFF' + SET @CurrentExtendedInfo = (SELECT * + FROM (SELECT CAST(@CurrentRowCount AS nvarchar(max)) AS [RowCount], + CAST(@CurrentModificationCounter AS nvarchar(max)) AS ModificationCounter + ) ExtendedInfo FOR XML RAW('ExtendedInfo'), ELEMENTS) END - - IF EXISTS (SELECT * FROM @CurrentAlterIndexWithClauseArguments) + ELSE BEGIN - SELECT @CurrentCommand += ' WITH (' + STRING_AGG(Argument, ', ') WITHIN GROUP (ORDER BY ID ASC) + ')' - FROM @CurrentAlterIndexWithClauseArguments + SET @CurrentExtendedInfo = NULL END - 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 + IF @CurrentUpdateStatistics = 'Y' AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) BEGIN - SET @CurrentDelay = DATEADD(ss,@Delay,'1900-01-01') - WAITFOR DELAY @CurrentDelay - END - END + SET @CurrentDatabaseContext = @CurrentDatabaseName - SET @CurrentMaxDOP = @MaxDOP + SET @CurrentCommandType = 'UPDATE_STATISTICS' - -- 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 + SET @CurrentCommand = '' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + SET @CurrentCommand += 'UPDATE STATISTICS ' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName) + ' ' + QUOTENAME(@CurrentStatisticsName) - 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 @CurrentMaxDOP IS NOT NULL AND (@Version >= 14.03015 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('MAXDOP = ' + CAST(@CurrentMaxDOP AS nvarchar(max))) + END - IF @CurrentStatisticsID IS NOT NULL AND @CurrentUpdateStatistics = 'Y' AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) - BEGIN - SET @CurrentDatabaseContext = @CurrentDatabaseName + IF @CurrentStatisticsSample = 100 + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('FULLSCAN') + END - SET @CurrentCommandType = 'UPDATE_STATISTICS' + IF @CurrentStatisticsSample IS NOT NULL AND @CurrentStatisticsSample <> 100 + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('SAMPLE ' + CAST(@CurrentStatisticsSample AS nvarchar(max)) + ' PERCENT') + END - 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 @CurrentStatisticsPersistSample = 'Y' + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('PERSIST_SAMPLE_PERCENT = ON') + END - IF @CurrentMaxDOP IS NOT NULL AND ((@Version >= 12.06024 AND @Version < 13) OR (@Version >= 13.05026 AND @Version < 14) OR @Version >= 14.030154 OR SERVERPROPERTY('EngineEdition') IN (5, 8)) - BEGIN - INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) - SELECT 'MAXDOP = ' + CAST(@CurrentMaxDOP AS nvarchar) - END + IF @CurrentStatisticsPersistSample = 'N' + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('PERSIST_SAMPLE_PERCENT = OFF') + END - IF @CurrentStatisticsSample = 100 - BEGIN - INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) - SELECT 'FULLSCAN' - END + IF @CurrentNoRecompute = 1 + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('NORECOMPUTE') + END - IF @CurrentStatisticsSample IS NOT NULL AND @CurrentStatisticsSample <> 100 - BEGIN - INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) - SELECT 'SAMPLE ' + CAST(@CurrentStatisticsSample AS nvarchar) + ' PERCENT' - END + IF @CurrentStatisticsResample = 'Y' + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('RESAMPLE') + END - IF @CurrentNoRecompute = 1 - BEGIN - INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) - SELECT 'NORECOMPUTE' - END + IF EXISTS (SELECT * FROM @CurrentUpdateStatisticsWithClauseArguments) + BEGIN + SELECT @CurrentCommand += ' WITH ' + STRING_AGG(Argument, ', ') WITHIN GROUP (ORDER BY ID ASC) + FROM @CurrentUpdateStatisticsWithClauseArguments + END - IF @CurrentStatisticsResample = 'Y' - BEGIN - INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) - SELECT 'RESAMPLE' - END + IF @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 AND @CurrentPartitionNumber IS NOT NULL SET @CurrentCommand += ' ON PARTITIONS(' + CAST(@CurrentPartitionNumber AS nvarchar(max)) + ')' - IF EXISTS (SELECT * FROM @CurrentUpdateStatisticsWithClauseArguments) - BEGIN - SELECT @CurrentCommand += ' WITH ' + STRING_AGG(Argument, ', ') WITHIN GROUP (ORDER BY ID ASC) - FROM @CurrentUpdateStatisticsWithClauseArguments + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 2, @Comment = @CurrentComment, @DatabaseName = @CurrentDatabaseName, @SchemaName = @CurrentSchemaName, @ObjectName = @CurrentObjectName, @ObjectType = @CurrentObjectType, @IndexName = @CurrentIndexName, @IndexType = @CurrentIndexType, @StatisticsName = @CurrentStatisticsName, @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 END - - 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 + SET AlterIndexCompleted = 1, + UpdateStatisticsCompleted = 1 WHERE Selected = 1 AND Completed = 0 AND [Order] = @CurrentIxOrder AND ID = @CurrentIxID + -- Update that index operations on remaining partitions are completed where no action is needed + IF @CurrentAlterIndexCompleted = 0 AND @CurrentIndexID IS NOT NULL AND @PartitionLevel = 'Y' AND @CurrentIsPartition = 1 AND (SELECT COUNT(DISTINCT FragmentationGroup) FROM @ActionsPreferred) < 3 + BEGIN + UPDATE tmpIndexesStatistics + SET AlterIndexCompleted = 1 + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN @PhysicalStats PhysicalStats ON tmpIndexesStatistics.ObjectID = PhysicalStats.ObjectID AND tmpIndexesStatistics.IndexID = PhysicalStats.IndexID AND tmpIndexesStatistics.PartitionNumber = PhysicalStats.PartitionNumber + WHERE tmpIndexesStatistics.ObjectID = @CurrentObjectID + AND tmpIndexesStatistics.IndexID = @CurrentIndexID + AND tmpIndexesStatistics.AlterIndexCompleted = 0 + AND tmpIndexesStatistics.ResumableIndexOperation = 0 + AND NOT EXISTS (SELECT * + FROM @ActionsPreferred ActionsPreferred + WHERE ActionsPreferred.FragmentationGroup = CASE + WHEN PhysicalStats.FragmentationLevel >= @FragmentationLevel2 THEN 'High' + WHEN PhysicalStats.FragmentationLevel >= @FragmentationLevel1 AND PhysicalStats.FragmentationLevel < @FragmentationLevel2 THEN 'Medium' + WHEN PhysicalStats.FragmentationLevel < @FragmentationLevel1 THEN 'Low' + END) + END + + -- Update that statistics on remaining partitions are completed where no update is needed + IF @CurrentUpdateStatisticsCompleted = 0 AND @CurrentStatisticsID IS NOT NULL AND @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 AND NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) + BEGIN + UPDATE tmpIndexesStatistics + SET UpdateStatisticsCompleted = 1 + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN @IncrementalStatsProperties IncrementalStatsProperties ON tmpIndexesStatistics.ObjectID = IncrementalStatsProperties.ObjectID AND tmpIndexesStatistics.StatisticsID = IncrementalStatsProperties.StatisticsID AND tmpIndexesStatistics.PartitionNumber = IncrementalStatsProperties.PartitionNumber + WHERE tmpIndexesStatistics.ObjectID = @CurrentObjectID + AND tmpIndexesStatistics.StatisticsID = @CurrentStatisticsID + AND tmpIndexesStatistics.Selected = 1 + AND tmpIndexesStatistics.Completed = 0 + AND IncrementalStatsProperties.ModificationCounter IS NOT NULL + AND ((@OnlyModifiedStatistics = 'Y' AND NOT (IncrementalStatsProperties.ModificationCounter > 0)) + OR (@StatisticsModificationLevel IS NOT NULL AND NOT ((IncrementalStatsProperties.ModificationCounter * 1. / NULLIF(IncrementalStatsProperties.[Rows],0)) * 100 >= @StatisticsModificationLevel OR (IncrementalStatsProperties.ModificationCounter > 0 AND IncrementalStatsProperties.ModificationCounter >= SQRT(IncrementalStatsProperties.[Rows] * 1000))))) + END + -- Clear variables SET @CurrentDatabaseContext = NULL @@ -8989,15 +10083,17 @@ BEGIN SET @CurrentStatisticsName = NULL SET @CurrentPartitionID = NULL SET @CurrentPartitionNumber = NULL - SET @CurrentPartitionCount = NULL + SET @CurrentInRowDataPageCount = NULL + SET @CurrentAlterIndexCompleted = NULL + SET @CurrentUpdateStatisticsCompleted = NULL SET @CurrentIsPartition = NULL + SET @CurrentIsLastPartition = NULL SET @CurrentIndexExists = NULL SET @CurrentStatisticsExists = NULL SET @CurrentIsImageText = NULL - SET @CurrentIsNewLOB = NULL SET @CurrentIsFileStream = NULL SET @CurrentHasClusteredColumnstore = NULL - SET @CurrentHasNonClusteredColumnstore = NULL + SET @CurrentIsColumnstoreOrdered = NULL SET @CurrentIsComputed = NULL SET @CurrentIsClusteredIndexComputed = NULL SET @CurrentIsTimestamp = NULL @@ -9005,6 +10101,7 @@ BEGIN SET @CurrentHasFilter = NULL SET @CurrentNoRecompute = NULL SET @CurrentIsIncremental = NULL + SET @CurrentObjectHasRows = NULL SET @CurrentRowCount = NULL SET @CurrentModificationCounter = NULL SET @CurrentOnReadOnlyFileGroup = NULL @@ -9016,6 +10113,7 @@ BEGIN SET @CurrentMaxDOP = NULL SET @CurrentUpdateStatistics = NULL SET @CurrentStatisticsSample = NULL + SET @CurrentStatisticsPersistSample = NULL SET @CurrentStatisticsResample = NULL DELETE FROM @CurrentActionsAllowed @@ -9030,8 +10128,9 @@ BEGIN 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 + SET @ReturnCode = @Error + RAISERROR(@EmptyLine,10,1) WITH NOWAIT END -- Update that the database is completed @@ -9063,25 +10162,40 @@ BEGIN SET @CurrentDatabaseState = NULL SET @CurrentInStandby = NULL SET @CurrentRecoveryModel = NULL - - SET @CurrentReplicaID = NULL + SET @CurrentDatabaseHasReadOnlyFileGroup = NULL + SET @CurrentAvailabilityGroupReplicaID = NULL SET @CurrentAvailabilityGroupID = NULL SET @CurrentAvailabilityGroup = NULL SET @CurrentAvailabilityGroupRole = NULL + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = NULL + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = NULL + SET @CurrentDistributedAvailabilityGroup = NULL + SET @CurrentDistributedAvailabilityGroupReplicaID = NULL + SET @CurrentDistributedAvailabilityGroupRole = NULL + SET @CurrentDatabaseMirroringRole = NULL SET @CurrentCommand = NULL DELETE FROM @tmpIndexesStatistics - END + TRUNCATE TABLE #Objects + TRUNCATE TABLE #Indexes + TRUNCATE TABLE #Stats + TRUNCATE TABLE #ExistingObjects + TRUNCATE TABLE #ExistingIndexes + DELETE FROM @tmpResumableOperations + DELETE FROM @PhysicalStats + DELETE FROM @IncrementalStatsProperties + + END -- End of database loop ---------------------------------------------------------------------------------------------------- --// Log completing information //-- ---------------------------------------------------------------------------------------------------- Logging: - SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar,SYSDATETIME(),120) + SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar(max),SYSDATETIME(),120) RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT RAISERROR(@EmptyLine,10,1) WITH NOWAIT @@ -9099,7 +10213,6 @@ 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 @@ -9116,7 +10229,6 @@ BEGIN DECLARE @LogDirectory nvarchar(max) DECLARE @TokenServer nvarchar(max) - DECLARE @TokenJobID nvarchar(max) DECLARE @TokenJobName nvarchar(max) DECLARE @TokenStepID nvarchar(max) DECLARE @TokenStepName nvarchar(max) @@ -9133,8 +10245,6 @@ BEGIN CommandTSQL nvarchar(max), CommandCmdExec nvarchar(max), DatabaseName varchar(max), - OutputFileNamePart01 nvarchar(max), - OutputFileNamePart02 nvarchar(max), Selected bit DEFAULT 0, Completed bit DEFAULT 0) @@ -9143,27 +10253,16 @@ BEGIN 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 @HostPlatform = host_platform + FROM sys.dm_os_host_info SELECT @DirectorySeparator = CASE WHEN @HostPlatform = 'Windows' THEN '\' @@ -9171,18 +10270,13 @@ BEGIN 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))' + SET @TokenJobName = '$' + '(ESCAPE_SQUOTE(JOBNAME))' + SET @TokenStepName = '$' + '(ESCAPE_SQUOTE(STEPNAME))' - IF @Version >= 13 - BEGIN - SET @TokenJobName = '$' + '(ESCAPE_SQUOTE(JOBNAME))' - SET @TokenStepName = '$' + '(ESCAPE_SQUOTE(STEPNAME))' - END - - IF @Version >= 12 AND @HostPlatform = 'Windows' + IF @HostPlatform = 'Windows' BEGIN SET @TokenLogDirectory = '$' + '(ESCAPE_SQUOTE(SQLLOGDIR))' END @@ -9211,15 +10305,8 @@ BEGIN 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 + SELECT @LogDirectory = [path] + FROM sys.dm_os_server_diagnostics_log_configurations IF @OutputFileDirectory IS NOT NULL AND RIGHT(@OutputFileDirectory,1) = @DirectorySeparator BEGIN @@ -9239,74 +10326,59 @@ BEGIN SET @JobOwner = SUSER_SNAME(0x01) END - INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01, OutputFileNamePart02) - SELECT 'DatabaseBackup - SYSTEM_DATABASES - FULL', + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName) + VALUES('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' + @DatabaseName) - INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01, OutputFileNamePart02) - SELECT 'DatabaseBackup - USER_DATABASES - DIFF', + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName) + VALUES('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' + @DatabaseName) - INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01, OutputFileNamePart02) - SELECT 'DatabaseBackup - USER_DATABASES - FULL', + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName) + VALUES('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' + @DatabaseName) - INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01, OutputFileNamePart02) - SELECT 'DatabaseBackup - USER_DATABASES - LOG', + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName) + VALUES('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', + @DatabaseName) + + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName) + VALUES('DatabaseIntegrityCheck - SYSTEM_DATABASES', + 'EXECUTE [dbo].[DatabaseIntegrityCheck]' + CHAR(13) + CHAR(10) + '@Databases = ''SYSTEM_DATABASES'',' + CHAR(13) + CHAR(10) + '@NoInformationalMessages = ''Y'',' + CHAR(13) + CHAR(10) + '@LogToTable = ''' + @LogToTable + '''', + @DatabaseName) + + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName) + VALUES('DatabaseIntegrityCheck - USER_DATABASES', + 'EXECUTE [dbo].[DatabaseIntegrityCheck]' + CHAR(13) + CHAR(10) + '@Databases = ''USER_DATABASES'',' + CHAR(13) + CHAR(10) + '@NoInformationalMessages = ''Y'',' + CHAR(13) + CHAR(10) + '@LogToTable = ''' + @LogToTable + '''', + @DatabaseName) + + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName) + VALUES('IndexOptimize - USER_DATABASES', 'EXECUTE [dbo].[IndexOptimize]' + CHAR(13) + CHAR(10) + '@Databases = ''USER_DATABASES'',' + CHAR(13) + CHAR(10) + '@LogToTable = ''' + @LogToTable + '''', - @DatabaseName, - 'IndexOptimize' + @DatabaseName) - INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01) - SELECT 'sp_delete_backuphistory', + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName) + VALUES('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' + 'msdb') - INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01) - SELECT 'sp_purge_jobhistory', + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName) + VALUES('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' + 'msdb') - INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName, OutputFileNamePart01) - SELECT 'CommandLog Cleanup', + INSERT INTO @Jobs ([Name], CommandTSQL, DatabaseName) + VALUES('CommandLog Cleanup', 'DELETE FROM [dbo].[CommandLog]' + CHAR(13) + CHAR(10) + 'WHERE StartTime < DATEADD(dd,-30,GETDATE())', - @DatabaseName, - 'CommandLogCleanup' + @DatabaseName) - 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' + INSERT INTO @Jobs ([Name], CommandCmdExec) + VALUES('Output File Cleanup', + 'powershell.exe -NoProfile -Command "Get-ChildItem -LiteralPath ''' + REPLACE(COALESCE(@OutputFileDirectory,@TokenLogDirectory,@LogDirectory),'''','''''') + ''' -Filter ''*_*_*_*.txt'' -File | Where-Object { $_.LastWriteTime.Date -le (Get-Date).Date.AddDays(-30) } | ForEach-Object { Write-Output (''del '' + $_.FullName); Remove-Item -LiteralPath $_.FullName }"') IF @AmazonRDS = 1 BEGIN @@ -9334,48 +10406,22 @@ BEGIN 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 + SELECT TOP 1 @CurrentJobID = JobID, + @CurrentJobName = [Name], + @CurrentCommandTSQL = CommandTSQL, + @CurrentCommandCmdExec = CommandCmdExec, + @CurrentDatabaseName = DatabaseName FROM @Jobs WHERE Completed = 0 AND Selected = 1 ORDER BY JobID ASC - IF @CurrentCommandTSQL IS NOT NULL AND @AmazonRDS = 1 + IF @CurrentCommandTSQL IS NOT NULL 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' @@ -9385,9 +10431,7 @@ BEGIN 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' + SET @CurrentOutputFileName = COALESCE(@OutputFileDirectory,@TokenLogDirectory,@LogDirectory) + @DirectorySeparator + @TokenJobName + '_' + @TokenStepID + '_' + @TokenDate + '_' + @TokenTime + '.txt' IF LEN(@CurrentOutputFileName) > 200 SET @CurrentOutputFileName = NULL END @@ -9408,8 +10452,6 @@ BEGIN SET @CurrentCommandTSQL = NULL SET @CurrentCommandCmdExec = NULL SET @CurrentDatabaseName = NULL - SET @CurrentOutputFileNamePart01 = NULL - SET @CurrentOutputFileNamePart02 = NULL SET @CurrentJobStepCommand = NULL SET @CurrentJobStepSubSystem = NULL SET @CurrentJobStepDatabaseName = NULL @@ -9428,7 +10470,7 @@ DECLARE @AmazonRDS bit = CASE WHEN SERVERPROPERTY('EngineEdition') IN (5, 8) THE 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 + DECLARE JobCursor CURSOR LOCAL 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 OR command LIKE '%DatabaseBackup%@ModificationLevel%' OR command LIKE '%DatabaseBackup%@LogSizeSinceLastLogBackup%' OR command LIKE '%DatabaseBackup%@TimeSinceLastLogBackup%' OPEN JobCursor @@ -9437,6 +10479,9 @@ BEGIN WHILE @@FETCH_STATUS = 0 BEGIN SET @command = REPLACE(@command, '@CheckSum', '@Checksum') + SET @command = REPLACE(@command, '@ModificationLevel', '@MinModificationLevel') + SET @command = REPLACE(@command, '@LogSizeSinceLastLogBackup', '@MinLogSizeSinceLastLogBackup') + SET @command = REPLACE(@command, '@TimeSinceLastLogBackup', '@MinTimeSinceLastLogBackup') EXECUTE msdb.dbo.sp_update_jobstep @job_id = @job_id, @step_id = @step_id, @command = @command diff --git a/MaintenanceSolutionAzureSQLDatabase.sql b/MaintenanceSolutionAzureSQLDatabase.sql new file mode 100644 index 00000000..2477f0b6 --- /dev/null +++ b/MaintenanceSolutionAzureSQLDatabase.sql @@ -0,0 +1,5453 @@ +/* + +SQL Server Maintenance Solution - Azure SQL Database + +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: 2026-08-23 14:46:45 + +You can contact me by e-mail at ola@hallengren.com. + +Ola Hallengren +https://ola.hallengren.com + +*/ + +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 +EXEC 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, +@EncryptionKey nvarchar(max) = NULL, +@EncryptionKeyPlaceholder nvarchar(max) = 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: 2026-08-23 14:46:45 //-- + ---------------------------------------------------------------------------------------------------- + + 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 NOT NULL) + + 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) + + DECLARE @CommandMasked nvarchar(max) + + ---------------------------------------------------------------------------------------------------- + --// Check core requirements //-- + ---------------------------------------------------------------------------------------------------- + + IF NOT (SELECT uses_ansi_nulls FROM sys.sql_modules WHERE [object_id] = @@PROCID) = 1 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('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]) + VALUES('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]) + VALUES('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]) + VALUES('The value for the parameter @DatabaseContext is not supported. Specify the name of an existing database.', 16, 1) + END + + IF @Command IS NULL OR @Command = '' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Command is not supported. The value cannot be NULL or empty.', 16, 1) + END + + IF @CommandType IS NULL OR @CommandType = '' OR LEN(@CommandType) > 60 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @CommandType is not supported. The value cannot be NULL or empty, and the maximum length is 60 characters.', 16, 1) + END + + IF @Mode NOT IN(1,2) OR @Mode IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Mode is not supported. Supported values are 1 and 2.', 16, 1) + END + + IF (@EncryptionKey IS NULL AND @EncryptionKeyPlaceholder IS NOT NULL) OR (@EncryptionKey IS NOT NULL AND @EncryptionKeyPlaceholder IS NULL) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameters @EncryptionKey and @EncryptionKeyPlaceholder must be specified together.', 16, 1) + END + + IF @LockMessageSeverity NOT IN(10,16) OR @LockMessageSeverity IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @LockMessageSeverity is not supported. Supported values are 10 and 16.', 16, 1) + END + + IF LEN(@ExecuteAsUser) > 128 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @ExecuteAsUser is not supported. The maximum length is 128 characters.', 16, 1) + END + + IF @LogToTable NOT IN('Y','N') OR @LogToTable IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @LogToTable is not supported. Supported values are ''Y'' and ''N''.', 16, 1) + END + + IF @Execute NOT IN('Y','N') OR @Execute IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Execute is not supported. Supported values are ''Y'' and ''N''.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + --// Raise errors //-- + ---------------------------------------------------------------------------------------------------- + + DECLARE ErrorCursor CURSOR LOCAL 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 + + ---------------------------------------------------------------------------------------------------- + --// Mask encryption key //-- + ---------------------------------------------------------------------------------------------------- + + SET @CommandMasked = CASE WHEN @EncryptionKeyPlaceholder IS NULL THEN @Command ELSE REPLACE(@Command,@EncryptionKeyPlaceholder,'********') END + + SET @Command = CASE WHEN @EncryptionKeyPlaceholder IS NULL THEN @Command ELSE REPLACE(@Command,@EncryptionKeyPlaceholder,REPLACE(ISNULL(@EncryptionKey,''),'''','''''')) END + + ---------------------------------------------------------------------------------------------------- + --// Log initial information //-- + ---------------------------------------------------------------------------------------------------- + + SET @StartTime = SYSDATETIME() + + SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar(max),@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: ' + @CommandMasked + 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, @CommandMasked, @StartTime) + + SET @ID = SCOPE_IDENTITY() + END + + ---------------------------------------------------------------------------------------------------- + --// 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(max)) + ', ' + ISNULL(ERROR_MESSAGE(),'') + SET @Severity = CASE WHEN ERROR_NUMBER() IN(1205, 1222, 5245) THEN @LockMessageSeverity ELSE ERROR_SEVERITY() END + RAISERROR('%s',@Severity,1,@ErrorMessage) WITH NOWAIT + + IF NOT (ERROR_NUMBER() IN(1205, 1222, 5245) 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(max)) + '.' ELSE '' END + CONVERT(nvarchar(max),DATEADD(SECOND,DATEDIFF(SECOND,@StartTime,@EndTime),'1900-01-01'),108) + RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT + + SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar(max),@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].[DatabaseIntegrityCheck]') AND type in (N'P', N'PC')) +BEGIN +EXEC 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: 2026-08-23 14:46:45 //-- + ---------------------------------------------------------------------------------------------------- + + 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 TABLE (ID int IDENTITY PRIMARY KEY, + [Name] nvarchar(max) NOT NULL, + ValueNvarchar nvarchar(max), + ValueInt int, + ValueDatetime datetime2) + + DECLARE @ParametersString nvarchar(max) + DECLARE @CurrentParameterName nvarchar(max) + DECLARE @CurrentParameterValueNvarchar nvarchar(max) + DECLARE @CurrentParameterValueInt int + DECLARE @CurrentParameterValueDatetime datetime2 + DECLARE @CurrentParameterDelimiter nvarchar(max) + DECLARE @CurrentParameterMessage nvarchar(max) + + DECLARE @HostPlatform nvarchar(max) + DECLARE @ContainedAvailabilityGroupID uniqueidentifier + DECLARE @ContainedAvailabilityGroupListenerConnection bit + DECLARE @IsSysadmin bit = IS_SRVROLEMEMBER('sysadmin') + + 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 @CurrentAvailabilityGroupReplicaID 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 @CurrentSecondaryRoleAllowConnections nvarchar(max) + DECLARE @CurrentIsPreferredBackupReplica bit + DECLARE @CurrentDistributedAvailabilityGroup nvarchar(max) + DECLARE @CurrentDistributedAvailabilityGroupReplicaID uniqueidentifier + DECLARE @CurrentDistributedAvailabilityGroupRole nvarchar(max) + 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 NOT NULL) + + DECLARE @CurrentMessage nvarchar(max) + DECLARE @CurrentSeverity int + DECLARE @CurrentState int + + DECLARE @tmpDatabases TABLE (ID int IDENTITY, + DatabaseName nvarchar(128), + DatabaseType nvarchar(1), + AvailabilityGroup bit, + [Snapshot] bit, + StartPosition int, + LastCommandTime datetime2, + DatabaseSize bigint, + LastGoodCheckDbTime datetime2, + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) + + DECLARE @tmpAvailabilityGroups TABLE (ID int IDENTITY PRIMARY KEY, + AvailabilityGroupName nvarchar(128), + StartPosition int, + Selected bit DEFAULT 0) + + DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(128), + AvailabilityGroupName nvarchar(128)) + + DECLARE @tmpFileGroups TABLE (ID int IDENTITY, + FileGroupID int, + FileGroupName nvarchar(128), + StartPosition int, + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) + + DECLARE @tmpObjects TABLE (ID int IDENTITY, + SchemaID int, + SchemaName nvarchar(128), + ObjectID int, + ObjectName nvarchar(128), + ObjectType nvarchar(2), + StartPosition int, + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) + + DECLARE @SelectedDatabases TABLE (DatabaseName nvarchar(max), + DatabaseType nvarchar(1), + AvailabilityGroup bit, + 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 @ProductVersion nvarchar(max) = CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + DECLARE @ProductUpdateType nvarchar(max) = CAST(SERVERPROPERTY('ProductUpdateType') AS nvarchar(max)) + DECLARE @EngineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int) + DECLARE @Edition nvarchar(max) = CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + DECLARE @IsHadrEnabled bit = CAST(SERVERPROPERTY('IsHadrEnabled') AS bit) + DECLARE @IsClustered bit = CAST(SERVERPROPERTY('IsClustered') AS bit) + DECLARE @ServerName nvarchar(max) = CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) + + DECLARE @Version numeric(18,10) = CAST(PARSENAME(@ProductVersion,4) + '.' + PARSENAME(@ProductVersion,3) + PARSENAME(@ProductVersion,2) AS numeric(18,10)) + + IF @EngineEdition = 8 AND @ProductVersion = '12.0.2000.8' AND @ProductUpdateType = 'CU' + BEGIN + SET @Version = 16.01000 + END + + IF @EngineEdition <> 5 + BEGIN + SELECT @HostPlatform = host_platform + FROM sys.dm_os_host_info + END + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) + BEGIN + SET @CurrentCommand = 'SELECT @ParamContainedAvailabilityGroupID = contained_availability_group_id FROM sys.dm_exec_sessions WHERE session_id = @@SPID' + + EXECUTE sp_executesql @stmt = @CurrentCommand, @params = N'@ParamContainedAvailabilityGroupID uniqueidentifier OUTPUT', @ParamContainedAvailabilityGroupID = @ContainedAvailabilityGroupID OUTPUT + END + + SET @ContainedAvailabilityGroupListenerConnection = CASE WHEN @ContainedAvailabilityGroupID IS NOT NULL THEN 1 ELSE 0 END + + DECLARE @AmazonRDS bit = CASE WHEN @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 //-- + ---------------------------------------------------------------------------------------------------- + + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Databases', @Databases) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@CheckCommands', @CheckCommands) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@PhysicalOnly', @PhysicalOnly) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataPurity', @DataPurity) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@NoIndex', @NoIndex) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ExtendedLogicalChecks', @ExtendedLogicalChecks) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@NoInformationalMessages', @NoInformationalMessages) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@TabLock', @TabLock) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FileGroups', @FileGroups) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Objects', @Objects) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MaxDOP', @MaxDOP) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroups', @AvailabilityGroups) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroupReplicas', @AvailabilityGroupReplicas) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Updateability', @Updateability) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@TimeLimit', @TimeLimit) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@LockTimeout', @LockTimeout) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@LockMessageSeverity', @LockMessageSeverity) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StringDelimiter', @StringDelimiter) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabaseOrder', @DatabaseOrder) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabasesInParallel', @DatabasesInParallel) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@LogToTable', @LogToTable) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Execute', @Execute) + + SELECT @ParametersString = STRING_AGG(CAST([Name] + ' = ' + CASE WHEN ValueNvarchar IS NOT NULL THEN '''' + REPLACE(ValueNvarchar,'''','''''') + '''' WHEN ValueInt IS NOT NULL THEN CAST(ValueInt AS nvarchar(max)) WHEN ValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), ValueDatetime, 21) + '''' ELSE 'NULL' END AS nvarchar(max)), ', ') WITHIN GROUP (ORDER BY [ID] ASC) + FROM @Parameters + + SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar(max),@StartTime,120) + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'Server: ' + @ServerName + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'Version: ' + @ProductVersion + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'Edition: ' + @Edition + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + IF @EngineEdition = 8 + BEGIN + SET @StartMessage = 'Update type: ' + @ProductUpdateType + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @EngineEdition <> 5 + BEGIN + SET @StartMessage = 'Platform: ' + ISNULL(@HostPlatform, 'N/A') + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) + BEGIN + SET @StartMessage = 'Contained availability group connection: ' + CASE WHEN @ContainedAvailabilityGroupListenerConnection = 1 THEN 'Yes' WHEN @ContainedAvailabilityGroupListenerConnection = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @EngineEdition <> 5 + BEGIN + SET @StartMessage = 'Is sysadmin: ' + CASE WHEN @IsSysadmin = 1 THEN 'Yes' WHEN @IsSysadmin = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + SET @StartMessage = 'Database: ' + QUOTENAME(DB_NAME()) + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'Procedure: ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + 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 + + SET @StartMessage = 'Command:' + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'EXECUTE ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + DECLARE ParameterCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Name], ValueNvarchar, ValueInt, ValueDatetime, CASE WHEN [ID] = MAX([ID]) OVER() THEN '' ELSE ',' END FROM @Parameters ORDER BY [ID] ASC + + OPEN ParameterCursor + + FETCH ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + + WHILE @@FETCH_STATUS = 0 + BEGIN + SET @CurrentParameterMessage = @CurrentParameterName + ' = ' + CASE WHEN @CurrentParameterValueNvarchar IS NOT NULL THEN '''' + REPLACE(@CurrentParameterValueNvarchar,'''','''''') + '''' WHEN @CurrentParameterValueInt IS NOT NULL THEN CAST(@CurrentParameterValueInt AS nvarchar(max)) WHEN @CurrentParameterValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), @CurrentParameterValueDatetime, 21) + '''' ELSE 'NULL' END + @CurrentParameterDelimiter + + RAISERROR('%s',10,1,@CurrentParameterMessage) WITH NOWAIT + + FETCH NEXT FROM ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + END + + CLOSE ParameterCursor + + DEALLOCATE ParameterCursor + + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + + ---------------------------------------------------------------------------------------------------- + --// Check core requirements //-- + ---------------------------------------------------------------------------------------------------- + + IF NOT (SELECT uses_ansi_nulls FROM sys.sql_modules WHERE [object_id] = @@PROCID) = 1 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('ANSI_NULLS has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-integrity-check.html.', 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]) + VALUES('QUOTED_IDENTIFIER has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-integrity-check.html.', 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]) + VALUES('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]) + VALUES('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]) + VALUES('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]) + VALUES('The table Queue is missing. It is required when @DatabasesInParallel = ''Y''. 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]) + VALUES('The table QueueDatabase is missing. It is required when @DatabasesInParallel = ''Y''. Download https://ola.hallengren.com/scripts/QueueDatabase.sql.', 16, 1) + END + + IF @@TRANCOUNT <> 0 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The stored procedure cannot be executed inside a transaction. The transaction count (@@TRANCOUNT) has to be 0. See https://ola.hallengren.com/sql-server-integrity-check.html.', 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 @IsHadrEnabled = 1 + BEGIN + INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName) + SELECT name AS AvailabilityGroupName + 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]) + 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] + 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(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(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(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]) + VALUES('The value for the parameter @Databases is not supported. The value could not be parsed into a list of databases. See https://ola.hallengren.com/sql-server-integrity-check.html#Databases.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + --// Select availability groups //-- + ---------------------------------------------------------------------------------------------------- + + IF @AvailabilityGroups IS NOT NULL AND @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(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(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(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 = '')) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @AvailabilityGroups is not supported. The value could not be parsed into a list of availability groups. See https://ola.hallengren.com/sql-server-integrity-check.html#AvailabilityGroups.', 16, 1) + END + + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 0 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @AvailabilityGroups can only be used when availability groups are enabled on the instance. See https://ola.hallengren.com/sql-server-integrity-check.html#AvailabilityGroups.', 16, 1) + END + + IF (@Databases IS NULL AND @AvailabilityGroups IS NULL) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('You need to specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) + END + + IF (@Databases IS NOT NULL AND @AvailabilityGroups IS NOT NULL) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('You can only specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) + 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]) + VALUES('The value for the parameter @CheckCommands is not supported. Supported values are CHECKDB, CHECKFILEGROUP, CHECKALLOC, CHECKTABLE and CHECKCATALOG. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 16, 1) + END + + IF EXISTS (SELECT * FROM @SelectedCheckCommands GROUP BY CheckCommand HAVING COUNT(*) > 1) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @CheckCommands is not supported. The same check command has been specified more than once. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 16, 1) + END + + IF NOT EXISTS (SELECT * FROM @SelectedCheckCommands) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @CheckCommands is not supported. The value cannot be NULL or empty. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 16, 1) + 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]) + VALUES('The value for the parameter @CheckCommands is not supported. CHECKDB cannot be combined with CHECKFILEGROUP, CHECKALLOC, CHECKTABLE or CHECKCATALOG. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 16, 1) + 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]) + VALUES('The value for the parameter @CheckCommands is not supported. CHECKFILEGROUP cannot be combined with CHECKALLOC or CHECKTABLE. See https://ola.hallengren.com/sql-server-integrity-check.html#CheckCommands.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @PhysicalOnly NOT IN ('Y','N') OR @PhysicalOnly IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @PhysicalOnly is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#PhysicalOnly.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @DataPurity NOT IN ('Y','N') OR @DataPurity IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DataPurity is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#DataPurity.', 16, 1) + END + + IF @PhysicalOnly = 'Y' AND @DataPurity = 'Y' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameters @PhysicalOnly and @DataPurity cannot be used together. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @NoIndex NOT IN ('Y','N') OR @NoIndex IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @NoIndex is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#NoIndex.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @ExtendedLogicalChecks NOT IN ('Y','N') OR @ExtendedLogicalChecks IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @ExtendedLogicalChecks is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#ExtendedLogicalChecks.', 16, 1) + END + + IF @PhysicalOnly = 'Y' AND @ExtendedLogicalChecks = 'Y' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameters @PhysicalOnly and @ExtendedLogicalChecks cannot be used together. See https://ola.hallengren.com/sql-server-integrity-check.html.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @NoInformationalMessages NOT IN ('Y','N') OR @NoInformationalMessages IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @NoInformationalMessages is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#NoInformationalMessages.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @TabLock NOT IN ('Y','N') OR @TabLock IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @TabLock is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#TabLock.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS(SELECT * FROM @SelectedFileGroups WHERE DatabaseName IS NULL OR FileGroupName IS NULL) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @FileGroups is not supported. The value contains one or more items that could not be parsed. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 16, 1) + END + + IF @FileGroups IS NOT NULL AND NOT EXISTS(SELECT * FROM @SelectedFileGroups) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @FileGroups is not supported. The value could not be parsed into a list of filegroups. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 16, 1) + END + + IF @FileGroups IS NOT NULL AND NOT EXISTS (SELECT * FROM @SelectedCheckCommands WHERE CheckCommand = 'CHECKFILEGROUP') + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @FileGroups can only be used together with @CheckCommands = ''CHECKFILEGROUP''. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 16, 1) + 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]) + VALUES('The value for the parameter @Objects is not supported. The value contains one or more items that could not be parsed. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 16, 1) + END + + IF (@Objects IS NOT NULL AND NOT EXISTS(SELECT * FROM @SelectedObjects)) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Objects is not supported. The value could not be parsed into a list of objects. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 16, 1) + END + + IF (@Objects IS NOT NULL AND NOT EXISTS (SELECT * FROM @SelectedCheckCommands WHERE CheckCommand = 'CHECKTABLE')) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @Objects can only be used together with @CheckCommands = ''CHECKTABLE''. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @MaxDOP < 0 OR @MaxDOP > 64 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MaxDOP is not supported. The value has to be between 0 and 64. See https://ola.hallengren.com/sql-server-integrity-check.html#MaxDOP.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @AvailabilityGroupReplicas NOT IN('ALL','PRIMARY','SECONDARY','PREFERRED_BACKUP_REPLICA') OR @AvailabilityGroupReplicas IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @AvailabilityGroupReplicas is not supported. Supported values are ALL, PRIMARY, SECONDARY and PREFERRED_BACKUP_REPLICA. See https://ola.hallengren.com/sql-server-integrity-check.html#AvailabilityGroupReplicas.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @Updateability NOT IN('READ_ONLY','READ_WRITE','ALL') OR @Updateability IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Updateability is not supported. Supported values are ALL, READ_ONLY and READ_WRITE. See https://ola.hallengren.com/sql-server-integrity-check.html#Updateability.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @TimeLimit < 0 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @TimeLimit is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-integrity-check.html#TimeLimit.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @LockTimeout < 0 OR @LockTimeout > 86400 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @LockTimeout is not supported. The value has to be between 0 and 86400. See https://ola.hallengren.com/sql-server-integrity-check.html#LockTimeout.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @LockMessageSeverity NOT IN(10, 16) OR @LockMessageSeverity IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @LockMessageSeverity is not supported. Supported values are 10 and 16. See https://ola.hallengren.com/sql-server-integrity-check.html#LockMessageSeverity.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) <> 1 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @StringDelimiter is not supported. The value has to be exactly one character. See https://ola.hallengren.com/sql-server-integrity-check.html#StringDelimiter.', 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]) + VALUES('The value for the parameter @DatabaseOrder is not supported. Supported values are 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 and REPLICA_LAST_GOOD_CHECK_DESC. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 16, 1) + END + + IF @DatabaseOrder IN('DATABASE_LAST_GOOD_CHECK_ASC','DATABASE_LAST_GOOD_CHECK_DESC') AND NOT (@Version >= 14.03029 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DatabaseOrder is not supported. DATABASEPROPERTYEX(''DatabaseName'', ''LastGoodCheckDbTime'') is not available in this version of SQL Server. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 16, 1) + END + + IF @DatabaseOrder IN('REPLICA_LAST_GOOD_CHECK_ASC','REPLICA_LAST_GOOD_CHECK_DESC') AND @LogToTable = 'N' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DatabaseOrder is not supported. You need to provide the parameter @LogToTable = ''Y''. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 16, 1) + 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]) + VALUES('The value for the parameter @DatabaseOrder is not supported. You need to provide the parameter @CheckCommands = ''CHECKDB''. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 16, 1) + END + + IF @DatabaseOrder IS NOT NULL AND @EngineEdition = 5 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @DatabaseOrder is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabaseOrder.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @DatabasesInParallel NOT IN('Y','N') OR @DatabasesInParallel IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DatabasesInParallel is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabasesInParallel.', 16, 1) + END + + IF @DatabasesInParallel = 'Y' AND @EngineEdition = 5 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @DatabasesInParallel is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-integrity-check.html#DatabasesInParallel.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @LogToTable NOT IN('Y','N') OR @LogToTable IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @LogToTable is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#LogToTable.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @Execute NOT IN('Y','N') OR @Execute IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Execute is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-integrity-check.html#Execute.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + --// Check that selected databases and availability groups exist //-- + ---------------------------------------------------------------------------------------------------- + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) + FROM @SelectedDatabases + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) + + IF @ErrorMessage IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#Databases.', 10, 1) + END + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) + FROM @SelectedFileGroups + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) + + IF @ErrorMessage IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The following databases in the @FileGroups parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 10, 1) + END + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) + FROM @SelectedObjects + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) + + IF @ErrorMessage IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The following databases in the @Objects parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 10, 1) + END + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(AvailabilityGroupName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY AvailabilityGroupName ASC) + FROM @SelectedAvailabilityGroups + WHERE AvailabilityGroupName NOT LIKE '%[%]%' + AND AvailabilityGroupName NOT IN (SELECT AvailabilityGroupName FROM @tmpAvailabilityGroups) + + IF @ErrorMessage IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The following availability groups do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#AvailabilityGroups.', 10, 1) + END + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) + 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 @ErrorMessage IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The following databases have been selected in the @FileGroups parameter, but not in the @Databases or @AvailabilityGroups parameters: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#FileGroups.', 10, 1) + END + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) + 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 @ErrorMessage IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The following databases have been selected in the @Objects parameter, but not in the @Databases or @AvailabilityGroups parameters: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-integrity-check.html#Objects.', 10, 1) + END + + ---------------------------------------------------------------------------------------------------- + --// Check @@SERVERNAME //-- + ---------------------------------------------------------------------------------------------------- + + IF UPPER(@@SERVERNAME) <> UPPER(@ServerName) AND @IsHadrEnabled = 1 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The @@SERVERNAME does not match SERVERPROPERTY(''ServerName''). See ' + CASE WHEN @IsClustered = 0 THEN 'https://learn.microsoft.com/en-us/sql/database-engine/install-windows/rename-a-computer-that-hosts-a-stand-alone-instance-of-sql-server' WHEN @IsClustered = 1 THEN 'https://learn.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 LOCAL 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] = @ParametersString + + IF @QueueID IS NULL + BEGIN + BEGIN TRANSACTION + + SELECT @QueueID = QueueID + FROM dbo.[Queue] WITH (UPDLOCK, HOLDLOCK) + WHERE SchemaName = @SchemaName + AND ObjectName = @ObjectName + AND [Parameters] = @ParametersString + + IF @QueueID IS NULL + BEGIN + INSERT INTO dbo.[Queue] (SchemaName, ObjectName, [Parameters]) + VALUES(@SchemaName, @ObjectName, @ParametersString) + + 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 COLLATE DATABASE_DEFAULT = 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 COLLATE DATABASE_DEFAULT AND Selected = 1) + + UPDATE QueueDatabase + SET DatabaseOrder = tmpDatabases.[Order] + FROM dbo.QueueDatabase QueueDatabase + INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT = 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(max)) + ', ' + 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 -- 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 + 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(max),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 @IsHadrEnabled = 1 + BEGIN + SELECT @CurrentAvailabilityGroupReplicaID = 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 = @CurrentAvailabilityGroupReplicaID + + IF @CurrentAvailabilityGroupReplicaID IS NULL AND @CurrentAvailabilityGroupID IS NULL AND @Version >= 16 + BEGIN + SET @CurrentAvailabilityGroupID = @ContainedAvailabilityGroupID + + SELECT @CurrentAvailabilityGroupReplicaID = replica_id + FROM sys.dm_hadr_availability_replica_states + WHERE group_id = @CurrentAvailabilityGroupID + AND is_local = 1 + END + + SELECT @CurrentSecondaryRoleAllowConnections = secondary_role_allow_connections_desc + FROM sys.availability_replicas + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + + SELECT @CurrentAvailabilityGroupRole = role_desc + FROM sys.dm_hadr_availability_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND database_id = DB_ID(@CurrentDatabaseName) + + IF @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState IS NULL AND @ContainedAvailabilityGroupListenerConnection = 1 + BEGIN + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND DB_NAME(database_id) = @CurrentDatabaseName + END + + SELECT @CurrentAvailabilityGroup = [name], + @CurrentAvailabilityGroupBackupPreference = UPPER(automated_backup_preference_desc) + FROM sys.availability_groups + WHERE group_id = @CurrentAvailabilityGroupID + END + + IF @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 @IsHadrEnabled = 1 AND @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SELECT @CurrentDistributedAvailabilityGroup = availability_groups.[name], + @CurrentDistributedAvailabilityGroupReplicaID = availability_replicas.replica_id + FROM sys.availability_groups availability_groups + INNER JOIN sys.availability_replicas availability_replicas ON availability_groups.group_id = availability_replicas.group_id + INNER JOIN sys.availability_groups availability_groups_local ON availability_replicas.replica_server_name = availability_groups_local.[name] + WHERE availability_groups.is_distributed = 1 + AND availability_groups_local.group_id = @CurrentAvailabilityGroupID + + SELECT @CurrentDistributedAvailabilityGroupRole = dm_hadr_availability_replica_states.role_desc + FROM sys.dm_hadr_availability_replica_states dm_hadr_availability_replica_states + WHERE dm_hadr_availability_replica_states.replica_id = @CurrentDistributedAvailabilityGroupReplicaID + END + + IF @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 + + 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 + + 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 @CurrentDistributedAvailabilityGroup IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Distributed availability group: ' + ISNULL(@CurrentDistributedAvailabilityGroup,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Distributed availability group role: ' + ISNULL(@CurrentDistributedAvailabilityGroupRole,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Replica role in distributed availability group: ' + CASE WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Global primary' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Forwarder' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in secondary availability group' + WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in primary availability group' ELSE 'N/A' END + 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 @CurrentDatabaseState IN('ONLINE','EMERGENCY') + AND NOT (@CurrentUserAccess = 'SINGLE_USER') + AND (@CurrentAvailabilityGroupRole = 'PRIMARY' OR (@CurrentAvailabilityGroupRole = 'SECONDARY' AND @EngineEdition = 3) OR @CurrentAvailabilityGroup IS NULL) + 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 @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(max)) + '; ' + 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(max)) + + 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 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) + 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(REPLACE(SelectedFileGroups.DatabaseName,'[','[[]'),'_','[_]') AND tmpFileGroups.FileGroupName LIKE REPLACE(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(REPLACE(SelectedFileGroups.DatabaseName,'[','[[]'),'_','[_]') AND tmpFileGroups.FileGroupName LIKE REPLACE(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(REPLACE(SelectedFileGroups.DatabaseName,'[','[[]'),'_','[_]') AND tmpFileGroups.FileGroupName LIKE REPLACE(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 + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)) + '.' + QUOTENAME(FileGroupName), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC, FileGroupName ASC) + FROM @SelectedFileGroups SelectedFileGroups + WHERE DatabaseName = @CurrentDatabaseName + AND FileGroupName NOT LIKE '%[%]%' + AND NOT EXISTS (SELECT * FROM @tmpFileGroups WHERE FileGroupName = SelectedFileGroups.FileGroupName) + + IF @ErrorMessage IS NOT NULL + BEGIN + SET @ErrorMessage = 'The following file groups do not exist: ' + @ErrorMessage + '.' + RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT + 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(max)) + '; ' + 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(max)) + ', ' + 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(max)) + '; ' + 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(max)) + + 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 @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(max)) + '; ' + SET @CurrentCommand += 'DBCC CHECKALLOC (' + QUOTENAME(@CurrentDatabaseName) + IF @NoIndex = 'Y' SET @CurrentCommand += ', NOINDEX' + 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 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) AND (tables.is_memory_optimized = 0 OR is_memory_optimized IS NULL) ORDER BY schemas.name ASC, objects.name ASC' + + INSERT INTO @tmpObjects (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType) + 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(REPLACE(SelectedObjects.DatabaseName,'[','[[]'),'_','[_]') AND tmpObjects.SchemaName LIKE REPLACE(REPLACE(SelectedObjects.SchemaName,'[','[[]'),'_','[_]') AND tmpObjects.ObjectName LIKE REPLACE(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(REPLACE(SelectedObjects.DatabaseName,'[','[[]'),'_','[_]') AND tmpObjects.SchemaName LIKE REPLACE(REPLACE(SelectedObjects.SchemaName,'[','[[]'),'_','[_]') AND tmpObjects.ObjectName LIKE REPLACE(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(REPLACE(SelectedObjects.DatabaseName,'[','[[]'),'_','[_]') AND tmpObjects.SchemaName LIKE REPLACE(REPLACE(SelectedObjects.SchemaName,'[','[[]'),'_','[_]') AND tmpObjects.ObjectName LIKE REPLACE(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 + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)) + '.' + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC, SchemaName ASC, ObjectName ASC) + 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 @ErrorMessage IS NOT NULL + BEGIN + SET @ErrorMessage = 'The following objects do not exist: ' + @ErrorMessage + '.' + RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT + 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(max)) + '; ' + 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) AND (tables.is_memory_optimized = 0 OR is_memory_optimized IS NULL) 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(max)) + ', ' + 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(max)) + '; ' + SET @CurrentCommand += 'DBCC CHECKTABLE (N''' + REPLACE(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(max)) + + 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 (@CurrentAvailabilityGroupRole = 'PRIMARY' OR @CurrentAvailabilityGroupRole IS NULL) AND (@CurrentDistributedAvailabilityGroupRole = 'PRIMARY' OR @CurrentDistributedAvailabilityGroupRole IS NULL) AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + SET @CurrentDatabaseContext = CASE WHEN @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(max)) + '; ' + 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 + SET @ReturnCode = @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 @CurrentAvailabilityGroupReplicaID = NULL + SET @CurrentAvailabilityGroupID = NULL + SET @CurrentAvailabilityGroup = NULL + SET @CurrentAvailabilityGroupRole = NULL + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = NULL + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = NULL + SET @CurrentAvailabilityGroupBackupPreference = NULL + SET @CurrentSecondaryRoleAllowConnections = NULL + SET @CurrentIsPreferredBackupReplica = NULL + SET @CurrentDistributedAvailabilityGroup = NULL + SET @CurrentDistributedAvailabilityGroupReplicaID = NULL + SET @CurrentDistributedAvailabilityGroupRole = NULL + SET @CurrentDatabaseMirroringRole = NULL + + SET @CurrentDatabaseContext = NULL + SET @CurrentCommand = NULL + SET @CurrentCommandOutput = NULL + SET @CurrentCommandType = NULL + + DELETE FROM @tmpFileGroups + DELETE FROM @tmpObjects + + END -- End of database loop + + ---------------------------------------------------------------------------------------------------- + --// Log completing information //-- + ---------------------------------------------------------------------------------------------------- + + Logging: + SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar(max),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 +EXEC 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, +@DataCompression nvarchar(max) = NULL, +@WaitAtLowPriorityMaxDuration int = NULL, +@WaitAtLowPriorityAbortAfterWait nvarchar(max) = NULL, +@Resumable nvarchar(max) = 'N', +@LOBCompaction nvarchar(max) = 'Y', +@UpdateStatistics nvarchar(max) = NULL, +@OnlyModifiedStatistics nvarchar(max) = 'N', +@StatisticsModificationLevel int = NULL, +@StatisticsSample int = NULL, +@StatisticsPersistSample nvarchar(max) = NULL, +@StatisticsResample nvarchar(max) = 'N', +@PartitionLevel nvarchar(max) = 'Y', +@MSShippedObjects nvarchar(max) = 'N', +@Indexes nvarchar(max) = NULL, +@TimeLimit int = NULL, +@Delay int = NULL, +@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: 2026-08-23 14:46:45 //-- + ---------------------------------------------------------------------------------------------------- + + 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 TABLE (ID int IDENTITY PRIMARY KEY, + [Name] nvarchar(max) NOT NULL, + ValueNvarchar nvarchar(max), + ValueInt int, + ValueDatetime datetime2) + + DECLARE @ParametersString nvarchar(max) + DECLARE @CurrentParameterName nvarchar(max) + DECLARE @CurrentParameterValueNvarchar nvarchar(max) + DECLARE @CurrentParameterValueInt int + DECLARE @CurrentParameterValueDatetime datetime2 + DECLARE @CurrentParameterDelimiter nvarchar(max) + DECLARE @CurrentParameterMessage nvarchar(max) + + DECLARE @HostPlatform nvarchar(max) + DECLARE @ContainedAvailabilityGroupID uniqueidentifier + DECLARE @ContainedAvailabilityGroupListenerConnection bit + DECLARE @IsSysadmin bit = IS_SRVROLEMEMBER('sysadmin') + + 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 @CurrentDatabaseHasReadOnlyFileGroup bit + DECLARE @CurrentAvailabilityGroupReplicaID uniqueidentifier + DECLARE @CurrentAvailabilityGroupID uniqueidentifier + DECLARE @CurrentAvailabilityGroup nvarchar(max) + DECLARE @CurrentAvailabilityGroupRole nvarchar(max) + DECLARE @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState nvarchar(max) + DECLARE @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth nvarchar(max) + DECLARE @CurrentDistributedAvailabilityGroup nvarchar(max) + DECLARE @CurrentDistributedAvailabilityGroupReplicaID uniqueidentifier + DECLARE @CurrentDistributedAvailabilityGroupRole 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 NOT NULL) + + 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 @CurrentInRowDataPageCount bigint + DECLARE @CurrentAlterIndexCompleted bit + DECLARE @CurrentUpdateStatisticsCompleted bit + DECLARE @CurrentIsPartition bit + DECLARE @CurrentIsLastPartition bit + DECLARE @CurrentIndexExists bit + DECLARE @CurrentStatisticsExists bit + DECLARE @CurrentIsImageText bit + DECLARE @CurrentIsFileStream bit + DECLARE @CurrentHasClusteredColumnstore bit + DECLARE @CurrentIsColumnstoreOrdered bit + DECLARE @CurrentIsComputed bit + DECLARE @CurrentIsClusteredIndexComputed bit + DECLARE @CurrentIsTimestamp bit + DECLARE @CurrentAllowPageLocks bit + DECLARE @CurrentHasFilter bit + DECLARE @CurrentNoRecompute bit + DECLARE @CurrentIsIncremental bit + DECLARE @CurrentObjectHasRows 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 @CurrentStatisticsPersistSample nvarchar(max) + DECLARE @CurrentStatisticsResample nvarchar(max) + DECLARE @CurrentDelay datetime + + DECLARE @tmpDatabases TABLE (ID int IDENTITY, + DatabaseName nvarchar(128), + DatabaseType nvarchar(1), + AvailabilityGroup bit, + StartPosition int, + DatabaseSize bigint, + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + Completed bit DEFAULT 0, + PRIMARY KEY (Selected, Completed, [Order], ID)) + + DECLARE @tmpAvailabilityGroups TABLE (ID int IDENTITY PRIMARY KEY, + AvailabilityGroupName nvarchar(128), + StartPosition int, + Selected bit DEFAULT 0) + + DECLARE @tmpDatabasesAvailabilityGroups TABLE (DatabaseName nvarchar(128), + AvailabilityGroupName nvarchar(128)) + + DECLARE @tmpIndexesStatistics TABLE (ID int IDENTITY, + SchemaID int, + SchemaName nvarchar(128), + ObjectID int, + ObjectName nvarchar(128), + ObjectType nvarchar(2), + IsMemoryOptimized bit, + IndexID int, + IndexName nvarchar(128), + IndexType int, + AllowPageLocks bit, + HasFilter bit, + IsImageText bit, + IsFileStream bit, + HasClusteredColumnstore bit, + IsColumnstoreOrdered bit, + IsComputed bit, + IsClusteredIndexComputed bit, + IsTimestamp bit, + OnReadOnlyFileGroup bit, + ResumableIndexOperation bit, + StatisticsID int, + StatisticsName nvarchar(128), + [NoRecompute] bit, + IsIncremental bit, + PartitionID bigint, + PartitionNumber int, + IsPartition bit, + IsLastPartition bit, + InRowDataPageCount bigint, + StartPosition int, + [Order] int DEFAULT 0, + Selected bit DEFAULT 0, + AlterIndexCompleted bit DEFAULT 0, + UpdateStatisticsCompleted bit DEFAULT 0, + Completed AS CAST(CASE WHEN AlterIndexCompleted = 1 AND UpdateStatisticsCompleted = 1 THEN 1 ELSE 0 END AS bit) PERSISTED, + PRIMARY KEY (Selected, Completed, [Order], ID), + INDEX IX_ObjectID_IndexID_PartitionNumber NONCLUSTERED (ObjectID, IndexID, PartitionNumber), + INDEX IX_ObjectID_StatisticsID_PartitionNumber NONCLUSTERED (ObjectID, StatisticsID, PartitionNumber)) + + DROP TABLE IF EXISTS #SelectedIndexes + + CREATE TABLE #SelectedIndexes (DatabaseName nvarchar(max) COLLATE DATABASE_DEFAULT, + SchemaName nvarchar(max) COLLATE DATABASE_DEFAULT, + ObjectName nvarchar(max) COLLATE DATABASE_DEFAULT, + IndexName nvarchar(max) COLLATE DATABASE_DEFAULT, + StartPosition int, + Selected bit) + + DROP TABLE IF EXISTS #Objects + + CREATE TABLE #Objects (ObjectID int NOT NULL, + SchemaID int, + SchemaName nvarchar(128) COLLATE DATABASE_DEFAULT, + ObjectName nvarchar(128) COLLATE DATABASE_DEFAULT, + ObjectType nvarchar(2) COLLATE DATABASE_DEFAULT, + IsMemoryOptimized bit, + HasClusteredColumnstore bit, + IsClusteredIndexComputed bit, + IsClusteredIndexDisabled bit, + PRIMARY KEY (ObjectID)) + + DROP TABLE IF EXISTS #Indexes + + CREATE TABLE #Indexes (ObjectID int NOT NULL, + IndexID int NOT NULL, + IndexName nvarchar(128) COLLATE DATABASE_DEFAULT, + IndexType int, + DataSpaceID int, + IsPartitioned bit, + AllowPageLocks bit, + HasFilter bit, + IsImageText bit, + IsFileStream bit, + IsColumnstoreOrdered bit, + IsComputed bit, + IsTimestamp bit, + PRIMARY KEY (ObjectID, IndexID)) + + DROP TABLE IF EXISTS #Stats + + CREATE TABLE #Stats (ObjectID int NOT NULL, + StatisticsID int NOT NULL, + StatisticsName nvarchar(128) COLLATE DATABASE_DEFAULT, + [NoRecompute] bit, + IsIncremental bit, + IsIndex bit, + PRIMARY KEY (ObjectID, StatisticsID)) + + DROP TABLE IF EXISTS #ExistingObjects + + CREATE TABLE #ExistingObjects (SchemaName nvarchar(max) COLLATE DATABASE_DEFAULT, + ObjectName nvarchar(max) COLLATE DATABASE_DEFAULT) + + DROP TABLE IF EXISTS #ExistingIndexes + + CREATE TABLE #ExistingIndexes (SchemaName nvarchar(max) COLLATE DATABASE_DEFAULT, + ObjectName nvarchar(max) COLLATE DATABASE_DEFAULT, + IndexName nvarchar(max) COLLATE DATABASE_DEFAULT) + + DECLARE @tmpResumableOperations TABLE (ObjectID int NOT NULL, + IndexID int NOT NULL, + PartitionNumber int) + + DECLARE @SelectedDatabases TABLE (DatabaseName nvarchar(max), + DatabaseType nvarchar(1), + AvailabilityGroup bit, + 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 @PhysicalStats TABLE (ObjectID int, + IndexID int, + PartitionNumber int, + FragmentationLevel float, + PageCount bigint, + PRIMARY KEY (ObjectID, IndexID, PartitionNumber)) + + DECLARE @IncrementalStatsProperties TABLE (ObjectID int, + StatisticsID int, + PartitionNumber int, + [Rows] bigint, + ModificationCounter bigint, + PRIMARY KEY (ObjectID, StatisticsID, PartitionNumber)) + + 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)) + + DECLARE @CurrentUpdateStatisticsWithClauseArguments TABLE (ID int IDENTITY, + Argument nvarchar(max)) + + DECLARE @Error int = 0 + DECLARE @ReturnCode int = 0 + + DECLARE @EmptyLine nvarchar(max) = CHAR(9) + + DECLARE @ProductVersion nvarchar(max) = CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)) + DECLARE @ProductUpdateType nvarchar(max) = CAST(SERVERPROPERTY('ProductUpdateType') AS nvarchar(max)) + DECLARE @EngineEdition int = CAST(SERVERPROPERTY('EngineEdition') AS int) + DECLARE @Edition nvarchar(max) = CAST(SERVERPROPERTY('Edition') AS nvarchar(max)) + DECLARE @IsHadrEnabled bit = CAST(SERVERPROPERTY('IsHadrEnabled') AS bit) + DECLARE @ServerName nvarchar(max) = CAST(SERVERPROPERTY('ServerName') AS nvarchar(max)) + + DECLARE @Collation nvarchar(128) = CAST(DATABASEPROPERTYEX(DB_NAME(),'Collation') AS nvarchar(128)) + + DECLARE @Version numeric(18,10) = CAST(PARSENAME(@ProductVersion,4) + '.' + PARSENAME(@ProductVersion,3) + PARSENAME(@ProductVersion,2) AS numeric(18,10)) + + IF @EngineEdition = 8 AND @ProductVersion = '12.0.2000.8' AND @ProductUpdateType = 'CU' + BEGIN + SET @Version = 16.01000 + END + + IF @EngineEdition <> 5 + BEGIN + SELECT @HostPlatform = host_platform + FROM sys.dm_os_host_info + END + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) + BEGIN + SET @CurrentCommand = 'SELECT @ParamContainedAvailabilityGroupID = contained_availability_group_id FROM sys.dm_exec_sessions WHERE session_id = @@SPID' + + EXECUTE sp_executesql @stmt = @CurrentCommand, @params = N'@ParamContainedAvailabilityGroupID uniqueidentifier OUTPUT', @ParamContainedAvailabilityGroupID = @ContainedAvailabilityGroupID OUTPUT + END + + SET @ContainedAvailabilityGroupListenerConnection = CASE WHEN @ContainedAvailabilityGroupID IS NOT NULL THEN 1 ELSE 0 END + + DECLARE @AmazonRDS bit = CASE WHEN @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 //-- + ---------------------------------------------------------------------------------------------------- + + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Databases', @Databases) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FragmentationLow', @FragmentationLow) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FragmentationMedium', @FragmentationMedium) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@FragmentationHigh', @FragmentationHigh) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@FragmentationLevel1', @FragmentationLevel1) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@FragmentationLevel2', @FragmentationLevel2) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MinNumberOfPages', @MinNumberOfPages) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MaxNumberOfPages', @MaxNumberOfPages) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@SortInTempdb', @SortInTempdb) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@MaxDOP', @MaxDOP) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@FillFactor', @FillFactor) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@PadIndex', @PadIndex) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DataCompression', @DataCompression) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@WaitAtLowPriorityMaxDuration', @WaitAtLowPriorityMaxDuration) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@WaitAtLowPriorityAbortAfterWait', @WaitAtLowPriorityAbortAfterWait) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Resumable', @Resumable) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@LOBCompaction', @LOBCompaction) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@UpdateStatistics', @UpdateStatistics) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@OnlyModifiedStatistics', @OnlyModifiedStatistics) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@StatisticsModificationLevel', @StatisticsModificationLevel) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@StatisticsSample', @StatisticsSample) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StatisticsPersistSample', @StatisticsPersistSample) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StatisticsResample', @StatisticsResample) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@PartitionLevel', @PartitionLevel) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@MSShippedObjects', @MSShippedObjects) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Indexes', @Indexes) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@TimeLimit', @TimeLimit) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@Delay', @Delay) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@AvailabilityGroups', @AvailabilityGroups) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@LockTimeout', @LockTimeout) + INSERT INTO @Parameters ([Name], ValueInt) VALUES('@LockMessageSeverity', @LockMessageSeverity) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@StringDelimiter', @StringDelimiter) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabaseOrder', @DatabaseOrder) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@DatabasesInParallel', @DatabasesInParallel) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@ExecuteAsUser', @ExecuteAsUser) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@LogToTable', @LogToTable) + INSERT INTO @Parameters ([Name], ValueNvarchar) VALUES('@Execute', @Execute) + + SELECT @ParametersString = STRING_AGG(CAST([Name] + ' = ' + CASE WHEN ValueNvarchar IS NOT NULL THEN '''' + REPLACE(ValueNvarchar,'''','''''') + '''' WHEN ValueInt IS NOT NULL THEN CAST(ValueInt AS nvarchar(max)) WHEN ValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), ValueDatetime, 21) + '''' ELSE 'NULL' END AS nvarchar(max)), ', ') WITHIN GROUP (ORDER BY [ID] ASC) + FROM @Parameters + + SET @StartMessage = 'Date and time: ' + CONVERT(nvarchar(max),@StartTime,120) + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'Server: ' + @ServerName + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'Version: ' + @ProductVersion + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'Edition: ' + @Edition + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + IF @EngineEdition = 8 + BEGIN + SET @StartMessage = 'Update type: ' + @ProductUpdateType + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @EngineEdition <> 5 + BEGIN + SET @StartMessage = 'Platform: ' + ISNULL(@HostPlatform, 'N/A') + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @Version >= 16 AND @EngineEdition NOT IN(5, 8) + BEGIN + SET @StartMessage = 'Contained availability group connection: ' + CASE WHEN @ContainedAvailabilityGroupListenerConnection = 1 THEN 'Yes' WHEN @ContainedAvailabilityGroupListenerConnection = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + IF @EngineEdition <> 5 + BEGIN + SET @StartMessage = 'Is sysadmin: ' + CASE WHEN @IsSysadmin = 1 THEN 'Yes' WHEN @IsSysadmin = 0 THEN 'No' ELSE 'N/A' END + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + END + + SET @StartMessage = 'Database: ' + QUOTENAME(DB_NAME()) + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'Procedure: ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + 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 + + SET @StartMessage = 'Command:' + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + SET @StartMessage = 'EXECUTE ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName) + RAISERROR('%s',10,1,@StartMessage) WITH NOWAIT + + DECLARE ParameterCursor CURSOR LOCAL FAST_FORWARD FOR SELECT [Name], ValueNvarchar, ValueInt, ValueDatetime, CASE WHEN [ID] = MAX([ID]) OVER() THEN '' ELSE ',' END FROM @Parameters ORDER BY [ID] ASC + + OPEN ParameterCursor + + FETCH ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + + WHILE @@FETCH_STATUS = 0 + BEGIN + SET @CurrentParameterMessage = @CurrentParameterName + ' = ' + CASE WHEN @CurrentParameterValueNvarchar IS NOT NULL THEN '''' + REPLACE(@CurrentParameterValueNvarchar,'''','''''') + '''' WHEN @CurrentParameterValueInt IS NOT NULL THEN CAST(@CurrentParameterValueInt AS nvarchar(max)) WHEN @CurrentParameterValueDatetime IS NOT NULL THEN '''' + CONVERT(nvarchar(max), @CurrentParameterValueDatetime, 21) + '''' ELSE 'NULL' END + @CurrentParameterDelimiter + + RAISERROR('%s',10,1,@CurrentParameterMessage) WITH NOWAIT + + FETCH NEXT FROM ParameterCursor INTO @CurrentParameterName, @CurrentParameterValueNvarchar, @CurrentParameterValueInt, @CurrentParameterValueDatetime, @CurrentParameterDelimiter + END + + CLOSE ParameterCursor + + DEALLOCATE ParameterCursor + + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + + ---------------------------------------------------------------------------------------------------- + --// Check core requirements //-- + ---------------------------------------------------------------------------------------------------- + + IF NOT (SELECT uses_ansi_nulls FROM sys.sql_modules WHERE [object_id] = @@PROCID) = 1 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('ANSI_NULLS has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 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]) + VALUES('QUOTED_IDENTIFIER has to be set to ON for the stored procedure. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 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]) + VALUES('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]) + VALUES('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]) + VALUES('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]) + VALUES('The table Queue is missing. It is required when @DatabasesInParallel = ''Y''. 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]) + VALUES('The table QueueDatabase is missing. It is required when @DatabasesInParallel = ''Y''. Download https://ola.hallengren.com/scripts/QueueDatabase.sql.', 16, 1) + END + + IF @@TRANCOUNT <> 0 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The stored procedure cannot be executed inside a transaction. The transaction count (@@TRANCOUNT) has to be 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 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 @IsHadrEnabled = 1 + BEGIN + INSERT INTO @tmpAvailabilityGroups (AvailabilityGroupName) + SELECT name AS AvailabilityGroupName + 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) + 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 + 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(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(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(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]) + VALUES('The value for the parameter @Databases is not supported. The value could not be parsed into a list of databases. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Databases.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + --// Select availability groups //-- + ---------------------------------------------------------------------------------------------------- + + IF @AvailabilityGroups IS NOT NULL AND @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(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(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(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 = '')) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @AvailabilityGroups is not supported. The value could not be parsed into a list of availability groups. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#AvailabilityGroups.', 16, 1) + END + + IF @AvailabilityGroups IS NOT NULL AND @IsHadrEnabled = 0 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @AvailabilityGroups can only be used when availability groups are enabled on the instance. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#AvailabilityGroups.', 16, 1) + END + + IF (@Databases IS NULL AND @AvailabilityGroups IS NULL) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('You need to specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) + END + + IF (@Databases IS NOT NULL AND @AvailabilityGroups IS NOT NULL) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('You can only specify one of the parameters @Databases and @AvailabilityGroups. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) + 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) + + INSERT INTO #SelectedIndexes (DatabaseName, SchemaName, ObjectName, IndexName, StartPosition, Selected) + SELECT DatabaseName, SchemaName, ObjectName, IndexName, StartPosition, Selected + FROM @SelectedIndexes + + ---------------------------------------------------------------------------------------------------- + --// 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]) + VALUES('The value for the parameter @FragmentationLow is not supported. Supported values are INDEX_REBUILD_ONLINE, INDEX_REBUILD_OFFLINE and INDEX_REORGANIZE. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLow.', 16, 1) + END + + IF EXISTS (SELECT * FROM @ActionsPreferred WHERE FragmentationGroup = 'Low' GROUP BY [Action] HAVING COUNT(*) > 1) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @FragmentationLow is not supported. The same action has been specified more than once. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLow.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS (SELECT [Action] FROM @ActionsPreferred WHERE FragmentationGroup = 'Medium' AND [Action] NOT IN(SELECT * FROM @Actions)) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @FragmentationMedium is not supported. Supported values are INDEX_REBUILD_ONLINE, INDEX_REBUILD_OFFLINE and INDEX_REORGANIZE. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationMedium.', 16, 1) + END + + IF EXISTS (SELECT * FROM @ActionsPreferred WHERE FragmentationGroup = 'Medium' GROUP BY [Action] HAVING COUNT(*) > 1) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @FragmentationMedium is not supported. The same action has been specified more than once. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationMedium.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF EXISTS (SELECT [Action] FROM @ActionsPreferred WHERE FragmentationGroup = 'High' AND [Action] NOT IN(SELECT * FROM @Actions)) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @FragmentationHigh is not supported. Supported values are INDEX_REBUILD_ONLINE, INDEX_REBUILD_OFFLINE and INDEX_REORGANIZE. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationHigh.', 16, 1) + END + + IF EXISTS (SELECT * FROM @ActionsPreferred WHERE FragmentationGroup = 'High' GROUP BY [Action] HAVING COUNT(*) > 1) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @FragmentationHigh is not supported. The same action has been specified more than once. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationHigh.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @FragmentationLevel1 <= 0 OR @FragmentationLevel1 >= 100 OR @FragmentationLevel1 IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @FragmentationLevel1 is not supported. The value has to be greater than 0 and less than 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLevel1.', 16, 1) + END + + + ---------------------------------------------------------------------------------------------------- + + IF @FragmentationLevel2 <= 0 OR @FragmentationLevel2 >= 100 OR @FragmentationLevel2 IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @FragmentationLevel2 is not supported. The value has to be greater than 0 and less than 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLevel2.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @FragmentationLevel2 <= @FragmentationLevel1 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @FragmentationLevel2 has to be greater than the value for @FragmentationLevel1. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FragmentationLevel2.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @MinNumberOfPages < 0 OR @MinNumberOfPages IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MinNumberOfPages is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MinNumberOfPages.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @MaxNumberOfPages < 0 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MaxNumberOfPages is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MaxNumberOfPages.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @MinNumberOfPages > @MaxNumberOfPages + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MaxNumberOfPages has to be greater than or equal to the value for @MinNumberOfPages. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MaxNumberOfPages.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @SortInTempdb NOT IN('Y','N') OR @SortInTempdb IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @SortInTempdb is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#SortInTempdb.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @MaxDOP < 0 OR @MaxDOP > 64 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MaxDOP is not supported. The value has to be between 0 and 64. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MaxDOP.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @FillFactor <= 0 OR @FillFactor > 100 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @FillFactor is not supported. The value has to be between 1 and 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#FillFactor.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @PadIndex NOT IN('Y','N') + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @PadIndex is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#PadIndex.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @DataCompression NOT IN('NONE', 'PAGE', 'ROW') + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DataCompression is not supported. Supported values are NONE, PAGE and ROW. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DataCompression.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @WaitAtLowPriorityMaxDuration < 0 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @WaitAtLowPriorityMaxDuration is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#WaitAtLowPriorityMaxDuration.', 16, 1) + END + + IF @WaitAtLowPriorityAbortAfterWait = 'SELF' AND @WaitAtLowPriorityMaxDuration = 0 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @WaitAtLowPriorityMaxDuration is not supported. The value has to be greater than 0 when @WaitAtLowPriorityAbortAfterWait = ''SELF''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#WaitAtLowPriorityMaxDuration.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @WaitAtLowPriorityAbortAfterWait NOT IN('NONE','SELF','BLOCKERS') + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @WaitAtLowPriorityAbortAfterWait is not supported. Supported values are NONE, SELF and BLOCKERS. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#WaitAtLowPriorityAbortAfterWait.', 16, 1) + 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]) + VALUES('The parameters @WaitAtLowPriorityMaxDuration and @WaitAtLowPriorityAbortAfterWait can only be used together. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @Resumable NOT IN('Y','N') OR @Resumable IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Resumable is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Resumable.', 16, 1) + END + + IF @Resumable = 'Y' AND @SortInTempdb = 'Y' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('You can only specify one of the parameters @Resumable and @SortInTempdb. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @LOBCompaction NOT IN('Y','N') OR @LOBCompaction IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @LOBCompaction is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#LOBCompaction.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @UpdateStatistics NOT IN('ALL','COLUMNS','INDEX') + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @UpdateStatistics is not supported. Supported values are ALL, COLUMNS and INDEX. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#UpdateStatistics.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @OnlyModifiedStatistics NOT IN('Y','N') OR @OnlyModifiedStatistics IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @OnlyModifiedStatistics is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#OnlyModifiedStatistics.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @StatisticsModificationLevel <= 0 OR @StatisticsModificationLevel > 100 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @StatisticsModificationLevel is not supported. The value has to be greater than 0 and less than or equal to 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsModificationLevel.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @OnlyModifiedStatistics = 'Y' AND @StatisticsModificationLevel IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('You can only specify one of the parameters @OnlyModifiedStatistics and @StatisticsModificationLevel. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @StatisticsSample <= 0 OR @StatisticsSample > 100 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @StatisticsSample is not supported. The value has to be between 1 and 100. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsSample.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @StatisticsPersistSample NOT IN('Y','N') + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @StatisticsPersistSample is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsPersistSample.', 16, 1) + END + + IF @StatisticsPersistSample IS NOT NULL AND @StatisticsSample IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @StatisticsPersistSample can only be used together with @StatisticsSample. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsPersistSample.', 16, 1) + END + + IF @StatisticsPersistSample IS NOT NULL AND @StatisticsResample = 'Y' + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameters @StatisticsPersistSample and @StatisticsResample cannot be used together. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html.', 16, 1) + END + + IF @StatisticsPersistSample IS NOT NULL AND NOT (@Version >= 14.03006 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @StatisticsPersistSample is not supported. PERSIST_SAMPLE_PERCENT is not supported in this version of SQL Server. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsPersistSample.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @StatisticsResample NOT IN('Y','N') OR @StatisticsResample IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @StatisticsResample is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsResample.', 16, 1) + END + + IF @StatisticsResample = 'Y' AND @StatisticsSample IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('Setting @StatisticsResample to ''Y'' cannot be combined with @StatisticsSample. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StatisticsResample.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @PartitionLevel NOT IN('Y','N') OR @PartitionLevel IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @PartitionLevel is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#PartitionLevel.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @MSShippedObjects NOT IN('Y','N') OR @MSShippedObjects IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @MSShippedObjects is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#MSShippedObjects.', 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]) + VALUES('The value for the parameter @Indexes is not supported. The value contains one or more items that could not be parsed. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Indexes.', 16, 1) + END + + IF @Indexes IS NOT NULL AND NOT EXISTS(SELECT * FROM @SelectedIndexes) + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Indexes is not supported. The value could not be parsed into a list of indexes. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Indexes.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @TimeLimit < 0 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @TimeLimit is not supported. The value has to be greater than or equal to 0. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#TimeLimit.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @Delay < 0 OR @Delay >= 86400 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Delay is not supported. The value has to be greater than or equal to 0 and less than 86400. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Delay.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @LockTimeout < 0 OR @LockTimeout > 86400 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @LockTimeout is not supported. The value has to be between 0 and 86400. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#LockTimeout.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @LockMessageSeverity NOT IN(10, 16) OR @LockMessageSeverity IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @LockMessageSeverity is not supported. Supported values are 10 and 16. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#LockMessageSeverity.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @StringDelimiter IS NULL OR LEN(@StringDelimiter) <> 1 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @StringDelimiter is not supported. The value has to be exactly one character. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#StringDelimiter.', 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]) + VALUES('The value for the parameter @DatabaseOrder is not supported. Supported values are DATABASE_NAME_ASC, DATABASE_NAME_DESC, DATABASE_SIZE_ASC and DATABASE_SIZE_DESC. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DatabaseOrder.', 16, 1) + END + + IF @DatabaseOrder IS NOT NULL AND @EngineEdition = 5 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @DatabaseOrder is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DatabaseOrder.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @DatabasesInParallel NOT IN('Y','N') OR @DatabasesInParallel IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @DatabasesInParallel is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DatabasesInParallel.', 16, 1) + END + + IF @DatabasesInParallel = 'Y' AND @EngineEdition = 5 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The parameter @DatabasesInParallel is not supported in Azure SQL Database. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#DatabasesInParallel.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF LEN(@ExecuteAsUser) > 128 + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @ExecuteAsUser is not supported. The maximum length is 128 characters. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#ExecuteAsUser.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @LogToTable NOT IN('Y','N') OR @LogToTable IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @LogToTable is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#LogToTable.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + + IF @Execute NOT IN('Y','N') OR @Execute IS NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The value for the parameter @Execute is not supported. Supported values are ''Y'' and ''N''. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Execute.', 16, 1) + END + + ---------------------------------------------------------------------------------------------------- + --// Check that selected databases and availability groups exist //-- + ---------------------------------------------------------------------------------------------------- + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) + FROM @SelectedDatabases + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) + + IF @ErrorMessage IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The following databases in the @Databases parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Databases.', 10, 1) + END + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) + FROM @SelectedIndexes + WHERE DatabaseName NOT LIKE '%[%]%' + AND DatabaseName NOT IN (SELECT DatabaseName FROM @tmpDatabases) + + IF @ErrorMessage IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The following databases in the @Indexes parameter do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Indexes.', 10, 1) + END + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(AvailabilityGroupName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY AvailabilityGroupName ASC) + FROM @SelectedAvailabilityGroups + WHERE AvailabilityGroupName NOT LIKE '%[%]%' + AND AvailabilityGroupName NOT IN (SELECT AvailabilityGroupName FROM @tmpAvailabilityGroups) + + IF @ErrorMessage IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The following availability groups do not exist: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#AvailabilityGroups.', 10, 1) + END + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC) + 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 @ErrorMessage IS NOT NULL + BEGIN + INSERT INTO @Errors ([Message], Severity, [State]) + VALUES('The following databases have been selected in the @Indexes parameter, but not in the @Databases or @AvailabilityGroups parameters: ' + @ErrorMessage + '. See https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html#Indexes.', 10, 1) + END + + ---------------------------------------------------------------------------------------------------- + --// Raise errors //-- + ---------------------------------------------------------------------------------------------------- + + DECLARE ErrorCursor CURSOR LOCAL 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 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] = @ParametersString + + IF @QueueID IS NULL + BEGIN + BEGIN TRANSACTION + + SELECT @QueueID = QueueID + FROM dbo.[Queue] WITH (UPDLOCK, HOLDLOCK) + WHERE SchemaName = @SchemaName + AND ObjectName = @ObjectName + AND [Parameters] = @ParametersString + + IF @QueueID IS NULL + BEGIN + INSERT INTO dbo.[Queue] (SchemaName, ObjectName, [Parameters]) + VALUES(@SchemaName, @ObjectName, @ParametersString) + + 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 COLLATE DATABASE_DEFAULT = 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 COLLATE DATABASE_DEFAULT AND Selected = 1) + + UPDATE QueueDatabase + SET DatabaseOrder = tmpDatabases.[Order] + FROM dbo.QueueDatabase QueueDatabase + INNER JOIN @tmpDatabases tmpDatabases ON QueueDatabase.DatabaseName COLLATE DATABASE_DEFAULT = 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(max)) + ', ' + 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 -- 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 + 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(max),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 @IsHadrEnabled = 1 + BEGIN + SELECT @CurrentAvailabilityGroupReplicaID = 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 = @CurrentAvailabilityGroupReplicaID + + IF @CurrentAvailabilityGroupReplicaID IS NULL AND @CurrentAvailabilityGroupID IS NULL AND @Version >= 16 + BEGIN + SET @CurrentAvailabilityGroupID = @ContainedAvailabilityGroupID + + SELECT @CurrentAvailabilityGroupReplicaID = replica_id + FROM sys.dm_hadr_availability_replica_states + WHERE group_id = @CurrentAvailabilityGroupID + AND is_local = 1 + END + + SELECT @CurrentAvailabilityGroupRole = role_desc + FROM sys.dm_hadr_availability_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND database_id = DB_ID(@CurrentDatabaseName) + + IF @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState IS NULL AND @ContainedAvailabilityGroupListenerConnection = 1 + BEGIN + SELECT @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = synchronization_state_desc, + @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = synchronization_health_desc + FROM sys.dm_hadr_database_replica_states + WHERE replica_id = @CurrentAvailabilityGroupReplicaID + AND DB_NAME(database_id) = @CurrentDatabaseName + END + + SELECT @CurrentAvailabilityGroup = [name] + FROM sys.availability_groups + WHERE group_id = @CurrentAvailabilityGroupID + END + + IF @IsHadrEnabled = 1 AND @CurrentAvailabilityGroup IS NOT NULL + BEGIN + SELECT @CurrentDistributedAvailabilityGroup = availability_groups.[name], + @CurrentDistributedAvailabilityGroupReplicaID = availability_replicas.replica_id + FROM sys.availability_groups availability_groups + INNER JOIN sys.availability_replicas availability_replicas ON availability_groups.group_id = availability_replicas.group_id + INNER JOIN sys.availability_groups availability_groups_local ON availability_replicas.replica_server_name = availability_groups_local.[name] + WHERE availability_groups.is_distributed = 1 + AND availability_groups_local.group_id = @CurrentAvailabilityGroupID + + SELECT @CurrentDistributedAvailabilityGroupRole = dm_hadr_availability_replica_states.role_desc + FROM sys.dm_hadr_availability_replica_states dm_hadr_availability_replica_states + WHERE dm_hadr_availability_replica_states.replica_id = @CurrentDistributedAvailabilityGroupReplicaID + END + + IF @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 + + 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 + END + + IF @CurrentDistributedAvailabilityGroup IS NOT NULL + BEGIN + SET @DatabaseMessage = 'Distributed availability group: ' + ISNULL(@CurrentDistributedAvailabilityGroup,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Distributed availability group role: ' + ISNULL(@CurrentDistributedAvailabilityGroupRole,'N/A') + RAISERROR('%s',10,1,@DatabaseMessage) WITH NOWAIT + + SET @DatabaseMessage = 'Replica role in distributed availability group: ' + CASE WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Global primary' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'PRIMARY' THEN 'Forwarder' + WHEN @CurrentDistributedAvailabilityGroupRole = 'SECONDARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in secondary availability group' + WHEN @CurrentDistributedAvailabilityGroupRole = 'PRIMARY' AND @CurrentAvailabilityGroupRole = 'SECONDARY' THEN 'Secondary replica in primary availability group' ELSE 'N/A' END + 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 + SET @Error = @@ERROR + SET @ReturnCode = @Error + 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 (@CurrentDistributedAvailabilityGroup IS NOT NULL AND (@CurrentDistributedAvailabilityGroupRole <> 'PRIMARY' OR @CurrentDistributedAvailabilityGroupRole IS NULL)) + AND NOT (@AmazonRDS = 1 AND @CurrentDatabaseName = 'rdsadmin') + AND NOT (@CurrentIsReadOnly = 1) + AND (@CurrentExecuteAsUserExists = 1 OR @CurrentExecuteAsUserExists IS NULL) + BEGIN + + IF (EXISTS(SELECT * FROM @ActionsPreferred) OR @UpdateStatistics IS NOT NULL) AND (SYSDATETIME() < DATEADD(SECOND,@TimeLimit,@StartTime) OR @TimeLimit IS NULL) + BEGIN + -- Select objects + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT objects.[object_id] AS ObjectID' + + ', objects.[schema_id] AS SchemaID' + + ', schemas.[name] AS SchemaName' + + ', objects.[name] AS ObjectName' + + ', RTRIM(objects.[type]) AS ObjectType' + + ', ISNULL(tables.is_memory_optimized, 0) AS IsMemoryOptimized' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') THEN 'CASE WHEN EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = objects.object_id AND [type] = 5) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS HasClusteredColumnstore' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') AND @Resumable = 'Y' THEN '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 = objects.object_id) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsClusteredIndexComputed' + + ', CASE WHEN EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = objects.object_id AND [type] = 1 AND is_disabled = 1) THEN 1 ELSE 0 END AS IsClusteredIndexDisabled' + + ' 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 (tables.is_external = 0 OR tables.is_external IS NULL)' + + CASE WHEN @MSShippedObjects = 'N' THEN ' AND objects.is_ms_shipped = 0' ELSE '' END + IF @Indexes IS NOT NULL AND EXISTS(SELECT * FROM @SelectedIndexes WHERE Selected = 1) AND NOT EXISTS(SELECT * FROM @SelectedIndexes WHERE Selected = 1 AND DatabaseName = '%' AND SchemaName = '%' AND ObjectName = '%') + BEGIN + SET @CurrentCommand += ' AND EXISTS(SELECT * FROM #SelectedIndexes SelectedIndexes WHERE @ParamDatabaseName LIKE REPLACE(REPLACE(SelectedIndexes.DatabaseName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND schemas.[name] LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND objects.[name] LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND SelectedIndexes.Selected = 1)' + END + IF @Indexes IS NOT NULL AND EXISTS(SELECT * FROM @SelectedIndexes WHERE Selected = 0 AND IndexName = '%') + BEGIN + SET @CurrentCommand += ' AND NOT EXISTS(SELECT * FROM #SelectedIndexes SelectedIndexes WHERE @ParamDatabaseName LIKE REPLACE(REPLACE(SelectedIndexes.DatabaseName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND schemas.[name] LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND objects.[name] LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,''['',''[[]''),''_'',''[_]'') COLLATE ' + @Collation + ' AND SelectedIndexes.IndexName = ''%'' AND SelectedIndexes.Selected = 0)' + END + + INSERT INTO #Objects (ObjectID, SchemaID, SchemaName, ObjectName, ObjectType, IsMemoryOptimized, HasClusteredColumnstore, IsClusteredIndexComputed, IsClusteredIndexDisabled) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseName nvarchar(max)', @ParamDatabaseName = @CurrentDatabaseName + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select indexes + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT indexes.[object_id] AS ObjectID' + + ', indexes.index_id AS IndexID' + + ', indexes.[name] AS IndexName' + + ', indexes.[type] AS IndexType' + + ', indexes.data_space_id AS DataSpaceID' + + ', CASE WHEN EXISTS (SELECT * FROM sys.partition_schemes partition_schemes WHERE partition_schemes.data_space_id = indexes.data_space_id) THEN 1 ELSE 0 END AS IsPartitioned' + + ', indexes.allow_page_locks AS AllowPageLocks' + + ', indexes.has_filter AS HasFilter' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') THEN '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] = indexes.object_id AND types.name IN(''image'',''text'',''ntext'')) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsImageText' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') THEN 'CASE WHEN indexes.[type] = 1 AND EXISTS(SELECT * FROM sys.columns columns WHERE columns.[object_id] = indexes.object_id AND columns.is_filestream = 1) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsFileStream' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') AND (@Version >= 16 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) THEN 'CASE WHEN EXISTS(SELECT * FROM sys.index_columns index_columns WHERE index_columns.[object_id] = indexes.[object_id] AND index_columns.index_id = indexes.index_id AND index_columns.column_store_order_ordinal = 1) THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsColumnstoreOrdered' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') AND @Resumable = 'Y' THEN '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 OR index_columns.is_included_column = 1) 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' ELSE 'NULL' END + ' AS IsComputed' + + ', ' + CASE WHEN @EngineEdition IN (3, 5, 8) AND EXISTS(SELECT * FROM @ActionsPreferred WHERE [Action] = 'INDEX_REBUILD_ONLINE') AND @Resumable = 'Y' THEN '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.key_ordinal > 0 OR index_columns.partition_ordinal > 0) AND index_columns.[object_id] = indexes.[object_id] AND index_columns.index_id = indexes.index_id AND types.[name] = ''timestamp'') THEN 1 ELSE 0 END' ELSE 'NULL' END + ' AS IsTimestamp' + + ' FROM sys.indexes indexes' + + ' INNER JOIN #Objects Objects ON indexes.[object_id] = Objects.ObjectID' + + ' AND indexes.[type] IN(1,2,3,4,5,6,7)' + + ' AND indexes.is_disabled = 0' + + ' AND indexes.is_hypothetical = 0' + + INSERT INTO #Indexes (ObjectID, IndexID, IndexName, IndexType, DataSpaceID, IsPartitioned, AllowPageLocks, HasFilter, IsImageText, IsFileStream, IsColumnstoreOrdered, IsComputed, IsTimestamp) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select statistics + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT stats.[object_id] AS ObjectID' + + ', stats.stats_id AS StatisticsID' + + ', stats.name AS StatisticsName' + + ', stats.no_recompute AS NoRecompute' + + ', stats.is_incremental AS IsIncremental' + + ', CASE WHEN EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = stats.[object_id] AND indexes.index_id = stats.stats_id) THEN 1 ELSE 0 END AS IsIndex' + + ' FROM sys.stats stats' + + ' INNER JOIN #Objects Objects ON stats.[object_id] = Objects.ObjectID' + + INSERT INTO #Stats (ObjectID, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, IsIndex) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select paused resumable index operations + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT index_resumable_operations.object_id AS ObjectID' + + ', index_resumable_operations.index_id AS IndexID' + + ', ' + CASE WHEN @PartitionLevel = 'Y' THEN 'index_resumable_operations.partition_number AS PartitionNumber' WHEN @PartitionLevel = 'N' THEN 'NULL AS PartitionNumber' END + + ' FROM sys.index_resumable_operations index_resumable_operations' + + ' WHERE index_resumable_operations.state_desc = ''PAUSED''' + + INSERT INTO @tmpResumableOperations (ObjectID, IndexID, PartitionNumber) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + IF EXISTS(SELECT * FROM @ActionsPreferred) OR @UpdateStatistics IN('ALL','INDEX') + BEGIN + -- Check if there are read-only filegroups in the database + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT @ParamDatabaseHasReadOnlyFileGroup = CASE WHEN EXISTS(SELECT * FROM sys.filegroups filegroups WHERE filegroups.is_read_only = 1) THEN 1 ELSE 0 END' + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseHasReadOnlyFileGroup bit OUTPUT', @ParamDatabaseHasReadOnlyFileGroup = @CurrentDatabaseHasReadOnlyFileGroup OUTPUT + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select clustered, nonclustered and hash indexes + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Indexes.IndexID AS IndexID' + + ', Indexes.IndexName AS IndexName' + + ', Indexes.IndexType AS IndexType' + + ', Indexes.AllowPageLocks AS AllowPageLocks' + + ', Indexes.HasFilter AS HasFilter' + + ', Indexes.IsImageText AS IsImageText' + + ', Indexes.IsFileStream AS IsFileStream' + + ', Objects.HasClusteredColumnstore AS HasClusteredColumnstore' + + ', Indexes.IsColumnstoreOrdered AS IsColumnstoreOrdered' + + ', Indexes.IsComputed AS IsComputed' + + ', Objects.IsClusteredIndexComputed AS IsClusteredIndexComputed' + + ', Indexes.IsTimestamp AS IsTimestamp' + + CASE WHEN @CurrentDatabaseHasReadOnlyFileGroup = 1 THEN ', CASE WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.destination_data_spaces destination_data_spaces ON Indexes.DataSpaceID = 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.ObjectID AND indexes2.[index_id] = Indexes.IndexID' + 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.DataSpaceID = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND Indexes.ObjectID = indexes2.[object_id] AND Indexes.IndexID = indexes2.index_id) THEN 1' + + ' WHEN Indexes.IndexType = 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.ObjectID) THEN 1 ELSE 0 END AS OnReadOnlyFileGroup' ELSE ', 0 AS OnReadOnlyFileGroup' END + + ', 0 AS ResumableIndexOperation' + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ', Stats.StatisticsID AS StatisticsID' ELSE ', NULL AS StatisticsID' END + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ', Stats.StatisticsName AS StatisticsName' ELSE ', NULL AS StatisticsName' END + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ', Stats.[NoRecompute] AS NoRecompute' ELSE ', NULL AS NoRecompute' END + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ', Stats.IsIncremental AS IsIncremental' ELSE ', NULL AS IsIncremental' END + + ', ' + 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 'Indexes.IsPartitioned AS IsPartition' WHEN @PartitionLevel = 'N' THEN '0 AS IsPartition' END + + ', ' + CASE WHEN (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) THEN 'dm_db_partition_stats.in_row_data_page_count AS InRowDataPageCount' ELSE 'NULL AS InRowDataPageCount' END + + ' FROM #Indexes Indexes' + + ' INNER JOIN #Objects Objects ON Indexes.ObjectID = Objects.ObjectID' + + CASE WHEN @UpdateStatistics IN('ALL','INDEX') THEN ' INNER JOIN #Stats Stats ON Indexes.ObjectID = Stats.ObjectID AND Indexes.IndexID = Stats.StatisticsID' ELSE '' END + IF @PartitionLevel = 'Y' + BEGIN + SET @CurrentCommand += ' INNER JOIN sys.partitions partitions ON Indexes.ObjectID = partitions.[object_id] AND Indexes.IndexID = partitions.index_id' + END + IF @PartitionLevel = 'Y' AND (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) + BEGIN + SET @CurrentCommand += ' INNER JOIN sys.dm_db_partition_stats dm_db_partition_stats ON partitions.object_id = dm_db_partition_stats.object_id AND partitions.index_id = dm_db_partition_stats.index_id AND partitions.partition_number = dm_db_partition_stats.partition_number' + END + IF @PartitionLevel = 'N' AND (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) + BEGIN + SET @CurrentCommand += ' INNER JOIN (SELECT object_id, index_id, SUM(in_row_data_page_count) AS in_row_data_page_count FROM sys.dm_db_partition_stats GROUP BY object_id, index_id) dm_db_partition_stats ON Indexes.ObjectID = dm_db_partition_stats.object_id AND Indexes.IndexID = dm_db_partition_stats.index_id' + END + SET @CurrentCommand += ' WHERE Objects.ObjectType IN(''U'',''V'')' + + ' AND Indexes.IndexType IN(1,2,7)' + + CASE WHEN (@UpdateStatistics = 'COLUMNS' OR @UpdateStatistics IS NULL) AND @MinNumberOfPages > 0 THEN ' AND dm_db_partition_stats.in_row_data_page_count >= @ParamMinNumberOfPages' ELSE '' END + + CASE WHEN (@UpdateStatistics = 'COLUMNS' OR @UpdateStatistics IS NULL) AND @MaxNumberOfPages IS NOT NULL THEN ' AND dm_db_partition_stats.in_row_data_page_count <= @ParamMaxNumberOfPages' ELSE '' END + + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, IndexID, IndexName, IndexType, AllowPageLocks, HasFilter, IsImageText, IsFileStream, HasClusteredColumnstore, IsColumnstoreOrdered, IsComputed, IsClusteredIndexComputed, IsTimestamp, OnReadOnlyFileGroup, ResumableIndexOperation, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, PartitionID, PartitionNumber, IsPartition, InRowDataPageCount) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamMinNumberOfPages int, @ParamMaxNumberOfPages int', @ParamMinNumberOfPages = @MinNumberOfPages, @ParamMaxNumberOfPages = @MaxNumberOfPages + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select XML and spatial indexes + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Indexes.IndexID AS IndexID' + + ', Indexes.IndexName AS IndexName' + + ', Indexes.IndexType AS IndexType' + + ', Indexes.AllowPageLocks AS AllowPageLocks' + + ', Indexes.HasFilter AS HasFilter' + + ', Indexes.IsImageText AS IsImageText' + + ', Indexes.IsFileStream AS IsFileStream' + + ', Objects.HasClusteredColumnstore AS HasClusteredColumnstore' + + ', Indexes.IsColumnstoreOrdered AS IsColumnstoreOrdered' + + ', Indexes.IsComputed AS IsComputed' + + ', Objects.IsClusteredIndexComputed AS IsClusteredIndexComputed' + + ', Indexes.IsTimestamp AS IsTimestamp' + + CASE WHEN @CurrentDatabaseHasReadOnlyFileGroup = 1 THEN ', CASE WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.destination_data_spaces destination_data_spaces ON Indexes.DataSpaceID = 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.ObjectID AND indexes2.[index_id] = Indexes.IndexID) THEN 1' + + ' WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.filegroups filegroups ON Indexes.DataSpaceID = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND Indexes.ObjectID = indexes2.[object_id] AND Indexes.IndexID = indexes2.index_id) THEN 1 ELSE 0 END AS OnReadOnlyFileGroup' ELSE ', 0 AS OnReadOnlyFileGroup' END + + ', 0 AS ResumableIndexOperation' + + ', 0 AS IsPartition' + + ' FROM #Indexes Indexes' + + ' INNER JOIN #Objects Objects ON Indexes.ObjectID = Objects.ObjectID' + + ' WHERE Objects.ObjectType = ''U''' + + ' AND Indexes.IndexType IN(3,4)' + + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, IndexID, IndexName, IndexType, AllowPageLocks, HasFilter, IsImageText, IsFileStream, HasClusteredColumnstore, IsColumnstoreOrdered, IsComputed, IsClusteredIndexComputed, IsTimestamp, OnReadOnlyFileGroup, ResumableIndexOperation, IsPartition) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select columnstore indexes + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Indexes.IndexID AS IndexID' + + ', Indexes.IndexName AS IndexName' + + ', Indexes.IndexType AS IndexType' + + ', Indexes.AllowPageLocks AS AllowPageLocks' + + ', Indexes.HasFilter AS HasFilter' + + ', Indexes.IsImageText AS IsImageText' + + ', Indexes.IsFileStream AS IsFileStream' + + ', Objects.HasClusteredColumnstore AS HasClusteredColumnstore' + + ', Indexes.IsColumnstoreOrdered AS IsColumnstoreOrdered' + + ', Indexes.IsComputed AS IsComputed' + + ', Objects.IsClusteredIndexComputed AS IsClusteredIndexComputed' + + ', Indexes.IsTimestamp AS IsTimestamp' + + CASE WHEN @CurrentDatabaseHasReadOnlyFileGroup = 1 THEN ', CASE WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.destination_data_spaces destination_data_spaces ON Indexes.DataSpaceID = 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.ObjectID AND indexes2.[index_id] = Indexes.IndexID' + 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.DataSpaceID = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND Indexes.ObjectID = indexes2.[object_id] AND Indexes.IndexID = indexes2.index_id) THEN 1' + + ' WHEN Indexes.IndexType = 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.ObjectID) THEN 1 ELSE 0 END AS OnReadOnlyFileGroup' ELSE ', 0 AS OnReadOnlyFileGroup' END + + ', 0 AS ResumableIndexOperation' + + ', NULL AS StatisticsID' + + ', NULL AS StatisticsName' + + ', NULL AS NoRecompute' + + ', NULL 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 'Indexes.IsPartitioned AS IsPartition' WHEN @PartitionLevel = 'N' THEN '0 AS IsPartition' END + + ', ' + CASE WHEN (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) THEN 'dm_db_partition_stats.in_row_data_page_count AS InRowDataPageCount' ELSE 'NULL AS InRowDataPageCount' END + + ' FROM #Indexes Indexes' + + ' INNER JOIN #Objects Objects ON Indexes.ObjectID = Objects.ObjectID' + IF @PartitionLevel = 'Y' + BEGIN + SET @CurrentCommand += ' INNER JOIN sys.partitions partitions ON Indexes.ObjectID = partitions.[object_id] AND Indexes.IndexID = partitions.index_id' + END + IF @PartitionLevel = 'Y' AND (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) + BEGIN + SET @CurrentCommand += ' INNER JOIN sys.dm_db_partition_stats dm_db_partition_stats ON partitions.object_id = dm_db_partition_stats.object_id AND partitions.index_id = dm_db_partition_stats.index_id AND partitions.partition_number = dm_db_partition_stats.partition_number' + END + IF @PartitionLevel = 'N' AND (@MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL) + BEGIN + SET @CurrentCommand += ' INNER JOIN (SELECT object_id, index_id, SUM(in_row_data_page_count) AS in_row_data_page_count FROM sys.dm_db_partition_stats GROUP BY object_id, index_id) dm_db_partition_stats ON Indexes.ObjectID = dm_db_partition_stats.object_id AND Indexes.IndexID = dm_db_partition_stats.index_id' + END + SET @CurrentCommand += ' WHERE Objects.ObjectType = ''U''' + + ' AND Indexes.IndexType IN(5,6)' + + CASE WHEN (@UpdateStatistics = 'COLUMNS' OR @UpdateStatistics IS NULL) AND @MinNumberOfPages > 0 THEN ' AND dm_db_partition_stats.in_row_data_page_count >= @ParamMinNumberOfPages' ELSE '' END + + CASE WHEN (@UpdateStatistics = 'COLUMNS' OR @UpdateStatistics IS NULL) AND @MaxNumberOfPages IS NOT NULL THEN ' AND dm_db_partition_stats.in_row_data_page_count <= @ParamMaxNumberOfPages' ELSE '' END + + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, IndexID, IndexName, IndexType, AllowPageLocks, HasFilter, IsImageText, IsFileStream, HasClusteredColumnstore, IsColumnstoreOrdered, IsComputed, IsClusteredIndexComputed, IsTimestamp, OnReadOnlyFileGroup, ResumableIndexOperation, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, PartitionID, PartitionNumber, IsPartition, InRowDataPageCount) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamMinNumberOfPages int, @ParamMaxNumberOfPages int', @ParamMinNumberOfPages = @MinNumberOfPages, @ParamMaxNumberOfPages = @MaxNumberOfPages + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + END + + IF @UpdateStatistics IN('ALL','COLUMNS') + BEGIN + -- Select non-incremental column level statistics + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Stats.StatisticsID AS StatisticsID' + + ', Stats.StatisticsName AS StatisticsName' + + ', Stats.[NoRecompute] AS NoRecompute' + + ', Stats.IsIncremental AS IsIncremental' + + ', NULL AS PartitionNumber' + + ', 0 AS IsPartition' + + ' FROM #Stats Stats' + + ' INNER JOIN #Objects Objects ON Stats.ObjectID = Objects.ObjectID' + + ' WHERE Stats.IsIndex = 0' + + ' AND Stats.IsIncremental = 0' + + ' AND Objects.IsClusteredIndexDisabled = 0' + + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, PartitionNumber, IsPartition) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + + -- Select incremental column level statistics + SET @CurrentCommand = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;' + + ' SELECT Objects.SchemaID AS SchemaID' + + ', Objects.SchemaName AS SchemaName' + + ', Objects.ObjectID AS ObjectID' + + ', Objects.ObjectName AS ObjectName' + + ', Objects.ObjectType AS ObjectType' + + ', Objects.IsMemoryOptimized AS IsMemoryOptimized' + + ', Stats.StatisticsID AS StatisticsID' + + ', Stats.StatisticsName AS StatisticsName' + + ', Stats.[NoRecompute] AS NoRecompute' + + ', Stats.IsIncremental AS IsIncremental' + + ', ' + CASE WHEN @PartitionLevel = 'Y' THEN 'partitions.partition_number' ELSE 'NULL' END + ' AS PartitionNumber' + + ', ' + CASE WHEN @PartitionLevel = 'Y' THEN '1' ELSE '0' END + ' AS IsPartition' + + ' FROM #Stats Stats' + + ' INNER JOIN #Objects Objects ON Stats.ObjectID = Objects.ObjectID' + IF @PartitionLevel = 'Y' + BEGIN + SET @CurrentCommand += ' INNER JOIN sys.partitions partitions ON partitions.[object_id] = Stats.ObjectID AND partitions.index_id IN (0, 1)' + END + SET @CurrentCommand += ' WHERE Objects.IsMemoryOptimized = 0' + + ' AND Stats.IsIndex = 0' + + ' AND Stats.IsIncremental = 1' + + ' AND Objects.IsClusteredIndexDisabled = 0' + + INSERT INTO @tmpIndexesStatistics (SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, StatisticsID, StatisticsName, [NoRecompute], IsIncremental, PartitionNumber, IsPartition) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand + SET @Error = @@ERROR + IF @Error <> 0 + BEGIN + SET @ReturnCode = @Error + END + END + + IF @PartitionLevel = 'Y' + BEGIN + UPDATE tmpIndexesStatistics + SET tmpIndexesStatistics.IsLastPartition = CASE WHEN tmpIndexesStatistics.PartitionNumber = LastPartitionNumbers.LastPartitionNumber THEN 1 ELSE 0 END + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN (SELECT ObjectID, IndexID, MAX(PartitionNumber) AS LastPartitionNumber FROM @tmpIndexesStatistics WHERE IndexID IS NOT NULL AND PartitionNumber IS NOT NULL GROUP BY ObjectID, IndexID) LastPartitionNumbers ON tmpIndexesStatistics.ObjectID = LastPartitionNumbers.ObjectID AND tmpIndexesStatistics.IndexID = LastPartitionNumbers.IndexID + OPTION (RECOMPILE) + END + + UPDATE tmpIndexesStatistics + SET tmpIndexesStatistics.ResumableIndexOperation = 1 + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN @tmpResumableOperations tmpResumableOperations ON tmpIndexesStatistics.ObjectID = tmpResumableOperations.ObjectID AND tmpIndexesStatistics.IndexID = tmpResumableOperations.IndexID AND (tmpIndexesStatistics.PartitionNumber = tmpResumableOperations.PartitionNumber OR tmpResumableOperations.PartitionNumber IS NULL) + OPTION (RECOMPILE) + + 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(REPLACE(SelectedIndexes.DatabaseName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.SchemaName LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.ObjectName LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,'[','[[]'),'_','[_]') AND COALESCE(tmpIndexesStatistics.IndexName,tmpIndexesStatistics.StatisticsName) LIKE REPLACE(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(REPLACE(SelectedIndexes.DatabaseName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.SchemaName LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.ObjectName LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,'[','[[]'),'_','[_]') AND COALESCE(tmpIndexesStatistics.IndexName,tmpIndexesStatistics.StatisticsName) LIKE REPLACE(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(REPLACE(SelectedIndexes.DatabaseName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.SchemaName LIKE REPLACE(REPLACE(SelectedIndexes.SchemaName,'[','[[]'),'_','[_]') AND tmpIndexesStatistics.ObjectName LIKE REPLACE(REPLACE(SelectedIndexes.ObjectName,'[','[[]'),'_','[_]') AND COALESCE(tmpIndexesStatistics.IndexName,tmpIndexesStatistics.StatisticsName) LIKE REPLACE(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 + + -- Update that alter index is completed for rows that have no index, if no index actions have been selected, for rows on read-only filegroups, or based on the page counts + UPDATE @tmpIndexesStatistics + SET AlterIndexCompleted = 1 + WHERE IndexID IS NULL + OR NOT EXISTS (SELECT * FROM @ActionsPreferred) + OR OnReadOnlyFileGroup = 1 + OR NOT (((InRowDataPageCount >= @MinNumberOfPages OR @MinNumberOfPages = 0) AND (InRowDataPageCount <= @MaxNumberOfPages OR @MaxNumberOfPages IS NULL)) OR InRowDataPageCount IS NULL) + + -- Update that update statistics is completed for rows that have no statistics + UPDATE @tmpIndexesStatistics + SET UpdateStatisticsCompleted = 1 + WHERE StatisticsID IS NULL + OR (IndexID IS NOT NULL AND @PartitionLevel = 'Y' AND IsIncremental = 0 AND IsLastPartition = 0) + + SET @CurrentCommand = 'SELECT schemas.[name] AS SchemaName, objects.[name] AS ObjectName' + + ' FROM sys.objects objects' + + ' INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id]' + + ' WHERE objects.[type] IN(''U'',''V'')' + + ' AND EXISTS(SELECT * FROM #SelectedIndexes SelectedIndexes' + + ' WHERE SelectedIndexes.DatabaseName = @ParamDatabaseName' + + ' AND SelectedIndexes.SchemaName NOT LIKE ''%[%]%''' + + ' AND SelectedIndexes.ObjectName NOT LIKE ''%[%]%''' + + ' AND schemas.[name] = SelectedIndexes.SchemaName COLLATE ' + @Collation + + ' AND objects.[name] = SelectedIndexes.ObjectName COLLATE ' + @Collation + ')' + + INSERT INTO #ExistingObjects (SchemaName, ObjectName) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseName nvarchar(max)', @ParamDatabaseName = @CurrentDatabaseName + + SET @CurrentCommand = 'SELECT schemas.[name] AS SchemaName, objects.[name] AS ObjectName, [Names].[name] AS IndexName' + + ' FROM sys.objects objects' + + ' INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id]' + + ' CROSS APPLY (SELECT indexes.[name] FROM sys.indexes indexes WHERE indexes.[object_id] = objects.[object_id] AND indexes.[type] <> 0' + + ' UNION SELECT stats.[name] FROM sys.stats stats WHERE stats.[object_id] = objects.[object_id]) [Names]' + + ' WHERE objects.[type] IN(''U'',''V'')' + + ' AND EXISTS(SELECT * FROM #SelectedIndexes SelectedIndexes' + + ' WHERE SelectedIndexes.DatabaseName = @ParamDatabaseName' + + ' AND SelectedIndexes.SchemaName NOT LIKE ''%[%]%''' + + ' AND SelectedIndexes.ObjectName NOT LIKE ''%[%]%''' + + ' AND SelectedIndexes.IndexName NOT LIKE ''%[%]%''' + + ' AND schemas.[name] = SelectedIndexes.SchemaName COLLATE ' + @Collation + + ' AND objects.[name] = SelectedIndexes.ObjectName COLLATE ' + @Collation + + ' AND [Names].[name] = SelectedIndexes.IndexName COLLATE ' + @Collation + ')' + + INSERT INTO #ExistingIndexes (SchemaName, ObjectName, IndexName) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseName nvarchar(max)', @ParamDatabaseName = @CurrentDatabaseName + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)) + '.' + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC, SchemaName ASC, ObjectName ASC) + FROM @SelectedIndexes SelectedIndexes + WHERE DatabaseName = @CurrentDatabaseName + AND SchemaName NOT LIKE '%[%]%' + AND ObjectName NOT LIKE '%[%]%' + AND IndexName LIKE '%[%]%' + AND NOT EXISTS (SELECT * FROM #ExistingObjects WHERE SchemaName = SelectedIndexes.SchemaName AND ObjectName = SelectedIndexes.ObjectName) + + IF @ErrorMessage IS NOT NULL + BEGIN + SET @ErrorMessage = 'The following objects in the @Indexes parameter do not exist: ' + @ErrorMessage + '.' + RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + END + + SELECT @ErrorMessage = STRING_AGG(CAST(QUOTENAME(DatabaseName) AS nvarchar(max)) + '.' + QUOTENAME(SchemaName) + '.' + QUOTENAME(ObjectName) + '.' + QUOTENAME(IndexName), ', ') + WITHIN GROUP (ORDER BY DatabaseName ASC, SchemaName ASC, ObjectName ASC, IndexName ASC) + FROM @SelectedIndexes SelectedIndexes + WHERE DatabaseName = @CurrentDatabaseName + AND SchemaName NOT LIKE '%[%]%' + AND ObjectName NOT LIKE '%[%]%' + AND IndexName NOT LIKE '%[%]%' + AND NOT EXISTS (SELECT * FROM #ExistingIndexes WHERE SchemaName = SelectedIndexes.SchemaName AND ObjectName = SelectedIndexes.ObjectName AND IndexName = SelectedIndexes.IndexName) + + IF @ErrorMessage IS NOT NULL + BEGIN + SET @ErrorMessage = 'The following indexes in the @Indexes parameter do not exist: ' + @ErrorMessage + '.' + RAISERROR('%s',10,1,@ErrorMessage) WITH NOWAIT + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + END + 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, + @CurrentIsFileStream = IsFileStream, + @CurrentHasClusteredColumnstore = HasClusteredColumnstore, + @CurrentIsColumnstoreOrdered = IsColumnstoreOrdered, + @CurrentIsComputed = IsComputed, + @CurrentIsClusteredIndexComputed = IsClusteredIndexComputed, + @CurrentIsTimestamp = IsTimestamp, + @CurrentOnReadOnlyFileGroup = OnReadOnlyFileGroup, + @CurrentResumableIndexOperation = ResumableIndexOperation, + @CurrentStatisticsID = StatisticsID, + @CurrentStatisticsName = StatisticsName, + @CurrentNoRecompute = [NoRecompute], + @CurrentIsIncremental = IsIncremental, + @CurrentPartitionID = PartitionID, + @CurrentPartitionNumber = PartitionNumber, + @CurrentIsPartition = IsPartition, + @CurrentIsLastPartition = IsLastPartition, + @CurrentInRowDataPageCount = InRowDataPageCount, + @CurrentAlterIndexCompleted = AlterIndexCompleted, + @CurrentUpdateStatisticsCompleted = UpdateStatisticsCompleted + FROM @tmpIndexesStatistics + WHERE Selected = 1 + AND Completed = 0 + ORDER BY [Order] ASC + + IF @@ROWCOUNT = 0 + BEGIN + BREAK + END + + IF @CurrentAlterIndexCompleted = 0 AND @CurrentIndexID IS NOT NULL AND EXISTS(SELECT * FROM @ActionsPreferred) AND @CurrentOnReadOnlyFileGroup = 0 + BEGIN + SET @CurrentMaxDOP = @MaxDOP + + -- Does the index exist? + SET @CurrentCommand = '' + + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + + 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(max)) + ', ' + 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 + + -- Is the index fragmented? + IF EXISTS(SELECT [Priority], [Action], COUNT(*) FROM @ActionsPreferred GROUP BY [Priority], [Action] HAVING COUNT(*) <> 3) OR @MinNumberOfPages > 0 OR @MaxNumberOfPages IS NOT NULL + BEGIN + IF NOT EXISTS (SELECT * FROM @PhysicalStats WHERE ObjectID = @CurrentObjectID AND IndexID = @CurrentIndexID) + BEGIN + SET @CurrentCommand = '' + + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + + IF @CurrentIndexType IN(5, 6) + BEGIN + SET @CurrentCommand += 'SELECT object_id, index_id, partition_number, MAX(avg_fragmentation_in_percent), SUM(page_count) FROM sys.dm_db_index_physical_stats(DB_ID(@ParamDatabaseName), @ParamObjectID, @ParamIndexID, NULL, ''LIMITED'') WHERE alloc_unit_type_desc = ''IN_ROW_DATA'' AND index_level = 0 GROUP BY object_id, index_id, partition_number' + END + ELSE + BEGIN + SET @CurrentCommand += 'SELECT object_id, index_id, partition_number, avg_fragmentation_in_percent, page_count FROM sys.dm_db_index_physical_stats(DB_ID(@ParamDatabaseName), @ParamObjectID, @ParamIndexID, NULL, ''LIMITED'') WHERE alloc_unit_type_desc = ''IN_ROW_DATA'' AND index_level = 0' + END + + BEGIN TRY + INSERT INTO @PhysicalStats (ObjectID, IndexID, PartitionNumber, FragmentationLevel, PageCount) + EXECUTE sp_executesql @stmt = @CurrentCommand, @params = N'@ParamDatabaseName nvarchar(max), @ParamObjectID int, @ParamIndexID int', @ParamDatabaseName = @CurrentDatabaseName, @ParamObjectID = @CurrentObjectID, @ParamIndexID = @CurrentIndexID + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 + + UPDATE @tmpIndexesStatistics + SET AlterIndexCompleted = 1 + WHERE ObjectID = @CurrentObjectID + AND IndexID = @CurrentIndexID + AND AlterIndexCompleted = 0 + + GOTO NoAction + END CATCH + END + + IF @CurrentPartitionNumber IS NULL + BEGIN + SELECT @CurrentFragmentationLevel = MAX(FragmentationLevel), + @CurrentPageCount = SUM(PageCount) + FROM @PhysicalStats + WHERE ObjectID = @CurrentObjectID + AND IndexID = @CurrentIndexID + END + ELSE + BEGIN + SELECT @CurrentFragmentationLevel = FragmentationLevel, + @CurrentPageCount = PageCount + FROM @PhysicalStats + WHERE ObjectID = @CurrentObjectID + AND IndexID = @CurrentIndexID + AND PartitionNumber = @CurrentPartitionNumber + END + END + + -- Select fragmentation group + SET @CurrentFragmentationGroup = CASE + WHEN @CurrentFragmentationLevel >= @FragmentationLevel2 THEN 'High' + WHEN @CurrentFragmentationLevel >= @FragmentationLevel1 AND @CurrentFragmentationLevel < @FragmentationLevel2 THEN 'Medium' + WHEN @CurrentFragmentationLevel < @FragmentationLevel1 THEN 'Low' + END + + -- Which actions are allowed? + IF NOT (@CurrentIsMemoryOptimized = 1) + AND NOT (@CurrentAllowPageLocks = 0) + BEGIN + INSERT INTO @CurrentActionsAllowed ([Action]) + VALUES ('INDEX_REORGANIZE') + END + IF NOT (@CurrentIsMemoryOptimized = 1) + BEGIN + INSERT INTO @CurrentActionsAllowed ([Action]) + VALUES ('INDEX_REBUILD_OFFLINE') + END + IF @EngineEdition IN (3, 5, 8) + AND NOT (@CurrentIsMemoryOptimized = 1) + AND NOT (@CurrentIndexType = 1 AND @CurrentIsImageText = 1 AND @CurrentIsImageText IS NOT NULL) + AND NOT (@CurrentIndexType = 1 AND @CurrentIsFileStream = 1 AND @CurrentIsFileStream IS NOT NULL) + AND NOT (@CurrentIndexType = 3) + AND NOT (@CurrentIndexType = 4) + AND NOT (@CurrentIndexType = 5 AND NOT (@Version >= 15 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous'))) + AND NOT (@CurrentIndexType = 2 AND @CurrentHasClusteredColumnstore = 1 AND @CurrentHasClusteredColumnstore IS NOT NULL AND NOT (@Version >= 15 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous'))) + AND NOT (@CurrentIndexType = 5 AND @CurrentIsColumnstoreOrdered = 1 AND @CurrentIsColumnstoreOrdered IS NOT NULL AND NOT (@Version >= 17 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous'))) + BEGIN + INSERT INTO @CurrentActionsAllowed ([Action]) + VALUES ('INDEX_REBUILD_ONLINE') + END + + -- Decide action + IF (@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 @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentIndexType IN (1, 2) AND @CurrentAllowPageLocks = 0 + BEGIN + SET @CurrentMaxDOP = 1 + END + + -- Create index comment + IF @CurrentAction 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 + ', ' + IF @CurrentIsImageText IS NOT NULL SET @CurrentComment += 'ImageText: ' + CASE WHEN @CurrentIsImageText = 1 THEN 'Yes' WHEN @CurrentIsImageText = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentIsFileStream IS NOT NULL SET @CurrentComment += 'FileStream: ' + CASE WHEN @CurrentIsFileStream = 1 THEN 'Yes' WHEN @CurrentIsFileStream = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentHasClusteredColumnstore IS NOT NULL 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 @CurrentIsColumnstoreOrdered IS NOT NULL AND @CurrentIndexType = 5 SET @CurrentComment += 'IsColumnstoreOrdered: ' + CASE WHEN @CurrentIsColumnstoreOrdered = 1 THEN 'Yes' WHEN @CurrentIsColumnstoreOrdered = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentIsComputed IS NOT NULL SET @CurrentComment += 'Computed: ' + CASE WHEN @CurrentIsComputed = 1 THEN 'Yes' WHEN @CurrentIsComputed = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentIsClusteredIndexComputed IS NOT NULL AND @CurrentIndexType = 2 SET @CurrentComment += 'ClusteredIndexComputed: ' + CASE WHEN @CurrentIsClusteredIndexComputed = 1 THEN 'Yes' WHEN @CurrentIsClusteredIndexComputed = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @CurrentIsTimestamp IS NOT NULL SET @CurrentComment += 'Timestamp: ' + CASE WHEN @CurrentIsTimestamp = 1 THEN 'Yes' WHEN @CurrentIsTimestamp = 0 THEN 'No' ELSE 'N/A' END + ', ' + IF @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(max)),'N/A') + ', ' + SET @CurrentComment += 'Fragmentation: ' + ISNULL(CAST(@CurrentFragmentationLevel AS nvarchar(max)),'N/A') + END + + IF @CurrentAction IS NOT NULL AND (@CurrentPageCount IS NOT NULL OR @CurrentFragmentationLevel IS NOT NULL) + BEGIN + SET @CurrentExtendedInfo = (SELECT * + FROM (SELECT CAST(@CurrentPageCount AS nvarchar(max)) AS [PageCount], + CAST(@CurrentFragmentationLevel AS nvarchar(max)) AS Fragmentation + ) ExtendedInfo FOR XML RAW('ExtendedInfo'), ELEMENTS) + END + + IF @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(max)) + '; ' + 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(max)) + + 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) + VALUES('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) + VALUES('SORT_IN_TEMPDB = OFF') + END + + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('ONLINE = ON' + CASE WHEN @WaitAtLowPriorityMaxDuration IS NOT NULL THEN ' (WAIT_AT_LOW_PRIORITY (MAX_DURATION = ' + CAST(@WaitAtLowPriorityMaxDuration AS nvarchar(max)) + ', ABORT_AFTER_WAIT = ' + UPPER(@WaitAtLowPriorityAbortAfterWait) + '))' ELSE '' END) + END + + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentResumableIndexOperation = 1 AND @WaitAtLowPriorityMaxDuration IS NOT NULL + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('WAIT_AT_LOW_PRIORITY (MAX_DURATION = ' + CAST(@WaitAtLowPriorityMaxDuration AS nvarchar(max)) + ', ABORT_AFTER_WAIT = ' + UPPER(@WaitAtLowPriorityAbortAfterWait) + ')') + END + + IF @CurrentAction = 'INDEX_REBUILD_OFFLINE' AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('ONLINE = OFF') + END + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @CurrentMaxDOP IS NOT NULL + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('MAXDOP = ' + CAST(@CurrentMaxDOP AS nvarchar(max))) + 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) + VALUES('FILLFACTOR = ' + CAST(@FillFactor AS nvarchar(max))) + END + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @PadIndex IS NOT NULL AND @CurrentIsPartition = 0 AND @CurrentIndexType IN(1,2,3,4) AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('PAD_INDEX = ' + CASE WHEN @PadIndex = 'Y' THEN 'ON' WHEN @PadIndex = 'N' THEN 'OFF' END) + END + + IF @CurrentAction IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') AND @DataCompression IS NOT NULL AND @CurrentIndexType IN(1,2,4) AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('DATA_COMPRESSION = ' + @DataCompression) + END + + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND @CurrentResumableIndexOperation = 0 + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES(CASE WHEN @Resumable = 'Y' AND @CurrentIndexType IN(1,2) AND (@CurrentIsComputed = 0 OR @CurrentIsComputed IS NULL) AND (@CurrentIsClusteredIndexComputed = 0 OR @CurrentIsClusteredIndexComputed IS NULL) AND (@CurrentIsTimestamp = 0 OR @CurrentIsTimestamp IS NULL) AND @CurrentHasFilter = 0 AND (@CurrentHasClusteredColumnstore = 0 OR @CurrentHasClusteredColumnstore IS NULL) THEN 'RESUMABLE = ON' ELSE 'RESUMABLE = OFF' END) + END + + IF @CurrentAction = 'INDEX_REBUILD_ONLINE' AND ((@Resumable = 'Y' AND @CurrentIndexType IN(1,2) AND (@CurrentIsComputed = 0 OR @CurrentIsComputed IS NULL) AND (@CurrentIsClusteredIndexComputed = 0 OR @CurrentIsClusteredIndexComputed IS NULL) AND (@CurrentIsTimestamp = 0 OR @CurrentIsTimestamp IS NULL) AND @CurrentHasFilter = 0 AND (@CurrentHasClusteredColumnstore = 0 OR @CurrentHasClusteredColumnstore IS NULL)) OR @CurrentResumableIndexOperation = 1) AND @TimeLimit IS NOT NULL + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('MAX_DURATION = ' + CAST(CASE WHEN DATEDIFF(MINUTE,SYSDATETIME(),DATEADD(SECOND,@TimeLimit,@StartTime)) < 1 THEN 1 WHEN DATEDIFF(MINUTE,SYSDATETIME(),DATEADD(SECOND,@TimeLimit,@StartTime)) > 10080 THEN 10080 ELSE DATEDIFF(MINUTE,SYSDATETIME(),DATEADD(SECOND,@TimeLimit,@StartTime)) END AS nvarchar(max))) + END + + IF @CurrentAction IN('INDEX_REORGANIZE') AND @LOBCompaction = 'Y' + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('LOB_COMPACTION = ON') + END + + IF @CurrentAction IN('INDEX_REORGANIZE') AND @LOBCompaction = 'N' + BEGIN + INSERT INTO @CurrentAlterIndexWithClauseArguments (Argument) + VALUES('LOB_COMPACTION = OFF') + END + + IF EXISTS (SELECT * FROM @CurrentAlterIndexWithClauseArguments) + BEGIN + SELECT @CurrentCommand += ' WITH (' + STRING_AGG(Argument, ', ') WITHIN GROUP (ORDER BY ID ASC) + ')' + FROM @CurrentAlterIndexWithClauseArguments + END + + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @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 + END + + -- Should the statistics be updated? + IF @CurrentUpdateStatisticsCompleted = 0 + AND @CurrentStatisticsID IS NOT NULL + AND ((@UpdateStatistics = 'ALL' AND (@CurrentIndexType IN (1,2,7) OR @CurrentIndexID IS NULL)) OR (@UpdateStatistics = 'INDEX' AND @CurrentIndexID IS NOT NULL AND @CurrentIndexType IN (1,2,7)) OR (@UpdateStatistics = 'COLUMNS' AND @CurrentIndexID IS NULL)) + AND ((@CurrentIsPartition = 0 AND (@CurrentAction NOT IN('INDEX_REBUILD_ONLINE','INDEX_REBUILD_OFFLINE') OR @CurrentAction IS NULL)) OR (@CurrentIsPartition = 1 AND (@CurrentIsLastPartition = 1 OR (@PartitionLevel = 'Y' AND @CurrentIsIncremental = 1)))) + BEGIN + SET @CurrentMaxDOP = @MaxDOP + + -- Does the statistics exist? + SET @CurrentCommand = '' + + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + + 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(max)) + ', ' + 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 + + -- Check non-incremental statistics properties + IF NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) AND NOT (@PartitionLevel = 'Y' AND @CurrentIsIncremental = 1) + BEGIN + SET @CurrentCommand = '' + + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + + SET @CurrentCommand += 'SELECT @ParamRowCount = [rows], @ParamModificationCounter = modification_counter FROM sys.dm_db_stats_properties (@ParamObjectID, @ParamStatisticsID)' + + BEGIN TRY + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamObjectID int, @ParamStatisticsID int, @ParamRowCount bigint OUTPUT, @ParamModificationCounter bigint OUTPUT', @ParamObjectID = @CurrentObjectID, @ParamStatisticsID = @CurrentStatisticsID, @ParamRowCount = @CurrentRowCount OUTPUT, @ParamModificationCounter = @CurrentModificationCounter OUTPUT + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 + + -- Check incremental statistics properties + IF NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) AND @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 + AND NOT EXISTS (SELECT * FROM @IncrementalStatsProperties WHERE ObjectID = @CurrentObjectID AND StatisticsID = @CurrentStatisticsID) + BEGIN + SET @CurrentCommand = '' + + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + + BEGIN + SET @CurrentCommand += 'SELECT object_id, stats_id, partition_number, [rows], modification_counter FROM sys.dm_db_incremental_stats_properties (@ParamObjectID, @ParamStatisticsID)' + END + + BEGIN TRY + INSERT INTO @IncrementalStatsProperties (ObjectID, StatisticsID, PartitionNumber, [Rows], ModificationCounter) + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamObjectID int, @ParamStatisticsID int', @ParamObjectID = @CurrentObjectID, @ParamStatisticsID = @CurrentStatisticsID + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + 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 + + UPDATE @tmpIndexesStatistics + SET UpdateStatisticsCompleted = 1 + WHERE ObjectID = @CurrentObjectID + AND StatisticsID = @CurrentStatisticsID + AND UpdateStatisticsCompleted = 0 + + GOTO NoAction + END CATCH + END + + IF NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) AND @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 + BEGIN + SELECT @CurrentRowCount = [Rows], + @CurrentModificationCounter = [ModificationCounter] + FROM @IncrementalStatsProperties + WHERE ObjectID = @CurrentObjectID + AND StatisticsID = @CurrentStatisticsID + AND PartitionNumber = @CurrentPartitionNumber + END + + -- Check partition statistics + IF NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) AND @CurrentModificationCounter IS NULL + BEGIN + SET @CurrentCommand = '' + IF @LockTimeout IS NOT NULL SET @CurrentCommand = 'SET LOCK_TIMEOUT ' + CAST(@LockTimeout * 1000 AS nvarchar(max)) + '; ' + + IF @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 + BEGIN + SET @CurrentCommand += 'SELECT @ParamObjectHasRows = CASE WHEN EXISTS (SELECT * FROM sys.dm_db_partition_stats WHERE [object_id] = @ParamObjectID AND index_id IN (0,1) AND partition_number = @ParamPartitionNumber AND row_count > 0) THEN 1 ELSE 0 END' + END + ELSE + BEGIN + SET @CurrentCommand += 'SELECT @ParamObjectHasRows = CASE WHEN EXISTS (SELECT * FROM sys.dm_db_partition_stats WHERE [object_id] = @ParamObjectID AND index_id IN (0,1) AND row_count > 0) THEN 1 ELSE 0 END' + END + + BEGIN TRY + EXECUTE @CurrentDatabase_sp_executesql @stmt = @CurrentCommand, @params = N'@ParamObjectID int, @ParamPartitionNumber int, @ParamObjectHasRows bit OUTPUT', @ParamObjectID = @CurrentObjectID, @ParamPartitionNumber = @CurrentPartitionNumber, @ParamObjectHasRows = @CurrentObjectHasRows OUTPUT + END TRY + BEGIN CATCH + SET @ErrorMessage = 'Msg ' + CAST(ERROR_NUMBER() AS nvarchar(max)) + ', ' + ISNULL(ERROR_MESSAGE(),'') + CASE WHEN ERROR_NUMBER() = 1222 THEN ' The object ' + QUOTENAME(@CurrentDatabaseName) + '.' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName) + ' is locked. The row count 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 + + -- Update statistics? + IF ((@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 ((@CurrentIndexType IN (1,2) OR @CurrentIndexID IS NULL) AND @CurrentModificationCounter IS NULL AND @CurrentObjectHasRows = 1)) + BEGIN + SET @CurrentUpdateStatistics = 'Y' + END + ELSE + BEGIN + SET @CurrentUpdateStatistics = 'N' + END + + SET @CurrentStatisticsSample = @StatisticsSample + SET @CurrentStatisticsPersistSample = @StatisticsPersistSample + SET @CurrentStatisticsResample = @StatisticsResample + + -- Incremental statistics only supports RESAMPLE + IF @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 + BEGIN + SET @CurrentStatisticsSample = NULL + SET @CurrentStatisticsPersistSample = NULL + SET @CurrentStatisticsResample = 'Y' + END + + -- Create statistics comment + IF @CurrentUpdateStatistics = 'Y' + BEGIN + SET @CurrentComment = 'ObjectType: ' + CASE WHEN @CurrentObjectType = 'U' THEN 'Table' WHEN @CurrentObjectType = 'V' THEN 'View' ELSE 'N/A' END + ', ' + SET @CurrentComment += 'StatisticsType: ' + 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 'Yes' WHEN @CurrentIsIncremental = 0 THEN 'No' ELSE 'N/A' END + ', ' + SET @CurrentComment += 'RowCount: ' + ISNULL(CAST(@CurrentRowCount AS nvarchar(max)),'N/A') + ', ' + SET @CurrentComment += 'ModificationCounter: ' + ISNULL(CAST(@CurrentModificationCounter AS nvarchar(max)),'N/A') + END + + IF @CurrentUpdateStatistics = 'Y' AND (@CurrentRowCount IS NOT NULL OR @CurrentModificationCounter IS NOT NULL) + BEGIN + SET @CurrentExtendedInfo = (SELECT * + FROM (SELECT CAST(@CurrentRowCount AS nvarchar(max)) AS [RowCount], + CAST(@CurrentModificationCounter AS nvarchar(max)) AS ModificationCounter + ) ExtendedInfo FOR XML RAW('ExtendedInfo'), ELEMENTS) + END + ELSE + BEGIN + SET @CurrentExtendedInfo = NULL + END + + IF @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(max)) + '; ' + SET @CurrentCommand += 'UPDATE STATISTICS ' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentObjectName) + ' ' + QUOTENAME(@CurrentStatisticsName) + + IF @CurrentMaxDOP IS NOT NULL AND (@Version >= 14.03015 OR @EngineEdition = 5 OR (@EngineEdition = 8 AND @ProductUpdateType = 'Continuous')) + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('MAXDOP = ' + CAST(@CurrentMaxDOP AS nvarchar(max))) + END + + IF @CurrentStatisticsSample = 100 + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('FULLSCAN') + END + + IF @CurrentStatisticsSample IS NOT NULL AND @CurrentStatisticsSample <> 100 + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('SAMPLE ' + CAST(@CurrentStatisticsSample AS nvarchar(max)) + ' PERCENT') + END + + IF @CurrentStatisticsPersistSample = 'Y' + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('PERSIST_SAMPLE_PERCENT = ON') + END + + IF @CurrentStatisticsPersistSample = 'N' + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('PERSIST_SAMPLE_PERCENT = OFF') + END + + IF @CurrentNoRecompute = 1 + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('NORECOMPUTE') + END + + IF @CurrentStatisticsResample = 'Y' + BEGIN + INSERT INTO @CurrentUpdateStatisticsWithClauseArguments (Argument) + VALUES('RESAMPLE') + END + + IF EXISTS (SELECT * FROM @CurrentUpdateStatisticsWithClauseArguments) + BEGIN + SELECT @CurrentCommand += ' WITH ' + STRING_AGG(Argument, ', ') WITHIN GROUP (ORDER BY ID ASC) + FROM @CurrentUpdateStatisticsWithClauseArguments + END + + IF @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 AND @CurrentPartitionNumber IS NOT NULL SET @CurrentCommand += ' ON PARTITIONS(' + CAST(@CurrentPartitionNumber AS nvarchar(max)) + ')' + + EXECUTE @CurrentCommandOutput = dbo.CommandExecute @DatabaseContext = @CurrentDatabaseContext, @Command = @CurrentCommand, @CommandType = @CurrentCommandType, @Mode = 2, @Comment = @CurrentComment, @DatabaseName = @CurrentDatabaseName, @SchemaName = @CurrentSchemaName, @ObjectName = @CurrentObjectName, @ObjectType = @CurrentObjectType, @IndexName = @CurrentIndexName, @IndexType = @CurrentIndexType, @StatisticsName = @CurrentStatisticsName, @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 + END + END + + NoAction: + + -- Update that the index or statistics is completed + UPDATE @tmpIndexesStatistics + SET AlterIndexCompleted = 1, + UpdateStatisticsCompleted = 1 + WHERE Selected = 1 + AND Completed = 0 + AND [Order] = @CurrentIxOrder + AND ID = @CurrentIxID + + -- Update that index operations on remaining partitions are completed where no action is needed + IF @CurrentAlterIndexCompleted = 0 AND @CurrentIndexID IS NOT NULL AND @PartitionLevel = 'Y' AND @CurrentIsPartition = 1 AND (SELECT COUNT(DISTINCT FragmentationGroup) FROM @ActionsPreferred) < 3 + BEGIN + UPDATE tmpIndexesStatistics + SET AlterIndexCompleted = 1 + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN @PhysicalStats PhysicalStats ON tmpIndexesStatistics.ObjectID = PhysicalStats.ObjectID AND tmpIndexesStatistics.IndexID = PhysicalStats.IndexID AND tmpIndexesStatistics.PartitionNumber = PhysicalStats.PartitionNumber + WHERE tmpIndexesStatistics.ObjectID = @CurrentObjectID + AND tmpIndexesStatistics.IndexID = @CurrentIndexID + AND tmpIndexesStatistics.AlterIndexCompleted = 0 + AND tmpIndexesStatistics.ResumableIndexOperation = 0 + AND NOT EXISTS (SELECT * + FROM @ActionsPreferred ActionsPreferred + WHERE ActionsPreferred.FragmentationGroup = CASE + WHEN PhysicalStats.FragmentationLevel >= @FragmentationLevel2 THEN 'High' + WHEN PhysicalStats.FragmentationLevel >= @FragmentationLevel1 AND PhysicalStats.FragmentationLevel < @FragmentationLevel2 THEN 'Medium' + WHEN PhysicalStats.FragmentationLevel < @FragmentationLevel1 THEN 'Low' + END) + END + + -- Update that statistics on remaining partitions are completed where no update is needed + IF @CurrentUpdateStatisticsCompleted = 0 AND @CurrentStatisticsID IS NOT NULL AND @PartitionLevel = 'Y' AND @CurrentIsIncremental = 1 AND NOT (@OnlyModifiedStatistics = 'N' AND @StatisticsModificationLevel IS NULL) + BEGIN + UPDATE tmpIndexesStatistics + SET UpdateStatisticsCompleted = 1 + FROM @tmpIndexesStatistics tmpIndexesStatistics + INNER JOIN @IncrementalStatsProperties IncrementalStatsProperties ON tmpIndexesStatistics.ObjectID = IncrementalStatsProperties.ObjectID AND tmpIndexesStatistics.StatisticsID = IncrementalStatsProperties.StatisticsID AND tmpIndexesStatistics.PartitionNumber = IncrementalStatsProperties.PartitionNumber + WHERE tmpIndexesStatistics.ObjectID = @CurrentObjectID + AND tmpIndexesStatistics.StatisticsID = @CurrentStatisticsID + AND tmpIndexesStatistics.Selected = 1 + AND tmpIndexesStatistics.Completed = 0 + AND IncrementalStatsProperties.ModificationCounter IS NOT NULL + AND ((@OnlyModifiedStatistics = 'Y' AND NOT (IncrementalStatsProperties.ModificationCounter > 0)) + OR (@StatisticsModificationLevel IS NOT NULL AND NOT ((IncrementalStatsProperties.ModificationCounter * 1. / NULLIF(IncrementalStatsProperties.[Rows],0)) * 100 >= @StatisticsModificationLevel OR (IncrementalStatsProperties.ModificationCounter > 0 AND IncrementalStatsProperties.ModificationCounter >= SQRT(IncrementalStatsProperties.[Rows] * 1000))))) + END + + -- 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 @CurrentInRowDataPageCount = NULL + SET @CurrentAlterIndexCompleted = NULL + SET @CurrentUpdateStatisticsCompleted = NULL + SET @CurrentIsPartition = NULL + SET @CurrentIsLastPartition = NULL + SET @CurrentIndexExists = NULL + SET @CurrentStatisticsExists = NULL + SET @CurrentIsImageText = NULL + SET @CurrentIsFileStream = NULL + SET @CurrentHasClusteredColumnstore = NULL + SET @CurrentIsColumnstoreOrdered = NULL + SET @CurrentIsComputed = NULL + SET @CurrentIsClusteredIndexComputed = NULL + SET @CurrentIsTimestamp = NULL + SET @CurrentAllowPageLocks = NULL + SET @CurrentHasFilter = NULL + SET @CurrentNoRecompute = NULL + SET @CurrentIsIncremental = NULL + SET @CurrentObjectHasRows = 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 @CurrentStatisticsPersistSample = NULL + SET @CurrentStatisticsResample = 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 + SET @Error = @@ERROR + SET @ReturnCode = @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 @CurrentExecuteAsUserExists = NULL + SET @CurrentUserAccess = NULL + SET @CurrentIsReadOnly = NULL + SET @CurrentDatabaseState = NULL + SET @CurrentInStandby = NULL + SET @CurrentRecoveryModel = NULL + SET @CurrentDatabaseHasReadOnlyFileGroup = NULL + SET @CurrentAvailabilityGroupReplicaID = NULL + SET @CurrentAvailabilityGroupID = NULL + SET @CurrentAvailabilityGroup = NULL + SET @CurrentAvailabilityGroupRole = NULL + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationState = NULL + SET @CurrentAvailabilityGroupDatabaseReplicaSynchronizationHealth = NULL + SET @CurrentDistributedAvailabilityGroup = NULL + SET @CurrentDistributedAvailabilityGroupReplicaID = NULL + SET @CurrentDistributedAvailabilityGroupRole = NULL + + SET @CurrentDatabaseMirroringRole = NULL + + SET @CurrentCommand = NULL + + DELETE FROM @tmpIndexesStatistics + + TRUNCATE TABLE #Objects + TRUNCATE TABLE #Indexes + TRUNCATE TABLE #Stats + TRUNCATE TABLE #ExistingObjects + TRUNCATE TABLE #ExistingIndexes + DELETE FROM @tmpResumableOperations + DELETE FROM @PhysicalStats + DELETE FROM @IncrementalStatsProperties + + END -- End of database loop + + ---------------------------------------------------------------------------------------------------- + --// Log completing information //-- + ---------------------------------------------------------------------------------------------------- + + Logging: + SET @EndMessage = 'Date and time: ' + CONVERT(nvarchar(max),SYSDATETIME(),120) + RAISERROR('%s',10,1,@EndMessage) WITH NOWAIT + + RAISERROR(@EmptyLine,10,1) WITH NOWAIT + + IF @ReturnCode <> 0 + BEGIN + RETURN @ReturnCode + END + + ---------------------------------------------------------------------------------------------------- + +END + +GO + diff --git a/README.md b/README.md index 1571725e..79a644c7 100644 --- a/README.md +++ b/README.md @@ -4,43 +4,52 @@ [![forks badge]][forks] [![issues badge]][issues] [![bug report badge]][bug report] +[![SQL Server bug badge]][SQL Server bug] [![feature request badge]][feature request] ## Getting Started -Download [MaintenanceSolution.sql](/MaintenanceSolution.sql). -This script creates all the objects and jobs that you need. +Download the script for your platform: + - [MaintenanceSolution.sql](/MaintenanceSolution.sql): SQL Server 2017, SQL Server 2019, SQL Server 2022, SQL Server 2025, and Azure SQL Managed Instance. This script creates all the objects and jobs that you need. + - [MaintenanceSolutionAzureSQLDatabase.sql](/MaintenanceSolutionAzureSQLDatabase.sql): Azure SQL Database. This script creates the objects for integrity check and index and statistics maintenance. You can also download the objects as separate scripts: - - [DatabaseBackup](/DatabaseBackup.sql): SQL Server Backup - - [DatabaseIntegrityCheck](/DatabaseIntegrityCheck.sql): SQL Server Integrity Check - - [IndexOptimize](/IndexOptimize.sql): SQL Server Index and Statistics Maintenance - - [CommandExecute](/CommandExecute.sql): Stored procedure to execute and log commands - - [CommandLog](/CommandLog.sql): Table to log commands + - [DatabaseBackup.sql](/DatabaseBackup.sql): Stored procedure to back up databases + - [DatabaseIntegrityCheck.sql](/DatabaseIntegrityCheck.sql): Stored procedure to check the integrity of databases + - [IndexOptimize.sql](/IndexOptimize.sql): Stored procedure to rebuild and reorganize indexes and update statistics + - [CommandExecute.sql](/CommandExecute.sql): Stored procedure to execute and log commands + - [CommandLog.sql](/CommandLog.sql): Table to log commands + - [Queue.sql](/Queue.sql): Table for processing databases in parallel + - [QueueDatabase.sql](/QueueDatabase.sql): Table for processing databases in parallel + +Note that you always need CommandExecute; DatabaseBackup, DatabaseIntegrityCheck, and IndexOptimize use it. + +When you update DatabaseBackup, DatabaseIntegrityCheck, or IndexOptimize, you should also update CommandExecute. -Note that you always need CommandExecute; DatabaseBackup, DatabaseIntegrityCheck, and IndexOptimize are using it. You need CommandLog if you are going to use the option to log commands to a table. -Supported versions: SQL Server 2008, SQL Server 2008 R2, SQL Server 2012, SQL Server 2014, SQL Server 2016, SQL Server 2017, SQL Server 2019, SQL Server 2022, SQL Server 2025, Azure SQL Database, and Azure SQL Managed Instance +You can verify the scripts against the SHA-256 checksums in [SHA256SUMS.txt](/SHA256SUMS.txt). ## Documentation - + - SQL Server Backup: [website](https://ola.hallengren.com/sql-server-backup.html) · [repository](/docs/sql-server-backup.md) + - SQL Server Integrity Check: [website](https://ola.hallengren.com/sql-server-integrity-check.html) · [repository](/docs/sql-server-integrity-check.md) + - SQL Server Index and Statistics Maintenance: [website](https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html) · [repository](/docs/sql-server-index-and-statistics-maintenance.md) + - Frequently Asked Questions: [website](https://ola.hallengren.com/frequently-asked-questions.html) + - Version History: [website](https://ola.hallengren.com/versions.html) [licence badge]:https://img.shields.io/badge/license-MIT-blue.svg [stars badge]:https://img.shields.io/github/stars/olahallengren/sql-server-maintenance-solution.svg [forks badge]:https://img.shields.io/github/forks/olahallengren/sql-server-maintenance-solution.svg [issues badge]:https://img.shields.io/github/issues/olahallengren/sql-server-maintenance-solution.svg [bug report badge]:https://img.shields.io/github/issues/olahallengren/sql-server-maintenance-solution/Bug%20Report.svg +[SQL Server bug badge]:https://img.shields.io/github/issues/olahallengren/sql-server-maintenance-solution/SQL%20Server%20Bug.svg [feature request badge]:https://img.shields.io/github/issues/olahallengren/sql-server-maintenance-solution/Feature%20Request.svg -[licence]:https://github.com/olahallengren/sql-server-maintenance-solution/blob/master/LICENSE +[licence]:/LICENSE [stars]:https://github.com/olahallengren/sql-server-maintenance-solution/stargazers [forks]:https://github.com/olahallengren/sql-server-maintenance-solution/network [issues]:https://github.com/olahallengren/sql-server-maintenance-solution/issues [bug report]:https://github.com/olahallengren/sql-server-maintenance-solution/issues?q=is%3Aopen+is%3Aissue+label%3A%22Bug+Report%22 +[SQL Server bug]:https://github.com/olahallengren/sql-server-maintenance-solution/issues?q=is%3Aopen+is%3Aissue+label%3A%22SQL+Server+Bug%22 [feature request]:https://github.com/olahallengren/sql-server-maintenance-solution/issues?q=is%3Aopen+is%3Aissue+label%3A%22Feature+Request%22 diff --git a/SHA256SUMS.txt b/SHA256SUMS.txt new file mode 100644 index 00000000..28c96e64 --- /dev/null +++ b/SHA256SUMS.txt @@ -0,0 +1,9 @@ +89c96d0a900c8d8d172b021d8d34b0f7c8a2c8e694891b8a201b7229533237bc CommandExecute.sql +7a508fa7ed562ec5ee09d4f587f7f2a87f7a4eb3c57450602ee99cdeb4e18b31 CommandLog.sql +f3acadfd302b9af135b5d6b09d732eb68509c2eab08adf26377e1a7d162cdde6 DatabaseBackup.sql +f96df950a9b6dd9e63c18224641e8f8486802119090d0f582e4dfaa643874ea3 DatabaseIntegrityCheck.sql +cbef7d1c82d8e84b08a05df8aec5d5502ddef7cd091e3fc4836076ddad67bbeb IndexOptimize.sql +e9ef6b051bfbadddb1b105054e07e424b0267337b248012e25256c3f0a00a2f3 MaintenanceSolution.sql +8ce92c06224c0ed7fce4e67598d5f93179f423645d9e61e72685a65fdb49162b MaintenanceSolutionAzureSQLDatabase.sql +c30c3b986cc53efe75420e073af5ae9f0830c8f5b0dd1989649e249533588308 Queue.sql +8e61a33a6b6755ceae483968d8dedea87da3f82534dc37fba4786e9fbe9c1ec9 QueueDatabase.sql diff --git a/docs/sql-server-backup.md b/docs/sql-server-backup.md new file mode 100644 index 00000000..2d1679de --- /dev/null +++ b/docs/sql-server-backup.md @@ -0,0 +1,997 @@ + + +# SQL Server Backup + +This documentation is generated from [ola.hallengren.com/sql-server-backup.html](https://ola.hallengren.com/sql-server-backup.html), which is the primary source. + +DatabaseBackup is the SQL Server Maintenance Solution’s stored procedure for backing up databases. + +## Download + +Download the script for your platform: + +- [MaintenanceSolution.sql](/MaintenanceSolution.sql): SQL Server 2017, SQL Server 2019, SQL Server 2022, SQL Server 2025, and Azure SQL Managed Instance. This script creates all the objects and jobs that you need. + +You can also [download](https://github.com/olahallengren/sql-server-maintenance-solution) the objects as separate scripts. The SQL Server Maintenance Solution is available on [GitHub](https://github.com/olahallengren/sql-server-maintenance-solution). + +## License + +The SQL Server Maintenance Solution is [free](/LICENSE). + +## Parameters + +### Databases + +Select databases. The keywords SYSTEM_DATABASES, USER_DATABASES, ALL_DATABASES, and AVAILABILITY_GROUP_DATABASES are supported. The hyphen character (-) is used to exclude databases, and the percent character (%) is used for wildcard selection. All of these operations can be combined by using the comma (,). + +| Value | Description | +| --- | --- | +| SYSTEM_DATABASES | All system databases (master, msdb, and model) | +| USER_DATABASES | All user databases | +| ALL_DATABASES | All databases | +| AVAILABILITY_GROUP_DATABASES | All databases in availability groups | +| USER_DATABASES, -AVAILABILITY_GROUP_DATABASES | All user databases that are not in availability groups | +| Db1 | The database Db1 | +| Db1, Db2 | The databases Db1 and Db2 | +| USER_DATABASES, -Db1 | All user databases except Db1 | +| %Db% | All databases that have “Db” in the name | +| %Db%, -Db1 | All databases that have “Db” in the name except Db1 | +| ALL_DATABASES, -%Db% | All databases that do not have “Db” in the name | + +### Directory + +Specify backup root directories, which can be local directories or network shares. If you specify multiple directories, then the backup files are striped evenly across the directories. Specify multiple directories by using the comma (,). If no directory is specified, then the SQL Server default backup directory is used. + +| Value | Description | +| --- | --- | +| NULL | Back up to the SQL Server default backup directory. This is the default. | +| C:\\Backup | Back up to the directory C:\\Backup. | +| C:\\Backup, D:\\Backup | Back up to the directories C:\\Backup and D:\\Backup. | +| \\\\Server1\\Backup | Back up to the network share \\\\Server1\\Backup. | +| \\\\Server1\\Backup, \\\\Server2\\Backup | Back up to the network shares \\\\Server1\\Backup and \\\\Server2\\Backup. | +| NUL | Back up to NUL. | + +DatabaseBackup creates a directory structure with server name, instance name, database name, and backup type under the backup root directory. If the database is part of an availability group, then cluster name and availability group name are used instead of server name and instance name. + +### BackupType + +Specify the type of backup: full, differential, or transaction log. + +| Value | Description | +| --- | --- | +| FULL | Full backup | +| DIFF | Differential backup | +| LOG | Transaction log backup | + +DatabaseBackup uses the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command: BACKUP DATABASE for the full backup, BACKUP DATABASE WITH DIFFERENTIAL for the differential backup, and BACKUP LOG for the transaction log backup. + +### Verify + +Verify the backup. + +| Value | Description | +| --- | --- | +| Y | Verify the backup. | +| N | Do not verify the backup. This is the default. | + +The Verify option in DatabaseBackup uses the SQL Server [RESTORE VERIFYONLY](https://learn.microsoft.com/en-us/sql/t-sql/statements/restore-statements-verifyonly-transact-sql) command. + +### CleanupTime + +Specify the time, in hours, after which the backup files are deleted. If no time is specified, then no backup files are deleted. + +By default, backup files are deleted after each database is backed up and verified. Backup files are deleted only if the backup and verification of the database were successful. + +DatabaseBackup has a check to verify that transaction log backups that are newer than the most recent full or differential backup are not deleted. This is to guarantee that you can always perform a point-in-time restore. + +DatabaseBackup uses the extended stored procedure xp_delete_file to delete backup files. xp_delete_file deletes files based on a directory, a file extension, and a modified date. Therefore if you are using date or time tokens (e.g. {Year}, {Month}, and {Day}) in the parameters @DirectoryStructure or @AvailabilityGroupDirectoryStructure, then old backup files may be in different directories, and may not be deleted. + +Cleanup is not supported when backing up to URL. + +### CleanupMode + +Specify whether old backup files should be deleted before or after the backup has been performed. + +| Value | Description | +| --- | --- | +| BEFORE_BACKUP | Delete old backup files before the backup has been performed. | +| AFTER_BACKUP | Delete old backup files after the backup and verification have been performed. If the backup or verify fails, then no backup files are deleted. This is the default. | + +### Compress + +Compress the backup. If no value is specified, then the backup compression default in sys.configurations is used. + +| Value | Description | +| --- | --- | +| NULL | Use the backup compression default in sys.configurations. This is the default. | +| Y | Compress the backup. | +| N | Do not compress the backup. | + +The Compress option in DatabaseBackup uses the COMPRESSION and NO_COMPRESSION options in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### CompressionAlgorithm + +Specify the backup compression algorithm. + +| Value | Description | +| --- | --- | +| NULL | Use the backup compression algorithm in sys.configurations. This is the default. | +| MS_XPRESS | SQL Server backup compression | +| QAT_DEFLATE | Intel QuickAssist Technology (QAT) backup compression | +| ZSTD | Zstandard backup compression | + +The CompressionAlgorithm option in DatabaseBackup uses the COMPRESSION ... ALGORITHM option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### CompressionLevel + +Specify the backup compression level. + +| Value | Description | +| --- | --- | +| LOW | Low | +| MEDIUM | Medium | +| HIGH | High | + +The CompressionLevel option in DatabaseBackup uses the COMPRESSION ... LEVEL option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### CopyOnly + +Perform a copy-only backup. + +| Value | Description | +| --- | --- | +| Y | Perform a copy-only backup. | +| N | Perform a normal backup. This is the default. | + +The CopyOnly option in DatabaseBackup uses the COPY_ONLY option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### ChangeBackupType + +Change the backup type if a differential or transaction-log backup cannot be performed. + +| Value | Description | +| --- | --- | +| Y | Change the backup type if a backup cannot be performed. | +| N | Skip the backup if a backup cannot be performed. This is the default. | + +DatabaseBackup checks differential_base_lsn in [sys.master_files](https://learn.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-master-files-transact-sql) to determine whether a differential backup can be performed. If a differential backup is not possible, then the database is skipped by default. Alternatively, you can set ChangeBackupType to Y to have a full backup performed instead. + +DatabaseBackup checks last_log_backup_lsn in [sys.database_recovery_status](https://learn.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-database-recovery-status-transact-sql) to determine whether a transaction log backup in full or bulk-logged recovery model can be performed. If a transaction log backup is not possible, then the database is skipped by default. Alternatively, you can set ChangeBackupType to Y to have a differential or full backup performed instead. + +### BackupSoftware + +Specify third-party backup software; otherwise, SQL Server native backup is performed. + +| Value | Description | +| --- | --- | +| NULL | SQL Server native backup (the default) | +| DATA_DOMAIN_BOOST | [Dell EMC Data Domain Boost](https://www.dell.com/en-us/shop/storage-servers-and-networking-for-business/sf/powerprotect-data-domain) | +| LITESPEED | [Quest LiteSpeed for SQL Server](https://www.quest.com/products/litespeed-for-sql-server) | +| SQLBACKUP | [Red Gate SQL Backup Pro](https://www.red-gate.com/products/sql-backup/) | +| SQLSAFE | [Idera SQL Safe Backup](https://www.idera.com/products/sql-safe-backup/) | + +### Checksum + +Enable backup checksums. + +| Value | Description | +| --- | --- | +| NULL | Use the checksum default in sys.configurations. This is the default. | +| Y | Enable backup checksums. | +| N | Do not enable backup checksums. | + +The Checksum option in DatabaseBackup uses the CHECKSUM option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### BlockSize + +Specify the physical blocksize in bytes. + +The BlockSize option in DatabaseBackup uses the BLOCKSIZE option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### BufferCount + +Specify the number of I/O buffers to be used for the backup operation. + +The BufferCount option in DatabaseBackup uses the BUFFERCOUNT option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### MaxTransferSize + +Specify the largest unit of transfer, in bytes, to be used between SQL Server and the backup media. + +The MaxTransferSize option in DatabaseBackup uses the MAXTRANSFERSIZE option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### NumberOfFiles + +Specify the number of backup files. The default is the number of backup directories and the maximum is 64 files. + +### MinBackupSizeForMultipleFiles + +Specify a minimum backup size in MB for when DatabaseBackup should back up to multiple files. + +### MaxFileSize + +Specify a maximum backup file size in MB. DatabaseBackup will dynamically calculate the number of backup files. + +### CompressionLevelNumeric + +Set the LiteSpeed, Red Gate SQL Backup Pro, or Idera SQL Safe Backup compression level. + +In LiteSpeed, the compression levels 0 to 8 are supported. In Red Gate SQL Backup Pro, levels 0 to 4 are supported, and in Idera SQL Safe Backup, levels 1 to 4 are supported. + +### Description + +Enter a description for the backup. + +The Description option in DatabaseBackup uses the DESCRIPTION option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### BackupSetName + +Enter a name for the backup set. + +The BackupSetName option in DatabaseBackup uses the NAME option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### Threads + +Specify the LiteSpeed, Red Gate SQL Backup Pro, or Idera SQL Safe Backup number of threads. The maximum number of threads is 32. + +### Throttle + +Specify the LiteSpeed maximum CPU usage, as a percentage. + +### Encrypt + +Encrypt the backup. + +| Value | Description | +| --- | --- | +| Y | Encrypt the backup. | +| N | Do not encrypt the backup. This is the default. | + +The Encrypt option in DatabaseBackup uses the ENCRYPTION option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### EncryptionAlgorithm + +Specify the type of encryption. + +| Value | Description | +| --- | --- | +| NULL | No encryption (the default) | +| RC2_40 | RC2 40-bit encryption (LiteSpeed) | +| RC2_56 | RC2 56-bit encryption (LiteSpeed) | +| RC2_112 | RC2 112-bit encryption (LiteSpeed) | +| RC2_128 | RC2 128-bit encryption (LiteSpeed) | +| TRIPLE_DES_3KEY | Triple DES encryption (SQL Server native encryption or LiteSpeed) | +| RC4_128 | RC4 128-bit encryption (LiteSpeed) | +| AES_128 | AES 128-bit encryption (SQL Server native encryption, LiteSpeed, Red Gate SQL Backup Pro, or Idera SQL Safe Backup) | +| AES_192 | AES 192-bit encryption (SQL Server native encryption or LiteSpeed) | +| AES_256 | AES 256-bit encryption (SQL Server native encryption, LiteSpeed, Red Gate SQL Backup Pro, or Idera SQL Safe Backup) | + +The EncryptionAlgorithm option in DatabaseBackup uses the ENCRYPTION and ALGORITHM options in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### ServerCertificate + +Server certificate that is used to encrypt the backup. + +The ServerCertificate option in DatabaseBackup uses the ENCRYPTION and SERVER CERTIFICATE options in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### ServerAsymmetricKey + +Asymmetric key that is used to encrypt the backup. + +The ServerAsymmetricKey option in DatabaseBackup uses the ENCRYPTION and SERVER ASYMMETRIC KEY options in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### EncryptionKey + +Key that is used to encrypt the backup. This is used with LiteSpeed, Red Gate SQL Backup Pro, and Idera SQL Safe Backup. + +### ReadWriteFileGroups + +Perform a backup of the primary filegroup and any read/write filegroups. + +| Value | Description | +| --- | --- | +| Y | Perform a backup of the primary filegroup and any read/write filegroups. | +| N | Perform a normal backup. This is the default. | + +The ReadWriteFileGroups option in DatabaseBackup uses the READ_WRITE_FILEGROUPS option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### OverrideBackupPreference + +Override the backup preference for availability groups. This option only applies to copy-only full backups and regular transaction log backups. + +| Value | Description | +| --- | --- | +| Y | Override the backup preference for availability groups. | +| N | Do not override the backup preference for availability groups. This is the default. | + +### NoRecovery + +Perform a backup of the tail of the log and leave the database in the RESTORING state. + +| Value | Description | +| --- | --- | +| Y | Perform a backup of the tail of the log. | +| N | Perform a normal backup. This is the default. | + +The NoRecovery option in DatabaseBackup uses the NORECOVERY option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### URL + +Specify the URL for backup to Azure Blob Storage. + +The URL option in DatabaseBackup uses the URL option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### Credential + +Specify a CREDENTIAL for backup to Azure Blob Storage. + +The Credential option in DatabaseBackup uses the CREDENTIAL option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### MirrorDirectory + +Specify one or more directories to perform a mirrored backup. + +The MirrorDirectory option in DatabaseBackup uses the MIRROR TO option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### MirrorCleanupTime + +Specify the time, in hours, after which the backup files are deleted in the mirror directories. If no time is specified, then no backup files are deleted. + +By default, backup files are deleted after each database is backed up and verified. Backup files are deleted only if the backup and verification of the database were successful. + +DatabaseBackup has a check to verify that transaction log backups that are newer than the most recent full or differential backup are not deleted. This is to guarantee that you can always perform a point-in-time restore. + +DatabaseBackup uses the extended stored procedure xp_delete_file to delete backup files. xp_delete_file deletes files based on a directory, a file extension, and a modified date. Therefore if you are using date or time tokens (e.g. {Year}, {Month}, and {Day}) in the parameters @DirectoryStructure or @AvailabilityGroupDirectoryStructure, then old backup files may be in different directories, and may not be deleted. + +Cleanup is not supported when backing up to URL. + +### MirrorCleanupMode + +Specify whether old backup files in the mirror directory should be deleted before or after the backup has been performed. + +| Value | Description | +| --- | --- | +| BEFORE_BACKUP | Delete old backup files before the backup has been performed. | +| AFTER_BACKUP | Delete old backup files after the backup has been performed. This is the default. | + +### MirrorURL + +Specify the URL for a mirrored backup to Azure Blob Storage. + +The MirrorURL option in DatabaseBackup uses the MIRROR TO URL option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### AvailabilityGroups + +Select availability groups. The keyword ALL_AVAILABILITY_GROUPS is supported. The hyphen character (-) is used to exclude availability groups, and the percent character (%) is used for wildcard selection. All of these operations can be combined by using the comma (,). + +| Value | Description | +| --- | --- | +| ALL_AVAILABILITY_GROUPS | All availability groups | +| AG1 | The availability group AG1 | +| AG1, AG2 | The availability groups AG1 and AG2 | +| ALL_AVAILABILITY_GROUPS, -AG1 | All availability groups except AG1 | +| %AG% | All availability groups that have “AG” in the name | +| %AG%, -AG1 | All availability groups that have “AG” in the name except AG1 | +| ALL_AVAILABILITY_GROUPS, -%AG% | All availability groups that do not have “AG” in the name | + +### Updateability + +Select READ_ONLY/READ_WRITE databases. + +| Value | Description | +| --- | --- | +| ALL | READ_ONLY and READ_WRITE databases. This is the default. | +| READ_ONLY | READ_ONLY databases | +| READ_WRITE | READ_WRITE databases | + +is_read_only in [sys.databases](https://learn.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-databases-transact-sql) is used to check if a database is READ_ONLY or READ_WRITE. + +### AdaptiveCompression + +Automatically select the optimal compression level based on CPU usage or disk I/O. This option is only available for LiteSpeed. + +| Value | Description | +| --- | --- | +| SIZE | Optimize the backup compression for size. | +| SPEED | Optimize the backup compression for speed. | + +### MinModificationLevel + +Specify a percentage for when a differential backup will be changed to a full backup. This option can only be used together with @ChangeBackupType = 'Y'. + +DatabaseBackup checks allocated_extent_page_count and modified_extent_page_count in [sys.dm_db_file_space_usage](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-db-file-space-usage-transact-sql) to calculate how much of a database has been modified. + +### MinDatabaseSizeForDifferentialBackup + +Specify the minimum database size for when a differential backup will be performed. If this parameter is used with @ChangeBackupType = 'Y', the backup type will be changed to a full backup for databases smaller than this size. + +DatabaseBackup checks allocated_extent_page_count in [sys.dm_db_file_space_usage](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-db-file-space-usage-transact-sql) to get the size of the database. + +### MinLogSizeSinceLastLogBackup + +Specify a minimum size (MB) for the amount of log that has been generated since the last log backup. This option can only be used together with @MinTimeSinceLastLogBackup. + +DatabaseBackup checks log_since_last_log_backup_mb in [sys.dm_db_log_stats](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-db-log-stats-transact-sql) to determine how much log has been generated since the last log backup. + +If the database is participating in an availability group as a secondary replica, the log will be backed up, regardless of this parameter. + +### MinTimeSinceLastLogBackup + +Specify a minimum time, in seconds, since the last log backup. This option can only be used together with @MinLogSizeSinceLastLogBackup. + +DatabaseBackup checks log_backup_time in [sys.dm_db_log_stats](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-db-log-stats-transact-sql) to determine when the transaction log was last backed up. + +If the database is participating in an availability group as a secondary replica, the log will be backed up, regardless of this parameter. + +### DataDomainBoostHost + +Specify the name of the Data Domain server. + +### DataDomainBoostUser + +Specify the name of the Data Domain user. + +### DataDomainBoostDevicePath + +Specify the name and the path of the Data Domain storage unit. + +### DataDomainBoostLockboxPath + +Specify the folder that contains the Data Domain lockbox file. + +### DataDomainBoostNoOutputTable + +Specify whether the output table from emc_run_backup is returned. + +| Value | Description | +| --- | --- | +| Y | The output table from emc_run_backup is not returned. | +| N | The output table from emc_run_backup is returned. This is the default. | + +### DirectoryStructure + +Specify the backup sub-directory structure for databases that are not in an availability group. + +You can use the following tokens: + +| Token | Description | +| --- | --- | +| ServerName | Server name | +| InstanceName | Instance name | +| ServiceName | Service name | +| DatabaseName | Database name | +| BackupType | Backup type | +| Partial | PARTIAL for partial backups | +| CopyOnly | COPY_ONLY for copy-only backups | +| Description | Backup description | +| BackupSetName | Backup set name | +| Year | Year | +| Month | Month | +| Day | Day | +| Week | Week | +| Weekday | Weekday | +| Hour | Hour | +| Minute | Minute | +| Second | Second | +| Millisecond | Millisecond | +| Microsecond | Microsecond | +| MajorVersion | Major version | +| MinorVersion | Minor version | +| DirectorySeparator | The directory separator | + +Default directory structure: {ServerName}${InstanceName}{DirectorySeparator}{DatabaseName}{DirectorySeparator}{BackupType}\_{Partial}\_{CopyOnly} + +Tokens that do not apply will be removed. For example, the token {CopyOnly} (and the associated \_) will be removed if it is not a copy-only backup. + +If the parameter is set to NULL, no sub-directories will be created. + +### AvailabilityGroupDirectoryStructure + +Specify the backup sub-directory structure for databases that are in an availability group. + +You can use the following tokens: + +| Token | Description | +| --- | --- | +| ServerName | Server name | +| InstanceName | Instance name | +| ServiceName | Service name | +| ClusterName | Cluster name | +| AvailabilityGroupName | Availability group name | +| DatabaseName | Database name | +| BackupType | Backup type | +| Partial | PARTIAL for partial backups | +| CopyOnly | COPY_ONLY for copy-only backups | +| Description | Backup description | +| BackupSetName | Backup set name | +| Year | Year | +| Month | Month | +| Day | Day | +| Week | Week | +| Weekday | Weekday | +| Hour | Hour | +| Minute | Minute | +| Second | Second | +| Millisecond | Millisecond | +| Microsecond | Microsecond | +| MajorVersion | Major version | +| MinorVersion | Minor version | +| DirectorySeparator | The directory separator | + +Default directory structure: {ClusterName}${AvailabilityGroupName}{DirectorySeparator}{DatabaseName}{DirectorySeparator}{BackupType}\_{Partial}\_{CopyOnly} + +Tokens that do not apply will be removed. For example, the token {CopyOnly} (and the associated \_) will be removed if it is not a copy-only backup. + +If the parameter is set to NULL, no sub-directories will be created. + +### DirectoryStructureCase + +Specify the case of the directory structure. + +| Value | Description | +| --- | --- | +| NULL | Original case | +| LOWER | Lower case | +| UPPER | Upper case | + +### FileName + +Specify the file name for databases that are not in an availability group. + +You can use the following tokens: + +| Token | Description | +| --- | --- | +| ServerName | Server name | +| InstanceName | Instance name | +| ServiceName | Service name | +| DatabaseName | Database name | +| BackupType | Backup type | +| Partial | PARTIAL for partial backups | +| CopyOnly | COPY_ONLY for copy-only backups | +| Description | Backup description | +| BackupSetName | Backup set name | +| Year | Year | +| Month | Month | +| Day | Day | +| Week | Week | +| Weekday | Weekday | +| Hour | Hour | +| Minute | Minute | +| Second | Second | +| Millisecond | Millisecond | +| Microsecond | Microsecond | +| FileNumber | The file number when you are backing up to multiple files | +| NumberOfFiles | The number of files when you are backing up to multiple files | +| FileExtension | The file extension | +| MajorVersion | Major version | +| MinorVersion | Minor version | + +Default file name: {ServerName}${InstanceName}\_{DatabaseName}\_{BackupType}\_{Partial}\_{CopyOnly}\_{Year}{Month}{Day}\_{Hour}{Minute}{Second}\_{FileNumber}.{FileExtension} + +Tokens that do not apply will be removed. For example, the token {CopyOnly} (and the associated \_) will be removed if it is not a copy-only backup. + +### AvailabilityGroupFileName + +Specify the file name for databases that are in an availability group. + +You can use the following tokens: + +| Token | Description | +| --- | --- | +| ServerName | Server name | +| InstanceName | Instance name | +| ServiceName | Service name | +| ClusterName | Cluster name | +| AvailabilityGroupName | Availability group name | +| DatabaseName | Database name | +| BackupType | Backup type | +| Partial | PARTIAL for partial backups | +| CopyOnly | COPY_ONLY for copy-only backups | +| Description | Backup description | +| BackupSetName | Backup set name | +| Year | Year | +| Month | Month | +| Day | Day | +| Week | Week | +| Weekday | Weekday | +| Hour | Hour | +| Minute | Minute | +| Second | Second | +| Millisecond | Millisecond | +| Microsecond | Microsecond | +| FileNumber | The file number when you are backing up to multiple files | +| NumberOfFiles | The number of files when you are backing up to multiple files | +| FileExtension | The file extension | +| MajorVersion | Major version | +| MinorVersion | Minor version | + +Default file name: {ClusterName}${AvailabilityGroupName}\_{DatabaseName}\_{BackupType}\_{Partial}\_{CopyOnly}\_{Year}{Month}{Day}\_{Hour}{Minute}{Second}\_{FileNumber}.{FileExtension} + +Tokens that do not apply will be removed. For example, the token {CopyOnly} (and the associated \_) will be removed if it is not a copy-only backup. + +### FileNameCase + +Specify the case of the file name. + +| Value | Description | +| --- | --- | +| NULL | Original case | +| LOWER | Lower case | +| UPPER | Upper case | + +### TokenTimezone + +Specify the time zone for the tokens in the directory structure and file name. + +| Value | Description | +| --- | --- | +| LOCAL | Local time zone | +| UTC | UTC time zone | + +### FileExtensionFull + +Specify the file extension for full backups. + +By default "bak" is used for SQL Server native backups, "bak" is used for LiteSpeed, "sqb" is used for Red Gate SQL Backup Pro, and "safe" is used for Idera SQL Safe Backup. + +### FileExtensionDiff + +Specify the file extension for differential backups. + +By default "bak" is used for SQL Server native backups, "bak" is used for LiteSpeed, "sqb" is used for Red Gate SQL Backup Pro, and "safe" is used for Idera SQL Safe Backup. + +### FileExtensionLog + +Specify the file extension for log backups. + +By default "trn" is used for SQL Server native backups, "trn" is used for LiteSpeed, "sqb" is used for Red Gate SQL Backup Pro, and "safe" is used for Idera SQL Safe Backup. + +### Init + +Specify whether the backup file should be overwritten. + +| Value | Description | +| --- | --- | +| Y | Overwrite the backup file. | +| N | Append the backup to the backup file. This is the default. | + +The Init option in DatabaseBackup uses the INIT option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### Format + +Specify whether a new media header should be created. + +| Value | Description | +| --- | --- | +| Y | Create a new media header. | +| N | Preserve the existing media header. This is the default. | + +The Format option in DatabaseBackup uses the FORMAT option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### ObjectLevelRecoveryMap + +Generate a map file during a backup for Object Level Recovery. This option is only supported in LiteSpeed. + +| Value | Description | +| --- | --- | +| Y | Generate a map file. | +| N | Do not generate a map file. This is the default. | + +### ExcludeLogShippedFromLogBackup + +Exclude databases configured for log shipping from log backups. + +| Value | Description | +| --- | --- | +| Y | Exclude databases configured for log shipping from log backups. This is the default. | +| N | Do not exclude databases configured for log shipping from log backups. | + +### ExcludeSeedingFromLogBackup + +Exclude databases from log backups while they are being seeded. + +| Value | Description | +| --- | --- | +| Y | Exclude databases from log backups while they are being seeded. | +| N | Do not exclude databases from log backups while they are being seeded. This is the default. | + +DatabaseBackup checks [sys.dm_hadr_physical_seeding_stats](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-objects/sys-dm-hadr-physical-seeding-stats) to determine whether the database is being seeded. + +### DirectoryCheck + +Check if the backup root directory exists. + +| Value | Description | +| --- | --- | +| Y | Check if the backup root directory exists. This is the default. | +| N | Do not check if the backup root directory exists. | + +### BackupOptions + +Options for backup to AWS S3 storage. + +The BackupOptions option in DatabaseBackup uses the BACKUP_OPTIONS option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### Stats + +Display the percentage completion of the backup operation. + +The Stats option in DatabaseBackup uses the STATS option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### ExpireDate + +Specify when the backup set for this backup can be overwritten. + +The ExpireDate option in DatabaseBackup uses the EXPIREDATE option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### RetainDays + +Specify the number of days that must elapse before this backup media set can be overwritten. + +The RetainDays option in DatabaseBackup uses the RETAINDAYS option in the SQL Server [BACKUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql) command. + +### AllowNonCopyOnlyBackupsOnForwarder + +Specify whether non-copy-only backups should be allowed on the distributed availability group forwarder. + +| Value | Description | +| --- | --- | +| Y | Non-copy-only backups are allowed on the distributed availability group forwarder. | +| N | Non-copy-only backups are not allowed on the distributed availability group forwarder. This is the default. | + +### StringDelimiter + +Specify the string delimiter. By default, the string delimiter is the comma. + +### DatabaseOrder + +Specify the database order. + +| Value | Description | +| --- | --- | +| NULL | The order in which the databases have been specified. Then ascending by the database name. This is the default. | +| DATABASE_NAME_ASC | Ascending by the database name | +| DATABASE_NAME_DESC | Descending by the database name | +| DATABASE_SIZE_ASC | Ascending by the database size | +| DATABASE_SIZE_DESC | Descending by the database size | +| LOG_SIZE_SINCE_LAST_LOG_BACKUP_ASC | Ascending by log_since_last_log_backup_mb in sys.dm_db_log_stats | +| LOG_SIZE_SINCE_LAST_LOG_BACKUP_DESC | Descending by log_since_last_log_backup_mb in sys.dm_db_log_stats | + +### DatabasesInParallel + +Process databases in parallel. + +| Value | Description | +| --- | --- | +| Y | Process databases in parallel. | +| N | Process databases one at a time. This is the default. | + +You can process databases in parallel by creating multiple jobs with the same parameters, and adding the parameter @DatabasesInParallel = 'Y'. + +### LogToTable + +Log commands to the table dbo.CommandLog. + +| Value | Description | +| --- | --- | +| Y | Log commands to the table. | +| N | Do not log commands to the table. This is the default. | + +### Execute + +Execute commands. By default, the commands are executed normally. If this parameter is set to N, then the commands are printed only. + +| Value | Description | +| --- | --- | +| Y | Execute commands. This is the default. | +| N | Only print commands. | + +## Examples + +### A. Back up all user databases, using checksums and compression; verify the backup; and delete old backup files + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@Directory = 'C:\Backup', +@BackupType = 'FULL', +@Verify = 'Y', +@Compress = 'Y', +@Checksum = 'Y', +@CleanupTime = 24 +``` + +### B. Back up all user databases to a network share, and verify the backup + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@Directory = '\\Server1\Backup', +@BackupType = 'FULL', +@Verify = 'Y' +``` + +### C. Back up all user databases across four network shares, and verify the backup + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@Directory = '\\Server1\Backup, \\Server2\Backup, \\Server3\Backup, \\Server4\Backup', +@BackupType = 'FULL', +@Verify = 'Y', +@NumberOfFiles = 4 +``` + +### D. Back up all user databases to 64 files, using checksums and compression and setting the buffer count and the maximum transfer size + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@Directory = 'C:\Backup', +@BackupType = 'FULL', +@Compress = 'Y', +@Checksum = 'Y', +@BufferCount = 50, +@MaxTransferSize = 4194304, +@NumberOfFiles = 64 +``` + +### E. Back up all user databases to Azure Blob Storage, using compression + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@URL = 'https://myaccount.blob.core.windows.net/mycontainer', +@BackupType = 'FULL', +@Compress = 'Y', +@Verify = 'Y' +``` + +### F. Back up all user databases to S3 storage, using compression + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@URL = 's3://myaccount.s3.us-east-1.amazonaws.com/mybucket', +@BackupType = 'FULL', +@Compress = 'Y', +@Verify = 'Y', +@MaxTransferSize = 20971520, +@BackupOptions = '{"s3": {"region":"us-east-1"}}' +``` + +### G. Back up the transaction log of all user databases, using the option to change the backup type if a log backup cannot be performed + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@Directory = 'C:\Backup', +@BackupType = 'LOG', +@ChangeBackupType = 'Y' +``` + +### H. Back up all user databases, using compression, encryption, and a server certificate + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@Directory = 'C:\Backup', +@BackupType = 'FULL', +@Compress = 'Y', +@Encrypt = 'Y', +@EncryptionAlgorithm = 'AES_256', +@ServerCertificate = 'MyCertificate' +``` + +### I. Back up all user databases, using compression, encryption, and LiteSpeed, and limiting the CPU usage to 10 percent + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@Directory = 'C:\Backup', +@BackupType = 'FULL', +@BackupSoftware = 'LITESPEED', +@Compress = 'Y', +@Encrypt = 'Y', +@EncryptionAlgorithm = 'AES_256', +@EncryptionKey = 'MyPassword', +@Throttle = 10 +``` + +### J. Back up all user databases, using compression, encryption, and Red Gate SQL Backup Pro + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@Directory = 'C:\Backup', +@BackupType = 'FULL', +@BackupSoftware = 'SQLBACKUP', +@Compress = 'Y', +@Encrypt = 'Y', +@EncryptionAlgorithm = 'AES_256', +@EncryptionKey = 'MyPassword' +``` + +### K. Back up all user databases, using compression, encryption, and Idera SQL Safe Backup + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@Directory = 'C:\Backup', +@BackupType = 'FULL', +@BackupSoftware = 'SQLSAFE', +@Compress = 'Y', +@Encrypt = 'Y', +@EncryptionAlgorithm = 'AES_256', +@EncryptionKey = '8tPyzp4i1uF/ydAN1DqevdXDeVoryWRL' +``` + +### L. Back up all user databases, using mirrored backups + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@Directory = 'C:\Backup', +@MirrorDirectory = 'D:\Backup', +@BackupType = 'FULL', +@Compress = 'Y', +@Verify = 'Y', +@CleanupTime = 24, +@MirrorCleanupTime = 48 +``` + +### M. Back up all user databases, using Data Domain Boost + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@BackupType = 'FULL', +@Checksum = 'Y', +@BackupSoftware = 'DATA_DOMAIN_BOOST', +@DataDomainBoostHost = 'Host', +@DataDomainBoostUser = 'User', +@DataDomainBoostDevicePath = '/DevicePath', +@DataDomainBoostLockboxPath = 'C:\Program Files\DPSAPPS\common\lockbox', +@DataDomainBoostNoOutputTable = 'Y', +@CleanupTime = 24 +``` + +### N. Back up all user databases, with the default directory structure and file names + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@Directory = 'C:\Backup', +@BackupType = 'FULL', +@DirectoryStructure = '{ServerName}${InstanceName}{DirectorySeparator}{DatabaseName}{DirectorySeparator}{BackupType}_{Partial}_{CopyOnly}', +@AvailabilityGroupDirectoryStructure = '{ClusterName}${AvailabilityGroupName}{DirectorySeparator}{DatabaseName}{DirectorySeparator}{BackupType}_{Partial}_{CopyOnly}', +@FileName = '{ServerName}${InstanceName}_{DatabaseName}_{BackupType}_{Partial}_{CopyOnly}_{Year}{Month}{Day}_{Hour}{Minute}{Second}_{FileNumber}.{FileExtension}', +@AvailabilityGroupFileName = '{ClusterName}${AvailabilityGroupName}_{DatabaseName}_{BackupType}_{Partial}_{CopyOnly}_{Year}{Month}{Day}_{Hour}{Minute}{Second}_{FileNumber}.{FileExtension}' +``` + +### O. Back up all user databases, to a directory structure without the server name, instance name, cluster name, and availability group name + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@Directory = 'C:\Backup', +@BackupType = 'FULL', +@DirectoryStructure = '{DatabaseName}{DirectorySeparator}{BackupType}_{Partial}_{CopyOnly}', +@AvailabilityGroupDirectoryStructure = '{DatabaseName}{DirectorySeparator}{BackupType}_{Partial}_{CopyOnly}' +``` + +### P. Back up all user databases, without creating any sub-directories + +```sql +EXECUTE dbo.DatabaseBackup +@Databases = 'USER_DATABASES', +@Directory = 'C:\Backup', +@BackupType = 'FULL', +@DirectoryStructure = NULL, +@AvailabilityGroupDirectoryStructure = NULL +``` + +## Execution + +You can execute the stored procedures from T-SQL job steps, and use [MaintenanceSolution.sql](/MaintenanceSolution.sql) to create the jobs. diff --git a/docs/sql-server-index-and-statistics-maintenance.md b/docs/sql-server-index-and-statistics-maintenance.md new file mode 100644 index 00000000..580c5212 --- /dev/null +++ b/docs/sql-server-index-and-statistics-maintenance.md @@ -0,0 +1,527 @@ + + +# SQL Server Index and Statistics Maintenance + +This documentation is generated from [ola.hallengren.com/sql-server-index-and-statistics-maintenance.html](https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html), which is the primary source. + +IndexOptimize is the SQL Server Maintenance Solution’s stored procedure for rebuilding and reorganizing indexes and updating statistics. + +## Download + +Download the script for your platform: + +- [MaintenanceSolution.sql](/MaintenanceSolution.sql): SQL Server 2017, SQL Server 2019, SQL Server 2022, SQL Server 2025, and Azure SQL Managed Instance. This script creates all the objects and jobs that you need. +- [MaintenanceSolutionAzureSQLDatabase.sql](/MaintenanceSolutionAzureSQLDatabase.sql): Azure SQL Database. This script creates the objects for integrity check and index and statistics maintenance. + +You can also [download](https://github.com/olahallengren/sql-server-maintenance-solution) the objects as separate scripts. The SQL Server Maintenance Solution is available on [GitHub](https://github.com/olahallengren/sql-server-maintenance-solution). + +## License + +The SQL Server Maintenance Solution is [free](/LICENSE). + +## Parameters + +### Databases + +Select databases. The keywords SYSTEM_DATABASES, USER_DATABASES, ALL_DATABASES, and AVAILABILITY_GROUP_DATABASES are supported. The hyphen character (-) is used to exclude databases, and the percent character (%) is used for wildcard selection. All of these operations can be combined by using the comma (,). + +| Value | Description | +| --- | --- | +| SYSTEM_DATABASES | All system databases (master, msdb, and model) | +| USER_DATABASES | All user databases | +| ALL_DATABASES | All databases | +| AVAILABILITY_GROUP_DATABASES | All databases in availability groups | +| USER_DATABASES, -AVAILABILITY_GROUP_DATABASES | All user databases that are not in availability groups | +| Db1 | The database Db1 | +| Db1, Db2 | The databases Db1 and Db2 | +| USER_DATABASES, -Db1 | All user databases except Db1 | +| %Db% | All databases that have “Db” in the name | +| %Db%, -Db1 | All databases that have “Db” in the name except Db1 | +| ALL_DATABASES, -%Db% | All databases that do not have “Db” in the name | + +### FragmentationLow + +Specify index maintenance operations to be performed on a low-fragmented index. + +| Value | Description | +| --- | --- | +| INDEX_REBUILD_ONLINE | Rebuild index online. | +| INDEX_REBUILD_OFFLINE | Rebuild index offline. | +| INDEX_REORGANIZE | Reorganize index. | +| INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE | Rebuild index online. Rebuild index offline if online rebuilding is not supported on an index. | +| INDEX_REBUILD_ONLINE,INDEX_REORGANIZE | Rebuild index online. Reorganize index if online rebuilding is not supported on an index. | +| INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE | Reorganize index. Rebuild index online if reorganizing is not supported on an index. Rebuild index offline if reorganizing and online rebuilding are not supported on an index. | +| NULL | Do not perform index maintenance. This is the default for a low-fragmented index. | + +An online index rebuild or an index reorganization is not always possible. Because of this, you can specify multiple index-maintenance operations for each fragmentation group. These operations are prioritized from left to right: If the first operation is supported for the index, then that operation is used; if the first operation is not supported, then the second operation is used (if supported), and so on. If none of the specified operations are supported for an index, then that index is not maintained. + +IndexOptimize uses the SQL Server [ALTER INDEX](https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-index-transact-sql) command: REBUILD WITH (ONLINE = ON) to rebuild indexes online, REBUILD WITH (ONLINE = OFF) to rebuild indexes offline, and REORGANIZE to reorganize indexes. + +### FragmentationMedium + +Specify index maintenance operations to be performed on a medium-fragmented index. + +| Value | Description | +| --- | --- | +| INDEX_REBUILD_ONLINE | Rebuild index online. | +| INDEX_REBUILD_OFFLINE | Rebuild index offline. | +| INDEX_REORGANIZE | Reorganize index. | +| INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE | Rebuild index online. Rebuild index offline if online rebuilding is not supported on an index. | +| INDEX_REBUILD_ONLINE,INDEX_REORGANIZE | Rebuild index online. Reorganize index if online rebuilding is not supported on an index. | +| INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE | Reorganize index. Rebuild index online if reorganizing is not supported on an index. Rebuild index offline if reorganizing and online rebuilding are not supported on an index. This is the default for a medium-fragmented index. | +| NULL | Do not perform index maintenance. | + +An online index rebuild or an index reorganization is not always possible. Because of this, you can specify multiple index-maintenance operations for each fragmentation group. These operations are prioritized from left to right: If the first operation is supported for the index, then that operation is used; if the first operation is not supported, then the second operation is used (if supported), and so on. If none of the specified operations are supported for an index, then that index is not maintained. + +IndexOptimize uses the SQL Server [ALTER INDEX](https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-index-transact-sql) command: REBUILD WITH (ONLINE = ON) to rebuild indexes online, REBUILD WITH (ONLINE = OFF) to rebuild indexes offline, and REORGANIZE to reorganize indexes. + +### FragmentationHigh + +Specify index maintenance operations to be performed on a high-fragmented index. + +| Value | Description | +| --- | --- | +| INDEX_REBUILD_ONLINE | Rebuild index online. | +| INDEX_REBUILD_OFFLINE | Rebuild index offline. | +| INDEX_REORGANIZE | Reorganize index. | +| INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE | Rebuild index online. Rebuild index offline if online rebuilding is not supported on an index.
This is the default for a high-fragmented index. | +| INDEX_REBUILD_ONLINE,INDEX_REORGANIZE | Rebuild index online. Reorganize index if online rebuilding is not supported on an index. | +| INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE | Reorganize index. Rebuild index online if reorganizing is not supported on an index. Rebuild index offline if reorganizing and online rebuilding are not supported on an index. | +| NULL | Do not perform index maintenance. | + +An online index rebuild or an index reorganization is not always possible. Because of this, you can specify multiple index-maintenance operations for each fragmentation group. These operations are prioritized from left to right: If the first operation is supported for the index, then that operation is used; if the first operation is not supported, then the second operation is used (if supported), and so on. If none of the specified operations are supported for an index, then that index is not maintained. + +IndexOptimize uses the SQL Server [ALTER INDEX](https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-index-transact-sql) command: REBUILD WITH (ONLINE = ON) to rebuild indexes online, REBUILD WITH (ONLINE = OFF) to rebuild indexes offline, and REORGANIZE to reorganize indexes. + +### FragmentationLevel1 + +Set the lower limit, as a percentage, for medium fragmentation. The default is 5 percent. This is based on Microsoft’s recommendation in [Books Online](https://learn.microsoft.com/en-us/sql/relational-databases/indexes/reorganize-and-rebuild-indexes). + +IndexOptimize checks avg_fragmentation_in_percent in [sys.dm_db_index_physical_stats](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-db-index-physical-stats-transact-sql) to determine the fragmentation. + +### FragmentationLevel2 + +Set the lower limit, as a percentage, for high fragmentation. The default is 30 percent. This is based on Microsoft’s recommendation in [Books Online](https://learn.microsoft.com/en-us/sql/relational-databases/indexes/reorganize-and-rebuild-indexes). + +IndexOptimize checks avg_fragmentation_in_percent in [sys.dm_db_index_physical_stats](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-db-index-physical-stats-transact-sql) to determine the fragmentation. + +### MinNumberOfPages + +Set a size, in pages; indexes with a smaller number of pages are skipped for index maintenance. The default is 1000 pages. This is based on Microsoft’s recommendation. + +IndexOptimize checks page_count in [sys.dm_db_index_physical_stats](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-db-index-physical-stats-transact-sql) to determine the size of the index. + +### MaxNumberOfPages + +Set a size, in pages; indexes with a greater number of pages are skipped for index maintenance. The default is no limitation. + +IndexOptimize checks page_count in [sys.dm_db_index_physical_stats](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-db-index-physical-stats-transact-sql) to determine the size of the index. + +### SortInTempdb + +Use tempdb for sort operations when rebuilding indexes. + +| Value | Description | +| --- | --- | +| Y | Use tempdb for sort operations when rebuilding indexes. | +| N | Do not use tempdb for sort operations when rebuilding indexes. This is the default. | + +The SortInTempdb option in IndexOptimize uses the SORT_IN_TEMPDB option in the SQL Server [ALTER INDEX](https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-index-transact-sql) command. + +### MaxDOP + +Specify the number of CPUs to use when rebuilding indexes. If this number is not specified, the global maximum degree of parallelism is used. + +The MaxDOP option in IndexOptimize uses the MAXDOP option in the SQL Server [ALTER INDEX](https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-index-transact-sql) command. + +### FillFactor + +Indicate, as a percentage, how full the pages should be made when rebuilding indexes. If a percentage is not specified, the fill factor in [sys.indexes](https://learn.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-indexes-transact-sql) is used. + +The FillFactor option in IndexOptimize uses the FILLFACTOR option in the SQL Server [ALTER INDEX](https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-index-transact-sql) command. + +### PadIndex + +Apply the percentage of free space that the fill factor specifies to the intermediate-level pages of the index. + +| Value | Description | +| --- | --- | +| Y | Apply the percentage of free space that the fill factor specifies to the intermediate-level pages of the index. | +| N | The intermediate-level pages of the index are filled to near capacity. | +| NULL | Leave the pad index setting unchanged. This is the default. | + +The PadIndex option in IndexOptimize uses the PAD_INDEX option in the SQL Server [ALTER INDEX](https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-index-transact-sql) command. + +### DataCompression + +Set the data compression type that is applied when rebuilding indexes. + +| Value | Description | +| --- | --- | +| NONE | Rebuild the index without data compression, removing any existing compression. | +| ROW | Rebuild the index with row compression. | +| PAGE | Rebuild the index with page compression. | +| NULL | Leave the existing data compression setting unchanged. This is the default. | + +The DataCompression option in IndexOptimize uses the DATA_COMPRESSION option in the SQL Server [ALTER INDEX](https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-index-transact-sql) command. + +### WaitAtLowPriorityMaxDuration + +The time, in minutes, that an online index rebuild operation will wait for low-priority locks. + +The WaitAtLowPriorityMaxDuration option in IndexOptimize uses the WAIT_AT_LOW_PRIORITY and MAX_DURATION options in the SQL Server [ALTER INDEX](https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-index-transact-sql) command. + +### WaitAtLowPriorityAbortAfterWait + +The action that will be performed after an online index rebuild operation has been waiting for low-priority locks. + +| Value | Description | +| --- | --- | +| NONE | Continue waiting for locks with normal priority. | +| SELF | Abort the online index rebuild operation. | +| BLOCKERS | Kill user transactions that block the online index rebuild operation. | + +The WaitAtLowPriorityAbortAfterWait option in IndexOptimize uses the WAIT_AT_LOW_PRIORITY and ABORT_AFTER_WAIT options in the SQL Server [ALTER INDEX](https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-index-transact-sql) command. + +### Resumable + +Specify whether an online index operation is resumable. + +| Value | Description | +| --- | --- | +| Y | Index operation is resumable. | +| N | Index operation is not resumable. This is the default. | + +The Resumable option in IndexOptimize uses the RESUMABLE option in the SQL Server [ALTER INDEX](https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-index-transact-sql) command. + +### LOBCompaction + +Compact pages that contain large object (LOB) columns when reorganizing indexes. + +| Value | Description | +| --- | --- | +| Y | Compact pages that contain LOB columns when reorganizing indexes. This is the default. | +| N | Do not compact pages that contain LOB columns when reorganizing indexes. | + +The LOBCompaction option in IndexOptimize uses the LOB_COMPACTION option in the SQL Server [ALTER INDEX](https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-index-transact-sql) command. + +### UpdateStatistics + +Update statistics. + +| Value | Description | +| --- | --- | +| ALL | Update index and column statistics. | +| INDEX | Update index statistics. | +| COLUMNS | Update column statistics. | +| NULL | Do not perform statistics maintenance. This is the default. | + +IndexOptimize uses the SQL Server [UPDATE STATISTICS](https://learn.microsoft.com/en-us/sql/t-sql/statements/update-statistics-transact-sql) command to update statistics. + +### OnlyModifiedStatistics + +Update statistics only if any rows have been modified since the most recent statistics update. + +| Value | Description | +| --- | --- | +| Y | Update statistics only if any rows have been modified since the most recent statistics update. | +| N | Update statistics regardless of whether any rows have been modified. This is the default. | + +IndexOptimize checks modification_counter in [sys.dm_db_stats_properties](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-db-stats-properties-transact-sql). For incremental statistics it checks modification_counter in [sys.dm_db_incremental_stats_properties](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-db-incremental-stats-properties-transact-sql). + +### StatisticsModificationLevel + +Specify a percentage of modified rows for when the statistics should be updated. Statistics will also be updated when the number of modified rows has reached a decreasing, dynamic threshold, SQRT(number of rows \* 1000). + +IndexOptimize checks the columns modification_counter and rows in [sys.dm_db_stats_properties](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-db-stats-properties-transact-sql). For incremental statistics it checks the columns modification_counter and rows in [sys.dm_db_incremental_stats_properties](https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-db-incremental-stats-properties-transact-sql). + +### StatisticsSample + +Indicate, as a percentage, how much of a table is gathered when updating statistics. A value of 100 is equivalent to a full scan. If no value is specified, then SQL Server automatically computes the required sample. + +The StatisticsSample option in IndexOptimize uses the SAMPLE and FULLSCAN options in the SQL Server [UPDATE STATISTICS](https://learn.microsoft.com/en-us/sql/t-sql/statements/update-statistics-transact-sql) command. + +### StatisticsPersistSample + +Specify whether the sampling percentage is persisted for subsequent statistics updates. + +| Value | Description | +| --- | --- | +| Y | Persist the sampling percentage. | +| N | Remove any existing sampling persistence. | +| NULL | Leave the existing persistence setting unchanged. This is the default. | + +The StatisticsPersistSample option in IndexOptimize uses the PERSIST_SAMPLE_PERCENT option in the SQL Server [UPDATE STATISTICS](https://learn.microsoft.com/en-us/sql/t-sql/statements/update-statistics-transact-sql) command. + +StatisticsPersistSample can only be used together with StatisticsSample. + +### StatisticsResample + +Update statistics with the most recent sample. + +| Value | Description | +| --- | --- | +| Y | Update statistics with the most recent sample. | +| N | Let SQL Server automatically compute the required sample. This is the default. | + +The StatisticsResample option in IndexOptimize uses the RESAMPLE option in the SQL Server [UPDATE STATISTICS](https://learn.microsoft.com/en-us/sql/t-sql/statements/update-statistics-transact-sql) command. + +You cannot combine the options StatisticsSample and StatisticsResample. + +### PartitionLevel + +Maintain partitioned indexes on the partition level. If this parameter is set to Y, the fragmentation level and page count are checked for each partition. The appropriate index maintenance (reorganize or rebuild) is then performed for each partition. + +| Value | Description | +| --- | --- | +| Y | Maintain partitioned indexes on the partition level. This is the default. | +| N | Maintain partitioned indexes on the index level. | + +### MSShippedObjects + +Maintain indexes and statistics on objects that are created by internal SQL Server components. + +| Value | Description | +| --- | --- | +| Y | Maintain indexes and statistics on objects that are created by internal SQL Server components. | +| N | Do not maintain indexes and statistics on objects that are created by internal SQL Server components. This is the default. | + +IndexOptimize checks is_ms_shipped in [sys.objects](https://learn.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-objects-transact-sql) to determine whether an object was created by an internal SQL Server component. + +### Indexes + +Select indexes. If this parameter is not specified, all indexes are selected. The ALL_INDEXES keyword is supported. The hyphen character (-) is used to exclude indexes, and the percent character (%) is used for wildcard selection. All of these operations can be combined by using the comma (,). + +| Value | Description | +| --- | --- | +| ALL_INDEXES | All indexes | +| Db1.Schema1.Tbl1.Idx1 | The index Idx1 on the object Schema1.Tbl1 in the database Db1 | +| Db1.Schema1.Tbl1.Idx1, Db2.Schema2.Tbl2.Idx2 | The index Idx1 on the object Schema1.Tbl1 in the database Db1 and the index Idx2 on the object Schema2.Tbl2 in the database Db2 | +| Db1.Schema1.Tbl1 | All indexes on the object Schema1.Tbl1 in the database Db1 | +| Db1.Schema1.Tbl1, Db2.Schema2.Tbl2 | All indexes on the object Schema1.Tbl1 in the database Db1 and all indexes on the object Schema2.Tbl2 in the database Db2 | +| Db1.Schema1.% | All indexes in the schema Schema1 in the database Db1 | +| %.Schema1.% | All indexes in the schema Schema1 in all databases | +| ALL_INDEXES, -Db1.Schema1.Tbl1.Idx1 | All indexes except the index Idx1 on the object Schema1.Tbl1 in the database Db1 | +| ALL_INDEXES, -Db1.Schema1.Tbl1 | All indexes except indexes on the object Schema1.Tbl1 in the database Db1 | + +### TimeLimit + +Set the time, in seconds, after which no commands are executed. By default, the time is not limited. + +### Delay + +Set the delay, in seconds, between index commands. By default, there is no delay. + +### AvailabilityGroups + +Select availability groups. The keyword ALL_AVAILABILITY_GROUPS is supported. The hyphen character (-) is used to exclude availability groups, and the percent character (%) is used for wildcard selection. All of these operations can be combined by using the comma (,). + +| Value | Description | +| --- | --- | +| ALL_AVAILABILITY_GROUPS | All availability groups | +| AG1 | The availability group AG1 | +| AG1, AG2 | The availability groups AG1 and AG2 | +| ALL_AVAILABILITY_GROUPS, -AG1 | All availability groups except AG1 | +| %AG% | All availability groups that have “AG” in the name | +| %AG%, -AG1 | All availability groups that have “AG” in the name except AG1 | +| ALL_AVAILABILITY_GROUPS, -%AG% | All availability groups that do not have “AG” in the name | + +### LockTimeout + +Set the time, in seconds, that a command waits for a lock to be released. By default, the time is not limited. + +The LockTimeout option uses the [SET LOCK_TIMEOUT](https://learn.microsoft.com/en-us/sql/t-sql/statements/set-lock-timeout-transact-sql) statement in SQL Server. + +### LockMessageSeverity + +Set the severity for lock timeouts and deadlocks. + +| Value | Description | +| --- | --- | +| 10 | This is an informational message. | +| 16 | This is an error message. This is the default. | + +### StringDelimiter + +Specify the string delimiter. By default, the string delimiter is the comma. + +### DatabaseOrder + +Specify the database order. + +| Value | Description | +| --- | --- | +| NULL | The order in which the databases have been specified. Then ascending by the database name. This is the default. | +| DATABASE_NAME_ASC | Ascending by the database name | +| DATABASE_NAME_DESC | Descending by the database name | +| DATABASE_SIZE_ASC | Ascending by the database size | +| DATABASE_SIZE_DESC | Descending by the database size | + +### DatabasesInParallel + +Process databases in parallel. + +| Value | Description | +| --- | --- | +| Y | Process databases in parallel. | +| N | Process databases one at a time. This is the default. | + +You can process databases in parallel by creating multiple jobs with the same parameters, and adding the parameter @DatabasesInParallel = 'Y'. + +### ExecuteAsUser + +Change the execution context to a user. The user can be dbo or any other user. The user has to exist in all databases that you are working with. + +The ExecuteAsUser option in IndexOptimize uses the [EXECUTE AS](https://learn.microsoft.com/en-us/sql/t-sql/statements/execute-as-transact-sql) command in SQL Server. + +### LogToTable + +Log commands to the table dbo.CommandLog. + +| Value | Description | +| --- | --- | +| Y | Log commands to the table. | +| N | Do not log commands to the table. This is the default. | + +### Execute + +Execute commands. By default, the commands are executed normally. If this parameter is set to N, then the commands are printed only. + +| Value | Description | +| --- | --- | +| Y | Execute commands. This is the default. | +| N | Only print commands. | + +## Examples + +### A. Rebuild or reorganize all indexes with fragmentation on all user databases + +```sql +EXECUTE dbo.IndexOptimize +@Databases = 'USER_DATABASES', +@FragmentationLow = NULL, +@FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationLevel1 = 5, +@FragmentationLevel2 = 30 +``` + +### B. Rebuild or reorganize all indexes with fragmentation and update modified statistics on all user databases + +```sql +EXECUTE dbo.IndexOptimize +@Databases = 'USER_DATABASES', +@FragmentationLow = NULL, +@FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationLevel1 = 5, +@FragmentationLevel2 = 30, +@UpdateStatistics = 'ALL', +@OnlyModifiedStatistics = 'Y' +``` + +### C. Update statistics on all user databases + +```sql +EXECUTE dbo.IndexOptimize +@Databases = 'USER_DATABASES', +@FragmentationLow = NULL, +@FragmentationMedium = NULL, +@FragmentationHigh = NULL, +@UpdateStatistics = 'ALL' +``` + +### D. Update modified statistics on all user databases + +```sql +EXECUTE dbo.IndexOptimize +@Databases = 'USER_DATABASES', +@FragmentationLow = NULL, +@FragmentationMedium = NULL, +@FragmentationHigh = NULL, +@UpdateStatistics = 'ALL', +@OnlyModifiedStatistics = 'Y' +``` + +### E. Rebuild or reorganize all indexes with fragmentation on all user databases, performing sort operations in tempdb and using all available CPUs + +```sql +EXECUTE dbo.IndexOptimize +@Databases = 'USER_DATABASES', +@FragmentationLow = NULL, +@FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationLevel1 = 5, +@FragmentationLevel2 = 30, +@SortInTempdb = 'Y', +@MaxDOP = 0 +``` + +### F. Rebuild or reorganize all indexes with fragmentation on all user databases, using the option to maintain partitioned indexes on the partition level + +```sql +EXECUTE dbo.IndexOptimize +@Databases = 'USER_DATABASES', +@FragmentationLow = NULL, +@FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationLevel1 = 5, +@FragmentationLevel2 = 30, +@PartitionLevel = 'Y' +``` + +### G. Rebuild or reorganize all indexes with fragmentation on all user databases, with a time limit so that no commands are executed after 3600 seconds + +```sql +EXECUTE dbo.IndexOptimize +@Databases = 'USER_DATABASES', +@FragmentationLow = NULL, +@FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationLevel1 = 5, +@FragmentationLevel2 = 30, +@TimeLimit = 3600 +``` + +### H. Rebuild or reorganize all indexes with fragmentation on the table Production.Product in the database AdventureWorks + +```sql +EXECUTE dbo.IndexOptimize +@Databases = 'AdventureWorks', +@FragmentationLow = NULL, +@FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationLevel1 = 5, +@FragmentationLevel2 = 30, +@Indexes = 'AdventureWorks.Production.Product' +``` + +### I. Rebuild or reorganize all indexes with fragmentation except indexes on the table Production.Product in the database AdventureWorks + +```sql +EXECUTE dbo.IndexOptimize +@Databases = 'USER_DATABASES', +@FragmentationLow = NULL, +@FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationLevel1 = 5, +@FragmentationLevel2 = 30, +@Indexes = 'ALL_INDEXES, -AdventureWorks.Production.Product' +``` + +### J. Rebuild or reorganize all indexes with fragmentation on all user databases and log the results to a table + +```sql +EXECUTE dbo.IndexOptimize +@Databases = 'USER_DATABASES', +@FragmentationLow = NULL, +@FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', +@FragmentationLevel1 = 5, +@FragmentationLevel2 = 30, +@LogToTable = 'Y' +``` + +## Execution + +You can execute the stored procedures from T-SQL job steps, and use [MaintenanceSolution.sql](/MaintenanceSolution.sql) to create the jobs. diff --git a/docs/sql-server-integrity-check.md b/docs/sql-server-integrity-check.md new file mode 100644 index 00000000..99b179f2 --- /dev/null +++ b/docs/sql-server-integrity-check.md @@ -0,0 +1,357 @@ + + +# SQL Server Integrity Check + +This documentation is generated from [ola.hallengren.com/sql-server-integrity-check.html](https://ola.hallengren.com/sql-server-integrity-check.html), which is the primary source. + +DatabaseIntegrityCheck is the SQL Server Maintenance Solution’s stored procedure for checking the integrity of databases. + +## Download + +Download the script for your platform: + +- [MaintenanceSolution.sql](/MaintenanceSolution.sql): SQL Server 2017, SQL Server 2019, SQL Server 2022, SQL Server 2025, and Azure SQL Managed Instance. This script creates all the objects and jobs that you need. +- [MaintenanceSolutionAzureSQLDatabase.sql](/MaintenanceSolutionAzureSQLDatabase.sql): Azure SQL Database. This script creates the objects for integrity check and index and statistics maintenance. + +You can also [download](https://github.com/olahallengren/sql-server-maintenance-solution) the objects as separate scripts. The SQL Server Maintenance Solution is available on [GitHub](https://github.com/olahallengren/sql-server-maintenance-solution). + +## License + +The SQL Server Maintenance Solution is [free](/LICENSE). + +## Parameters + +### Databases + +Select databases. The keywords SYSTEM_DATABASES, USER_DATABASES, ALL_DATABASES, and AVAILABILITY_GROUP_DATABASES are supported. The hyphen character (-) is used to exclude databases, and the percent character (%) is used for wildcard selection. All of these operations can be combined by using the comma (,). + +| Value | Description | +| --- | --- | +| SYSTEM_DATABASES | All system databases (master, msdb, and model) | +| USER_DATABASES | All user databases | +| ALL_DATABASES | All databases | +| AVAILABILITY_GROUP_DATABASES | All databases in availability groups | +| USER_DATABASES, -AVAILABILITY_GROUP_DATABASES | All user databases that are not in availability groups | +| Db1 | The database Db1 | +| Db1, Db2 | The databases Db1 and Db2 | +| USER_DATABASES, -Db1 | All user databases except Db1 | +| %Db% | All databases that have “Db” in the name | +| %Db%, -Db1 | All databases that have “Db” in the name except Db1 | +| ALL_DATABASES, -%Db% | All databases that do not have “Db” in the name | + +### CheckCommands + +Specify the integrity check commands to be performed. + +| Value | Description | +| --- | --- | +| CHECKDB | Check the database. This is the default. | +| CHECKFILEGROUP | Check the filegroups. | +| CHECKTABLE | Check the tables and the indexed views. | +| CHECKALLOC | Check the disk space allocation structures. | +| CHECKCATALOG | Check the catalog consistency. | +| CHECKALLOC,CHECKCATALOG | Check the disk space allocation structures and the catalog consistency. | +| CHECKFILEGROUP,CHECKCATALOG | Check the filegroups and the catalog consistency. | +| CHECKALLOC,CHECKTABLE,CHECKCATALOG | Check the disk space allocation structures, the tables and the indexed views, and the catalog consistency. | + +DatabaseIntegrityCheck uses these SQL Server DBCC commands: [DBCC CHECKDB](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkdb-transact-sql) to check the database, [DBCC CHECKFILEGROUP](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkfilegroup-transact-sql) to check the filegroups, [DBCC CHECKTABLE](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checktable-transact-sql) to check the tables and the indexed views, [DBCC CHECKALLOC](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkalloc-transact-sql) to check the disk space allocation structures, and [DBCC CHECKCATALOG](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkcatalog-transact-sql) to check the catalog consistency. + +### PhysicalOnly + +Limit the checks to the physical structures of the database. + +| Value | Description | +| --- | --- | +| Y | Limit the checks to the physical structures of the database. | +| N | Do not limit the checks to the physical structures of the database. This is the default. | + +The PhysicalOnly option in DatabaseIntegrityCheck uses the PHYSICAL_ONLY option in the SQL Server [DBCC CHECKDB](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkdb-transact-sql), [DBCC CHECKFILEGROUP](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkfilegroup-transact-sql), and [DBCC CHECKTABLE](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checktable-transact-sql) commands. + +### DataPurity + +Check for column values that are not valid or out of range. + +| Value | Description | +| --- | --- | +| Y | Check for column values that are not valid or out of range. | +| N | Do not check for column values that are not valid or out of range. This is the default. | + +The DataPurity option in DatabaseIntegrityCheck uses the DATA_PURITY option in the SQL Server [DBCC CHECKDB](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkdb-transact-sql) and [DBCC CHECKTABLE](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checktable-transact-sql) commands. + +### NoIndex + +Do not check nonclustered indexes. + +| Value | Description | +| --- | --- | +| Y | Do not check nonclustered indexes. | +| N | Check nonclustered indexes. This is the default. | + +The NoIndex option in DatabaseIntegrityCheck uses the NOINDEX option in the SQL Server [DBCC CHECKDB](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkdb-transact-sql), [DBCC CHECKFILEGROUP](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkfilegroup-transact-sql), [DBCC CHECKTABLE](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checktable-transact-sql), and [DBCC CHECKALLOC](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkalloc-transact-sql) commands. + +### ExtendedLogicalChecks + +Perform extended logical checks. + +| Value | Description | +| --- | --- | +| Y | Perform extended logical checks. | +| N | Do not perform extended logical checks. This is the default. | + +The ExtendedLogicalChecks option in DatabaseIntegrityCheck uses the EXTENDED_LOGICAL_CHECKS option in the SQL Server [DBCC CHECKDB](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkdb-transact-sql) command. + +You cannot combine the options PhysicalOnly and ExtendedLogicalChecks. + +### NoInformationalMessages + +Suppress all informational messages. + +| Value | Description | +| --- | --- | +| Y | Suppress all informational messages. | +| N | Do not suppress informational messages. This is the default. | + +The NoInformationalMessages option in DatabaseIntegrityCheck uses the NO_INFOMSGS option in the SQL Server [DBCC CHECKDB](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkdb-transact-sql), [DBCC CHECKFILEGROUP](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkfilegroup-transact-sql), [DBCC CHECKTABLE](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checktable-transact-sql), [DBCC CHECKALLOC](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkalloc-transact-sql), and [DBCC CHECKCATALOG](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkcatalog-transact-sql) commands. + +### TabLock + +Use locks instead of an internal database snapshot. + +| Value | Description | +| --- | --- | +| Y | Use locks to perform the consistency checks. | +| N | Use an internal database snapshot to perform the consistency checks. This is the default. | + +The TabLock option in DatabaseIntegrityCheck uses the TABLOCK option in the SQL Server [DBCC CHECKDB](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkdb-transact-sql), [DBCC CHECKFILEGROUP](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkfilegroup-transact-sql), [DBCC CHECKTABLE](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checktable-transact-sql), and [DBCC CHECKALLOC](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkalloc-transact-sql) commands. + +### FileGroups + +Select filegroups. The ALL_FILEGROUPS keyword is supported. The hyphen character (-) is used to exclude filegroups, and the percent character (%) is used for wildcard selection. All of these operations can be combined by using the comma (,). + +| Value | Description | +| --- | --- | +| ALL_FILEGROUPS | All filegroups | +| Db1.FileGroup1 | The filegroup FileGroup1 in the database Db1 | +| Db1.FileGroup1, Db2.FileGroup2 | The filegroup FileGroup1 in the database Db1 and the filegroup FileGroup2 in the database Db2 | +| ALL_FILEGROUPS, -Db1.FileGroup1 | All filegroups except the filegroup FileGroup1 in the database Db1 | +| Db1.%FileGroup% | All filegroups in the database Db1 that have “FileGroup” in the name | + +This option can be used only if CHECKFILEGROUP is specified in the CheckCommands option. + +### Objects + +Select objects. The ALL_OBJECTS keyword is supported. The hyphen character (-) is used to exclude objects, and the percent character (%) is used for wildcard selection. All of these operations can be combined by using the comma (,). + +| Value | Description | +| --- | --- | +| ALL_OBJECTS | All objects | +| Db1.Schema1.Tbl1 | The object Schema1.Tbl1 in the database Db1 | +| Db1.Schema1.Object1, Db2.Schema2.Object2 | The object Schema1.Object1 in the database Db1 and the object Schema2.Object2 in the database Db2 | +| ALL_OBJECTS, -Db1.Schema1.Object1 | All objects except the object Schema1.Object1 in the database Db1 | +| Db1.Schema1.% | All objects in the schema Schema1 in the database Db1 | + +This option can be used only if CHECKTABLE is specified in the CheckCommands option. + +### MaxDOP + +Specify the number of CPUs to use when checking the database, filegroup, or table. If this number is not specified, the global maximum degree of parallelism is used. + +The MaxDOP option in DatabaseIntegrityCheck uses the MAXDOP option in the SQL Server [DBCC CHECKDB](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkdb-transact-sql), [DBCC CHECKFILEGROUP](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkfilegroup-transact-sql), and [DBCC CHECKTABLE](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checktable-transact-sql) commands. + +### AvailabilityGroups + +Select availability groups. The keyword ALL_AVAILABILITY_GROUPS is supported. The hyphen character (-) is used to exclude availability groups, and the percent character (%) is used for wildcard selection. All of these operations can be combined by using the comma (,). + +| Value | Description | +| --- | --- | +| ALL_AVAILABILITY_GROUPS | All availability groups | +| AG1 | The availability group AG1 | +| AG1, AG2 | The availability groups AG1 and AG2 | +| ALL_AVAILABILITY_GROUPS, -AG1 | All availability groups except AG1 | +| %AG% | All availability groups that have “AG” in the name | +| %AG%, -AG1 | All availability groups that have “AG” in the name except AG1 | +| ALL_AVAILABILITY_GROUPS, -%AG% | All availability groups that do not have “AG” in the name | + +### AvailabilityGroupReplicas + +Specify which replicas in availability groups should be checked. + +| Value | Description | +| --- | --- | +| ALL | Perform the checks on all replicas. This is the default. | +| PRIMARY | Perform the checks on the primary replica. | +| SECONDARY | Perform the checks on the secondary replicas. | +| PREFERRED_BACKUP_REPLICA | Perform the checks on the preferred backup replica. | + +### Updateability + +Select READ_ONLY/READ_WRITE databases. + +| Value | Description | +| --- | --- | +| ALL | READ_ONLY and READ_WRITE databases. This is the default. | +| READ_ONLY | READ_ONLY databases | +| READ_WRITE | READ_WRITE databases | + +is_read_only in [sys.databases](https://learn.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-databases-transact-sql) is used to check if a database is READ_ONLY or READ_WRITE. + +### TimeLimit + +Set the time, in seconds, after which no commands are executed. By default, the time is not limited. + +### LockTimeout + +Set the time, in seconds, that a command waits for a lock to be released. By default, the time is not limited. + +The LockTimeout option uses the [SET LOCK_TIMEOUT](https://learn.microsoft.com/en-us/sql/t-sql/statements/set-lock-timeout-transact-sql) statement in SQL Server. + +### LockMessageSeverity + +Set the severity for lock timeouts and deadlocks. + +| Value | Description | +| --- | --- | +| 10 | This is an informational message. | +| 16 | This is an error message. This is the default. | + +### StringDelimiter + +Specify the string delimiter. By default, the string delimiter is the comma. + +### DatabaseOrder + +Specify the database order. + +| Value | Description | +| --- | --- | +| NULL | The order in which the databases have been specified. Then ascending by the database name. This is the default. | +| DATABASE_NAME_ASC | Ascending by the database name | +| DATABASE_NAME_DESC | Descending by the database name | +| DATABASE_SIZE_ASC | Ascending by the database size | +| DATABASE_SIZE_DESC | Descending by the database size | +| DATABASE_LAST_GOOD_CHECK_ASC | Ascending by LastGoodCheckDbTime in DATABASEPROPERTYEX | +| DATABASE_LAST_GOOD_CHECK_DESC | Descending by LastGoodCheckDbTime in DATABASEPROPERTYEX | +| REPLICA_LAST_GOOD_CHECK_ASC | Ascending by the last successful checkdb in the dbo.CommandLog table | +| REPLICA_LAST_GOOD_CHECK_DESC | Descending by the last successful checkdb in the dbo.CommandLog table | + +### DatabasesInParallel + +Process databases in parallel. + +| Value | Description | +| --- | --- | +| Y | Process databases in parallel. | +| N | Process databases one at a time. This is the default. | + +You can process databases in parallel by creating multiple jobs with the same parameters, and adding the parameter @DatabasesInParallel = 'Y'. + +### LogToTable + +Log commands to the table dbo.CommandLog. + +| Value | Description | +| --- | --- | +| Y | Log commands to the table. | +| N | Do not log commands to the table. This is the default. | + +### Execute + +Execute commands. By default, the commands are executed normally. If this parameter is set to N, then the commands are printed only. + +| Value | Description | +| --- | --- | +| Y | Execute commands. This is the default. | +| N | Only print commands. | + +## Examples + +### A. Check the integrity of all user databases + +```sql +EXECUTE dbo.DatabaseIntegrityCheck +@Databases = 'USER_DATABASES', +@CheckCommands = 'CHECKDB' +``` + +### B. Check the physical integrity of all user databases + +```sql +EXECUTE dbo.DatabaseIntegrityCheck +@Databases = 'USER_DATABASES', +@CheckCommands = 'CHECKDB', +@PhysicalOnly = 'Y' +``` + +### C. Check the integrity of all user databases, using the option not to check nonclustered indexes + +```sql +EXECUTE dbo.DatabaseIntegrityCheck +@Databases = 'USER_DATABASES', +@CheckCommands = 'CHECKDB', +@NoIndex = 'Y' +``` + +### D. Check the integrity of all user databases, using the option to perform extended logical checks + +```sql +EXECUTE dbo.DatabaseIntegrityCheck +@Databases = 'USER_DATABASES', +@CheckCommands = 'CHECKDB', +@ExtendedLogicalChecks = 'Y' +``` + +### E. Check the integrity of the filegroup PRIMARY in the database AdventureWorks + +```sql +EXECUTE dbo.DatabaseIntegrityCheck +@Databases = 'AdventureWorks', +@CheckCommands = 'CHECKFILEGROUP', +@FileGroups = 'AdventureWorks.PRIMARY' +``` + +### F. Check the integrity of all filegroups except the filegroup PRIMARY in the database AdventureWorks + +```sql +EXECUTE dbo.DatabaseIntegrityCheck +@Databases = 'USER_DATABASES', +@CheckCommands = 'CHECKFILEGROUP', +@FileGroups = 'ALL_FILEGROUPS, -AdventureWorks.PRIMARY' +``` + +### G. Check the integrity of the table Production.Product in the database AdventureWorks + +```sql +EXECUTE dbo.DatabaseIntegrityCheck +@Databases = 'AdventureWorks', +@CheckCommands = 'CHECKTABLE', +@Objects = 'AdventureWorks.Production.Product' +``` + +### H. Check the integrity of all tables except the table Production.Product in the database AdventureWorks + +```sql +EXECUTE dbo.DatabaseIntegrityCheck +@Databases = 'USER_DATABASES', +@CheckCommands = 'CHECKTABLE', +@Objects = 'ALL_OBJECTS, -AdventureWorks.Production.Product' +``` + +### I. Check the disk-space allocation structures of all user databases + +```sql +EXECUTE dbo.DatabaseIntegrityCheck +@Databases = 'USER_DATABASES', +@CheckCommands = 'CHECKALLOC' +``` + +### J. Check the catalog consistency of all user databases + +```sql +EXECUTE dbo.DatabaseIntegrityCheck +@Databases = 'USER_DATABASES', +@CheckCommands = 'CHECKCATALOG' +``` + +## Execution + +You can execute the stored procedures from T-SQL job steps, and use [MaintenanceSolution.sql](/MaintenanceSolution.sql) to create the jobs.