TL;DR
- Size the migration by PL/SQL and application SQL volume. Code volume sets the timeline, the risk, and the test plan.
- Test for silent behavior differences first. Empty strings, NULL concatenation, and Oracle DATE values behave differently on PostgreSQL without raising an error, so each one needs an explicit test case.
- Treat application conversion as its own phase. Java and .NET data access, embedded and dynamic SQL, batch jobs, and reports all carry Oracle dialect that schema converters never read.
- Pick the data load method from the downtime SLA. A maintenance window with a bulk load is the safer default. Use change data capture when the business cannot accept that window.
- Validate converted code and business outputs before cutover. Run functions, procedures, and reports against the same inputs on both engines and compare the results line by line.
What does an Oracle to PostgreSQL Migration Involve?
An Oracle to PostgreSQL migration moves a database estate’s schema, PL/SQL code, data, and every piece of application code that talks to Oracle onto PostgreSQL, and proves the result behaves the same. Converter tools automate most schema translation and the data copy, and leave stored code, application SQL, and silent engine differences to manual work.
AWS’s Oracle to PostgreSQL walkthrough states that its Database Migration Service does not migrate secondary indexes, sequences, default values, stored procedures, triggers, synonyms, or views [1]. Those objects go through a separate schema conversion tool.
This guide is for engineering leaders, data platform leads, and architects scoping an Oracle exit, and for the CIOs and CTOs deciding whether to fund one.
The conversion work is the same whether PostgreSQL runs on-premises or as a managed service in any cloud. An Oracle exit is a database modernization program on any target, and hosting is a separate decision.
Teams leaving Oracle at the runtime layer as well often pair the database move with an Oracle Java to OpenJDK migration.
PostgreSQL as an Oracle Replacement: Go/No-Go Decision by Workload
PostgreSQL can replace Oracle for a wide range of transactional and reporting workloads. Make the go/no-go decision per workload, since one blocker in one application can change the economics of a portfolio-wide move.
| Disposition | When the Workload Points to It |
| Move | Standard SQL and PL/SQL, a supported application stack, no hard dependency on Oracle-only features |
| Rewrite into the application tier | Heavy PL/SQL packages holding session state, where the logic needs rework anyway and belongs in Java or .NET services |
| Retire | Low usage, duplicated capability, or reports that can move to an existing platform |
| Stay on Oracle | A vendor application certified only on Oracle, an availability design built on RAC, or deep use of Oracle-only features with no acceptable replacement |
Three blockers override licensing math in go/no-go calls.
- Vendor certification. A packaged application supported only on Oracle stays on Oracle, whatever the conversion cost. Moving it voids vendor support and puts every future upgrade on the internal team.
- RAC-dependent availability. Applications designed around Oracle RAC’s shared-disk clustering need a new availability design on PostgreSQL, usually streaming replication with automated failover. Scope the replacement as a separate architecture workstream.
- Deep package state and Oracle-only features. Heavy use of package variables, Advanced Queuing, or APEX requires redesign of the affected modules. Oracle Spatial maps to PostGIS with function-level rewrites.
The business case nets licensing savings against conversion effort, test effort, and the cost of running two engines during the transition.
What Has to Move in an Oracle to Postgres Migration
An Oracle to Postgres migration touches six layers. Tool coverage is high for schema and data, partial for PL/SQL, and low for every layer that sits outside the database.
| Layer | What Moves | Typical Tool Coverage | Where the Risk Sits |
| Schema and data types | Tables, indexes, constraints, sequences, views, types | High. Open-source, commercial, and cloud tools all convert DDL | Type choices such as NUMBER and DATE that compile cleanly and change behavior or performance |
| PL/SQL | Packages, procedures, functions, triggers | Partial. Routine logic converts. State, transactions, and dynamic SQL need rework | Package state, autonomous transactions, built-in packages |
| Data | Rows, LOBs, sequence values | High. Bulk load and CDC tools are mature | Precision, character set, and empty-string handling during the load |
| Application SQL and data access | Java JDBC and JPA/Hibernate, .NET ADO.NET, embedded and dynamic SQL, ORM dialect settings | Low. Schema converters rarely read application code | Oracle syntax inside strings, hints, ROWNUM paging, error-code handling |
| Batch, reports, and scripts | Shell and SQL*Plus scripts, SQL*Loader control files, scheduler jobs, report queries | Low | Jobs that fail silently or run at the wrong time |
| Integrations | Database links, feeds, ETL jobs, downstream consumers | Low | Consumers that break at cutover and surface days later |
The application layer is where Oracle migrations overrun. Oracle dialect sits in Java strings, ORM configuration, report definitions, and shell scripts spread across repositories the database team does not own.
Little of it appears in a schema conversion report, so it has to be inventoried separately before any estimate is credible.

Oracle to Postgres Migration Challenges, Ranked by Risk
Oracle to Postgres migration challenges are easier to plan for when ranked by how they fail. Silent differences rank first, because converted code runs and returns wrong results. Conversion hotspots rank second, because they fail loudly and absorb the manual effort. Performance ranks third, because it surfaces late, under production load.
Silent Behavioral Differences Between Oracle and PostgreSQL
Each difference below passes compilation and routine functional tests and still changes query results. The table gives the business impact, and the SQL under each item shows the difference on both engines.
| Difference | Oracle Result | PostgreSQL Result | Business Impact |
Empty string ('') | Stored as NULL | Stored as a value | Required-field checks and missing-value reports stop matching |
| NULL inside text concatenation | NULL is skipped | Whole result becomes NULL | Names, addresses, and labels built from parts go missing |
| DATE column mapped to DATE | Keeps date and time | Keeps date only | Timestamps lose their time of day with no error |
Empty strings are values in PostgreSQL. Required-field checks and missing-value reports stop matching after cutover. Oracle treats a zero-length string as NULL [2], and PostgreSQL stores it as an empty string.
-- tax_code is NOT NULL on both engines
INSERT INTO invoice (id, tax_code) VALUES (1, '');
-- Oracle: rejected (ORA-01400)
-- PostgreSQL: accepted
-- After cutover, the app writes '' for "no middle name"
SELECT COUNT(*) FROM customer
WHERE middle_name IS NULL;
-- Oracle: counts those rows
-- PostgreSQL: skips themThe fix is one rule for the target, applied to data, constraints, and code together. Either convert empty strings to NULL at write time, with NULLIF in code or a trigger, or keep them and rewrite every IS NULL check to cover both cases.
NULL concatenation returns NULL. A full name or address built from parts returns NULL for every row with a missing part. Oracle’s concatenation operator ignores NULL operands [2], and PostgreSQL’s || returns NULL when any operand is NULL.
-- Oracle
SELECT 'Order ' || NULL || ' shipped' FROM dual;
-- Result: 'Order shipped'
-- PostgreSQL
SELECT 'Order ' || NULL || ' shipped';
-- Result: NULLReplace these expressions with concat() or concat_ws(), which skip NULL arguments, or wrap each operand in COALESCE.
Oracle DATE loses its time of day. Order, event, and audit timestamps lose their time with no error. Oracle DATE stores the date and the time to the second, and PostgreSQL DATE stores the date only. Converters default to a timestamp type, so the risk comes from hand-written or overridden DATE-to-DATE mappings.
-- Oracle
SELECT TO_CHAR(order_date, 'YYYY-MM-DD HH24:MI:SS')
FROM orders WHERE id = 42;
-- Result: 2026-09-24 14:35:10
-- PostgreSQL, column mapped to DATE
SELECT order_date FROM orders WHERE id = 42;
-- Result: 2026-09-24Map Oracle DATE to timestamp(0) unless profiling confirms a column never carries a time, and check custom type maps before the first load. Date arithmetic changes too, since Oracle date subtraction returns a number of days and PostgreSQL timestamp subtraction returns an interval.
PL/SQL Constructs That Need Manual Conversion
Converters translate routine PL/SQL well. Manual effort clusters in five constructs, and a count of each in the inventory is a stronger effort signal than line count alone.
- Packages and package variables. PostgreSQL has no packages. Its documentation recommends schemas to group functions and notes that there are no package-level variables either [3]. Session state held in package variables has to move to temporary tables, custom configuration settings, or the application tier.
- Autonomous transactions. PostgreSQL has no
PRAGMA AUTONOMOUS_TRANSACTION. Audit and error-logging routines that commit independently needdblink,pg_background, or a redesign. Both extensions run the work in a separate session, which adds connection or worker overhead. - Dynamic SQL built in strings. SQL assembled at runtime is invisible to static converters. It converts only when someone reads the string-building logic. PostgreSQL’s
EXECUTEtakes values throughUSING, and identifiers needquote_identorformat()[3]. - Hierarchical and paging queries.
CONNECT BYrewrites toWITH RECURSIVE. Oracle assignsROWNUMbeforeORDER BY, so a mechanical swap toLIMITcan change which rows a page returns. - Built-in packages.
UTL_FILEandDBMS_SCHEDULERhave no core equivalents, and file handling and scheduling logic usually move out of the database.DBMS_OUTPUTand mostDBMS_LOBcalls map toRAISE NOTICEand native string and bytea functions, and the Orafce extension covers part of the rest.
Oracle to PostgreSQL Performance Differences
Three causes recur behind post-cutover performance regressions.
- Planner differences. PostgreSQL’s planner makes different join and index choices from Oracle’s optimizer on the same query. Statistics targets,
work_mem, and index design need tuning per workload. - Oracle hints stop working. Inline
/*+ INDEX */hints are comments to PostgreSQL, and the project has stated it will not implement hints the way other databases do [4]. Thepg_hint_planextension and thepg_plan_advicemodule arriving in PostgreSQL 19 use their own syntax, so hinted queries need rewriting or new indexes. - NUMBER mapped to NUMERIC everywhere. Oracle NUMBER columns used as keys and counters perform better as
integerorbigint. A blanketNUMERICmapping slows joins, grows indexes, and forces casts in application code.
Capture a baseline of the top queries and batch windows on Oracle before conversion. Replay the same workload on PostgreSQL before cutover, when regressions can be fixed without user impact.
Start a $0 Modernization Assessment


+moreOracle to PostgreSQL Converter Options, Including AI-Assisted Conversion
An Oracle to PostgreSQL converter handles schema translation well and PL/SQL partially. The table lists what each category converts and what it leaves for manual work.
| Category | Converts | Leaves Behind | Enough When |
| Open-source (Ora2Pg) | Schema, data, PL/SQL through pattern-based rewrites, plus a migration cost report | Package state, autonomous transactions, dynamic SQL, application code | Standard schemas, moderate PL/SQL, a team comfortable reviewing the output |
| Commercial converters | Schema and PL/SQL with broader rule sets, some embedded SQL in application code | SQL built at runtime, business-logic review, validation | Large PL/SQL estates where wider rule coverage saves manual hours |
| Cloud-native toolkits (AWS SCT and DMS, Azure tooling) | Schema conversion with action items, data movement, CDC | Application SQL built at runtime, validation beyond the data. Azure’s application conversion is in preview | The target is that provider’s managed PostgreSQL |
| Query-only converters | Individual SQL statements | Stored procedures, triggers, PL/SQL, data | Converting ad hoc queries and report SQL |
| AI-assisted conversion | DDL, PL/SQL, and application SQL across languages, with explanations | Proof of equivalence, concurrency and state semantics | Paired with compile checks, output comparison, and human review |
Automation rates published by vendors count code objects converted, which is a different unit from effort or validated behavior. The PostgreSQL wiki’s tool directory lists Cortex’s claim of an average of 80% of code objects converted automatically [5]. Package state, dynamic SQL, and built-in packages concentrate in the remaining objects, so they carry a larger share of the manual hours.
Ora2Pg is the community default. It is free and maintained, converts schema and data reliably, and is a sound starting point before any commercial tool is evaluated.
How to Migrate Data From Oracle to Postgres
The method used to migrate data from Oracle to Postgres depends on the downtime the business will accept at cutover.
| Scenario | Method | Why |
| A window of several hours is acceptable | Export and bulk load with COPY, or Ora2Pg’s data export, during a maintenance window | Fewest moving parts, one consistent snapshot, simplest validation |
| Large volume, short window | Bulk load in advance, then change data capture to replay changes until cutover | Moves most of the data before the window and keeps the window short |
| Near-zero downtime is a contractual SLA | CDC with a rehearsed cutover and a defined rollback path | Adds a replication pipeline to build, monitor, and validate, so it needs a business reason |
| Several applications share one schema | Phase by application after coupling is mapped | Shared tables force a single cutover until access is split |
A maintenance window is the lower-risk default. A zero downtime migration adds design, monitoring, and testing cost that needs a contractual SLA behind it.
Three data issues surface during the load itself.
- Precision. NUMBER columns without declared precision can hold values a narrower target type rounds or rejects.
- Character sets. Legacy Oracle databases in single-byte character sets are usually converted to UTF-8 during the move, and invalid bytes stop the load.
- LOBs. CLOB and BLOB columns map to
textandbytea, and their size drives load time more than row count does. Values over 1 GB need PostgreSQL large objects or external storage.
Oracle to PostgreSQL Migration Validation for Data, Code, and Business Outputs
Validation for an Oracle to PostgreSQL migration runs at three levels. Row counts and checksums cover data, and code and business-output comparisons cover behavior, since every silent difference above passes a row count.
- Data. Row counts per table and partition, then column-level hashes computed after normalizing types and timestamp precision on both sides. Empty-string handling is checked as its own test.
- Converted code. Each converted function, procedure, and trigger runs against the same inputs on Oracle and PostgreSQL, and the outputs are compared. The empty-string, concatenation, and DATE cases become fixed test cases.
- Business outputs. Reports, batch results, invoices, and exports generated from both systems for the same period are compared line by line. A report total that differs by one cent points to a rounding or type issue upstream.
Data-level checks follow the same techniques used in any data migration validation, applied table by table. Code and business-output comparison is the part specific to an engine change, and it needs the Oracle system available as a reference until sign-off.
Map Where Behavioral Parity Risk Concentrates
Behavioral parity across converted PL/SQL and application code takes the most evidence in an Oracle exit. A $0 Modernization Assessment maps where that risk concentrates in your codebase before conversion starts.
Oracle to Postgres Migration Step by Step
The eight phases below lay out an Oracle to Postgres migration step by step. Phases 1, 5, and 7 carry the most unplanned effort when they are compressed.
- Inventory and effort sizing. Count PL/SQL objects by type, packages with state, dynamic SQL, and built-in package calls, plus every application, script, and job that connects to Oracle. A legacy system audit covers the application side of this inventory.
- Go/no-go per workload. Assign move, rewrite, retire, or stay to each application before conversion budget is committed.
- Schema and type mapping. Decide the DATE, NUMBER, empty-string, and identifier-case rules once, and apply them to schema, code, and data together. Keep identifiers unquoted and lowercase, since quoted uppercase names in converted DDL break application SQL.
- PL/SQL conversion. Run the converter, then work the hotspot list. Decide per package whether logic stays in PL/pgSQL or moves to the application tier.
- Application conversion. Treat Java and .NET data access, embedded SQL, ORM dialect settings, reports, and scripts as a separate workstream with its own owners and tests.
- Data rehearsal loads. Run full loads into a test environment at least twice, timing each one and validating at all three levels.
- Performance baselining. Replay the top queries and batch windows captured on Oracle and fix regressions before cutover.
- Cutover and rollback. Freeze Oracle writes, run the final load or CDC switch, validate, and keep a tested path back to Oracle until business outputs are signed off.
The phases overlap. Application conversion depends on the naming and type rules from phase 3, so starting it before those rules settle produces code that targets objects still in flux.

