Benchmark methodology
six converters, nine schemas, one live PostgreSQL 16

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.

The question and the bar

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.

The nine schemas

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.

Tools and versions

ToolVersion / mode
pgrecon0.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_migratorcurrent release, August 2026, over oracle_fdw 2.9.0 (Oracle Instant Client 23)
Dalibo PostgreSQL Migrator1.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
Ora2Pgv25.0, TYPE=TABLE,VIEW,SEQUENCE,TRIGGER,FUNCTION,PROCEDURE,PACKAGE
EDB Migration Toolkit55.13.0, -offlineMigration, community PostgreSQL target
AWS Schema Conversion Toolcurrent build, August 2026, via its batch CLI (.scts scripts)

How errors were counted

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.

Per-tool accommodations

Runs were kept as fair as each tool allows; everything a tool needed beyond its documentation is recorded here.

Results

ConverterStatements rejectedNotes
pgrecon 0.7.30 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_migrator40 plus two whole-schema crashes
Dalibo PostgreSQL Migrator 1.058 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 v2577 49 on OE, 18 on the lab schema, 8 on RECON_TEST; clean on the simple estates
EDB MTK 55.13135 of 477 emitted statements; the low counts on package libraries are absence, not quality
AWS SCT481 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.

Reproduce it

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.