Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 52 additions & 20 deletions sp_DatabaseRestore.sql
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,27 @@ BEGIN
END
END

/* Reject path-shaped parameters that contain shell metacharacters or control chars.
Single quotes are deliberately allowed (legal in Windows paths) and are escaped at concat sites instead. */
DECLARE @ForbiddenPathChars NVARCHAR(20) = N'"&|;^<>' + NCHAR(0) + NCHAR(10) + NCHAR(13);
DECLARE @ForbiddenPathPattern NVARCHAR(40) = N'%[' + @ForbiddenPathChars + N']%';
DECLARE @InvalidPathParam sysname = NULL;
IF @BackupPathFull LIKE @ForbiddenPathPattern SET @InvalidPathParam = N'@BackupPathFull';
IF @InvalidPathParam IS NULL AND @BackupPathDiff LIKE @ForbiddenPathPattern SET @InvalidPathParam = N'@BackupPathDiff';
IF @InvalidPathParam IS NULL AND @BackupPathLog LIKE @ForbiddenPathPattern SET @InvalidPathParam = N'@BackupPathLog';
IF @InvalidPathParam IS NULL AND @MoveDataDrive LIKE @ForbiddenPathPattern SET @InvalidPathParam = N'@MoveDataDrive';
IF @InvalidPathParam IS NULL AND @MoveLogDrive LIKE @ForbiddenPathPattern SET @InvalidPathParam = N'@MoveLogDrive';
IF @InvalidPathParam IS NULL AND @MoveFilestreamDrive LIKE @ForbiddenPathPattern SET @InvalidPathParam = N'@MoveFilestreamDrive';
IF @InvalidPathParam IS NULL AND @MoveFullTextCatalogDrive LIKE @ForbiddenPathPattern SET @InvalidPathParam = N'@MoveFullTextCatalogDrive';
IF @InvalidPathParam IS NULL AND @StandbyUndoPath LIKE @ForbiddenPathPattern SET @InvalidPathParam = N'@StandbyUndoPath';
IF @InvalidPathParam IS NULL AND @FileNamePrefix LIKE @ForbiddenPathPattern SET @InvalidPathParam = N'@FileNamePrefix';
IF @InvalidPathParam IS NULL AND @Database LIKE @ForbiddenPathPattern SET @InvalidPathParam = N'@Database';
IF @InvalidPathParam IS NOT NULL
BEGIN
RAISERROR('Parameter %s contains a character that is not allowed in a file path. Forbidden: " & | ; ^ < > or control characters.', 16, 1, @InvalidPathParam) WITH NOWAIT;
RETURN;
END;

