feat: complete governed knowledge lifecycle

This commit is contained in:
key
2026-08-03 14:34:54 +08:00
parent a275f98fad
commit df77998e0c
41 changed files with 4921 additions and 225 deletions
@@ -0,0 +1,66 @@
-- Space-scoped compatibility rollout for governed knowledge.
-- Additive and idempotent: absence of a scope row means LEGACY serving.
CREATE TABLE IF NOT EXISTS `aihr_knowledge_rollout_scope` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`knowledge_id` bigint NOT NULL,
`mode` varchar(20) NOT NULL DEFAULT 'LEGACY',
`governed_collection` varchar(100) NOT NULL DEFAULT 'aihr_knowledge_governed_v1',
`active_generation` bigint NOT NULL DEFAULT 1,
`candidate_generation` bigint DEFAULT NULL,
`updated_by` bigint DEFAULT NULL,
`reason` varchar(1000) DEFAULT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_knowledge_rollout_scope` (`tenant_id`, `knowledge_id`),
KEY `idx_aihr_knowledge_rollout_mode` (`tenant_id`, `mode`, `knowledge_id`),
CONSTRAINT `ck_aihr_knowledge_rollout_mode` CHECK (`mode` IN ('LEGACY','SHADOW','ENFORCED')),
CONSTRAINT `ck_aihr_knowledge_rollout_generation` CHECK (`active_generation` > 0),
CONSTRAINT `ck_aihr_knowledge_rollout_candidate_generation` CHECK
(`candidate_generation` IS NULL OR `candidate_generation` > `active_generation`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Per-space governed knowledge serving mode and active vector generation';
CREATE TABLE IF NOT EXISTS `aihr_knowledge_rollout_transition` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`knowledge_id` bigint NOT NULL,
`from_mode` varchar(20) NOT NULL,
`to_mode` varchar(20) NOT NULL,
`active_generation` bigint NOT NULL,
`reviewer_id` bigint NOT NULL,
`reason` varchar(1000) NOT NULL,
`readiness_json` json DEFAULT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_aihr_knowledge_rollout_transition` (`tenant_id`, `knowledge_id`, `id`),
CONSTRAINT `ck_aihr_knowledge_rollout_transition_from` CHECK (`from_mode` IN ('LEGACY','SHADOW','ENFORCED')),
CONSTRAINT `ck_aihr_knowledge_rollout_transition_to` CHECK (`to_mode` IN ('LEGACY','SHADOW','ENFORCED'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Immutable human-authorized knowledge rollout transitions';
-- Match the canonical tenant collation on upgraded databases.
SET @aihr_rollout_collation := (
SELECT collation_name FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_info' AND column_name = 'tenant_id'
LIMIT 1
);
SET @aihr_rollout_collation := COALESCE(@aihr_rollout_collation, 'utf8mb4_0900_ai_ci');
SET @aihr_rollout_ddl := CONCAT(
'ALTER TABLE `aihr_knowledge_rollout_scope` CONVERT TO CHARACTER SET utf8mb4 COLLATE ',
@aihr_rollout_collation
);
PREPARE aihr_rollout_stmt FROM @aihr_rollout_ddl;
EXECUTE aihr_rollout_stmt;
DEALLOCATE PREPARE aihr_rollout_stmt;
SET @aihr_rollout_ddl := CONCAT(
'ALTER TABLE `aihr_knowledge_rollout_transition` CONVERT TO CHARACTER SET utf8mb4 COLLATE ',
@aihr_rollout_collation
);
PREPARE aihr_rollout_stmt FROM @aihr_rollout_ddl;
EXECUTE aihr_rollout_stmt;
DEALLOCATE PREPARE aihr_rollout_stmt;
SET @aihr_rollout_ddl := NULL;
SET @aihr_rollout_collation := NULL;
@@ -0,0 +1,136 @@
-- Candidate/effective version pointers for governed knowledge.
-- Additive and idempotent. Existing current_version_id remains as a compatibility alias
-- until every read path has moved to published_version_id/candidate_version_id.
SET @aihr_column_exists := (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_data_asset'
AND column_name = 'published_version_id'
);
SET @aihr_ddl := IF(@aihr_column_exists = 0,
'ALTER TABLE `aihr_data_asset` ADD COLUMN `published_version_id` bigint DEFAULT NULL AFTER `current_version_id`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_column_exists := (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_data_asset'
AND column_name = 'candidate_version_id'
);
SET @aihr_ddl := IF(@aihr_column_exists = 0,
'ALTER TABLE `aihr_data_asset` ADD COLUMN `candidate_version_id` bigint DEFAULT NULL AFTER `published_version_id`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_column_exists := (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_data_asset'
AND column_name = 'published_time'
);
SET @aihr_ddl := IF(@aihr_column_exists = 0,
'ALTER TABLE `aihr_data_asset` ADD COLUMN `published_time` datetime DEFAULT NULL AFTER `candidate_version_id`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_column_exists := (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_data_version'
AND column_name = 'version_status'
);
SET @aihr_ddl := IF(@aihr_column_exists = 0,
'ALTER TABLE `aihr_data_version` ADD COLUMN `version_status` varchar(20) NOT NULL DEFAULT ''DRAFT'' AFTER `version_no`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_column_exists := (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_data_version'
AND column_name = 'revision_of_version_id'
);
SET @aihr_ddl := IF(@aihr_column_exists = 0,
'ALTER TABLE `aihr_data_version` ADD COLUMN `revision_of_version_id` bigint DEFAULT NULL AFTER `version_status`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_column_exists := (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_data_version'
AND column_name = 'revision_reason'
);
SET @aihr_ddl := IF(@aihr_column_exists = 0,
'ALTER TABLE `aihr_data_version` ADD COLUMN `revision_reason` varchar(500) DEFAULT NULL AFTER `revision_of_version_id`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_index_exists := (
SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'aihr_data_asset'
AND index_name = 'idx_aihr_data_asset_published_version'
);
SET @aihr_ddl := IF(@aihr_index_exists = 0,
'ALTER TABLE `aihr_data_asset` ADD KEY `idx_aihr_data_asset_published_version` (`tenant_id`, `published_version_id`)',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_index_exists := (
SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'aihr_data_asset'
AND index_name = 'idx_aihr_data_asset_candidate_version'
);
SET @aihr_ddl := IF(@aihr_index_exists = 0,
'ALTER TABLE `aihr_data_asset` ADD KEY `idx_aihr_data_asset_candidate_version` (`tenant_id`, `candidate_version_id`)',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_index_exists := (
SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'aihr_data_version'
AND index_name = 'idx_aihr_data_version_status'
);
SET @aihr_ddl := IF(@aihr_index_exists = 0,
'ALTER TABLE `aihr_data_version` ADD KEY `idx_aihr_data_version_status` (`tenant_id`, `version_status`, `id`)',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_check_exists := (
SELECT COUNT(*) FROM information_schema.table_constraints
WHERE table_schema = DATABASE() AND table_name = 'aihr_data_version'
AND constraint_name = 'ck_aihr_data_version_status' AND constraint_type = 'CHECK'
);
SET @aihr_ddl := IF(@aihr_check_exists > 0,
'ALTER TABLE `aihr_data_version` DROP CHECK `ck_aihr_data_version_status`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
ALTER TABLE `aihr_data_version`
ADD CONSTRAINT `ck_aihr_data_version_status`
CHECK (`version_status` IN ('DRAFT','PROCESSING','REVIEW_PENDING','QUARANTINED','APPROVED',
'PUBLISHED','SUPERSEDED','WITHDRAWN','FAILED'));
-- Backfill pointers without changing content or existing serving behavior.
UPDATE `aihr_data_asset`
SET published_version_id = CASE
WHEN published_version_id IS NULL AND lifecycle_status = 'PUBLISHED'
THEN current_version_id ELSE published_version_id END,
candidate_version_id = CASE
WHEN candidate_version_id IS NULL AND lifecycle_status IN ('REVIEW_PENDING','QUARANTINED','APPROVED')
THEN current_version_id ELSE candidate_version_id END,
published_time = CASE
WHEN published_time IS NULL AND lifecycle_status = 'PUBLISHED' THEN update_time
ELSE published_time END
WHERE current_version_id IS NOT NULL;
UPDATE `aihr_data_version` v
JOIN `aihr_data_asset` a ON a.tenant_id = v.tenant_id AND a.current_version_id = v.id
SET v.version_status = CASE a.lifecycle_status
WHEN 'PUBLISHED' THEN 'PUBLISHED'
WHEN 'DEPRECATED' THEN 'WITHDRAWN'
WHEN 'QUARANTINED' THEN 'QUARANTINED'
WHEN 'APPROVED' THEN 'APPROVED'
WHEN 'REVIEW_PENDING' THEN 'REVIEW_PENDING'
ELSE v.version_status END
WHERE v.version_status = 'DRAFT';
SET @aihr_column_exists := NULL;
SET @aihr_index_exists := NULL;
SET @aihr_check_exists := NULL;
SET @aihr_ddl := NULL;
@@ -0,0 +1,84 @@
-- Traceable review assistance and human-authorized low-risk batch review.
-- Suggestions never change asset, version, dataset or index state by themselves.
CREATE TABLE IF NOT EXISTS `aihr_review_assistance` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`asset_id` bigint NOT NULL,
`version_id` bigint NOT NULL,
`policy_version` varchar(50) NOT NULL,
`risk_level` varchar(20) NOT NULL,
`summary_json` json NOT NULL,
`suggestion_json` json NOT NULL,
`status` varchar(20) NOT NULL DEFAULT 'GENERATED',
`generated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`consumed_by` bigint DEFAULT NULL,
`consumed_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_review_assistance_version` (`tenant_id`, `version_id`, `policy_version`),
KEY `idx_aihr_review_assistance_queue` (`tenant_id`, `risk_level`, `status`, `generated_time`),
CONSTRAINT `ck_aihr_review_assistance_risk`
CHECK (`risk_level` IN ('LOW','MEDIUM','HIGH','CRITICAL')),
CONSTRAINT `ck_aihr_review_assistance_status`
CHECK (`status` IN ('GENERATED','CONSUMED','STALE'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Versioned deterministic review package and risk suggestion';
CREATE TABLE IF NOT EXISTS `aihr_review_batch` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`batch_key` varchar(100) NOT NULL,
`reason` varchar(1000) NOT NULL,
`requested_count` int NOT NULL,
`succeeded_count` int NOT NULL DEFAULT 0,
`failed_count` int NOT NULL DEFAULT 0,
`status` varchar(20) NOT NULL DEFAULT 'PROCESSING',
`reviewer_id` bigint NOT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`finish_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_review_batch_request` (`tenant_id`, `reviewer_id`, `batch_key`),
KEY `idx_aihr_review_batch_status` (`tenant_id`, `status`, `id`),
CONSTRAINT `ck_aihr_review_batch_status`
CHECK (`status` IN ('PROCESSING','SUCCEEDED','PARTIAL','FAILED'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Idempotent human-triggered low-risk review batch';
CREATE TABLE IF NOT EXISTS `aihr_review_batch_item` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`batch_id` bigint NOT NULL,
`asset_id` bigint NOT NULL,
`version_id` bigint NOT NULL,
`risk_level` varchar(20) NOT NULL,
`decision` varchar(20) NOT NULL DEFAULT 'PUBLISH',
`status` varchar(20) NOT NULL,
`review_id` bigint DEFAULT NULL,
`error_code` varchar(100) DEFAULT NULL,
`error_message` varchar(1000) DEFAULT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_review_batch_item` (`tenant_id`, `batch_id`, `asset_id`, `version_id`),
KEY `idx_aihr_review_batch_item_asset` (`tenant_id`, `asset_id`, `version_id`),
CONSTRAINT `ck_aihr_review_batch_item_status`
CHECK (`status` IN ('SUCCEEDED','FAILED','SKIPPED'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Per-item result and evidence for a human review batch';
SET @aihr_review_collation := (
SELECT collation_name FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_info' AND column_name = 'tenant_id'
LIMIT 1
);
SET @aihr_review_collation := COALESCE(@aihr_review_collation, 'utf8mb4_0900_ai_ci');
SET @aihr_review_ddl := CONCAT('ALTER TABLE `aihr_review_assistance` CONVERT TO CHARACTER SET utf8mb4 COLLATE ',
@aihr_review_collation);
PREPARE aihr_review_stmt FROM @aihr_review_ddl; EXECUTE aihr_review_stmt; DEALLOCATE PREPARE aihr_review_stmt;
SET @aihr_review_ddl := CONCAT('ALTER TABLE `aihr_review_batch` CONVERT TO CHARACTER SET utf8mb4 COLLATE ',
@aihr_review_collation);
PREPARE aihr_review_stmt FROM @aihr_review_ddl; EXECUTE aihr_review_stmt; DEALLOCATE PREPARE aihr_review_stmt;
SET @aihr_review_ddl := CONCAT('ALTER TABLE `aihr_review_batch_item` CONVERT TO CHARACTER SET utf8mb4 COLLATE ',
@aihr_review_collation);
PREPARE aihr_review_stmt FROM @aihr_review_ddl; EXECUTE aihr_review_stmt; DEALLOCATE PREPARE aihr_review_stmt;
SET @aihr_review_collation := NULL;
SET @aihr_review_ddl := NULL;
@@ -0,0 +1,153 @@
-- Version diff, rollback audit and vector-generation lineage.
-- Additive and idempotent. Historical chunks and vector generations remain recoverable.
SET @aihr_column_exists := (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_chunk_revision'
AND column_name = 'index_generation'
);
SET @aihr_ddl := IF(@aihr_column_exists = 0,
'ALTER TABLE `aihr_chunk_revision` ADD COLUMN `index_generation` bigint DEFAULT NULL AFTER `published_fragment_id`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_check_exists := (
SELECT COUNT(*) FROM information_schema.table_constraints
WHERE table_schema = DATABASE() AND table_name = 'aihr_review_decision'
AND constraint_name = 'ck_aihr_review_decision' AND constraint_type = 'CHECK'
);
SET @aihr_ddl := IF(@aihr_check_exists > 0,
'ALTER TABLE `aihr_review_decision` DROP CHECK `ck_aihr_review_decision`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
ALTER TABLE `aihr_review_decision`
ADD CONSTRAINT `ck_aihr_review_decision`
CHECK (`decision` IN ('APPROVE','REJECT','QUARANTINE','WITHDRAW','DEPRECATE','ROLLBACK'));
SET @aihr_column_exists := (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_chunk_revision'
AND column_name = 'vector_point_id'
);
SET @aihr_ddl := IF(@aihr_column_exists = 0,
'ALTER TABLE `aihr_chunk_revision` ADD COLUMN `vector_point_id` varchar(100) DEFAULT NULL AFTER `index_generation`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_index_exists := (
SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'aihr_chunk_revision'
AND index_name = 'idx_aihr_chunk_revision_generation'
);
SET @aihr_ddl := IF(@aihr_index_exists = 0,
'ALTER TABLE `aihr_chunk_revision` ADD KEY `idx_aihr_chunk_revision_generation` (`tenant_id`, `asset_id`, `version_id`, `index_generation`)',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_column_exists := (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_index_outbox'
AND column_name = 'index_generation'
);
SET @aihr_ddl := IF(@aihr_column_exists = 0,
'ALTER TABLE `aihr_index_outbox` ADD COLUMN `index_generation` bigint NOT NULL DEFAULT 1 AFTER `target_collection`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
CREATE TABLE IF NOT EXISTS `aihr_knowledge_generation` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`knowledge_id` bigint NOT NULL,
`generation` bigint NOT NULL,
`collection_name` varchar(100) NOT NULL,
`base_generation` bigint DEFAULT NULL,
`status` varchar(20) NOT NULL DEFAULT 'BUILDING',
`version_count` int NOT NULL DEFAULT 0,
`point_count` int NOT NULL DEFAULT 0,
`manifest_sha256` char(64) DEFAULT NULL,
`created_by` bigint DEFAULT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`ready_time` datetime DEFAULT NULL,
`activated_by` bigint DEFAULT NULL,
`activated_time` datetime DEFAULT NULL,
`retire_after` datetime DEFAULT NULL,
`last_error` varchar(1000) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_knowledge_generation` (`tenant_id`, `knowledge_id`, `generation`),
KEY `idx_aihr_knowledge_generation_status` (`tenant_id`, `status`, `knowledge_id`, `generation`),
CONSTRAINT `ck_aihr_knowledge_generation_status`
CHECK (`status` IN ('BUILDING','READY','ACTIVE','RETIRED','FAILED'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Immutable per-space vector generation manifest';
CREATE TABLE IF NOT EXISTS `aihr_generation_version` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`knowledge_id` bigint NOT NULL,
`generation` bigint NOT NULL,
`asset_id` bigint NOT NULL,
`version_id` bigint NOT NULL,
`content_sha256` char(64) NOT NULL,
`point_count` int NOT NULL DEFAULT 0,
`status` varchar(20) NOT NULL DEFAULT 'INCLUDED',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_generation_version` (`tenant_id`, `knowledge_id`, `generation`, `asset_id`),
KEY `idx_aihr_generation_version_lookup` (`tenant_id`, `asset_id`, `version_id`, `generation`),
CONSTRAINT `ck_aihr_generation_version_status` CHECK (`status` IN ('INCLUDED','EXCLUDED','FAILED'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Version membership and point count in a vector generation';
CREATE TABLE IF NOT EXISTS `aihr_version_diff` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`asset_id` bigint NOT NULL,
`from_version_id` bigint NOT NULL,
`to_version_id` bigint NOT NULL,
`policy_version` varchar(50) NOT NULL,
`summary_json` json NOT NULL,
`chunk_diff_json` json NOT NULL,
`created_by` bigint DEFAULT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_version_diff` (`tenant_id`, `from_version_id`, `to_version_id`, `policy_version`),
KEY `idx_aihr_version_diff_asset` (`tenant_id`, `asset_id`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Deterministic content and chunk-level version comparison';
CREATE TABLE IF NOT EXISTS `aihr_version_rollback` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`asset_id` bigint NOT NULL,
`from_version_id` bigint NOT NULL,
`to_version_id` bigint NOT NULL,
`from_generation` bigint NOT NULL,
`to_generation` bigint NOT NULL,
`reason` varchar(1000) NOT NULL,
`reviewer_id` bigint NOT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_aihr_version_rollback_asset` (`tenant_id`, `asset_id`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Human-authorized atomic effective-version rollback';
SET @aihr_generation_collation := (
SELECT collation_name FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_info' AND column_name = 'tenant_id'
LIMIT 1
);
SET @aihr_generation_collation := COALESCE(@aihr_generation_collation, 'utf8mb4_0900_ai_ci');
SET @aihr_generation_ddl := CONCAT('ALTER TABLE `aihr_knowledge_generation` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_generation_collation);
PREPARE aihr_generation_stmt FROM @aihr_generation_ddl; EXECUTE aihr_generation_stmt; DEALLOCATE PREPARE aihr_generation_stmt;
SET @aihr_generation_ddl := CONCAT('ALTER TABLE `aihr_generation_version` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_generation_collation);
PREPARE aihr_generation_stmt FROM @aihr_generation_ddl; EXECUTE aihr_generation_stmt; DEALLOCATE PREPARE aihr_generation_stmt;
SET @aihr_generation_ddl := CONCAT('ALTER TABLE `aihr_version_diff` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_generation_collation);
PREPARE aihr_generation_stmt FROM @aihr_generation_ddl; EXECUTE aihr_generation_stmt; DEALLOCATE PREPARE aihr_generation_stmt;
SET @aihr_generation_ddl := CONCAT('ALTER TABLE `aihr_version_rollback` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_generation_collation);
PREPARE aihr_generation_stmt FROM @aihr_generation_ddl; EXECUTE aihr_generation_stmt; DEALLOCATE PREPARE aihr_generation_stmt;
SET @aihr_column_exists := NULL;
SET @aihr_index_exists := NULL;
SET @aihr_check_exists := NULL;
SET @aihr_ddl := NULL;
SET @aihr_generation_collation := NULL;
SET @aihr_generation_ddl := NULL;
@@ -0,0 +1,110 @@
-- Governed withdrawal, retention, dual-control purge and reconciliation evidence.
-- Additive and idempotent. No existing asset is deleted or hidden by this migration.
SET @aihr_column_exists := (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_data_asset' AND column_name = 'retention_class'
);
SET @aihr_ddl := IF(@aihr_column_exists = 0,
'ALTER TABLE `aihr_data_asset` ADD COLUMN `retention_class` varchar(20) NOT NULL DEFAULT ''STANDARD'' AFTER `data_class`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_column_exists := (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_data_asset' AND column_name = 'legal_hold'
);
SET @aihr_ddl := IF(@aihr_column_exists = 0,
'ALTER TABLE `aihr_data_asset` ADD COLUMN `legal_hold` tinyint NOT NULL DEFAULT 0 AFTER `retention_class`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
SET @aihr_column_exists := (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_data_asset' AND column_name = 'delete_state'
);
SET @aihr_ddl := IF(@aihr_column_exists = 0,
'ALTER TABLE `aihr_data_asset` ADD COLUMN `delete_state` varchar(20) NOT NULL DEFAULT ''ACTIVE'' AFTER `legal_hold`',
'SELECT 1');
PREPARE aihr_stmt FROM @aihr_ddl; EXECUTE aihr_stmt; DEALLOCATE PREPARE aihr_stmt;
CREATE TABLE IF NOT EXISTS `aihr_deletion_request` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`request_key` varchar(100) NOT NULL,
`asset_id` bigint NOT NULL,
`version_id` bigint DEFAULT NULL,
`request_type` varchar(20) NOT NULL DEFAULT 'PURGE',
`retention_class` varchar(20) NOT NULL,
`reason` varchar(1000) NOT NULL,
`impact_snapshot_json` json NOT NULL,
`status` varchar(20) NOT NULL DEFAULT 'PENDING',
`requester_id` bigint NOT NULL,
`requested_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`approver_id` bigint DEFAULT NULL,
`approval_reason` varchar(1000) DEFAULT NULL,
`approved_time` datetime DEFAULT NULL,
`execute_after` datetime NOT NULL,
`executed_by` bigint DEFAULT NULL,
`executed_time` datetime DEFAULT NULL,
`last_error` varchar(1000) DEFAULT NULL,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_deletion_request_key` (`tenant_id`, `requester_id`, `request_key`),
KEY `idx_aihr_deletion_request_status` (`tenant_id`, `status`, `execute_after`),
KEY `idx_aihr_deletion_request_asset` (`tenant_id`, `asset_id`, `id`),
CONSTRAINT `ck_aihr_deletion_request_status`
CHECK (`status` IN ('PENDING','APPROVED','REJECTED','EXECUTING','EXECUTED','FAILED','CANCELLED')),
CONSTRAINT `ck_aihr_deletion_request_type` CHECK (`request_type` IN ('PURGE'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Dual-control physical deletion request and durable tombstone';
CREATE TABLE IF NOT EXISTS `aihr_reconciliation_run` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`scope_type` varchar(20) NOT NULL,
`scope_id` bigint DEFAULT NULL,
`status` varchar(20) NOT NULL,
`mysql_assets` int NOT NULL DEFAULT 0,
`mysql_fragments` int NOT NULL DEFAULT 0,
`qdrant_points` bigint DEFAULT NULL,
`issue_count` int NOT NULL DEFAULT 0,
`summary_json` json NOT NULL,
`created_by` bigint DEFAULT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`finish_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_aihr_reconciliation_run` (`tenant_id`, `create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Cross-store knowledge reconciliation evidence';
CREATE TABLE IF NOT EXISTS `aihr_reconciliation_issue` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`run_id` bigint NOT NULL,
`asset_id` bigint DEFAULT NULL,
`issue_code` varchar(60) NOT NULL,
`severity` varchar(20) NOT NULL,
`evidence_json` json NOT NULL,
`status` varchar(20) NOT NULL DEFAULT 'OPEN',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_aihr_reconciliation_issue` (`tenant_id`, `run_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Actionable MySQL, OSS and vector lineage mismatch';
SET @aihr_retention_collation := (
SELECT collation_name FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_info' AND column_name = 'tenant_id' LIMIT 1
);
SET @aihr_retention_collation := COALESCE(@aihr_retention_collation, 'utf8mb4_0900_ai_ci');
SET @aihr_retention_ddl := CONCAT('ALTER TABLE `aihr_deletion_request` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_retention_collation);
PREPARE aihr_retention_stmt FROM @aihr_retention_ddl; EXECUTE aihr_retention_stmt; DEALLOCATE PREPARE aihr_retention_stmt;
SET @aihr_retention_ddl := CONCAT('ALTER TABLE `aihr_reconciliation_run` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_retention_collation);
PREPARE aihr_retention_stmt FROM @aihr_retention_ddl; EXECUTE aihr_retention_stmt; DEALLOCATE PREPARE aihr_retention_stmt;
SET @aihr_retention_ddl := CONCAT('ALTER TABLE `aihr_reconciliation_issue` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_retention_collation);
PREPARE aihr_retention_stmt FROM @aihr_retention_ddl; EXECUTE aihr_retention_stmt; DEALLOCATE PREPARE aihr_retention_stmt;
SET @aihr_column_exists := NULL;
SET @aihr_ddl := NULL;
SET @aihr_retention_collation := NULL;
SET @aihr_retention_ddl := NULL;
@@ -0,0 +1,108 @@
-- Resumable legacy migration manifests, batches and dead-letter evidence.
CREATE TABLE IF NOT EXISTS `aihr_migration_run` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`run_key` varchar(100) NOT NULL,
`knowledge_id` bigint DEFAULT NULL,
`risk_scope` varchar(20) NOT NULL DEFAULT 'ALL',
`dry_run` tinyint NOT NULL DEFAULT 1,
`batch_size` int NOT NULL,
`cursor_attachment_id` bigint NOT NULL DEFAULT 0,
`status` varchar(20) NOT NULL DEFAULT 'PLANNED',
`discovered_count` int NOT NULL DEFAULT 0,
`staged_count` int NOT NULL DEFAULT 0,
`quarantined_count` int NOT NULL DEFAULT 0,
`failed_count` int NOT NULL DEFAULT 0,
`manifest_sha256` char(64) NOT NULL,
`requested_by` bigint NOT NULL,
`verified_dry_run_id` bigint DEFAULT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`start_time` datetime DEFAULT NULL,
`finish_time` datetime DEFAULT NULL,
`last_error` varchar(1000) DEFAULT NULL,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_migration_run_key` (`tenant_id`, `requested_by`, `run_key`),
KEY `idx_aihr_migration_run_status` (`tenant_id`, `status`, `id`),
CONSTRAINT `ck_aihr_migration_run_status`
CHECK (`status` IN ('PLANNED','RUNNING','COMPLETED','COMPLETED_WITH_ERRORS','FAILED','CANCELLED'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Resumable governed migration manifest';
CREATE TABLE IF NOT EXISTS `aihr_migration_batch` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`run_id` bigint NOT NULL,
`batch_no` int NOT NULL,
`from_cursor` bigint NOT NULL,
`to_cursor` bigint NOT NULL,
`status` varchar(20) NOT NULL,
`discovered_count` int NOT NULL DEFAULT 0,
`staged_count` int NOT NULL DEFAULT 0,
`reparsed_count` int NOT NULL DEFAULT 0,
`quarantined_count` int NOT NULL DEFAULT 0,
`failed_count` int NOT NULL DEFAULT 0,
`result_json` json NOT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`finish_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_migration_batch` (`tenant_id`, `run_id`, `batch_no`),
KEY `idx_aihr_migration_batch_run` (`tenant_id`, `run_id`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Migration cursor checkpoint and batch evidence';
CREATE TABLE IF NOT EXISTS `aihr_migration_dead_letter` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`run_id` bigint NOT NULL,
`attachment_id` bigint NOT NULL,
`status` varchar(20) NOT NULL DEFAULT 'OPEN',
`attempt_count` int NOT NULL DEFAULT 1,
`last_error` varchar(1000) DEFAULT NULL,
`next_attempt_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`resolved_asset_id` bigint DEFAULT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_migration_dead_letter` (`tenant_id`, `run_id`, `attachment_id`),
KEY `idx_aihr_migration_dead_letter_retry` (`tenant_id`, `status`, `next_attempt_time`),
CONSTRAINT `ck_aihr_migration_dead_letter_status` CHECK (`status` IN ('OPEN','RETRYING','RESOLVED','ABANDONED'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Explicit migration retry and dead-letter queue';
SET @aihr_migration_column_exists := (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_migration_run'
AND column_name = 'verified_dry_run_id'
);
SET @aihr_migration_ddl := IF(@aihr_migration_column_exists = 0,
'ALTER TABLE `aihr_migration_run` ADD COLUMN `verified_dry_run_id` bigint DEFAULT NULL AFTER `requested_by`',
'SELECT 1');
PREPARE aihr_migration_stmt FROM @aihr_migration_ddl; EXECUTE aihr_migration_stmt; DEALLOCATE PREPARE aihr_migration_stmt;
SET @aihr_migration_index_exists := (
SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'aihr_migration_run'
AND index_name = 'idx_aihr_migration_verified_dry_run'
);
SET @aihr_migration_ddl := IF(@aihr_migration_index_exists = 0,
'ALTER TABLE `aihr_migration_run` ADD KEY `idx_aihr_migration_verified_dry_run` (`tenant_id`, `verified_dry_run_id`)',
'SELECT 1');
PREPARE aihr_migration_stmt FROM @aihr_migration_ddl; EXECUTE aihr_migration_stmt; DEALLOCATE PREPARE aihr_migration_stmt;
SET @aihr_migration_collation := (
SELECT collation_name FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_info' AND column_name = 'tenant_id' LIMIT 1
);
SET @aihr_migration_collation := COALESCE(@aihr_migration_collation, 'utf8mb4_0900_ai_ci');
SET @aihr_migration_ddl := CONCAT('ALTER TABLE `aihr_migration_run` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_migration_collation);
PREPARE aihr_migration_stmt FROM @aihr_migration_ddl; EXECUTE aihr_migration_stmt; DEALLOCATE PREPARE aihr_migration_stmt;
SET @aihr_migration_ddl := CONCAT('ALTER TABLE `aihr_migration_batch` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_migration_collation);
PREPARE aihr_migration_stmt FROM @aihr_migration_ddl; EXECUTE aihr_migration_stmt; DEALLOCATE PREPARE aihr_migration_stmt;
SET @aihr_migration_ddl := CONCAT('ALTER TABLE `aihr_migration_dead_letter` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_migration_collation);
PREPARE aihr_migration_stmt FROM @aihr_migration_ddl; EXECUTE aihr_migration_stmt; DEALLOCATE PREPARE aihr_migration_stmt;
SET @aihr_migration_collation := NULL;
SET @aihr_migration_ddl := NULL;
SET @aihr_migration_column_exists := NULL;
SET @aihr_migration_index_exists := NULL;
@@ -0,0 +1,80 @@
-- Shadow retrieval evidence and explicit generation activation gates.
CREATE TABLE IF NOT EXISTS `aihr_shadow_comparison` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`knowledge_id` bigint NOT NULL,
`generation` bigint NOT NULL,
`query_sha256` char(64) NOT NULL,
`query_text` varchar(1000) NOT NULL,
`legacy_result_json` json NOT NULL,
`governed_result_json` json NOT NULL,
`legacy_top1_fragment_id` bigint DEFAULT NULL,
`governed_top1_fragment_id` bigint DEFAULT NULL,
`top1_agreement` tinyint NOT NULL DEFAULT 0,
`overlap_at_5` decimal(8,6) NOT NULL DEFAULT 0,
`governed_empty` tinyint NOT NULL DEFAULT 0,
`latency_ms` int NOT NULL DEFAULT 0,
`source` varchar(30) NOT NULL DEFAULT 'MANUAL_SAMPLE',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_shadow_comparison` (`tenant_id`, `knowledge_id`, `generation`, `query_sha256`),
KEY `idx_aihr_shadow_comparison_metrics` (`tenant_id`, `knowledge_id`, `generation`, `create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Legacy versus governed retrieval comparison without serving impact';
CREATE TABLE IF NOT EXISTS `aihr_activation_gate` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`knowledge_id` bigint NOT NULL,
`candidate_generation` bigint NOT NULL,
`status` varchar(20) NOT NULL,
`sample_count` int NOT NULL,
`top1_agreement_rate` decimal(8,6) NOT NULL,
`overlap_at_5_avg` decimal(8,6) NOT NULL,
`governed_empty_rate` decimal(8,6) NOT NULL,
`blocking_asset_count` int NOT NULL,
`open_critical_issue_count` int NOT NULL,
`dead_letter_count` int NOT NULL,
`generation_status` varchar(20) NOT NULL,
`threshold_json` json NOT NULL,
`evidence_json` json NOT NULL,
`evaluated_by` bigint NOT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_aihr_activation_gate` (`tenant_id`, `knowledge_id`, `candidate_generation`, `id`),
CONSTRAINT `ck_aihr_activation_gate_status` CHECK (`status` IN ('PASSED','BLOCKED'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Immutable evidence for human generation activation';
CREATE TABLE IF NOT EXISTS `aihr_generation_activation` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`knowledge_id` bigint NOT NULL,
`from_generation` bigint NOT NULL,
`to_generation` bigint NOT NULL,
`gate_id` bigint NOT NULL,
`action` varchar(20) NOT NULL,
`reason` varchar(1000) NOT NULL,
`reviewer_id` bigint NOT NULL,
`rollback_deadline` datetime DEFAULT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_aihr_generation_activation` (`tenant_id`, `knowledge_id`, `id`),
CONSTRAINT `ck_aihr_generation_activation_action` CHECK (`action` IN ('ACTIVATE','ROLLBACK'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Human generation activation and rollback audit';
SET @aihr_shadow_collation := (
SELECT collation_name FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_info' AND column_name = 'tenant_id' LIMIT 1
);
SET @aihr_shadow_collation := COALESCE(@aihr_shadow_collation, 'utf8mb4_0900_ai_ci');
SET @aihr_shadow_ddl := CONCAT('ALTER TABLE `aihr_shadow_comparison` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_shadow_collation);
PREPARE aihr_shadow_stmt FROM @aihr_shadow_ddl; EXECUTE aihr_shadow_stmt; DEALLOCATE PREPARE aihr_shadow_stmt;
SET @aihr_shadow_ddl := CONCAT('ALTER TABLE `aihr_activation_gate` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_shadow_collation);
PREPARE aihr_shadow_stmt FROM @aihr_shadow_ddl; EXECUTE aihr_shadow_stmt; DEALLOCATE PREPARE aihr_shadow_stmt;
SET @aihr_shadow_ddl := CONCAT('ALTER TABLE `aihr_generation_activation` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_shadow_collation);
PREPARE aihr_shadow_stmt FROM @aihr_shadow_ddl; EXECUTE aihr_shadow_stmt; DEALLOCATE PREPARE aihr_shadow_stmt;
SET @aihr_shadow_collation := NULL;
SET @aihr_shadow_ddl := NULL;