Skip to content

Schema diff between connections

Pro tier. Tools → Schema diff opens a side-by-side comparison between two open connections.

The dialog has two pickers — Source and Target, each showing your active sessions. Same connection on both sides (e.g. dev DB twice) is allowed; same engine on both sides isn’t required (PG → MySQL diffs work, with type-mapping hints).

By default, everything:

  • Tables — present in source vs target, plus per-table:
    • Columns: name, type, NULL, default, identity / AUTO_INCREMENT
    • Primary key (composition + order)
    • Foreign keys (target column, ON DELETE / UPDATE actions)
    • Indexes (column composition, uniqueness, partial WHERE clauses)
    • Triggers (full body)
    • Comments
  • Views — definition + materialised flag
  • Sequences (PG) — current value, increment, min/max
  • Functions / procedures — full body (with normalisation for whitespace + comments so cosmetic-only diffs are filtered)
  • Schemas / databases — character set + collation
  • Optional: data — per-table row count + sample-row diff (locked decision: schema diff is full scope by default, with a schema-only toggle)

The diff renders as a tree:

✗ users (in source, missing in target)
+ columns: subscription_tier, subscribed_at
✓ orders (matches)
~ products (differs)
~ column: price NUMERIC(10,2) → DECIMAL(12,2)
~ index: idx_products_active NEW: WHERE archived_at IS NULL
- column: legacy_sku (in source, missing in target)
+ stripe_events (in target, missing in source)

Click any difference to see the full proposed ALTER statement.

Below the tree: Apply changes → opens a SQL preview with one ALTER / CREATE / DROP per difference, in dependency-safe order.

You can:

  • Uncheck individual differences to skip them in this run
  • Edit the SQL inline (e.g. tweak the type cast for a wide column)
  • Pick the target to apply against (defaults to “Target”, but can go to a third connection for migration-staging workflows)
  • Save the SQL as a migration — emits two *.up.sql / *.down.sql files into your project’s migrations/ folder

Diffing PG → MySQL or MySQL → SQLite shows type-mapping warnings:

~ column: created_at TIMESTAMPTZ → DATETIME
⚠ MySQL DATETIME has no timezone — UTC offset will be lost
Suggested: store offset separately, or use TIMESTAMP

The diff is informational by default — you choose whether to proceed. If the target engine genuinely can’t represent the source’s type (PG JSONB → MariaDB without JSON support), the row is flagged red with no auto-suggested ALTER.

  • Very wide tables (>500 columns) — diff works, just slow (~5–10s for full comparison). Use the “schema-only” toggle to skip data-side comparisons for an instant result.
  • Materialised views with circular dependencies — Quay flags them but doesn’t yet auto-resolve order. You may need to manually edit the apply SQL to drop + recreate in the right sequence.
  • Per-row data diff — currently a sample (first 100 rows) + count comparison. A full row-by-row diff for a 10M-row table is expensive; that path is gated behind an explicit “full data diff” button with size warning.
  • Pre-deployment check: diff dev → prod before pushing a migration. Catches “I forgot to add the index in the deploy script” the day before it goes live.
  • Drift detection: weekly diff between two prod replicas to catch out-of-band changes (someone hand-edited the DB).
  • Migration generation: diff “what I have” → “what I want” (across two test DBs), generate the ALTER, save as a migration.