Skip to content

Commit ec90827

Browse files
Claudeclaude
authored andcommitted
fix: schema SQL is fully idempotent — safe to re-run for upgrades
Setup SQL now handles upgrades gracefully: - ALTER TABLE ADD COLUMN IF NOT EXISTS for all columns (runs before indexes so embedding columns exist before ivfflat index creation) - DROP POLICY IF EXISTS before CREATE POLICY (policies have no IF NOT EXISTS clause) - DROP TRIGGER IF EXISTS before CREATE TRIGGER - CREATE OR REPLACE FUNCTION for RPC functions (already idempotent) Users can re-run `gitmem-mcp activate` or apply setup SQL again after upgrading gitmem-mcp and any new columns/tables will be added without errors or data loss. Verified: schema applies cleanly on fresh DB and re-runs without error on existing DB with data. Resolves GIT-45 concern #6 (no schema migration path). Also resolves concern #4 (pgvector confirmed available on Supabase free tier). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 06a1533 commit ec90827

1 file changed

Lines changed: 80 additions & 49 deletions

File tree

schema/setup.sql

Lines changed: 80 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,20 @@ CREATE TABLE IF NOT EXISTS gitmem_learnings (
3838
updated_at TIMESTAMPTZ DEFAULT NOW()
3939
);
4040

41-
-- Index for faster vector search
41+
-- Migration: ensure all columns exist (idempotent for upgrades)
42+
ALTER TABLE gitmem_learnings ADD COLUMN IF NOT EXISTS embedding vector(1536);
43+
ALTER TABLE gitmem_learnings ADD COLUMN IF NOT EXISTS source_linear_issue TEXT;
44+
ALTER TABLE gitmem_learnings ADD COLUMN IF NOT EXISTS persona_name TEXT;
45+
ALTER TABLE gitmem_learnings ADD COLUMN IF NOT EXISTS why_this_matters TEXT;
46+
ALTER TABLE gitmem_learnings ADD COLUMN IF NOT EXISTS action_protocol TEXT;
47+
ALTER TABLE gitmem_learnings ADD COLUMN IF NOT EXISTS self_check_criteria TEXT;
48+
ALTER TABLE gitmem_learnings ADD COLUMN IF NOT EXISTS is_active BOOLEAN DEFAULT true;
49+
ALTER TABLE gitmem_learnings ADD COLUMN IF NOT EXISTS decay_multiplier FLOAT DEFAULT 1.0;
50+
ALTER TABLE gitmem_learnings ADD COLUMN IF NOT EXISTS repeat_mistake BOOLEAN DEFAULT false;
51+
ALTER TABLE gitmem_learnings ADD COLUMN IF NOT EXISTS related_scar_id UUID;
52+
ALTER TABLE gitmem_learnings ADD COLUMN IF NOT EXISTS repeat_mistake_details JSONB;
53+
54+
-- Indexes (created after migration ensures columns exist)
4255
CREATE INDEX IF NOT EXISTS idx_gitmem_learnings_embedding
4356
ON gitmem_learnings USING ivfflat (embedding vector_cosine_ops)
4457
WITH (lists = 10);
@@ -71,6 +84,14 @@ CREATE TABLE IF NOT EXISTS gitmem_sessions (
7184
updated_at TIMESTAMPTZ DEFAULT NOW()
7285
);
7386

87+
-- Migration: ensure all columns exist
88+
ALTER TABLE gitmem_sessions ADD COLUMN IF NOT EXISTS embedding vector(1536);
89+
ALTER TABLE gitmem_sessions ADD COLUMN IF NOT EXISTS linear_issue TEXT;
90+
ALTER TABLE gitmem_sessions ADD COLUMN IF NOT EXISTS recording_path TEXT;
91+
ALTER TABLE gitmem_sessions ADD COLUMN IF NOT EXISTS transcript_path TEXT;
92+
ALTER TABLE gitmem_sessions ADD COLUMN IF NOT EXISTS close_compliance JSONB;
93+
ALTER TABLE gitmem_sessions ADD COLUMN IF NOT EXISTS rapport_summary TEXT;
94+
7495
CREATE INDEX IF NOT EXISTS idx_gitmem_sessions_agent
7596
ON gitmem_sessions (agent);
7697

@@ -96,6 +117,12 @@ CREATE TABLE IF NOT EXISTS gitmem_decisions (
96117
created_at TIMESTAMPTZ DEFAULT NOW()
97118
);
98119

