From 4a9ca10c9497b85aae4019d931ebd1feaa80a9a7 Mon Sep 17 00:00:00 2001 From: loganb Date: Fri, 20 Mar 2026 14:52:46 -0700 Subject: [PATCH] Omit two tables from audit log archiving --- .../sqlserver/onprc_ehr-26.001-26.002.sql | 217 ++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 onprc_ehr/resources/schemas/dbscripts/sqlserver/onprc_ehr-26.001-26.002.sql diff --git a/onprc_ehr/resources/schemas/dbscripts/sqlserver/onprc_ehr-26.001-26.002.sql b/onprc_ehr/resources/schemas/dbscripts/sqlserver/onprc_ehr-26.001-26.002.sql new file mode 100644 index 000000000..4101b6ed1 --- /dev/null +++ b/onprc_ehr/resources/schemas/dbscripts/sqlserver/onprc_ehr-26.001-26.002.sql @@ -0,0 +1,217 @@ +SET QUOTED_IDENTIFIER ON; +GO + +ALTER PROCEDURE [audit].[ArchiveAuditTables] ( + @RetentionMonths INT OUTPUT +) +AS +BEGIN + SET NOCOUNT ON; + + -- Declare variables + DECLARE @SourceDB NVARCHAR(128) = DB_NAME(), + @DestDB NVARCHAR(128) = 'labkey_audit', + @SchemaName NVARCHAR(128) = 'audit'; + + SET @RetentionMonths = CASE WHEN @RetentionMonths - 6 > 12 THEN @RetentionMonths - 6 ELSE 12 END; + PRINT N'Archiving audit logs older than ' + CAST(@RetentionMonths AS NVARCHAR(3)) + N' months old' + + DECLARE @CutoffDate DATETIME = DATEADD(MONTH, -@RetentionMonths, GETDATE()); + + + -- Validate if source database exists + IF NOT EXISTS (SELECT 1 FROM sys.databases WHERE name = @SourceDB) +BEGIN + RAISERROR('Source database "%s" does not exist.', 16, 1, @SourceDB); + RETURN; +END + + -- Validate if destination database exists + IF NOT EXISTS (SELECT 1 FROM sys.databases WHERE name = @DestDB) +BEGIN + RAISERROR('Destination database "%s" does not exist.', 16, 1, @DestDB); + RETURN; +END + + -- Create ArchiveAuditLog table if not exists (useful for testing) + DECLARE @CreateLogTableSQL NVARCHAR(MAX) = ' + IF NOT EXISTS (SELECT 1 FROM ' + QUOTENAME(@DestDB) + '.INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = ''dbo'' AND TABLE_NAME = ''ArchiveAuditLog'') + BEGIN + EXEC(''USE ' + QUOTENAME(@DestDB) + '; + CREATE TABLE dbo.ArchiveAuditLog ( + LogID INT IDENTITY(1,1) NOT NULL, + TableName NVARCHAR(128) NOT NULL, + Operation NVARCHAR(50) NOT NULL, + StartTime DATETIME NOT NULL, + EndTime DATETIME NULL, + Status NVARCHAR(50) NULL, + RecordsProcessed INT NULL, + ErrorMessage NVARCHAR(MAX) NULL, + RetentionMonths INT NULL, + CONSTRAINT PK_ArchiveAuditLog PRIMARY KEY (LogID) + )''); + END'; + +EXEC sp_executesql @CreateLogTableSQL; + + -- Create RetentionMonths column in ArchiveAuditLog table if not exist + DECLARE @CreateRetentionColumnSQL NVARCHAR(MAX) = ' + IF NOT EXISTS (SELECT 1 FROM ' + QUOTENAME(@DestDB) + '.INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = ''dbo'' + AND TABLE_NAME = ''ArchiveAuditLog'' + AND COLUMN_NAME = ''RetentionMonths'') + BEGIN + EXEC(''USE ' + QUOTENAME(@DestDB) + '; + ALTER TABLE dbo.ArchiveAuditLog + ADD RetentionMonths INT NULL + ''); + END'; + +EXEC sp_executesql @CreateRetentionColumnSQL; + + -- Validate if source schema exists + DECLARE @SourceSchemaCheck NVARCHAR(MAX) = ' + IF NOT EXISTS (SELECT 1 FROM ' + QUOTENAME(@SourceDB) + '.sys.schemas WHERE name = ''' + @SchemaName + ''') + BEGIN + RAISERROR(''Source schema "%s" does not exist'', 16, 1, ''' + @SchemaName + '''); + END'; + +EXEC sp_executesql @SourceSchemaCheck; + + -- Create destination schema if not exists + DECLARE @CreateDestSchemaSQL NVARCHAR(MAX) = ' + IF NOT EXISTS (SELECT 1 FROM ' + QUOTENAME(@DestDB) + '.sys.schemas WHERE name = ''' + @SchemaName + ''') + BEGIN + EXEC ' + QUOTENAME(@DestDB) + '.sys.sp_executesql N''CREATE SCHEMA ' + QUOTENAME(@SchemaName) + '''; + END'; + +EXEC sp_executesql @CreateDestSchemaSQL; + + -- Get list of tables to process +CREATE TABLE #TableList (TableName NVARCHAR(128)); + +DECLARE @GetTablesSQL NVARCHAR(MAX) = ' + INSERT INTO #TableList + SELECT TABLE_NAME + FROM ' + QUOTENAME(@SourceDB) + '.INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = ''' + @SchemaName + ''' + AND TABLE_NAME NOT IN (''c3d330_userauditdomain'', ''c3d317_groupauditdomain'')'; + +EXEC sp_executesql @GetTablesSQL; + + DECLARE @CurrentTable NVARCHAR(128); + DECLARE TableCursor CURSOR LOCAL FAST_FORWARD FOR +SELECT TableName FROM #TableList; + +OPEN TableCursor; +FETCH NEXT FROM TableCursor INTO @CurrentTable; + +WHILE @@FETCH_STATUS = 0 +BEGIN + DECLARE @LogID INT; + + -- Log the start of archiving for current table + DECLARE @InsertLogSQL NVARCHAR(MAX) = ' + USE ' + QUOTENAME(@DestDB) + '; + INSERT INTO dbo.ArchiveAuditLog + (TableName, Operation, StartTime, Status, RetentionMonths) + VALUES (''' + @CurrentTable + ''', ''Archive'', GETDATE(), ''Started'', ' + CAST(@RetentionMonths AS NVARCHAR(10)) + '); + SELECT @LogIDOUT = SCOPE_IDENTITY();'; + +EXEC sp_executesql @InsertLogSQL, N'@LogIDOUT INT OUTPUT', @LogIDOUT = @LogID OUTPUT; + +BEGIN TRY + DECLARE @FullSourceTable NVARCHAR(512) = QUOTENAME(@SourceDB) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@CurrentTable), + @FullDestTable NVARCHAR(512) = QUOTENAME(@DestDB) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@CurrentTable), + @ColumnList NVARCHAR(MAX) = ''; + + -- Create destination table if it doesn't exist + DECLARE @CheckTableSQL NVARCHAR(MAX) = ' + IF NOT EXISTS (SELECT 1 FROM ' + QUOTENAME(@DestDB) + '.INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = ''' + @SchemaName + ''' + AND TABLE_NAME = ''' + @CurrentTable + ''') + BEGIN + SELECT * INTO ' + @FullDestTable + ' + FROM ' + @FullSourceTable + ' + WHERE 1 = 0; + END'; + +EXEC sp_executesql @CheckTableSQL; + + -- Get column list (excluding identity columns) +CREATE TABLE #Columns (ColumnName NVARCHAR(128), IsIdentity BIT); + +DECLARE @GetColumnsSQL NVARCHAR(MAX) = ' + INSERT INTO #Columns + SELECT c.name AS ColumnName, + COLUMNPROPERTY(OBJECT_ID(''' + @FullSourceTable + '''), c.name, ''IsIdentity'') AS IsIdentity + FROM ' + QUOTENAME(@SourceDB) + '.sys.columns c + JOIN ' + QUOTENAME(@SourceDB) + '.sys.tables t ON c.object_id = t.object_id + JOIN ' + QUOTENAME(@SourceDB) + '.sys.schemas s ON t.schema_id = s.schema_id + WHERE s.name = ''' + @SchemaName + ''' + AND t.name = ''' + @CurrentTable + ''''; + +EXEC sp_executesql @GetColumnsSQL; + +SELECT @ColumnList = STRING_AGG(QUOTENAME(ColumnName), ', ') +FROM #Columns +WHERE IsIdentity = 0; + +DROP TABLE #Columns; + +-- Archive data +BEGIN TRANSACTION; + + DECLARE @ArchiveSQL NVARCHAR(MAX) = ' + INSERT INTO ' + @FullDestTable + ' (' + @ColumnList + ') + SELECT ' + @ColumnList + ' + FROM ' + @FullSourceTable + ' + WHERE Created < @CutoffDate; + + DECLARE @RecordsInserted INT = @@ROWCOUNT; + + DELETE FROM ' + @FullSourceTable + ' + WHERE Created < @CutoffDate; + + DECLARE @RecordsDeleted INT = @@ROWCOUNT; + + UPDATE ' + QUOTENAME(@DestDB) + '.dbo.ArchiveAuditLog + SET RecordsProcessed = @RecordsInserted, + EndTime = GETDATE(), + Status = ''Success'' + WHERE LogID = @LogID;'; + +EXEC sp_executesql @ArchiveSQL, + N'@CutoffDate DATETIME, @LogID INT', + @CutoffDate = @CutoffDate, + @LogID = @LogID; + +COMMIT TRANSACTION; +END TRY +BEGIN CATCH +IF @@TRANCOUNT > 0 + ROLLBACK TRANSACTION; + + DECLARE @ErrorMessage NVARCHAR(4000) = 'Error archiving ' + @CurrentTable + ': ' + ERROR_MESSAGE(); + + DECLARE @UpdateLogSQL NVARCHAR(MAX) = ' + UPDATE ' + QUOTENAME(@DestDB) + '.dbo.ArchiveAuditLog + SET EndTime = GETDATE(), + Status = ''Error'', + ErrorMessage = @ErrorMessage + WHERE LogID = ' + CAST(@LogID AS NVARCHAR(10)); + +EXEC sp_executesql @UpdateLogSQL, N'@ErrorMessage NVARCHAR(4000)', @ErrorMessage = @ErrorMessage; + + PRINT @ErrorMessage; +END CATCH + +FETCH NEXT FROM TableCursor INTO @CurrentTable; +END + +CLOSE TableCursor; +DEALLOCATE TableCursor; + +DROP TABLE #TableList; +END \ No newline at end of file