feat: add query observability and collation safeguards

This commit is contained in:
key
2026-08-04 02:15:03 +08:00
parent 721839c847
commit 12fc9fdddc
22 changed files with 575 additions and 22 deletions
@@ -0,0 +1,62 @@
-- Final query-result observability. Additive and idempotent on MySQL 8.
SET @aihr_query_log_exists := (
SELECT COUNT(*) FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log'
);
SET SESSION group_concat_max_len = 16384;
SET @aihr_query_result_ddl := 'SELECT 1';
SET @aihr_query_result_ddl := IF(@aihr_query_log_exists = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log' AND column_name = 'normalized_query_hash'), 'ALTER TABLE `aihr_knowledge_query_log` ADD COLUMN `normalized_query_hash` char(64) DEFAULT NULL COMMENT ''SHA-256 of normalized query''', @aihr_query_result_ddl);
PREPARE aihr_query_result_stmt FROM @aihr_query_result_ddl; EXECUTE aihr_query_result_stmt; DEALLOCATE PREPARE aihr_query_result_stmt;
SET @aihr_query_result_ddl := IF(@aihr_query_log_exists = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log' AND column_name = 'intent'), 'ALTER TABLE `aihr_knowledge_query_log` ADD COLUMN `intent` varchar(40) DEFAULT NULL', 'SELECT 1');
PREPARE aihr_query_result_stmt FROM @aihr_query_result_ddl; EXECUTE aihr_query_result_stmt; DEALLOCATE PREPARE aihr_query_result_stmt;
SET @aihr_query_result_ddl := IF(@aihr_query_log_exists = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log' AND column_name = 'domain'), 'ALTER TABLE `aihr_knowledge_query_log` ADD COLUMN `domain` varchar(80) DEFAULT NULL', 'SELECT 1');
PREPARE aihr_query_result_stmt FROM @aihr_query_result_ddl; EXECUTE aihr_query_result_stmt; DEALLOCATE PREPARE aihr_query_result_stmt;
SET @aihr_query_result_ddl := IF(@aihr_query_log_exists = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log' AND column_name = 'needs_clarification'), 'ALTER TABLE `aihr_knowledge_query_log` ADD COLUMN `needs_clarification` tinyint(1) NOT NULL DEFAULT 0', 'SELECT 1');
PREPARE aihr_query_result_stmt FROM @aihr_query_result_ddl; EXECUTE aihr_query_result_stmt; DEALLOCATE PREPARE aihr_query_result_stmt;
SET @aihr_query_result_ddl := IF(@aihr_query_log_exists = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log' AND column_name = 'outcome'), 'ALTER TABLE `aihr_knowledge_query_log` ADD COLUMN `outcome` varchar(40) DEFAULT NULL COMMENT ''ANSWERED/NEEDS_CLARIFICATION/NO_EVIDENCE/REJECTED/FAILED/DATA_TOOL/MEDIA''', 'SELECT 1');
PREPARE aihr_query_result_stmt FROM @aihr_query_result_ddl; EXECUTE aihr_query_result_stmt; DEALLOCATE PREPARE aihr_query_result_stmt;
SET @aihr_query_result_ddl := IF(@aihr_query_log_exists = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log' AND column_name = 'outcome_reason'), 'ALTER TABLE `aihr_knowledge_query_log` ADD COLUMN `outcome_reason` varchar(64) DEFAULT NULL', 'SELECT 1');
PREPARE aihr_query_result_stmt FROM @aihr_query_result_ddl; EXECUTE aihr_query_result_stmt; DEALLOCATE PREPARE aihr_query_result_stmt;
SET @aihr_query_result_ddl := IF(@aihr_query_log_exists = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log' AND column_name = 'candidate_count'), 'ALTER TABLE `aihr_knowledge_query_log` ADD COLUMN `candidate_count` int NOT NULL DEFAULT 0', 'SELECT 1');
PREPARE aihr_query_result_stmt FROM @aihr_query_result_ddl; EXECUTE aihr_query_result_stmt; DEALLOCATE PREPARE aihr_query_result_stmt;
SET @aihr_query_result_ddl := IF(@aihr_query_log_exists = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log' AND column_name = 'evidence_count'), 'ALTER TABLE `aihr_knowledge_query_log` ADD COLUMN `evidence_count` int NOT NULL DEFAULT 0', 'SELECT 1');
PREPARE aihr_query_result_stmt FROM @aihr_query_result_ddl; EXECUTE aihr_query_result_stmt; DEALLOCATE PREPARE aihr_query_result_stmt;
SET @aihr_query_result_ddl := IF(@aihr_query_log_exists = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log' AND column_name = 'citation_count'), 'ALTER TABLE `aihr_knowledge_query_log` ADD COLUMN `citation_count` int NOT NULL DEFAULT 0', 'SELECT 1');
PREPARE aihr_query_result_stmt FROM @aihr_query_result_ddl; EXECUTE aihr_query_result_stmt; DEALLOCATE PREPARE aihr_query_result_stmt;
SET @aihr_query_result_ddl := IF(@aihr_query_log_exists = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log' AND column_name = 'rerank_applied'), 'ALTER TABLE `aihr_knowledge_query_log` ADD COLUMN `rerank_applied` tinyint(1) NOT NULL DEFAULT 0', 'SELECT 1');
PREPARE aihr_query_result_stmt FROM @aihr_query_result_ddl; EXECUTE aihr_query_result_stmt; DEALLOCATE PREPARE aihr_query_result_stmt;
SET @aihr_query_result_ddl := IF(@aihr_query_log_exists = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log' AND column_name = 'answer_hash'), 'ALTER TABLE `aihr_knowledge_query_log` ADD COLUMN `answer_hash` char(64) DEFAULT NULL', 'SELECT 1');
PREPARE aihr_query_result_stmt FROM @aihr_query_result_ddl; EXECUTE aihr_query_result_stmt; DEALLOCATE PREPARE aihr_query_result_stmt;
SET @aihr_query_result_ddl := IF(@aihr_query_log_exists = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log' AND column_name = 'answer_length'), 'ALTER TABLE `aihr_knowledge_query_log` ADD COLUMN `answer_length` int NOT NULL DEFAULT 0', 'SELECT 1');
PREPARE aihr_query_result_stmt FROM @aihr_query_result_ddl; EXECUTE aihr_query_result_stmt; DEALLOCATE PREPARE aihr_query_result_stmt;
SET @aihr_query_result_ddl := IF(@aihr_query_log_exists = 1 AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log' AND column_name = 'update_time'), 'ALTER TABLE `aihr_knowledge_query_log` ADD COLUMN `update_time` datetime DEFAULT NULL', 'SELECT 1');
PREPARE aihr_query_result_stmt FROM @aihr_query_result_ddl; EXECUTE aihr_query_result_stmt; DEALLOCATE PREPARE aihr_query_result_stmt;
SET @aihr_query_outcome_index_exists := (
SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log'
AND index_name = 'idx_aihr_query_outcome'
);
SET @aihr_query_outcome_index_ddl := IF(
@aihr_query_log_exists = 1 AND @aihr_query_outcome_index_exists = 0,
'ALTER TABLE `aihr_knowledge_query_log` ADD KEY `idx_aihr_query_outcome` (`tenant_id`, `outcome`, `create_time`)',
'SELECT 1'
);
PREPARE aihr_query_outcome_index_stmt FROM @aihr_query_outcome_index_ddl;
EXECUTE aihr_query_outcome_index_stmt;
DEALLOCATE PREPARE aihr_query_outcome_index_stmt;
SET @aihr_query_domain_index_exists := (
SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'aihr_knowledge_query_log'
AND index_name = 'idx_aihr_query_domain'
);
SET @aihr_query_domain_index_ddl := IF(
@aihr_query_log_exists = 1 AND @aihr_query_domain_index_exists = 0,
'ALTER TABLE `aihr_knowledge_query_log` ADD KEY `idx_aihr_query_domain` (`tenant_id`, `domain`, `create_time`)',
'SELECT 1'
);
PREPARE aihr_query_domain_index_stmt FROM @aihr_query_domain_index_ddl;
EXECUTE aihr_query_domain_index_stmt;
DEALLOCATE PREPARE aihr_query_domain_index_stmt;
@@ -0,0 +1,48 @@
-- Normalize every AIHR table's textual columns to the MySQL 8 canonical
-- collation. This is additive, repeatable, and safe for partially upgraded
-- databases: only existing tables with a non-canonical character column are
-- rewritten.
DROP PROCEDURE IF EXISTS aihr_unify_all_collations;
DELIMITER //
CREATE PROCEDURE aihr_unify_all_collations()
BEGIN
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE current_table VARCHAR(128);
DECLARE table_cursor CURSOR FOR
SELECT DISTINCT c.table_name
FROM information_schema.columns c
WHERE c.table_schema = DATABASE()
AND c.table_name LIKE 'aihr\\_%' ESCAPE '\\'
AND c.character_set_name IS NOT NULL
AND (
c.character_set_name <> 'utf8mb4'
OR c.collation_name <> 'utf8mb4_0900_ai_ci'
)
ORDER BY c.table_name;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN table_cursor;
normalize_loop: LOOP
FETCH table_cursor INTO current_table;
IF done THEN
LEAVE normalize_loop;
END IF;
SET @aihr_collation_ddl := CONCAT(
'ALTER TABLE `', REPLACE(current_table, '`', '``'),
'` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci'
);
PREPARE aihr_collation_stmt FROM @aihr_collation_ddl;
EXECUTE aihr_collation_stmt;
DEALLOCATE PREPARE aihr_collation_stmt;
END LOOP;
CLOSE table_cursor;
SET @aihr_collation_ddl := NULL;
END//
DELIMITER ;
CALL aihr_unify_all_collations();
DROP PROCEDURE aihr_unify_all_collations;