120+
-- Migration: ensure all columns exist
121+
ALTER TABLE gitmem_decisions ADD COLUMN IF NOT EXISTS embedding vector(1536);
122+
ALTER TABLE gitmem_decisions ADD COLUMN IF NOT EXISTS personas_involved TEXT[] DEFAULT '{}';
123+
ALTER TABLE gitmem_decisions ADD COLUMN IF NOT EXISTS docs_affected TEXT[] DEFAULT '{}';
124+
ALTER TABLE gitmem_decisions ADD COLUMN IF NOT EXISTS linear_issue TEXT;
125+
99126
CREATE INDEX IF NOT EXISTS idx_gitmem_decisions_session
100127
ON gitmem_decisions (session_id);
101128

@@ -125,6 +152,13 @@ CREATE INDEX IF NOT EXISTS idx_gitmem_scar_usage_scar
125152
CREATE INDEX IF NOT EXISTS idx_gitmem_scar_usage_session
126153
ON gitmem_scar_usage (session_id);
127154

155+
-- Migration: add columns for older installs
156+
ALTER TABLE gitmem_scar_usage ADD COLUMN IF NOT EXISTS issue_id TEXT;
157+
ALTER TABLE gitmem_scar_usage ADD COLUMN IF NOT EXISTS issue_identifier TEXT;
158+
ALTER TABLE gitmem_scar_usage ADD COLUMN IF NOT EXISTS acknowledged_at TIMESTAMPTZ;
159+
ALTER TABLE gitmem_scar_usage ADD COLUMN IF NOT EXISTS referenced BOOLEAN;
160+
ALTER TABLE gitmem_scar_usage ADD COLUMN IF NOT EXISTS variant_id UUID;
161+
128162
-- ============================================================================
129163
-- Threads table (cross-session work tracking)
130164
-- ============================================================================
@@ -397,54 +431,49 @@ ALTER TABLE gitmem_query_metrics ENABLE ROW LEVEL SECURITY;
397431
ALTER TABLE scar_enforcement_variants ENABLE ROW LEVEL SECURITY;
398432

