@@ -28,32 +28,25 @@ EXEC dbo.DatabaseRestore
28
28
@RunRecovery = 1;
29
29
*/
30
30
31
- USE [master]
31
+ IF OBJECT_ID (' dbo.sp_DatabaseRestore' ) IS NULL
32
+ EXEC (' CREATE PROCEDURE dbo.sp_DatabaseRestore AS RETURN 0;' )
32
33
GO
33
34
34
- SET ANSI_NULLS ON
35
- GO
36
- SET QUOTED_IDENTIFIER ON
37
- GO
38
-
39
- CREATE PROCEDURE [dbo].[DatabaseRestore]
35
+ ALTER PROCEDURE [dbo].[sp_DatabaseRestore]
40
36
@Database NVARCHAR (128 ), @RestoreDatabaseName NVARCHAR (128 ) = NULL , @BackupPathFull NVARCHAR (MAX ), @BackupPathLog NVARCHAR (MAX ),
41
37
@MoveFiles bit = 0 , @MoveDataDrive NVARCHAR (260 ) = NULL , @MoveLogDrive NVARCHAR (260 ) = NULL , @TestRestore bit = 0 , @RunCheckDB bit = 0 ,
42
38
@ContinueLogs bit = 0 , @RunRecovery bit = 0
43
39
AS
44
-
45
40
SET NOCOUNT ON ;
46
41
47
42
DECLARE @cmd NVARCHAR (4000 ), @sql NVARCHAR (MAX ), @LastFullBackup NVARCHAR (500 ), @BackupFile NVARCHAR (500 );
48
43
DECLARE @FileList TABLE (BackupFile NVARCHAR (255 ));
49
44
50
- DECLARE @MoveDataLocation AS NVARCHAR (500 ), @MoveDataLocationName AS NVARCHAR (500 ), @MoveLogLocation AS NVARCHAR (500 ), @MoveLogLocationName AS NVARCHAR (500 );
51
-
52
45
IF @RestoreDatabaseName IS NULL
53
46
SET @RestoreDatabaseName = @Database;
54
47
55
48
-- get list of files
56
- SET @cmd = ' DIR /b '+ @BackupPathFull;
49
+ SET @cmd = ' DIR /b " '+ @BackupPathFull + ' "' ;
57
50
INSERT INTO @FileList (BackupFile)
58
51
EXEC master .sys .xp_cmdshell @cmd;
59
52
@@ -65,7 +58,23 @@ WHERE BackupFile LIKE '%.bak'
65
58
AND
66
59
BackupFile LIKE ' %'+ @Database+ ' %' ;
67
60
68
- DECLARE @FileListParameters TABLE
61
+ -- Get the SQL Server version number because the columns returned by RESTORE commands vary by version
62
+ -- Based on: https://www.brentozar.com/archive/2015/05/sql-server-version-detection/
63
+ -- Need to capture BuildVersion because RESTORE HEADERONLY changed with 2014 CU1, not RTM
64
+ DECLARE @ProductVersion AS varchar (20 ) = CAST (SERVERPROPERTY (' productversion' ) AS varchar (20 ));
65
+ DECLARE @MajorVersion AS smallint = CAST (PARSENAME (@ProductVersion, 4 ) AS smallint );
66
+ DECLARE @MinorVersion AS smallint = CAST (PARSENAME (@ProductVersion, 3 ) AS smallint );
67
+ DECLARE @BuildVersion AS smallint = CAST (PARSENAME (@ProductVersion, 2 ) AS smallint );
68
+
69
+ IF @MajorVersion < 10
70
+ BEGIN
71
+ RAISERROR (' Sorry, DatabaseRestore doesn'' t work on versions of SQL prior to 2008.' , 15 , 1 );
72
+ RETURN ;
73
+ END
74
+
75
+ -- Build SQL for RESTORE FILELIST ONLY
76
+ IF OBJECT_ID (N ' tempdb..#FileListParameters' ) IS NOT NULL DROP TABLE #FileListParameters;
77
+ CREATE TABLE #FileListParameters
69
78
(
70
79
LogicalName NVARCHAR (128 ) NOT NULL
71
80
,PhysicalName NVARCHAR (260 ) NOT NULL
@@ -90,11 +99,67 @@ DECLARE @FileListParameters TABLE
90
99
,TDEThumbprint VARBINARY (32 ) NULL
91
100
,SnapshotUrl NVARCHAR (360 ) NULL
92
101
);
102
+ DECLARE @FileListParamSQL AS nvarchar (4000 );
103
+ SET @FileListParamSQL =
104
+ ' INSERT INTO #FileListParameters
105
+ (LogicalName, PhysicalName, Type, FileGroupName, Size, MaxSize, FileID, CreateLSN, DropLSN
106
+ ,UniqueID, ReadOnlyLSN, ReadWriteLSN, BackupSizeInBytes, SourceBlockSize, FileGroupID, LogGroupGUID
107
+ ,DifferentialBaseLSN, DifferentialBaseGUID, IsReadOnly, IsPresent, TDEThumbprint'
108
+
109
+ IF @MajorVersion >= 13
110
+ SET @FileListParamSQL + = ' , SnapshotUrl'
111
+
112
+ SET @FileListParamSQL + = ' )' + CHAR (13 ) + CHAR (10 );
113
+ SET @FileListParamSQL + = ' EXEC ('' RESTORE FILELISTONLY FROM DISK='' '' {Path}'' '' '' )' ;
114
+
115
+ SET @sql = REPLACE (@FileListParamSQL, ' {Path}' , @BackupPathFull + @LastFullBackup);
116
+ PRINT @sql;
117
+ EXEC (@sql);
118
+
119
+ -- Build SQL for RESTORE HEADERONLY - this will be used a bit further below
120
+ IF OBJECT_ID (N ' tempdb..#Headers' ) IS NOT NULL DROP TABLE #Headers;
121
+ CREATE TABLE #Headers (
122
+ BackupName VARCHAR (256 ), BackupDescription VARCHAR (256 ), BackupType VARCHAR (256 ), ExpirationDate VARCHAR (256 ),
123
+ Compressed VARCHAR (256 ), Position VARCHAR (256 ), DeviceType VARCHAR (256 ), UserName VARCHAR (256 ), ServerName VARCHAR (
124
+ 256 ), DatabaseName VARCHAR (256 ), DatabaseVersion VARCHAR (256 ), DatabaseCreationDate VARCHAR (256 ), BackupSize VARCHAR (
125
+ 256 ), FirstLSN VARCHAR (256 ), LastLSN VARCHAR (256 ), CheckpointLSN VARCHAR (256 ), DatabaseBackupLSN VARCHAR (256 ),
126
+ BackupStartDate VARCHAR (256 ), BackupFinishDate VARCHAR (256 ), SortOrder VARCHAR (256 ), CodePage VARCHAR (256 ),
127
+ UnicodeLocaleId VARCHAR (256 ), UnicodeComparisonStyle VARCHAR (256 ), CompatibilityLevel VARCHAR (256 ), SoftwareVendorId
128
+ VARCHAR (256 ), SoftwareVersionMajor VARCHAR (256 ), SoftwareVersionMinor VARCHAR (256 ), SoftwareVersionBuild VARCHAR (256 ),
129
+ MachineName VARCHAR (256 ), Flags VARCHAR (256 ), BindingID VARCHAR (256 ), RecoveryForkID VARCHAR (256 ), Collation VARCHAR (
130
+ 256 ), FamilyGUID VARCHAR (256 ), HasBulkLoggedData VARCHAR (256 ), IsSnapshot VARCHAR (256 ), IsReadOnly VARCHAR (256 ),
131
+ IsSingleUser VARCHAR (256 ), HasBackupChecksums VARCHAR (256 ), IsDamaged VARCHAR (256 ), BeginsLogChain VARCHAR (256 ),
132
+ HasIncompleteMetaData VARCHAR (256 ), IsForceOffline VARCHAR (256 ), IsCopyOnly VARCHAR (256 ), FirstRecoveryForkID VARCHAR
133
+ (256 ), ForkPointLSN VARCHAR (256 ), RecoveryModel VARCHAR (256 ), DifferentialBaseLSN VARCHAR (256 ), DifferentialBaseGUID
134
+ VARCHAR (256 ), BackupTypeDescription VARCHAR (256 ), BackupSetGUID VARCHAR (256 ), CompressedBackupSize VARCHAR (256 ),
135
+ Containment VARCHAR (256 ), KeyAlgorithm NVARCHAR (32 ), EncryptorThumbprint VARBINARY (20 ), EncryptorType NVARCHAR (32 ),
136
+ --
137
+ -- Seq added to retain order by
138
+ --
139
+ Seq INT NOT NULL IDENTITY (1 , 1 )
140
+ );
93
141
94
- INSERT INTO @FileListParameters
95
- EXEC (' RESTORE FILELISTONLY FROM DISK='' '+ @BackupPathFull + @LastFullBackup+ ' '' ' );
142
+ DECLARE @HeadersSQL AS nvarchar (4000 );
143
+ SET @HeadersSQL =
144
+ ' INSERT INTO #Headers
145
+ (BackupName, BackupDescription, BackupType, ExpirationDate, Compressed, Position, DeviceType, UserName, ServerName
146
+ ,DatabaseName, DatabaseVersion, DatabaseCreationDate, BackupSize, FirstLSN, LastLSN, CheckpointLSN, DatabaseBackupLSN
147
+ ,BackupStartDate, BackupFinishDate, SortOrder, CodePage, UnicodeLocaleId, UnicodeComparisonStyle, CompatibilityLevel
148
+ ,SoftwareVendorId, SoftwareVersionMajor, SoftwareVersionMinor, SoftwareVersionBuild, MachineName, Flags, BindingID
149
+ ,RecoveryForkID, Collation, FamilyGUID, HasBulkLoggedData, IsSnapshot, IsReadOnly, IsSingleUser, HasBackupChecksums
150
+ ,IsDamaged, BeginsLogChain, HasIncompleteMetaData, IsForceOffline, IsCopyOnly, FirstRecoveryForkID, ForkPointLSN
151
+ ,RecoveryModel, DifferentialBaseLSN, DifferentialBaseGUID, BackupTypeDescription, BackupSetGUID, CompressedBackupSize'
152
+
153
+ IF @MajorVersion >= 11
154
+ SET @HeadersSQL + = CHAR (13 ) + CHAR (10 ) + ' ,Containment' ;
96
155
97
- DECLARE @MoveOption AS NVARCHAR (1000 )= ' ' ;
156
+ IF @MajorVersion >= 13 OR (@MajorVersion = 12 AND @BuildVersion >= 2342 )
157
+ SET @HeadersSQL + = ' , KeyAlgorithm, EncryptorThumbprint, EncryptorType'
158
+
159
+ SET @HeadersSQL + = ' )' + CHAR (13 ) + CHAR (10 );
160
+ SET @HeadersSQL + = ' EXEC ('' RESTORE HEADERONLY FROM DISK='' '' {Path}'' '' '' )' ;
161
+
162
+ DECLARE @MoveOption AS NVARCHAR (MAX )= ' ' ;
98
163
99
164
IF @MoveFiles = 1
100
165
BEGIN
@@ -105,7 +170,7 @@ BEGIN
105
170
WHEN Type = ' D' THEN @MoveDataDrive
106
171
WHEN Type = ' L' THEN @MoveLogDrive
107
172
END + REVERSE (LEFT (REVERSE (PhysicalName), CHARINDEX (' \' , REVERSE (PhysicalName), 1 )- 1 )) + ' '' ' AS logicalcmds
108
- FROM @ FileListParameters)
173
+ FROM # FileListParameters)
109
174
SELECT @MoveOption = @MoveOption + logicalcmds
110
175
FROM Files;
111
176
END ;
@@ -117,41 +182,23 @@ BEGIN
117
182
EXECUTE @sql = [dbo].[CommandExecute] @Command = @sql, @CommandType = ' RESTORE DATABASE' , @Mode = 1 , @DatabaseName = @Database, @LogToTable = ' Y' , @Execute = ' Y' ;
118
183
119
184
-- get the backup completed data so we can apply tlogs from that point forwards
120
- DECLARE @Headers TABLE (
121
- BackupName VARCHAR (256 ), BackupDescription VARCHAR (256 ), BackupType VARCHAR (256 ), ExpirationDate VARCHAR (256 ),
122
- Compressed VARCHAR (256 ), Position VARCHAR (256 ), DeviceType VARCHAR (256 ), UserName VARCHAR (256 ), ServerName VARCHAR (
123
- 256 ), DatabaseName VARCHAR (256 ), DatabaseVersion VARCHAR (256 ), DatabaseCreationDate VARCHAR (256 ), BackupSize VARCHAR (
124
- 256 ), FirstLSN VARCHAR (256 ), LastLSN VARCHAR (256 ), CheckpointLSN VARCHAR (256 ), DatabaseBackupLSN VARCHAR (256 ),
125
- BackupStartDate VARCHAR (256 ), BackupFinishDate VARCHAR (256 ), SortOrder VARCHAR (256 ), CodePage VARCHAR (256 ),
126
- UnicodeLocaleId VARCHAR (256 ), UnicodeComparisonStyle VARCHAR (256 ), CompatibilityLevel VARCHAR (256 ), SoftwareVendorId
127
- VARCHAR (256 ), SoftwareVersionMajor VARCHAR (256 ), SoftwareVersionMinor VARCHAR (256 ), SoftwareVersionBuild VARCHAR (256 ),
128
- MachineName VARCHAR (256 ), Flags VARCHAR (256 ), BindingID VARCHAR (256 ), RecoveryForkID VARCHAR (256 ), Collation VARCHAR (
129
- 256 ), FamilyGUID VARCHAR (256 ), HasBulkLoggedData VARCHAR (256 ), IsSnapshot VARCHAR (256 ), IsReadOnly VARCHAR (256 ),
130
- IsSingleUser VARCHAR (256 ), HasBackupChecksums VARCHAR (256 ), IsDamaged VARCHAR (256 ), BeginsLogChain VARCHAR (256 ),
131
- HasIncompleteMetaData VARCHAR (256 ), IsForceOffline VARCHAR (256 ), IsCopyOnly VARCHAR (256 ), FirstRecoveryForkID VARCHAR
132
- (256 ), ForkPointLSN VARCHAR (256 ), RecoveryModel VARCHAR (256 ), DifferentialBaseLSN VARCHAR (256 ), DifferentialBaseGUID
133
- VARCHAR (256 ), BackupTypeDescription VARCHAR (256 ), BackupSetGUID VARCHAR (256 ), CompressedBackupSize VARCHAR (256 ),
134
- Containment VARCHAR (256 ), KeyAlgorithm NVARCHAR (32 ), EncryptorThumbprint VARBINARY (20 ), EncryptorType NVARCHAR (32 ),
135
- --
136
- -- Seq added to retain order by
137
- --
138
- Seq INT NOT NULL IDENTITY (1 , 1 )
139
- );
140
-
141
- INSERT INTO @Headers
142
- EXEC (' RESTORE HEADERONLY FROM DISK = '' '+ @BackupPathFull + @LastFullBackup+ ' '' ' );
143
-
144
- DECLARE @BackupDateTime AS CHAR (15 ), @FullLastLSN BIGINT ;
185
+
186
+ SET @sql = REPLACE (@HeadersSQL, ' {Path}' , @BackupPathFull + @LastFullBackup);
187
+ PRINT @sql;
188
+ EXECUTE (@sql);
189
+
190
+ DECLARE @BackupDateTime AS CHAR (15 ), @FullLastLSN NUMERIC (25 , 0 );
145
191
146
192
SELECT @BackupDateTime = RIGHT (@LastFullBackup, 19 )
147
193
148
- SELECT @FullLastLSN = CAST (LastLSN AS BIGINT ) FROM @Headers WHERE BackupType = 1 ;
194
+ SELECT @FullLastLSN = CAST (LastLSN AS NUMERIC (25 , 0 )) FROM #Headers WHERE BackupType = 1 ;
195
+
149
196
END ;
150
197
ELSE
151
198
BEGIN
152
- DECLARE @DatabaseLastLSN BIGINT ;
199
+ DECLARE @DatabaseLastLSN NUMERIC ( 25 , 0 ) ;
153
200
154
- SELECT @DatabaseLastLSN = CAST (f .redo_start_lsn AS BIGINT )
201
+ SELECT @DatabaseLastLSN = CAST (f .redo_start_lsn AS NUMERIC ( 25 , 0 ) )
155
202
FROM master .sys .databases d
156
203
JOIN master .sys .master_files f ON d .database_id = f .database_id
157
204
WHERE d .name = @RestoreDatabaseName AND f .file_id = 1
160
207
-- Clear out table variables for translogs
161
208
DELETE FROM @FileList;
162
209
163
- SET @cmd = ' DIR /b '+ @BackupPathLog;
210
+ SET @cmd = ' DIR /b " '+ @BackupPathLog + ' "' ;
164
211
INSERT INTO @FileList (BackupFile)
165
212
EXEC master .sys .xp_cmdshell @cmd;
166
213
@@ -174,23 +221,24 @@ DECLARE BackupFiles CURSOR FOR
174
221
175
222
OPEN BackupFiles;
176
223
177
- DECLARE @i tinyint = 1 , @LogFirstLSN BIGINT , @LogLastLSN BIGINT ; ;
224
+ DECLARE @i tinyint = 1 , @LogFirstLSN NUMERIC ( 25 , 0 ), @LogLastLSN NUMERIC ( 25 , 0 ) ;
178
225
179
226
-- Loop through all the files for the database
180
227
FETCH NEXT FROM BackupFiles INTO @BackupFile;
181
228
WHILE @@FETCH_STATUS = 0
182
229
BEGIN
183
230
IF @i = 1
184
231
BEGIN
185
- INSERT INTO @Headers
186
- EXEC (' RESTORE HEADERONLY FROM DISK = '' '+ @BackupPathLog + @BackupFile+ ' '' ' );
232
+ SET @sql = REPLACE (@HeadersSQL, ' {Path}' , @BackupPathLog + @BackupFile);
233
+ PRINT @sql;
234
+ EXECUTE (@sql);
187
235
188
- SELECT @LogFirstLSN = CAST (FirstLSN AS BIGINT ) , @LogLastLSN = CAST (LastLSN AS BIGINT ) FROM @ Headers WHERE BackupType = 2 ;
236
+ SELECT @LogFirstLSN = CAST (FirstLSN AS NUMERIC ( 25 , 0 )) , @LogLastLSN = CAST (LastLSN AS NUMERIC ( 25 , 0 )) FROM # Headers WHERE BackupType = 2 ;
189
237
190
238
IF (@ContinueLogs = 0 AND @LogFirstLSN <= @FullLastLSN AND @FullLastLSN <= @LogLastLSN) OR (@ContinueLogs = 1 AND @LogFirstLSN <= @DatabaseLastLSN AND @DatabaseLastLSN < @LogLastLSN)
191
239
SET @i = 2 ;
192
240
193
- DELETE FROM @ Headers WHERE BackupType = 2 ;
241
+ DELETE FROM # Headers WHERE BackupType = 2 ;
194
242
END ;
195
243
196
244
IF @i = 2
0 commit comments