Database Diff Tool
Overview
Compare database schemas between two environments (development vs. staging, staging vs.
Prerequisites
- Connection credentials to both source and target databases
psql or mysql CLI configured to connect to both environments
- Read access to
information_schema and pg_catalog (PostgreSQL) or information_schema (MySQL)
- Permission to run
pg_dump --schema-only for full schema extraction
- Understanding of which environment is the "source of truth" (typically the migration-managed environment)
Instructions
Extract the full schema from both databases for comparison:
- PostgreSQL:
pg_dump --schema-only --no-owner --no-privileges -f schema_source.sql source_db and repeat for target_db
- MySQL:
mysqldump --no-data --routines --triggers source_db > schema_source.sql
- Alternatively, query
information_schema directly for programmatic comparison
Compare tables present in each database:
SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = 'source_db' EXCEPT SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = 'target_db'
- This reveals tables that exist in source but not in target (and vice versa)
Compare columns for each shared table:
- Query
information_schema.columns from both databases for: column_name, data_type, character_maximum_length, is_nullable, column_default, ordinal_position
- Flag differences in data type, nullability, default values, and column ordering
- Detect added columns (in source, not target) and dropped columns (in target, not source)
Compare indexes:
- PostgreSQL: Query
pg_indexes for indexname, indexdef on each database
- MySQL: Query
information_schema.STATISTICS for INDEX_NAME, COLUMN_NAME, NON_UNIQUE
- Flag missing, extra, or differently-defined indexes
Compare constraints (primary keys, foreign keys, unique, check):
- Query
information_schema.table_constraints and information_schema.key_column_usage
- Detect missing foreign keys, changed constraint names, and altered check constraint expressions
Compare functions, stored procedures, and triggers:
- PostgreSQL: Query
pg_proc for function signatures and pg_trigger for trigger definitions
- MySQL: Query
information_schema.ROUTINES and information_schema.TRIGGERS
- Compare function bodies for logical differences
Compare enum types and custom types (PostgreSQL):
- Query
pg_type and pg_enum for enum label differences
- Detect added or removed enum values (note: PostgreSQL only supports adding enum values, not removing)
Generate a structured diff report categorizing differences as:
- Added: Objects in source not present in target (require CREATE statements)
- Removed: Objects in target not present in source (require DROP statements, confirm intentional)
- Modified: Objects differing between source and target (require ALTER statements)
Generate migration SQL to synchronize the target database to match the source:
- CREATE TABLE for new tables, ALTER TABLE ADD COLUMN for new columns
- ALTER TABLE ALTER COLUMN for type changes, ALTER TABLE DROP COLUMN for removed columns
- CREATE INDEX / DROP INDEX for index differences
- Include transaction wrapping and rollback-safe operations
Validate the generated migration by applying it to a copy of the target database and re-running the diff. The second diff should report zero differences, confirming the migration produces the expected state.
Output
- Schema diff report listing all differences categorized by type (added, removed, modified)
- Migration SQL script to synchronize target schema to match source
- Rollback SQL script to reverse the migration if needed
- Side-by-side comparison of differing object definitions
- Drift detection summary highlighting changes not tracked in migration files
Error Handling
| Error |
Cause |
Solution |
| Connection refused to one database |
Network or credential issue on source or target |
Verify connection strings; check firewall rules; confirm credentials work with direct psql or mysql connection |
Permission denied on pg_catalog queries |
User lacks read access to system catalogs |
Grant pg_read_all_settings role; or use pg_dump --schema-only which requires fewer privileges |
| False positive differences from default value formatting |
PostgreSQL normalizes default expressions differently in different versions |
Normalize default value strings before comparison; ignore whitespace differences; compare semantic equivalence |
| Enum type modification blocked |
PostgreSQL does not support removing enum values or reordering |
Create a new enum type, migrate the column, drop the old type; document this as a multi-step migration |
| Generated migration fails on target |
Target has data that violates new constraints |
Add data validation queries before constraint creation; backfill default values; handle edge cases in migration |
Examples
Detecting schema drift between staging and production: After 3 months without auditing, the diff reveals: 2 columns added to production manually (not in migrations), 1 index missing from staging, and 3 functions with different implementations. A migration script is generated to bring staging in sync, and the manual production changes are backported into migration files.
Pre-deployment schema validation: Before deploying a release with 5 migration files, run the diff between the post-migration staging schema and the expected schema. The diff catches a migration that accidentally dropped a constraint that a later migration depends on. The migration ordering is fixed before production deployment.
Comparing PostgreSQL schemas across major version upgrade: Schema extracted from PostgreSQL 14 and compared against PostgreSQL 16 after migration. Diff reveals function signature changes for built-in function calls, updated default values for new parameters, and deprecated syntax in stored procedures. Migration script updates function definitions for the new version.
Resources
1---
2name: comparing-database-schemas-2
3description: |
4 Process use when you need to work with schema comparison.
5 This skill provides database schema diff and sync with comprehensive guidance and automation.
6 Trigger with phrases like "compare schemas", "diff databases",
7 or "sync database schemas".
8
9allowed-tools: Read, Write, Edit, Grep, Glob, Bash(psql:*), Bash(mysql:*), Bash(mongosh:*)
10version: 1.0.0
11author: Jeremy Longshore <jeremy@intentsolutions.io>
12license: MIT
13compatible-with: claude-code, codex, openclaw
14tags: [database, comparing-database]
15
16---
17
18# Database Diff Tool
19
20## Overview
21
22Compare database schemas between two environments (development vs. staging, staging vs.
23
24## Prerequisites
25
26- Connection credentials to both source and target databases
27- `psql` or `mysql` CLI configured to connect to both environments
28- Read access to `information_schema` and `pg_catalog` (PostgreSQL) or `information_schema` (MySQL)
29- Permission to run `pg_dump --schema-only` for full schema extraction
30- Understanding of which environment is the "source of truth" (typically the migration-managed environment)
31
32## Instructions
33
341. Extract the full schema from both databases for comparison:
35 - PostgreSQL: `pg_dump --schema-only --no-owner --no-privileges -f schema_source.sql source_db` and repeat for target_db
36 - MySQL: `mysqldump --no-data --routines --triggers source_db > schema_source.sql`
37 - Alternatively, query `information_schema` directly for programmatic comparison
38
392. Compare tables present in each database:
40 - `SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = 'source_db' EXCEPT SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = 'target_db'`
41 - This reveals tables that exist in source but not in target (and vice versa)
42
433. Compare columns for each shared table:
44 - Query `information_schema.columns` from both databases for: `column_name`, `data_type`, `character_maximum_length`, `is_nullable`, `column_default`, `ordinal_position`
45 - Flag differences in data type, nullability, default values, and column ordering
46 - Detect added columns (in source, not target) and dropped columns (in target, not source)
47
484. Compare indexes:
49 - PostgreSQL: Query `pg_indexes` for `indexname`, `indexdef` on each database
50 - MySQL: Query `information_schema.STATISTICS` for `INDEX_NAME`, `COLUMN_NAME`, `NON_UNIQUE`
51 - Flag missing, extra, or differently-defined indexes
52
535. Compare constraints (primary keys, foreign keys, unique, check):
54 - Query `information_schema.table_constraints` and `information_schema.key_column_usage`
55 - Detect missing foreign keys, changed constraint names, and altered check constraint expressions
56
576. Compare functions, stored procedures, and triggers:
58 - PostgreSQL: Query `pg_proc` for function signatures and `pg_trigger` for trigger definitions
59 - MySQL: Query `information_schema.ROUTINES` and `information_schema.TRIGGERS`
60 - Compare function bodies for logical differences
61
627. Compare enum types and custom types (PostgreSQL):
63 - Query `pg_type` and `pg_enum` for enum label differences
64 - Detect added or removed enum values (note: PostgreSQL only supports adding enum values, not removing)
65
668. Generate a structured diff report categorizing differences as:
67 - **Added**: Objects in source not present in target (require CREATE statements)
68 - **Removed**: Objects in target not present in source (require DROP statements, confirm intentional)
69 - **Modified**: Objects differing between source and target (require ALTER statements)
70
719. Generate migration SQL to synchronize the target database to match the source:
72 - CREATE TABLE for new tables, ALTER TABLE ADD COLUMN for new columns
73 - ALTER TABLE ALTER COLUMN for type changes, ALTER TABLE DROP COLUMN for removed columns
74 - CREATE INDEX / DROP INDEX for index differences
75 - Include transaction wrapping and rollback-safe operations
76
7710. Validate the generated migration by applying it to a copy of the target database and re-running the diff. The second diff should report zero differences, confirming the migration produces the expected state.
78
79## Output
80
81- **Schema diff report** listing all differences categorized by type (added, removed, modified)
82- **Migration SQL script** to synchronize target schema to match source
83- **Rollback SQL script** to reverse the migration if needed
84- **Side-by-side comparison** of differing object definitions
85- **Drift detection summary** highlighting changes not tracked in migration files
86
87## Error Handling
88
89| Error | Cause | Solution |
90|-------|-------|---------|
91| Connection refused to one database | Network or credential issue on source or target | Verify connection strings; check firewall rules; confirm credentials work with direct `psql` or `mysql` connection |
92| Permission denied on `pg_catalog` queries | User lacks read access to system catalogs | Grant `pg_read_all_settings` role; or use `pg_dump --schema-only` which requires fewer privileges |
93| False positive differences from default value formatting | PostgreSQL normalizes default expressions differently in different versions | Normalize default value strings before comparison; ignore whitespace differences; compare semantic equivalence |
94| Enum type modification blocked | PostgreSQL does not support removing enum values or reordering | Create a new enum type, migrate the column, drop the old type; document this as a multi-step migration |
95| Generated migration fails on target | Target has data that violates new constraints | Add data validation queries before constraint creation; backfill default values; handle edge cases in migration |
96
97## Examples
98
99**Detecting schema drift between staging and production**: After 3 months without auditing, the diff reveals: 2 columns added to production manually (not in migrations), 1 index missing from staging, and 3 functions with different implementations. A migration script is generated to bring staging in sync, and the manual production changes are backported into migration files.
100
101**Pre-deployment schema validation**: Before deploying a release with 5 migration files, run the diff between the post-migration staging schema and the expected schema. The diff catches a migration that accidentally dropped a constraint that a later migration depends on. The migration ordering is fixed before production deployment.
102
103**Comparing PostgreSQL schemas across major version upgrade**: Schema extracted from PostgreSQL 14 and compared against PostgreSQL 16 after migration. Diff reveals function signature changes for built-in function calls, updated default values for new parameters, and deprecated syntax in stored procedures. Migration script updates function definitions for the new version.
104
105## Resources
106
107- PostgreSQL information_schema: https://www.postgresql.org/docs/current/information-schema.html
108- MySQL information_schema: https://dev.mysql.com/doc/refman/8.0/en/information-schema.html
109- pg_dump schema-only mode: https://www.postgresql.org/docs/current/app-pgdump.html
110- migra (PostgreSQL schema diff tool): https://github.com/djrobstep/migra
111- Skeema (MySQL schema management): https://www.skeema.io/