Files
SIG-CM2.0/database/migrations/V010_ROLLBACK.sql

184 lines
7.3 KiB
MySQL
Raw Normal View History

feat(db): V010 audit infrastructure + temporal tables Applied to SIGCM2 (dev) and SIGCM2_Test. V010__audit_infrastructure.sql (idempotent, ~280 LoC): - Filegroups AUDIT_HOT + AUDIT_COLD with physical files (per-DB logical names via DB_NAME() prefix to avoid collision in dev/test). - pf/ps_AuditEvent_Monthly + pf/ps_SecurityEvent_Monthly (RANGE RIGHT, DATETIME2(3), 14 boundaries 2026-01..2027-02 → 15 partitions). Job extends forward monthly in B11. - dbo.AuditEvent (partitioned, clustered PK on OccurredAt+Id) + 4 indexes (Actor/Target/Action/Correlation) with PAGE compression. - dbo.SecurityEvent (partitioned) + 3 indexes (Actor/Action_Result/Ip_Failure). - CHECK constraints: Action LIKE '%.%', ISJSON(Metadata), Result IN (success|failure). - SYSTEM_VERSIONING ON in Usuario/Rol/Permiso/RolPermiso with 10 YEARS retention + PAGE compression in history tables. - No hard FK on ActorUserId → Usuario.Id (soft FK — audit must survive user deletion). V010_ROLLBACK.sql: emergency reversal (WARNING: destroys all audit history). database/README.md: migration order + V010 prod-apply notes. tests/SIGCM2.TestSupport/SqlTestFixture.cs: - EnsureV010SchemaAsync() validates audit infra is applied (fails fast with clear message if not — migration itself requires ALTER DATABASE privileges and is applied manually via sqlcmd). - Respawn TablesToIgnore extended with *_History (engine rejects direct DELETE on system-versioned history tables). tests/SIGCM2.Api.Tests/Audit/V010MigrationTests.cs — 5 smoke tests: - AuditEvent insert+roundtrip with CorrelationId. - CK_AuditEvent_Action rejects Action without '.'. - CK_AuditEvent_Metadata rejects non-JSON. - CK_SecurityEvent_Result rejects invalid Result. - Usuario SYSTEM_VERSIONING: temporal query FOR SYSTEM_TIME AS OF returns pre-update state + Usuario_History populated. Suite: 130/130 passing (previous 124 + spike B0 + 5 new B1). No regressions. Refs: sdd/udt-010-auditoria-trazabilidad/{spec#REQ-AUD-1,2, #REQ-SEC-1, design#D-4, tasks}
2026-04-16 13:10:04 -03:00
-- V010_ROLLBACK.sql
-- Reversa de V010__audit_infrastructure.sql.
--
-- ⚠️ ADVERTENCIA: ejecutar este script ELIMINA toda la historia auditada.
-- - dbo.AuditEvent y dbo.SecurityEvent se dropean (junto con datos).
-- - History tables (Usuario_History, Rol_History, Permiso_History, RolPermiso_History) se dropean.
-- - Particionamiento, filegroups y archivos físicos se desmontan.
--
-- Uso intended: ROLLBACK de emergencia en entornos NO-productivos.
-- En prod futuro, este script NO se ejecuta: si hace falta revertir, se hace
-- restore de backup previo a V010.
SET QUOTED_IDENTIFIER ON;
SET ANSI_NULLS ON;
SET NOCOUNT ON;
GO
-- ═══════════════════════════════════════════════════════════════════════
-- 1. Apagar SYSTEM_VERSIONING + remover columnas PERIOD en las 4 tablas
-- ═══════════════════════════════════════════════════════════════════════
-- Usuario
IF EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.Usuario') AND temporal_type = 2)
BEGIN
ALTER TABLE dbo.Usuario SET (SYSTEM_VERSIONING = OFF);
PRINT 'Usuario: SYSTEM_VERSIONING OFF.';
END
GO
IF EXISTS (SELECT 1 FROM sys.periods WHERE object_id = OBJECT_ID('dbo.Usuario'))
BEGIN
ALTER TABLE dbo.Usuario DROP PERIOD FOR SYSTEM_TIME;
PRINT 'Usuario: PERIOD FOR SYSTEM_TIME dropped.';
END
GO
IF COL_LENGTH('dbo.Usuario', 'ValidFrom') IS NOT NULL
BEGIN
ALTER TABLE dbo.Usuario DROP CONSTRAINT IF EXISTS DF_Usuario_ValidFrom;
ALTER TABLE dbo.Usuario DROP CONSTRAINT IF EXISTS DF_Usuario_ValidTo;
ALTER TABLE dbo.Usuario DROP COLUMN ValidFrom, ValidTo;
PRINT 'Usuario: ValidFrom/ValidTo dropped.';
END
GO
IF OBJECT_ID(N'dbo.Usuario_History', N'U') IS NOT NULL
BEGIN
DROP TABLE dbo.Usuario_History;
PRINT 'Usuario_History dropped.';
END
GO
-- Rol
IF EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.Rol') AND temporal_type = 2)
ALTER TABLE dbo.Rol SET (SYSTEM_VERSIONING = OFF);
GO
IF EXISTS (SELECT 1 FROM sys.periods WHERE object_id = OBJECT_ID('dbo.Rol'))
ALTER TABLE dbo.Rol DROP PERIOD FOR SYSTEM_TIME;
GO
IF COL_LENGTH('dbo.Rol', 'ValidFrom') IS NOT NULL
BEGIN
ALTER TABLE dbo.Rol DROP CONSTRAINT IF EXISTS DF_Rol_ValidFrom;
ALTER TABLE dbo.Rol DROP CONSTRAINT IF EXISTS DF_Rol_ValidTo;
ALTER TABLE dbo.Rol DROP COLUMN ValidFrom, ValidTo;
END
GO
IF OBJECT_ID(N'dbo.Rol_History', N'U') IS NOT NULL DROP TABLE dbo.Rol_History;
GO
-- Permiso
IF EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.Permiso') AND temporal_type = 2)
ALTER TABLE dbo.Permiso SET (SYSTEM_VERSIONING = OFF);
GO
IF EXISTS (SELECT 1 FROM sys.periods WHERE object_id = OBJECT_ID('dbo.Permiso'))
ALTER TABLE dbo.Permiso DROP PERIOD FOR SYSTEM_TIME;
GO
IF COL_LENGTH('dbo.Permiso', 'ValidFrom') IS NOT NULL
BEGIN
ALTER TABLE dbo.Permiso DROP CONSTRAINT IF EXISTS DF_Permiso_ValidFrom;
ALTER TABLE dbo.Permiso DROP CONSTRAINT IF EXISTS DF_Permiso_ValidTo;
ALTER TABLE dbo.Permiso DROP COLUMN ValidFrom, ValidTo;
END
GO
IF OBJECT_ID(N'dbo.Permiso_History', N'U') IS NOT NULL DROP TABLE dbo.Permiso_History;
GO
-- RolPermiso
IF EXISTS (SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('dbo.RolPermiso') AND temporal_type = 2)
ALTER TABLE dbo.RolPermiso SET (SYSTEM_VERSIONING = OFF);
GO
IF EXISTS (SELECT 1 FROM sys.periods WHERE object_id = OBJECT_ID('dbo.RolPermiso'))
ALTER TABLE dbo.RolPermiso DROP PERIOD FOR SYSTEM_TIME;
GO
IF COL_LENGTH('dbo.RolPermiso', 'ValidFrom') IS NOT NULL
BEGIN
ALTER TABLE dbo.RolPermiso DROP CONSTRAINT IF EXISTS DF_RolPermiso_ValidFrom;
ALTER TABLE dbo.RolPermiso DROP CONSTRAINT IF EXISTS DF_RolPermiso_ValidTo;
ALTER TABLE dbo.RolPermiso DROP COLUMN ValidFrom, ValidTo;
END
GO
IF OBJECT_ID(N'dbo.RolPermiso_History', N'U') IS NOT NULL DROP TABLE dbo.RolPermiso_History;
GO
-- ═══════════════════════════════════════════════════════════════════════
-- 2. Drop AuditEvent + SecurityEvent
-- ═══════════════════════════════════════════════════════════════════════
IF OBJECT_ID(N'dbo.AuditEvent', N'U') IS NOT NULL
BEGIN
DROP TABLE dbo.AuditEvent;
PRINT 'Table dbo.AuditEvent dropped.';
END
GO
IF OBJECT_ID(N'dbo.SecurityEvent', N'U') IS NOT NULL
BEGIN
DROP TABLE dbo.SecurityEvent;
PRINT 'Table dbo.SecurityEvent dropped.';
END
GO
-- ═══════════════════════════════════════════════════════════════════════
-- 3. Drop partition schemes + functions
-- ═══════════════════════════════════════════════════════════════════════
IF EXISTS (SELECT 1 FROM sys.partition_schemes WHERE name = 'ps_AuditEvent_Monthly')
DROP PARTITION SCHEME ps_AuditEvent_Monthly;
GO
IF EXISTS (SELECT 1 FROM sys.partition_functions WHERE name = 'pf_AuditEvent_Monthly')
DROP PARTITION FUNCTION pf_AuditEvent_Monthly;
GO
IF EXISTS (SELECT 1 FROM sys.partition_schemes WHERE name = 'ps_SecurityEvent_Monthly')
DROP PARTITION SCHEME ps_SecurityEvent_Monthly;
GO
IF EXISTS (SELECT 1 FROM sys.partition_functions WHERE name = 'pf_SecurityEvent_Monthly')
DROP PARTITION FUNCTION pf_SecurityEvent_Monthly;
GO
-- ═══════════════════════════════════════════════════════════════════════
-- 4. Remover archivos físicos y filegroups
-- ═══════════════════════════════════════════════════════════════════════
DECLARE @dbName NVARCHAR(128) = DB_NAME();
DECLARE @hotLogical NVARCHAR(128) = @dbName + N'_AUDIT_HOT';
DECLARE @coldLogical NVARCHAR(128) = @dbName + N'_AUDIT_COLD';
DECLARE @sql NVARCHAR(MAX);
IF EXISTS (SELECT 1 FROM sys.database_files WHERE name = @hotLogical)
BEGIN
SET @sql = N'ALTER DATABASE CURRENT REMOVE FILE [' + @hotLogical + N'];';
EXEC sp_executesql @sql;
PRINT 'File ' + @hotLogical + ' removed.';
END
IF EXISTS (SELECT 1 FROM sys.database_files WHERE name = @coldLogical)
BEGIN
SET @sql = N'ALTER DATABASE CURRENT REMOVE FILE [' + @coldLogical + N'];';
EXEC sp_executesql @sql;
PRINT 'File ' + @coldLogical + ' removed.';
END
IF EXISTS (SELECT 1 FROM sys.filegroups WHERE name = 'AUDIT_HOT')
EXEC sp_executesql N'ALTER DATABASE CURRENT REMOVE FILEGROUP AUDIT_HOT;';
IF EXISTS (SELECT 1 FROM sys.filegroups WHERE name = 'AUDIT_COLD')
EXEC sp_executesql N'ALTER DATABASE CURRENT REMOVE FILEGROUP AUDIT_COLD;';
GO
PRINT '';
PRINT 'V010 rolled back. Audit infrastructure removed. All audit history is permanently LOST.';
GO