Saturday, July 11, 2026

Oracle to PostgreSQL Migration Architecture | AIDBAHUB
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
Foundation Phase
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
ToolPurposeLicense
Ora2PgFull assessment + schema/code exportFree (GPL)
AWS SCTSchema Conversion Tool with reportFree (AWS)
exachkOracle health check pre-migrationMOS download
AWR ReportsWorkload analysis, top SQL captureOracle Diag Pack
Phase 02 · Schema Conversion
Schema Conversion — Ora2Pg
DDL Migration
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 TypePostgreSQL TypeNotes
NUMBER(p,s)NUMERIC(p,s)Exact match
VARCHAR2(n)TEXT or VARCHAR(n)TEXT preferred
DATETIMESTAMPOracle DATE has time!
CLOBTEXTNo size limit in PG
BLOBBYTEABinary storage
RAW(n)BYTEABinary equivalent
NVARCHAR2TEXTPG is always Unicode
FLOAT / BINARY_FLOATDOUBLE PRECISIONApproximate numeric
XMLTYPEXMLNative XML type
SDO_GEOMETRYGEOMETRY (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
Highest Effort
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
Bulk Transfer
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
MethodSpeedBest For
oracle_fdw INSERT...SELECTMediumDirect transfer, small-medium tables
ora2pg COPY formatFastAny size, file-based
AWS DMS Full LoadVery FastAWS-hosted environments
Oracle Data Pump + pg_restoreMediumAir-gapped networks
pgloaderFastParallel, 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
Zero Downtime Key
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
ToolTransportLatency
DebeziumKafka topics< 1 second
AWS DMSDMS replicationSeconds
GoldenGateDirect / KafkaSub-second
pglogicalLogical replicationSub-second
StriimStreamingSub-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
Quality Gate
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
Go/No-Go Gate
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
D-Day
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
StrategyDowntimeRisk
Blue-Green Switch~5 minutesLow — instant rollback
DNS Cutover~0 (TTL-based)Medium — TTL delay
Connection Pool RedirectZeroLow — pgBouncer target switch
Feature Flag RolloutZeroVery 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 ObjectPostgreSQL EquivalentEffort & Notes
TABLETABLELow — Ora2Pg auto-converts types
INDEXINDEX (B-Tree / GIN / GiST)Low — same concept, different type options
SEQUENCESEQUENCE / IDENTITY columnLow — GENERATED ALWAYS AS IDENTITY preferred
VIEWVIEWLow — mostly auto-converts
MVIEWMATERIALIZED VIEWMedium — refresh syntax differs slightly
PROCEDUREFUNCTION (PL/pgSQL)Medium — syntax differences, no PRAGMA
FUNCTIONFUNCTION (PL/pgSQL)Medium — RETURN vs RETURNS keyword
PACKAGESCHEMA + grouped FUNCTIONsHigh — no direct equivalent; package state lost
TRIGGERTRIGGER + FUNCTIONMedium — PG triggers call a separate function
SYNONYMsearch_path / VIEW / ALTER TABLE SET SCHEMAMedium — per use-case
DATABASE LINKpostgres_fdw FOREIGN SERVERMedium — FDW setup required
PARTITION (RANGE/LIST)Declarative PARTITION BY RANGE/LISTMedium — syntax different, INTERVAL → pg_partman
DBMS_SCHEDULER JOBpg_cron extensionMedium — cron syntax, different approach
DBMS_OUTPUTRAISE NOTICE / RAISE LOGLow — simple replacement
CONNECT BY PRIORWITH RECURSIVE CTEHigh — complete query rewrite needed
ROWNUMLIMIT / ROW_NUMBER() OVER()Medium — pattern-specific rewrite
DECODE()CASE WHEN ... THEN ... ENDLow — straightforward replacement
NVL(a,b)COALESCE(a,b)Low — direct replacement
SYSDATENOW() / CURRENT_TIMESTAMPLow — 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 differMedium — format string differences (MM vs MM is same, but MI vs MM for minutes)
MERGE INTOINSERT ... ON CONFLICT DO UPDATEMedium — syntax completely different
BULK COLLECT / FORALLSet-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 indexMedium — 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: