Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
63 changes: 63 additions & 0 deletions .github/scripts/backup-remote-push-regression.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* Manual integration test on two disposable SQL Server instances.
Install this branch's sp_BlitzBackups in source master. Provision an exclusive
FRKPushRemote linked server to the destination, with data access and RPC OUT,
and a login that can create/drop databases and alter dbo.backupset there.
Run with sqlcmd -b. The test toggles only this dedicated link's RPC OUT option.
No source msdb metadata is modified. */
IF DB_ID(N'FRKPushSource') IS NOT NULL
THROW 51000,'Source fixture already exists.',1;
IF EXISTS(SELECT 1 FROM FRKPushRemote.master.sys.databases WHERE name=N'FRKPushHistory')
THROW 51000,'Remote fixture already exists.',1;
CREATE DATABASE FRKPushSource;
GO
DECLARE @File nvarchar(512)=CONVERT(nvarchar(400),SERVERPROPERTY('InstanceDefaultDataPath'))+N'FRKPushSource.bak',
@Definition nvarchar(max),@UUID uniqueidentifier,@RemoteCreated bit=0;
BEGIN TRY
BACKUP DATABASE FRKPushSource TO DISK=@File WITH INIT;
SELECT @UUID=backup_set_uuid FROM msdb.dbo.backupset WHERE database_name=N'FRKPushSource'
AND database_guid=(SELECT database_guid FROM sys.database_recovery_status WHERE database_id=DB_ID(N'FRKPushSource'));
SELECT @Definition=definition FROM sys.sql_modules WHERE object_id=OBJECT_ID(N'dbo.sp_BlitzBackups');
SET @Definition=REPLACE(@Definition,N'ALTER PROCEDURE',N'CREATE PROCEDURE');
EXEC FRKPushSource.sys.sp_executesql @Definition;
EXEC(N'CREATE DATABASE FRKPushHistory;') AT FRKPushRemote;
SET @RemoteCreated=1;
EXEC(N'SELECT TOP(0) * INTO FRKPushHistory.dbo.backupset FROM msdb.dbo.backupset;') AT FRKPushRemote;
/* Existing legacy table: remote DDL adds media columns, then four-part DML fills them. */
EXEC FRKPushSource.dbo.sp_BlitzBackups @PushBackupHistoryToListener=1,
@WriteBackupsToListenerName=N'FRKPushRemote',@WriteBackupsToDatabaseName=N'FRKPushHistory',@WriteBackupsLastHours=1;
EXEC sys.sp_executesql N'IF NOT EXISTS(SELECT 1 FROM FRKPushRemote.FRKPushHistory.dbo.backupset WHERE backup_set_uuid=@UUID
AND frk_media_is_usable=1 AND frk_media_has_discard=0)
THROW 51000,''Remote schema upgrade or media mapping failed.'',1;',N'@UUID uniqueidentifier',@UUID;
EXEC sys.sp_executesql N'UPDATE FRKPushRemote.FRKPushHistory.dbo.backupset SET frk_media_is_usable=0,frk_media_has_discard=1 WHERE backup_set_uuid=@UUID;',N'@UUID uniqueidentifier',@UUID;
EXEC master.dbo.sp_serveroption N'FRKPushRemote',N'rpc out',N'false';
/* A retained full outside a zero-hour window must refresh already populated facts without RPC. */
EXEC FRKPushSource.dbo.sp_BlitzBackups @PushBackupHistoryToListener=1,
@WriteBackupsToListenerName=N'FRKPushRemote',@WriteBackupsToDatabaseName=N'FRKPushHistory',@WriteBackupsLastHours=0;
EXEC sys.sp_executesql N'IF NOT EXISTS(SELECT 1 FROM FRKPushRemote.FRKPushHistory.dbo.backupset WHERE backup_set_uuid=@UUID
AND frk_media_is_usable=1 AND frk_media_has_discard=0)
THROW 51000,''Remote existing media facts were not refreshed without RPC.'',1;',N'@UUID uniqueidentifier',@UUID;
EXEC master.dbo.sp_serveroption N'FRKPushRemote',N'rpc out',N'true';
EXEC(N'USE FRKPushHistory; CREATE TABLE dbo.UpdateAudit(RowsUpdated int);') AT FRKPushRemote;
EXEC(N'USE FRKPushHistory; EXEC(N''CREATE TRIGGER dbo.TrackUpdates ON dbo.backupset AFTER UPDATE AS INSERT dbo.UpdateAudit SELECT COUNT(*) FROM inserted;'');') AT FRKPushRemote;
EXEC FRKPushSource.dbo.sp_BlitzBackups @PushBackupHistoryToListener=1,
@WriteBackupsToListenerName=N'FRKPushRemote',@WriteBackupsToDatabaseName=N'FRKPushHistory',@WriteBackupsLastHours=0;
EXEC(N'IF EXISTS(SELECT 1 FROM FRKPushHistory.dbo.UpdateAudit WHERE RowsUpdated>0) THROW 51000,''Unchanged history rows were rewritten.'',1;') AT FRKPushRemote;
EXEC(N'USE FRKPushHistory; DROP TRIGGER dbo.TrackUpdates;') AT FRKPushRemote;
EXEC(N'ALTER TABLE FRKPushHistory.dbo.backupset DROP COLUMN frk_media_is_usable,frk_media_has_discard; DELETE FRKPushHistory.dbo.backupset;') AT FRKPushRemote;
EXEC master.dbo.sp_serveroption N'FRKPushRemote',N'rpc out',N'false';
/* Legacy destinations without RPC retain the supported history-only push. */
EXEC FRKPushSource.dbo.sp_BlitzBackups @PushBackupHistoryToListener=1,
@WriteBackupsToListenerName=N'FRKPushRemote',@WriteBackupsToDatabaseName=N'FRKPushHistory',@WriteBackupsLastHours=1;
EXEC sys.sp_executesql N'IF NOT EXISTS(SELECT 1 FROM FRKPushRemote.FRKPushHistory.dbo.backupset WHERE backup_set_uuid=@UUID)
THROW 51000,''Legacy history push requires RPC.'',1;',N'@UUID uniqueidentifier',@UUID;
END TRY
BEGIN CATCH
EXEC master.dbo.sp_serveroption N'FRKPushRemote',N'rpc out',N'true';
IF @RemoteCreated=1 EXEC(N'DROP DATABASE FRKPushHistory;') AT FRKPushRemote;
IF DB_ID(N'FRKPushSource') IS NOT NULL DROP DATABASE FRKPushSource;
THROW;
END CATCH;
EXEC master.dbo.sp_serveroption N'FRKPushRemote',N'rpc out',N'true';
EXEC(N'DROP DATABASE FRKPushHistory;') AT FRKPushRemote;
DROP DATABASE FRKPushSource;
PRINT 'PASS: remote push schema upgrade, media refresh, and RPC-disabled compatibility';
23 changes: 14 additions & 9 deletions sp_BlitzBackups.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1939,25 +1939,30 @@ END

