Advertisement

← kuato

postgres/schema.sql

4.6 KB Download file
-- Claude Code Session Memory - PostgreSQL Schema
-- Run this to set up the database

-- Enable required extensions
CREATE EXTENSION IF NOT EXISTS pg_trgm;  -- For fuzzy text matching

-- Main sessions table
CREATE TABLE IF NOT EXISTS sessions (
    id VARCHAR(36) PRIMARY KEY,  -- Claude Code session UUID

    -- Timestamps
    started_at TIMESTAMP WITH TIME ZONE,
    ended_at TIMESTAMP WITH TIME ZONE,
    synced_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),

    -- Metadata
    git_branch VARCHAR(255),
    cwd TEXT,
    version VARCHAR(50),
    message_count INTEGER DEFAULT 0,

    -- Token usage (totals)
    input_tokens BIGINT DEFAULT 0,
    output_tokens BIGINT DEFAULT 0,
    cache_creation_tokens BIGINT DEFAULT 0,
    cache_read_tokens BIGINT DEFAULT 0,

    -- Extracted data (JSONB for flexibility)
    user_messages JSONB DEFAULT '[]'::jsonb,
    tools_used JSONB DEFAULT '[]'::jsonb,
    files_touched JSONB DEFAULT '[]'::jsonb,
    models_used JSONB DEFAULT '[]'::jsonb,
    model_tokens JSONB DEFAULT '{}'::jsonb,

    -- Full-text search
    search_text TEXT,
    search_vector TSVECTOR,

    -- File reference
    transcript_path TEXT,
    transcript_hash VARCHAR(32),  -- MD5 for change detection

    -- Optional classification (if you want to add LLM summaries later)
    summary TEXT,
    category VARCHAR(100),

    -- Timestamps
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Indexes for common queries
CREATE INDEX IF NOT EXISTS idx_sessions_ended_at ON sessions(ended_at DESC);
CREATE INDEX IF NOT EXISTS idx_sessions_started_at ON sessions(started_at DESC);
CREATE INDEX IF NOT EXISTS idx_sessions_category ON sessions(category);

-- Full-text search index (GIN for performance)
CREATE INDEX IF NOT EXISTS idx_sessions_search_vector ON sessions USING GIN(search_vector);

-- Trigram index for fuzzy matching on summary
CREATE INDEX IF NOT EXISTS idx_sessions_summary_trgm ON sessions USING GIN(summary gin_trgm_ops);

-- JSONB indexes for filtering
CREATE INDEX IF NOT EXISTS idx_sessions_tools_used ON sessions USING GIN(tools_used);
CREATE INDEX IF NOT EXISTS idx_sessions_files_touched ON sessions USING GIN(files_touched);

-- Function to update search vector
CREATE OR REPLACE FUNCTION update_session_search_vector()
RETURNS TRIGGER AS $$
BEGIN
    NEW.search_vector :=
        setweight(to_tsvector('english', COALESCE(NEW.summary, '')), 'A') ||
        setweight(to_tsvector('english', COALESCE(NEW.search_text, '')), 'B') ||
        setweight(to_tsvector('english', COALESCE(
            CASE
                WHEN jsonb_typeof(NEW.tools_used) = 'array' AND jsonb_array_length(NEW.tools_used) > 0
                THEN array_to_string(ARRAY(SELECT jsonb_array_elements_text(NEW.tools_used)), ' ')
                ELSE ''
            END, ''
        )), 'C') ||
        setweight(to_tsvector('english', COALESCE(
            CASE
                WHEN jsonb_typeof(NEW.files_touched) = 'array' AND jsonb_array_length(NEW.files_touched) > 0
                THEN regexp_replace(
                    array_to_string(ARRAY(SELECT jsonb_array_elements_text(NEW.files_touched)), ' '),
                    '[/\-_.]', ' ', 'g'
                )
                ELSE ''
            END, ''
        )), 'C') ||
        -- Also index the cwd for project-level searches
        setweight(to_tsvector('english', COALESCE(
            regexp_replace(NEW.cwd, '[/\-_.]', ' ', 'g'), ''
        )), 'D');
    NEW.updated_at := NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Trigger to auto-update search vector
DROP TRIGGER IF EXISTS trigger_update_session_search_vector ON sessions;
CREATE TRIGGER trigger_update_session_search_vector
    BEFORE INSERT OR UPDATE ON sessions
    FOR EACH ROW
    EXECUTE FUNCTION update_session_search_vector();

-- Helper view for common queries
CREATE OR REPLACE VIEW session_stats AS
SELECT
    COUNT(*) as total_sessions,
    SUM(input_tokens) as total_input_tokens,
    SUM(output_tokens) as total_output_tokens,
    SUM(cache_creation_tokens) as total_cache_creation_tokens,
    SUM(cache_read_tokens) as total_cache_read_tokens,
    SUM(message_count) as total_messages,
    MIN(started_at) as earliest_session,
    MAX(ended_at) as latest_session
FROM sessions;

-- View for recent sessions
CREATE OR REPLACE VIEW recent_sessions AS
SELECT
    id,
    started_at,
    ended_at,
    git_branch,
    message_count,
    input_tokens,
    output_tokens,
    tools_used,
    files_touched,
    user_messages,
    summary,
    category
FROM sessions
WHERE ended_at > NOW() - INTERVAL '7 days'
ORDER BY ended_at DESC;