Generate the extraction script
pgrecon script --source-version 19 writes a SQL*Plus script tailored to your Oracle release. For 9.2 to 11.1 estates, --legacy produces a variant that runs on the older dictionary views.
Free, open-source Oracle to PostgreSQL migration reconnaissance: 80 rules, effort estimates and offline analysis.
Generated on request from the live content of this site. Nothing about it is stored ahead of time. File name Tech-Style-pgrecon-Product-Brochure.pdf.
Free, open-source migration reconnaissance. It answers the one question that decides whether an Oracle-to-PostgreSQL migration succeeds or overruns: what is actually in there, and what will it cost to move? It parses your PL/SQL with a real grammar, reports every incompatibility with the object it was found in (and the line, for code), estimates the effort in person-days, and converts the schema it can prove into PostgreSQL DDL, all without ever connecting to your database.
$ pip install pgreconOracle licensing pushes organisations towards PostgreSQL, and the schema rarely stands in the way. What sinks the timeline is twenty years of PL/SQL full of Oracle-only constructs with no direct equivalent, found in month nine because nobody measured the code before committing to a date.
pgrecon is the measurement step. It reads the code you already have, tells you which constructs will not survive the move and where each one lives, and turns that into a defensible number. Its rules are the list of things that hurt us on real migrations, heavy-industry estates on very old Oracle versions among them.
| Status | v0.7.3 on PyPI · alpha |
|---|---|
| Licence | Apache 2.0, free for commercial use |
| Rules | 80, each with fixture tests |
| Analysis | Fully offline, with no DB connection |
The only thing that runs near production is a read-only script your own DBA inspects first.
pgrecon script --source-version 19 writes a SQL*Plus script tailored to your Oracle release. For 9.2 to 11.1 estates, --legacy produces a variant that runs on the older dictionary views.
It is plain SELECT statements against the data dictionary, with nothing to approve in a change board meeting. sqlplus readonly_user@service @pgrecon_extract.sql SCHEMA_NAME produces a directory of dump files. SQL*Plus is the only requirement on the database server; no Python, no agent, no network access.
pgrecon load dump_dir --db inventory.db parses the extract into a local database: every table, index, package, trigger, view, job and dependency, with the source text of each program unit.
pgrecon report --db inventory.db walks the parse tree with all 80 rules and prints every finding: rule ID, severity from info to blocker, the object it lives in, and the evidence. Add --remedies for what to do about each one, or --format json to feed a tracker.
pgrecon estimate --db inventory.db converts the inventory and the findings into a low / expected / high range in person-days, and shows the arithmetic that produced it.
pgrecon convert --db inventory.db emits PostgreSQL DDL for everything it can carry faithfully, and a residue file naming every object it declined and why. Still offline, still from the same inventory.
pgrecon runbook --db inventory.db does not move rows. It writes what the move needs: a script that applies the converted schema, a data-only mover configuration, row-count and spot-sum validation SQL for both engines, the post-load sequence and materialised-view steps, and the cutover checklist.
You now hold a defensible scope before a single line has been ported, and the same numbers can be re-run against a later extract to show progress.
Every rule ships with fixture tests, a severity, a remediation note and a documented explanation you can read with pgrecon explain RULE_ID.
| Domain | Rules | Representative findings |
|---|---|---|
| PL/SQL code | 18 | Autonomous transactions, dynamic SQL, FORALL, collection types, the empty-string NULL trap |
| Schema objects | 14 | Database links and the remote calls made over them, scheduler jobs, materialised view logs, queues, evolved types, unparseable DDL |
| Storage | 12 | Interval partitioning, global temporary tables, IOTs, read-only tables, bitmap and function-based indexes |
| SQL constructs | 13 | CONNECT BY, Oracle outer-join syntax, ROWNUM, MERGE, SYS_CONTEXT, the MODEL clause, PIVOT, flashback queries |
| Data types | 9 | LONG, XMLTYPE, ROWID, BFILE, SDO_GEOMETRY, BYTE-semantics strings, TIMESTAMP WITH LOCAL TIME ZONE |
| System packages | 5 | UTL_FILE, UTL_HTTP / SMTP / TCP, DBMS_SQL, DBMS_LOB, DBMS_OUTPUT |
| Performance | 5 | Optimiser hints, global indexes on partitioned tables, plan baselines, query-rewrite MVs |
| Environment | 2 | Character-set encoding decision, object grants to migrate |
| Packages | 2 | Package-level state, initialisation blocks |
Each finding names the object and the location inside it, so an engineer can open the code and see the problem rather than take the tool's word for it.
Findings run from info to blocker. A blocker is something with no PostgreSQL equivalent that will require a design decision, beyond a mere syntax difference.
--remedies prints what to do about each finding: the PostgreSQL equivalent, the extension that covers it, or the redesign it forces.
If the parser cannot read a program unit, that is reported as a finding and never silently skipped. Unknown code is a risk, and it is accounted for as one.
Same dump in, same findings out. Two people running the same extract get the same report, which is what makes it usable in a commercial conversation.
--format json exports the full finding set for a backlog, a spreadsheet or a dashboard, so the assessment survives the meeting it was made for.
A synthetic estate of 5,000 tables and 100,000 lines of PL/SQL across 1,600 stored units (one of them a 16,000-line package body) loads and deep-parses in under 40 seconds on a laptop, and reports in about two. The generator ships with the source, so the measurement is reproducible rather than quoted.
An adversarial dump generator throws names past 63 bytes in ASCII and Hangul, names that collide once Oracle's namespaces fold into PostgreSQL's, reserved words as columns, every partition layout and spools in a foreign code page. CI runs a fresh window of seeds nightly and checks that every object the loader stored is either created in the DDL or named in the residue.
An estimate nobody can interrogate is worth nothing in a budget meeting. pgrecon estimate builds a low, expected and high figure in person-days from five components (setup, schema conversion, remediating the findings, porting PL/SQL by volume, and moving the data) and prints each one, so your team can argue with any line and re-run with their own assumptions.
The honest caveat is calibration: the model comes from real projects but does not know your team, your test estate or your change control. That judgement is what our assessment report adds.
# estimate from the bundled sample inventory pgrecon estimate --db sample.db Migration effort estimate (person-days) baseline and environment 5.0 schema conversion 1.7 finding remediation 74.3 PL/SQL porting by volume 0.8 data movement 0.0 development subtotal 81.7 With testing and stabilization: low 106, expected 131, high 180 person-days (5.1 to 8.6 person-months)Since v0.2 the same inventory drives a converter. pgrecon convert writes two files, offline like everything else. The first is PostgreSQL DDL for what it can carry faithfully:
The second file is the residue: one line per declined object, naming the construct and the line. Packages, CONNECT BY, autonomous transactions and BULK COLLECT are refused by name rather than guessed at, and a routine that calls a refused routine is refused with it.
# convert from the same inventory the report came from pgrecon convert --db inventory.db schema_pg.sql what converts, provably schema_residue.txt what does not, named and locatedEvery converter claims to convert an Oracle schema. The benchmark asks a narrower question: apply the tool's own output to a real PostgreSQL 16, statement by statement, and count how many statements PostgreSQL itself rejects. A missing feature or a style complaint does not count; a statement the target database refuses does. That is the bar a real migration hits in production, whether the tool warned about it or not.
| Converter | Statements rejected | Notes |
|---|---|---|
| pgrecon 0.7.3 | 0 | Of 423 statements on the eight public schemas, re-measured in CI on 2026-10-05, and 0 in the August nine-schema round. Every decline is a named residue line |
| CYBERTEC ora_migrator | 40 | Plus two whole-schema crashes; PL/SQL is out of its scope by design and is not counted against it |
| Ora2Pg v25 | 77 | Clean on the simple estates; needed two accommodations to apply at all |
| EDB Migration Toolkit 55.13 | 135 | Of 477 emitted statements, with entire package libraries omitted without a word and reported as success |
| AWS SCT | 481 | Clean on HR only; surviving objects depend on the aws_oracle_ext runtime extension |
| Dalibo PostgreSQL Migrator 1.0 | 58 | Of 445 statements on the eight public schemas, run once on 2026-09-19; emits no views or code |
One command per step, and nothing hidden behind a service.
| pgrecon script | Generate the SQL*Plus extraction script. --source-version targets your Oracle release; --legacy produces the 9.2 to 11.1 variant. |
|---|---|
| pgrecon load | Parse a dump directory into a local inventory database. --encoding handles dumps that are not UTF-8. |
| pgrecon report | Run all rules and print the findings. --remedies adds remediation guidance; --format json exports the full set. |
| pgrecon explain | Print the full documentation for a single rule ID: what it detects, why it matters and how to resolve it. With no argument it lists the whole catalogue with severities. |
| pgrecon estimate | Produce the low / expected / high effort range in person-days, with the component breakdown. |
| pgrecon convert | Emit PostgreSQL DDL for everything provably convertible, plus a residue file naming every object it declined and where it lives. |
| pgrecon runbook | Write the data-movement artifacts, offline: a schema apply script, a data-only mover configuration, row-count and spot-sum validation SQL for both engines, the post-load sequence and materialised-view steps, and the cutover checklist. |
| pgrecon info | Summarise an inventory: its metadata, a count of every object type it holds, and how much of the DDL parsed. |
pip install pgrecon # no Oracle yet? the repository carries a sample dump git clone https://github.com/Muzzammil242/pgrecon pgrecon load pgrecon/examples/dump_oracle21c --db sample.db # or run it against your own estate pgrecon script --source-version 19 # ... your DBA runs the generated script and returns dump_dir/ pgrecon load dump_dir --db inventory.db pgrecon report --db inventory.db --remedies pgrecon estimate --db inventory.db pgrecon convert --db inventory.db pgrecon runbook --db inventory.dbThe free tool answers the engineer's question: what is in there and what breaks. The assessment report answers the board's: what does this cost, how long does it take, in what order, and what could go wrong.
Typical turnaround is two weeks from receiving the dump. The fee is credited against a subsequent migration engagement if you choose to go ahead with us.
| Runtime | Python 3.11 or later, installed from PyPI with pip install pgrecon. |
|---|---|
| Source Oracle | 11.2 and later with the standard script, verified nightly in CI against Oracle XE 21c and Oracle Free 23ai. Oracle 9.2 to 11.1 via pgrecon script --legacy, itself verified nightly in CI against Oracle XE 11g. |
| Target | PostgreSQL. Rules are written against stock PostgreSQL, noting where an extension covers the gap. |
| On the database server | A SQL*Plus client (12.2 or later for the standard script) and a read-only account with catalogue access. No Python, no agent, no outbound connection. |
| Analysis store | A local single-file inventory database built from the dump. |
| Analysis method | Grammar-based parsing of DDL and PL/SQL into a parse tree; a unit the parser rejects still gets a token-level scan, so it is never skipped. |
| Output | Terminal report with severities and remedies, JSON export, per-rule explanations, effort estimate, converted PostgreSQL DDL with a named residue file, and a migration runbook. |
| Testing | Every rule carries fixture tests; the suite runs against real Oracle extracts, the converter's output is applied to live PostgreSQL 16, 17 and 18 on every commit, and a fresh window of fuzz seeds runs nightly. |
| Licence | Apache License 2.0. Issues and pull requests welcome. |
Assessment, schema and PL/SQL conversion, data movement, cutover and support.
What happens to the reporting layer once the database has moved.
Everything else we publish on PyPI, npm and GitHub.
Tech Style Ltd is a UK technology company building enterprise software, procurement intelligence and AI platforms. Tell us what you are trying to do and we will tell you honestly whether we are the right people for it.
| This document online | www.tech-style.co/product-pgrecon |
|---|---|
| Website | www.tech-style.co |
| Enquiries | hello@tech-style.co |
| Products | www.tech-style.co/products |
| Partner programme | www.tech-style.co/partners |
pgrecon: Product Brochure, typeset when you click, from the content above.