IF @PushMediaFacts=1
BEGIN
/* Refresh the push window and retained full anchors with missing facts.
/* Refresh media facts for the push window and retained full anchors.
Recompute existing values so corrected source metadata is reflected; missing
source rows are left unchanged. Widen @WriteBackupsLastHours for older logs.
Evaluate media on the source, never by joining unqualified central media IDs. */
SET @StringToExecute=N'UPDATE h SET
frk_media_is_usable=f.frk_media_is_usable,frk_media_has_discard=f.frk_media_has_discard,
first_family_number=b.first_family_number,last_family_number=b.last_family_number,
first_recovery_fork_guid=b.first_recovery_fork_guid,last_recovery_fork_guid=b.last_recovery_fork_guid,
fork_point_lsn=b.fork_point_lsn,differential_base_lsn=b.differential_base_lsn,
differential_base_guid=b.differential_base_guid,is_copy_only=b.is_copy_only
FROM '+QUOTENAME(@WriteBackupsToListenerName)+N'.'+QUOTENAME(@WriteBackupsToDatabaseName)+N'.dbo.backupset h
JOIN msdb.dbo.backupset b ON b.backup_set_uuid=h.backup_set_uuid
CROSS APPLY (SELECT
frk_media_is_usable=CASE WHEN b.first_family_number>0 AND b.last_family_number>=b.first_family_number
AND (SELECT COUNT(DISTINCT m.family_sequence_number) FROM msdb.dbo.backupmediafamily m
WHERE m.media_set_id=b.media_set_id
AND m.family_sequence_number BETWEEN b.first_family_number AND b.last_family_number
AND m.physical_device_name IS NOT NULL AND UPPER(m.physical_device_name)<>N''NUL''
AND m.physical_device_name<>N''/dev/null'')=b.last_family_number-b.first_family_number+1 THEN 1 ELSE 0 END,
frk_media_has_discard=CASE WHEN EXISTS(SELECT 1 FROM msdb.dbo.backupmediafamily m WHERE m.media_set_id=b.media_set_id
AND (UPPER(m.physical_device_name)=N''NUL'' OR m.physical_device_name=N''/dev/null'')) THEN 1 ELSE 0 END,
first_family_number=b.first_family_number,last_family_number=b.last_family_number,
first_recovery_fork_guid=b.first_recovery_fork_guid,last_recovery_fork_guid=b.last_recovery_fork_guid,
fork_point_lsn=b.fork_point_lsn,differential_base_lsn=b.differential_base_lsn,
differential_base_guid=b.differential_base_guid,is_copy_only=b.is_copy_only
FROM '+QUOTENAME(@WriteBackupsToListenerName)+N'.'+QUOTENAME(@WriteBackupsToDatabaseName)+N'.dbo.backupset h
JOIN msdb.dbo.backupset b ON b.backup_set_uuid=h.backup_set_uuid
AND (UPPER(m.physical_device_name)=N''NUL'' OR m.physical_device_name=N''/dev/null'')) THEN 1 ELSE 0 END) f
WHERE (b.backup_start_date>=DATEADD(hour,@Hours,SYSDATETIME()) OR b.type=''D'')
AND (h.frk_media_is_usable IS NULL OR h.frk_media_has_discard IS NULL);';
AND EXISTS(SELECT f.frk_media_is_usable,f.frk_media_has_discard,b.first_family_number,b.last_family_number,b.first_recovery_fork_guid,b.last_recovery_fork_guid,b.fork_point_lsn,b.differential_base_lsn,b.differential_base_guid,b.is_copy_only
EXCEPT SELECT h.frk_media_is_usable,h.frk_media_has_discard,h.first_family_number,h.last_family_number,h.first_recovery_fork_guid,h.last_recovery_fork_guid,h.fork_point_lsn,h.differential_base_lsn,h.differential_base_guid,h.is_copy_only);';
EXEC sys.sp_executesql @StringToExecute,N'@Hours int',@Hours=@WriteBackupsLastHours;
END;

Expand Down