fix(persistence): prevent assistant message loss from column truncation and JDBC charset

This commit is contained in:
matevip 2026-05-06 15:18:34 +08:00
parent 0665a6be09
commit da6fa5089d
3 changed files with 51 additions and 1 deletions

View File

@ -5,7 +5,12 @@ spring:
# 自动创建的库用了 server 默认字符集也不会影响数据。要求 DB user 具备 CREATE 权限(默认 root 可)。
# 如果使用受限账号,请提前手工执行:
# CREATE DATABASE mateclaw CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:mateclaw}?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
# characterEncoding=utf8mb4 + connectionCollation=utf8mb4_unicode_ci force
# the JDBC connection to negotiate 4-byte UTF-8 end-to-end. With 3-byte
# `utf8` the driver rejects emoji and CJK supplementary-plane chars even
# when the table charset is utf8mb4, surfacing as
# `Data truncation: Incorrect string value` on assistant message INSERT.
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:mateclaw}?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8mb4&connectionCollation=utf8mb4_unicode_ci&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: ${DB_USERNAME:root}
password: ${DB_PASSWORD:mateclaw123}

View File

@ -0,0 +1,7 @@
-- V91: Mirror MySQL widening of mate_message.content / content_parts and
-- mate_skill.skill_content. H2's TEXT is already CLOB (effectively unbounded)
-- so the change is a no-op semantically; it keeps both dialects in sync.
ALTER TABLE mate_message ALTER COLUMN content CLOB;
ALTER TABLE mate_message ALTER COLUMN content_parts CLOB;
ALTER TABLE mate_skill ALTER COLUMN skill_content CLOB;

View File

@ -0,0 +1,38 @@
-- V91: Widen mate_message.content / content_parts and mate_skill.skill_content
-- from TEXT (64KB) to MEDIUMTEXT (16MB).
--
-- TEXT caps at 65,535 bytes. A multi-turn ReAct session accumulates tool calls
-- and observations into content_parts JSON well past that cap, and a long
-- Chinese final answer (~22k chars × 3 bytes UTF-8) overflows `content`.
-- The truncation rejects the assistant message INSERT after the SSE stream
-- has already finished, so users see the reply live but it disappears on
-- page reload (only the user message survives in the DB).
--
-- Idempotent: only modifies the column when its current type is still TEXT.
SET @c := (SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'mate_message'
AND COLUMN_NAME = 'content');
SET @s := IF(@c = 'text',
'ALTER TABLE mate_message MODIFY COLUMN content MEDIUMTEXT',
'SELECT 1');
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @c := (SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'mate_message'
AND COLUMN_NAME = 'content_parts');
SET @s := IF(@c = 'text',
'ALTER TABLE mate_message MODIFY COLUMN content_parts MEDIUMTEXT',
'SELECT 1');
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @c := (SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'mate_skill'
AND COLUMN_NAME = 'skill_content');
SET @s := IF(@c = 'text',
'ALTER TABLE mate_skill MODIFY COLUMN skill_content MEDIUMTEXT',
'SELECT 1');
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;