96 lines
3.1 KiB
PL/PgSQL
96 lines
3.1 KiB
PL/PgSQL
-- Migration: 007_versioning_schema.sql
|
|||
|
|
-- Phase 7.1: Entity & Edge Versioning
|
||
|
|
-- Tracks full snapshots on every mutation for rollback capability
|
||
|
|
|
||
|
|
BEGIN;
|
||
|
|
|
||
|
|
-- Entity version snapshots (immutable)
|
||
|
|
CREATE TABLE IF NOT EXISTS memory_entity_version (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
entity_id VARCHAR(255) NOT NULL,
|
||
|
|
version_num INTEGER NOT NULL,
|
||
|
|
operation VARCHAR(20) NOT NULL CHECK (operation IN ('create', 'update', 'delete')),
|
||
|
|
|
||
|
|
-- Full snapshot JSONB
|
||
|
|
snapshot JSONB NOT NULL,
|
||
|
|
|
||
|
|
-- Audit metadata
|
||
|
|
changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
changed_by VARCHAR(255) NOT NULL, -- JWT sub claim
|
||
|
|
fields_changed TEXT[] DEFAULT '{}',
|
||
|
|
|
||
|
|
-- Temporal
|
||
|
|
t_created TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
|
||
|
|
-- Constraints
|
||
|
|
UNIQUE(entity_id, version_num),
|
||
|
|
CONSTRAINT valid_version_num CHECK (version_num > 0)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Edge version snapshots
|
||
|
|
CREATE TABLE IF NOT EXISTS memory_edge_version (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
edge_id UUID NOT NULL,
|
||
|
|
version_num INTEGER NOT NULL,
|
||
|
|
operation VARCHAR(20) NOT NULL CHECK (operation IN ('create', 'update', 'delete')),
|
||
|
|
|
||
|
|
-- Full snapshot JSONB
|
||
|
|
snapshot JSONB NOT NULL,
|
||
|
|
|
||
|
|
-- Audit metadata
|
||
|
|
changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
changed_by VARCHAR(255) NOT NULL,
|
||
|
|
fields_changed TEXT[] DEFAULT '{}',
|
||
|
|
|
||
|
|
-- Temporal
|
||
|
|
t_created TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
|
||
|
|
-- Constraints
|
||
|
|
UNIQUE(edge_id, version_num),
|
||
|
|
CONSTRAINT valid_version_num CHECK (version_num > 0)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Indexes for efficient lookups
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_entity_version_entity_id
|
||
|
|
ON memory_entity_version(entity_id, version_num DESC);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_entity_version_changed_at
|
||
|
|
ON memory_entity_version(changed_at DESC);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_entity_version_changed_by
|
||
|
|
ON memory_entity_version(changed_by);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_version_edge_id
|
||
|
|
ON memory_edge_version(edge_id, version_num DESC);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_version_changed_at
|
||
|
|
ON memory_edge_version(changed_at DESC);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_edge_version_changed_by
|
||
|
|
ON memory_edge_version(changed_by);
|
||
|
|
|
||
|
|
-- Immutability enforcement: version tables are append-only
|
||
|
|
CREATE OR REPLACE FUNCTION prevent_version_table_modification()
|
||
|
|
RETURNS TRIGGER AS $$
|
||
|
|
BEGIN
|
||
|
|
RAISE EXCEPTION 'Version tables are immutable';
|
||
|
|
END;
|
||
|
|
$$ LANGUAGE plpgsql;
|
||
|
|
|
||
|
|
CREATE TRIGGER memory_entity_version_immutable
|
||
|
|
BEFORE UPDATE OR DELETE ON memory_entity_version
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION prevent_version_table_modification();
|
||
|
|
|
||
|
|
CREATE TRIGGER memory_edge_version_immutable
|
||
|
|
BEFORE UPDATE OR DELETE ON memory_edge_version
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION prevent_version_table_modification();
|
||
|
|
|
||
|
|
COMMIT;
|
||
|
|
|
||
|
|
-- Rollback (for reference):
|
||
|
|
-- DROP TRIGGER memory_entity_version_immutable ON memory_entity_version;
|
||
|
|
-- DROP TRIGGER memory_edge_version_immutable ON memory_edge_version;
|
||
|
|
-- DROP FUNCTION prevent_version_table_modification();
|
||
|
|
-- DROP TABLE memory_entity_version;
|
||
|
|
-- DROP TABLE memory_edge_version;
|