-- Diagnóstico preparado, NO ejecutado contra ningún destino en este preflight. -- Solo SELECT. Ejecutar únicamente con autorización de lectura, credenciales de -- mínimo privilegio y timeout impuesto por el cliente/operador, sin exponerlas. -- Fase 1: detenerse si faltan tablas/columnas. No ejecutar la fase 2 en legacy/parcial. SELECT VERSION() AS version, @@session.foreign_key_checks AS session_fk, @@global.foreign_key_checks AS global_fk, @@session.check_constraint_checks AS session_checks, @@global.check_constraint_checks AS global_checks; SELECT TABLE_NAME, TABLE_TYPE, ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME IN ('resources', 'resource_definitions', 'resource_template_fields', 'resource_characteristics', 'resource_specification_fields', 'resource_specification_field_choices', 'resource_specification_sections', 'resource_revisions', 'resource_revision_characteristics', 'resource_revision_specifications', 'resource_revision_specification_fields', 'resource_revision_specification_choices', 'software_installations', 'software_installation_revisions') ORDER BY TABLE_NAME; SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, CHARACTER_SET_NAME, COLLATION_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME IN ('resources', 'software_installations', 'software_installation_revisions') ORDER BY TABLE_NAME, ORDINAL_POSITION; SELECT TABLE_NAME, CONSTRAINT_NAME, CHECK_CLAUSE FROM information_schema.CHECK_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND TABLE_NAME = 'resources' ORDER BY CONSTRAINT_NAME; SELECT TABLE_NAME, CONSTRAINT_NAME, COLUMN_NAME, ORDINAL_POSITION, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = DATABASE() AND REFERENCED_TABLE_NAME IS NOT NULL AND TABLE_NAME IN ('resources', 'resource_revisions', 'resource_characteristics', 'resource_specification_fields', 'resource_revision_specifications', 'resource_revision_specification_fields', 'software_installations', 'software_installation_revisions') ORDER BY TABLE_NAME, CONSTRAINT_NAME, ORDINAL_POSITION; SELECT TABLE_NAME, INDEX_NAME, NON_UNIQUE, SEQ_IN_INDEX, COLUMN_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME IN ('resources', 'resource_revisions', 'software_installations', 'software_installation_revisions') ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX; -- Fase 2: solo tras confirmar esquema scoped completo y tablas anteriores. -- Sin nombres de clientes/productos, actores, UUID ni valores de identidad en la salida. SELECT COUNT(*) AS stock_os_templates FROM resource_definitions WHERE stockKey = 'template:os' AND kind = 'template' AND companyId IS NULL; SELECT field.stockKey, binding.required, binding.position, COUNT(*) AS bindings FROM resource_definitions template JOIN resource_template_fields binding ON binding.templateId = template.id JOIN resource_definitions field ON field.id = binding.fieldId WHERE template.stockKey = 'template:os' AND template.companyId IS NULL GROUP BY field.stockKey, binding.required, binding.position ORDER BY binding.position; SELECT choice.value, choice.position, COUNT(*) AS definitions FROM resource_definitions field JOIN resource_field_choices choice ON choice.fieldId = field.id WHERE field.stockKey = 'field:os_edition_state' AND field.companyId IS NULL GROUP BY choice.value, choice.position ORDER BY choice.position; SELECT r.scope, r.active, CASE WHEN template.stockKey = 'template:os' THEN 'stock' ELSE 'custom' END AS template_kind, COUNT(*) AS os_resources FROM resources r JOIN resource_definitions template ON template.id = r.templateId WHERE template.usage = 'os' GROUP BY r.scope, r.active, template_kind; SELECT COUNT(*) AS invalid_scope_owner FROM resources WHERE scope IS NULL OR NOT ((scope = 'company' AND CHAR_LENGTH(scope) = 7 AND companyId IS NOT NULL) OR (scope = 'global' AND CHAR_LENGTH(scope) = 6 AND companyId IS NULL)); WITH identity_values AS ( SELECT r.id, r.scope, r.active, MAX(CASE WHEN f.stockKey = 'field:provider' THEN NULLIF(TRIM(c.textValue), '') END) AS provider, MAX(CASE WHEN f.stockKey = 'field:os_family' THEN NULLIF(TRIM(c.textValue), '') END) AS family, MAX(CASE WHEN f.stockKey = 'field:os_product_version' THEN NULLIF(TRIM(c.textValue), '') END) AS product_version, MAX(CASE WHEN f.stockKey = 'field:os_edition_state' THEN c.textValue END) AS edition_state, MAX(CASE WHEN f.stockKey = 'field:software_edition' THEN NULLIF(TRIM(c.textValue), '') END) AS edition FROM resources r JOIN resource_definitions template ON template.id = r.templateId AND template.usage = 'os' LEFT JOIN resource_characteristics c ON c.resourceId = r.id LEFT JOIN resource_definitions f ON f.id = c.fieldId GROUP BY r.id, r.scope, r.active ) SELECT scope, active, COUNT(*) AS resources, SUM(provider IS NULL OR family IS NULL OR product_version IS NULL OR edition_state IS NULL OR edition_state NOT IN ('known', 'not_applicable') OR (edition_state = 'known' AND edition IS NULL) OR (edition_state = 'not_applicable' AND edition IS NOT NULL)) AS incomplete_or_inconsistent FROM identity_values GROUP BY scope, active; -- Un particular incompleto o un borrador desconocido no es por sí mismo un error. -- Esta consulta no implementa toda la policy semántica ni la revisión de duplicados. SELECT r.scope, r.active, s.state, COUNT(*) AS installations FROM software_installations s JOIN resources r ON r.id = s.resourceId JOIN resource_definitions template ON template.id = r.templateId AND template.usage = 'os' GROUP BY r.scope, r.active, s.state; SELECT r.scope, history.state, COUNT(*) AS revisions FROM software_installation_revisions history JOIN resources r ON r.id = history.resourceId JOIN resource_definitions template ON template.id = r.templateId AND template.usage = 'os' GROUP BY r.scope, history.state; SELECT installations_per_company, COUNT(*) AS companies FROM ( SELECT companyId, COUNT(*) AS installations_per_company FROM software_installations GROUP BY companyId ) volumes GROUP BY installations_per_company ORDER BY installations_per_company; SELECT COUNT(*) AS invalid_installation_references FROM software_installations s LEFT JOIN assets a ON a.id = s.assetId LEFT JOIN resources r ON r.id = s.resourceId LEFT JOIN resources license ON license.id = s.licenseResourceId WHERE a.id IS NULL OR a.companyId <> s.companyId OR r.id IS NULL OR (r.scope = 'company' AND r.companyId <> s.companyId) OR (s.licenseResourceId IS NOT NULL AND (license.id IS NULL OR license.scope <> 'company' OR license.companyId <> s.companyId)); SELECT COUNT(*) AS orphan_resource_revisions FROM resource_revisions h LEFT JOIN resources r ON r.id = h.resourceId WHERE r.id IS NULL; SELECT COUNT(*) AS orphan_installation_revisions FROM software_installation_revisions h LEFT JOIN software_installations s ON s.id = h.configurationId WHERE s.id IS NULL; SELECT COUNT(*) AS invalid_historical_references FROM software_installation_revisions h JOIN software_installations s ON s.id = h.configurationId LEFT JOIN assets a ON a.id = h.assetId LEFT JOIN resources r ON r.id = h.resourceId LEFT JOIN resources license ON license.id = h.licenseResourceId WHERE a.id IS NULL OR a.companyId <> s.companyId OR r.id IS NULL OR (r.scope = 'company' AND r.companyId <> s.companyId) OR (h.licenseResourceId IS NOT NULL AND (license.id IS NULL OR license.scope <> 'company' OR license.companyId <> s.companyId));