Oracle → PostgreSQL Migration Complete Architecture Guide (Step-by-Step) Complete Guide for DBAs 🔥
ORACLE
→
POSTGRESQL
Migration Architecture Guide · AIDBAHUB
Cloud Ready · Zero Downtime
Enterprise Database Migration
Oracle
→
PostgreSQL
Migration Architecture
A complete step-by-step migration blueprint — from Assessment & Planning through Cutover & Go-Live. Click any phase in the architecture diagram below to expand its detailed steps, tools, scripts, and key considerations.
Zero Downtime
CDC-based cutover
High Performance
FDW / DMS tooling
Data Integrity
Row-count + checksum
Scalable
AWS / Azure / On-Prem
Secure
TLS + RBAC enforced
Migration Architecture Flow
Click any step to view details, scripts & key considerations
SOURCE (Old System)
🔴
ORACLE
On-Prem / AWS / Azure
PL/SQL Code
LOB Data (BLOB/CLOB)
Indexes & Constraints
Sequences & Synonyms
Database Links
Triggers & Packages
Partitioned Tables
Jobs (DBMS_SCHEDULER)
MIGRATION PROCESS
Phase 01
Assessment & Planning
Tools: ora2pg --type TABLE --assess | AWR | exachk
Phase 02
Schema Conversion (Ora2Pg)
Tools: Ora2Pg, AWS SCT, pgloader
Phase 03
Code Conversion — PL/SQL → PL/pgSQL
Tools: Ora2Pg, SQLines, Manual conversion
Phase 04
Initial Data Load
Tools: FDW, pg_dump, AWS DMS, ora2pg --type COPY
Phase 05
CDC — Change Data Capture
Tools: Debezium, AWS DMS, GoldenGate, pglogical
Phase 06
Data Transformation
Tools: dbt, Python ETL, pg_transform functions
Phase 07
Data Validation
Tools: ora2pg --type TEST, pgCompare, custom SQL
Phase 08
Cutover & Go-Live
Strategy: Blue-Green / DNS Switch / Connection Pool
TARGET (New System)
🐘
PostgreSQL
AWS / Azure / On-Prem
PL/pgSQL Code
Migrated Data (BYTEA/TEXT)
New Indexes (B-Tree/GIN)
Sequences (IDENTITY)
FDW (replaces DB Links)
Functions & Extensions
Declarative Partitioning
pg_cron (replaces DBMS_SCHED)
▲ Click any migration phase above to expand detailed steps, tools, scripts & key considerations
Phase 01 · Assessment & Planning
Assessment & Planning
Step-by-Step Process
- 01Run Ora2Pg in assessment mode to get a migration complexity score (0–100)
- 02Inventory all Oracle objects: tables, views, procedures, packages, triggers, sequences, jobs
- 03Analyse AWR/ASH reports to understand workload patterns and peak load windows
- 04Identify unsupported features: CONNECT BY, ROWNUM, ROWID, Oracle-specific data types
- 05Estimate data volume per table — plan FDW vs DMS vs file-based transfer
- 06Define downtime window and cutover strategy (zero-downtime vs planned window)
- 07Build migration team: DBA, Developer, QA, Project Manager
- 08Set up DEV/UAT PostgreSQL environment for testing
Assessment Command
ora2pg — Full Assessment Report
# Install ora2pg cpan install Ora2Pg # Run full assessment — generates migration score ora2pg --type TABLE --assess \ -t '(TABLE|VIEW|MVIEW|SEQUENCE|TRIGGER|PROCEDURE|FUNCTION|PACKAGE|TYPE)' \ -o assessment_report.html # Check score: 0=easy, 100=complex # Cost unit: 1=trivial, 5=moderate, 10=complex conversion # Oracle object inventory SQL SELECT object_type, COUNT(*) cnt FROM dba_objects WHERE owner = 'APP_OWNER' GROUP BY object_type ORDER BY cnt DESC;
Key Considerations
ROWNUM vs LIMIT
Oracle uses WHERE ROWNUM < N. PostgreSQL uses LIMIT N. Every ROWNUM usage needs rewriting.
CONNECT BY
Hierarchical queries need rewriting as WITH RECURSIVE CTEs in PostgreSQL. High effort.
AWR Analysis
Capture top 50 SQL by elapsed time. These need manual review and explain plan comparison post-migration.
Migration Score
Score 0–30: fast migration. Score 31–60: weeks. Score 60+: requires significant code rewrite budget.
Key Tools
| Tool | Purpose | License |
|---|---|---|
| Ora2Pg | Full assessment + schema/code export | Free (GPL) |
| AWS SCT | Schema Conversion Tool with report | Free (AWS) |
| exachk | Oracle health check pre-migration | MOS download |
| AWR Reports | Workload analysis, top SQL capture | Oracle Diag Pack |
Phase 02 · Schema Conversion
Schema Conversion — Ora2Pg
Step-by-Step Process
- 01Export Oracle DDL (tables, indexes, constraints) using ora2pg --type TABLE
- 02Convert Oracle data types to PostgreSQL equivalents (NUMBER → NUMERIC, VARCHAR2 → TEXT)
- 03Convert SEQUENCE to GENERATED ALWAYS AS IDENTITY or CREATE SEQUENCE
- 04Rewrite CONNECT BY hierarchical queries as WITH RECURSIVE CTEs
- 05Convert Oracle partitioning (INTERVAL, LIST, RANGE) to PostgreSQL declarative partitioning
- 06Replace SYNONYM with schema search_path or views
- 07Replace DATABASE LINK with postgres_fdw foreign data wrapper
- 08Test schema in target PostgreSQL and fix errors iteratively
Schema Conversion Script
Ora2Pg — Export all schema objects
# Export tables ora2pg --type TABLE -o tables.sql # Export sequences ora2pg --type SEQUENCE -o sequences.sql # Export indexes ora2pg --type INDEX -o indexes.sql # Export constraints ora2pg --type CONSTRAINT -o constraints.sql # Export views ora2pg --type VIEW -o views.sql # Apply to PostgreSQL psql -U postgres -d targetdb -f tables.sql psql -U postgres -d targetdb -f sequences.sql psql -U postgres -d targetdb -f indexes.sql
Oracle → PostgreSQL Type Mapping
| Oracle Type | PostgreSQL Type | Notes |
|---|---|---|
| NUMBER(p,s) | NUMERIC(p,s) | Exact match |
| VARCHAR2(n) | TEXT or VARCHAR(n) | TEXT preferred |
| DATE | TIMESTAMP | Oracle DATE has time! |
| CLOB | TEXT | No size limit in PG |
| BLOB | BYTEA | Binary storage |
| RAW(n) | BYTEA | Binary equivalent |
| NVARCHAR2 | TEXT | PG is always Unicode |
| FLOAT / BINARY_FLOAT | DOUBLE PRECISION | Approximate numeric |
| XMLTYPE | XML | Native XML type |
| SDO_GEOMETRY | GEOMETRY (PostGIS) | Needs PostGIS ext |
Oracle DATE Trap
Oracle DATE stores date AND time. PostgreSQL DATE is date-only. Always use TIMESTAMP in PostgreSQL to avoid data loss.
Case Sensitivity
Oracle is case-insensitive for identifiers. PostgreSQL lowercases unquoted names. Watch for quoted mixed-case identifiers.
Phase 03 · Code Conversion
PL/SQL → PL/pgSQL Conversion
Step-by-Step Process
- 01Export all procedures, functions, packages, triggers with ora2pg --type PROCEDURE
- 02Convert Oracle packages to PostgreSQL schemas + functions (no direct package equivalent)
- 03Replace DBMS_OUTPUT.PUT_LINE with RAISE NOTICE
- 04Replace SYSDATE / SYSTIMESTAMP with NOW() / CURRENT_TIMESTAMP
- 05Replace NVL() with COALESCE(), DECODE() with CASE WHEN
- 06Rewrite BULK COLLECT / FORALL as PostgreSQL set-based operations
- 07Replace ROWNUM with ROW_NUMBER() OVER() or LIMIT
- 08Convert Oracle REF CURSOR to PostgreSQL REFCURSOR or RETURNS TABLE
- 09Replace DBMS_SCHEDULER jobs with pg_cron extension
- 10Test each stored procedure in isolation with unit tests
PL/SQL vs PL/pgSQL Mapping
Oracle PL/SQL → PostgreSQL PL/pgSQL
-- ORACLE PL/SQL CREATE OR REPLACE PROCEDURE get_emp( p_id IN NUMBER, p_name OUT VARCHAR2 ) IS BEGIN SELECT ename INTO p_name FROM emp WHERE empno = p_id; DBMS_OUTPUT.PUT_LINE(p_name); EXCEPTION WHEN NO_DATA_FOUND THEN p_name := 'NOT FOUND'; END; -- POSTGRESQL PL/pgSQL EQUIVALENT CREATE OR REPLACE FUNCTION get_emp( p_id BIGINT ) RETURNS TEXT LANGUAGE plpgsql AS $$ DECLARE v_name TEXT; BEGIN SELECT ename INTO v_name FROM emp WHERE empno = p_id; IF NOT FOUND THEN RETURN 'NOT FOUND'; END IF; RAISE NOTICE '%', v_name; RETURN v_name; END; $$;
Oracle Packages
No direct equivalent. Split into schemas + functions. Package state (global variables) needs rethinking.
BULK COLLECT
Replace with set-based SQL. PG processes sets natively — row-by-row loops in PL/pgSQL are slow.
Phase 04 · Initial Data Load
Initial Data Load — FDW / DMS
Step-by-Step Process
- 01Disable triggers and foreign key constraints on target for load speed
- 02Drop indexes on target tables before loading (recreate after load)
- 03Export data from Oracle using ora2pg --type COPY or Oracle Data Pump
- 04Use COPY command for fast bulk load into PostgreSQL (10x faster than INSERT)
- 05For large tables (>1TB): use AWS DMS full-load or oracle_fdw parallel fetch
- 06Load large tables in parallel — split by partition key or ROWID range
- 07Re-enable constraints, rebuild indexes (use REINDEX CONCURRENTLY)
- 08Run VACUUM ANALYZE on all loaded tables to update statistics
FDW Initial Load Script
oracle_fdw — Direct Oracle → PostgreSQL Transfer
-- Install oracle_fdw extension CREATE EXTENSION oracle_fdw; -- Create foreign server pointing to Oracle CREATE SERVER oracle_src FOREIGN DATA WRAPPER oracle_fdw OPTIONS (dbserver '//oracle-host:1521/ORCL'); CREATE USER MAPPING FOR postgres SERVER oracle_src OPTIONS (user 'oracle_user', password 'secret'); -- Import Oracle table as foreign table IMPORT FOREIGN SCHEMA "APP_OWNER" FROM SERVER oracle_src INTO oracle_staging; -- Transfer data directly (no intermediate files!) INSERT INTO customers SELECT * FROM oracle_staging.customers; -- For large tables: parallel load with ranges -- Split into parallel psql sessions by ID range INSERT INTO orders SELECT * FROM oracle_staging.orders WHERE order_id BETWEEN 1 AND 5000000;
Load Strategy Comparison
| Method | Speed | Best For |
|---|---|---|
| oracle_fdw INSERT...SELECT | Medium | Direct transfer, small-medium tables |
| ora2pg COPY format | Fast | Any size, file-based |
| AWS DMS Full Load | Very Fast | AWS-hosted environments |
| Oracle Data Pump + pg_restore | Medium | Air-gapped networks |
| pgloader | Fast | Parallel, self-contained |
Performance Tuning
PostgreSQL — optimize for bulk load
-- Temporarily increase for load speed SET maintenance_work_mem = '4GB'; SET max_wal_size = '8GB'; -- Disable WAL logging for initial load (unlogged) ALTER TABLE big_table SET UNLOGGED; -- Load data here... ALTER TABLE big_table SET LOGGED; -- After load: update statistics VACUUM ANALYZE customers; VACUUM ANALYZE orders;
Phase 05 · Change Data Capture
CDC — Change Data Capture
Step-by-Step Process
- 01Enable Oracle Supplemental Logging for all migrated tables
- 02Configure Debezium Oracle connector or AWS DMS CDC task pointing to Oracle LogMiner
- 03Start CDC AFTER initial load completes — capture all changes from load start SCN
- 04CDC events stream through Kafka topic (Debezium) or DMS replication instance
- 05Apply changes to PostgreSQL target: INSERT/UPDATE/DELETE replayed in order
- 06Monitor replication lag — target must stay within 30 seconds of source
- 07Run side-by-side validation queries on both databases to verify consistency
- 08Keep CDC running until cutover day — then flush final changes and switch
Debezium CDC Configuration
Debezium Oracle Connector Config (JSON)
{
"name": "oracle-migration-cdc",
"connector.class":
"io.debezium.connector.oracle.OracleConnector",
"database.hostname": "oracle-host",
"database.port": "1521",
"database.dbname": "ORCL",
"database.user": "logminer_user",
"table.include.list":
"APP.CUSTOMERS,APP.ORDERS,APP.PRODUCTS",
"snapshot.mode": "schema_only",
// ^ Use schema_only since initial load done via FDW
"log.mining.strategy": "online_catalog",
"database.history.kafka.bootstrap.servers":
"kafka:9092"
}
CDC Tools Comparison
| Tool | Transport | Latency |
|---|---|---|
| Debezium | Kafka topics | < 1 second |
| AWS DMS | DMS replication | Seconds |
| GoldenGate | Direct / Kafka | Sub-second |
| pglogical | Logical replication | Sub-second |
| Striim | Streaming | Sub-second |
Oracle Prerequisites
Oracle — Enable supplemental logging
-- Enable at database level ALTER DATABASE ADD SUPPLEMENTAL LOG DATA; -- Enable for all columns on migrated tables ALTER TABLE app.customers ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS; -- Create LogMiner user for Debezium CREATE USER logminer_user IDENTIFIED BY 'secure_pass'; GRANT LOGMINING TO logminer_user; GRANT SELECT ON V_$DATABASE TO logminer_user; GRANT SELECT ON V_$LOG TO logminer_user;
Phase 06 · Data Transformation
Data Transformation
Step-by-Step Process
- 01Convert Oracle DATE columns (with time) to TIMESTAMP in PostgreSQL
- 02Transform CHAR(n) trailing spaces to trimmed TEXT values
- 03Convert RAW/BLOB binary data to BYTEA format using encode/decode
- 04Reformat NUMBER(1,0) boolean columns to PostgreSQL BOOLEAN
- 05Handle NULL semantics — Oracle treats '' as NULL; PostgreSQL does not
- 06Normalise character encoding — ensure UTF-8 consistency throughout
- 07Transform NLS date format strings to PostgreSQL to_char format codes
Transformation Queries
PostgreSQL — Common transformation fixes
-- Fix empty string vs NULL (Oracle vs PG difference) UPDATE customers SET middle_name = NULL WHERE middle_name = ''; -- Convert NUMBER(1) 0/1 to BOOLEAN ALTER TABLE users ADD COLUMN is_active_bool BOOLEAN; UPDATE users SET is_active_bool = (is_active = 1); ALTER TABLE users DROP COLUMN is_active; ALTER TABLE users RENAME is_active_bool TO is_active; -- Trim CHAR(n) trailing spaces UPDATE products SET category = RTRIM(category) WHERE category <> RTRIM(category);
Critical Differences to Handle
Empty String = NULL
Oracle: INSERT '' stores NULL. PostgreSQL: '' is an empty string. All empty string → NULL conversions must be explicit.
Oracle DATE has TIME
Oracle DATE = date + time. PostgreSQL DATE = date only. Always map Oracle DATE to PostgreSQL TIMESTAMP.
CHAR Padding
Oracle CHAR(10) pads with spaces. PostgreSQL CHAR(10) also pads. But comparisons may differ. Use TEXT and TRIM.
NLS Settings
Oracle NLS_DATE_FORMAT affects implicit date conversions. PostgreSQL uses DateStyle. Set explicitly in session or config.
Phase 07 · Data Validation
Data Validation
Step-by-Step Process
- 01Row count validation — every table must match exactly between Oracle and PostgreSQL
- 02Checksum validation — hash aggregate of key columns to detect data corruption
- 03Business rule validation — run critical business queries on both DBs, compare results
- 04Referential integrity check — all FK relationships still hold after migration
- 05Run application regression test suite against PostgreSQL target
- 06Performance baseline — top 50 critical queries must run within 110% of Oracle time
- 07UAT (User Acceptance Testing) — business stakeholders validate key reports and workflows
- 08Sign-off document from all teams before scheduling cutover
Validation Scripts
Row count + checksum validation
-- Oracle: generate row counts SELECT table_name, num_rows -- from stats FROM dba_tables WHERE owner = 'APP_OWNER' ORDER BY table_name; -- PostgreSQL: match row counts SELECT schemaname, tablename, n_live_tup AS est_rows, pg_size_pretty( pg_total_relation_size(schemaname||'.'||tablename) ) AS size FROM pg_stat_user_tables ORDER BY n_live_tup DESC; -- Exact count comparison query (both DBs) SELECT (SELECT COUNT(*) FROM oracle_staging.customers) AS oracle_cnt, (SELECT COUNT(*) FROM customers) AS pg_cnt, (SELECT COUNT(*) FROM oracle_staging.customers) - (SELECT COUNT(*) FROM customers) AS difference;
STOP if Count Mismatch
ANY row count mismatch must be investigated and resolved before cutover. Zero tolerance for data loss.
Validation Report
Generate a signed validation report. Business, DBA, and Dev leads must sign off before go-live is approved.
Phase 08 · Cutover & Go-Live
Cutover & Go-Live
Cutover Runbook — Step by Step
- 01Schedule maintenance window (even for zero-downtime: plan a 15-min rollback window)
- 02Freeze application — put app into read-only mode or enable maintenance page
- 03Wait for CDC lag to reach ZERO — confirm PostgreSQL is 100% caught up
- 04Run final row count validation across all critical tables
- 05Stop CDC / Debezium connector — no more changes from Oracle
- 06Update connection pool / application config to point to PostgreSQL
- 07Update DNS or load balancer endpoint to PostgreSQL cluster
- 08Remove maintenance page — release application traffic to PostgreSQL
- 09Monitor: query performance, error rates, connection counts for 4 hours
- 10Keep Oracle in READ-ONLY for 48 hours as fallback. Then decommission.
Cutover Strategies
| Strategy | Downtime | Risk |
|---|---|---|
| Blue-Green Switch | ~5 minutes | Low — instant rollback |
| DNS Cutover | ~0 (TTL-based) | Medium — TTL delay |
| Connection Pool Redirect | Zero | Low — pgBouncer target switch |
| Feature Flag Rollout | Zero | Very Low — gradual traffic shift |
Rollback Plan
Rollback — Switch back to Oracle
-- If rollback needed within 48h of cutover: -- Step 1: Re-enable Oracle as write target -- Step 2: Reverse CDC: PostgreSQL → Oracle -- (Use pglogical or custom triggers) -- Step 3: Update connection string back to Oracle -- Step 4: Investigate root cause -- Step 5: Fix and re-attempt cutover -- PostgreSQL: check if system is healthy SELECT count(*), state FROM pg_stat_activity WHERE datname = current_database() GROUP BY state; -- Monitor error rate after cutover SELECT query, calls, mean_exec_time, stddev_exec_time FROM pg_stat_statements WHERE mean_exec_time > 1000 -- queries > 1 second ORDER BY mean_exec_time DESC LIMIT 20;
Migration Timeline — Typical Schedule
Timeline varies by database size and complexity score
Week 1–2
Assessment
- Ora2Pg assessment run
- Object inventory
- AWR analysis
- Team formation
- Target env setup
Week 3–4
Schema & Code
- DDL conversion
- PL/SQL → PL/pgSQL
- Package decomposition
- Jobs → pg_cron
- Dev env testing
Week 5–6
Initial Load
- FDW / DMS setup
- Full data transfer
- Index rebuild
- VACUUM ANALYZE
- First count check
Week 7–8
CDC & Testing
- Debezium CDC live
- Regression testing
- Performance tuning
- UAT round 1
- Lag monitoring
Week 9–10
Validation
- Full row counts
- Checksum validation
- Business UAT
- Perf benchmarks
- Sign-off obtained
Week 11
Cutover
- Final rehearsal
- Maintenance window
- DNS/pool switch
- 4-hour monitoring
- Oracle standby 48h
Key Considerations
Critical factors that determine migration success
Schema Design Differences
PostgreSQL and Oracle handle schema objects differently. Plan for each of these conversions during design phase.
- Oracle PACKAGES → PostgreSQL schemas + grouped functions
- Oracle SYNONYMS → search_path or views
- Oracle DATABASE LINKS → postgres_fdw extension
- Oracle ROWID → PostgreSQL ctid (not recommended for app use)
- Oracle GLOBAL TEMP TABLE → PostgreSQL temp tables (different scope)
Performance Tuning After Migration
PostgreSQL's query planner differs from Oracle CBO. Queries that were fast in Oracle may need index or query tuning.
- Run VACUUM ANALYZE immediately after data load
- Review top 50 AWR queries — recreate execution plans
- Set work_mem per query type (joins, sorts)
- Use pg_stat_statements to find slow queries post-migration
- Tune shared_buffers (25% RAM), effective_cache_size (75% RAM)
Transaction & Locking Differences
PostgreSQL uses MVCC — readers never block writers. But locking semantics differ in important ways.
- Oracle READ COMMITTED ≈ PostgreSQL READ COMMITTED (similar)
- Oracle uses row-level locking; PG uses page-level for some ops
- PostgreSQL DDL (ALTER TABLE) takes AccessExclusiveLock — plan carefully
- Use SKIP LOCKED for queue-style applications
- Dead tuples accumulate — autovacuum must be properly configured
Security & Authentication
Map Oracle user/role model to PostgreSQL's role-based access control system.
- Oracle PROFILE → PostgreSQL pg_hba.conf + scram-sha-256
- Oracle VPD (Row-Level Security) → PostgreSQL RLS policies
- Oracle Fine-Grained Auditing → pgaudit extension
- Oracle Transparent Data Encryption → PostgreSQL TDE (pg_tde ext) or filesystem encryption
- Map Oracle CONNECT role to PostgreSQL LOGIN privilege
Data Validation Strategy
Never skip data validation. A systematic approach ensures zero data loss and business continuity.
- Level 1: Row counts per table (automated, every 6 hours during CDC)
- Level 2: Checksum on key columns (MD5 aggregate comparison)
- Level 3: Business rule validation (report outputs match exactly)
- Level 4: Application regression test suite (>95% pass rate required)
- Level 5: UAT sign-off by business stakeholders
Essential PostgreSQL Extensions
Install these extensions immediately after setting up the target PostgreSQL instance.
- oracle_fdw — connect to Oracle during migration and for FDW
- pg_cron — replace Oracle DBMS_SCHEDULER jobs
- pgaudit — audit logging for compliance (replaces Oracle FGA)
- pg_stat_statements — query performance monitoring
- pg_partman — automatic partition management
- PostGIS — if Oracle SDO_GEOMETRY spatial data exists
High-Risk Conversion Items
These Oracle features require the most manual effort and testing. Allocate 3x more time than expected.
- CONNECT BY PRIOR → WITH RECURSIVE (complex rewrites)
- Oracle PACKAGES with package-level variables (state)
- BULK COLLECT / FORALL bulk operations
- Oracle-specific analytic functions (FIRST_VALUE IGNORE NULLS etc.)
- Oracle MERGE statement → PostgreSQL INSERT ON CONFLICT
- DBMS_CRYPTO / UTL_FILE / UTL_HTTP → extension alternatives
Rollback & Contingency Planning
Always have a tested rollback plan. The ability to roll back gives confidence to proceed with cutover.
- Keep Oracle in READ-ONLY for minimum 48 hours post-cutover
- Define clear rollback triggers: error rate >5%, response time >3x baseline
- Test rollback procedure in full rehearsal before real cutover
- Reverse CDC capability: PostgreSQL → Oracle (for data written post-cutover)
- Maintain application dual-write capability for first 24 hours if possible
Oracle → PostgreSQL Object Mapping
Complete reference for every Oracle object type
| Oracle Object | PostgreSQL Equivalent | Effort & Notes |
|---|---|---|
| TABLE | TABLE | Low — Ora2Pg auto-converts types |
| INDEX | INDEX (B-Tree / GIN / GiST) | Low — same concept, different type options |
| SEQUENCE | SEQUENCE / IDENTITY column | Low — GENERATED ALWAYS AS IDENTITY preferred |
| VIEW | VIEW | Low — mostly auto-converts |
| MVIEW | MATERIALIZED VIEW | Medium — refresh syntax differs slightly |
| PROCEDURE | FUNCTION (PL/pgSQL) | Medium — syntax differences, no PRAGMA |
| FUNCTION | FUNCTION (PL/pgSQL) | Medium — RETURN vs RETURNS keyword |
| PACKAGE | SCHEMA + grouped FUNCTIONs | High — no direct equivalent; package state lost |
| TRIGGER | TRIGGER + FUNCTION | Medium — PG triggers call a separate function |
| SYNONYM | search_path / VIEW / ALTER TABLE SET SCHEMA | Medium — per use-case |
| DATABASE LINK | postgres_fdw FOREIGN SERVER | Medium — FDW setup required |
| PARTITION (RANGE/LIST) | Declarative PARTITION BY RANGE/LIST | Medium — syntax different, INTERVAL → pg_partman |
| DBMS_SCHEDULER JOB | pg_cron extension | Medium — cron syntax, different approach |
| DBMS_OUTPUT | RAISE NOTICE / RAISE LOG | Low — simple replacement |
| CONNECT BY PRIOR | WITH RECURSIVE CTE | High — complete query rewrite needed |
| ROWNUM | LIMIT / ROW_NUMBER() OVER() | Medium — pattern-specific rewrite |
| DECODE() | CASE WHEN ... THEN ... END | Low — straightforward replacement |
| NVL(a,b) | COALESCE(a,b) | Low — direct replacement |
| SYSDATE | NOW() / CURRENT_TIMESTAMP | Low — direct replacement |
| SUBSTR(s,1,n) | SUBSTRING(s,1,n) or SUBSTR(s,1,n) | Low — PostgreSQL supports both |
| TO_CHAR(date,'fmt') | TO_CHAR(date,'fmt') — format codes differ | Medium — format string differences (MM vs MM is same, but MI vs MM for minutes) |
| MERGE INTO | INSERT ... ON CONFLICT DO UPDATE | Medium — syntax completely different |
| BULK COLLECT / FORALL | Set-based SQL / UNNEST() | High — redesign as set operations |
| VPD (Fine-Grained Security) | Row-Level Security (RLS) | Medium — different implementation approach |
| Oracle Text (Full-Text) | tsvector / tsquery / GIN index | Medium — native PG full-text search |
Migration Checklist
Click each item to mark as complete — track your progress
✦ Pre-Migration
✓
Ora2Pg assessment completed — migration score documented
✓
All Oracle objects inventoried (tables, procs, packages, triggers)
✓
AWR top 50 SQL captured and reviewed
✓
Target PostgreSQL instance provisioned and configured
✓
Extensions installed: oracle_fdw, pg_cron, pgaudit, pg_stat_statements
✓
Migration team identified and roles assigned
✓
Rollback plan documented and tested
✓
Cutover strategy agreed (Blue-Green / DNS / Pool)
✓
Oracle supplemental logging enabled
✓
Schema conversion completed and tested on DEV environment
✓
PL/SQL → PL/pgSQL conversion done and unit tested
✓
pg_hba.conf and postgresql.conf tuned for production load
✦ Migration & Go-Live
✓
Initial full data load completed successfully
✓
All indexes rebuilt and VACUUM ANALYZE run on all tables
✓
CDC / Debezium running — lag monitored < 30 seconds
✓
Row count validation passed for ALL tables
✓
Checksum validation passed for all critical tables
✓
Application regression test suite: >95% pass rate
✓
UAT completed — business stakeholders signed off
✓
Performance benchmarks within 110% of Oracle baseline
✓
Cutover rehearsal completed — timed and documented
✓
Cutover executed — app traffic on PostgreSQL
✓
4-hour post-cutover monitoring completed — no critical issues
✓
Oracle kept in READ-ONLY for 48h then decommissioned
Post a Comment: