Cursor generates SQL migrations that break databases when it lacks schema context or uses outdated column names. By referencing your current schema file with @file, adding database rules to .cursor/rules, and validating migrations with a dry-run step, you prevent data loss and ensure Cursor produces accurate schema changes every time.
Why Cursor breaks database migrations and how to fix it
Cursor generates SQL migrations by inferring schema from the files in your project context. Without explicit schema references, it guesses column names, types, and constraints, often incorrectly. This tutorial shows how to give Cursor accurate schema context, set up guardrails for migration safety, and validate output before it touches your database.
Prerequisites
- Cursor installed with a project that uses database migrations
- A current schema file or migration history in your project
- Familiarity with your migration tool (Prisma, Knex, TypeORM, raw SQL)
- Database backup or development environment for testing
Step-by-step guide
Export your current schema as a reference file
Export your current schema as a reference file
Create a schema snapshot that Cursor can reference. This gives the AI an accurate picture of your current database state. For Prisma, your schema.prisma file works. For raw SQL projects, export the schema and save it in your project.
1-- schema/current_schema.sql2-- Generated: 2026-03-253-- Database: PostgreSQL 1645CREATE TABLE users (6 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),7 email VARCHAR(255) UNIQUE NOT NULL,8 name VARCHAR(100) NOT NULL,9 role VARCHAR(20) DEFAULT 'user',10 created_at TIMESTAMP DEFAULT NOW(),11 updated_at TIMESTAMP DEFAULT NOW()12);1314CREATE TABLE orders (15 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),16 user_id UUID REFERENCES users(id) ON DELETE CASCADE,17 status VARCHAR(20) DEFAULT 'pending',18 total DECIMAL(10, 2) NOT NULL,19 created_at TIMESTAMP DEFAULT NOW()20);2122CREATE INDEX idx_orders_user_id ON orders(user_id);23CREATE INDEX idx_orders_status ON orders(status);Pro tip: Regenerate this file after every migration. Use pg_dump --schema-only for PostgreSQL or mysqldump --no-data for MySQL.
Expected result: A single file containing your complete current database schema that Cursor can reference.
Add database migration rules to .cursor/rules
Add database migration rules to .cursor/rules
Create rules that enforce safe migration patterns. These rules prevent Cursor from generating destructive operations without safeguards and ensure correct SQL syntax for your database engine.
1---2description: Database migration safety rules3globs: "migrations/**/*.sql,prisma/**/*.prisma"4alwaysApply: true5---67## Database Migration Rules8- ALWAYS reference @schema/current_schema.sql before generating migrations9- Use IF NOT EXISTS for CREATE TABLE and CREATE INDEX10- Use IF EXISTS for DROP operations11- NEVER use DROP TABLE or DROP COLUMN without explicit user confirmation12- ALWAYS include a rollback/down migration13- Use explicit column types, NEVER use implicit type coercion14- Add NOT NULL constraints only with DEFAULT values for existing data15- Database engine: PostgreSQL 1616- ALWAYS add created_at and updated_at timestamps to new tablesExpected result: Cursor will follow safe migration patterns and reference your schema before generating changes.
Generate a migration with proper context
Generate a migration with proper context
When asking Cursor to create a migration, always reference your schema file and any recent migration files. Open Chat (Cmd+L) and provide explicit context about what change you need.
1// Cursor Chat prompt (Cmd+L):2// @schema/current_schema.sql3// @migrations/20260320_add_orders_table.sql4// Generate a new migration to add a 'shipping_address'5// JSONB column to the orders table with a default empty6// object. Include both UP and DOWN migrations.7// Reference the current orders table schema.89-- Expected output:10-- UP11ALTER TABLE orders12 ADD COLUMN shipping_address JSONB DEFAULT '{}'::jsonb;1314-- DOWN15ALTER TABLE orders16 DROP COLUMN IF EXISTS shipping_address;Expected result: Cursor generates a migration that correctly references the existing orders table structure with proper UP and DOWN sections.
Validate the migration with a dry run
Validate the migration with a dry run
Before applying any Cursor-generated migration, validate it against your development database. Use Cursor's terminal (Cmd+K in terminal) to run a syntax check or dry run. This catches column name mismatches, type errors, and constraint violations before they reach production.
1-- For PostgreSQL, use a transaction with ROLLBACK:2BEGIN;34-- Paste the Cursor-generated migration here5ALTER TABLE orders6 ADD COLUMN shipping_address JSONB DEFAULT '{}'::jsonb;78-- Check that it applied correctly9\d orders1011-- Roll back without committing12ROLLBACK;Pro tip: Wrap every migration test in BEGIN/ROLLBACK so your development database stays clean during validation.
Expected result: The migration runs without errors inside the transaction, and ROLLBACK leaves your database unchanged.
Update your schema reference after applying migrations
Update your schema reference after applying migrations
After successfully running a migration, regenerate your schema reference file. This keeps Cursor's context accurate for the next migration. Use Cmd+K in the terminal to generate the updated schema dump.
1# Cmd+K in Cursor terminal:2# "Generate a pg_dump command that exports only the3# schema (no data) from my local database to4# schema/current_schema.sql"56pg_dump --schema-only --no-owner --no-privileges \7 -f schema/current_schema.sql \8 $DATABASE_URLExpected result: An updated schema file reflecting the latest migration, ready for Cursor to reference in future prompts.
Complete working example
1-- Migration: Add shipping fields to orders table2-- Generated with Cursor assistance3-- Date: 2026-03-2545-- ============ UP ============67ALTER TABLE orders8 ADD COLUMN IF NOT EXISTS shipping_address JSONB DEFAULT '{}'::jsonb;910ALTER TABLE orders11 ADD COLUMN IF NOT EXISTS shipping_method VARCHAR(50);1213ALTER TABLE orders14 ADD COLUMN IF NOT EXISTS tracking_number VARCHAR(100);1516ALTER TABLE orders17 ADD COLUMN IF NOT EXISTS shipped_at TIMESTAMP;1819ALTER TABLE orders20 ADD COLUMN IF NOT EXISTS delivered_at TIMESTAMP;2122-- Add index for shipping status queries23CREATE INDEX IF NOT EXISTS idx_orders_shipped_at24 ON orders(shipped_at)25 WHERE shipped_at IS NOT NULL;2627-- Add partial index for undelivered orders28CREATE INDEX IF NOT EXISTS idx_orders_undelivered29 ON orders(status)30 WHERE delivered_at IS NULL AND status = 'shipped';3132-- ============ DOWN ============3334DROP INDEX IF EXISTS idx_orders_undelivered;35DROP INDEX IF EXISTS idx_orders_shipped_at;3637ALTER TABLE orders DROP COLUMN IF EXISTS delivered_at;38ALTER TABLE orders DROP COLUMN IF EXISTS shipped_at;39ALTER TABLE orders DROP COLUMN IF EXISTS tracking_number;40ALTER TABLE orders DROP COLUMN IF EXISTS shipping_method;41ALTER TABLE orders DROP COLUMN IF EXISTS shipping_address;Common mistakes
Why it's a problem: Not providing the current schema as context
How to avoid: Always include @schema/current_schema.sql or @prisma/schema.prisma in your migration prompts.
Why it's a problem: Accepting ALTER TABLE ADD COLUMN NOT NULL without a DEFAULT
How to avoid: Add a .cursor/rules directive: 'Add NOT NULL constraints only with DEFAULT values for existing data.'
Why it's a problem: Skipping the DOWN migration
How to avoid: Explicitly ask for 'both UP and DOWN migrations' in every prompt. Add this requirement to your database rules.
Why it's a problem: Running Cursor-generated migrations directly on production
How to avoid: Always test migrations on a development database first using BEGIN/ROLLBACK transactions.
Best practices
- Maintain an up-to-date schema reference file and regenerate it after every migration
- Always reference @schema/current_schema.sql in migration prompts
- Add database safety rules to .cursor/rules requiring IF EXISTS/IF NOT EXISTS guards
- Request both UP and DOWN migrations in every prompt
- Test all generated migrations in a transaction with ROLLBACK before committing
- Use Plan Mode (Shift+Tab) for complex multi-table migrations to review the plan first
- Include your database engine and version in .cursor/rules so Cursor uses correct syntax
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I have a PostgreSQL 16 database with a users table (id UUID, email VARCHAR, name VARCHAR, role VARCHAR, created_at, updated_at) and an orders table (id UUID, user_id UUID FK, status VARCHAR, total DECIMAL, created_at). Generate a migration to add shipping_address JSONB, shipping_method, tracking_number, shipped_at, and delivered_at columns to orders. Include UP and DOWN migrations with IF EXISTS guards.
In Cursor Chat (Cmd+L): @schema/current_schema.sql @migrations/ Generate a new migration to add shipping fields (shipping_address JSONB, shipping_method VARCHAR, tracking_number VARCHAR, shipped_at TIMESTAMP, delivered_at TIMESTAMP) to the orders table. Include both UP and DOWN. Use IF NOT EXISTS guards. Add appropriate indexes.
Frequently asked questions
Why does Cursor generate column names that do not match my schema?
Cursor infers column names from your application code, not from the actual database. If your code uses camelCase but your database uses snake_case, Cursor may generate the wrong names. Reference the actual schema file to fix this.
Can Cursor generate Prisma migrations?
Yes. Reference @prisma/schema.prisma in your prompt and ask Cursor to update the schema file. Then run npx prisma migrate dev yourself to generate the actual SQL migration from the updated schema.
How do I handle migrations for multiple database engines?
Specify the database engine in your .cursor/rules file and in each prompt. If your project supports multiple engines, create separate rule files for each: database-postgres.mdc and database-mysql.mdc.
Should I let Cursor run migrations directly in Agent mode?
No. Even with YOLO mode, never let Cursor execute database migrations automatically. Always review the SQL, test with BEGIN/ROLLBACK, and run the final migration manually or through your established CI/CD pipeline.
What if Cursor generates a migration that loses data?
If you followed the dry-run step with BEGIN/ROLLBACK, no data was lost. If a destructive migration was applied, use the DOWN migration to revert, or restore from your pre-migration backup.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation