A smart tool to sync missing rows between two PostgreSQL databases.
Project description
Did your intern point production to the wrong DB?
Did your devops team swear "nothing changed" before disappearing?
Did your CTO say "just restore from backup" like you weren't already stressed?
Yeah. We've all been there.
Welcome to db-shifter — the script that digs through your old PostgreSQL database and copies only the missing rows into your new one. Oh, it also syncs columns too — just add the --deep-check flag.
No overwrites. No dumb
pg_dumprestores. Just calculated migration that doesn't wreck your existing data.
Why?
Because your CTO is a clown.
Because your devops team "accidentally" pointed production at the wrong database.
Because you need to copy missing data table-by-table and you're too busy to do it manually.
db-shifter handles the grunt work while you figure out who to blame.
Features
Core Functionality
- ✅ Auto-detects all tables in
publicschema - ✅ Finds primary keys automatically
- ✅ Copies only missing rows in the new DB
- ✅ Skips duplicates (doesn't ruin your existing data)
Advanced Features
- 🔧 Column-based sync — specify exactly which columns to sync
- 🔄 Circular FK handling — automatically detects and handles circular foreign key dependencies
- 🧠 Smart sync order optimization based on FK relationships
- 🛡️ FK constraint handling — no more FK violation errors
Installation
Quick Install
pip install db-shifter
From Source
git clone https://github.com/goodness5/db-shifter.git
cd db-shifter
pip install -e .
Usage
Sync Mode (Default)
Basic Sync
db-shifter --old-db-url postgresql://user:pass@oldhost/db \
--new-db-url postgresql://user:pass@newhost/db
Column-based Sync
Sync only specific columns (useful when schemas differ):
db-shifter --old-db-url postgresql://user:pass@oldhost/db \
--new-db-url postgresql://user:pass@newhost/db \
--columns id,name,email,created_at
Single Table Sync
db-shifter --old-db-url postgresql://user:pass@oldhost/db \
--new-db-url postgresql://user:pass@newhost/db \
--table users
Deep Check Mode
Compare and update column values for existing rows (not just insert missing rows):
db-shifter --old-db-url postgresql://user:pass@oldhost/db \
--new-db-url postgresql://user:pass@newhost/db \
--deep-check
This will:
- Insert missing rows (as usual)
- Compare column values for rows that exist in both databases
- Update rows where column values differ
Insert Mode
Insert records directly into a database table. Perfect for adding data without writing SQL.
Insert from JSON String
db-shifter --db-url postgresql://user:pass@host/db \
--table users \
--data '{"name":"John Doe","email":"john@example.com","age":30}'
Insert Multiple Records from JSON
db-shifter --db-url postgresql://user:pass@host/db \
--table users \
--data '[{"name":"John","email":"john@example.com"},{"name":"Jane","email":"jane@example.com"}]'
Insert from JSON File
db-shifter --db-url postgresql://user:pass@host/db \
--table users \
--file data.json
Insert from CSV File
db-shifter --db-url postgresql://user:pass@host/db \
--table users \
--file data.csv
Handle Conflicts Gracefully
# Ignore conflicts (skip duplicate records)
db-shifter --db-url postgresql://user:pass@host/db \
--table users \
--file data.json \
--on-conflict ignore
# Update on conflict (upsert)
db-shifter --db-url postgresql://user:pass@host/db \
--table users \
--file data.json \
--on-conflict update
Command-line Options
Sync Mode Options
| Flag | Description |
|---|---|
--dry-run |
Simulate the transfer, no data is hurt |
--verbose |
Prints detailed logs of every row |
--table <name> |
Sync just one table |
--skip-fk |
Ignores foreign key errors |
--columns <list> |
Sync only specified columns (comma-separated). Primary key is always included. |
--deep-check |
Deep check: compare column values for existing rows and update differences |
Insert Mode Options
| Flag | Description |
|---|---|
--db-url |
Database connection string (required) |
--table |
Table name to insert into (required) |
--data |
JSON data (object or array) or path to JSON/CSV file |
--file |
Path to JSON or CSV file |
--dry-run |
Simulate the insert, no data is changed |
--verbose |
Print detailed logs of every record |
--skip-fk |
Ignore foreign key errors |
--on-conflict |
What to do on primary key conflict: ignore, update, or error (default) |
How It Works
1. Connects to both databases
2. Lists all public tables
3. Checks the primary keys
4. Pulls rows missing from the new database
5. Inserts them without wrecking existing rows
⚠️ Caution
Important Notes:
- Assumes you have primary keys (don't be a barbarian)
- Circular FK dependencies are detected and handled, but you may need to manually re-add FK constraints after sync
- If you're syncing 50GB of data, don't cry when it takes time
- Backups are your friend. Don't be dumb.
Circular Foreign Key Handling
When db-shifter detects circular foreign key dependencies (e.g., Table A → Table B → Table A), it will:
- Detect the cycle and warn you about it
- Temporarily disable FK constraints for tables in the cycle during sync
- Sync all data without FK constraint violations
- Note: You may need to manually re-add FK constraints after sync completes
Example output:
Circular foreign key dependencies detected:
Cycle 1: users → profiles → users
Will sync these tables in multiple passes with FK constraint handling
Coming Soon
- 🕐 Timestamp-based syncing (
created_atsupport) - 🗄️
.sqlite→ postgres sync - 📦
.bakand json data support input - 🖥️ GUI with a "FIX EVERYTHING" button (for product managers)
Contributing
Found a bug? Good.
Fix it, submit a PR, and don't drop your cashapp in the description.
License
MIT. Do whatever you want. Just don't call me if you drop prod again.
Made with ❤️ (and frustration)
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file db_shifter-0.1.9.tar.gz.
File metadata
- Download URL: db_shifter-0.1.9.tar.gz
- Upload date:
- Size: 17.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e6ae02ddb31c46505956a4150db4c187b0160003b14d565bf5ed53b01c86d7a
|
|
| MD5 |
90b08bc4ae5b09a101886768f3fb00dd
|
|
| BLAKE2b-256 |
7b99adbc9d1a97b59e1a4c91375a3d91494ae29865840e7bfffbfeb6e851f11f
|
File details
Details for the file db_shifter-0.1.9-py3-none-any.whl.
File metadata
- Download URL: db_shifter-0.1.9-py3-none-any.whl
- Upload date:
- Size: 15.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2cf987946341a92a847683d5cce104c3328312da40a2a6d05bc74f69343f606c
|
|
| MD5 |
c2c4809e127007a9a32f89e2a15cbca3
|
|
| BLAKE2b-256 |
21e456fcaec3ad18edeb794f568022805ae10ed9799ee955cb9da0f3ce0a12a4
|