Everything behind the numbers on the pgrecon page: what ran, which versions, what each tool needed to run at all, and how to reproduce it. August 2026; the pgrecon row re-measured at 0.7.3 on 2026-10-05; Dalibo's PostgreSQL Migrator added on 2026-09-19.
Every converter claims to convert an Oracle schema. The benchmark asks one narrower question: when the tool's output is applied to a real PostgreSQL, statement by statement, how many statements does PostgreSQL itself reject? An error here is not a missing feature or a style complaint - it is a statement the target database refuses. That is the same bar a real migration hits in production, whether a tool warned about it or not.
A zero on that bar does not mean everything converts, and the number only means something next to how much was attempted; the coverage numbers are stated below with the same precision as the error counts.
tests/fixtures/synthetic/synthetic_schema.sql.Source database: Oracle XE 21c (21.3), loaded with all nine
schemas. Target: a stock postgres:16 container. No
compatibility extensions installed unless a tool's own output
requires one, in which case that is recorded below.
Two rows were measured later than the August round, in GitHub Actions with the same image and the same eight public schemas, the private lab schema excluded: pgrecon at 0.7.3 on 2026-10-05, and Dalibo's PostgreSQL Migrator 1.0.0 on 2026-09-19. The pgrecon run, its logs, and its SQL are public; the Dalibo run was a private one-off, and its job definition, log, and SQL are available on request.
| Tool | Version / mode |
|---|---|
| pgrecon | 0.7.3, pgrecon convert from an offline dump; re-measured 2026-10-05 on the eight public schemas by the repository's Benchmark workflow (the run) |
| CYBERTEC ora_migrator | current release, August 2026, over oracle_fdw 2.9.0 (Oracle Instant Client 23) |
| Dalibo PostgreSQL Migrator | 1.0.0 (released 2026-09-04) with transqlate 0.8.0; pg_migrate inspect, convert, dump --schema-only --format plain, connected as SYSTEM, scope set to the eight public schemas; measured 2026-09-19 in a private one-off run with the same job shape as the pgrecon row; the job definition, its log, and the SQL it produced are kept offline and available on request |
| Ora2Pg | v25.0, TYPE=TABLE,VIEW,SEQUENCE,TRIGGER,FUNCTION,PROCEDURE,PACKAGE |
| EDB Migration Toolkit | 55.13.0, -offlineMigration, community PostgreSQL target |
| AWS Schema Conversion Tool | current build, August 2026, via its batch CLI (.scts scripts) |
Each tool's generated SQL was applied to a fresh database in the
same PostgreSQL 16 with psql -qX and
ON_ERROR_STOP off, so every statement gets its chance;
the error count is the number of lines PostgreSQL answered with
ERROR. Function bodies were applied with
check_function_bodies on wherever the tool's own output
did not override it. Where a tool emitted per-schema files they were
applied in the tool's own recommended order.
Runs were kept as fair as each tool allows; everything a tool needed beyond its documentation is recorded here.
ON_ERROR_STOP inside itself, so as shipped it
stops at its own first error (OE stopped at statement 29); the
self-abort was stripped to measure the full count. Its
SYS_GUID mapping silently requires the
uuid-ossp extension, which was pre-created.libaio symlink for the instant client on
Debian 13). Two schemas - utPLSQL and the private lab schema -
crashed whole in db_migrate_prepare on the
staging schema's own foreign keys and were never attempted;
they are counted as crashes, not errors. PL/SQL is out of its
scope by design and is not counted against it.--force
option, which is its own partial-migration path. Nothing else
was changed. Version 1.0 marks views, materialized views,
routines, packages, triggers, types and partitions as partially
implemented, and none of them reached its schema-only dump, so
its row is tables, sequences, indexes, keys and checks. Its
PL/SQL parser could not read 16 package bodies of the corpus;
those are counted as absence, not as errors. One of its
rejected statements is PostgreSQL 17 syntax sent to the
benchmark's PostgreSQL 16. Data copy and the web UI, its
strongest parts, are outside this benchmark's question.aws_oracle_ext runtime extension - the
portability question a client actually faces. Its converted
routines create cleanly (PostgreSQL only syntax-checks
function bodies) and depend on that extension when called;
the count reflects statements rejected at apply time.| Converter | Statements rejected | Notes |
|---|---|---|
| pgrecon 0.7.3 | 0 | 423 statements over the eight public schemas, re-measured 2026-10-05 in CI; the August round on all nine was also 0; every decline a named residue line |
| CYBERTEC ora_migrator | 40 | plus two whole-schema crashes |
| Dalibo PostgreSQL Migrator 1.0 | 58 | of 445 statements on the eight public schemas: 6 tables failed to create (object-type and virtual columns, one ROWID) and 48 of the 58 are their indexes, keys and defaults failing after them; no views or code emitted |
| Ora2Pg v25 | 77 | 49 on OE, 18 on the lab schema, 8 on RECON_TEST; clean on the simple estates |
| EDB MTK 55.13 | 135 | of 477 emitted statements; the low counts on package libraries are absence, not quality |
| AWS SCT | 481 | clean on HR only; 214 on utPLSQL, 76 on Alexandria, 73 on PLJSON |
Coverage, stated with the same precision. At 0.7.3 on the eight public schemas (2026-10-05, in CI): 594 objects the converter is answerable for - tables, views, materialized views, sequences, synonyms, routines, triggers, indexes, and constraints - of which 241 were created in the emitted DDL, 41 percent; 353 were declined by name in the residue report; none were lost. Above 95 percent on HR and CO, 76 percent on RECON_TEST, 81 percent on Logger, 40 percent on OE, whose object types nothing converts mechanically, 25 percent on utPLSQL, 4 percent on PLJSON, and none on Alexandria, which is almost entirely packages; on those three the residue points at the package flattener. The August round, all nine schemas and a count over every object, put the figure at 51 percent. One hierarchical view was additionally verified row-for-row against the live Oracle running the original.
The other tools' outputs are unchanged from their August runs; none of them has released a new version since. Between full runs the converter is guarded by CI, which applies its output to PostgreSQL 16, 17, and 18 on every commit and fuzzes it nightly against adversarial schemas.
podman run -d --name oracle-xe -e ORACLE_PASSWORD=... gvenzl/oracle-xe:21 podman run -d --name pg-target -e POSTGRES_PASSWORD=... postgres:16 # install HR/OE/CO from db-sample-schemas v21.1, the four projects # from their repositories, RECON_TEST from this repo's fixture pip install pgrecon==0.7.3 # the version the pgrecon row was measured with pgrecon script # extraction script; run per schema via sqlplus pgrecon load dump_dir --db est.db pgrecon convert --db est.db psql -qX -f schema_pg.sql 2>&1 | grep -c ERROR:
Or fork the repository and run its Benchmark workflow from the Actions tab: it starts Oracle XE 21c and PostgreSQL 16 inside the job, installs the eight public schemas, extracts, converts, applies, and prints the table above with the count of statements rejected and the coverage per schema. That is how the 2026-10-05 numbers were produced, and the run, its logs, and every dump, DDL file, and residue report it made are public.
Run each competing tool per its own documentation against the same schemas, apply with the same psql invocation, count the same way. If a number here looks wrong, that is a fair reaction - reproduce it and say so on the issue tracker.