fix(config): also clean NodeAcl + release ExternalIdReservation in SystemPlatform cleanup migration
This commit is contained in:
+61
@@ -29,6 +29,15 @@ namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations
|
|||||||
/// is keyed off the set of SystemPlatform namespaces, so on a DB with zero such rows every
|
/// is keyed off the set of SystemPlatform namespaces, so on a DB with zero such rows every
|
||||||
/// DELETE simply affects 0 rows (idempotent / safe to run repeatedly).
|
/// DELETE simply affects 0 rows (idempotent / safe to run repeatedly).
|
||||||
/// </para>
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Two tables carry LOGICAL refs with no physical FK and so were easy to miss: <c>NodeAcl</c>
|
||||||
|
/// (per-scope grants keyed by <c>ScopeKind</c> + <c>ScopeId</c>) is cleaned for the
|
||||||
|
/// Namespace/Equipment/FolderSegment/Tag scopes that get deleted, and
|
||||||
|
/// <c>ExternalIdReservation</c> (fleet-wide ZTag/SAPID reservations keyed by
|
||||||
|
/// <c>EquipmentUuid</c>) is RELEASED — not deleted — for the affected equipment so the
|
||||||
|
/// reserved external IDs free up while the audit row survives. Both run before the entity
|
||||||
|
/// rows they reference are removed so the id subqueries still resolve.
|
||||||
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class CleanupSystemPlatformNamespaces : Migration
|
public partial class CleanupSystemPlatformNamespaces : Migration
|
||||||
@@ -44,6 +53,58 @@ namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations
|
|||||||
-- stray equipment-scoped tag / Equipment could be bound to a SystemPlatform-namespace driver. Clean
|
-- stray equipment-scoped tag / Equipment could be bound to a SystemPlatform-namespace driver. Clean
|
||||||
-- both, plus anything that FKs into the affected Equipment (VirtualTag / ScriptedAlarm / state).
|
-- both, plus anything that FKs into the affected Equipment (VirtualTag / ScriptedAlarm / state).
|
||||||
|
|
||||||
|
-- Logical-reference cleanup that must run FIRST -------------------------------------------
|
||||||
|
-- NodeAcl rows carry purely LOGICAL scope refs (ScopeKind + ScopeId, no physical FK). When the
|
||||||
|
-- scoped entity is deleted below the ScopeId dangles and the ACL evaluator would still match it,
|
||||||
|
-- silently granting/denying on a node that no longer exists. ScopeKind persists as a string via
|
||||||
|
-- HasConversion<string>, so the values below ('Namespace'/'Equipment'/'FolderSegment'/'Tag') are
|
||||||
|
-- the literal enum member names. These deletes must read the entity-id subqueries while those rows
|
||||||
|
-- still exist, hence they run before the entity DELETEs further down.
|
||||||
|
|
||||||
|
-- Namespace-scoped grants pointing at the SystemPlatform namespaces themselves.
|
||||||
|
DELETE FROM [NodeAcl]
|
||||||
|
WHERE [ScopeKind] = 'Namespace'
|
||||||
|
AND [ScopeId] IN (
|
||||||
|
SELECT [NamespaceId] FROM [Namespace] WHERE [Kind] = 'SystemPlatform');
|
||||||
|
|
||||||
|
-- Equipment- and FolderSegment-scoped grants pointing at Equipment that hangs off the affected
|
||||||
|
-- DriverInstances (both share Equipment.EquipmentId as the ScopeId).
|
||||||
|
DELETE FROM [NodeAcl]
|
||||||
|
WHERE [ScopeKind] IN ('Equipment', 'FolderSegment')
|
||||||
|
AND [ScopeId] IN (
|
||||||
|
SELECT [EquipmentId] FROM [Equipment]
|
||||||
|
WHERE [DriverInstanceId] IN (
|
||||||
|
SELECT [DriverInstanceId] FROM [DriverInstance]
|
||||||
|
WHERE [NamespaceId] IN (
|
||||||
|
SELECT [NamespaceId] FROM [Namespace] WHERE [Kind] = 'SystemPlatform')));
|
||||||
|
|
||||||
|
-- Tag-scoped grants pointing at Tags bound to the affected DriverInstances (ScopeId = Tag.TagId).
|
||||||
|
DELETE FROM [NodeAcl]
|
||||||
|
WHERE [ScopeKind] = 'Tag'
|
||||||
|
AND [ScopeId] IN (
|
||||||
|
SELECT [TagId] FROM [Tag]
|
||||||
|
WHERE [DriverInstanceId] IN (
|
||||||
|
SELECT [DriverInstanceId] FROM [DriverInstance]
|
||||||
|
WHERE [NamespaceId] IN (
|
||||||
|
SELECT [NamespaceId] FROM [Namespace] WHERE [Kind] = 'SystemPlatform')));
|
||||||
|
|
||||||
|
-- ExternalIdReservation is NOT generation-versioned and must outlive equipment deletion as a
|
||||||
|
-- RELEASE, not a delete: active rows (ReleasedAt IS NULL) hold a fleet-wide unique reservation of
|
||||||
|
-- the ZTag/SAPID under a filtered unique index. If we left them active after dropping the
|
||||||
|
-- Equipment, the same external IDs could never be reused. Release them so the IDs free up while the
|
||||||
|
-- audit row survives. Run BEFORE the Equipment DELETE so the EquipmentUuid subquery still resolves.
|
||||||
|
UPDATE [ExternalIdReservation]
|
||||||
|
SET [ReleasedAt] = SYSUTCDATETIME(),
|
||||||
|
[ReleasedBy] = 'CleanupSystemPlatformNamespaces',
|
||||||
|
[ReleaseReason] = 'Equipment removed by SystemPlatform-namespace cleanup migration (retired NamespaceKind).'
|
||||||
|
WHERE [ReleasedAt] IS NULL
|
||||||
|
AND [EquipmentUuid] IN (
|
||||||
|
SELECT [EquipmentUuid] FROM [Equipment]
|
||||||
|
WHERE [DriverInstanceId] IN (
|
||||||
|
SELECT [DriverInstanceId] FROM [DriverInstance]
|
||||||
|
WHERE [NamespaceId] IN (
|
||||||
|
SELECT [NamespaceId] FROM [Namespace] WHERE [Kind] = 'SystemPlatform')));
|
||||||
|
|
||||||
-- Grandchildren of the affected Equipment ------------------------------------------------
|
-- Grandchildren of the affected Equipment ------------------------------------------------
|
||||||
-- ScriptedAlarmState is keyed by ScriptedAlarm.ScriptedAlarmId; remove before its ScriptedAlarm.
|
-- ScriptedAlarmState is keyed by ScriptedAlarm.ScriptedAlarmId; remove before its ScriptedAlarm.
|
||||||
DELETE FROM [ScriptedAlarmState]
|
DELETE FROM [ScriptedAlarmState]
|
||||||
|
|||||||
Reference in New Issue
Block a user