Skip to main content

Overview

Studley AI uses SQL migration files to manage database schema changes. All migrations are stored in the /scripts directory and should be run in sequential order.

Migration Files

Migrations are numbered sequentially:
scripts/
├── 001_initialize_database.sql
├── 002_create_admin_config_table.sql
├── 003_add_banner_link.sql
├── 004_create_shared_sessions_table.sql
├── 005_fix_site_settings_schema.sql
├── 007_create_feedback_tables.sql
├── 007_create_rate_limit_table.sql
├── 008_create_shared_quizzes_table.sql
├── 010_create_user_profiles.sql
├── 010_create_user_system_tables.sql
├── 012_create_admin_user.sql
├── 013_create_ai_workspace_tables.sql
├── 014_add_favorites_to_study_items.sql
├── 015_add_first_last_name_to_users.sql
├── 016_enhance_quiz_results_table.sql
├── 017_make_quiz_item_id_nullable.sql
├── 018_fix_quiz_results_table.sql
├── 019_recreate_results_tables.sql
├── 025_create_shared_materials_table.sql
├── 026_create_notifications_table.sql
└── [additional migrations]
Some migration numbers are duplicated (e.g., 007, 010) due to parallel development. Both files should be run.

Running Migrations

Initial Database Setup

For a fresh database installation, run migrations in order: 1. Enable Required Extensions
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
2. Run Core Migrations Start with the initialization script:
psql -d your_database -f scripts/001_initialize_database.sql
Or using Supabase SQL Editor:
  1. Open Supabase Dashboard → SQL Editor
  2. Copy contents of 001_initialize_database.sql
  3. Click “Run”