--File Extension cleanup
IF @FileExtensionDiff LIKE '%.%'
BEGIN
Expand Down Expand Up @@ -625,7 +646,7 @@ BEGIN
END
ELSE
BEGIN
SET @cmd = N'DIR /b "' + @CurrentBackupPathFull + N'"';
SET @cmd = N'DIR /b "' + REPLACE(@CurrentBackupPathFull, N'''', N'''''') + N'"';
IF @Debug = 1
BEGIN
IF @cmd IS NULL PRINT '@cmd is NULL for @CurrentBackupPathFull';
Expand Down Expand Up @@ -762,7 +783,7 @@ BEGIN
SET @FileListParamSQL += N')' + NCHAR(13) + NCHAR(10);
SET @FileListParamSQL += N'EXEC (''RESTORE FILELISTONLY FROM DISK=''''{Path}'''''')';

SET @sql = REPLACE(@FileListParamSQL, N'{Path}', @CurrentBackupPathFull + @LastFullBackup);
SET @sql = REPLACE(@FileListParamSQL, N'{Path}', REPLACE(@CurrentBackupPathFull + @LastFullBackup, N'''', REPLICATE(N'''', 4)));

IF @Debug = 1
BEGIN
Expand All @@ -778,7 +799,7 @@ BEGIN
END

--get the backup completed data so we can apply tlogs from that point forwards
SET @sql = REPLACE(@HeadersSQL, N'{Path}', @CurrentBackupPathFull + @LastFullBackup);
SET @sql = REPLACE(@HeadersSQL, N'{Path}', REPLACE(@CurrentBackupPathFull + @LastFullBackup, N'''', REPLICATE(N'''', 4)));

IF @Debug = 1
BEGIN
Expand Down Expand Up @@ -939,7 +960,7 @@ BEGIN

SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' FROM '
+ STUFF(
(SELECT CHAR( 10 ) + ',DISK=''' + BackupPath + BackupFile + ''''
(SELECT CHAR( 10 ) + ',DISK=''' + REPLACE(BackupPath, '''', '''''') + REPLACE(BackupFile, '''', '''''') + ''''
FROM #SplitFullBackups
ORDER BY BackupFile
FOR XML PATH ('')),
Expand All @@ -949,7 +970,7 @@ BEGIN
END;
ELSE
BEGIN
SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' FROM DISK = ''' + @CurrentBackupPathFull + @LastFullBackup + N''' WITH NORECOVERY, REPLACE' + @BackupParameters + @MoveOption + NCHAR(13) + NCHAR(10);
SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' FROM DISK = ''' + REPLACE(@CurrentBackupPathFull, N'''', N'''''') + REPLACE(@LastFullBackup, N'''', N'''''') + N''' WITH NORECOVERY, REPLACE' + @BackupParameters + @MoveOption + NCHAR(13) + NCHAR(10);
END
IF (@StandbyMode = 1)
BEGIN
Expand All @@ -959,11 +980,11 @@ BEGIN
END
ELSE IF (SELECT COUNT(*) FROM #SplitFullBackups) > 0
BEGIN
SET @sql = @sql + ', STANDBY = ''' + @StandbyUndoPath + @Database + 'Undo.ldf''' + NCHAR(13) + NCHAR(10);
SET @sql = @sql + ', STANDBY = ''' + REPLACE(@StandbyUndoPath, N'''', N'''''') + REPLACE(@Database, N'''', N'''''') + 'Undo.ldf''' + NCHAR(13) + NCHAR(10);
END
ELSE
BEGIN
SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' FROM DISK = ''' + @CurrentBackupPathFull + @LastFullBackup + N''' WITH REPLACE' + @BackupParameters + @MoveOption + N' , STANDBY = ''' + @StandbyUndoPath + @Database + 'Undo.ldf''' + NCHAR(13) + NCHAR(10);
SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' FROM DISK = ''' + REPLACE(@CurrentBackupPathFull, N'''', N'''''') + REPLACE(@LastFullBackup, N'''', N'''''') + N''' WITH REPLACE' + @BackupParameters + @MoveOption + N' , STANDBY = ''' + REPLACE(@StandbyUndoPath, N'''', N'''''') + REPLACE(@Database, N'''', N'''''') + 'Undo.ldf''' + NCHAR(13) + NCHAR(10);
END
END;
IF @Debug = 1 OR @Execute = 'N'
Expand Down Expand Up @@ -1050,7 +1071,7 @@ BEGIN
END
ELSE
BEGIN
SET @cmd = N'DIR /b "' + @CurrentBackupPathDiff + N'"';
SET @cmd = N'DIR /b "' + REPLACE(@CurrentBackupPathDiff, N'''', N'''''') + N'"';
IF @Debug = 1
BEGIN
IF @cmd IS NULL PRINT '@cmd is NULL for @CurrentBackupPathDiff';
Expand Down Expand Up @@ -1131,7 +1152,7 @@ BEGIN
IF @Debug = 1 RAISERROR ('Split backups found', 0, 1) WITH NOWAIT;
SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' FROM '
+ STUFF(
(SELECT CHAR( 10 ) + ',DISK=''' + BackupPath + BackupFile + ''''
(SELECT CHAR( 10 ) + ',DISK=''' + REPLACE(BackupPath, '''', '''''') + REPLACE(BackupFile, '''', '''''') + ''''
FROM #SplitDiffBackups
ORDER BY BackupFile
FOR XML PATH ('')),
Expand All @@ -1140,7 +1161,7 @@ BEGIN
'' ) + N' WITH NORECOVERY, REPLACE' + @BackupParameters + @MoveOption + NCHAR(13) + NCHAR(10);
END;
ELSE
SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' FROM DISK = ''' + @CurrentBackupPathDiff + @LastDiffBackup + N''' WITH NORECOVERY' + @BackupParameters + @MoveOption + NCHAR(13) + NCHAR(10);
SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' FROM DISK = ''' + REPLACE(@CurrentBackupPathDiff, N'''', N'''''') + REPLACE(@LastDiffBackup, N'''', N'''''') + N''' WITH NORECOVERY' + @BackupParameters + @MoveOption + NCHAR(13) + NCHAR(10);

IF (@StandbyMode = 1)
BEGIN
Expand All @@ -1149,9 +1170,9 @@ BEGIN
IF @Execute = 'Y' OR @Debug = 1 RAISERROR('The file path of the undo file for standby mode was not specified. The database will not be restored in standby mode.', 0, 1) WITH NOWAIT;
END
ELSE IF (SELECT COUNT(*) FROM #SplitDiffBackups) > 0
SET @sql = @sql + ', STANDBY = ''' + @StandbyUndoPath + @Database + 'Undo.ldf''' + NCHAR(13) + NCHAR(10);
SET @sql = @sql + ', STANDBY = ''' + REPLACE(@StandbyUndoPath, N'''', N'''''') + REPLACE(@Database, N'''', N'''''') + 'Undo.ldf''' + NCHAR(13) + NCHAR(10);
ELSE
SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' FROM DISK = ''' + @BackupPathDiff + @LastDiffBackup + N''' WITH STANDBY = ''' + @StandbyUndoPath + @Database + 'Undo.ldf''' + @BackupParameters + @MoveOption + NCHAR(13) + NCHAR(10);
SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' FROM DISK = ''' + REPLACE(@BackupPathDiff, N'''', N'''''') + REPLACE(@LastDiffBackup, N'''', N'''''') + N''' WITH STANDBY = ''' + REPLACE(@StandbyUndoPath, N'''', N'''''') + REPLACE(@Database, N'''', N'''''') + 'Undo.ldf''' + @BackupParameters + @MoveOption + NCHAR(13) + NCHAR(10);
END;
IF @Debug = 1 OR @Execute = 'N'
BEGIN
Expand All @@ -1162,7 +1183,7 @@ BEGIN
EXECUTE @sql = [dbo].[CommandExecute] @DatabaseContext=N'master', @Command = @sql, @CommandType = 'RESTORE DATABASE', @Mode = 1, @DatabaseName = @UnquotedRestoreDatabaseName, @LogToTable = 'Y', @Execute = 'Y';

--get the backup completed data so we can apply tlogs from that point forwards
SET @sql = REPLACE(@HeadersSQL, N'{Path}', @CurrentBackupPathDiff + @LastDiffBackup);
SET @sql = REPLACE(@HeadersSQL, N'{Path}', REPLACE(@CurrentBackupPathDiff + @LastDiffBackup, N'''', REPLICATE(N'''', 4)));

IF @Debug = 1
BEGIN
Expand Down Expand Up @@ -1235,7 +1256,7 @@ BEGIN
END
ELSE
BEGIN
SET @cmd = N'DIR /b "' + @CurrentBackupPathLog + N'"';
SET @cmd = N'DIR /b "' + REPLACE(@CurrentBackupPathLog, N'''', N'''''') + N'"';
IF @Debug = 1
BEGIN
IF @cmd IS NULL PRINT '@cmd is NULL for @CurrentBackupPathLog';
Expand Down Expand Up @@ -1365,7 +1386,7 @@ IF (@StandbyMode = 1)
IF @Execute = 'Y' OR @Debug = 1 RAISERROR('The file path of the undo file for standby mode was not specified. Logs will not be restored in standby mode.', 0, 1) WITH NOWAIT;
END;
ELSE
SET @LogRecoveryOption = N'STANDBY = ''' + @StandbyUndoPath + @Database + 'Undo.ldf''';
SET @LogRecoveryOption = N'STANDBY = ''' + REPLACE(@StandbyUndoPath, N'''', N'''''') + REPLACE(@Database, N'''', N'''''') + 'Undo.ldf''';
END;

IF (@LogRecoveryOption = N'')
Expand Down Expand Up @@ -1450,7 +1471,7 @@ WHERE BackupFile IS NOT NULL;
IF @i = 1

BEGIN
SET @sql = REPLACE(@HeadersSQL, N'{Path}', @CurrentBackupPathLog + @BackupFile);
SET @sql = REPLACE(@HeadersSQL, N'{Path}', REPLACE(@CurrentBackupPathLog + @BackupFile, N'''', REPLICATE(N'''', 4)));

IF @Debug = 1
BEGIN
Expand Down Expand Up @@ -1490,7 +1511,7 @@ WHERE BackupFile IS NOT NULL;
IF @Debug = 1 RAISERROR ('Split backups found', 0, 1) WITH NOWAIT;
SET @sql = N'RESTORE LOG ' + @RestoreDatabaseName + N' FROM '
+ STUFF(
(SELECT CHAR( 10 ) + ',DISK=''' + BackupPath + BackupFile + ''''
(SELECT CHAR( 10 ) + ',DISK=''' + REPLACE(BackupPath, '''', '''''') + REPLACE(BackupFile, '''', '''''') + ''''
FROM #SplitLogBackups
WHERE DenseRank = @LogRestoreRanking
ORDER BY BackupFile
Expand All @@ -1500,7 +1521,7 @@ WHERE BackupFile IS NOT NULL;
'' ) + N' WITH ' + @LogRecoveryOption + NCHAR(13) + NCHAR(10);
END;
ELSE
SET @sql = N'RESTORE LOG ' + @RestoreDatabaseName + N' FROM DISK = ''' + @CurrentBackupPathLog + @BackupFile + N''' WITH ' + @LogRecoveryOption + NCHAR(13) + NCHAR(10);
SET @sql = N'RESTORE LOG ' + @RestoreDatabaseName + N' FROM DISK = ''' + REPLACE(@CurrentBackupPathLog, N'''', N'''''') + REPLACE(@BackupFile, N'''', N'''''') + N''' WITH ' + @LogRecoveryOption + NCHAR(13) + NCHAR(10);

IF @Debug = 1 OR @Execute = 'N'
BEGIN
Expand Down Expand Up @@ -1582,7 +1603,7 @@ IF @DatabaseOwner IS NOT NULL
BEGIN
IF EXISTS (SELECT * FROM master.dbo.syslogins WHERE syslogins.loginname = @DatabaseOwner)
BEGIN
SET @sql = N'ALTER AUTHORIZATION ON DATABASE::' + @RestoreDatabaseName + ' TO [' + @DatabaseOwner + ']';
SET @sql = N'ALTER AUTHORIZATION ON DATABASE::' + @RestoreDatabaseName + N' TO ' + QUOTENAME(@DatabaseOwner);

IF @Debug = 1 OR @Execute = 'N'
BEGIN
Expand Down Expand Up @@ -1672,8 +1693,19 @@ END;'

IF @RunStoredProcAfterRestore IS NOT NULL AND LEN(LTRIM(@RunStoredProcAfterRestore)) > 0
BEGIN
DECLARE @RunStoredProcSchema sysname = NULLIF(PARSENAME(@RunStoredProcAfterRestore, 2), N'');
DECLARE @RunStoredProcName sysname = PARSENAME(@RunStoredProcAfterRestore, 1);
IF @RunStoredProcName IS NULL
OR PARSENAME(@RunStoredProcAfterRestore, 3) IS NOT NULL
OR PARSENAME(@RunStoredProcAfterRestore, 4) IS NOT NULL
BEGIN
RAISERROR('@RunStoredProcAfterRestore must be a procedure name or schema.procedure (1- or 2-part name).', 16, 1) WITH NOWAIT;
RETURN;
END;
PRINT 'Attempting to run ' + @RunStoredProcAfterRestore
SET @sql = N'EXEC ' + @RestoreDatabaseName + '.' + @RunStoredProcAfterRestore
SET @sql = N'EXEC ' + @RestoreDatabaseName + N'.'
+ COALESCE(QUOTENAME(@RunStoredProcSchema) + N'.', N'')
+ QUOTENAME(@RunStoredProcName);
Comment on lines +1712 to +1715

IF @Debug = 1 OR @Execute = 'N'
BEGIN
Expand Down