Gen AI for Oracle to PostgreSQL Code Conversion and Output Verification
Gen AI speeds up pattern-heavy Oracle to PostgreSQL conversion spread across many files, with verification designed in for logic that depends on runtime state.
AI conversion performs well on three kinds of work.
- Translating PL/SQL syntax and idioms, such as
NVLtoCOALESCE,DECODEtoCASE, and sequence calls. - Finding and rewriting Oracle SQL embedded in Java, .NET, scripts, and report definitions.
- Explaining what a legacy package does, which shortens human review.
AI conversion needs human verification in four areas.
- Package state and session behavior that only appear at runtime.
- Concurrency and transaction semantics, including autonomous transactions and locking.
- SQL assembled from strings at runtime, where the final statement never appears in source.
- Semantic differences with no syntax change, such as empty-string handling.
Microsoft’s PostgreSQL extension for VS Code runs schema conversion first and application conversion second, compiles converted DDL against a scratch database, and raises review tasks for objects that need human judgment [6]. Its application conversion is still a preview feature.
AI-generated PL/pgSQL and application SQL should clear three gates before merge. The code compiles, it returns the same outputs as Oracle on the same inputs, and an engineer reviews the diff. Programs that repeat these gates across teams formalize them as safety nets for Gen AI in legacy modernization, with compile, parity, and review checks enforced in the pipeline.
How Long an Oracle to PostgreSQL Migration Takes
Published estimates for an Oracle to PostgreSQL migration vary by orders of magnitude, because they measure different things. Schema and data for a small, standard database can move in days, and a PL/SQL-heavy estate with several applications on top can run for many months.
Effort follows code volume and complexity. Seven drivers explain the spread.
- PL/SQL line count, and the share held in packages with session state.
- Dynamic SQL and built-in package calls.
- Applications, scripts, and jobs that connect to Oracle.
- How application SQL is written, whether through an ORM, stored-procedure calls, or hand-built strings.
- Integrations and downstream consumers to re-point.
- Performance-critical queries and batch windows to re-tune.
- The validation depth the business requires before sign-off.
Ora2Pg’s assessment report turns the first two drivers into a number. It scores objects in cost units of five minutes by default and moves an estate from grade B to grade C once estimated rewriting passes 10 person-days [7]. Grade A means the migration can largely run automatically, and technical levels from 1 to 5 describe how much stored code and trigger rewriting is involved.
The report covers database objects only. Application SQL, scripts, and integrations need their own count, and on application-heavy estates that count can exceed the database figure.
How Legacyleap’s Gen AI Agents Run an Oracle to PostgreSQL Migration
Legacyleap is a Gen AI-powered legacy modernization platform built on multi-agent orchestration. For an Oracle exit, its five agents run the whole-estate approach this guide describes across the Assess, Comprehend, Modernize, Validate, and Deploy lifecycle.
- Assessment Agent. Inventories the PL/SQL and application estate, flags hotspots such as package state and dynamic SQL, and produces a dependency map with a migration effort and timeline estimate.
- Documentation Agent. Reconstructs the business logic held in packages, procedures, and triggers, along with data flows and integration maps, including where no documentation exists.
- Recommendation Agent. Recommends per module whether logic stays in the database or moves to a Java or .NET service tier, and produces an ordered migration plan with module-level effort and risk scores.
- Modernization Agent. Executes the migration plan as diff-based pull requests, including moving PL/SQL business logic into Java or .NET services and refactoring the data-access code that calls it. Roughly 70% of the modernization work is automated, and engineers review and direct the rest.
- QA Agent. Generates unit, integration, regression, and API tests and produces behavior parity validation reports against the Oracle baseline before cutover.
No agent merges, deploys, or executes code autonomously. Every change is a diff-based pull request that an engineer reviews.
Full-codebase grounding analyzes application SQL and the PL/SQL it calls together, so cross-layer dependencies appear in one map. All processing runs inside the organization’s own infrastructure, and source code never leaves the environment. The same agents run broader data platform modernization programs when the Oracle exit is one part of a wider data estate change.
Next Steps for Planning an Oracle to Postgres Migration
Code and behavior that depend on Oracle set the scope of an Oracle to Postgres migration. Sizing the estate by PL/SQL and application SQL, testing silent differences first, and validating business outputs before cutover give the program a realistic plan and a credible sign-off.
For teams scoping an Oracle exit, the $0 Modernization Assessment produces the estate inventory, a dependency and module map, a risk and complexity heatmap, and a modernization plan for one representative codebase. It takes 3 to 5 days and runs inside the client’s environment. More than 150 production-grade assessments have been completed to date.
A technical demo shows how the five agents take an approved plan through conversion and parity validation.
Size the Oracle Estate Before Conversion Starts
A $0 Modernization Assessment produces the estate inventory, a dependency and module map, a risk and complexity heatmap, and a modernization plan for one representative codebase in 3 to 5 days, at no cost and inside your own environment.
FAQ
Only for the Oracle licenses that are terminated. A partial termination can reprice support on the licenses that remain, so plan the license exit per contract alongside the workload plan.
The standard pattern is one schema per package with a naming rule for private functions, since PostgreSQL has no package-private scope. A setup function called at session start replaces the package initialization block.
Map it to timestamp(0), then check queries that wrap the column in TRUNC(). They convert to date_trunc() and need a matching expression index to keep their Oracle-era performance.
Convert set-based data processing that sits close to the tables. Move business rules that change often, hold session state, or already need a rewrite, since Java or .NET services are easier to test and staff.
Define cutover readiness as a CDC replication lag threshold held for a set period. After the final sync, reset every PostgreSQL sequence to the source’s current value before the application writes again.
Yes, with Oracle as the system of record and one-way CDC replication to PostgreSQL until business outputs match. Avoid dual writes from the application, which create reconciliation work that outlasts the migration.
References
[1] AWS Database Migration Service Step-by-Step Walkthroughs. Migrating an Oracle Database to PostgreSQL
[2] Oracle Database SQL Language Reference 19c. Nulls
[3] PostgreSQL 18 Documentation. Porting from Oracle PL/SQL
[4] PostgreSQL Wiki. Optimizer Hints Discussion
[5] PostgreSQL Wiki. Oracle to Postgres Conversion
[6] Microsoft Learn. Migrate Oracle to PostgreSQL with the PostgreSQL extension for Visual Studio Code
[7] GitHub (darold/ora2pg). Ora2Pg README