3. Run Subsequent Migrations Run each migration file in numerical order:
# Using psql
for file in scripts/*.sql; do
  echo "Running $file..."
  psql -d your_database -f "$file"
done
Important: Always backup your database before running migrations in production.

Supabase Migration Process

For Supabase projects: Using Supabase CLI:
# Login to Supabase
supabase login

# Link your project
supabase link --project-ref your-project-ref

# Create a new migration
supabase db new migration_name

# Apply migrations
supabase db push
Using Supabase Dashboard:
  1. Navigate to SQL Editor in your Supabase dashboard
  2. Create a new query
  3. Paste migration SQL
  4. Click “Run”
  5. Verify success in Table Editor

Migration Tracking

Create a migrations tracking table:
CREATE TABLE IF NOT EXISTS schema_migrations (
  id SERIAL PRIMARY KEY,
  migration_name TEXT NOT NULL UNIQUE,
  executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Record each migration after running
INSERT INTO schema_migrations (migration_name) 
VALUES ('001_initialize_database');

Key Migrations Explained

001_initialize_database.sql

Purpose: Creates core authentication and user management tables Tables Created:
  • users - User accounts
  • accounts - OAuth providers
  • sessions - Active sessions
  • verification_tokens - Email verification
  • credit_usage - Credit transactions
  • generations - Generation history
Run this first on any new database.

010_create_user_system_tables.sql

Purpose: Creates user profile and library system Tables Created:
  • user_profiles - Extended profile data
  • user_folders - Content organization
  • user_library_items - Saved content
  • admin_notifications - Platform announcements
Includes: RLS policies for all tables

010_create_user_profiles.sql

Purpose: Alternative user profile schema (may be redundant with above) Tables Created:
  • public.profiles - User profile information
This may conflict with 010_create_user_system_tables.sql. Check your database to see which profile table structure you’re using.

012_create_admin_user.sql

Purpose: Creates initial admin account Manual Steps Required:
  1. Create user in Supabase Auth dashboard first
  2. Note the user ID
  3. Update migration script with correct user ID
  4. Run migration to grant admin privileges

013_create_ai_workspace_tables.sql

Purpose: AI Workspace feature tables Tables Created:
  • ai_chat_sessions - Chat sessions
  • ai_chat_messages - Individual messages
  • workspace_documents - Uploaded documents

019_recreate_results_tables.sql

Purpose: Fixes quiz/flashcard/writing results schema Warning: This drops existing results tables!
DROP TABLE IF EXISTS quiz_results CASCADE;
DROP TABLE IF EXISTS flashcard_results CASCADE;
DROP TABLE IF EXISTS writing_results CASCADE;
This migration deletes data! Backup your results tables before running.

007_create_feedback_tables.sql

Purpose: User feedback and bug reporting Tables Created:
  • bug_reports - Bug submissions
  • user_feedback - General feedback
Includes: RLS policies allowing anonymous submissions

025_create_shared_materials_table.sql

Purpose: Material sharing functionality Tables Created:
  • shared_materials - Persistent sharing links
Features:
  • Public access tokens
  • Expiration dates
  • Resharing controls

Custom Migrations

Creating a New Migration

1. Create the file:
# Next available number
touch scripts/027_your_migration_name.sql
2. Write your migration:
-- Description: Brief description of what this migration does
-- Author: Your name
-- Date: 2026-03-07

-- Create new table
CREATE TABLE IF NOT EXISTS new_feature (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Add indexes
CREATE INDEX idx_new_feature_user_id ON new_feature(user_id);

-- Enable RLS
ALTER TABLE new_feature ENABLE ROW LEVEL SECURITY;

-- Create policies
CREATE POLICY "Users can view their own records" 
ON new_feature 
FOR SELECT 
USING (auth.uid() = user_id);
3. Test locally:
psql -d your_local_db -f scripts/027_your_migration_name.sql
4. Apply to production (after thorough testing)

Migration Best Practices

Migration Guidelines:
  • Always use IF NOT EXISTS for CREATE statements
  • Include rollback scripts for destructive changes
  • Test on staging environment first
  • Document breaking changes clearly
  • Version control all migration files
  • Never modify existing migration files after they’ve been deployed

Rollback Strategy

For each migration, create a corresponding rollback:
-- 027_your_migration_name_rollback.sql
-- Rollback for migration 027

DROP TABLE IF EXISTS new_feature;

Common Migration Tasks

Adding a Column

-- Add column if it doesn't exist
DO $$ 
BEGIN
  IF NOT EXISTS (
    SELECT 1 FROM information_schema.columns 
    WHERE table_name = 'users' AND column_name = 'new_column'
  ) THEN
    ALTER TABLE users ADD COLUMN new_column TEXT;
  END IF;
END $$;

Creating an Index

CREATE INDEX IF NOT EXISTS idx_table_column 
ON table_name(column_name);

-- Concurrent index (doesn't lock table)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_table_column 
ON table_name(column_name);

Modifying a Column

-- Change column type
ALTER TABLE users 
ALTER COLUMN credits TYPE BIGINT;

-- Add NOT NULL constraint
ALTER TABLE users 
ALTER COLUMN email SET NOT NULL;

-- Add default value
ALTER TABLE users 
ALTER COLUMN credits SET DEFAULT 5000;

Adding Foreign Keys

ALTER TABLE child_table
ADD CONSTRAINT fk_parent
FORGN KEY (parent_id) 
REFERENCES parent_table(id) 
ON DELETE CASCADE;

Troubleshooting

Migration Failed: Table Already Exists

Solution: Use IF NOT EXISTS or check if migration was already applied
SELECT * FROM schema_migrations 
WHERE migration_name = '027_your_migration';

Migration Failed: Permission Denied

Solution: Ensure you’re using the correct database role:
-- Check current role
SELECT current_user;

-- Switch to postgres or service_role
SET ROLE postgres;

RLS Policy Conflicts

Solution: Drop existing policies before recreating:
DROP POLICY IF EXISTS "policy_name" ON table_name;

CREATE POLICY "policy_name" ON table_name
  FOR SELECT USING (true);

Foreign Key Constraint Violation

Solution: Ensure referenced tables exist and have matching data types:
-- Check if parent table exists
SELECT * FROM information_schema.tables 
WHERE table_name = 'parent_table';

-- Check column types match
SELECT column_name, data_type 
FROM information_schema.columns 
WHERE table_name IN ('parent_table', 'child_table');

Production Deployment

Pre-Deployment Checklist

  • Backup database
  • Test migration on staging
  • Review RLS policies
  • Check for breaking changes
  • Prepare rollback script
  • Schedule maintenance window if needed
  • Notify users of downtime (if applicable)

Deployment Steps

  1. Backup Production Database
# Supabase backup
supabase db dump -f backup_$(date +%Y%m%d_%H%M%S).sql
  1. Apply Migration
supabase db push
  1. Verify Migration
-- Check tables were created
SELECT table_name 
FROM information_schema.tables 
WHERE table_schema = 'public';

-- Verify data integrity
SELECT COUNT(*) FROM your_new_table;
  1. Monitor Application
Watch for errors in application logs after deployment.

Rollback Procedure

If migration fails:
# Restore from backup
psql -d your_database -f backup_20260307_120000.sql

# Or run rollback script
psql -d your_database -f scripts/027_your_migration_rollback.sql

Database Schema

View complete database structure

RLS Policies

Understanding Row Level Security

Admin Dashboard

Return to admin overview

User Management

Manage users and data

Build docs developers (and LLMs) love