399433
-- Service role has full access (used by the MCP server)
400-
CREATE POLICY "Service role full access" ON gitmem_learnings
401-
FOR ALL USING (auth.role() = 'service_role');
402-
403-
CREATE POLICY "Service role full access" ON gitmem_sessions
404-
FOR ALL USING (auth.role() = 'service_role');
405-
406-
CREATE POLICY "Service role full access" ON gitmem_decisions
407-
FOR ALL USING (auth.role() = 'service_role');
408-
409-
CREATE POLICY "Service role full access" ON gitmem_scar_usage
410-
FOR ALL USING (auth.role() = 'service_role');
411-
412-
CREATE POLICY "Service role full access" ON gitmem_threads
413-
FOR ALL USING (auth.role() = 'service_role');
414-
415-
CREATE POLICY "Service role full access" ON knowledge_triples
416-
FOR ALL USING (auth.role() = 'service_role');
417-
418-
CREATE POLICY "Service role full access" ON gitmem_query_metrics
419-
FOR ALL USING (auth.role() = 'service_role');
420-
421-
CREATE POLICY "Service role full access" ON scar_enforcement_variants
422-
FOR ALL USING (auth.role() = 'service_role');
423-
424-
-- Block anonymous access
425-
CREATE POLICY "Block anonymous access" ON gitmem_learnings
426-
FOR ALL USING (auth.role() != 'anon');
427-
428-
CREATE POLICY "Block anonymous access" ON gitmem_sessions
429-
FOR ALL USING (auth.role() != 'anon');
430-
431-
CREATE POLICY "Block anonymous access" ON gitmem_decisions
432-
FOR ALL USING (auth.role() != 'anon');
433-
434-
CREATE POLICY "Block anonymous access" ON gitmem_scar_usage
435-
FOR ALL USING (auth.role() != 'anon');
436-
437-
CREATE POLICY "Block anonymous access" ON gitmem_threads
438-
FOR ALL USING (auth.role() != 'anon');
439-
440-
CREATE POLICY "Block anonymous access" ON knowledge_triples
441-
FOR ALL USING (auth.role() != 'anon');
442-
443-
CREATE POLICY "Block anonymous access" ON gitmem_query_metrics
444-
FOR ALL USING (auth.role() != 'anon');
445-
446-
CREATE POLICY "Block anonymous access" ON scar_enforcement_variants
447-
FOR ALL USING (auth.role() != 'anon');
434+
-- Drop-then-create for idempotency (CREATE POLICY has no IF NOT EXISTS)
435+
DO $$ BEGIN
436+
-- gitmem_learnings
437+
DROP POLICY IF EXISTS "Service role full access" ON gitmem_learnings;
438+
CREATE POLICY "Service role full access" ON gitmem_learnings FOR ALL USING (auth.role() = 'service_role');
439+
DROP POLICY IF EXISTS "Block anonymous access" ON gitmem_learnings;
440+
CREATE POLICY "Block anonymous access" ON gitmem_learnings FOR ALL USING (auth.role() != 'anon');
441+
-- gitmem_sessions
442+
DROP POLICY IF EXISTS "Service role full access" ON gitmem_sessions;
443+
CREATE POLICY "Service role full access" ON gitmem_sessions FOR ALL USING (auth.role() = 'service_role');
444+
DROP POLICY IF EXISTS "Block anonymous access" ON gitmem_sessions;
445+
CREATE POLICY "Block anonymous access" ON gitmem_sessions FOR ALL USING (auth.role() != 'anon');
446+
-- gitmem_decisions
447+
DROP POLICY IF EXISTS "Service role full access" ON gitmem_decisions;
448+
CREATE POLICY "Service role full access" ON gitmem_decisions FOR ALL USING (auth.role() = 'service_role');
449+
DROP POLICY IF EXISTS "Block anonymous access" ON gitmem_decisions;
450+
CREATE POLICY "Block anonymous access" ON gitmem_decisions FOR ALL USING (auth.role() != 'anon');
451+
-- gitmem_scar_usage
452+
DROP POLICY IF EXISTS "Service role full access" ON gitmem_scar_usage;
453+
CREATE POLICY "Service role full access" ON gitmem_scar_usage FOR ALL USING (auth.role() = 'service_role');
454+
DROP POLICY IF EXISTS "Block anonymous access" ON gitmem_scar_usage;
455+
CREATE POLICY "Block anonymous access" ON gitmem_scar_usage FOR ALL USING (auth.role() != 'anon');
456+
-- gitmem_threads
457+
DROP POLICY IF EXISTS "Service role full access" ON gitmem_threads;
458+
CREATE POLICY "Service role full access" ON gitmem_threads FOR ALL USING (auth.role() = 'service_role');
459+
DROP POLICY IF EXISTS "Block anonymous access" ON gitmem_threads;
460+
CREATE POLICY "Block anonymous access" ON gitmem_threads FOR ALL USING (auth.role() != 'anon');
461+
-- knowledge_triples
462+
DROP POLICY IF EXISTS "Service role full access" ON knowledge_triples;
463+
CREATE POLICY "Service role full access" ON knowledge_triples FOR ALL USING (auth.role() = 'service_role');
464+
DROP POLICY IF EXISTS "Block anonymous access" ON knowledge_triples;
465+
CREATE POLICY "Block anonymous access" ON knowledge_triples FOR ALL USING (auth.role() != 'anon');
466+
-- gitmem_query_metrics
467+
DROP POLICY IF EXISTS "Service role full access" ON gitmem_query_metrics;
468+
CREATE POLICY "Service role full access" ON gitmem_query_metrics FOR ALL USING (auth.role() = 'service_role');
469+
DROP POLICY IF EXISTS "Block anonymous access" ON gitmem_query_metrics;
470+
CREATE POLICY "Block anonymous access" ON gitmem_query_metrics FOR ALL USING (auth.role() != 'anon');
471+
-- scar_enforcement_variants
472+
DROP POLICY IF EXISTS "Service role full access" ON scar_enforcement_variants;
473+
CREATE POLICY "Service role full access" ON scar_enforcement_variants FOR ALL USING (auth.role() = 'service_role');
474+
DROP POLICY IF EXISTS "Block anonymous access" ON scar_enforcement_variants;
475+
CREATE POLICY "Block anonymous access" ON scar_enforcement_variants FOR ALL USING (auth.role() != 'anon');
476+
END $$;
448477

449478
-- ============================================================================
450479
-- Auto-update timestamps
@@ -457,10 +486,12 @@ BEGIN
457486
END;
458487
$$ LANGUAGE plpgsql;
459488

489+
DROP TRIGGER IF EXISTS gitmem_learnings_updated ON gitmem_learnings;
460490
CREATE TRIGGER gitmem_learnings_updated
461491
BEFORE UPDATE ON gitmem_learnings
462492
FOR EACH ROW EXECUTE FUNCTION gitmem_update_timestamp();
463493

494+
DROP TRIGGER IF EXISTS gitmem_sessions_updated ON gitmem_sessions;
464495
CREATE TRIGGER gitmem_sessions_updated
465496
BEFORE UPDATE ON gitmem_sessions
466497
FOR EACH ROW EXECUTE FUNCTION gitmem_update_timestamp();

0 commit comments

Comments
 (0)