1 Assess Compat · Type notes
2 Connect Test src & tgt
3 Schema Extract Reflect · Constraints
4 Type Convert Cross-engine mapping
5 Load / Sync Full · Incr · CDC
6 Verify & Report Counts · Constraints · HTML
SOURCE DATABASES MIGRATIONBRIDGE ENGINE TARGET DATABASES ON-PREMISES SQL SERVER 🗄 SQL Server 2022 Latest · Full feature set 🗄 SQL Server 2019 Big Data Clusters · CTP features 🗄 SQL Server 2017 Linux support · Graph DB 🗄 SQL Server 2016 Stretch DB · Query Store SQL Server 2014 / 2012 Legacy · upgrade path CLOUD SQL SERVER Azure SQL Database PaaS · Elastic pools · DTU/vCore Azure SQL Managed Instance Near 100% compat · VNET 🌿 AWS RDS for SQL Server Multi-AZ · S3 backup · SE/EE OTHER SOURCES 🐘 PostgreSQL (VM / On-Prem) psycopg2 · Reverse migration 🐬 MySQL / MariaDB pymysql · InnoDB FK enforcement 🔮 Oracle oracledb (thin) · ROWID/LONG mapping ⚡ Endrias Bridge Python 3.8+ · Tkinter GUI · SQLAlchemy 2.0 ① ASSESS • Compatibility check per migration path • Type conversion preview · Constraint inventory ② CONNECT • Test source + target connections • Create DB if missing · Ensure schemas ③ SCHEMA EXTRACT • Reflect: tables, PK / UQ / FK / CHECK / DEFAULT • All indexes · Sort tables by FK dependency ④ TYPE CONVERT • Cross-engine type mapping (25+ rules) • Strip collations · Strip server defaults ⑤ LOAD / SYNC • Full: chunked copy (10,000 rows/batch) • Incremental: watermark · CDC: SQL Server native ⑥ VERIFY & REPORT • Row counts · Random sample match • Constraint count diff · HTML report export ⑧ SCHEMA OBJECTS • Views · Procs · Triggers · UDTTs · Statistics • Phase 5: Filtered · Columnstore · Full-Text · Spatial indexes • Same-engine: auto · Cross-engine: manual rewrite 🔑 Connection Security Encrypted credentials · keyring · TLS · Least-privilege model pyodbc · psycopg2 · pymysql · oracledb SQL SERVER TARGETS 🗄 SQL Server Upgrade 2016 → 2017 → 2019 → 2022 DMA · compat-level · same-engine auto Azure SQL Database Elastic Jobs · serverless · Query Store Azure SQL Managed Instance SQL Agent · CLR · linked servers 🌿 AWS RDS for SQL Server S3 backup restore · Multi-AZ failover POSTGRESQL TARGETS 🐘 PostgreSQL VM / On-Prem UUID · JSONB · partial indexes Azure DB for PostgreSQL Flex Flexible server · zone redundancy 🌿 AWS RDS for PostgreSQL Multi-AZ · Performance Insights 🌿 Amazon Aurora (PostgreSQL) Serverless v2 · global DB MYSQL TARGETS 🐬 MySQL / MariaDB VM InnoDB · CHECK 8.0.16+ · utf8mb4 Azure DB for MySQL Flexible Zone redundant · read replicas 🌿 AWS RDS / Aurora (MySQL) Aurora MySQL · Multi-AZ NOSQL / ANALYTICS TARGETS 🍃 MongoDB pymongo Cosmos (Mongo API) 📦 DynamoDB boto3 PK/SK mapping 🌐 Cosmos DB azure-cosmos Core SQL API ⚡ Redis redis-py KV store 🔍 Elasticsearch elasticsearch 8.x Row per document ❄ Snowflake snowflake-sqlalchemy Analytics / DW 💠 Cassandra cassandra-driver Wide-column LEGEND Forward migration Reverse / bidirectional AWS / MySQL path Azure path SQL Server family PostgreSQL family MySQL family NoSQL / Analytics Endrias Bridge · Architecture Reference · 2026

Constraint Migration — What Migrates Automatically via SQLAlchemy Metadata

PRIMARY KEY
UNIQUE Constraint
FOREIGN KEY (with use_alter)
CHECK Constraint *
DEFAULT Values
B-Tree Indexes
UNIQUE Indexes
INCLUDE Columns (PG)
~ CLUSTERED → B-Tree **
Filtered → Partial Index
COLUMNSTORE Index
Full-Text Index †
Spatial Index
Pre-migration inventory log
Post-migration count diff

* MySQL CHECK: enforced only on MySQL 8.0.16+, stored on earlier versions  ·  ** Clustered indexes become B-Tree on PostgreSQL/MySQL (no clustered index concept)  ·  † Full-Text requires SQLSERVER_FULLTEXT option enabled in the RDS option group (one-time setup)

What Each Status Means — Implications & Required Manual Steps

Constraint / Index Status What happens during migration Implication / manual steps required
PRIMARY KEY ✔ Auto Reflected from source metadata and recreated on target exactly. None. IDENTITY / SERIAL / AUTO_INCREMENT semantics are translated per-engine.
UNIQUE Constraint ✔ Auto Reflected and recreated as a UNIQUE constraint on the target table. None.
FOREIGN KEY ✔ Auto Created with use_alter=True so circular FKs are added in a second pass after all tables exist. None. Cross-schema FKs require both schemas to be migrated in the same run.
CHECK Constraint ✔ Auto * SQL expression reflected and recreated. On MySQL < 8.0.16 the constraint is stored but silently not enforced by the engine. Verify MySQL target version. On 8.0.16+ enforcement is identical to SQL Server.
DEFAULT Values ✔ Auto Server-side DEFAULT expressions reflected and applied to target DDL. Functions like GETDATE() are translated to NOW() / CURRENT_TIMESTAMP. Complex expressions may need manual review.
B-Tree Indexes ✔ Auto All standard non-clustered indexes reflected and created on target. None. Fill factor and PAD_INDEX hints are stripped (target-engine specific).
UNIQUE Indexes ✔ Auto Reflected as UniqueConstraint in SQLAlchemy metadata; enforced on target. None.
INCLUDE Columns (PG) ✔ Auto PostgreSQL covering index INCLUDE columns are preserved when migrating PostgreSQL → PostgreSQL. INCLUDE columns are silently dropped when the target is not PostgreSQL (MySQL/SQL Server do not support the INCLUDE syntax in the same way).
Pre-migration inventory log ✔ Auto Before copy begins, every constraint and index on every source table is written to the migration log. Use this log as your reference checklist to verify ✗ items after migration. Search the log for [inventory] lines.
Post-migration count diff ✔ Auto After all tables are copied, source and target row counts are compared and the diff is logged. A non-zero diff means rows were lost or duplicated — investigate before cutover. Count mismatch does NOT block the migration; it is a warning only.
CLUSTERED → B-Tree ~ Partial SQL Server clustered indexes (rows physically sorted by PK on disk) are migrated as standard B-Tree indexes on PostgreSQL / MySQL — which have no native clustered concept. Performance implication: Range scans on the PK (e.g. WHERE ClaimId BETWEEN 1 AND 1000000) will be slower on PostgreSQL because rows are heap-stored rather than clustered.

Fix (PostgreSQL): Run CLUSTER <table> USING <pk_index>; post-migration — one-time cost, significant read gain for large tables. Requires an exclusive lock — schedule during low-traffic window.
MySQL InnoDB: Unaffected — InnoDB always clusters on the PK automatically.
Filtered → Partial Index ✔ Auto SQL Server filtered indexes (CREATE INDEX … WHERE IsActive = 1) are scraped from sys.indexes.filter_definition and recreated on the target with the original WHERE clause. Runs after data copy as part of Phase 5 index migration. Filters using SQL Server-only functions (CONVERT, ISNULL) will fail DDL on PostgreSQL/MySQL targets — check the log for Filtered indexes: N failed and rewrite those manually.
MySQL: Does not support partial indexes — filtered index DDL will error; the error is logged and skipped.
COLUMNSTORE Index ✔ Auto Clustered and non-clustered columnstore indexes are scraped from sys.indexes WHERE type IN (5,6) and recreated on the target after data copy as part of Phase 5 index migration. Same-engine (SQL Server → SQL Server / Azure SQL / RDS) only. No equivalent on PostgreSQL or MySQL targets — Phase 5 will log an error for each and continue; verify the log for Columnstore indexes: N failed.
PostgreSQL target: Use materialized views with pre-aggregation for reporting workloads.
MySQL target: No equivalent — consider a dedicated analytics replica.
Full-Text Index † ✔ Auto † Full-text catalogs are created first (sys.fulltext_catalogs), then full-text indexes (sys.fulltext_indexes) are applied automatically after data copy. Queries using CONTAINS() and FREETEXT() will work on the target. † One-time AWS prerequisite: RDS option group must include the SQLSERVER_FULLTEXT option before migration runs (RDS Console → Option groups → Add option). Without it, catalog creation will fail — logged and skipped, migration still completes.
PostgreSQL target: Not applicable — add a tsvector column + GIN index manually and rewrite queries.
MySQL target: Not applicable — use ALTER TABLE t ADD FULLTEXT INDEX ft_idx(col); manually.
Spatial Index ✔ Auto Spatial indexes are scraped from sys.spatial_indexes — tessellation scheme and bounding box are preserved — and recreated on the target after data copy as part of Phase 5 index migration. Same-engine (SQL Server → SQL Server / Azure SQL / RDS) only.
⚠ Warning: If you used [type_overrides] geography = NVARCHAR(4000), the column is plain text on the target — spatial index creation will fail (logged, not fatal).
PostgreSQL target: Install PostGIS → convert column to geometryCREATE INDEX … USING GIST(col);
MySQL target (InnoDB 5.7.5+): Column must be NOT NULL → ALTER TABLE t ADD SPATIAL INDEX(col);
🍃MongoDBpymongo · Cosmos Mongo API
📦Amazon DynamoDBboto3 · PK/SK mapping
🌐Azure Cosmos DBazure-cosmos · Core SQL
Redisredis-py · KV store
🔍Elasticsearchelasticsearch 8.x
Snowflakesnowflake-sqlalchemy
💠Cassandracassandra-driver

SQL Server → SQL Server Upgrade

  • Same-engine: no type rewriting applied
  • Use DMA for compat-level validation before upgrade
  • Deprecated types flagged: TEXT, NTEXT, IMAGE
  • Views / procs / triggers auto-copied (same-engine only)
  • All constraints carry across automatically

SQL Server → PostgreSQL

  • UNIQUEIDENTIFIER → native UUID (PostgreSQL) or CHAR(36)
  • DATETIME2 / DATETIMEOFFSET → TIMESTAMP WITH TIME ZONE
  • NVARCHAR / NCHAR → VARCHAR / CHAR
  • Filtered indexes → partial indexes (WHERE clause preserved)
  • Circular FKs: deferred via use_alter

SQL Server → MySQL

  • IDENTITY → AUTO_INCREMENT
  • BIT → TINYINT(1)
  • CHECK constraints: InnoDB 8.0.16+ only for enforcement
  • FK constraints: InnoDB engine required (not MyISAM)
  • SSMA for MySQL recommended for large schemas

Reverse Migrations (Vice Versa)

  • PostgreSQL → SQL Server: SERIAL → IDENTITY, BOOLEAN → BIT
  • MySQL → SQL Server: AUTO_INCREMENT → IDENTITY(1,1)
  • UUID → UNIQUEIDENTIFIER on SQL Server targets
  • JSONB → NVARCHAR(MAX) on SQL Server
  • All constraint types migrate; circular FK ordering handled

Sync Modes

  • Full: two-lane parallel copy (fast lane: concurrent small tables; slow lane: sequential large tables)
  • PK-range seeding: WHERE pk > last_pk chunking for all INT/BIGINT PK tables — checkpoint saved after every batch
  • Transient-error auto-retry: TCP drops / Azure SQL resets retried up to 5× per batch (5 s → 120 s back-off, engine.dispose() between attempts)
  • Incremental: watermark column (updated_at / id)
  • CDC: SQL Server native Change Data Capture — LOB tables on Azure SQL DB auto-downgrade to watermark
  • BACPAC Seed: SqlPackage export/import for 1 TB+ tables
  • Chunk size: auto-reduced for LOB tables to stay ≤ 256 MB/batch; XML tables use DATALENGTH-based sizing (off-row payload) instead of allocation-page estimate
  • BCP copy returns actual method used (BCP vs PK-range fallback) — log label is always accurate
  • PostgreSQL SSL: sslmode=require enforced on all PostgreSQL family connections — required by Azure DB for PostgreSQL Flexible Server and AWS RDS for PostgreSQL

Large DB & Performance Notes

  • Validated: 1.07 B-row tables, 1.41 TB total database with JSON/LOB columns (production environment, June 2026)
  • Validated: MySQL (VM / On-Prem) → Azure SQL Database — world DB, 3 tables, 5,302 rows, ENUM mapping, FK waves, index rebuild (June 2026)
  • PK-range checkpoint resume — interrupted migrations continue from last committed PK, not row 0
  • LOB-aware chunk sizing — auto-caps batches at 256 MB for wide-row tables; XML off-row payload measured via DATALENGTH, not page allocation
  • BIGINT overflow pre-flight — warns when INT PK approaches SQL Server INT_MAX (2,147,483,647)
  • Fast catalog row count (sys.dm_db_partition_stats) — Resume pre-flight completes in seconds, not minutes
  • Transient-error auto-retry — TCP connection drops silently retried; migration never skips a table due to a network blip
  • Resume progress accuracy — skipped table row counts seed grand_total and _rows_done so progress bar starts at correct % and ETA is based on actual remaining rows
  • Recommended: ≥1 Gbps network, 16+ GB RAM migration host, same-region as target
  • ODBC Driver 17 or 18 required for SQL Server sources

Windows VM Initialization — Boot Flow

Sequence executed on first boot by the Endrias Bridge Windows Service

BOOT Windows Service Endrias Bridge · pywin32 · auto-start InitManager Orchestrates sources + plugins tried in order, first wins run sequentially Metadata Sources First to respond wins — others skipped 💿 ConfigDrive v2 ISO / HDD partition · D:\openstack\latest 🌐 OpenStack HTTP 169.254.169.254/openstack/latest EC2 HTTP 169.254.169.254/latest/meta-data Plugins Execute in fixed order · each can skip if not needed SetHostnamePlugin sets computer name CreateUserPlugin local user + group SetPasswordPlugin sets account password NetworkConfigPlugin static IP / DNS InjectSSHKeysPlugin ExecuteUserdataPlugin LEGEND Metadata source (first wins) Plugin step (sequential) Control / data flow