<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>PL/SQL | John Nessime</title>
	<atom:link href="https://john-nessime.com/blog/tag/pl-sql/feed/" rel="self" type="application/rss+xml" />
	<link>https://john-nessime.com/blog/tag/pl-sql/</link>
	<description>Cloud, DevOps, Data &#38; AI — Built, Tested, Explained</description>
	<lastBuildDate>Sat, 03 Oct 2026 06:43:14 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.2</generator>

<image>
	<url>https://john-nessime.com/blog/wp-content/uploads/2026/07/cropped-jn-32x32.png</url>
	<title>PL/SQL | John Nessime</title>
	<link>https://john-nessime.com/blog/tag/pl-sql/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>AWS Schema Conversion Tool for Oracle: What Converts and What Quietly Doesn&#8217;t</title>
		<link>https://john-nessime.com/blog/technical-guides/aws-schema-conversion-tool-oracle/</link>
		
		<dc:creator><![CDATA[John Nessime]]></dc:creator>
		<pubDate>Sat, 03 Oct 2026 06:43:14 +0000</pubDate>
				<category><![CDATA[Cloud Computing]]></category>
		<category><![CDATA[Databases]]></category>
		<category><![CDATA[Technical Guides]]></category>
		<category><![CDATA[Amazon Aurora]]></category>
		<category><![CDATA[Amazon RDS]]></category>
		<category><![CDATA[AWS DMS]]></category>
		<category><![CDATA[AWS SCT]]></category>
		<category><![CDATA[Cutover Planning]]></category>
		<category><![CDATA[Data Type Mapping]]></category>
		<category><![CDATA[Database Migration]]></category>
		<category><![CDATA[Migration Assessment]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[PL/SQL]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Schema Conversion]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Vendor Lock-In]]></category>
		<guid isPermaLink="false">https://john-nessime.com/blog/?p=388</guid>

					<description><![CDATA[<p>The assessment report grades syntax, not behavior. A four-tier breakdown of what AWS SCT converts cleanly, what it converts while silently changing meaning, what it only handles through the extension pack, what it refuses outright, and the whole category it never looks at.</p>
<p>The post <a href="https://john-nessime.com/blog/technical-guides/aws-schema-conversion-tool-oracle/">AWS Schema Conversion Tool for Oracle: What Converts and What Quietly Doesn&#8217;t</a> appeared first on <a href="https://john-nessime.com/blog">John Nessime</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Open the assessment report on a mid-sized Oracle schema and the top-line number looks fantastic. Ninety-something percent of objects convert automatically. The red items are counted, categorized, and given effort estimates. It reads like a project plan you could take to a steering committee.</p>



<p class="wp-block-paragraph">The part that hurts you later is not on that page.</p>



<p class="wp-block-paragraph">The AWS Schema Conversion Tool for Oracle grades <em>syntax</em>. It tells you whether it could emit valid PostgreSQL for an object. It does not tell you whether that object will behave the same way at runtime. A trigger can convert with a green tick and fire in a different order. A column can convert with a green tick and silently drop the time component off every date in the table. A procedure can convert with a green tick and run twenty times slower because every string function call now goes through a wrapper.</p>



<p class="wp-block-paragraph">This post sorts the tool&#8217;s output into four buckets: what converts cleanly, what converts while quietly changing meaning, what converts only through the extension pack, and what it refuses outright. Then the category nobody budgets for, which is everything it never looks at.</p>



<h2 class="wp-block-heading">What the AWS Schema Conversion Tool for Oracle actually does</h2>



<p class="wp-block-paragraph">It is a Java desktop application. It connects over JDBC to source and target, reads the Oracle data dictionary, parses the DDL and PL/SQL it finds, and generates equivalent PostgreSQL objects and PL/pgSQL. The converted code stays inside the project until you explicitly apply it to the target or save it out as SQL files.</p>



<p class="wp-block-paragraph">Three things follow from that description, and they matter more than any feature list.</p>



<ul class="wp-block-list">
<li>It only sees what is stored in the database. Code that lives in your application, your job scheduler, or a reporting tool is invisible to it unless you point the separate application SQL conversion feature at those files yourself.</li>

<li>It only sees what the connecting user can see. Connect with a thin account and half the schema will not appear in the report at all.</li>

<li>It moves structure and code. It does not move rows. That is AWS DMS, and it is a separate exercise with its own failure modes.</li>
</ul>



<p class="wp-block-paragraph">There is also a command line interface, which is the part most teams discover too late. It uses scenario files rather than flag-style arguments, and it needs Amazon Corretto 11 installed alongside the tool.</p>



<pre class="wp-block-code"><code># Interactive mode, run from the AWS SCT app folder.
# End each command with a / on its own line.
java -jar AWSSchemaConversionToolBatch.jar -type interactive

# Script mode, running a saved .scts scenario end to end.
java -jar /opt/aws-schema-conversion-tool/lib/app/AWSSchemaConversionToolBatch.jar 
  -type scts -script /home/sct/project/convert.scts</code></pre>



<p class="wp-block-paragraph">Script mode turns a one-off conversion into something you can rerun, which matters because on a real migration you will convert the schema many times as you clean up the source. Re-clicking through the GUI each time is how mistakes creep in.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Tier one: converts cleanly, and you can trust it</h2>



<p class="wp-block-paragraph">Credit where it is due. For the bulk of an ordinary OLTP schema the tool is good, and hand-writing this would be miserable.</p>



<ul class="wp-block-list">
<li>Table definitions: names, columns, ordinary data types, NOT NULL, defaults, primary keys, unique constraints, foreign keys.</li>

<li>Standard b-tree indexes, including most single-column functional indexes.</li>

<li>Ordinary views. The basic CREATE VIEW syntax is close to identical between the two engines.</li>

<li>Sequences and identity columns, despite the syntax looking nothing alike.</li>

<li>ANSI-standard SQL, joins, subqueries, and almost all window and analytic functions.</li>

<li>PL/SQL flow control: loops, blocks, conditionals, exception structure.</li>

<li>The four standard transaction isolation levels, plus BEGIN, COMMIT, and ROLLBACK.</li>
</ul>



<p class="wp-block-paragraph">If your schema is mostly tables, views, and reporting SQL with modest procedural code, the automation figure in the report is roughly honest and you should believe it.</p>



<h2 class="wp-block-heading">Tier two: converts green, behaves differently</h2>



<p class="wp-block-paragraph">This is the tier that costs money, because none of it shows up as a red action item. The object converted. The DDL applied. The application connected. Something is wrong three weeks later and nobody thinks to look back at the schema conversion.</p>



<h3 class="wp-block-heading">Oracle DATE is not PostgreSQL date</h3>



<p class="wp-block-paragraph">Oracle&#8217;s DATE type carries a time component down to the second. PostgreSQL&#8217;s <code>date</code> is a calendar day and nothing more. Map an Oracle DATE to a PostgreSQL <code>date</code> and every timestamp in that column is truncated, permanently, without an error. The correct target is <code>timestamp</code>. Check the conversion settings and check the generated DDL before you apply, because this one is not recoverable after the data lands.</p>



<h3 class="wp-block-heading">NUMBER without precision becomes numeric, and numeric is slow</h3>



<p class="wp-block-paragraph">An unqualified Oracle NUMBER is an extremely flexible container, so the default mapping to PostgreSQL <code>numeric</code> is technically correct. It is also arbitrary-precision arithmetic implemented in software. Put that on a surrogate key that participates in every join and index in your application and you have bought yourself a performance problem that no amount of index tuning will fix.</p>



<p class="wp-block-paragraph">Find the candidates before conversion, not after:</p>



<pre class="wp-block-code"><code>-- Oracle: unqualified NUMBER columns, the ones that default to numeric.
SELECT table_name, column_name, data_precision, data_scale
FROM   user_tab_columns
WHERE  data_type = 'NUMBER'
AND    data_precision IS NULL
ORDER  BY table_name, column_name;</code></pre>



<p class="wp-block-paragraph">For each one, look at the actual minimum, maximum, and scale of the data, then decide whether it is really an <code>integer</code>, a <code>bigint</code>, or a genuine <code>numeric(p,s)</code>. Surrogate keys and foreign keys are almost always integers pretending to be NUMBER. Money columns usually are not.</p>



<h3 class="wp-block-heading">Empty string is NULL in Oracle and is not in PostgreSQL</h3>



<p class="wp-block-paragraph">This is the single most reliable source of post-cutover bugs, and it is not a conversion defect. Oracle treats an empty string as NULL. PostgreSQL treats it as a zero-length string, which is a real value. Every <code>IS NULL</code> check in your procedural code and your application was written under Oracle&#8217;s rule.</p>



<p class="wp-block-paragraph">Concatenation flips too. In Oracle, concatenating a NULL is a no-op. In PostgreSQL, concatenating a NULL with the <code>||</code> operator yields NULL for the whole expression. A report that used to print a name with a missing middle initial now prints nothing at all.</p>



<p class="wp-block-paragraph">After the data lands, count the divergence before you let users near it:</p>



<pre class="wp-block-code"><code>-- PostgreSQL: how many rows are empty string rather than NULL?
SELECT count(*) FILTER (WHERE middle_name = '')   AS empty_string,
       count(*) FILTER (WHERE middle_name IS NULL) AS real_null
FROM   app.customer;</code></pre>



<p class="wp-block-paragraph">If <code>empty_string</code> is non-zero anywhere, you have a decision to make: normalize the data on load, or audit every NULL check that touches those columns. Normalizing is usually cheaper and is far easier to verify.</p>



<h3 class="wp-block-heading">The warnings the tool does raise, that people scroll past</h3>



<p class="wp-block-paragraph">Some of these semantic differences do appear in the action list, just not in red. They are worth reading individually rather than in aggregate.</p>



<ul class="wp-block-list">
<li>GREATEST and LEAST are flagged as potentially producing different results from the source, because of how each engine handles NULL arguments and type coercion.</li>

<li>Timestamp handling inside converted partitioned tables is flagged as potentially divergent.</li>

<li>Converted code is flagged as possibly incorrect where bind variable names, user-defined functions, or dynamic SQL are involved. That is the tool telling you it guessed.</li>

<li>Trigger firing order is not carried over. Oracle&#8217;s FOLLOWS and PRECEDES clauses have no equivalent, and PostgreSQL fires triggers on the same event in alphabetical order by trigger name. If your Oracle triggers depended on sequencing, you now depend on naming.</li>

<li>An empty exception block in the converted output is called out explicitly. Oracle code that swallowed errors will now swallow them differently.</li>
</ul>



<p class="wp-block-paragraph">One more that is not an action code at all: Oracle PL/SQL routinely issues COMMIT in the middle of a procedure. A PostgreSQL function cannot do that. Either the logic becomes a procedure invoked with CALL, or the transaction boundary moves out into the calling application. That is a design decision, not a find-and-replace.</p>



<h2 class="wp-block-heading">Tier three: converts, but only through the extension pack</h2>



<p class="wp-block-paragraph">When there is no native PostgreSQL equivalent for something Oracle provides, the tool installs an extension pack: an additional schema on the target, named <code>aws_oracle_ext</code> for Oracle sources, containing wrapper functions, tables, and supporting objects that emulate the source behavior. For some capabilities like job scheduling and sending mail, the pack also deploys AWS Lambda functions.</p>



<p class="wp-block-paragraph">So a call to <code>INSTR</code> in your PL/SQL comes out the other side as <code>aws_oracle_ext.INSTR</code>. Package variables, which have no PostgreSQL equivalent because PL/pgSQL has no packages, get routed through getter and setter functions in the pack.</p>



<p class="wp-block-paragraph">The case for accepting this is real. If the goal is off Oracle licensing by a fixed date, the pack converts code that would otherwise need a developer per object. That is the difference between a migration that ships and one that stalls in refactoring, and nobody gets a bonus for elegant PL/pgSQL on a project that missed its window.</p>



<p class="wp-block-paragraph">The case against is equally real:</p>



<ul class="wp-block-list">
<li>Wrapper functions sit between your code and the engine. In hot loops, that overhead is measurable, and the package-variable emulation in particular is not free.</li>

<li>You have swapped one dependency for another. The pack is AWS-specific code living in your database, which is an awkward thing to explain if the stated goal was ending vendor lock-in.</li>

<li>Every time the pack is applied, its components are overwritten. Anything you hand-tuned inside that schema is gone.</li>

<li>Upgrades and target-version changes now have an extra moving part to validate.</li>
</ul>



<p class="wp-block-paragraph">There is a middle path. The tool can be configured to use the open-source <code>orafce</code> extension, which implements a subset of Oracle functions and packages natively in PostgreSQL, in place of some pack wrappers. My rule of thumb: use the pack to get the migration over the line, then treat the size of the <code>aws_oracle_ext</code> call graph as a debt register you burn down deliberately, starting with whatever appears in your slowest queries. Rewriting collections as native arrays or JSON, and package variables as parameters, buys back most of the performance.</p>



<h2 class="wp-block-heading">Tier four: it stops and hands you the work</h2>



<p class="wp-block-paragraph">These are the red items: the honest part of the report, and paradoxically the least dangerous, because you can see them and plan around them. Not exhaustive, but this is what turns up on real Oracle schemas.</p>



<ul class="wp-block-list">
<li><strong>Database links.</strong> No automation at all. The mechanism has to be rebuilt, usually on <code>postgres_fdw</code>, and foreign data wrappers do not support calling user-defined functions on the remote side. If your architecture leans on links, this is a workstream, not a task.</li>

<li><strong>Autonomous transactions.</strong> PostgreSQL has no direct equivalent. Logging patterns that relied on committing independently of the parent transaction need redesign, often with <code>dblink</code> back to the same instance or an out-of-band queue.</li>

<li><strong>ROWID and UROWID.</strong> No equivalent, and any code that reads ROWID from a table is flagged separately. Row-identity tricks in PL/SQL are a rewrite.</li>

<li><strong>Dynamic SQL and DBMS_SQL.</strong> Anything assembled at runtime the parser cannot resolve statically comes back untouched or with a health warning.</li>

<li><strong>Synonyms.</strong> Not supported, and neither is their usage, so every reference through a synonym has to be resolved by hand or mimicked with views and search paths.</li>

<li><strong>Global temporary tables, index-organized tables, external tables, clustered tables, object tables, nested tables, virtual columns, BFILE.</strong> Storage and type features with no target equivalent.</li>

<li><strong>Bitmap indexes, domain indexes, multi-column functional indexes.</strong> The first two have no counterpart; the third is a documented conversion gap.</li>

<li><strong>Associative arrays, VARRAY of VARRAY, multi-dimensional arrays, object type methods, member functions in user-defined types.</strong> Collection-heavy PL/SQL is where the automation percentage collapses.</li>

<li><strong>Compound triggers, system triggers, REFERENCING clauses, conditional predicates.</strong> The trigger model differs enough that anything beyond a plain row trigger needs attention.</li>

<li><strong>Java stored routines and table functions.</strong> Not converted.</li>

<li><strong>Query hints.</strong> Partially converted, and you should delete them all first anyway. Hints tuned for Oracle&#8217;s optimizer are noise to a different planner. Add them back only after schema, index, and query work have failed.</li>

<li><strong>Materialized view incremental refresh and DML on materialized views.</strong> The view itself may convert; the refresh strategy will not.</li>
</ul>



<h3 class="wp-block-heading">Some red items are more conservative than your actual target</h3>



<p class="wp-block-paragraph">Here is a detail that saves real budget. The action code index and the migration playbook were written against a baseline target, and PostgreSQL has moved since. MERGE is listed as unsupported, but PostgreSQL has had a native MERGE statement since version 15. FOR UPDATE SKIP LOCKED is listed as unsupported, and it has been in PostgreSQL for a long time. GROUPING SETS, CUBE, and ROLLUP are listed as unsupported and are also long-standing PostgreSQL features.</p>



<p class="wp-block-paragraph">What this means practically: an action code tells you the converter will not emit the construct for you. It is not a statement about your target engine. Before you scope a rewrite for anything on that list, check the feature against the PostgreSQL version you are actually deploying. Some of those &#8220;rewrites&#8221; are a copy-paste with a syntax tweak.</p>



<h2 class="wp-block-heading">What it never looks at</h2>



<p class="wp-block-paragraph">This is the category that wrecks timelines, because it is not in the report at all. Not green, not red. Out of scope, therefore invisible.</p>



<ul class="wp-block-list">
<li><strong>Application code.</strong> There is a separate application SQL conversion feature that scans source files for embedded SQL, but it is opt-in, file-based, and you have to aim it. Nothing happens automatically.</li>

<li><strong>Scheduled work.</strong> DBMS_SCHEDULER jobs, cron entries calling SQL*Plus, shell wrappers, ETL controllers. The schema converts; the thing that runs it every night does not.</li>

<li><strong>Grants, roles, profiles, and proxy authentication.</strong> Your security model is a separate design exercise on the target.</li>

<li><strong>Oracle features with no schema footprint.</strong> Data Guard, RAC, ASM, RMAN, Oracle Text indexes, fine-grained access control policies. Each is its own replacement decision.</li>

<li><strong>Optimizer statistics and execution plans.</strong> Nothing about performance carries over. You are starting from zero on the target, and that is the right expectation to set with stakeholders early.</li>

<li><strong>Everything downstream.</strong> BI dashboards, reporting layers, and integration jobs that embed Oracle-flavored SQL will break on the day you cut over, and none of them appear in the assessment.</li>
</ul>



<p class="wp-block-paragraph">Inventory these before conversion, not after. A one-page list of every consumer of the schema is worth more than a week of tuning.</p>



<h2 class="wp-block-heading">Desktop tool or DMS Schema Conversion?</h2>



<p class="wp-block-paragraph">DMS Schema Conversion is the managed, browser-based version built on the same engine. Instead of a laptop install you create an instance profile, register source and target data providers with credentials in Secrets Manager, and run the project from the console, with metadata staged in S3.</p>



<p class="wp-block-paragraph">AWS now points standard OLTP conversions at the managed option, and generative AI assisted conversion is available there on supported paths. The desktop tool remains available and still covers ground the managed feature does not.</p>



<ul class="wp-block-list">
<li><strong>Pick the desktop tool</strong> when you need scripting through the CLI, want to edit the SQL for source and target inside the project, need migration rules and tree filters, are converting a data warehouse target, or want the application SQL and ETL conversion features.</li>

<li><strong>Pick DMS Schema Conversion</strong> when your source is reachable from a VPC, you would rather not manage a workstation with database credentials on it, your conversion path and region are supported, and the generative AI assist is useful to you.</li>
</ul>



<p class="wp-block-paragraph">Check supported paths, versions, and regions before committing either way; they differ between the two. On the desktop side, note the supported operating systems are Windows, Fedora, and Ubuntu, all 64-bit. If your analysts are on Macs, plan for a Linux jump box. A small VPS from a provider like Contabo or InterServer, or an EC2 instance inside the VPC, is the usual answer, and it has the side benefit of keeping the JDBC connection close to the source rather than dragging a dictionary read across a home broadband link.</p>



<p class="wp-block-paragraph">If the Oracle source is on-premises and the conversion box is not, you need a network path that a DBA will sign off on. That is normally a site-to-site VPN or Direct Connect. For an individual consultant working against a client&#8217;s staging environment, a commercial VPN such as NordVPN or Surfshark is sometimes what is on offer, but treat it as a stopgap: it gets you connectivity, not an audit trail.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Troubleshooting</h2>



<h3 class="wp-block-heading">The CLI jar refuses to open</h3>



<p class="wp-block-paragraph">A common failure is an unexpected error opening <code>AWSSchemaConversionToolBatch.jar</code>. Recent OpenJDK builds cap the size of jar signature files and the SCT jar exceeds the default. Raising that limit on the java invocation is the widely reported workaround:</p>



<pre class="wp-block-code"><code>java -Djdk.jar.maxSignatureFileSize=20000000 
  -jar AWSSchemaConversionToolBatch.jar -type interactive</code></pre>



<p class="wp-block-paragraph">Confirm you are on the Corretto 11 runtime the tool expects before chasing anything more exotic.</p>



<h3 class="wp-block-heading">It hangs on a large schema, or will not connect at all</h3>



<p class="wp-block-paragraph">Thousands of objects in one schema puts the JVM under memory pressure; Global settings exposes JVM options, so raise the heap and retry on a machine with real memory. Separately, JDBC driver paths live in Global settings under Drivers, per engine. A driver sitting on disk but not registered there fails in a way that looks like a network problem and is not.</p>



<h3 class="wp-block-heading">&#8220;The package body doesn&#8217;t include source code&#8221;</h3>



<p class="wp-block-paragraph">The source is wrapped. Obfuscated PL/SQL cannot be parsed, so it cannot be converted. Either find the original in version control or accept a from-scratch rewrite. Finding this on a critical package late in the project is a bad day, so check for wrapped objects on day one.</p>



<h3 class="wp-block-heading">Objects will not apply, or the report is missing things</h3>



<p class="wp-block-paragraph">Failures on apply are usually dependency order: an object references a parent that was not converted or sits outside the schema mapping, and the action list says so explicitly. Convert the whole dependency closure, not just the objects you care about. A short report is usually privileges, since the tool reads through the connecting account&#8217;s visibility. Compare its object counts against a direct dictionary count before trusting any percentage in it.</p>



<h2 class="wp-block-heading">Common mistakes</h2>



<ul class="wp-block-list">
<li>Treating the automation percentage as a completion estimate. It measures objects, not effort, and one gnarly package can outweigh four hundred clean tables.</li>

<li>Applying to the target before reviewing the generated DDL. Save to SQL, read it, diff it on the next run.</li>

<li>Accepting default type mappings wholesale. DATE and unqualified NUMBER are the two that reliably hurt.</li>

<li>Carrying Oracle hints across. Start with none and earn each one back.</li>

<li>Scoping rewrites straight off the action codes without checking your target PostgreSQL version first.</li>

<li>Leaving project files and scenario scripts containing database credentials sitting on a laptop after the engagement. They are plain files; when the work is done, wipe them properly with a tool like O&amp;O SafeErase rather than dragging them to the trash.</li>
</ul>



<h2 class="wp-block-heading">Best practices</h2>



<ol class="wp-block-list">
<li>Connect with an account that can see the whole schema, then verify object counts against the dictionary before reading any percentage.</li>

<li>Profile your NUMBER and DATE columns first and set the type mappings deliberately. Do this before the first conversion, not after the first load.</li>

<li>Script the conversion with the CLI so every run is identical and reviewable.</li>

<li>Sort red action items by object criticality, not by count. Twenty flags on a nightly batch job matter more than two hundred on a deprecated reporting view.</li>

<li>Write the semantic test suite before cutover: empty string versus NULL, date boundaries, numeric rounding, trigger ordering, transaction behavior in converted routines.</li>

<li>Instrument the target from day one. Query-level metrics in Amazon CloudWatch, or a Grafana stack against pg_stat_statements, will show you which extension pack wrappers are actually costing you before users complain.</li>
</ol>



<h2 class="wp-block-heading">Frequently asked questions</h2>



<h3 class="wp-block-heading">Is the AWS Schema Conversion Tool free?</h3>



<p class="wp-block-paragraph">The tool itself is a free download from AWS. What you pay for is everything around it: the compute you run it on, the target database, DMS replication instances for the data move, and the engineering time on the objects it hands back to you. That last item is the whole budget on most migrations.</p>



<h3 class="wp-block-heading">Does it migrate data as well as schema?</h3>



<p class="wp-block-paragraph">No. It converts structure and code. Moving rows is AWS DMS, which handles both a full load and ongoing change capture. They are designed to be used together, and each has its own set of behaviors worth understanding separately.</p>



<h3 class="wp-block-heading">Can I skip the extension pack entirely?</h3>



<p class="wp-block-paragraph">You can, but you are then hand-writing PostgreSQL equivalents for every Oracle built-in the pack would have covered. Configuring the tool to prefer <code>orafce</code> where possible is the usual compromise: fewer AWS-specific wrappers, open-source implementation, still automated. Full independence is achievable and is a refactoring project, not a setting.</p>



<h3 class="wp-block-heading">Why did my report say 95% automatic and the project still take months?</h3>



<p class="wp-block-paragraph">Because the percentage counts objects, ignores runtime semantics entirely, and excludes application code, scheduled jobs, security, and downstream consumers. Tier two and the out-of-scope list in this post are where the months go.</p>



<h3 class="wp-block-heading">Should I clean up the Oracle schema before or after conversion?</h3>



<p class="wp-block-paragraph">Before, every time. Dropping dead objects, unwrapping code you own, and removing synonyms on the source reduces the surface the tool has to reason about and shrinks the red list. It is also the only cleanup you can do while you still have a working system to test against.</p>



<h3 class="wp-block-heading">How do I verify the conversion actually worked?</h3>



<p class="wp-block-paragraph">Row counts and DDL comparison are the floor, not the ceiling. Run the same business queries against both engines and compare results, with deliberate coverage of NULL handling, date boundaries, sorting and collation, and numeric rounding. Anything that only shows up under concurrency needs a load test, not a smoke test.</p>



<h2 class="wp-block-heading">The one thing worth remembering</h2>



<p class="wp-block-paragraph">The AWS Schema Conversion Tool for Oracle is a good parser and an honest one about the things it cannot parse. The red list is trustworthy. The trouble is that a green tick means &#8220;I emitted valid PostgreSQL,&#8221; not &#8220;this behaves the way it did.&#8221; Dates lose their time, unqualified NUMBER lands as slow arithmetic, empty strings stop being NULL, triggers reorder themselves alphabetically, and none of that appears as a problem in the report.</p>



<p class="wp-block-paragraph">Use it for what it is good at, which is the tedium you should never do by hand. Then spend your review time on the objects that passed, because that is where the expensive surprises live. Treat the assessment report as an inventory, never as a schedule.</p>



<h2 class="wp-block-heading">Need a second pair of eyes on an Oracle to PostgreSQL conversion?</h2>



<p class="wp-block-paragraph">Most of the value in this work is in the review, not the click. Things I help with:</p>



<ul class="wp-block-list">
<li>Reading an assessment report properly: separating object counts from real effort, and re-scoping red items against the PostgreSQL version you are actually deploying.</li>

<li>Auditing type mappings before you apply, with a column-level profile of NUMBER and DATE usage and a concrete mapping decision for each.</li>

<li>Building the semantic test suite that catches empty-string and NULL divergence, trigger ordering, and rounding differences before users find them.</li>

<li>Scripting the conversion through the SCT CLI so it is repeatable, reviewable, and lives in version control alongside the generated DDL.</li>

<li>Planning the extension pack burn-down: which <code>aws_oracle_ext</code> calls are costing you, and what the native PostgreSQL replacement looks like.</li>

<li>Mapping the out-of-scope surface, meaning scheduled jobs, grants, downstream reports and integrations, so cutover day has no surprises in it.</li>
</ul>



<p class="wp-block-paragraph">Send me an assessment report, a converted DDL file, or the action item list, and I will tell you what I would actually worry about in it.</p>



<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://www.upwork.com/freelancers/~01f15a912ad84a6620" target="_blank" rel="noreferrer noopener">Work with me on Upwork</a></div>
</div>

<p>The post <a href="https://john-nessime.com/blog/technical-guides/aws-schema-conversion-tool-oracle/">AWS Schema Conversion Tool for Oracle: What Converts and What Quietly Doesn&#8217;t</a> appeared first on <a href="https://john-nessime.com/blog">John Nessime</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
