muse-agent-example/数据库/迁移/V0025__旧库迁移来源与映射.sql
zizi cf0f4fb985 W20–W24:保存知识方法、迁移框架、审校修订与案例行为的集成成果
接续 88dd570,保存 W20–W24 已实现的共享接口、业务入口、迁移、工作台、测试与文档。
W20/W22/W23 保持 in_progress,W21/W24 保持 verified;此提交不宣称方法或规则正式启用、多轮返修、真实角色评测完成。

W25 新增实验、标定、逐调用交付与角色执行及其迁移/测试/索引留在实施工作树,原有私人和旧实现保留项不纳入。

验证:离线 571、前端 33 通过;PG 469 项通过、2 项浏览器未启用,2 项误带入的 W25 用例已移出本提交;最终任务与交付边界 37 项通过。make 检查、最终类型、84 项资源及 diff 检查通过。未重跑浏览器或 Pi 宿主,不以合成调用认证外部模型效果。
独立整体审查四维通过;证据保存在 R2-20260909/提交W20-W24。
2026-09-13 16:24:42 +08:00

71 lines
3.3 KiB
PL/PgSQL

-- 数据演进:隔离目标身份与迁移来源/回执;不代替任何业务域的实体或候选表。
CREATE TABLE public.muse_legacy_target (
singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton),
target_id uuid NOT NULL UNIQUE,
system_identifier text NOT NULL,
data_directory text NOT NULL,
host text NOT NULL,
port integer NOT NULL CHECK (port BETWEEN 1 AND 65535),
database_name text NOT NULL,
database_oid oid NOT NULL,
role_name text NOT NULL CHECK (role_name='muse_app'),
run_purpose public.muse_purpose NOT NULL CHECK (run_purpose='production'),
created_at timestamptz NOT NULL DEFAULT clock_timestamp()
);
-- 应用角色不能把任意已有库自行标为隔离目标。
REVOKE ALL ON public.muse_legacy_target FROM muse_app, muse_eval;
GRANT SELECT ON public.muse_legacy_target TO muse_app;
CREATE TABLE public.muse_legacy_import_record (
author_id text NOT NULL,
run_purpose public.muse_purpose NOT NULL CHECK (run_purpose='production'),
source_key text NOT NULL CHECK (length(source_key)>0),
source_hash text NOT NULL CHECK (source_hash ~ '^[0-9a-f]{64}$'),
mapping_hash text NOT NULL CHECK (mapping_hash ~ '^[0-9a-f]{64}$'),
original jsonb NOT NULL CHECK (jsonb_typeof(original)='object'),
command_id text NOT NULL,
route jsonb NOT NULL CHECK (jsonb_typeof(route)='object'),
frozen_request jsonb,
state text NOT NULL DEFAULT 'reserved'
CHECK (state IN ('reserved','mapped','historical','quarantined')),
reason_code text NOT NULL DEFAULT '',
targets jsonb NOT NULL DEFAULT '[]'::jsonb CHECK (jsonb_typeof(targets)='array'),
receipt jsonb,
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
updated_at timestamptz NOT NULL DEFAULT clock_timestamp(),
PRIMARY KEY (author_id, source_key),
UNIQUE (author_id, command_id),
CHECK (state <> 'mapped' OR
(frozen_request IS NOT NULL AND receipt IS NOT NULL AND jsonb_array_length(targets)>0)),
CHECK (state = 'mapped' OR (receipt IS NULL AND targets='[]'::jsonb))
);
REVOKE DELETE ON public.muse_legacy_import_record FROM muse_app;
CREATE FUNCTION public.muse_guard_legacy_import_record() RETURNS trigger
LANGUAGE plpgsql AS $$
BEGIN
IF (NEW.author_id, NEW.run_purpose, NEW.source_key, NEW.source_hash,
NEW.mapping_hash, NEW.original, NEW.command_id, NEW.route)
IS DISTINCT FROM
(OLD.author_id, OLD.run_purpose, OLD.source_key, OLD.source_hash,
OLD.mapping_hash, OLD.original, OLD.command_id, OLD.route) THEN
RAISE EXCEPTION '迁移来源、映射与命令身份不可改写';
END IF;
IF OLD.frozen_request IS NOT NULL AND NEW.frozen_request IS DISTINCT FROM OLD.frozen_request THEN
RAISE EXCEPTION '迁移目标请求已经冻结';
END IF;
IF OLD.state <> 'reserved' AND
(NEW.state, NEW.reason_code, NEW.targets, NEW.receipt, NEW.frozen_request)
IS DISTINCT FROM
(OLD.state, OLD.reason_code, OLD.targets, OLD.receipt, OLD.frozen_request) THEN
RAISE EXCEPTION '迁移终态及回执不可改写';
END IF;
NEW.updated_at := clock_timestamp();
RETURN NEW;
END;
$$;
CREATE TRIGGER muse_legacy_import_record_guard
BEFORE UPDATE ON public.muse_legacy_import_record
FOR EACH ROW EXECUTE FUNCTION public.muse_guard_legacy_import_record();