Common database issues and their solutions
FATAL: password authentication failed
could not connect to server
timeout expired
SASL authentication failed
-- Error: SSL connection is required
-- Solution 1: Add SSL mode to connection string
postgresql://user:pass@host:5432/db?sslmode=require
-- Solution 2: Set environment variable
export PGSSLMODE=require
Check migration status
supabase migration list
If local only
# Remove the local migration file
rm supabase/migrations/[timestamp]_migration_name.sql
# Create new one with different name
supabase migration new fixed_migration_name
If already deployed
# Create a fix migration
supabase migration new fix_previous_migration
# Add your fixes to the new migration
-- Error: relation "table_name" already exists
-- Fix: Use IF NOT EXISTS
CREATE TABLE IF NOT EXISTS public.table_name (
id uuid PRIMARY KEY DEFAULT gen_random_uuid()
);
-- For columns
ALTER TABLE public.table_name
ADD COLUMN IF NOT EXISTS new_column text;
-- For constraints
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'constraint_name'
) THEN
ALTER TABLE public.table_name
ADD CONSTRAINT constraint_name CHECK (column > 0);
END IF;
END $$;
# Symptoms: Foreign key constraints fail
# Solution 1: Check migration order
ls -la supabase/migrations/
# Solution 2: Combine related migrations
cat supabase/migrations/*create_tables* > supabase/migrations/[timestamp]_create_all_tables.sql
# Solution 3: Defer constraints
BEGIN;
SET CONSTRAINTS ALL DEFERRED;
-- Your migration SQL here
COMMIT;
Check if RLS is enabled
SELECT
schemaname,
tablename,
rowsecurity
FROM pg_tables
WHERE schemaname = 'public'
AND tablename = 'your_table';
Check policies exist
SELECT
pol.polname as policy_name,
pol.polcmd as command,
pol.polroles as roles,
CASE
WHEN pol.polpermissive THEN 'PERMISSIVE'
ELSE 'RESTRICTIVE'
END as type
FROM pg_policies pol
WHERE pol.schemaname = 'public'
AND pol.tablename = 'your_table';
Test as specific user
-- Set user context
SET request.jwt.claim.sub TO 'user-uuid-here';
-- Test query
SELECT * FROM public.your_table;
-- Reset context
RESET request.jwt.claim.sub;
Debug policy
-- Create debug policy
CREATE POLICY "debug_policy" ON public.your_table
FOR SELECT
USING (
RAISE NOTICE 'User ID: %, Row ID: %', auth.uid(), id
RETURNING true
);
-- Common RLS debugging queries
-- Check current user
SELECT
auth.uid() as user_id,
auth.role() as role,
auth.email() as email;
-- Test policy condition directly
SELECT
s.*,
(EXISTS (
SELECT 1 FROM public.profiles p
WHERE p.user_id = auth.uid()
AND p.student_id = s.id
)) as policy_passes
FROM public.students s;
-- Bypass RLS temporarily (dev only!)
ALTER TABLE public.your_table DISABLE ROW LEVEL SECURITY;
-- Don't forget to re-enable!
ALTER TABLE public.your_table ENABLE ROW LEVEL SECURITY;
-- Enable query timing
\timing on
-- Get query plan
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
SELECT * FROM public.student_school_interactions
WHERE student_id = 'uuid'
AND liked = true
ORDER BY interaction_date DESC;
-- Find missing indexes
SELECT
schemaname,
tablename,
attname,
n_distinct,
most_common_vals
FROM pg_stats
WHERE tablename = 'student_school_interactions'
AND attname IN ('student_id', 'liked');
-- Check table sizes
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size,
pg_size_pretty(pg_relation_size(schemaname||'.'||tablename)) as table_size,
pg_size_pretty(pg_indexes_size(schemaname||'.'||tablename)) as indexes_size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
-- Find bloated indexes
SELECT
schemaname,
tablename,
indexname,
pg_size_pretty(pg_relation_size(indexrelid)) as index_size,
idx_tup_read,
idx_tup_fetch
FROM pg_stat_user_indexes
WHERE idx_tup_read > 0
ORDER BY pg_relation_size(indexrelid) DESC;
-- Clean up
VACUUM ANALYZE; -- Gentle cleanup
VACUUM FULL; -- Aggressive, locks tables
REINDEX CONCURRENTLY INDEX index_name; -- Rebuild index
-- Error: violates foreign key constraint
-- Find orphaned records
SELECT ssi.*
FROM public.student_school_interactions ssi
LEFT JOIN public.students s ON ssi.student_id = s.id
WHERE s.id IS NULL;
-- Fix: Delete orphans or add missing parents
DELETE FROM public.student_school_interactions
WHERE student_id NOT IN (
SELECT id FROM public.students
);
-- Prevent future issues
ALTER TABLE public.student_school_interactions
DROP CONSTRAINT fk_student,
ADD CONSTRAINT fk_student
FOREIGN KEY (student_id)
REFERENCES public.students(id)
ON DELETE CASCADE;
-- Find and fix common inconsistencies
-- Mismatched counts
WITH counts AS (
SELECT
c.id,
c.message_count,
COUNT(m.id) as actual_count
FROM public.conversations c
LEFT JOIN public.messages m ON c.id = m.conversation_id
GROUP BY c.id, c.message_count
)
UPDATE public.conversations
SET message_count = counts.actual_count
FROM counts
WHERE conversations.id = counts.id
AND counts.message_count != counts.actual_count;
-- Orphaned records
DELETE FROM public.conversation_participants
WHERE conversation_id NOT IN (
SELECT id FROM public.conversations
);
-- Circular references
WITH RECURSIVE hierarchy AS (
SELECT id, parent_id, 1 as level
FROM public.categories
WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.parent_id, h.level + 1
FROM public.categories c
JOIN hierarchy h ON c.parent_id = h.id
WHERE h.level < 10 -- Prevent infinite recursion
)
SELECT * FROM hierarchy
ORDER BY level, id;
-- Check storage usage
SELECT
auth.uid() as user_id,
bucket_id,
COUNT(*) as file_count,
pg_size_pretty(SUM(COALESCE(metadata->>'size', '0')::bigint)) as total_size
FROM storage.objects
GROUP BY auth.uid(), bucket_id;
-- Clean up orphaned files
DELETE FROM storage.objects
WHERE owner NOT IN (
SELECT id FROM auth.users
);
-- Check auth configuration
SELECT * FROM auth.users WHERE email = 'user@example.com';
-- Verify user metadata
SELECT
id,
email,
raw_user_meta_data,
created_at,
last_sign_in_at
FROM auth.users
ORDER BY created_at DESC
LIMIT 10;
-- Fix missing profiles
INSERT INTO public.profiles (id, role)
SELECT
id as id,
'student' as role
FROM auth.users
WHERE id NOT IN (
SELECT id FROM public.profiles
);
-- Find blocking queries
SELECT
blocked_locks.pid AS blocked_pid,
blocked_activity.usename AS blocked_user,
blocking_locks.pid AS blocking_pid,
blocking_activity.usename AS blocking_user,
blocked_activity.query AS blocked_statement,
blocking_activity.query AS blocking_statement
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity
ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks
ON blocking_locks.locktype = blocked_locks.locktype
AND blocking_locks.relation = blocked_locks.relation
AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity
ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;
-- Kill blocking query (use carefully!)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE pid = [blocking_pid];
-- Create restore point (before risky operation)
SELECT pg_create_restore_point('before_risky_change');
-- Use transactions
BEGIN;
-- Your risky operations
SAVEPOINT my_savepoint;
-- More operations
-- If something goes wrong:
ROLLBACK TO my_savepoint;
-- Or if all good:
COMMIT;
-- Restore from backup (contact Supabase support for production)
Check Supabase Status
Review Logs
# In Supabase Dashboard
# Settings → Logs → Database logs
Contact Support