Files
workspace/code/fms/fms-api/config/migrations/verify_module_architecture.sql
T
2026-08-03 22:54:03 +08:00

164 lines
7.4 KiB
Transact-SQL

/* Read-only verification for the module architecture cutover. */
SET NOCOUNT ON;
IF DB_NAME() <> N'FMS'
THROW 53500, N'This verification can only run against the FMS database.', 1;
IF EXISTS (
SELECT required.name
FROM (VALUES
(N's_module'), (N's_module_page'), (N's_module_data'), (N's_module_report'),
(N's_module_dashboard'), (N's_module_workflow'), (N's_function_node'),
(N's_page_node'), (N's_module_profile'), (N's_module_field'),
(N's_module_field_list'), (N's_module_field_form'), (N's_module_field_query'),
(N's_module_action'), (N's_module_relation'), (N'b_role'), (N'b_user_role'),
(N'b_role_data_scope')
) AS required(name)
LEFT JOIN sys.tables AS actual ON actual.name = required.name AND actual.schema_id = SCHEMA_ID(N'dbo')
WHERE actual.object_id IS NULL
)
THROW 53501, N'Required module architecture tables are missing.', 1;
IF EXISTS (
SELECT required.name
FROM (VALUES
(N'v_s_function_node'), (N'v_s_page_node'), (N'v_s_module_runtime'),
(N'v_s_module_profile'), (N'v_s_module_action'),
(N'v_s_user_function_permission'), (N'v_s_user_page_permission'),
(N'v_s_user_action_permission')
) AS required(name)
LEFT JOIN sys.views AS actual ON actual.name = required.name AND actual.schema_id = SCHEMA_ID(N'dbo')
WHERE actual.object_id IS NULL
)
THROW 53502, N'Required module runtime views are missing.', 1;
IF EXISTS (
SELECT 1
FROM (VALUES
(N's_module_field_edit'), (N's_module_power'), (N'b_user_module'),
(N'b_user_power'), (N'b_other_bmfl')
) AS legacy(name)
INNER JOIN sys.tables AS actual ON actual.name = legacy.name AND actual.schema_id = SCHEMA_ID(N'dbo')
)
THROW 53503, N'Legacy module metadata tables still exist.', 1;
IF EXISTS (
SELECT 1
FROM dbo.s_module AS module_row
LEFT JOIN dbo.s_module_page AS page_row ON page_row.b_module_id = module_row.b_id
LEFT JOIN dbo.s_module_data AS data_row ON data_row.b_module_id = module_row.b_id
LEFT JOIN dbo.s_module_report AS report_row ON report_row.b_module_id = module_row.b_id
LEFT JOIN dbo.s_module_dashboard AS dashboard_row ON dashboard_row.b_module_id = module_row.b_id
LEFT JOIN dbo.s_module_workflow AS workflow_row ON workflow_row.b_module_id = module_row.b_id
WHERE (CASE WHEN page_row.b_module_id IS NULL THEN 0 ELSE 1 END
+ CASE WHEN data_row.b_module_id IS NULL THEN 0 ELSE 1 END
+ CASE WHEN report_row.b_module_id IS NULL THEN 0 ELSE 1 END
+ CASE WHEN dashboard_row.b_module_id IS NULL THEN 0 ELSE 1 END
+ CASE WHEN workflow_row.b_module_id IS NULL THEN 0 ELSE 1 END) <> 1
OR (module_row.b_module_type = 'page' AND page_row.b_module_id IS NULL)
OR (module_row.b_module_type = 'data' AND data_row.b_module_id IS NULL)
OR (module_row.b_module_type = 'report' AND report_row.b_module_id IS NULL)
OR (module_row.b_module_type = 'dashboard' AND dashboard_row.b_module_id IS NULL)
OR (module_row.b_module_type = 'workflow' AND workflow_row.b_module_id IS NULL)
)
THROW 53504, N'Module subtype records are incomplete or conflicting.', 1;
IF EXISTS (
SELECT 1
FROM dbo.s_module AS module_row
LEFT JOIN dbo.s_module_profile AS profile_row
ON profile_row.b_module_id = module_row.b_id AND profile_row.b_default = 1
WHERE module_row.b_module_type = 'data' AND profile_row.b_id IS NULL
)
THROW 53505, N'A data module has no default profile.', 1;
IF EXISTS (
SELECT 1
FROM dbo.s_module_profile AS profile_row
INNER JOIN dbo.s_module AS module_row ON module_row.b_id = profile_row.b_module_id
WHERE module_row.b_module_type <> 'data'
)
THROW 53506, N'A profile belongs to a non-data module.', 1;
IF EXISTS (
SELECT 1
FROM dbo.s_function_node AS child
INNER JOIN dbo.s_function_node AS parent_row ON parent_row.b_id = child.b_parent_id
WHERE parent_row.b_node_type <> 'directory'
)
THROW 53507, N'A function node is placed below a non-directory node.', 1;
IF EXISTS (
SELECT 1
FROM dbo.s_page_node AS child
INNER JOIN dbo.s_page_node AS parent_row
ON parent_row.b_page_module_id = child.b_page_module_id
AND parent_row.b_id = child.b_parent_id
WHERE parent_row.b_node_type = 'module_ref'
)
THROW 53508, N'A page node is placed below a module reference.', 1;
DECLARE @FunctionCycle bit = 0;
;WITH function_tree AS (
SELECT b_id AS origin_id, b_parent_id AS current_id,
CAST(N'/' + CONVERT(nvarchar(30), b_id) + N'/' AS nvarchar(max)) AS visited
FROM dbo.s_function_node
WHERE b_parent_id IS NOT NULL
UNION ALL
SELECT tree.origin_id, parent_row.b_parent_id,
tree.visited + CONVERT(nvarchar(30), parent_row.b_id) + N'/'
FROM function_tree AS tree
INNER JOIN dbo.s_function_node AS parent_row ON parent_row.b_id = tree.current_id
WHERE tree.current_id IS NOT NULL
AND tree.visited NOT LIKE N'%/' + CONVERT(nvarchar(30), parent_row.b_id) + N'/%'
)
SELECT TOP (1) @FunctionCycle = 1
FROM function_tree WHERE current_id = origin_id
OPTION (MAXRECURSION 32767);
IF @FunctionCycle = 1
THROW 53509, N'The function tree contains a cycle.', 1;
DECLARE @PageCycle bit = 0;
;WITH page_tree AS (
SELECT b_page_module_id AS page_id, b_id AS origin_id, b_parent_id AS current_id,
CAST(N'/' + CONVERT(nvarchar(30), b_id) + N'/' AS nvarchar(max)) AS visited
FROM dbo.s_page_node
WHERE b_parent_id IS NOT NULL
UNION ALL
SELECT tree.page_id, tree.origin_id, parent_row.b_parent_id,
tree.visited + CONVERT(nvarchar(30), parent_row.b_id) + N'/'
FROM page_tree AS tree
INNER JOIN dbo.s_page_node AS parent_row
ON parent_row.b_page_module_id = tree.page_id AND parent_row.b_id = tree.current_id
WHERE tree.current_id IS NOT NULL
AND tree.visited NOT LIKE N'%/' + CONVERT(nvarchar(30), parent_row.b_id) + N'/%'
)
SELECT TOP (1) @PageCycle = 1
FROM page_tree WHERE current_id = origin_id
OPTION (MAXRECURSION 32767);
IF @PageCycle = 1
THROW 53510, N'The page tree contains a cycle.', 1;
IF EXISTS (SELECT 1 FROM dbo.s_module WHERE b_i18n <> 'meta.module.' + CONVERT(varchar(30), b_id))
THROW 53511, N'Module i18n keys are not ID based.', 1;
IF EXISTS (SELECT 1 FROM dbo.s_function_node WHERE b_i18n <> 'meta.node.' + CONVERT(varchar(30), b_id))
THROW 53512, N'Function node i18n keys are not ID based.', 1;
IF EXISTS (SELECT 1 FROM dbo.s_page_node WHERE b_i18n <> 'meta.node.' + CONVERT(varchar(30), b_id))
THROW 53513, N'Page node i18n keys are not ID based.', 1;
IF EXISTS (SELECT 1 FROM dbo.s_module_profile WHERE b_i18n <> 'meta.profile.' + CONVERT(varchar(30), b_id))
THROW 53514, N'Profile i18n keys are not ID based.', 1;
IF EXISTS (SELECT 1 FROM dbo.s_module_field WHERE b_i18n <> 'meta.field.' + CONVERT(varchar(30), b_id))
THROW 53515, N'Field i18n keys are not ID based.', 1;
IF EXISTS (SELECT 1 FROM dbo.s_module_action WHERE b_i18n <> 'meta.action.' + CONVERT(varchar(30), b_id))
THROW 53516, N'Action i18n keys are not ID based.', 1;
SELECT
(SELECT COUNT(*) FROM dbo.s_module) AS module_count,
(SELECT COUNT(*) FROM dbo.s_function_node) AS function_node_count,
(SELECT COUNT(*) FROM dbo.s_page_node) AS page_node_count,
(SELECT COUNT(*) FROM dbo.s_module_profile) AS profile_count,
(SELECT COUNT(*) FROM dbo.s_module_field) AS field_count,
(SELECT COUNT(*) FROM dbo.s_module_action) AS action_count;