<?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>Julien Delattre, auteur/autrice sur dbi Blog</title>
	<atom:link href="https://www.dbi-services.com/blog/author/juliendelattre/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.dbi-services.com/blog/author/juliendelattre/</link>
	<description></description>
	<lastBuildDate>Thu, 05 Nov 2026 13:05:08 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/cropped-favicon_512x512px-min-32x32.png</url>
	<title>Julien Delattre, auteur/autrice sur dbi Blog</title>
	<link>https://www.dbi-services.com/blog/author/juliendelattre/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>applyOps and manual oplog replay for MongoDB PITR</title>
		<link>https://www.dbi-services.com/blog/applyops-and-manual-oplog-replay-for-mongodb-pitr/</link>
					<comments>https://www.dbi-services.com/blog/applyops-and-manual-oplog-replay-for-mongodb-pitr/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Thu, 05 Nov 2026 07:21:00 +0000</pubDate>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[NoSQL]]></category>
		<category><![CDATA[applyops]]></category>
		<category><![CDATA[Backup]]></category>
		<category><![CDATA[Mongo]]></category>
		<category><![CDATA[mongodb]]></category>
		<category><![CDATA[oplog]]></category>
		<category><![CDATA[PITR]]></category>
		<category><![CDATA[Restore]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=48120</guid>

					<description><![CDATA[<p>mongorestore --oplogReplay gives you point-in-time recovery for a whole instance, replayed from the captured oplog of a single backup. It has no equivalent for a single database or a single collection. Say you need “restore just this one collection to 4:35pm” instead of the whole instance. The supported tooling has nothing to offer for in-place [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/applyops-and-manual-oplog-replay-for-mongodb-pitr/">applyOps and manual oplog replay for MongoDB PITR</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><code>mongorestore --oplogReplay</code> gives you point-in-time recovery for a whole instance, replayed from the captured oplog of a single backup. It has no equivalent for a single database or a single collection. Say you need “restore just this one collection to 4:35pm” instead of the whole instance. The supported tooling has nothing to offer for in-place namespace point-in-time recovery (PITR). The only way to build it yourself is with <code class="nowrap">applyOps</code>, the server command <code class="nowrap">mongorestore</code> uses to replay oplog entries.</p>



<p class="wp-block-paragraph">In fact, <code class="nowrap">mongorestore</code> refuses to combine <code class="nowrap">--oplogReplay</code> with any filter, like <code class="nowrap">--db</code> or <code class="nowrap">--nsInclude</code>:</p>



<pre class="wp-block-code"><code>$ mongorestore --oplogReplay --db myapp --drop dump/myapp
The --db and --collection flags are deprecated for this use-case; please use --nsInclude instead, i.e. with --nsInclude=${DATABASE}.${COLLECTION}
Failed: cannot use --oplogReplay with includes specified
0 document(s) restored successfully. 0 document(s) failed to restore.

$ mongorestore --oplogReplay --nsInclude 'myapp.*' --drop dump
Failed: cannot use --oplogReplay with includes specified
0 document(s) restored successfully. 0 document(s) failed to restore.</code></pre>



<p class="wp-block-paragraph">I was facing these issues while adding point-in-time recovery to the MongoDB DMK. I ran into several things that were not obvious from the <code class="nowrap">applyOps</code> reference page alone.</p>



<h2 id="what-applyops-does" class="wp-block-heading">What <code class="nowrap">applyOps</code> does</h2>



<p class="wp-block-paragraph"><code class="nowrap">applyOps</code> is a database command that takes a list of raw operations, in the exact format oplog entries are stored in. It applies them directly, bypassing the normal insert/update/delete driver API:</p>



<pre class="wp-block-code"><code>db.adminCommand({
  applyOps: &#091;
    { op: "i", ns: "myapp.orders", o: { _id: 1, status: "shipped" } }
  ]
})</code></pre>



<p class="wp-block-paragraph">Each entry uses the oplog field names:</p>



<ul class="wp-block-list">
<li><code class="nowrap">op</code> is <code class="nowrap">i</code> for insert, <code class="nowrap">u</code> for update, <code class="nowrap">d</code> for delete, <code class="nowrap">c</code> for a collection-level command.</li>



<li><code class="nowrap">ns</code> is the target namespace.</li>



<li><code class="nowrap">o</code> the operation data.
The MongoDB documentation describes it as primarily an internal command, mainly there to support sharding. <code class="nowrap">mongorestore</code> uses it as its replay mechanism, by reading oplog entries out of a backup, and handing them to <code class="nowrap">applyOps</code>.</li>
</ul>



<p class="wp-block-paragraph">This is the part that makes namespace-scoped PITR possible. To achieve this in-place, you need to read the oplog, keep only the entries for the database or collection you care about, and replay just these operations.</p>



<h2 id="batch-problem-not-every-write-is-a-separate-oplog-entry" class="wp-block-heading">Batch problem: not every write is a separate oplog entry</h2>



<p class="wp-block-paragraph">The first problem comes from something as simple as <code class="nowrap">insertMany()</code>. A batch of statements committed together doesn’t produce one oplog entry per statement. It produces a single top-level entry with <code>op: "c"</code>, <code>ns: "admin.$cmd"</code>, and the real operations nested inside <code class="nowrap">o.applyOps</code>:</p>



<pre class="wp-block-code"><code>{"op":"c","ns":"admin.$cmd","o":{"applyOps":&#091;
    {"op":"i","ns":"myapp.orders","o":{"_id":1,"n":1}},
    {"op":"i","ns":"myapp.orders","o":{"_id":2,"n":2}},
    {"op":"i","ns":"myapp.orders","o":{"_id":3,"n":3}}
]},"ts":{"$timestamp":{"t":1787326648,"i":2}}}</code></pre>



<p class="wp-block-paragraph">A namespace filter that only looks at the top-level <code class="nowrap">ns</code> field never sees <code class="nowrap">myapp.orders</code> here at all. It sees <code class="nowrap">admin.$cmd</code> and moves on, skipping every write inside the batch. Any manual oplog replay must <strong>recursively unpack nested <code class="nowrap">applyOps</code> entries</strong> before filtering by namespace, or it will drop real writes without any error.</p>



<h2 id="applyops-rejects-entries-from-a-freshly-restored-collection" class="wp-block-heading"><code class="nowrap">applyOps</code> rejects entries from a freshly restored collection</h2>



<p class="wp-block-paragraph">Once the batch problem was fixed, replaying entries against a just-restored collection still failed:</p>



<pre class="wp-block-code"><code>MongoServerError: applyOps had one or more errors applying ops</code></pre>



<p class="wp-block-paragraph">The response contained no per-entry detail beyond <code>results: [false, false]</code>. The real cause is that every oplog entry also has a <code class="nowrap">ui</code> field. It is the internal UUID of the collection at the moment the entry was written. <code class="nowrap">applyOps</code> checks that UUID against the UUID of the live collection and rejects the operation on any mismatch.</p>



<p class="wp-block-paragraph"><code class="nowrap">mongorestore</code> always assigns a <strong>new</strong> UUID to a collection it creates during a restore. The exception is when you explicitly pass both <code class="nowrap">--drop</code> and <code class="nowrap">--preserveUUID</code> together. The UUID of a restored collection never matches what the oplog entries reference, which is why every single op gets rejected, correctly but without saying why.</p>



<p class="wp-block-paragraph"><code class="nowrap">mongorestore</code> oplog replay code strips the <code class="nowrap">ui</code> field before calling <code class="nowrap">applyOps</code>, unless <code class="nowrap">--preserveUUID</code> was explicitly requested:</p>



<pre class="wp-block-code"><code>if !restore.OutputOptions.PreserveUUID {
    op.UI = nil
}</code></pre>



<p class="wp-block-paragraph">When building a custom oplog replay instead of using <code class="nowrap">mongorestore</code>, you need the same fix. <strong>Drop <code class="nowrap">ui</code> from every entry</strong> before using it with <code class="nowrap">applyOps</code>. The operation then resolves by namespace, instead of a UUID that no longer exists.</p>



<h2 id="ejson-relaxed-mode-changes-your-data-types" class="wp-block-heading"><code class="nowrap">EJSON</code> relaxed mode changes your data types</h2>



<p class="wp-block-paragraph">Reading oplog entries back out of an archive, to filter and replay them, means going through <code class="nowrap">EJSON.stringify()</code> and <code class="nowrap">EJSON.parse()</code> in <code class="nowrap">mongosh</code>. Both default to <strong>relaxed mode</strong>, and relaxed mode collapses <code class="nowrap">NumberLong</code> into a JavaScript number:</p>



<pre class="wp-block-code"><code>&gt; EJSON.stringify({txnNumber: NumberLong(1)})
'{"txnNumber":1}'

&gt; EJSON.stringify({txnNumber: NumberLong(1)}, null, 0, {relaxed: false})
'{"txnNumber":{"$numberLong":"1"}}'</code></pre>



<p class="wp-block-paragraph">If you feed the relaxed-mode output back into <code class="nowrap">applyOps</code>, the server rejects it:</p>



<pre class="wp-block-code"><code>BSON field 'OplogEntryBase.txnNumber' is the wrong type 'int', expected type 'long'</code></pre>



<p class="wp-block-paragraph">In this case, nothing about the data itself was wrong, the type was just downgraded through JSON. You must pass <code>{relaxed: false}</code> on both <code class="nowrap">EJSON.stringify()</code> and <code class="nowrap">EJSON.parse()</code>. Do it whenever oplog entries (or any BSON document with <code class="nowrap">Long</code>, <code class="nowrap">Timestamp</code>, or <code class="nowrap">Decimal128</code> fields) need to persist through a JSON <code class="nowrap">stringify</code>&#8211;<code class="nowrap">parse</code> round trip.</p>



<h2 id="dropdatabase-inside-a-replay-window" class="wp-block-heading"><code class="nowrap">dropDatabase</code> inside a replay window</h2>



<p class="wp-block-paragraph">The last important point is more a warning than a bug. A <code class="nowrap">dropDatabase</code> in the oplog window is replayed like any other entry, and it deletes data. I tested it in three steps.</p>



<ol class="wp-block-list">
<li><code class="nowrap">applyOps</code> refuses a batch that mixes <code class="nowrap">dropDatabase</code> with other operations:</li>
</ol>



<pre class="wp-block-code"><code>&gt; print(EJSON.stringify(db.adminCommand({applyOps: &#091;
...     {op: "i", ns: "myapp.orders", o: {_id: 10}},
...     {op: "c", ns: "myapp.$cmd", o: {dropDatabase: 1}}
... ]})))
dropDatabase in an applyOps must be the only entry</code></pre>



<ol class="wp-block-list">
<li>The same <code class="nowrap">dropDatabase</code> sent alone is accepted and applied:</li>
</ol>



<pre class="wp-block-code"><code>&gt; print(EJSON.stringify(db.adminCommand({applyOps: &#091;{op: "c", ns: "myapp.$cmd", o: {dropDatabase: 1}}]})))
{"applied":1,"results":&#091;true],"ok":1,...}</code></pre>



<ol class="wp-block-list">
<li><code>mongorestore --oplogReplay</code> does not stop on a <code class="nowrap">dropDatabase</code>. The base dump had the document <code>_id: 1</code>. The captured oplog then inserted <code>_id: 2</code>, dropped the database, re-created the collection and inserted <code>_id: 3</code>:</li>
</ol>



<pre class="wp-block-code"><code>$ mongorestore --oplogReplay --drop dump
replaying oplog
applied 6 oplog entries</code></pre>



<pre class="wp-block-code"><code>&gt; db.getSiblingDB("myapp").orders.find()
&#091;
  {
    _id: 3
  }
]</code></pre>



<p class="wp-block-paragraph">Documents created before the drop are gone, because the restore followed the history up to the end of the window. Whether to stop at a <code class="nowrap">dropDatabase</code> depends on what you restore:</p>



<ul class="wp-block-list">
<li>A single collection restored in place: stop before any <code class="nowrap">dropDatabase</code>. Replaying the drop would also delete the other collections of the database, which are not part of the restore.</li>



<li>A whole database or instance: replaying <code class="nowrap">dropDatabase</code> is correct, since the drop is part of the history. The risk is a target time set after the drop, which restores an empty database.</li>
</ul>



<p class="wp-block-paragraph">In both cases, I would recommend to detect the <code class="nowrap">dropDatabase</code> in the replay window and report its timestamp. If your target moment is after a real <code class="nowrap">dropDatabase</code>, replay deletes the data, because the drop is part of the history. Pick a target just before the drop’s timestamp instead. Of course, this works only if your base backup is older than the drop. The oplog slices must also cover the gap, since a backup taken after the drop no longer contains the data.</p>



<h2 id="summary" class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">Building point-in-time recovery (PITR) for a single database or collection on top of <code class="nowrap">applyOps</code> means dealing with four things. <code class="nowrap">mongorestore</code> already handles them for a full-instance restore, and you now have to handle them yourself:</p>



<ul class="wp-block-list">
<li>Unpack nested <code class="nowrap">applyOps</code> batches before filtering by namespace, or multi-document writes are silently ignored.</li>



<li>Strip the <code class="nowrap">ui</code> field from every entry before replay, matching what <code class="nowrap">mongorestore</code> does by default.</li>



<li>Use <code>{relaxed: false}</code> on every <code class="nowrap">EJSON.stringify()</code>/<code class="nowrap">EJSON.parse()</code> round trip, or <code class="nowrap">Long</code>/<code class="nowrap">Timestamp</code>/<code class="nowrap">Decimal128</code> and other fields can be changed.</li>



<li>Stop at a <code class="nowrap">dropDatabase</code> when restoring a single collection in place.</li>
</ul>
<p>L’article <a href="https://www.dbi-services.com/blog/applyops-and-manual-oplog-replay-for-mongodb-pitr/">applyOps and manual oplog replay for MongoDB PITR</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/applyops-and-manual-oplog-replay-for-mongodb-pitr/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>GoldenGate captures TRUNCATE on a global temporary table and the replicat abends</title>
		<link>https://www.dbi-services.com/blog/goldengate-captures-truncate-on-a-global-temporary-table-and-the-replicat-abends/</link>
					<comments>https://www.dbi-services.com/blog/goldengate-captures-truncate-on-a-global-temporary-table-and-the-replicat-abends/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Mon, 02 Nov 2026 07:38:00 +0000</pubDate>
				<category><![CDATA[GoldenGate]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[26]]></category>
		<category><![CDATA[ddl]]></category>
		<category><![CDATA[extract]]></category>
		<category><![CDATA[ogg]]></category>
		<category><![CDATA[replicat]]></category>
		<category><![CDATA[Troubleshooting]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=48139</guid>

					<description><![CDATA[<p>In an extract report, I noticed that the CREATE and DROP of a global temporary table were excluded (OGG-10473), while the TRUNCATE of the same table was included (OGG-00487). If the extract writes the TRUNCATE to the trail but not the CREATE, the replicat will try to apply it on a target where the table [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/goldengate-captures-truncate-on-a-global-temporary-table-and-the-replicat-abends/">GoldenGate captures TRUNCATE on a global temporary table and the replicat abends</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In an extract report, I noticed that the <code class="nowrap">CREATE</code> and <code class="nowrap">DROP</code> of a global temporary table were excluded (<code class="nowrap"><a class="ogg-code-link" href="https://juliendelattre.com/tools/goldengate-error-code-explorer/OGG-10473/" target="_blank" rel="noopener noreferrer">OGG-10473</a></code>), while the <code class="nowrap">TRUNCATE</code> of the same table was included (<code class="nowrap"><a class="ogg-code-link" href="https://juliendelattre.com/tools/goldengate-error-code-explorer/OGG-00487/" target="_blank" rel="noopener noreferrer">OGG-00487</a></code>). If the extract writes the <code class="nowrap">TRUNCATE</code> to the trail but not the <code class="nowrap">CREATE</code>, the replicat will try to apply it on a target where the table does not exist. <strong>The replicat abends with <code class="nowrap">ORA-00942</code></strong>. Oracle support confirmed that <strong>this is the designed behavior for temporary tables</strong>, so the <code class="nowrap">TRUNCATE</code> has to be excluded or its error discarded in the replicat parameters. This post shows the failure, the affected DDL statements and ways to keep the replicat running.</p>



<h2 id="why-global-temporary-table-ddl-is-excluded" class="wp-block-heading">Why global temporary table DDL is excluded</h2>



<p class="wp-block-paragraph">The data of a global temporary table is private to each session and is not replicated. The extract therefore skips the DDL that creates and drops it, and the table does not exist on the target. However, <code class="nowrap">TRUNCATE</code> operations are captured by GoldenGate.</p>



<h2 id="setup" class="wp-block-heading">Setup</h2>



<ul class="wp-block-list">
<li>GoldenGate <code class="nowrap">23.26.4.0.0</code> Microservices Architecture, deployment <code class="nowrap">ogg_test_01</code>.</li>



<li>Source: <code class="nowrap">CDB01</code>/<code class="nowrap">PDB1</code>, Oracle 19c. Target: <code class="nowrap">CDB02</code>/<code class="nowrap">PDB2</code>, Oracle 19c.</li>



<li>A <code class="nowrap">GTTTEST</code> schema exists in both PDBs.</li>



<li>Integrated extract <code class="nowrap">EGTT</code> connects to <code class="nowrap">PDB1</code> directly (PDB-level capture), with <code>DDL INCLUDE ALL</code>.</li>



<li>Integrated replicat <code class="nowrap">RGTT</code> applies to <code class="nowrap">PDB2</code>, with <code>DDL INCLUDE ALL</code>.</li>
</ul>



<p class="wp-block-paragraph">The parameter file of the extract:</p>



<pre class="wp-block-code"><code>EXTRACT EGTT
EXTTRAIL gt
USERIDALIAS source_pdb1 DOMAIN OracleGoldenGate
DDL INCLUDE ALL
DDLOPTIONS REPORT
TABLE GTTTEST.*;</code></pre>



<p class="wp-block-paragraph">The parameter file of the replicat:</p>



<pre class="wp-block-code"><code>REPLICAT RGTT
USERIDALIAS oggadmin_pdb2 DOMAIN OracleGoldenGate
DDL INCLUDE ALL
DDLOPTIONS REPORT
MAP GTTTEST.*, TARGET GTTTEST.*;</code></pre>



<p class="wp-block-paragraph">The extract is registered with <code>REGISTER EXTRACT EGTT DATABASE</code>, and the replicat uses the checkpoint table <code class="nowrap">OGGADMIN.CHECKPOINT</code>.</p>



<h2 id="workload-on-the-source" class="wp-block-heading">Workload on the source</h2>



<p class="wp-block-paragraph">The three statements run as <code class="nowrap">GTTTEST</code> on <code class="nowrap">PDB1</code>:</p>



<pre class="wp-block-code"><code>SQL&gt; CREATE GLOBAL TEMPORARY TABLE temp_test (c number) ON COMMIT PRESERVE ROWS;

Table created.

SQL&gt; TRUNCATE TABLE temp_test;

Table truncated.

SQL&gt; DROP TABLE temp_test PURGE;

Table dropped.</code></pre>



<h2 id="what-the-extract-does" class="wp-block-heading">What the extract does</h2>



<p class="wp-block-paragraph">The extract report shows the three DDL statements:</p>



<ul class="wp-block-list">
<li><code class="nowrap">CREATE</code> and <code class="nowrap">DROP</code> are excluded.</li>



<li><code class="nowrap">TRUNCATE</code> is included.</li>
</ul>



<pre class="wp-block-code"><code>2026-10-24 07:26:05  INFO    OGG-01487  DDL found, operation &#091;CREATE GLOBAL TEMPORARY TABLE temp_test (c number) ON COMMIT PRESERVE ROWS (size 74)], ... ].
2026-10-24 07:26:05  INFO    OGG-10473  DDL on global temporary table excluded, optype &#091;CREATE], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
2026-10-24 07:26:05  INFO    OGG-01487  DDL found, operation &#091;TRUNCATE TABLE temp_test (size 24)], ... ].
2026-10-24 07:26:05  INFO    OGG-00487  DDL operation included &#091;INCLUDE ALL], optype &#091;TRUNCATE], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
2026-10-24 07:26:05  INFO    OGG-01487  DDL found, operation &#091;DROP TABLE temp_test PURGE (size 26)], ... ].
2026-10-24 07:26:05  INFO    OGG-10473  DDL on global temporary table excluded, optype &#091;DROP], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".</code></pre>



<p class="wp-block-paragraph">Only the <code class="nowrap">TRUNCATE</code> reaches the trail.</p>



<h2 id="what-the-replicat-does" class="wp-block-heading">What the replicat does</h2>



<p class="wp-block-paragraph">The replicat maps the <code class="nowrap">TRUNCATE</code> to <code class="nowrap">GTTTEST.TEMP_TEST</code> and executes it on <code class="nowrap">PDB2</code>. The table does not exist there, and the replicat abends:</p>



<pre class="wp-block-code"><code>2026-10-24 07:26:08  INFO    OGG-00482  DDL found, operation &#091;TRUNCATE TABLE temp_test (size 24)].
2026-10-24 07:26:08  INFO    OGG-00489  DDL is of mapped scope, after mapping new operation &#091;TRUNCATE TABLE "GTTTEST"."TEMP_TEST" (size 36)].
2026-10-24 07:26:08  INFO    OGG-00487  DDL operation included &#091;INCLUDE ALL], optype &#091;TRUNCATE], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
2026-10-24 07:26:08  INFO    OGG-00484  Executing DDL operation.
2026-10-24 07:26:08  ERROR   OGG-00519  Fatal error executing DDL replication: error &#091;Error code &#091;942], ORA-00942: table or view does not exist
2026-10-24 07:26:08  ERROR   OGG-30538  Fatal error executing DDL statement &#091;TRUNCATE TABLE "GTTTEST"."TEMP_TEST"].
2026-10-24 07:26:08  ERROR   OGG-00664  Error 942 on OCI call &#091;ORA] - ORA-00942: table or view does not exist
2026-10-24 07:26:13  ERROR   OGG-01668  PROCESS ABENDING.</code></pre>



<p class="wp-block-paragraph">The status in the <code class="nowrap">adminclient</code>:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; INFO ALL

Program     Status      Group       Type                   Lag at Chkpt       Time Since Chkpt
EXTRACT     RUNNING     EGTT        INTEGRATED                  00:00:03           00:00:04
REPLICAT    ABENDED     RGTT        INTEGRATED                  00:00:04           00:00:13</code></pre>



<h2 id="ddloptions-for-global-temporary-tables" class="wp-block-heading"><code class="nowrap">DDLOPTIONS</code> for global temporary tables</h2>



<p class="wp-block-paragraph">The <a href="https://docs.oracle.com/en/database/goldengate/core/26/reference/ddloptions.html" target="_blank" rel="noopener noreferrer"><code class="nowrap">DDLOPTIONS</code> documentation</a> describes two options for global temporary tables. By default, GTT DDL is not visible to the extract, which does not match the <code class="nowrap">TRUNCATE</code> captured above.</p>



<ul class="wp-block-list">
<li><code class="nowrap">CAPTUREGLOBALTEMPTABLE</code> (extract): makes GTT DDL visible to the extract. It is filtered like any other DDL and, if it passes, written to the trail.</li>



<li><code class="nowrap">NOAPPLYGLOBALTEMPTABLE</code> (replicat): prevents the replicat from applying GTT DDL. Without it, the replicat applies all GTT DDL from the trail.</li>
</ul>



<p class="wp-block-paragraph">Every case uses the same workload. It has the three GTT statements, then a regular table with one row going through <code class="nowrap">CREATE</code>, <code class="nowrap">TRUNCATE</code> and <code class="nowrap">DROP</code>. Each case starts from a new extract, replicat and trail.</p>



<h3 id="noapplyglobaltemptable-on-the-replicat" class="wp-block-heading"><code class="nowrap">NOAPPLYGLOBALTEMPTABLE</code> on the replicat</h3>



<p class="wp-block-paragraph">The parameter file of the replicat:</p>



<pre class="wp-block-code"><code>REPLICAT RGTT
USERIDALIAS oggadmin_pdb2 DOMAIN OracleGoldenGate
DDL INCLUDE ALL
DDLOPTIONS REPORT, NOAPPLYGLOBALTEMPTABLE
MAP GTTTEST.*, TARGET GTTTEST.*;</code></pre>



<p class="wp-block-paragraph"><strong>The replicat still abends on the <code class="nowrap">TRUNCATE</code></strong>, with the same errors as without the option:</p>



<pre class="wp-block-code"><code>INFO    OGG-00482  DDL found, operation &#091;TRUNCATE TABLE temp_test (size 24)].
ERROR   OGG-00519  Fatal error executing DDL replication: error &#091;Error code &#091;942], ORA-00942: table or view does not exist
ERROR   OGG-01668  PROCESS ABENDING.</code></pre>



<p class="wp-block-paragraph">If the GTT already exists on the target, the replicat does not abend, because the <code class="nowrap">TRUNCATE</code> is applied to that table.</p>



<h3 id="captureglobaltemptable-on-the-extract" class="wp-block-heading"><code class="nowrap">CAPTUREGLOBALTEMPTABLE</code> on the extract</h3>



<p class="wp-block-paragraph">With <code>DDLOPTIONS REPORT, CAPTUREGLOBALTEMPTABLE</code> in the extract, the <code class="nowrap">CREATE</code> and <code class="nowrap">DROP</code> are no longer excluded:</p>



<pre class="wp-block-code"><code>OGG-00487  DDL operation included &#091;INCLUDE ALL], optype &#091;CREATE], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
OGG-00487  DDL operation included &#091;INCLUDE ALL], optype &#091;TRUNCATE], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
OGG-00487  DDL operation included &#091;INCLUDE ALL], optype &#091;DROP], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".</code></pre>



<p class="wp-block-paragraph">The replicat creates the table on the target, truncates it and drops it. <strong>It keeps running:</strong></p>



<pre class="wp-block-code"><code>INFO    OGG-00482  DDL found, operation &#091;CREATE GLOBAL TEMPORARY TABLE temp_test (c number) ON COMMIT PRESERVE ROWS (size 74)].
INFO    OGG-00489  DDL is of mapped scope, after mapping new operation &#091;CREATE GLOBAL TEMPORARY TABLE "GTTTEST"."TEMP_TEST" (c number) ON COMMIT PRESERVE ROWS (size 86)].
INFO    OGG-00482  DDL found, operation &#091;TRUNCATE TABLE temp_test (size 24)].
INFO    OGG-00482  DDL found, operation &#091;DROP TABLE temp_test PURGE (size 26)].</code></pre>



<h3 id="both-options-together" class="wp-block-heading">Both options together</h3>



<p class="wp-block-paragraph">With <code class="nowrap">CAPTUREGLOBALTEMPTABLE</code> on the extract and <code class="nowrap">NOAPPLYGLOBALTEMPTABLE</code> on the replicat, the replicat excludes the <code class="nowrap">CREATE</code> and <strong>still executes the <code class="nowrap">TRUNCATE</code></strong>:</p>



<pre class="wp-block-code"><code>INFO    OGG-00482  DDL found, operation &#091;CREATE GLOBAL TEMPORARY TABLE temp_test (c number) ON COMMIT PRESERVE ROWS (size 74)].
INFO    OGG-10473  DDL on global temporary table excluded, optype &#091;CREATE], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
INFO    OGG-00482  DDL found, operation &#091;TRUNCATE TABLE temp_test (size 24)].
ERROR   OGG-00519  Fatal error executing DDL replication: error &#091;Error code &#091;942], ORA-00942: table or view does not exist
ERROR   OGG-01668  PROCESS ABENDING.</code></pre>



<p class="wp-block-paragraph">The replicat abends on the <code class="nowrap">TRUNCATE</code> before it reaches the <code class="nowrap">DROP</code>. To check the <code class="nowrap">DROP</code>, the same two options run with a workload that has no <code class="nowrap">TRUNCATE</code> on the global temporary table. <strong>The replicat excludes both the <code class="nowrap">CREATE</code> and the <code class="nowrap">DROP</code></strong> and keeps running:</p>



<pre class="wp-block-code"><code>INFO    OGG-00482  DDL found, operation &#091;CREATE GLOBAL TEMPORARY TABLE temp_test (c number) ON COMMIT PRESERVE ROWS (size 74)].
INFO    OGG-10473  DDL on global temporary table excluded, optype &#091;CREATE], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
INFO    OGG-00482  DDL found, operation &#091;DROP TABLE temp_test PURGE (size 26)].
INFO    OGG-10473  DDL on global temporary table excluded, optype &#091;DROP], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".</code></pre>



<p class="wp-block-paragraph">So <code class="nowrap">NOAPPLYGLOBALTEMPTABLE</code> skips the <code class="nowrap">CREATE</code> and <code class="nowrap">DROP</code> of a global temporary table, but not its <code class="nowrap">TRUNCATE</code>.</p>



<h2 id="which-ddl-statements-are-affected" class="wp-block-heading">Which DDL statements are affected</h2>



<p class="wp-block-paragraph">The <code class="nowrap">TRUNCATE</code> is not the only statement that behaves differently from <code class="nowrap">CREATE</code> and <code class="nowrap">DROP</code>. This workload runs on the source with the default extract (no <code class="nowrap">CAPTUREGLOBALTEMPTABLE</code>):</p>



<pre class="wp-block-code"><code>CREATE GLOBAL TEMPORARY TABLE temp_test (c number) ON COMMIT PRESERVE ROWS;
ALTER TABLE temp_test ADD (d number);
COMMENT ON TABLE temp_test IS 'gtt';
CREATE INDEX temp_test_i ON temp_test(c);
TRUNCATE TABLE temp_test;
ALTER TABLE temp_test RENAME TO temp_test2;
DROP TABLE temp_test2 PURGE;</code></pre>



<p class="wp-block-paragraph">The extract excludes five of the seven statements and <strong>includes <code class="nowrap">COMMENT</code> and <code class="nowrap">TRUNCATE</code></strong>:</p>



<pre class="wp-block-code"><code>INFO    OGG-10473  DDL on global temporary table excluded, optype &#091;CREATE], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
INFO    OGG-10473  DDL on global temporary table excluded, optype &#091;ALTER], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
INFO    OGG-00487  DDL operation included &#091;INCLUDE ALL], optype &#091;COMMENT], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
INFO    OGG-10473  DDL on global temporary table excluded, optype &#091;CREATE], objtype &#091;INDEX], objowner "GTTTEST", objname "TEMP_TEST".
INFO    OGG-00487  DDL operation included &#091;INCLUDE ALL], optype &#091;TRUNCATE], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
INFO    OGG-10473  DDL on global temporary table excluded, optype &#091;ALTER], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST2".
INFO    OGG-10473  DDL on global temporary table excluded, optype &#091;DROP], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST2".</code></pre>



<p class="wp-block-paragraph">With <code class="nowrap">CAPTUREGLOBALTEMPTABLE</code> on the extract and <code class="nowrap">NOAPPLYGLOBALTEMPTABLE</code> on the replicat, the extract captures every statement and the replicat makes the same decision. It excludes <code class="nowrap">CREATE</code> and <code class="nowrap">ALTER</code> (<code class="nowrap">OGG-10473</code>), then applies the <code class="nowrap">COMMENT</code> and <strong>abends</strong>:</p>



<pre class="wp-block-code"><code>INFO    OGG-00482  DDL found, operation &#091;ALTER TABLE temp_test ADD (d number) (size 36)].
INFO    OGG-10473  DDL on global temporary table excluded, optype &#091;ALTER], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
INFO    OGG-00482  DDL found, operation &#091;COMMENT ON TABLE temp_test IS 'gtt' (size 35)].
INFO    OGG-00487  DDL operation included &#091;INCLUDE ALL], optype &#091;COMMENT], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
ERROR   OGG-00519  Fatal error executing DDL replication: error &#091;Error code &#091;942], ORA-00942: table or view does not exist
ERROR   OGG-30538  Fatal error executing DDL statement &#091;COMMENT ON TABLE "GTTTEST"."TEMP_TEST" IS 'gtt'].
ERROR   OGG-01668  PROCESS ABENDING.</code></pre>



<p class="wp-block-paragraph">Each statement runs separately to see what the replicat does with it:</p>



<figure class="wp-block-table"><table><thead><tr><th>Statement on the global temporary table</th><th>Replicat</th></tr></thead><tbody><tr><td><code>CREATE TABLE</code>, <code>DROP TABLE</code></td><td>Excluded (<code class="nowrap">OGG-10473</code>)</td></tr><tr><td><code>ALTER TABLE .<wbr>.<wbr>.<wbr> ADD</code>, <code>ALTER TABLE .<wbr>.<wbr>.<wbr> RENAME TO</code></td><td>Excluded (<code class="nowrap">OGG-10473</code>)</td></tr><tr><td><code>CREATE INDEX</code>, <code class="nowrap">GRANT</code></td><td>Excluded (<code class="nowrap">OGG-10473</code>)</td></tr><tr><td><code>TRUNCATE TABLE</code></td><td><strong>Applied, abends with <code class="nowrap">ORA-00942</code></strong></td></tr><tr><td><code>COMMENT ON TABLE</code>, <code>COMMENT ON COLUMN</code></td><td><strong>Applied, abends with <code class="nowrap">ORA-00942</code></strong></td></tr></tbody></table></figure>



<h2 id="scope-and-truncate-options-do-not-change-it" class="wp-block-heading">Scope and truncate options do not change it</h2>



<p class="wp-block-paragraph">A difference in DDL scope could explain why <code class="nowrap">CREATE</code> is excluded and <code class="nowrap">TRUNCATE</code> is not. However, with <code>DDL INCLUDE MAPPED</code> on the replicat, the <code class="nowrap">TRUNCATE</code> is still included and the replicat abends:</p>



<pre class="wp-block-code"><code>INFO    OGG-00482  DDL found, operation &#091;TRUNCATE TABLE temp_test (size 24)].
INFO    OGG-00487  DDL operation included &#091;INCLUDE MAPPED], optype &#091;TRUNCATE], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
ERROR   OGG-00519  Fatal error executing DDL replication: error &#091;Error code &#091;942], ORA-00942: table or view does not exist
ERROR   OGG-30538  Fatal error executing DDL statement &#091;TRUNCATE TABLE "GTTTEST"."TEMP_TEST"].
ERROR   OGG-01668  PROCESS ABENDING.</code></pre>



<p class="wp-block-paragraph">The same happens with <code class="nowrap">GETTRUNCATES</code> and with <code class="nowrap">IGNORETRUNCATES</code> in the replicat, and with a global temporary table created with <code>ON COMMIT DELETE ROWS</code>. Those parameters control the truncate operation of the data, not the <code class="nowrap">TRUNCATE</code> DDL statement.</p>



<h2 id="answer-from-oracle-support" class="wp-block-heading">Answer from Oracle Support</h2>



<p class="wp-block-paragraph">I opened a service request with Oracle. The Product Team answered that <code>TRUNCATE TABLE</code> is a DDL operation and has to be excluded with an <code class="nowrap">EXCLUDE</code> clause of the <code class="nowrap">DDL</code> parameter. DDL cannot be filtered by temporary versus permanent tables, so the exclusion is done by table name. If that is too much work, the alternative is <code>DDLERROR 942 DISCARD</code>. I then pointed out that <code class="nowrap">CREATE</code> and <code class="nowrap">DROP</code> are DDL operations too and are excluded. The answer was that <strong>this is how it is designed for temporary tables</strong>.</p>



<h2 id="keeping-the-replicat-running" class="wp-block-heading">Keeping the replicat running</h2>



<h3 id="exclude-the-truncate-by-table-name" class="wp-block-heading">Exclude the <code class="nowrap">TRUNCATE</code> by table name</h3>



<p class="wp-block-paragraph">The <code class="nowrap">DDL</code> parameter of the replicat excludes the <code class="nowrap">TRUNCATE</code> of the table:</p>



<pre class="wp-block-code"><code>REPLICAT RGTT
USERIDALIAS oggadmin_pdb2 DOMAIN OracleGoldenGate
DDL INCLUDE ALL, EXCLUDE OBJNAME GTTTEST.TEMP_TEST OPTYPE TRUNCATE
DDLOPTIONS REPORT, NOAPPLYGLOBALTEMPTABLE
MAP GTTTEST.*, TARGET GTTTEST.*;</code></pre>



<p class="wp-block-paragraph">The replicat finds the <code class="nowrap">TRUNCATE</code> and skips it with an informational message, <code class="nowrap"><a class="ogg-code-link" href="https://juliendelattre.com/tools/goldengate-error-code-explorer/OGG-00488/" target="_blank" rel="noopener noreferrer">OGG-00488</a></code>. <strong>It keeps running</strong>:</p>



<pre class="wp-block-code"><code>INFO    OGG-00482  DDL found, operation &#091;CREATE GLOBAL TEMPORARY TABLE temp_test (c number) ON COMMIT PRESERVE ROWS (size 74)].
INFO    OGG-10473  DDL on global temporary table excluded, optype &#091;CREATE], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
INFO    OGG-00482  DDL found, operation &#091;TRUNCATE TABLE temp_test (size 24)].
INFO    OGG-00489  DDL is of mapped scope, after mapping new operation &#091;TRUNCATE TABLE "GTTTEST"."TEMP_TEST" (size 36)].
INFO    OGG-00488  DDL operation excluded &#091;EXCLUDE OBJNAME GTTTEST.TEMP_TEST OPTYPE TRUNCATE], optype &#091;TRUNCATE], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
INFO    OGG-00482  DDL found, operation &#091;DROP TABLE temp_test PURGE (size 26)].
INFO    OGG-10473  DDL on global temporary table excluded, optype &#091;DROP], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".</code></pre>



<p class="wp-block-paragraph">The exclusion works per table name, so it has to list every global temporary table. <code>COMMENT ON TABLE</code> and <code>COMMENT ON COLUMN</code> abend the same way, so the exclusion must cover them too.</p>



<h3 id="discard-the-ora-00942-error" class="wp-block-heading">Discard the <code class="nowrap">ORA-00942</code> error</h3>



<p class="wp-block-paragraph"><code>DDLERROR 942 DISCARD</code> needs no table list. The replicat applies the <code class="nowrap">TRUNCATE</code>, receives <code class="nowrap">ORA-00942</code> from the target and discards the error:</p>



<pre class="wp-block-code"><code>REPLICAT RGTT
USERIDALIAS oggadmin_pdb2 DOMAIN OracleGoldenGate
DISCARDFILE RGTT.dsc, APPEND
DDL INCLUDE ALL
DDLOPTIONS REPORT
DDLERROR 942 DISCARD
MAP GTTTEST.*, TARGET GTTTEST.*;</code></pre>



<p class="wp-block-paragraph">The report shows <code class="nowrap"><a class="ogg-code-link" href="https://juliendelattre.com/tools/goldengate-error-code-explorer/OGG-00494/" target="_blank" rel="noopener noreferrer">OGG-00494</a></code> instead of the abend:</p>



<pre class="wp-block-code"><code>INFO    OGG-00482  DDL found, operation &#091;TRUNCATE TABLE temp_test (size 24)].
INFO    OGG-00487  DDL operation included &#091;INCLUDE ALL], optype &#091;TRUNCATE], objtype &#091;TABLE], objowner "GTTTEST", objname "TEMP_TEST".
INFO    OGG-00494  DDL error discarded: error code &#091;942], filter &#091;include all (default)], error text &#091;Current time: 2026-10-24 07:55:57
 Error text &#091;Error code &#091;942], ORA-00942: table or view does not exist</code></pre>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; INFO ALL

Program     Status      Group       Type                   Lag at Chkpt       Time Since Chkpt
EXTRACT     RUNNING     EGTT        INTEGRATED                  00:00:03           00:00:07
REPLICAT    RUNNING     RGTT        INTEGRATED                  00:00:00           00:00:06</code></pre>



<p class="wp-block-paragraph">The discard file <code class="nowrap">RGTT.dsc</code> has the statement:</p>



<pre class="wp-block-code"><code>Current time: 2026-10-24 07:55:57
 Error text &#091;Error code &#091;942], ORA-00942: table or view does not exist
Help: https://docs.oracle.com/error-help/db/ora-00942/
], operation &#091;TRUNCATE TABLE "GTTTEST"."TEMP_TEST" (size 36)]
Operation failed at seqno 0 rba 1,333.</code></pre>



<p class="wp-block-paragraph">The parameter applies to every DDL statement that fails with <code class="nowrap">ORA-00942</code>, not only to temporary tables. A statement that fails because a regular table is missing on the target is discarded too, so check the discard file regularly.</p>



<h2 id="summary" class="wp-block-heading">Summary</h2>



<ul class="wp-block-list">
<li>With <code>DDL INCLUDE ALL</code>, the extract excludes <code class="nowrap">CREATE</code>, <code class="nowrap">ALTER</code>, <code>CREATE INDEX</code> and <code class="nowrap">DROP</code> of a global temporary table (<code class="nowrap">OGG-10473</code>) but includes <code class="nowrap">TRUNCATE</code> and <code class="nowrap">COMMENT</code> (<code class="nowrap">OGG-00487</code>).</li>



<li>The <code class="nowrap">TRUNCATE</code> is written to the trail and applied by the replicat on a target that has no such table.</li>



<li>The replicat abends with <code class="nowrap">ORA-00942</code> (<code class="nowrap"><a class="ogg-code-link" href="https://juliendelattre.com/tools/goldengate-error-code-explorer/OGG-00519/" target="_blank" rel="noopener noreferrer">OGG-00519</a></code>, <code class="nowrap"><a class="ogg-code-link" href="https://juliendelattre.com/tools/goldengate-error-code-explorer/OGG-30538/" target="_blank" rel="noopener noreferrer">OGG-30538</a></code>, <code class="nowrap"><a class="ogg-code-link" href="https://juliendelattre.com/tools/goldengate-error-code-explorer/OGG-01668/" target="_blank" rel="noopener noreferrer">OGG-01668</a></code>).</li>



<li><code>DDL INCLUDE MAPPED</code>, <code class="nowrap">GETTRUNCATES</code> and <code class="nowrap">IGNORETRUNCATES</code> do not change the abend.</li>



<li><code class="nowrap">NOAPPLYGLOBALTEMPTABLE</code> on the replicat does not prevent it, because the <code class="nowrap">TRUNCATE</code> is still executed.</li>



<li><code class="nowrap">CAPTUREGLOBALTEMPTABLE</code> on the extract makes the whole <code class="nowrap">CREATE</code>, <code class="nowrap">TRUNCATE</code> and <code class="nowrap">DROP</code> sequence replicate without error.</li>



<li>Oracle support confirmed that this is the designed behavior for temporary tables.</li>



<li><code>DDL EXCLUDE OBJNAME &lt;table&gt; OPTYPE TRUNCATE</code> on the replicat skips the <code class="nowrap">TRUNCATE</code> for the listed tables.</li>



<li><code>DDLERROR 942 DISCARD</code> on the replicat keeps it running for all tables and writes the failed statement to the discard file.</li>
</ul>
<p>L’article <a href="https://www.dbi-services.com/blog/goldengate-captures-truncate-on-a-global-temporary-table-and-the-replicat-abends/">GoldenGate captures TRUNCATE on a global temporary table and the replicat abends</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/goldengate-captures-truncate-on-a-global-temporary-table-and-the-replicat-abends/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Retrieving GoldenGate logs, reports, and diagnostics with the REST API</title>
		<link>https://www.dbi-services.com/blog/retrieving-goldengate-logs-reports-and-diagnostics-with-the-rest-api/</link>
					<comments>https://www.dbi-services.com/blog/retrieving-goldengate-logs-reports-and-diagnostics-with-the-rest-api/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Mon, 26 Oct 2026 07:52:00 +0000</pubDate>
				<category><![CDATA[Database Administration & Monitoring]]></category>
		<category><![CDATA[GoldenGate]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[administration]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[ggserr log]]></category>
		<category><![CDATA[log]]></category>
		<category><![CDATA[ogg]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Report]]></category>
		<category><![CDATA[rest]]></category>
		<category><![CDATA[rest api]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=48080</guid>

					<description><![CDATA[<p>I often need to retrieve report files or logs from GoldenGate deployments, and I wanted to find ways of automating this. Hence this blog, about retrieving all available logs with GoldenGate REST API. I previously wrote about the GoldenGate log files. Some of them (extract.log, replicat.log and others) are in fact just filters of other [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/retrieving-goldengate-logs-reports-and-diagnostics-with-the-rest-api/">Retrieving GoldenGate logs, reports, and diagnostics with the REST API</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">I often need to retrieve report files or logs from GoldenGate deployments, and I wanted to find ways of automating this. Hence this blog, about retrieving all available logs with GoldenGate REST API.</p>



<p class="wp-block-paragraph">I previously wrote about the <a href="https://www.dbi-services.com/blog/goldengate-26ai-logs-explained/" target="_blank" rel="noopener noreferrer">GoldenGate log files</a>. Some of them (<code class="nowrap">extract.log</code>, <code class="nowrap">replicat.log</code> and others) are in fact just filters of other logs (<code class="nowrap">ggserr.log</code> or <code class="nowrap">restapi.log</code>). These reconstructed logs do not have a REST API endpoint. For the others, let’s see how to retrieve them, using the <a href="https://juliendelattre.com/tools/ogg-python-client/" target="_blank" rel="noopener noreferrer">GoldenGate Python client I released earlier</a>.</p>



<h2 id="authenticating-to-the-rest-api" class="wp-block-heading">Authenticating to the REST API</h2>



<p class="wp-block-paragraph">With <code class="nowrap">oggrestapi.py</code>, authentication is handled once at client creation. When using <code class="nowrap">auto_discovery</code>, you won’t have to bother thinking about the port.</p>



<pre class="wp-block-code"><code>from oggrestapi import OGGRestAPI

ogg_client = OGGRestAPI(
    url="https://vmogg:7809",
    username="ogg",
    deployment="ogg_test_01",
    auto_discovery=True,
)</code></pre>



<p class="wp-block-paragraph">With <code class="nowrap">auto_discovery</code>, the client picks the right service port for each call. If you make direct <code class="nowrap">curl</code>/<code class="nowrap">requests</code> calls instead, you have to choose the service yourself:</p>



<ul class="wp-block-list">
<li><strong>Service Manager</strong> for <code class="nowrap">ServiceManager.log</code> and the deployment service logs (<code class="nowrap">adminsrvr.log</code>, <code class="nowrap">distsrvr.log</code>, etc.)</li>



<li><strong>Administration Service</strong> for report files, diagnostic files, <code class="nowrap">ggserr.log</code>. You can also retrieve <code class="nowrap">adminsrvr.log</code> from here.</li>
</ul>



<p class="wp-block-paragraph">The list of log files can be retrieved through <code>GET /services/v2/logs</code>. Each individual log is obtained through <code>GET /services/v2/logs/{log}</code>, on the port of the service you connect to. <code class="nowrap">default</code> is the service log: against <code class="nowrap">adminsrvr</code> (port <code class="nowrap">7810</code>), <code class="nowrap">default</code> is <code class="nowrap">adminsrvr.log</code>. To retrieve the content of the file, you need to send the <code>Accept: text/plain</code> header, or use the <code class="nowrap">content</code> toggle in <code class="nowrap">oggrestapi.py</code>.</p>



<pre class="wp-block-code"><code># /services/v2/logs - List all logs
&gt;&gt;&gt; ogg_client.list_logs(ogg_service="adminsrvr")
&#091;{'name': 'default'}, {'name': 'restapi'}, {'name': 'stdout'}, {'name': 'debug'}, {'name': 'events'}]

# /services/v2/logs/default - Get adminsrvr.log metadata
&gt;&gt;&gt; ogg_client.get_log(log="default", ogg_service="adminsrvr")
{'$schema': 'ogg:log', 'enabled': True, 'dataExists': True}

# /services/v2/logs/default (Accept: text/plain) - Get adminsrvr.log content
&gt;&gt;&gt; ogg_client.get_log(log="default", ogg_service="adminsrvr", content=True)
"...\n2026-09-26T17:21:30.612+0000 INFO | Registering containers &lt;nul&gt; (Thread 7)\n2026-09-26T17:21:31.820+0000 INFO | Configuration file 'EXT.prm' changed (Thread 7)\n2026-09-26T17:21:31.881+0000 INFO | Starting extract 'EXT' (EXTRACT:EXT)\n2026-09-26T17:21:31.915+0000 INFO | Task 'EXTRACT:EXT' started with process id 39143 (EXTRACT:EXT)\n"</code></pre>



<p class="wp-block-paragraph">You can also use the <code class="nowrap">get_service_log</code> directly, when using a reverse proxy or connecting to the Service Manager:</p>



<pre class="wp-block-code"><code># /services/v2/deployments/ogg_test_01/services/distsrvr/logs/default (Accept: text/plain) - Service Manager endpoint
# /services/ServiceManager/v2/deployments/ogg_test_01/services/distsrvr/logs/default (Accept: text/plain) - reverse proxy endpoint
&gt;&gt;&gt; ogg_client.get_service_log(deployment="ogg_test_01", service="distsrvr", content=True)
"...\n2026-09-26T17:33:46.183+0000 INFO | remove path Command Received for path PATHX (PATHX)\n2026-09-26T17:33:46.183+0000 WARN | Path stopped. (PATHX)\n"</code></pre>



<p class="wp-block-paragraph">The <code class="nowrap">debug</code> log is only relevant when you enable debug logging on a specific service. <code class="nowrap">events</code> is the critical events log (same content as <code class="nowrap">ER-events.log</code>), reachable directly as <code>GET /services/v2/logs/events</code> too, wrapped as <code class="nowrap">list_log_events()</code> :</p>



<pre class="wp-block-code"><code># /services/v2/logs/events (Accept: text/plain)
&gt;&gt;&gt; ogg_client.list_log_events(content=True)
"...Oracle GoldenGate Capture for Oracle, EXT.prm:  Checkpoint marked as from graceful shutdown, but records found after checkpoint in trail pdb1/e1.  Expected EOF Seqno 0, RBA 0.  Found Seqno 7, RBA 8884.\n"</code></pre>



<h2 id="service-manager-logs" class="wp-block-heading">Service Manager logs</h2>



<p class="wp-block-paragraph">From the Service Manager (port <code class="nowrap">7809</code> in my lab), <code class="nowrap">default</code> is the <code class="nowrap">ServiceManager.log</code>:</p>



<pre class="wp-block-code"><code># /services/v2/logs/default (Accept: text/plain)
&gt;&gt;&gt; ogg_client.get_service_manager_log()
"...2026-09-26T15:46:20.138+0000 INFO | CSRFTokenProtection: ENABLED.\n                                    Cross-Site Request Forgery checks using CSRF-Tokens will be performed. (Thread 12)\n"</code></pre>



<p class="wp-block-paragraph"><code class="nowrap">get_service_manager_log()</code> defaults to <code class="nowrap">content=True</code>, since fetching the metadata for this one specific log is not relevant (this log is always enabled).</p>



<h2 id="ggserrlog" class="wp-block-heading"><code class="nowrap">ggserr.log</code></h2>



<p class="wp-block-paragraph"><code class="nowrap">ggserr.log</code>, the deployment-wide message log, is not one of the names any <code class="nowrap">/logs</code> collection returns. It is provided by a separate Administration Service endpoint at <code class="nowrap">/services/v2/messages</code>. A <code class="nowrap">messages</code> key returns the log lines.</p>



<pre class="wp-block-code"><code># /services/v2/messages
&gt;&gt;&gt; lines = ogg_client.list_messages()&#091;"messages"]
&gt;&gt;&gt; len(lines)
53035</code></pre>



<p class="wp-block-paragraph">Once you find an <code class="nowrap">OGG-</code> message code in these lines, another endpoint (<code>GET /services/v2/oggerr/{message}</code>) returns its explanation:</p>



<pre class="wp-block-code"><code>&gt;&gt;&gt; ogg_client.get_ogg_error_info(message="OGG-30375", ogg_service="adminsrvr")
{'$schema': 'ogg:messageExplanation', 'severity': 'ERROR', 'title': 'There was a problem updating the Administration Service resources.', 'cause': 'An internal error occurred when trying to update the service resources. Possible causes include a corrupted or incorrect resources repository.', 'action': 'Check the error log for other messages adjacent to this one that might help you resolve the problem. If you cannot resolve the problem, contact Oracle Support.'}</code></pre>



<h2 id="adminclientlog-no-rest-endpoint" class="wp-block-heading"><code class="nowrap">adminclient.log</code>: no REST endpoint</h2>



<p class="wp-block-paragraph"><code class="nowrap">adminclient.log</code> is the client’s connection log. It is located in the client installation at <code class="nowrap">$OGG_HOME/var/log/adminclient.log</code>, not in any deployment, and no service can read it.</p>



<h2 id="report-files-rpt" class="wp-block-heading">Report files (<code class="nowrap">.rpt</code>)</h2>



<p class="wp-block-paragraph">The report files are accessible with <code class="nowrap">list_extract_reports</code> and <code class="nowrap">get_extract_report</code>:</p>



<pre class="wp-block-code"><code># /services/v2/extracts/EXT/info/reports
&gt;&gt;&gt; ogg_client.list_extract_reports(extract="EXT")
&#091;{'name': 'EXT.dsc'}, {'name': 'EXT.rpt'}]

# /services/v2/extracts/EXT/info/reports/EXT.rpt
&gt;&gt;&gt; ogg_client.get_extract_report(extract="EXT", report="EXT.rpt")
{'lines': &#091;'', '***********************************************************************', '                 Oracle GoldenGate Capture for Oracle', ...], '$schema': 'ogg:report'}</code></pre>



<p class="wp-block-paragraph"><code class="nowrap">get_current_extract_report()</code> skips the <code class="nowrap">.dsc</code>, resolves the current <code class="nowrap">.rpt</code> for you, and returns <code class="nowrap">None</code> if the extract has no report listed :</p>



<pre class="wp-block-code"><code># /services/v2/extracts/EXT/info/reports
# /services/v2/extracts/EXT/info/reports/EXT.rpt
&gt;&gt;&gt; ogg_client.get_current_extract_report(extract="EXT")
{'lines': &#091;'', '***********************************************************************', '                 Oracle GoldenGate Capture for Oracle', ...], '$schema': 'ogg:report'}</code></pre>



<p class="wp-block-paragraph">The same endpoints/methods exist for replicats.</p>



<h2 id="diagnostics" class="wp-block-heading">Diagnostics</h2>



<p class="wp-block-paragraph">If a diagnostics collection exists, it is a resource named <code class="nowrap">{process}.diagnostics</code>, listed under the same <code class="nowrap">info</code> path as reports. It has a status while it runs, and the start and finish times once done :</p>



<pre class="wp-block-code"><code># /services/v2/extracts/EXT/info/diagnostics - while running
&gt;&gt;&gt; ogg_client.list_extract_diagnostics(extract="EXT")
&#091;{'name': 'EXT.diagnostics', 'status': 'running', 'started': '2026-09-26T17:23:21Z'}]

# /services/v2/extracts/EXT/info/diagnostics - when done
&gt;&gt;&gt; ogg_client.list_extract_diagnostics(extract="EXT")
&#091;{'name': 'EXT.diagnostics', 'status': 'stopped', 'started': '2026-09-26T17:23:21Z', 'finished': '2026-09-26T17:33:22Z'}]</code></pre>



<p class="wp-block-paragraph">When several collections exist, the previous ones are kept as <code class="nowrap">EXT.diagnostics.1</code> (and so on), and the newest keeps the base name. An extract that never had a collection returns an empty list. It returns a JSON, but unlike reports and configuration files, it is a structured document with one entry per sample:</p>



<pre class="wp-block-code"><code># /services/v2/extracts/EXT/info/diagnostics/EXT.diagnostics
&gt;&gt;&gt; diag = ogg_client.get_extract_diagnostic(extract="EXT", diagnostic="EXT.diagnostics")
&gt;&gt;&gt; list(diag)
&#091;'$schema', 'started', 'finished', 'collectionFrequency', 'collectionDuration', 'status', 'samples']
&gt;&gt;&gt; diag&#091;"samples"]&#091;0]&#091;"processSamples"]&#091;0]&#091;"process"]
{'type': 'capture', 'state': {'status': 'waiting', 'detail': 'WAITING FOR TRANSACTION'}, 'bytesMined': 2264, 'bytesSent': 1524, 'messagesCaptured': 5, 'lag': 0}</code></pre>



<p class="wp-block-paragraph">The same pair exists for a replicat: <code class="nowrap">list_replicat_diagnostics()</code> / <code class="nowrap">get_replicat_diagnostic()</code>.</p>



<h2 id="configuration-files" class="wp-block-heading">Configuration files</h2>



<p class="wp-block-paragraph"><code class="nowrap">GLOBALS</code>, parameter files, and the rest of what a deployment keeps under its config directory come from <code class="nowrap">/services/v2/config/files</code>. That directory is <code class="nowrap">$OGG_CONF_HOME</code>, or <code class="nowrap">$OGG_DEPLOYMENT_HOME/etc/conf</code>. They return their content directly:</p>



<pre class="wp-block-code"><code># /services/v2/config/files
&gt;&gt;&gt; ogg_client.list_configuration_files(ogg_service="adminsrvr")
&#091;{'name': 'EXT1.prm'}, {'name': 'EXT2.prm'}, {'name': 'REP1.prm'}, ...]

# /services/v2/config/files/EXT1.prm
&gt;&gt;&gt; ogg_client.get_configuration_file(file="EXT1.prm", ogg_service="adminsrvr")
{'lines': &#091;'EXTRACT EXT1', 'USERIDALIAS pdb1 DOMAIN OracleGoldenGate', 'EXTTRAIL qa/xa', 'TABLE APP_PDB1.TEST;'], '$schema': 'ogg:config'}</code></pre>



<p class="wp-block-paragraph">To summarize, for each file:</p>



<figure class="wp-block-table"><table><thead><tr><th>File</th><th>API endpoint</th><th><code class="nowrap">oggrestapi.<wbr>py</code> call</th></tr></thead><tbody><tr><td>Service logs (<code class="nowrap">adminsrvr.<wbr>log</code>, etc.)</td><td><code class="nowrap">/services/v2/logs/default</code> with <code>Accept: text/plain</code></td><td><code>get_log('default', content=True)</code> / <code>get_service_log('default', content=True)</code></td></tr><tr><td>Service Manager logs (<code class="nowrap">ServiceManager.<wbr>log</code>, <code class="nowrap">restapi.<wbr>log</code>)</td><td>Same resource, against the Service Manager’s port</td><td><code class="nowrap">get_service_manager_log()</code></td></tr><tr><td><code class="nowrap">ggserr.<wbr>log</code></td><td><code class="nowrap">/services/v2/messages</code></td><td><code class="nowrap">list_messages()</code></td></tr><tr><td><code class="nowrap">adminclient.<wbr>log</code></td><td>None, it is a client-side file under <code class="nowrap">&lt;OGG_HOME&gt;/var/log</code></td><td>None (filesystem only)</td></tr><tr><td><code class="nowrap">extract.<wbr>log</code> or <code class="nowrap">replicat.<wbr>log</code></td><td>None, they are filtered views of <code class="nowrap">ggserr.<wbr>log</code></td><td>None (filtered view)</td></tr><tr><td>Reports (<code class="nowrap">.<wbr>rpt</code>)</td><td><code>/services/v2/extracts/{extract}/info/reports[/{report}]</code> (or <code class="nowrap">replicats</code>), content returned directly</td><td><code class="nowrap">list_extract_reports()</code> / <code class="nowrap">get_extract_report()</code>, or <code class="nowrap">get_current_extract_report()</code> / <code class="nowrap">get_current_replicat_report()</code></td></tr><tr><td>Diagnostics</td><td><code>/services/v2/extracts/{extract}/info/diagnostics[/{diagnostic}]</code></td><td><code class="nowrap">list_extract_diagnostics()</code> / <code class="nowrap">get_extract_diagnostic()</code></td></tr><tr><td>Configuration files</td><td><code class="nowrap">/services/v2/config/files/{file}</code> (content returned directly)</td><td><code class="nowrap">list_configuration_files()</code> / <code class="nowrap">get_configuration_file()</code></td></tr></tbody></table></figure>
<p>L’article <a href="https://www.dbi-services.com/blog/retrieving-goldengate-logs-reports-and-diagnostics-with-the-rest-api/">Retrieving GoldenGate logs, reports, and diagnostics with the REST API</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/retrieving-goldengate-logs-reports-and-diagnostics-with-the-rest-api/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Anatomy of an Enterprise Manager metric extension</title>
		<link>https://www.dbi-services.com/blog/metric-extension-export-format-enterprise-manager/</link>
					<comments>https://www.dbi-services.com/blog/metric-extension-export-format-enterprise-manager/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Fri, 23 Oct 2026 11:09:17 +0000</pubDate>
				<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[emcli]]></category>
		<category><![CDATA[enterprise manager]]></category>
		<category><![CDATA[metric extension]]></category>
		<category><![CDATA[OEM]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=47635</guid>

					<description><![CDATA[<p>Recently, I was working on deploying Metric Extensions automatically in Enterprise Manager, but the more I looked into it, the more it turned into a different question: what does a Metric Extension look like ? In a previous blog, I presented the SYSMAN tables containing the metric extensions definitions. I also showed how to retrieve [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/metric-extension-export-format-enterprise-manager/">Anatomy of an Enterprise Manager metric extension</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Recently, I was working on deploying Metric Extensions automatically in Enterprise Manager, but the more I looked into it, the more it turned into a different question: what does a Metric Extension look like ? In a <a href="https://www.dbi-services.com/blog/listing-and-querying-oracle-enterprise-manager-metric-extensions/" target="_blank" rel="noopener noreferrer">previous blog</a>, I presented the <code>SYSMAN</code> tables containing the metric extensions definitions. I also showed how to retrieve the targets where a metric extension is deployed. Now, it’s time to look at <strong>formatting metric extensions</strong>.</p>



<p class="wp-block-paragraph">Once again, <code>emcli help</code> does not show any verb for creating metric extensions:</p>



<pre class="wp-block-code"><code>$ emcli help | grep -i metric_extension
    export_metric_extension           -- Export a metric extension to an archive file
    get_unused_metric_extensions      -- Get a list of unused metric extensions
    import_metric_extension           -- Import a metric extension archive file
    publish_metric_extension          -- Publish a metric extension for use by all administrators
    save_metric_extension_draft       -- Save a deployable draft of a metric extension</code></pre>



<p class="wp-block-paragraph">Unfortunately, nothing in the documentation will help us to create a metric extension outside of the web UI. So let’s reverse engineer them. The plan is the following:</p>



<ul class="wp-block-list">
<li>Create metric extensions in the web UI</li>



<li>Export the XML files with <code>emcli</code></li>



<li>Later, create new metric extensions or update existing metrics more easily, directly with <code>emcli</code></li>
</ul>



<h2 id="building-a-minimal-metric-extension" class="wp-block-heading">Building a minimal metric extension</h2>



<p class="wp-block-paragraph">Let’s build the most basic metric extension first, with <code>Host</code> as the target type. I will choose the <code>OS Command - Multiple Columns</code> adapter, and just an <code>echo</code> command.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img fetchpriority="high" decoding="async" width="720" height="405" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/001_me-genprop-filled.png" alt="" class="wp-image-47636" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/001_me-genprop-filled.png 720w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/001_me-genprop-filled-300x169.png 300w" sizes="(max-width: 720px) 100vw, 720px" /></figure>
</div>


<p class="wp-block-paragraph">The adapter itself is <code>/bin/echo</code> with <code>em_result=1</code> as its argument and <code>em_result=</code> as the <em>Starts With</em> prefix. It is the same <code>em_result=</code>-prefixed, delimiter-tokenized convention the OEM plugin’s <code>OSLineToken</code> fetchlet uses, which turned out not to be a coincidence (more on that below). One column, <code>value</code>, type Number:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" width="1024" height="246" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/002_me-columns-value-added-1024x246.png" alt="" class="wp-image-47637" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/002_me-columns-value-added-1024x246.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/002_me-columns-value-added-300x72.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/002_me-columns-value-added-1536x369.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/002_me-columns-value-added-765x184.png 765w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/002_me-columns-value-added.png 1580w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p class="wp-block-paragraph">The <em><strong>Test</strong></em> step lets you run the metric job for real against a live target before committing to anything. Since the idea is to have a valid archive that we can play with, it should definitely be tested.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" width="1024" height="224" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/003_me-test-results-value1-1024x224.png" alt="" class="wp-image-47638" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/003_me-test-results-value1-1024x224.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/003_me-test-results-value1-1536x335.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/003_me-test-results-value1-300x66.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/003_me-test-results-value1-765x167.png 765w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/003_me-test-results-value1.png 1580w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<h2 id="exporting-the-metric-extension" class="wp-block-heading">Exporting the metric extension</h2>



<p class="wp-block-paragraph">You cannot export an <em>Editable</em> metric. The docs restrict <code>export_metric_extension</code> to <em>Production, Deployable Draft, and Published</em> versions. So the first step is promoting it, either via the UI or with <code>emcli</code>:</p>



<pre class="wp-block-code"><code>$ emcli save_metric_extension_draft -target_type=host -name='ME$minimal_uptime_check' -version=1</code></pre>



<p class="wp-block-paragraph">Don’t worry if you receive no output, this is expected. Then, you can export the metric:</p>



<pre class="wp-block-code"><code>$ emcli export_metric_extension -file_name=/tmp/minimal_uptime_check_v1.zip -target_type=host -name='ME$minimal_uptime_check' -version=1
Metric extension &#091;host, ME$minimal_uptime_check, 1] exported successfully to file /tmp/minimal_uptime_check_v1.zip</code></pre>



<p class="wp-block-paragraph">There are three files in the archive:</p>



<pre class="wp-block-code"><code>metadata/ME#24#minimal_uptime_check.xml
collection/ME#24#minimal_uptime_check.xml
mea.xml</code></pre>



<p class="wp-block-paragraph">The first one is the manifest, <code>mea.xml</code>:</p>



<pre class="wp-block-code"><code>&lt;MetricExtensionArchive name="MEAME$minimal_uptime_check"&gt;
&lt;MetricExtension name="ME$minimal_uptime_check" type="host" version="1" is_repos="FALSE" display_name="Minimal Uptime Check" description="Minimal test ME - single OS command column, no credentials." version_comment=" " metadata_digest="18147044f9d9fab88c8ebbee213d2e1e" coll_digest="e1a287a6df820c5e7d7fda0bd0bfcb3f" attach_digest="d41d8cd98f00b204e9800998ecf8427e"&gt;
&lt;/MetricExtension&gt;
&lt;/MetricExtensionArchive&gt;</code></pre>



<p class="wp-block-paragraph">The metric definition is in <code>metadata/ME#24#minimal_uptime_check.xml</code>:</p>



<pre class="wp-block-code"><code>&lt;TargetMetadataExt EXT_NAME="ME$minimal_uptime_check" EXT_VERSION="1" TARGET_TYPE="host"&gt;&lt;Metric NAME="ME$minimal_uptime_check" TYPE="TABLE"&gt;
&lt;Display&gt;
&lt;Label NLSID="NLS_METRIC_hostME$minimal_uptime_check"&gt;Minimal Uptime Check&lt;/Label&gt;
&lt;Description NLSID="NLS_DESCRIPTION_hostME$minimal_uptime_check"&gt;Minimal test ME - single OS command column, no credentials.&lt;/Description&gt;
&lt;/Display&gt;
&lt;TableDescriptor&gt;
&lt;ColumnDescriptor NAME="value" TYPE="NUMBER"&gt;
&lt;Display&gt;
&lt;Label NLSID="NLS_COLUMN_hostME$minimal_uptime_checkvalue"&gt;Value&lt;/Label&gt;
&lt;/Display&gt;
&lt;/ColumnDescriptor&gt;
&lt;/TableDescriptor&gt;
&lt;QueryDescriptor FETCHLET_ID="OSLineToken"&gt;
&lt;Property NAME="command" SCOPE="GLOBAL"&gt;/bin/echo&lt;/Property&gt;
&lt;Property NAME="scriptLoc" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;%scriptsDir%&lt;/Property&gt;
&lt;Property NAME="args" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;em_result=1&lt;/Property&gt;
&lt;Property NAME="delimiter" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;|&lt;/Property&gt;
&lt;Property NAME="startsWith" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;em_result=&lt;/Property&gt;
&lt;Property NAME="ENVEM_TARGET_NAME" SCOPE="INSTANCE" OPTIONAL="TRUE"&gt;NAME&lt;/Property&gt;
&lt;CredentialRef NAME="OSCreds"&gt;&lt;/CredentialRef&gt;
&lt;/QueryDescriptor&gt;
&lt;/Metric&gt;
&lt;/TargetMetadataExt&gt;</code></pre>



<p class="wp-block-paragraph">Here, we can see that:</p>



<ul class="wp-block-list">
<li><code>FETCHLET_ID="OSLineToken"</code> is the <strong>same fetchlet</strong> a full OEM plugin’s <code>TargetMetadata</code> XML uses for its <code>OSLineToken</code>-based collectors. The wizard is just generating the same fetchlet configuration.</li>



<li><code>command</code>, <code>args</code>, <code>delimiter</code> and <code>startsWith</code> are the <strong>basic properties</strong> fields the adapter step.</li>



<li><code>ENVEM_TARGET_NAME</code> is set even though I did not alter the Environment Variables section. This is the default.</li>
</ul>



<p class="wp-block-paragraph">The last file, <code>collection/ME#24#minimal_uptime_check.xml</code>, is the schedule:</p>



<pre class="wp-block-code"><code>&lt;TargetCollectionExt EXT_NAME="ME$minimal_uptime_check" EXT_VERSION="1" TARGET_TYPE="host"&gt;&lt;CollectionItem NAME="ME$minimal_uptime_check" UPLOAD="YES"&gt;
	&lt;Schedule&gt;
		&lt;IntervalSchedule INTERVAL="15" TIME_UNIT="Min"/&gt;
	&lt;/Schedule&gt;
	&lt;MetricColl NAME="ME$minimal_uptime_check"&gt;
	&lt;/MetricColl&gt;
&lt;/CollectionItem&gt;
&lt;/TargetCollectionExt&gt;</code></pre>



<h2 id="adding-a-second-column-and-a-real-environment-variable" class="wp-block-heading">Adding a second column and a real environment variable</h2>



<p class="wp-block-paragraph">Let’s build a second metric extension from the UI with the same command, but with two columns instead of one. I will also add a custom environment variable this time.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="350" height="200" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/004_me-envvar-dialog-filled.png" alt="" class="wp-image-47639" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/004_me-envvar-dialog-filled.png 350w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/004_me-envvar-dialog-filled-300x171.png 300w" sizes="auto, (max-width: 350px) 100vw, 350px" /></figure>
</div>


<p class="wp-block-paragraph">For comparison, this is what the Environment Variables table looks like before you add anything to it: <code>EM_TARGET_NAME</code> is already there.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="720" height="340" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/005_me-envvar-list-default.png" alt="" class="wp-image-47640" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/005_me-envvar-list-default.png 720w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/005_me-envvar-list-default-300x142.png 300w" sizes="auto, (max-width: 720px) 100vw, 720px" /></figure>
</div>


<p class="wp-block-paragraph">And the second column added on the <em>Columns</em> step:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="253" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/006_me-columns-two-added-1024x253.png" alt="" class="wp-image-47641" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/006_me-columns-two-added-1024x253.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/006_me-columns-two-added-766x189.png 766w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/006_me-columns-two-added-300x74.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/006_me-columns-two-added-1536x379.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/006_me-columns-two-added.png 1580w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p class="wp-block-paragraph">Exporting this metric extension and comparing the metadata against the first archive shows exactly what each choice changes:</p>



<pre class="wp-block-code"><code>$ diff unpacked/metadata/*.xml unpacked_v2/metadata/*.xml
1c1
&lt; &lt;TargetMetadataExt EXT_NAME="ME$minimal_uptime_check" EXT_VERSION="1" TARGET_TYPE="host"&gt;&lt;Metric NAME="ME$minimal_uptime_check" TYPE="TABLE"&gt;
---
&gt; &lt;TargetMetadataExt EXT_NAME="ME$minimal_uptime_check_v2" EXT_VERSION="1" TARGET_TYPE="host"&gt;&lt;Metric NAME="ME$minimal_uptime_check_v2" TYPE="TABLE"&gt;
3,4c3,4
&lt; &lt;Label NLSID="NLS_METRIC_hostME$minimal_uptime_check"&gt;Minimal Uptime Check&lt;/Label&gt;
&lt; &lt;Description NLSID="NLS_DESCRIPTION_hostME$minimal_uptime_check"&gt;Minimal test ME - single OS command column, no credentials.&lt;/Description&gt;
---
&gt; &lt;Label NLSID="NLS_METRIC_hostME$minimal_uptime_check_v2"&gt;Minimal Uptime Check V2&lt;/Label&gt;
&gt; &lt;Description NLSID="NLS_DESCRIPTION_hostME$minimal_uptime_check_v2"&gt;Variant with env var and 2 columns, for archive-format comparison.&lt;/Description&gt;
9c9,14
&lt; &lt;Label NLSID="NLS_COLUMN_hostME$minimal_uptime_checkvalue"&gt;Value&lt;/Label&gt;
---
&gt; &lt;Label NLSID="NLS_COLUMN_hostME$minimal_uptime_check_v2value"&gt;Value&lt;/Label&gt;
&gt; &lt;/Display&gt;
&gt; &lt;/ColumnDescriptor&gt;
&gt; &lt;ColumnDescriptor NAME="state" TYPE="STRING"&gt;
&gt; &lt;Display&gt;
&gt; &lt;Label NLSID="NLS_COLUMN_hostME$minimal_uptime_check_v2state"&gt;State&lt;/Label&gt;
16c21
&lt; &lt;Property NAME="args" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;em_result=1&lt;/Property&gt;
---
&gt; &lt;Property NAME="args" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;em_result=1|running&lt;/Property&gt;
19a25
&gt; &lt;Property NAME="ENVMY_CUSTOM_VAR" SCOPE="INSTANCE" OPTIONAL="TRUE"&gt;hello_world&lt;/Property&gt;</code></pre>



<p class="wp-block-paragraph">The second extension was saved under a new name, <code>ME$minimal_uptime_check_v2</code>, so every <code>EXT_NAME</code>, <code>NAME</code> and <code>NLSID</code> differs. Removing this noise, there are three changes that matter: the new <code>state</code> column, the <code>args</code> property, and the environment variable <code>ENVMY_CUSTOM_VAR</code>.</p>



<pre class="wp-block-code"><code>...
&gt; &lt;ColumnDescriptor NAME="state" TYPE="STRING"&gt;
&gt; &lt;Display&gt;
&gt; &lt;Label NLSID="NLS_COLUMN_hostME$minimal_uptime_checkstate"&gt;State&lt;/Label&gt;
&gt; &lt;/Display&gt;
&gt; &lt;/ColumnDescriptor&gt;

...

&lt; &lt;Property NAME="args" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;em_result=1&lt;/Property&gt;
---
&gt; &lt;Property NAME="args" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;em_result=1|running&lt;/Property&gt;

...

&gt; &lt;Property NAME="ENVMY_CUSTOM_VAR" SCOPE="INSTANCE" OPTIONAL="TRUE"&gt;hello_world&lt;/Property&gt;</code></pre>



<p class="wp-block-paragraph">The new column is a second <code>ColumnDescriptor</code>, in the same order as the pipe-delimited output (<code>em_result=1|running</code>). The environment variable becomes <code>ENVMY_CUSTOM_VAR=hello_world</code>.</p>



<h2 id="adding-an-uploaded-script" class="wp-block-heading">Adding an uploaded script</h2>



<p class="wp-block-paragraph">Both examples above run an OS command directly. Let’s create a metric extension that runs an <strong>uploaded script</strong> instead of a literal command. Exporting a metric extension with an uploaded Perl script adds a fourth path to the archive, alongside the usual <code>mea.xml</code>/<code>metadata</code>/<code>collection</code>:</p>



<pre class="wp-block-code"><code>mea.xml
metadata/ME#24#script_uptime_check.xml
collection/ME#24#script_uptime_check.xml
scripts/uptime_check.pl</code></pre>



<p class="wp-block-paragraph">The manifest lists the script by its real name, in a <code>&lt;scripts&gt;</code> element, with an updated <code>attach_digest</code> parameter.</p>



<pre class="wp-block-code"><code>&lt;MetricExtensionArchive name="MEAME$script_uptime_check"&gt;
&lt;MetricExtension name="ME$script_uptime_check" type="host" version="1" is_repos="FALSE" display_name="Script Uptime Check" description="ME cloned from minimal_uptime_check, adapter modified to run an uploaded script." version_comment=" " metadata_digest="e4778d5b4a756fed3d7e73aa55931d88" coll_digest="bc223105d5b2e6e31d5123eadcbd929b" attach_digest="d04278db542142ccb9a89be64c3e151e"&gt;
&lt;scripts name="uptime_check.pl"&gt;
&lt;/scripts&gt;
&lt;/MetricExtension&gt;
&lt;/MetricExtensionArchive&gt;</code></pre>



<p class="wp-block-paragraph">The <code>QueryDescriptor</code> has a new property called <code>script</code>, giving the script name. The <code>command</code> property is also updated, pointing at <code>perl</code> instead of <code>/bin/echo</code>.</p>



<pre class="wp-block-code"><code>&lt;QueryDescriptor FETCHLET_ID="OSLineToken"&gt;
&lt;Property NAME="command" SCOPE="GLOBAL"&gt;%perlBin%/perl&lt;/Property&gt;
&lt;Property NAME="script" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;%scriptsDir%/uptime_check.pl&lt;/Property&gt;
&lt;Property NAME="scriptLoc" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;%scriptsDir%&lt;/Property&gt;
&lt;Property NAME="delimiter" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;|&lt;/Property&gt;
&lt;Property NAME="startsWith" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;em_result=&lt;/Property&gt;
&lt;Property NAME="ENVEM_TARGET_NAME" SCOPE="INSTANCE" OPTIONAL="TRUE"&gt;NAME&lt;/Property&gt;
&lt;CredentialRef NAME="OSCreds"&gt;&lt;/CredentialRef&gt;
&lt;/QueryDescriptor&gt;</code></pre>



<h2 id="repository-side-metric-extension" class="wp-block-heading">Repository-side metric extension</h2>



<p class="wp-block-paragraph">Let’s conclude by creating a <strong>repository-side</strong> metric extension, which runs SQL against the OMS repository. With a <strong>repository-side Metric Extension</strong>, the <code>FETCHLET_ID</code> is <code>REPOSITORY_SQL</code>, and the properties are replaced by a <code>Source</code> property containing the SQL query:</p>



<pre class="wp-block-code"><code>&lt;QueryDescriptor FETCHLET_ID="REPOSITORY_SQL"&gt;
&lt;Property NAME="Type" SCOPE="GLOBAL"&gt;SQL&lt;/Property&gt;
&lt;Property NAME="Source" SCOPE="GLOBAL"&gt;SELECT target_guid,
       target_type,
       1 AS host_count
FROM mgmt$target
WHERE target_type = 'host'&lt;/Property&gt;
&lt;/QueryDescriptor&gt;</code></pre>



<p class="wp-block-paragraph">In the manifest, <code>is_repos="TRUE"</code> is what differentiates this kind of metric extension. The <code>&lt;Metric&gt;</code> element itself picks up <code>REPOSITORY="TRUE"</code> too, and the columns default to the query <code>SELECT</code> list (<code>TARGET_TYPE</code>, <code>HOST_COUNT</code> here) rather than anything typed by hand on the Columns step.</p>



<pre class="wp-block-code"><code>&lt;MetricExtensionArchive name="MEAME$sql_target_count"&gt;
&lt;MetricExtension name="ME$sql_target_count" type="host" version="1" is_repos="TRUE" display_name="SQL Target Count" description="Test ME using the repository-side SQL adapter: counts targets by status." version_comment=" " metadata_digest="0ca6af88e017fb535ae2d59f7f1b6143" coll_digest="e7be6de5a24037de10846bc29391bf2f" attach_digest="d41d8cd98f00b204e9800998ecf8427e"&gt;
&lt;/MetricExtension&gt;
&lt;/MetricExtensionArchive&gt;</code></pre>



<p class="wp-block-paragraph">With all of this in mind, let’s try to feed the Enterprise Manager a custom metric extension built entirely by hand, using a small Python script as the collector instead of a literal command.</p>



<h2 id="import-our-first-custom-metric-extension" class="wp-block-heading">Import our first custom metric extension</h2>



<p class="wp-block-paragraph">Here is a script reading the 1-minute load average from <code>/proc/loadavg</code> and printing it with the same <code>em_result=</code> prefix expected by <code>OSLineToken</code>.</p>



<pre class="wp-block-code"><code>#!/usr/bin/env python3
with open("/proc/loadavg") as f:
    load1 = f.read().split()&#091;0]
print(f"em_result={load1}")</code></pre>



<p class="wp-block-paragraph">We have three XML files and the script, written by hand rather than exported from the wizard. <code>metadata/ME#24#python_load_check.xml</code>:</p>



<pre class="wp-block-code"><code>&lt;TargetMetadataExt EXT_NAME="ME$python_load_check" EXT_VERSION="1" TARGET_TYPE="host"&gt;&lt;Metric NAME="ME$python_load_check" TYPE="TABLE"&gt;
&lt;Display&gt;
&lt;Label NLSID="NLS_METRIC_hostME$python_load_check"&gt;Python Load Check&lt;/Label&gt;
&lt;Description NLSID="NLS_DESCRIPTION_hostME$python_load_check"&gt;Runs an uploaded Python script that reads the 1-minute load average from /proc/loadavg.&lt;/Description&gt;
&lt;/Display&gt;
&lt;TableDescriptor&gt;
&lt;ColumnDescriptor NAME="value" TYPE="NUMBER"&gt;
&lt;Display&gt;
&lt;Label NLSID="NLS_COLUMN_hostME$python_load_checkvalue"&gt;Value&lt;/Label&gt;
&lt;/Display&gt;
&lt;/ColumnDescriptor&gt;
&lt;/TableDescriptor&gt;
&lt;QueryDescriptor FETCHLET_ID="OSLineToken"&gt;
&lt;Property NAME="command" SCOPE="GLOBAL"&gt;/usr/bin/python3&lt;/Property&gt;
&lt;Property NAME="script" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;%scriptsDir%/load_check.py&lt;/Property&gt;
&lt;Property NAME="scriptLoc" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;%scriptsDir%&lt;/Property&gt;
&lt;Property NAME="delimiter" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;|&lt;/Property&gt;
&lt;Property NAME="startsWith" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;em_result=&lt;/Property&gt;
&lt;Property NAME="ENVEM_TARGET_NAME" SCOPE="INSTANCE" OPTIONAL="TRUE"&gt;NAME&lt;/Property&gt;
&lt;CredentialRef NAME="OSCreds"&gt;&lt;/CredentialRef&gt;
&lt;/QueryDescriptor&gt;
&lt;/Metric&gt;
&lt;/TargetMetadataExt&gt;</code></pre>



<p class="wp-block-paragraph">The <code>command</code> property points at the interpreter (<code>/usr/bin/python3</code> instead of <code>%perlBin%/perl</code>), while <code>script</code> points at the uploaded file. Everything else is untouched. Here is the <code>collection/ME#24#python_load_check.xml</code> file:</p>



<pre class="wp-block-code"><code>&lt;TargetCollectionExt EXT_NAME="ME$python_load_check" EXT_VERSION="1" TARGET_TYPE="host"&gt;&lt;CollectionItem NAME="ME$python_load_check" UPLOAD="YES"&gt;
	&lt;Schedule&gt;
		&lt;IntervalSchedule INTERVAL="15" TIME_UNIT="Min"/&gt;
	&lt;/Schedule&gt;
	&lt;MetricColl NAME="ME$python_load_check"&gt;
	&lt;/MetricColl&gt;
&lt;/CollectionItem&gt;
&lt;/TargetCollectionExt&gt;</code></pre>



<p class="wp-block-paragraph">And the manifest <code>mea.xml</code>:</p>



<pre class="wp-block-code"><code>&lt;MetricExtensionArchive name="MEAME$python_load_check"&gt;
&lt;MetricExtension name="ME$python_load_check" type="host" version="1" is_repos="FALSE" display_name="Python Load Check" description="Runs an uploaded Python script that reads the 1-minute load average from /proc/loadavg." version_comment=" " metadata_digest="0000000000000000000000000000000" coll_digest="0000000000000000000000000000000" attach_digest="0000000000000000000000000000000"&gt;
&lt;scripts name="load_check.py"&gt;
&lt;/scripts&gt;
&lt;/MetricExtension&gt;
&lt;/MetricExtensionArchive&gt;</code></pre>



<p class="wp-block-paragraph">The digests are just zeroes here. The OMS recomputes and stores new digests when you import the metric.</p>



<p class="wp-block-paragraph">Now, let’s build the archive with the same layout <code>export_metric_extension</code> produces, assembled by hand instead:</p>



<pre class="wp-block-code"><code>$ find build -type f
build/mea.xml
build/metadata/ME#24#python_load_check.xml
build/collection/ME#24#python_load_check.xml
build/scripts/load_check.py

$ cd build &amp;&amp; zip -rq ../python_load_check.zip . &amp;&amp; cd ..
$ unzip -l python_load_check.zip
Archive:  python_load_check.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  08-27-2026 14:38   metadata/
     1212  08-27-2026 14:38   metadata/ME#24#python_load_check.xml
        0  08-27-2026 14:38   collection/
      321  08-27-2026 14:38   collection/ME#24#python_load_check.xml
        0  08-27-2026 14:38   scripts/
      116  08-27-2026 14:38   scripts/load_check.py
      529  08-27-2026 14:38   mea.xml
---------                     -------
     2178                     7 files</code></pre>



<p class="wp-block-paragraph">The last step is to import the metric extension with <code>emcli</code>:</p>



<pre class="wp-block-code"><code>$ emcli import_metric_extension -file_name=python_load_check.zip
Metric extension file python_load_check.zip imported successfully</code></pre>



<p class="wp-block-paragraph">You now know how to build metric extensions without going through the web UI.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/metric-extension-export-format-enterprise-manager/">Anatomy of an Enterprise Manager metric extension</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/metric-extension-export-format-enterprise-manager/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to detect a suspended GoldenGate extract</title>
		<link>https://www.dbi-services.com/blog/how-to-detect-a-suspended-goldengate-extract/</link>
					<comments>https://www.dbi-services.com/blog/how-to-detect-a-suspended-goldengate-extract/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Mon, 19 Oct 2026 06:59:00 +0000</pubDate>
				<category><![CDATA[GoldenGate]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[26ai]]></category>
		<category><![CDATA[adminclient]]></category>
		<category><![CDATA[eventactions]]></category>
		<category><![CDATA[extract]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[ogg]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[rest api]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=47510</guid>

					<description><![CDATA[<p>While testing EVENTACTIONS (SUSPEND) on an extract, I noticed that nothing in INFO tells you the process is suspended. The extract shows as RUNNING, the lag might look fine for a while, and yet it has stopped capturing. You may use SUSPEND on purpose (to pause an extract when a specific DDL goes by, for [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/how-to-detect-a-suspended-goldengate-extract/">How to detect a suspended GoldenGate extract</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">While testing <code>EVENTACTIONS (SUSPEND)</code> on an extract, I noticed that nothing in <code>INFO</code> tells you the process is suspended. The extract shows as <code>RUNNING</code>, the lag might look fine for a while, and yet it has stopped capturing. You may use <code>SUSPEND</code> on purpose (to pause an extract when a specific DDL goes by, for instance). Or someone else put it in a parameter file and you have to monitor the result. Either way, you need another way to know.</p>



<p class="wp-block-paragraph">In the end, <strong><code>SEND EXTRACT ... STATUS</code> is the only command that reports the suspended state</strong>, and it works the same way through the REST API, where the text is already parsed for you. A simpler check exists in the report file, generating an <code>OGG-01281</code>, but it has a limit I will show at the end.</p>



<p class="wp-block-paragraph">In this blog, I will run an <code>EXT1</code> extract capturing from <code>PDB1</code>. It has a DDL <code>EVENTACTIONS</code> trigger on a <code>CREATE USER</code> statement run in <code>PDB1</code>. The trigger suspends the extract whenever a user is created:</p>



<pre class="wp-block-code"><code>EXTRACT EXT1
USERIDALIAS pdb1 DOMAIN OracleGoldenGate
EXTTRAIL pdb1/e1
DDL INCLUDE ALL OBJTYPE 'USER' OPTYPE CREATE EVENTACTIONS (SUSPEND)
DDLOPTIONS REPORT
TABLE APP_PDB1.*;</code></pre>



<h2 id="extract-status-before-suspend" class="wp-block-heading">Extract status before <code>SUSPEND</code></h2>



<p class="wp-block-paragraph">Once started, the extract shows as <code>RUNNING</code> in <code>INFO ALL</code>:</p>



<pre class="wp-block-code"><code>OGG (http://vmogg:7810 ogg_test_01) 2&gt; INFO ALL
Program     Status      Group       Type                   Lag at Chkpt       Time Since Chkpt

EXTRACT     RUNNING     EXT1        INTEGRATED                  00:00:00           00:00:06</code></pre>



<p class="wp-block-paragraph"><code>SEND EXTRACT EXT1 STATUS</code> reports where the extract is in its life cycle. Here, the extract has nothing to capture, so the status is <code>At EOF</code>:</p>



<pre class="wp-block-code"><code>OGG (http://vmogg:7810 ogg_test_01) 2&gt; SEND EXTRACT EXT1 STATUS

Sending STATUS request to Extract group EXT1 ...

EXTRACT EXT1 (PID 21617)
  Current status: Recovery complete: At EOF

  Current read position:
  Redo thread #: 0
  Sequence #: 0
  RBA: 0
  Timestamp: 2026-09-26 06:26:24.000000
  SCN: 0.115334476 (115334476)
  Current write position:
  Sequence #: 11
  RBA: 6942
  Timestamp: 2026-09-26 06:26:20.463368
  Extract Trail: pdb1/e1</code></pre>



<h2 id="suspending-the-extract" class="wp-block-heading">Suspending the extract</h2>



<p class="wp-block-paragraph">Let’s create a user in <code>PDB1</code>, which matches the <code>DDL</code> rule:</p>



<pre class="wp-block-code"><code>SQL&gt; CREATE USER SUSPTEST IDENTIFIED BY "Suspend_Test_1";

User created.</code></pre>



<p class="wp-block-paragraph">Half a minute later, <code>INFO EXT1</code> still says <code>RUNNING</code> with a lag of 0 seconds, but the checkpoint was last updated 30 seconds ago:</p>



<pre class="wp-block-code"><code>OGG (http://vmogg:7810 ogg_test_01) 2&gt; INFO EXT1

Extract    EXT1      Last Started 2026-09-26 06:26   Status RUNNING
Checkpoint Lag       00:00:00 (updated 00:00:30 ago)
Process ID           21617
Log Read Checkpoint  Oracle Integrated Redo Logs, per-PDB
                     2026-09-26 06:26:18
                     SCN 0.115334462 (115334462)
Encryption Profile   LocalWallet
No Replicat groups found, but some coordinated threads may have been excluded.</code></pre>



<p class="wp-block-paragraph">Two minutes after the <code>CREATE USER</code>, the same command gives:</p>



<pre class="wp-block-code"><code>OGG (http://vmogg:7810 ogg_test_01) 2&gt; INFO EXT1

Extract    EXT1      Last Started 2026-09-26 06:26   Status RUNNING
Checkpoint Lag       00:00:00 (updated 00:02:00 ago)
Process ID           21617
Log Read Checkpoint  Oracle Integrated Redo Logs, per-PDB
                     2026-09-26 06:26:18
                     SCN 0.115334462 (115334462)
Encryption Profile   LocalWallet
No Replicat groups found, but some coordinated threads may have been excluded.</code></pre>



<p class="wp-block-paragraph">The status is still <code>RUNNING</code>, and <strong>the lag value does not grow</strong>: <code>Checkpoint Lag</code> stays at <code>00:00:00</code>. What grows is the time since the checkpoint was updated (<code>updated 00:00:30 ago</code>, then <code>updated 00:02:00 ago</code>), and the checkpoint position (<code>SCN 0.115334462</code>) never moves. That is a hint that something is wrong (or right), but nothing in this output says it is a suspend.</p>



<h2 id="send-extract-status-shows-the-suspended-state" class="wp-block-heading"><code>SEND EXTRACT STATUS</code> shows the suspended state</h2>



<p class="wp-block-paragraph">Now the same <code>SEND EXTRACT EXT1 STATUS</code> as before:</p>



<pre class="wp-block-code"><code>OGG (http://vmogg:7810 ogg_test_01) 2&gt; SEND EXTRACT EXT1 STATUS

Sending STATUS request to Extract group EXT1 ...

EXTRACT EXT1 (PID 21617)
  Current status: Recovery complete: Suspended, waiting to be resumed

  Current read position:
  Redo thread #: 1
  Sequence #: 3045
  RBA: 58777616
  Timestamp: 2026-09-26 06:26:27.000000
  SCN: 0.115334507 (115334507)
  Current write position:
  Sequence #: 11
  RBA: 6942
  Timestamp: 2026-09-26 06:26:20.463368
  Extract Trail: pdb1/e1</code></pre>



<p class="wp-block-paragraph">Comparing the two outputs, <strong>the only line that tells you the extract is suspended is <code>Current status</code></strong>:</p>



<ul class="wp-block-list">
<li>Before: <code>Current status: Recovery complete: At EOF</code>. It could have been some other message if it was still extracting.</li>



<li>After: <code>Current status: Recovery complete: Suspended, waiting to be resumed</code></li>
</ul>



<p class="wp-block-paragraph">Everything else in <code>INFO</code> is compatible with a healthy extract. To resume it, run <code>SEND EXTRACT EXT1 RESUME</code>.</p>



<h2 id="which-command-shows-the-suspend" class="wp-block-heading">Which command shows the suspend?</h2>



<p class="wp-block-paragraph"><code>STATUS EXTRACT</code> looks like the natural command, but it is not the same as <code>SEND EXTRACT ... STATUS</code>. On the suspended extract, <code>STATUS EXTRACT</code> gives:</p>



<pre class="wp-block-code"><code>OGG (http://vmogg:7810 ogg_test_01) 4&gt; STATUS EXTRACT EXT1
EXTRACT EXT1: RUNNING</code></pre>



<p class="wp-block-paragraph"><code>INFO EXT1 DETAIL</code> adds the trail, the outbound server SCNs and the file locations, but its header is the same as <code>INFO EXT1</code>:</p>



<pre class="wp-block-code"><code>OGG (http://vmogg:7810 ogg_test_01) 5&gt; INFO EXT1 DETAIL

Extract    EXT1      Last Started 2026-09-26 06:26   Status RUNNING
Checkpoint Lag       00:00:00 (updated 00:02:00 ago)
Process ID           21617
Log Read Checkpoint  Oracle Integrated Redo Logs, per-PDB
                     2026-09-26 06:26:18
                     SCN 0.115334462 (115334462)
Encryption Profile   LocalWallet

  Target Extract Trails:

  Trail Name                                       Seqno        RBA     Max MB Trail Type

  pdb1/e1                                             11       6942       2000 EXTTRAIL

Integrated Extract outbound server first scn: 0.115313606 (115313606)

Integrated Extract outbound server filtering start scn: 0.115313606 (115313606)</code></pre>



<figure class="wp-block-table"><table><thead><tr><th>Command</th><th>Before the suspend</th><th>After the suspend</th></tr></thead><tbody><tr><td><code>INFO ALL</code></td><td><code>RUNNING</code></td><td><code>RUNNING</code></td></tr><tr><td><code>INFO EXT1</code></td><td><code>RUNNING</code></td><td><code>RUNNING</code></td></tr><tr><td><code>INFO EXT1 DETAIL</code></td><td><code>RUNNING</code></td><td><code>RUNNING</code></td></tr><tr><td><code>STATUS EXTRACT EXT1</code></td><td><code>RUNNING</code></td><td><code>RUNNING</code></td></tr><tr><td><code>SEND EXTRACT EXT1 STATUS</code></td><td><code>Recovery complete: At EOF</code></td><td><code>Recovery complete: Suspended, waiting to be resumed</code></td></tr></tbody></table></figure>



<p class="wp-block-paragraph"><strong>Only <code>SEND EXTRACT ... STATUS</code> shows the suspend.</strong> <code>STATUS EXTRACT</code>, like <code>INFO</code>, just reports the state of the process.</p>



<h2 id="getting-the-status-with-the-rest-api" class="wp-block-heading">Getting the status with the REST API</h2>



<p class="wp-block-paragraph">The <code>SEND</code> commands are available through the <code>/services/{version}/extracts/{extract}/command</code> endpoint, and the response already contains the parsed status.</p>



<h3 id="with-the-oggrestapi-python-client" class="wp-block-heading">With the <code>oggrestapi</code> Python client</h3>



<p class="wp-block-paragraph">With the <a href="https://juliendelattre.com/tools/ogg-python-client/" target="_blank" rel="noopener noreferrer">GoldenGate Python client</a>, <code>execute_command_extract</code> sends the request. First, initialize the client:</p>



<pre class="wp-block-code"><code>&gt;&gt;&gt; from oggrestapi import OGGRestAPI
&gt;&gt;&gt; ogg_client = OGGRestAPI(
    url='http://vmogg:7810',
    username='ogg',
    password='ogg'
)
Connected to OGG REST API at http://vmogg:7810</code></pre>



<p class="wp-block-paragraph">Here is the raw output given by the <code>execute_command_extract</code> method:</p>



<pre class="wp-block-code"><code>&gt;&gt;&gt; resp = ogg_client.execute_command_extract('EXT1', data={'command': 'STATUS'})
&gt;&gt;&gt; resp
{'$schema': 'er:commandResult', 'reply': 'OKNODOT\tEXTRACT EXT1 (PID 41522)\n  Current status: Recovery complete: Suspended, waiting to be resumed\n\n  Current read position:\n  Redo thread #: 1\n  Sequence #: 3047\n  RBA: 92686352\n  Timestamp: 2026-09-26 08:48:41.000000\n  SCN: 0.115434621 (115434621)\n  Current write position:\n  Sequence #: 1\n  RBA: 5679\n  Timestamp: 2026-09-26 08:48:37.669072\n  Extract Trail: pdb1/r1\n\n', 'replyData': {'$schema': 'er:statusResult', 'status': {'groupName': 'EXT1', 'processId': '41522', 'processType': 'extract', 'status': 'Recovery complete: Suspended, waiting to be resumed', 'extractReadingPosition': &#091;{'$schema': 'dbms:positionInfo', 'position': {'redoThread': 1, 'csn': 115434621, 'seqno': 3047, 'rba': 92686352, 'timestamp': '2026-09-26T08:48:41.000Z'}}], 'extractWritingPosition': &#091;{'$schema': 'ogg:trailPosition', 'sequence': 1, 'offset': 5679, 'name': 'r1', 'path': 'pdb1/'}]}}}</code></pre>



<p class="wp-block-paragraph">In GoldenGate REST API, the status is available in <code>replyData.status.status</code>:</p>



<pre class="wp-block-code"><code>&gt;&gt;&gt; resp&#091;'replyData']&#091;'status']&#091;'status']
'Recovery complete: Suspended, waiting to be resumed'</code></pre>



<p class="wp-block-paragraph">To avoid repeating the <code>replyData</code> navigation everywhere, custom methods are available in <code>oggrestapi</code>:</p>



<pre class="wp-block-code"><code>def get_extract_status_detail(self, extract):
    result = self.execute_command_extract(extract, data={'command': 'STATUS'})
    return str(result.get('replyData', {}).get('status', {}).get('status', ''))

def is_extract_suspended(self, extract):
    return 'suspended' in self.get_extract_status_detail(extract).lower()</code></pre>



<pre class="wp-block-code"><code>&gt;&gt;&gt; ogg_client.get_extract_status_detail('EXT1')
'Recovery complete: Suspended, waiting to be resumed'

&gt;&gt;&gt; ogg_client.is_extract_suspended('EXT1')
True</code></pre>



<h3 id="with-the-requests-module" class="wp-block-heading">With the <code>requests</code> module</h3>



<p class="wp-block-paragraph">Without the client, send <code>STATUS</code> as the command with the <code>requests</code> library:</p>



<pre class="wp-block-code"><code>import requests

url = 'http://vmogg:7810/services/v2/extracts/EXT1'
auth = ('ogg', 'ogg')

response = requests.post(f'{url}/command', auth=auth, json={'command': 'STATUS'}).json()&#091;'response']</code></pre>



<p class="wp-block-paragraph">The response contains the same <code>replyData</code> field, with the status already extracted:</p>



<pre class="wp-block-code"><code>&gt;&gt;&gt; response&#091;'replyData']&#091;'status']&#091;'status']
'Recovery complete: Suspended, waiting to be resumed'</code></pre>



<p class="wp-block-paragraph">Note that this is not the same as the <code>info/status</code> endpoint, which returns the process state as <code>INFO</code> does. It says <code>running</code> for a suspended extract, but it contains the two values behind the lag line of <code>INFO</code>:</p>



<pre class="wp-block-code"><code>&gt;&gt;&gt; requests.get(f'{url}/info/status', auth=auth).json()&#091;'response']
{'$schema': 'ogg:extractStatus', 'status': 'running', 'processId': 21617, 'lastStarted': '2026-09-26T06:26:09.798Z', 'lag': 0, 'sinceLagReported': 125, 'position': '0.115334462'}</code></pre>



<p class="wp-block-paragraph"><code>lag</code> is the checkpoint lag of <code>INFO</code>, and <strong><code>sinceLagReported</code> is the <code>updated ... ago</code> value in seconds</strong>.</p>



<h3 id="adding-a-lag-check" class="wp-block-heading">Adding a lag check</h3>



<p class="wp-block-paragraph">For a monitoring script, the status can be paired with a lag check. <code>get_extract_status</code> costs a single <code>GET</code> call, and a healthy extract keeps updating its checkpoint. A large <code>sinceLagReported</code> is a cheap first filter before sending the <code>STATUS</code> command:</p>



<pre class="wp-block-code"><code>&gt;&gt;&gt; status = ogg_client.get_extract_status('EXT1')
&gt;&gt;&gt; status&#091;'status'] == 'running' and status&#091;'sinceLagReported'] &gt; 60 and ogg_client.is_extract_suspended('EXT1')
True</code></pre>



<p class="wp-block-paragraph">When this returns <code>True</code>, you have a running extract that has not moved for more than a minute and that reports itself as suspended. Whether you should then resume it depends on why the <code>SUSPEND</code> was there in the first place. An alert is usually the right answer. If you do want to resume it from the script, it is another <code>execute_command_extract</code> call:</p>



<pre class="wp-block-code"><code>&gt;&gt;&gt; ogg_client.execute_command_extract('EXT1', data={'command': 'RESUME'})</code></pre>



<h2 id="a-simpler-check-ogg-01281-in-the-report" class="wp-block-heading">A simpler check: <code>OGG-01281</code> in the report</h2>



<p class="wp-block-paragraph">The suspend also leaves a trace in the extract report file, with the <code>OGG-01281</code> message:</p>



<pre class="wp-block-code"><code>2026-09-26 06:26:29  INFO    OGG-00487  DDL operation included &#091;INCLUDE ALL OBJTYPE 'USER' OPTYPE CREATE EVENTACTIONS (SUSPEND)], optype &#091;CREATE], objtype &#091;USER], objowner "", objname "SUSPTEST".

2026-09-26 06:26:30  INFO    OGG-01281  Processed SUSPEND event from DDL on object name SUSPTEST, DDL seqno 0, marker seqno 0.</code></pre>



<p class="wp-block-paragraph">Is this message specific to <code>SUSPEND</code>? To check it, I ran the same <code>DDL</code> rule with other <code>EVENTACTIONS</code>, recreating the user each time:</p>



<pre class="wp-block-code"><code># DDL INCLUDE ALL OBJTYPE 'USER' OPTYPE CREATE EVENTACTIONS (ROLLOVER)
2026-09-26 06:29:32  INFO    OGG-01285  Processed ROLLOVER event from DDL on object name SUSPTEST, DDL seqno 0, marker seqno 0.

# DDL INCLUDE ALL OBJTYPE 'USER' OPTYPE CREATE EVENTACTIONS (LOG INFO)
2026-09-26 06:30:37  INFO    OGG-06117  Processed LOG event from DDL on object name SUSPTEST, DDL seqno 0, marker seqno 0.

# DDL INCLUDE ALL OBJTYPE 'USER' OPTYPE CREATE EVENTACTIONS (LOG WARNING)
2026-09-26 06:31:40  WARNING OGG-06118  Processed LOG event from DDL on object name SUSPTEST, DDL seqno 0, marker seqno 0.</code></pre>



<p class="wp-block-paragraph"><code>ROLLOVER</code> raises <code>OGG-01285</code>, and <code>LOG</code> raises <code>OGG-06117</code> (or <code>OGG-06118</code> with <code>WARNING</code>). <code>IGNORE</code> raises nothing at all beyond the <code>OGG-00487</code> line. So <strong><code>OGG-01281</code> is only raised by <code>SUSPEND</code></strong>.</p>



<p class="wp-block-paragraph">The limit of this check is that the report is a history, not a state. When resuming the extract, the <code>OGG-01281</code> line stays in the report, and a new message is written after it:</p>



<pre class="wp-block-code"><code>2026-09-26 06:26:30  INFO    OGG-01281  Processed SUSPEND event from DDL on object name SUSPTEST, DDL seqno 0, marker seqno 0.
2026-09-26 06:28:29  INFO    OGG-01824  Resume processing from SUSPEND state for process EXT1.</code></pre>



<p class="wp-block-paragraph"><strong>Finding <code>OGG-01281</code> in the report does not mean the extract is suspended now.</strong> You have to check that no <code>OGG-01824</code> comes after it. On top of that, the report is rolled over at each restart of the extract, so it only covers the current run. With the REST API, the report is available through <code>get_extract_report</code>, whose <code>lines</code> key contains the list of lines:</p>



<pre class="wp-block-code"><code>&gt;&gt;&gt; report = ogg_client.get_extract_report('EXT1', 'EXT1.rpt')
&gt;&gt;&gt; &#091;line for line in report&#091;'lines'] if 'OGG-01281' in line or 'OGG-01824' in line]
&#091;'2026-09-26 06:26:30  INFO    OGG-01281  Processed SUSPEND event from DDL on object name SUSPTEST, DDL seqno 0, marker seqno 0.']</code></pre>



<p class="wp-block-paragraph">This is useful to find out when and why the extract was suspended, since the message contains the object that triggered it. But to know whether it is suspended right now, <code>STATUS</code> is the reliable way.</p>



<h2 id="the-full-check-in-one-script" class="wp-block-heading">The full check in one script</h2>



<p class="wp-block-paragraph">Here is a small script with everything seen in this blog. It runs the lag check, the suspend check, and the log analysis to find the culprit, then prints an info message.</p>



<pre class="wp-block-code"><code>from oggrestapi import OGGRestAPI

ogg_client = OGGRestAPI(url='http://vmogg:7810', username='ogg', password='ogg')

extract = 'EXT1'
status = ogg_client.get_extract_status(extract)

# Lag check: a running extract whose checkpoint has not moved for a minute
if status&#091;'status'] == 'running' and status&#091;'sinceLagReported'] &gt; 60:
    # Suspend check
    if ogg_client.is_extract_suspended(extract):
        # Log analysis: the last OGG-01281 or OGG-01824 tells who suspended it
        report = ogg_client.get_extract_report(extract, f'{extract}.rpt')
        events = &#091;line for line in report&#091;'lines'] if 'OGG-01281' in line or 'OGG-01824' in line]
        if events and 'OGG-01281' in events&#091;-1]:
            print(f'INFO: {extract} is suspended. Culprit: {events&#091;-1]}')
        else:
            print(f'INFO: {extract} is suspended, but no OGG-01281 in the current report')
    else:
        print(f'INFO: {extract} is not suspended, but its checkpoint has not moved for {status&#091;"sinceLagReported"]} seconds')
else:
    print(f'INFO: {extract} is not suspended')</code></pre>



<pre class="wp-block-code"><code>INFO: EXT1 is suspended. Culprit: 2026-09-26 06:26:30  INFO    OGG-01281  Processed SUSPEND event from DDL on object name SUSPTEST, DDL seqno 0, marker seqno 0.</code></pre>



<h2 id="what-about-replicats" class="wp-block-heading">What about replicats?</h2>



<p class="wp-block-paragraph">It works the same way for a replicat. I tested it with an integrated replicat applying the trail of <code>EXT1</code>, with <code>DDL INCLUDE ALL OBJTYPE 'USER' OPTYPE CREATE EVENTACTIONS (SUSPEND)</code> in its parameter file. <code>INFO</code> reports <code>RUNNING</code>, and <code>SEND REPLICAT REP1 STATUS</code> reports the suspended state. <code>is_replicat_suspended</code> returns <code>True</code>, and <code>SEND REPLICAT REP1 RESUME</code> resumes it with an <code>OGG-01824</code>.</p>



<p class="wp-block-paragraph">The suspend is still logged with <code>OGG-01281</code>, but <strong>the culprit is not named</strong>. The replicat suspends before applying the DDL, so the report says <code>object name N/A</code>. The user name only appears in the <code>OGG-00487</code> line after the resume operation:</p>



<pre class="wp-block-code"><code>2026-09-26 08:56:19  INFO    OGG-01281  Processed SUSPEND event from DDL on object name N/A in file /u01/app/ogg/deployments/ogg_test_01/var/lib/data/pdb1/q1000000000, RBA 5284.
2026-09-26 08:58:02  INFO    OGG-01824  Resume processing from SUSPEND state for process REP1.
2026-09-26 08:58:02  INFO    OGG-00487  DDL operation included &#091;INCLUDE ALL OBJTYPE 'USER' OPTYPE CREATE EVENTACTIONS (SUSPEND)], optype &#091;CREATE], objtype &#091;USER], objowner "", objname "SUSPTEST".</code></pre>



<h2 id="summary" class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">To know whether a GoldenGate extract is suspended by an <code>EVENTACTIONS (SUSPEND)</code> rule:</p>



<ul class="wp-block-list">
<li>Do not rely on <code>INFO ALL</code>, <code>INFO</code>, <code>INFO DETAIL</code> or <code>STATUS EXTRACT</code>. They all report <code>RUNNING</code>, and the checkpoint lag stays the same. Only the <code>updated ... ago</code> value grows.</li>



<li>Run <code>SEND EXTRACT EXT1 STATUS</code> in the <code>adminclient</code>: <code>Current status: Recovery complete: Suspended, waiting to be resumed</code> appears when the extract is suspended.</li>



<li>In a script, use <code>is_extract_suspended</code> from the Python client, or send the same <code>STATUS</code> command to <code>/services/v2/extracts/EXT1/command</code> and read <code>replyData.status.status</code>.</li>



<li>Filter with <code>sinceLagReported</code> from <code>info/status</code> (for instance more than 60 seconds) before sending the <code>STATUS</code> command.</li>



<li>Use <code>OGG-01281</code> in the report to find out when and why the extract was suspended, and check that no <code>OGG-01824</code> came after it.</li>



<li>Resume with <code>SEND EXTRACT EXT1 RESUME</code>, or with the <code>RESUME</code> command through <code>execute_command_extract</code>.</li>
</ul>
<p>L’article <a href="https://www.dbi-services.com/blog/how-to-detect-a-suspended-goldengate-extract/">How to detect a suspended GoldenGate extract</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/how-to-detect-a-suspended-goldengate-extract/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Import order matters for GoldenGate instantiation filtering</title>
		<link>https://www.dbi-services.com/blog/import-order-matters-for-goldengate-instantiation-filtering/</link>
					<comments>https://www.dbi-services.com/blog/import-order-matters-for-goldengate-instantiation-filtering/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Thu, 15 Oct 2026 15:37:47 +0000</pubDate>
				<category><![CDATA[GoldenGate]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[19c]]></category>
		<category><![CDATA[DataPump]]></category>
		<category><![CDATA[expdp]]></category>
		<category><![CDATA[extract]]></category>
		<category><![CDATA[impdp]]></category>
		<category><![CDATA[Initial load]]></category>
		<category><![CDATA[instantiation]]></category>
		<category><![CDATA[migration]]></category>
		<category><![CDATA[ogg]]></category>
		<category><![CDATA[Partitioning]]></category>
		<category><![CDATA[replicat]]></category>
		<category><![CDATA[scn]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=47906</guid>

					<description><![CDATA[<p>When migrating a large database with GoldenGate, most of the volume is sometimes in old partitions which no longer change. A common shortcut is to move them first with expdp/impdp, even before GoldenGate is set up. Everything else, the active partitions and the non-partitioned tables, is exported once the extract is running, with PREPARECSN set. [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/import-order-matters-for-goldengate-instantiation-filtering/">Import order matters for GoldenGate instantiation filtering</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">When migrating a large database with GoldenGate, most of the volume is sometimes in old partitions which no longer change. A common shortcut is to move them first with <code>expdp</code>/<code>impdp</code>, even before GoldenGate is set up. Everything else, the active partitions and the non-partitioned tables, is exported once the extract is running, with <code>PREPARECSN</code> set. <a href="https://www.dbi-services.com/blog/goldengate-instantiation-csn-explained/" target="_blank" rel="noopener noreferrer">Instantiation filtering</a> then makes the replicat skip what Data Pump already loaded.</p>



<p class="wp-block-paragraph">However, there is an important aspect here: <code>dba_apply_instantiated_objects</code> has <strong>one row per table</strong>. Every <code>impdp</code> overwrites it, and there is no way for GoldenGate or Data Pump to know which import should be considered the correct one for populating the <code>dba_apply_instantiated_objects</code> view. The old partitions and the live data of a table share that row, so in a multi-import initial load the last import always wins.</p>



<h2 id="standard-scenario-importing-old-partitions-first" class="wp-block-heading">Standard scenario: importing old partitions first</h2>



<p class="wp-block-paragraph">The old partitions of all partitioned tables are moved first, in one dump, before GoldenGate is involved. Since no GoldenGate extract exists yet, <code>PREPARECSN</code> has not run either:</p>



<pre class="wp-block-code"><code>$ expdp sales/***@PDB1 dumpfile=old_part.dmp tables=SALES.ORDERS_PART:P_OLD,SALES.ITEMS_PART:P_OLD
. . exported "SALES"."ITEMS_PART":"P_OLD"                 5.523 KB       5 rows
. . exported "SALES"."ORDERS_PART":"P_OLD"                5.523 KB       5 rows
Job "SALES"."SYS_EXPORT_TABLE_01" successfully completed at Sun Oct 11 13:52:08 2026 elapsed 0 00:00:10

$ impdp sales/***@PDB2 dumpfile=old_part.dmp
. . imported "SALES"."ITEMS_PART":"P_OLD"                 5.523 KB       5 rows
. . imported "SALES"."ORDERS_PART":"P_OLD"                5.523 KB       5 rows
Job "SALES"."SYS_IMPORT_FULL_01" successfully completed at Sun Oct 11 13:52:12 2026 elapsed 0 00:00:02</code></pre>



<p class="wp-block-paragraph">Querying the instantiation view on the target returns nothing, as expected:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_database, source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
WHERE source_object_owner = 'SALES';

no rows selected</code></pre>



<p class="wp-block-paragraph">Data Pump only populates this view when the table was prepared with <code>PREPARECSN</code> before the export. These first imports are invisible to GoldenGate.</p>



<h3 id="starting-the-extract" class="wp-block-heading">Starting the extract</h3>



<p class="wp-block-paragraph">The extract connects to <code>PDB1</code> and captures the <code>SALES</code> schema into the trail <code>pt</code>. It is added, registered and started in the <code>adminclient</code>, logged in to <code>PDB1</code>. The registration SCN is where capture begins:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; DBLOGIN USERIDALIAS oggadmin_pdb1 DOMAIN OracleGoldenGate
Successfully logged into database PDB1.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 3&gt; ADD EXTRACT EXTPT, INTEGRATED TRANLOG, BEGIN NOW
2026-10-11T13:52:13Z  INFO    OGG-08100  Integrated Extract added.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 4&gt; REGISTER EXTRACT EXTPT DATABASE
2026-10-11T13:52:14Z  INFO    OGG-02003  Extract group EXTPT successfully registered with database at SCN 189554050.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 5&gt; ADD EXTTRAIL pt, EXTRACT EXTPT
2026-10-11T13:52:14Z  INFO    OGG-08100  EXTTRAIL added.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 6&gt; VIEW PARAMS EXTPT
EXTRACT EXTPT
USERIDALIAS oggadmin_pdb1 DOMAIN OracleGoldenGate
EXTTRAIL pt
TABLE SALES.*;

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 7&gt; START EXTRACT EXTPT
2026-10-11T13:52:14Z  INFO    OGG-00975  Extract group EXTPT starting.
2026-10-11T13:52:14Z  INFO    OGG-15426  Extract group EXTPT started.</code></pre>



<p class="wp-block-paragraph">Some workload follows on the live data, before <code>PREPARECSN</code>:</p>



<pre class="wp-block-code"><code>SQL&gt; INSERT INTO sales.orders_part VALUES (1001, 'ACTIVE');

1 row created.

SQL&gt; INSERT INTO sales.orders_part VALUES (1002, 'ACTIVE');

1 row created.

SQL&gt; INSERT INTO sales.items_part VALUES (1001, 'ACTIVE');

1 row created.

SQL&gt; INSERT INTO sales.items_part VALUES (1002, 'ACTIVE');

1 row created.

SQL&gt; INSERT INTO sales.customers VALUES (4, 'LIVE');

1 row created.

SQL&gt; COMMIT;

Commit complete.</code></pre>



<p class="wp-block-paragraph">Then the schema is prepared in the <code>adminclient</code>. <code>ADD SCHEMATRANDATA ... ALLCOLS</code> sets <code>PREPARECSN</code> on the schema in <code>NOWAIT</code> mode:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; DBLOGIN USERIDALIAS oggadmin_pdb1 DOMAIN OracleGoldenGate
Successfully logged into database PDB1.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 3&gt; ADD SCHEMATRANDATA SALES ALLCOLS
2026-10-11T13:52:15Z  INFO    OGG-01788  SCHEMATRANDATA has been added on schema "SALES".
2026-10-11T13:52:15Z  INFO    OGG-01976  SCHEMATRANDATA for scheduling columns has been added on schema "SALES".
2026-10-11T13:52:15Z  INFO    OGG-01977  SCHEMATRANDATA for all columns has been added on schema "SALES".
2026-10-11T13:52:15Z  INFO    OGG-10154  Schema level PREPARECSN set to mode NOWAIT on schema "SALES"</code></pre>



<h3 id="exporting-and-importing-the-live-data" class="wp-block-heading">Exporting and importing the live data</h3>



<p class="wp-block-paragraph">The active partitions are exported next, in a single <code>expdp</code>, deliberately <strong>without</strong> <code>FLASHBACK_SCN</code>. Data Pump chooses a consistent SCN itself, for each table:</p>



<pre class="wp-block-code"><code>$ expdp sales/***@PDB1 dumpfile=active_part.dmp tables=SALES.ORDERS_PART:P_ACTIVE,SALES.ITEMS_PART:P_ACTIVE
FLASHBACK automatically enabled to preserve database integrity.
. . exported "SALES"."ITEMS_PART":"P_ACTIVE"                5.5 KB       2 rows
. . exported "SALES"."ORDERS_PART":"P_ACTIVE"               5.5 KB       2 rows
Job "SALES"."SYS_EXPORT_TABLE_01" successfully completed at Sun Oct 11 13:52:29 2026 elapsed 0 00:00:12</code></pre>



<p class="wp-block-paragraph">Live inserts after that export are therefore not in the dump:</p>



<pre class="wp-block-code"><code>SQL&gt; INSERT INTO sales.orders_part VALUES (1003, 'ACTIVE');

1 row created.

SQL&gt; INSERT INTO sales.items_part VALUES (1003, 'ACTIVE');

1 row created.

SQL&gt; COMMIT;

Commit complete.</code></pre>



<p class="wp-block-paragraph">Every other table goes in a third export, at the schema level, excluding the partitioned tables. Any number of normal tables fits in this one export, and a new partitioned table is added to the exclusion:</p>



<pre class="wp-block-code"><code>$ cat rest.par
dumpfile=rest.dmp
schemas=SALES
exclude=TABLE:"IN ('ORDERS_PART','ITEMS_PART')"</code></pre>



<pre class="wp-block-code"><code>$ expdp sales/***@PDB1 parfile=rest.par
FLASHBACK automatically enabled to preserve database integrity.
. . exported "SALES"."CUSTOMERS"                          5.515 KB       4 rows
Job "SALES"."SYS_EXPORT_SCHEMA_01" successfully completed at Sun Oct 11 13:52:55 2026 elapsed 0 00:00:24</code></pre>



<p class="wp-block-paragraph">A live insert after this export is not in the dump either:</p>



<pre class="wp-block-code"><code>SQL&gt; INSERT INTO sales.customers VALUES (5, 'LIVE');

1 row created.

SQL&gt; COMMIT;

Commit complete.</code></pre>



<p class="wp-block-paragraph">The partitioned tables already exist on the target, so the active partitions are appended. A regular import creates the normal tables:</p>



<pre class="wp-block-code"><code>$ impdp sales/***@PDB2 dumpfile=active_part.dmp table_exists_action=append
Table "SALES"."ORDERS_PART" exists. Data will be appended to existing table but all dependent metadata will be skipped due to table_exists_action of append
Table "SALES"."ITEMS_PART" exists. Data will be appended to existing table but all dependent metadata will be skipped due to table_exists_action of append
. . imported "SALES"."ITEMS_PART":"P_ACTIVE"                5.5 KB       2 rows
. . imported "SALES"."ORDERS_PART":"P_ACTIVE"               5.5 KB       2 rows
Job "SALES"."SYS_IMPORT_FULL_01" successfully completed at Sun Oct 11 13:53:00 2026 elapsed 0 00:00:03

$ impdp sales/***@PDB2 dumpfile=rest.dmp
. . imported "SALES"."CUSTOMERS"                          5.515 KB       4 rows
Job "SALES"."SYS_IMPORT_FULL_01" successfully completed at Sun Oct 11 13:53:04 2026 elapsed 0 00:00:02</code></pre>



<p class="wp-block-paragraph">The instantiation view now has one row per table. The partitioned tables have SCNs from the second export, while <code>CUSTOMERS</code> has one from the third export:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_database, source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
WHERE source_object_owner = 'SALES'
ORDER BY source_object_name;

SOURCE_DATABASE SOURCE_OBJECT_NAME INSTANTIATION_SCN
--------------- ------------------ -----------------
PDB1            CUSTOMERS                  189585331
PDB1            ITEMS_PART                 189572011
PDB1            ORDERS_PART                189572008

3 rows selected.</code></pre>



<h3 id="replicat-with-instantiation-filtering" class="wp-block-heading">Replicat with instantiation filtering</h3>



<p class="wp-block-paragraph">Each instantiation SCN applies to the whole table, and we use instantiation filtering in the replicat. It is added and started in the <code>adminclient</code>:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; DBLOGIN USERIDALIAS oggadmin_pdb2 DOMAIN OracleGoldenGate
Successfully logged into database PDB2.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb2@CDB02/PDB2) 3&gt; ADD REPLICAT REPPT, INTEGRATED, EXTTRAIL pt, CHECKPOINTTABLE OGGADMIN.CHECKPOINT
2026-10-11T13:53:05Z  INFO    OGG-08100  Integrated Replicat added.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb2@CDB02/PDB2) 4&gt; VIEW PARAMS REPPT
REPLICAT REPPT
DBOPTIONS ENABLE_INSTANTIATION_FILTERING
USERIDALIAS oggadmin_pdb2 DOMAIN OracleGoldenGate
MAP SALES.*, TARGET SALES.*;

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb2@CDB02/PDB2) 5&gt; START REPLICAT REPPT
2026-10-11T13:53:05Z  INFO    OGG-00975  Replicat group REPPT starting.
2026-10-11T13:53:05Z  INFO    OGG-15445  Replicat group REPPT started.</code></pre>



<p class="wp-block-paragraph">Once the replicat has caught up, the target tables have the same number of rows as the source:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT COUNT(*) FROM sales.orders_part;

COUNT(*)
--------
       8

1 row selected.

SQL&gt; SELECT COUNT(*) FROM sales.items_part;

COUNT(*)
--------
       8

1 row selected.

SQL&gt; SELECT COUNT(*) FROM sales.customers;

COUNT(*)
--------
       5

1 row selected.</code></pre>



<p class="wp-block-paragraph">In the <code>adminclient</code>, we can see the transaction done after creating the extract, on each table:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; STATS REPLICAT REPPT, TOTAL, TABLE SALES.*

Sending STATS TOTAL TABLE SALES.* request to Replicat group REPPT ...


Start of statistics at 2026-10-11 13:53:18.


Integrated Replicat statistics:

        Total transactions                                 2.00
        Redirected                                         0.00
        Replicated procedures                              0.00
        DDL operations                                     0.00
        Stored procedures                                  0.00
        Datatype functionality                             0.00
        Operation type functionality                       0.00
        Event actions                                      0.00
        Database compatibility                             0.00
        Direct transactions ratio                          0.00%

Replicating from SALES.ORDERS_PART to PDB2.SALES.ORDERS_PART:

*** Total statistics since 2026-10-11 13:53:07 ***
    Total inserts                              1.00
    Total updates                              0.00
    Total deletes                              0.00
    Total upserts                              0.00
    Total discards                             0.00
    Total operations                           1.00

Replicating from SALES.ITEMS_PART to PDB2.SALES.ITEMS_PART:

*** Total statistics since 2026-10-11 13:53:07 ***
    Total inserts                              1.00
    Total updates                              0.00
    Total deletes                              0.00
    Total upserts                              0.00
    Total discards                             0.00
    Total operations                           1.00

Replicating from SALES.CUSTOMERS to PDB2.SALES.CUSTOMERS:

*** Total statistics since 2026-10-11 13:53:07 ***
    Total inserts                              1.00
    Total updates                              0.00
    Total deletes                              0.00
    Total upserts                              0.00
    Total discards                             0.00
    Total operations                           1.00

End of statistics.</code></pre>



<p class="wp-block-paragraph">The only inserts are the three rows added after the exports, one per table. There is no <code>ORA-00001</code>: the rows inserted before the exports were replayed from the trail and skipped by the instantiation filtering. Without <code>ENABLE_INSTANTIATION_FILTERING</code> they would have hit the primary key.</p>



<p class="wp-block-paragraph"><code>FLASHBACK_SCN</code> is not needed here. The replicat compares commit CSNs against <code>instantiation_scn</code>, not against the export’s flashback point. You only need <code>PREPARECSN</code> to have run before the export of the live data.</p>



<p class="wp-block-paragraph">Foreign keys between tables that are in different exports are something to keep in mind.</p>



<h2 id="wrong-order-importing-old-partitions-after" class="wp-block-heading">Wrong order: importing old partitions after</h2>



<p class="wp-block-paragraph">What happens if, for some reason, you run the initial load of the active partitions first, with <code>PREPARECSN</code> and instantiation filtering, and then migrate the old partitions? Let’s try this, by first adding <code>trandata</code>:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; DBLOGIN USERIDALIAS oggadmin_pdb1 DOMAIN OracleGoldenGate
Successfully logged into database PDB1.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 3&gt; ADD TRANDATA SALES.ORDERS_PART PREPARECSN NOWAIT
2026-10-11T13:53:42Z  INFO    OGG-15132  Logging of supplemental redo data enabled for table SALES.ORDERS_PART.
2026-10-11T13:53:42Z  INFO    OGG-15133  TRANDATA for scheduling columns has been added on table SALES.ORDERS_PART.
2026-10-11T13:53:42Z  INFO    OGG-15438  Enabling logical replication (with all keys) on auto capture table SALES.ORDERS_PART.
2026-10-11T13:53:42Z  INFO    OGG-15135  TRANDATA for instantiation CSN has been added on table SALES.ORDERS_PART.</code></pre>



<p class="wp-block-paragraph">The old partitions are exported before the extract exists:</p>



<pre class="wp-block-code"><code>$ expdp sales/***@PDB1 dumpfile=b_old.dmp tables=SALES.ORDERS_PART:P_OLD
FLASHBACK automatically enabled to preserve database integrity.
. . exported "SALES"."ORDERS_PART":"P_OLD"                5.523 KB       5 rows
Job "SALES"."SYS_EXPORT_TABLE_01" successfully completed at Sun Oct 11 13:53:55 2026 elapsed 0 00:00:11</code></pre>



<p class="wp-block-paragraph">The extract is then registered and started, as in the first scenario:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; DBLOGIN USERIDALIAS oggadmin_pdb1 DOMAIN OracleGoldenGate
Successfully logged into database PDB1.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 3&gt; ADD EXTRACT EXTPT, INTEGRATED TRANLOG, BEGIN NOW
2026-10-11T13:53:57Z  INFO    OGG-08100  Integrated Extract added.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 4&gt; REGISTER EXTRACT EXTPT DATABASE
2026-10-11T13:54:00Z  INFO    OGG-02003  Extract group EXTPT successfully registered with database at SCN 189596053.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 5&gt; ADD EXTTRAIL pt, EXTRACT EXTPT
2026-10-11T13:54:00Z  INFO    OGG-08100  EXTTRAIL added.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 6&gt; START EXTRACT EXTPT
2026-10-11T13:54:00Z  INFO    OGG-00975  Extract group EXTPT starting.
2026-10-11T13:54:00Z  INFO    OGG-15426  Extract group EXTPT started.</code></pre>



<p class="wp-block-paragraph">Rows <code>1001</code> and <code>1002</code> are inserted into <code>P_ACTIVE</code>, and the live data is exported. Row <code>1003</code> is inserted after the export:</p>



<pre class="wp-block-code"><code>$ expdp sales/***@PDB1 dumpfile=b_active.dmp tables=SALES.ORDERS_PART:P_ACTIVE
FLASHBACK automatically enabled to preserve database integrity.
. . exported "SALES"."ORDERS_PART":"P_ACTIVE"               5.5 KB       2 rows
Job "SALES"."SYS_EXPORT_TABLE_01" successfully completed at Sun Oct 11 13:54:09 2026 elapsed 0 00:00:08</code></pre>



<p class="wp-block-paragraph">This time, on the target, <strong>the live data is imported first</strong>:</p>



<pre class="wp-block-code"><code>$ impdp sales/***@PDB2 dumpfile=b_active.dmp
. . imported "SALES"."ORDERS_PART":"P_ACTIVE"               5.5 KB       2 rows
Job "SALES"."SYS_IMPORT_FULL_01" successfully completed at Sun Oct 11 13:54:14 2026 elapsed 0 00:00:02</code></pre>



<p class="wp-block-paragraph">For now, the boundary is above the registration SCN <code>189596053</code>:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
WHERE source_object_owner = 'SALES'
AND source_object_name = 'ORDERS_PART';

SOURCE_OBJECT_NAME INSTANTIATION_SCN
------------------ -----------------
ORDERS_PART                189610870

1 row selected.</code></pre>



<p class="wp-block-paragraph">The old partitions are then imported:</p>



<pre class="wp-block-code"><code>$ impdp sales/***@PDB2 dumpfile=b_old.dmp table_exists_action=append
Table "SALES"."ORDERS_PART" exists. Data will be appended to existing table but all dependent metadata will be skipped due to table_exists_action of append
. . imported "SALES"."ORDERS_PART":"P_OLD"                5.523 KB       5 rows
Job "SALES"."SYS_IMPORT_FULL_01" successfully completed at Sun Oct 11 13:54:17 2026 elapsed 0 00:00:01</code></pre>



<p class="wp-block-paragraph">If we check the table again, the boundary is now below the registration SCN, and more importantly below the instantiation SCN of the live data !</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
WHERE source_object_owner = 'SALES'
AND source_object_name = 'ORDERS_PART';

SOURCE_OBJECT_NAME INSTANTIATION_SCN
------------------ -----------------
ORDERS_PART                189594418

1 row selected.</code></pre>



<p class="wp-block-paragraph">This means that a replicat using instantiation filtering will not filter the transactions which happened before the first import. If we add the replicat in the <code>adminclient</code>, and start it:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; DBLOGIN USERIDALIAS oggadmin_pdb2 DOMAIN OracleGoldenGate
Successfully logged into database PDB2.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb2@CDB02/PDB2) 3&gt; ADD REPLICAT REPPT, INTEGRATED, EXTTRAIL pt, CHECKPOINTTABLE OGGADMIN.CHECKPOINT
2026-10-11T13:54:19Z  INFO    OGG-08100  Integrated Replicat added.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb2@CDB02/PDB2) 4&gt; START REPLICAT REPPT
2026-10-11T13:54:19Z  INFO    OGG-00975  Replicat group REPPT starting.
2026-10-11T13:54:19Z  INFO    OGG-15445  Replicat group REPPT started.</code></pre>



<p class="wp-block-paragraph">The replicat reads the instantiation SCN <code>189594418</code> from the target. It is now older than the live data export, so the transactions that export already contains are no longer filtered, and the replicat fails on the first trail record:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; INFO REPLICAT REPPT

Replicat   REPPT     Last Started 2026-10-11 13:54   Status ABENDED
INTEGRATED
Checkpoint Lag       00:00:00 (updated 00:00:19 ago)
Log Read Checkpoint  File pt000000000
                     First Record  RBA 0
Encryption Profile   LocalWallet</code></pre>



<p class="wp-block-paragraph">The replicat report shows the boundary and the error:</p>



<pre class="wp-block-code"><code>2026-10-11 13:54:25  INFO    OGG-10155  Instantiation CSN filtering is enabled on table SALES.ORDERS_PART at CSN 189,594,418.
2026-10-11 13:54:31  WARNING OGG-02544  Unhandled error (ORA-00001: unique constraint (.) violated
ORA-00001: unique constraint (SALES.SYS_C0010450) violated) while processing the record at SEQNO 0, RBA 1793 in Integrated mode. REPLICAT will retry in Direct mode.</code></pre>



<p class="wp-block-paragraph"><code>1001</code> and <code>1002</code> were already on the target from the live-data import, but their commit CSNs are now above the boundary.</p>



<h2 id="conclusion" class="wp-block-heading">Conclusion</h2>



<ul class="wp-block-list">
<li><code>instantiation_scn</code> is <strong>per table</strong>, not per partition. The last <code>impdp</code> will decide the boundary being used by instantiation filtering.</li>



<li><code>impdp</code> overwrites it <strong>unconditionally</strong>, with no warning.</li>



<li>When using instantiation filtering, always import the dump which should contain the boundary <strong>last</strong>.</li>



<li>Keep in mind that, depending on the context of your real replication, another instantiation method might be needed. Foreign keys, DDL replication, or many aspects a GoldenGate replication might need to address, were not covered in this blog.</li>
</ul>
<p>L’article <a href="https://www.dbi-services.com/blog/import-order-matters-for-goldengate-instantiation-filtering/">Import order matters for GoldenGate instantiation filtering</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/import-order-matters-for-goldengate-instantiation-filtering/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Removing instantiation filtering from a GoldenGate replicat</title>
		<link>https://www.dbi-services.com/blog/removing-instantiation-filtering-from-a-goldengate-replicat/</link>
					<comments>https://www.dbi-services.com/blog/removing-instantiation-filtering-from-a-goldengate-replicat/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Mon, 12 Oct 2026 06:59:00 +0000</pubDate>
				<category><![CDATA[GoldenGate]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[26ai]]></category>
		<category><![CDATA[checkpoint]]></category>
		<category><![CDATA[csn]]></category>
		<category><![CDATA[DataPump]]></category>
		<category><![CDATA[enable_instantiation_filtering]]></category>
		<category><![CDATA[instantiation]]></category>
		<category><![CDATA[ogg]]></category>
		<category><![CDATA[replicat]]></category>
		<category><![CDATA[rest api]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=47507</guid>

					<description><![CDATA[<p>In a recent blog, I explained how instantiation filtering works. DBOPTIONS ENABLE_INSTANTIATION_FILTERING makes a replicat discard any record whose commit CSN is at or below a table’s instantiation CSN. It applies everything above it. This replicat parameter should be kept during the whole initial load process. At some point the replicat has processed past the [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/removing-instantiation-filtering-from-a-goldengate-replicat/">Removing instantiation filtering from a GoldenGate replicat</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In a recent blog, I explained <a href="https://www.dbi-services.com/blog/goldengate-instantiation-csn-explained/" target="_blank" rel="noopener noreferrer">how instantiation filtering works</a>. <code>DBOPTIONS ENABLE_INSTANTIATION_FILTERING</code> makes a replicat discard any record whose commit CSN is at or below a table’s instantiation CSN. It applies everything above it. This replicat parameter should be kept during the whole initial load process.</p>



<p class="wp-block-paragraph">At some point the replicat has processed past the instantiation boundary for every table, and the filter has nothing left to do. Let’s answer the subsequent and very practical question: <strong>how to confirm, before editing the parameter file, that the replicat has cleared the boundary for every table it maps?</strong></p>



<h2 id="which-scn-should-i-check-" class="wp-block-heading">Which SCN should I check ?</h2>



<p class="wp-block-paragraph">To summarize what needs to be checked: <strong>it is safe to remove <code>DBOPTIONS ENABLE_INSTANTIATION_FILTERING</code> once the replicat last applied CSN is greater than the instantiation CSN of every table it maps.</strong></p>



<p class="wp-block-paragraph">To answer this question, you have two sources of information:</p>



<ul class="wp-block-list">
<li>The per-table boundary, stored in <code>dba_apply_instantiated_objects</code>, with one row per source object.</li>



<li>The replicat last applied CSN, stored in the checkpoint table in the <code>log_csn</code> column.</li>
</ul>



<h2 id="sql-check" class="wp-block-heading">SQL check</h2>



<p class="wp-block-paragraph">Read the boundaries first:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_database, source_object_owner, source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
ORDER BY instantiation_scn DESC;

SOURCE_DATABASE SOURCE_OBJECT_OWNER SOURCE_OBJECT_NAME INSTANTIATION_SCN
--------------- ------------------- ------------------ -----------------
PDB1            APP_PDB1            SALES                       52642820
PDB1            APP_PDB1            ORDERS                      52642649</code></pre>



<p class="wp-block-paragraph">Then read the replicat last applied CSN from its checkpoint table:</p>



<pre class="wp-block-code"><code>SELECT group_name, log_csn FROM oggadmin.checkpoint WHERE group_name = 'REP1';</code></pre>



<p class="wp-block-paragraph">You can safely remove <code>ENABLE_INSTANTIATION_FILTERING</code> when <code>log_csn</code> is greater than the <strong>highest</strong> <code>instantiation_scn</code> among the tables it maps. A one-shot verdict, scoped to a single source database:</p>



<pre class="wp-block-code"><code>SELECT 'REP1' AS group_name,
c.log_csn,
MAX(o.instantiation_scn) AS max_boundary,
CASE WHEN c.log_csn &gt; MAX(o.instantiation_scn) THEN 'SAFE TO REMOVE' ELSE 'NOT YET' END AS verdict
FROM dba_apply_instantiated_objects o
LEFT JOIN oggadmin.checkpoint c ON c.group_name = 'REP1'
WHERE o.source_database = 'PDB1'
GROUP BY c.log_csn;</code></pre>



<p class="wp-block-paragraph">Before a replicat starts for the first time, <code>log_csn</code> is <code>NULL</code> and the verdict is <code>NOT YET</code>:</p>



<pre class="wp-block-code"><code>GROUP_NAME LOG_CSN MAX_BOUNDARY VERDICT
---------- ------- ------------ -------
REP1                   52642820 NOT YET</code></pre>



<p class="wp-block-paragraph">A replicat may map more than one source database, or only a subset of the instantiated objects. Then narrow the join to the objects applied.</p>



<p class="wp-block-paragraph"><strong>WARNING:</strong> A replicat that has never started has no checkpoint row at all, so <code>log_csn</code> returns nothing. Treat that as “not safe”: there is no applied position, so it cannot have cleared anything. The verdict query above handles this case. It starts from the instantiated objects and <strong>left joins</strong> the checkpoint table. A missing row gives a <code>NULL</code> <code>log_csn</code>, and the verdict falls to <code>NOT YET</code> instead of the query returning no row at all.</p>



<p class="wp-block-paragraph">After some time, once the replicat catches up with the initial load, the same query returns:</p>



<pre class="wp-block-code"><code>GROUP_NAME LOG_CSN  MAX_BOUNDARY VERDICT
---------- -------- ------------ --------------
REP1       52643651     52642820 SAFE TO REMOVE</code></pre>



<p class="wp-block-paragraph">You can now delete the <code>DBOPTIONS ENABLE_INSTANTIATION_FILTERING</code> parameter and restart your replicat.</p>



<h2 id="adminclient-check" class="wp-block-heading"><code>adminclient</code> check</h2>



<p class="wp-block-paragraph">From the <code>adminclient</code>, <code>INFO REPLICAT REP1, DETAIL</code> reports the applied CSN without using <code>sqlplus</code>:</p>



<pre class="wp-block-code"><code>OGG&gt; INFO REPLICAT REP1, DETAIL
...
Last Committed Transaction CSN value: 52643651</code></pre>



<p class="wp-block-paragraph">Compare that number against the <code>instantiation_scn</code> from the view exactly as above.</p>



<h2 id="rest-api-check" class="wp-block-heading">REST API check</h2>



<p class="wp-block-paragraph">The CSN is reachable over the REST API, just not from a GET endpoint. You need to execute a command against the replicat using the <a href="https://docs.oracle.com/en/database/goldengate/core/26/oggra/op-services-version-replicats-replicat-command-post.html" target="_blank" rel="noopener noreferrer">POST /services/{version}/replicats/{replicat}/command</a> endpoint. Using <code>execute_command_replicat</code> in the <a href="https://juliendelattre.com/tools/ogg-python-client/" target="_blank" rel="noopener noreferrer"><code>oggrestapi</code></a> Python client:</p>



<pre class="wp-block-code"><code>from oggrestapi import OGGRestAPI

client = OGGRestAPI(url="http://localhost:7810", username="ogg", password="ogg")

resp = client.execute_command_replicat("REP1", data={"command": "INFO", "arguments": "DETAIL"})
output = "".join(r&#091;"reply"])
print(output)</code></pre>



<pre class="wp-block-code"><code>Replicat   REP1      Last Started 2026-09-20 13:29   Status RUNNING
Checkpoint Lag       00:00:00 (updated 00:00:02 ago)
Process ID           102660
Log Read Checkpoint  File aa000000002
                     2026-09-20 13:30:17.000000  RBA 18926
Encryption Profile   LocalWallet

Current Log BSN value: 52643543

Last Committed Transaction CSN value: 52646310

  Extract Source                          Begin             End

  aa000000002                             2020-01-01 00:00  2026-09-20 13:30:17.00
  aa000000000                             * Initialized *   2020-01-01 00:00:00.00
  aa000000000                             * Initialized *   First Record</code></pre>



<p class="wp-block-paragraph">To be truly automated, <code>Last Committed Transaction CSN value</code> has to be parsed out:</p>



<pre class="wp-block-code"><code>import re

match = re.search(r"Last Committed Transaction CSN value: (\d+)", output)
applied_csn = int(match.group(1))
print(applied_csn)</code></pre>



<pre class="wp-block-code"><code>52646310</code></pre>



<h2 id="information-in-the-report-file" class="wp-block-heading">Information in the report file</h2>



<p class="wp-block-paragraph">When starting a replicat during an initial load, the report file is filled with the following lines:</p>



<pre class="wp-block-code"><code>OGG-10155  Instantiation CSN filtering is enabled on table PDB1.APP_PDB1.ORDERS at CSN 52,642,649.
OGG-10185  Instantiation CSN filtering on table PDB1.APP_PDB1.ORDERS has started at CSN 52642649.
OGG-10186  Instantiation CSN filtering on table PDB1.APP_PDB1.ORDERS has ended, now passing transactions starting at CSN 52642649.</code></pre>



<ul class="wp-block-list">
<li>If the trail the replicat reads on that start contains records <strong>below</strong> a boundary, it filters them, and these lines appear.</li>



<li>If the trail begins <strong>at or after</strong> the boundary, nothing is filtered and <strong>no line appears at all</strong>.</li>
</ul>



<h2 id="performance-impact-of-leaving-enable_instantiation_filtering" class="wp-block-heading">Performance impact of leaving <code>ENABLE_INSTANTIATION_FILTERING</code></h2>



<p class="wp-block-paragraph">When migrating a database online with GoldenGate, you want your replicat to catch up quickly. I tested whether keeping <code>ENABLE_INSTANTIATION_FILTERING</code> in the parameter file had any effect on the lag of a replicat. I found no significant statistical evidence of this.</p>



<h2 id="summary" class="wp-block-heading">Summary</h2>



<ul class="wp-block-list">
<li><code>ENABLE_INSTANTIATION_FILTERING</code> is <strong>safe to remove</strong> when the replicat <code>log_csn</code> (last applied CSN) is above the <code>instantiation_scn</code> of every table it maps.</li>



<li>Get the applied CSN from <code>oggadmin.checkpoint.log_csn</code> (<code>sqlplus</code>), <code>INFO REPLICAT REP1, DETAIL</code> (<code>adminclient</code>), or <code>execute_command_replicat("REP1", data={"command": "INFO", "arguments": "DETAIL"})</code> over REST, which returns the same information.</li>



<li>The <code><a class="ogg-code-link" href="https://juliendelattre.com/tools/goldengate-error-code-explorer/OGG-10155/" target="_blank" rel="noopener noreferrer">OGG-10155</a>/10185/10186</code> report lines appear <strong>only when the replicat filters records on that start</strong>.</li>
</ul>
<p>L’article <a href="https://www.dbi-services.com/blog/removing-instantiation-filtering-from-a-goldengate-replicat/">Removing instantiation filtering from a GoldenGate replicat</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/removing-instantiation-filtering-from-a-goldengate-replicat/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>GoldenGate 26ai AutoSchema TYPEMAP: overriding the default datatype mapping</title>
		<link>https://www.dbi-services.com/blog/goldengate-26ai-autoschema-typemap-overriding-the-default-datatype-mapping/</link>
					<comments>https://www.dbi-services.com/blog/goldengate-26ai-autoschema-typemap-overriding-the-default-datatype-mapping/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Thu, 08 Oct 2026 06:20:00 +0000</pubDate>
				<category><![CDATA[GoldenGate]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[26ai]]></category>
		<category><![CDATA[autoschema]]></category>
		<category><![CDATA[autoschemaoptions]]></category>
		<category><![CDATA[datatype]]></category>
		<category><![CDATA[Initial load]]></category>
		<category><![CDATA[microservices]]></category>
		<category><![CDATA[ogg]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[replicat]]></category>
		<category><![CDATA[typemap]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=47671</guid>

					<description><![CDATA[<p>In my blog about the new automatic schema evolution feature in GoldenGate 26ai, I was replicating the following table from Oracle to PostgreSQL: The replicat created a PostgreSQL table from an Oracle one during an initial load. It worked, but one choice was not mine: the NUMBER primary key became a varchar(50). Automatic Schema Evolution [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/goldengate-26ai-autoschema-typemap-overriding-the-default-datatype-mapping/">GoldenGate 26ai AutoSchema TYPEMAP: overriding the default datatype mapping</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In my blog about the new <a href="https://www.dbi-services.com/blog/goldengate-26ai-automatic-schema-evolution-creating-missing-tables-during-an-initial-load/" target="_blank" rel="noopener noreferrer">automatic schema evolution</a> feature in GoldenGate 26ai, I was replicating the following table from Oracle to PostgreSQL:</p>



<pre class="wp-block-code"><code>CREATE TABLE autosch.t1 (
    id NUMBER PRIMARY KEY,
    name VARCHAR2(50),
    amount NUMBER(10,2),
    created DATE DEFAULT SYSDATE,
    note VARCHAR2(20) DEFAULT 'added'
);</code></pre>



<p class="wp-block-paragraph">The replicat created a PostgreSQL table from an Oracle one during an initial load. It worked, but one choice was not mine: the <code>NUMBER</code> primary key became a <code>varchar(50)</code>. <strong>Automatic Schema Evolution</strong> takes its datatypes from a <strong>default mapping</strong>, and the <code>TYPEMAP</code> option is how you override it.</p>



<p class="wp-block-paragraph">Automatic schema evolution is a <strong>preview feature</strong> in GoldenGate 26ai: its syntax, messages and behavior can change before it becomes generally available, so the results below apply to the 23.26.3 release I tested.</p>



<h2 id="where-the-default-mapping-comes-from" class="wp-block-heading">Where the default mapping comes from</h2>



<p class="wp-block-paragraph">Each GoldenGate 26ai home ships the default mappings as one JSON file per source database and per target database. They are in <code>$OGG_HOME/lib/utl/autoschema/MappingJSON</code>, where <code>MappingGenerator.py</code> combines a source and a target file. For this example, the files are <code>AutoSchemaOracleSourceMappings.json</code> and <code>AutoSchemaPostgreSQLTargetMappings.json</code>.</p>



<p class="wp-block-paragraph">Reading the two JSON files for an Oracle source and a PostgreSQL target, the default mappings that matter for the example table are:</p>



<figure class="wp-block-table"><table><thead><tr><th>Oracle source type</th><th>PostgreSQL target type</th></tr></thead><tbody><tr><td><code>number(precision,scale)</code></td><td><code>decimal(precision,scale)</code></td></tr><tr><td><code>number</code></td><td><code>varchar(precision)</code>, if the source data length is at most 10485760</td></tr><tr><td><code>number</code></td><td><code>text</code>, if the value is still too large to be accommodated</td></tr><tr><td><code>date</code></td><td><code>timestamp(precision)</code></td></tr></tbody></table></figure>



<p class="wp-block-paragraph">A <code>NUMBER</code> <strong>without</strong> precision maps to <code>varchar</code>, and a <code>NUMBER(p,s)</code> maps to <code>decimal(p,s)</code>. This is why the key of my source table did not become a number.</p>



<p class="wp-block-paragraph">For each test below, I drop <code>autosch.t1</code> on PostgreSQL and start a new replicat, always named <code>REPTM</code>, on the same file.</p>



<h2 id="overriding-one-type" class="wp-block-heading">Overriding one type</h2>



<p class="wp-block-paragraph"><code>AUTOSCHEMAOPTIONS TYPEMAP</code> must be written on a separate line, next to <code>DDL AUTOSCHEMA INCLUDE ALL</code>. The source type and the target type are separated by an <strong>equal sign</strong>. The option maps every occurrence of the source type to the given target type. <strong>Both types must be enclosed in single quotes</strong>, otherwise the replicat abends with <code>OGG-30694</code>:</p>



<pre class="wp-block-code"><code>REPLICAT REPTM
TARGETDB ogg_pg_01 USERIDALIAS pgalias DOMAIN OracleGoldenGate
DDL AUTOSCHEMA INCLUDE ALL
AUTOSCHEMAOPTIONS TYPEMAP 'number' = 'bigint'
MAP AUTOSCH.*, TARGET AUTOSCH.*;</code></pre>



<p class="wp-block-paragraph">The replicat report logs that the override was read:</p>



<pre class="wp-block-code"><code>2026-09-19T15:23:47.078+0000  INFO    OGG-30632  Oracle GoldenGate Delivery for PostgreSQL, REPTM.prm:  AutoSchemaOptions TypeMap is used, TypeNameMap: number -&gt; bigint.</code></pre>



<p class="wp-block-paragraph">The table created in PostgreSQL, seen with <code>\d autosch.t1</code> in <code>psql</code>, now shows a <em><code>bigint</code> as primary key</em></p>



<pre class="wp-block-code"><code>ogg_test_pg=# \d autosch.t1
                            Table "autosch.t1"
 Column  |              Type              | Collation | Nullable | Default
---------+--------------------------------+-----------+----------+---------
 id      | bigint                         |           | not null |
 name    | character varying(50)          |           |          |
 amount  | numeric(10,2)                  |           |          |
 created | timestamp(0) without time zone |           |          |
 note    | character varying(20)          |           |          |
Indexes:
    "t1_pkey" PRIMARY KEY, btree (id)</code></pre>



<p class="wp-block-paragraph">The <code>amount</code> column type did not change, since <code>'number'</code> matches the source type <code>NUMBER</code> without precision. <code>NUMBER(10,2)</code> is another source type, and is not affected by the <code>TYPEMAP</code> instruction.</p>



<h2 id="mapping-several-types-with-autoschemaoptions" class="wp-block-heading">Mapping several types with <code>AUTOSCHEMAOPTIONS</code></h2>



<p class="wp-block-paragraph">When mapping multiple types, each mapping needs a dedicated <code>AUTOSCHEMAOPTIONS</code> line:</p>



<pre class="wp-block-code"><code>DDL AUTOSCHEMA INCLUDE ALL
AUTOSCHEMAOPTIONS TYPEMAP 'number' = 'bigint'
AUTOSCHEMAOPTIONS TYPEMAP 'date' = 'date'
AUTOSCHEMAOPTIONS TYPEMAP 'number(10,2)' = 'real'</code></pre>



<p class="wp-block-paragraph">Every entry is logged separately:</p>



<pre class="wp-block-code"><code>2026-09-19T16:29:07.403+0000  INFO    OGG-30632  Oracle GoldenGate Delivery for PostgreSQL, REPTM.prm:  AutoSchemaOptions TypeMap is used, TypeNameMap: date -&gt; date.
2026-09-19T16:29:07.403+0000  INFO    OGG-30632  Oracle GoldenGate Delivery for PostgreSQL, REPTM.prm:  AutoSchemaOptions TypeMap is used, TypeNameMap: number -&gt; bigint.
2026-09-19T16:29:07.403+0000  INFO    OGG-30632  Oracle GoldenGate Delivery for PostgreSQL, REPTM.prm:  AutoSchemaOptions TypeMap is used, TypeNameMap: number\(10,2\) -&gt; real.</code></pre>



<p class="wp-block-paragraph">And this is the resulting table:</p>



<pre class="wp-block-code"><code> Column  |         Type          | Collation | Nullable | Default
---------+-----------------------+-----------+----------+---------
 id      | bigint                |           | not null |
 name    | character varying(50) |           |          |
 amount  | real                  |           |          |
 created | date                  |           |          |
 note    | character varying(20) |           |          |</code></pre>



<p class="wp-block-paragraph">All three overrides are applied. An interesting feature is that the replicat logs an <code>OGG-03056</code> warning about the target column being smaller than the source column:</p>



<pre class="wp-block-code"><code>2026-09-19T16:29:07.419+0000  WARNING OGG-03056  Oracle GoldenGate Delivery for PostgreSQL, REPTM.prm:  Source table AUTOSCH.T1 column CREATED data size exceeds the maximum target table autosch.t1 column created size. Automatic truncation is enabled for all tables/columns without further warnings.</code></pre>



<p class="wp-block-paragraph">As expected, <strong>mapping <code>DATE</code> to a PostgreSQL <code>date</code> loses the time part</strong>, and it can be seen in the target table:</p>



<pre class="wp-block-code"><code> id | name  | amount |  created   | note
----+-------+--------+------------+-------
  1 | row 1 |  10.25 | 2026-09-19 | added
  2 | row 2 |   20.5 | 2026-09-19 | added</code></pre>



<p class="wp-block-paragraph">An Oracle <code>DATE</code> has a time, so the default <code>timestamp(0)</code> mapping is the right one.</p>



<h2 id="autoschemaoptions-at-the-map-level" class="wp-block-heading"><code>AUTOSCHEMAOPTIONS</code> at the <code>MAP</code> level</h2>



<p class="wp-block-paragraph">The same option can be set on a <code>MAP</code> statement, between parentheses. It then only applies to the tables of that <code>MAP</code> statement instead of the whole replicat:</p>



<pre class="wp-block-code"><code>MAP AUTOSCH.*, TARGET AUTOSCH.*, AUTOSCHEMAOPTIONS (TYPEMAP 'number' = 'bigint');</code></pre>



<pre class="wp-block-code"><code>2026-09-19T16:29:33.344+0000  INFO    OGG-06506  Oracle GoldenGate Delivery for PostgreSQL, REPTM.prm:  Wildcard MAP resolved (entry AUTOSCH.*): MAP "AUTOSCH"."T1", TARGET AUTOSCH."T1", AUTOSCHEMAOPTIONS (TYPEMAP 'number' = 'bigint').
2026-09-19T16:29:33.344+0000  INFO    OGG-30632  Oracle GoldenGate Delivery for PostgreSQL, REPTM.prm:  AutoSchemaOptions TypeMap is used, TypeNameMap: number -&gt; bigint.</code></pre>



<p class="wp-block-paragraph">The table has a <code>bigint</code> key again. In this case, the other <code>number</code> columns outside of the <code>MAP</code> statement would keep the default <code>varchar(50)</code> mapping.</p>



<h2 id="remapping-a-single-column" class="wp-block-heading">Remapping a single column</h2>



<p class="wp-block-paragraph">To change a single column, write the column name on the left side of the equal sign, instead of specifying the source type. The two forms work, first with the full name of the column:</p>



<pre class="wp-block-code"><code>AUTOSCHEMAOPTIONS TYPEMAP AUTOSCH.T1.NAME = 'text'</code></pre>



<p class="wp-block-paragraph">And at the <code>MAP</code> level, with the column name alone:</p>



<pre class="wp-block-code"><code>MAP AUTOSCH.T1, TARGET AUTOSCH.T1, AUTOSCHEMAOPTIONS (TYPEMAP NAME = 'text');</code></pre>



<p class="wp-block-paragraph">Both give the same table, with only <code>name</code> changed into <code>text</code>:</p>



<pre class="wp-block-code"><code> Column  |              Type              | Collation | Nullable | Default
---------+--------------------------------+-----------+----------+---------
 id      | character varying(50)          |           | not null |
 name    | text                           |           |          |
 amount  | numeric(10,2)                  |           |          |
 created | timestamp(0) without time zone |           |          |
 note    | character varying(20)          |           |          |</code></pre>



<p class="wp-block-paragraph">To remap several columns on the same <code>MAP</code>, separate the mappings with <strong>commas</strong> inside the parentheses, and write <code>TYPEMAP</code> only once:</p>



<pre class="wp-block-code"><code>MAP AUTOSCH.T1, TARGET AUTOSCH.T1, AUTOSCHEMAOPTIONS (TYPEMAP NAME = 'text', NOTE = 'text');</code></pre>



<pre class="wp-block-code"><code> Column  |              Type              | Collation | Nullable | Default
---------+--------------------------------+-----------+----------+---------
 id      | character varying(50)          |           | not null |
 name    | text                           |           |          |
 amount  | numeric(10,2)                  |           |          |
 created | timestamp(0) without time zone |           |          |
 note    | text                           |           |          |</code></pre>



<p class="wp-block-paragraph">A type and a column can be mixed in the same list. For example, <code>AUTOSCHEMAOPTIONS (TYPEMAP 'number' = 'bigint', NAME = 'text')</code> gives a <code>bigint</code> key and a <code>text</code> name.</p>



<h2 id="to-summarize" class="wp-block-heading">To summarize</h2>



<ul class="wp-block-list">
<li>The default mapping files are located in <code>$OGG_HOME/lib/utl/autoschema/MappingJSON</code>. Check them before letting a replicat create tables. A <code>NUMBER</code> without precision becomes a <code>varchar</code> on PostgreSQL, for instance.</li>



<li>The override syntax is <code>AUTOSCHEMAOPTIONS TYPEMAP 'source_type' = 'target_type'</code>. Use an equal sign, both types in single quotes, and one mapping per line.</li>



<li>A source type is matched as written. An override on <code>'number'</code> does not change a <code>NUMBER(10,2)</code> column, which needs a separate <code>'number(10,2)'</code> entry.</li>



<li>On a <code>MAP</code>, put the option between parentheses to limit it to those tables. For one column, use <code>AUTOSCH.T1.NAME = 'text'</code> globally or <code>NAME = 'text'</code> on the <code>MAP</code>.</li>



<li>Pay attention to <code>OGG-03056</code> warnings: a target type smaller than the source, like <code>DATE</code> to <code>date</code>, truncates the data without stopping the initial load.</li>
</ul>
<p>L’article <a href="https://www.dbi-services.com/blog/goldengate-26ai-autoschema-typemap-overriding-the-default-datatype-mapping/">GoldenGate 26ai AutoSchema TYPEMAP: overriding the default datatype mapping</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/goldengate-26ai-autoschema-typemap-overriding-the-default-datatype-mapping/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>GoldenGate AI Service bug confirmed in the Maximum Input Characters setting</title>
		<link>https://www.dbi-services.com/blog/goldengate-ai-service-bug-confirmed-in-the-maximum-input-characters-setting/</link>
					<comments>https://www.dbi-services.com/blog/goldengate-ai-service-bug-confirmed-in-the-maximum-input-characters-setting/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Tue, 06 Oct 2026 05:25:17 +0000</pubDate>
				<category><![CDATA[GoldenGate]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[26]]></category>
		<category><![CDATA[26ai]]></category>
		<category><![CDATA[ai service]]></category>
		<category><![CDATA[embeddings]]></category>
		<category><![CDATA[gemini]]></category>
		<category><![CDATA[maximum]]></category>
		<category><![CDATA[microservices]]></category>
		<category><![CDATA[ogg]]></category>
		<category><![CDATA[Troubleshooting]]></category>
		<category><![CDATA[vector]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=47756</guid>

					<description><![CDATA[<p>In a previous blog, I noted that there might be a bug with the Maximum Input Characters setting in GoldenGate 26ai AI Service. The bug was confirmed by Oracle, so let’s see what it is exactly. Bug found in GoldenGate 26ai AI Service Maximum Input Characters is a setting in the AI Service. You can [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/goldengate-ai-service-bug-confirmed-in-the-maximum-input-characters-setting/">GoldenGate AI Service bug confirmed in the Maximum Input Characters setting</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In a <a href="https://www.dbi-services.com/blog/goldengate-26ai-ai-service-full-setup-with-a-free-gemini-key/" target="_blank" rel="noopener noreferrer">previous blog</a>, I noted that there might be a bug with the <code>Maximum Input Characters</code> setting in GoldenGate 26ai AI Service. The bug was confirmed by Oracle, so let’s see what it is exactly.</p>



<h2 id="bug-found-in-goldengate-26ai-ai-service" class="wp-block-heading">Bug found in GoldenGate 26ai AI Service</h2>



<p class="wp-block-paragraph"><code>Maximum Input Characters</code> is a setting in the AI Service. You can set it when registering a new model in the web UI.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="700" height="580" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/001_goldengate-26ai-ai-service-max-input-add-model-dialog.png" alt="" class="wp-image-47758" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/001_goldengate-26ai-ai-service-max-input-add-model-dialog.png 700w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/001_goldengate-26ai-ai-service-max-input-add-model-dialog-300x249.png 300w" sizes="auto, (max-width: 700px) 100vw, 700px" /></figure>
</div>


<p class="wp-block-paragraph">In the <a href="https://docs.oracle.com/en/database/goldengate/core/26/coredoc/sm-ads-ai-providers-and-models.html" target="_blank" rel="noopener noreferrer">Oracle documentation</a>, the parameter is described as a cap on how much text is sent to the embedding model before it leaves GoldenGate. This ensures that an <code>@AISERVICE</code> mapping cannot send more than intended, and does not reach a token limit, or increase your consumption.</p>



<p class="wp-block-paragraph">If this setting worked, a model with <code>Maximum Input Characters</code> set to <code>1</code> should truncate any input to a single character before calling the AI Service. This is not what I found when testing it.</p>



<h2 id="proof-of-the-bug" class="wp-block-heading">Proof of the bug</h2>



<h3 id="model-registration" class="wp-block-heading">Model registration</h3>



<p class="wp-block-paragraph">I registered two AI Service models against the same Gemini provider and the same remote model (<code>gemini-embedding-001</code>). They differ only in <code>Maximum Input Characters</code>: one set to <code>1000</code>, the other to <code>1</code>.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="900" height="620" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/002_goldengate-26ai-ai-service-max-input-model-max1000.png" alt="" class="wp-image-47759" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/002_goldengate-26ai-ai-service-max-input-model-max1000.png 900w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/002_goldengate-26ai-ai-service-max-input-model-max1000-300x207.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/002_goldengate-26ai-ai-service-max-input-model-max1000-768x529.png 768w" sizes="auto, (max-width: 900px) 100vw, 900px" /></figure>
</div>

<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="900" height="620" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/003_goldengate-26ai-ai-service-max-input-model-max1.png" alt="" class="wp-image-47760" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/003_goldengate-26ai-ai-service-max-input-model-max1.png 900w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/003_goldengate-26ai-ai-service-max-input-model-max1-768x529.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/003_goldengate-26ai-ai-service-max-input-model-max1-300x207.png 300w" sizes="auto, (max-width: 900px) 100vw, 900px" /></figure>
</div>


<p class="wp-block-paragraph">If I query both models with the REST API, I can see the settings properly saved, in <code>limits.maxInputCharacters</code>:</p>



<pre class="wp-block-code"><code>{
    "id": "gemini_embed_max1000",
    "name": "gemini-embedding-max1000",
    "type": "remote",
    "enabled": true,
    "loaded": false,
    "description": "Model with 1000 character limit",
    "capabilities": &#091;
        "embed"
    ],
    "providerId": "gemini",
    "remoteModelName": "gemini-embedding-001",
    "parameters": {},
    "limits": {
        "maxInputCharacters": 1000
    }
}</code></pre>



<pre class="wp-block-code"><code>{
    "id": "gemini_embed_max1",
    "name": "gemini-embedding-max1",
    "type": "remote",
    "enabled": true,
    "loaded": false,
    "description": "Model with 1 character limit",
    "capabilities": &#091;
        "embed"
    ],
    "providerId": "gemini",
    "remoteModelName": "gemini-embedding-001",
    "parameters": {},
    "limits": {
        "maxInputCharacters": 1
    }
}</code></pre>



<h3 id="replicat-used-in-the-test" class="wp-block-heading">Replicat used in the test</h3>



<p class="wp-block-paragraph">I built an <code>EXTAI</code>/<code>REPAI</code> pair between <code>PDB1.APP_PDB1.PRODUCTS</code> on the source and <code>PDB26.APP_PDB26.PRODUCTS</code> on the target. Its <code>COLMAP</code> maps the <code>description</code> column through three separate <code>@AISERVICE</code> calls into three different <code>VECTOR(3072, FLOAT32)</code> columns:</p>



<pre class="wp-block-code"><code>MAP PDB1.APP_PDB1.PRODUCTS, TARGET APP_PDB26.PRODUCTS,
COLMAP (
    USEDEFAULTS,
    desc_vector_max1000 = @AISERVICE(embed, 'gemini_embed_max1000', description),
    desc_vector_max1000_run2 = @AISERVICE(embed, 'gemini_embed_max1000', description),
    desc_vector_max1 = @AISERVICE(embed, 'gemini_embed_max1', description));</code></pre>



<p class="wp-block-paragraph">The first two calls use the same model (<code>gemini_embed_max1000</code>). This is to show the baseline argument that the same input, embedded with the same model twice, produce the same vector. The third call uses the 1 character model, to test the bug.</p>



<h2 id="baseline-and-embedding-noise" class="wp-block-heading">Baseline and embedding noise</h2>



<p class="wp-block-paragraph">The truncation is only relevant if identical inputs reliably produce identical output. Before comparing vectors across models, I first confirmed the embedding had no noise, by inserting the same 100 character <code>description</code> into two source rows and let both flow through the pipeline:</p>



<pre class="wp-block-code"><code>INSERT INTO app_pdb1.products (id, name, description) VALUES (101, 'AI Service Max Input Chars Test Row 1', 'GoldenGate 26ai AI Service test row for comparing embeddings across the max1000 and max1 test models');
INSERT INTO app_pdb1.products (id, name, description) VALUES (102, 'AI Service Max Input Chars Test Row 2', 'GoldenGate 26ai AI Service test row for comparing embeddings across the max1000 and max1 test models');
COMMIT;</code></pre>



<p class="wp-block-paragraph">Then, I compared the <code>gemini_embed_max1000</code> vector between the two rows. I also compared the two separate <code>@AISERVICE</code> calls to the same model within row 101 itself. Both come back with a cosine distance of <code>0</code>:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT VECTOR_DISTANCE(a.desc_vector_max1000, b.desc_vector_max1000, COSINE) dist_101_vs_102_same_input
FROM app_pdb26.products a, app_pdb26.products b
WHERE a.id = 101
AND b.id = 102;

DIST_101_VS_102_SAME_INPUT
--------------------------
                         0
</code></pre>



<p class="wp-block-paragraph">The conclusion is that <code>gemini-embedding-001</code> is deterministic for identical input. Now, if the <code>max1</code> vector is identical to the <code>max1000</code> vector, we can rule out embedding noise.</p>



<h2 id="does-the-1-character-limit-change-anything" class="wp-block-heading">Does the 1 character limit change anything?</h2>



<p class="wp-block-paragraph">With the same transaction, let’s look at the two different <code>@AISERVICE</code> calls, against a model capped at 1000 characters and one capped at 1:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT id, VECTOR_DISTANCE(desc_vector_max1000, desc_vector_max1, COSINE) dist_max1000_vs_max1, VECTOR_DISTANCE(desc_vector_max1000, desc_vector_max1000_run2, COSINE) dist_max1000_vs_rerun
FROM app_pdb26.products
WHERE id IN (101, 102);

ID  DIST_MAX1000_VS_MAX1 DIST_MAX1000_VS_RERUN
--- -------------------- ---------------------
101                    0                     0
102                    0                     0</code></pre>



<p class="wp-block-paragraph">On both models, the distance between the resulting vectors is zero. If the <code>Maximum Input Characters</code> were enforced, the <strong>vectors should be different</strong> between both models. This proves that the <code>Maximum Input Characters</code> setting saved as <code>1</code> had no effect on what was sent to Gemini.</p>



<p class="wp-block-paragraph">GoldenGate even has a <a href="https://docs.oracle.com/en/middleware/goldengate/core/23.26/error-messages/" target="_blank" rel="noopener noreferrer">documented warning</a> for a situation where the input would exceed the limit. <code>OGG-30677</code> is defined as <code>@AISERVICE source column {0} contains {1} characters, exceeding model {2}'s maximum input limit of {3} characters</code>. But in my case, the warning never appeared in the log files.</p>



<h2 id="what-can-break" class="wp-block-heading">What can break?</h2>



<p class="wp-block-paragraph">I added a fourth <code>@AISERVICE</code> call to the same replicat, mapping a <code>CLOB</code> column (<code>big_description</code>) through the 1000 character model. I then tried pushing progressively larger inputs.</p>



<p class="wp-block-paragraph">With 100000 characters, the <code>countTokens</code> endpoint reports this input at 23679 tokens, way more than the documented <code>inputTokenLimit</code> of <code>gemini-embedding-001</code>. It applied without any error and produced a real 3072 dimension vector, distinct from every other vector in this blog.</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT id, VECTOR_DIMS(desc_vector_bigtest) dims
FROM app_pdb26.products
WHERE id = 103;

ID  DIMS
--- ----
103 3072</code></pre>



<p class="wp-block-paragraph">Here, we can see that even the documented token limit of your provider might not stop the AI Service to work.</p>



<h3 id="reaching-quota-limit" class="wp-block-heading">Reaching quota limit</h3>



<p class="wp-block-paragraph">On top of the per-request token limit, depending on your billing settings, you might have a usage quota on the API key. A request can be rejected by Gemini, and this error is raised in <code>AIService.log</code>:</p>



<pre class="wp-block-code"><code>{
    "error": {
        "code": 429,
        "message": "You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. ",
        "status": "RESOURCE_EXHAUSTED"
    }
}</code></pre>



<p class="wp-block-paragraph">And in this case, the replicat abends with the following error:</p>



<pre class="wp-block-code"><code>2026-09-27 08:22:21  WARNING OGG-30679  Response from EMBED endpoint using model gemini_embed_max1000 returned error: code REMOTE_INFERENCE_FAILED, message 'HTTP 429 - Too Many Requests: {
  "error": {
    "code": 429,
    "message": "You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. ",
    "status": "RESOURCE_EXHAUSTED"
  }
}
'.

2026-09-27 08:22:21  WARNING OGG-01431  Canceled grouped transaction on PDB26.APP_PDB26.PRODUCTS, Mapping error.
2026-09-27 08:22:27  ERROR   OGG-01668  PROCESS ABENDING.</code></pre>



<h2 id="summary" class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph"><code>Maximum Input Characters</code> does not protect a replicat from an oversized <code>@AISERVICE</code> mapping. Because of this, the only real limit to your embeddings in GoldenGate is a usage quota. This can affect your replication, whether you rely on the setting for functional truncation or to lower your costs. Until the bug is fixed, be careful when using the AI Service!</p>
<p>L’article <a href="https://www.dbi-services.com/blog/goldengate-ai-service-bug-confirmed-in-the-maximum-input-characters-setting/">GoldenGate AI Service bug confirmed in the Maximum Input Characters setting</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/goldengate-ai-service-bug-confirmed-in-the-maximum-input-characters-setting/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>GoldenGate 26ai automatic schema evolution: creating missing tables during an initial load</title>
		<link>https://www.dbi-services.com/blog/goldengate-26ai-automatic-schema-evolution-creating-missing-tables-during-an-initial-load/</link>
					<comments>https://www.dbi-services.com/blog/goldengate-26ai-automatic-schema-evolution-creating-missing-tables-during-an-initial-load/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Mon, 05 Oct 2026 05:00:44 +0000</pubDate>
				<category><![CDATA[GoldenGate]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[26ai]]></category>
		<category><![CDATA[autoschema]]></category>
		<category><![CDATA[ddl]]></category>
		<category><![CDATA[Initial load]]></category>
		<category><![CDATA[microservices]]></category>
		<category><![CDATA[ogg]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[replicat]]></category>
		<category><![CDATA[schema evolution]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=47508</guid>

					<description><![CDATA[<p>For as long as I have used GoldenGate, an initial load had one prerequisite: the target tables must already exist. Data Pump takes care of it between two Oracle databases. For anything else, you write the CREATE TABLE statements yourself, with the datatypes you choose, or choose another initial load method. The Automatic Schema Evolution [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/goldengate-26ai-automatic-schema-evolution-creating-missing-tables-during-an-initial-load/">GoldenGate 26ai automatic schema evolution: creating missing tables during an initial load</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">For as long as I have used GoldenGate, an initial load had one prerequisite: <strong>the target tables must already exist</strong>. Data Pump takes care of it between two Oracle databases. For anything else, you write the <code>CREATE TABLE</code> statements yourself, with the datatypes you choose, or choose another initial load method. The <strong>Automatic Schema Evolution</strong> feature launched in <a href="https://docs.oracle.com/en/database/goldengate/core/26/release-notes/new-features.html#GUID-F48FEF44-A714-4216-8BA0-4A7B9A220CBA" target="_blank" rel="noopener noreferrer">GoldenGate 26ai</a> promises to <em>“automatically detect and propagate supported schema changes as part of the replication flow”</em>. I wanted to see it work, so I tried it from an Oracle 19c PDB to a PostgreSQL database.</p>



<p class="wp-block-paragraph">And it works. The replicat creates the missing table itself, with a default datatype mapping, and loads the rows in the target.</p>



<p class="wp-block-paragraph">Automatic schema evolution is a <strong>preview feature</strong> in GoldenGate 26ai: its syntax, messages and behavior can change before it becomes generally available, so the results below apply to the 23.26.3 release I tested.</p>



<h2 id="how-it-works" class="wp-block-heading">How it works</h2>



<p class="wp-block-paragraph">The feature replicates table structure changes between databases of the same or of different types. The exception is Oracle to Oracle, where regular DDL replication is the solution. It also creates tables during an initial load. The replicat must be 26ai or higher, but the extract writing the trail can be 19c or higher. On a live trail, the replicat also adds a column that appears on the source. The documentation lists <code>ADD COLUMN</code> as the one supported <code>ALTER TABLE</code>. But <strong>an initial load never alters an existing table</strong>: it leaves it alone or, with <code>OPTYPE RECREATE</code>, drops and recreates it.</p>



<p class="wp-block-paragraph">The replicat does not need anything from the source database. It builds the <code>CREATE TABLE</code> statement from the <strong>Table Definition Record</strong> (TDR) that GoldenGate already writes in the trail file.</p>



<p class="wp-block-paragraph"><code>AUTOSCHEMA</code> is a replicat-only option of <code>DDL</code>: the <code>DDL</code> parameter of the extract does not accept it. I put <code>DDL AUTOSCHEMA INCLUDE ALL</code> in the extract’s parameter file. The extract abends at startup with the same parsing error as an unrecognized keyword:</p>



<pre class="wp-block-code"><code>2026-09-26T05:54:27Z  ERROR   OGG-10151  (EXTPGL.prm) line 3: Parsing error, parameter &#091;DDL] has unrecognized keyword or extra value "AUTOSCHEMA".</code></pre>



<p class="wp-block-paragraph">That makes sense given what the feature does. It creates missing tables on the target, and the extract never touches the target database.</p>



<h2 id="the-setup" class="wp-block-heading">The setup</h2>



<ul class="wp-block-list">
<li>Source: Oracle 19c, <code>PDB1</code>, schema <code>AUTOSCH</code>, table <code>AUTOSCH.T1</code> with 5 rows, managed by the 26ai deployment <code>ogg_test_01</code> in GoldenGate <code>23.26.3.0.0</code>.</li>



<li>Target: PostgreSQL 16, database <code>pgstore</code>, an empty schema <code>autosch</code>, managed by a <strong>separate</strong> deployment <code>ogg_pg_01</code> from GoldenGate for PostgreSQL <code>23.26.3.0.1</code>.</li>
</ul>



<p class="wp-block-paragraph">The source table:</p>



<pre class="wp-block-code"><code>CREATE TABLE autosch.t1 (
    id NUMBER PRIMARY KEY,
    name VARCHAR2(50),
    amount NUMBER(10,2),
    created DATE DEFAULT SYSDATE
);</code></pre>



<p class="wp-block-paragraph">For the initial load, on the <code>ogg_test_01</code> deployment, <code>EXTPGL</code> is added as a <code>SOURCEISTABLE</code> extract.</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; DBLOGIN USERIDALIAS pdb1 DOMAIN OracleGoldenGate
Successfully logged into database PDB1.

OGG (http://localhost:7810 ogg_test_01 as pdb1@CDB01/PDB1) 3&gt; ADD EXTRACT EXTPGL, SOURCEISTABLE
2026-09-26T05:53:26Z  INFO    OGG-08100  Extract added.

OGG (http://localhost:7810 ogg_test_01 as pdb1@CDB01/PDB1) 4&gt; EDIT PARAMS EXTPGL
2026-09-26T05:53:26Z  INFO    OGG-10183  Parameter file EXTPGL.prm passed validity check.

OGG (http://localhost:7810 ogg_test_01 as pdb1@CDB01/PDB1) 5&gt; VIEW PARAMS EXTPGL
EXTRACT EXTPGL
USERIDALIAS pdb1 DOMAIN OracleGoldenGate
EXTFILE p1 MEGABYTES 100 PURGE
TABLE AUTOSCH.T1;

OGG (http://localhost:7810 ogg_test_01 as pdb1@CDB01/PDB1) 6&gt; START EXTRACT EXTPGL
2026-09-26T05:53:26Z  INFO    OGG-00975  Extract group EXTPGL starting.
2026-09-26T05:53:26Z  INFO    OGG-15426  Extract group EXTPGL started.</code></pre>



<p class="wp-block-paragraph">The extract report confirms the 5 rows went out to the file:</p>



<pre class="wp-block-code"><code>Report at 2026-09-26 05:53:26 (activity since 2026-09-26 05:53:26)

Output to p1:

From table AUTOSCH.T1:
       #                   inserts:         5
       #                   updates:         0
       #                   deletes:         0
       #                   upserts:         0
       #                  discards:         0</code></pre>



<p class="wp-block-paragraph">The target is a separate deployment, <code>ogg_pg_01</code>, from the Oracle GoldenGate for PostgreSQL binaries. A distribution path delivers the <code>p1</code> trail from <code>ogg_test_01</code> to <code>ogg_pg_01</code>. The replicat there, <code>REPPGL</code>, is added straight against the trail name, with <code>EXTSEQNO 0</code> telling it where to start reading. The checkpoint table is <code>oggadmin.checkpoint</code>:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7859 ogg_pg_01) 2&gt; DBLOGIN USERIDALIAS pgalias DOMAIN OracleGoldenGate
Successfully logged into database.

OGG (http://localhost:7859 ogg_pg_01 as pgalias@pgstore) 3&gt; ADD REPLICAT REPPGL, EXTFILE p1, EXTSEQNO 0, CHECKPOINTTABLE oggadmin.checkpoint
2026-09-26T09:14:42Z  INFO    OGG-30509  Connected to database server using ODBC DSN: ogg_pg_01 with SSLMODE 'disable'
2026-09-26T09:14:42Z  INFO    OGG-08100  Replicat added.</code></pre>



<h2 id="baseline-without-the-feature" class="wp-block-heading">Baseline: without the feature</h2>



<p class="wp-block-paragraph"><code>REPPGL</code>’s parameter file starts with just a <code>MAP</code>, no <code>DDL</code> clause at all:</p>



<pre class="wp-block-code"><code>REPLICAT REPPGL
TARGETDB ogg_pg_01 USERIDALIAS pgalias DOMAIN OracleGoldenGate
MAP AUTOSCH.*, TARGET AUTOSCH.*;</code></pre>



<p class="wp-block-paragraph">With this replicat, the initial load stops on the first table, as it always did:</p>



<pre class="wp-block-code"><code>2026-09-26 05:52:11  WARNING OGG-00869  Could not retrieve definition for table AUTOSCH.T1.
2026-09-26 05:52:11  ERROR   OGG-00199  Table AUTOSCH.T1 does not exist in target database.</code></pre>



<h2 id="enabling-automatic-schema-evolution" class="wp-block-heading">Enabling automatic schema evolution</h2>



<p class="wp-block-paragraph">The feature is called <code>AUTOSCHEMA</code>, but as a parameter on a line by itself, it is rejected:</p>



<pre class="wp-block-code"><code>2026-09-26T05:52:30Z  ERROR   OGG-10141  (REPPGL.prm) line 3 column 1: Parsing error, value "AUTOSCHEMA" syntax error.
2026-09-26T05:52:30Z  ERROR   OGG-10184  Parameter file REPPGL.prm failed validity check.</code></pre>



<p class="wp-block-paragraph"><code>AUTOSCHEMA</code> is an <strong>option of the <code>DDL</code> parameter</strong>, followed by the usual <code>INCLUDE</code> and <code>EXCLUDE</code> clauses:</p>



<pre class="wp-block-code"><code>REPLICAT REPPGL
TARGETDB ogg_pg_01 USERIDALIAS pgalias DOMAIN OracleGoldenGate
DDL AUTOSCHEMA INCLUDE ALL
MAP AUTOSCH.*, TARGET AUTOSCH.*;</code></pre>



<p class="wp-block-paragraph">The replicat starts and announces the feature, then finds the table missing and creates it:</p>



<pre class="wp-block-code"><code>2026-09-26 05:52:41  INFO    OGG-30684  The Automatic Schema Evolution feature is enabled. Please refer to the GoldenGate documentation for the supported operations, the datatype mappings, and the corresponding parameter options to enable/disable/override the functionality.
2026-09-26 05:52:41  WARNING OGG-00869  Could not retrieve definition for table AUTOSCH.T1.
2026-09-26 05:52:41  WARNING OGG-30633  Table AUTOSCH.T1 does not exist in target database, AutoSchema will attempt to create the missing table.
2026-09-26 05:52:41  INFO    OGG-02756  The definition for table AUTOSCH.T1 is obtained from the trail file.</code></pre>



<p class="wp-block-paragraph">The same warning <code>OGG-00869</code> as in the baseline is still there. But it is followed by <code>OGG-30633</code> and by <code>OGG-02756</code>, which shows that the definition comes from the trail file. In PostgreSQL:</p>



<pre class="wp-block-code"><code>pgstore=# \d autosch.t1
                            Table "autosch.t1"
 Column  |              Type              | Collation | Nullable | Default
---------+--------------------------------+-----------+----------+---------
 id      | character varying(50)          |           | not null |
 name    | character varying(50)          |           |          |
 amount  | numeric(10,2)                  |           |          |
 created | timestamp(0) without time zone |           |          |
Indexes:
    "t1_pkey" PRIMARY KEY, btree (id)

pgstore=# select * from autosch.t1 order by 1;
 id | name  | amount |       created
----+-------+--------+---------------------
 1  | row 1 |  10.25 | 2026-09-26 05:43:53
 2  | row 2 |  20.50 | 2026-09-26 05:43:53
 3  | row 3 |  30.75 | 2026-09-26 05:43:53
 4  | row 4 |  41.00 | 2026-09-26 05:43:53
 5  | row 5 |  51.25 | 2026-09-26 05:43:53
(5 rows)</code></pre>



<p class="wp-block-paragraph"><strong>The table was created, the primary key too, and the 5 rows were loaded.</strong> This is the default datatype mapping at work:</p>



<figure class="wp-block-table"><table><thead><tr><th>Oracle column</th><th>PostgreSQL column</th></tr></thead><tbody><tr><td><code>ID NUMBER</code> (primary key)</td><td><code>character varying(50)</code></td></tr><tr><td><code>NAME VARCHAR2(50)</code></td><td><code>character varying(50)</code></td></tr><tr><td><code>AMOUNT NUMBER(10,2)</code></td><td><code>numeric(10,2)</code></td></tr><tr><td><code>CREATED DATE</code></td><td><code>timestamp(0) without time zone</code></td></tr></tbody></table></figure>



<p class="wp-block-paragraph">The primary key is the surprise: a <code>NUMBER</code> without precision becomes a <code>varchar</code> on PostgreSQL. Check the mapping before you let a replicat create tables that an application will query. It can be overridden, which is the subject of another post.</p>



<h2 id="what-happens-when-the-table-already-exists" class="wp-block-heading">What happens when the table already exists</h2>



<p class="wp-block-paragraph">Between two loads, I added a column to the source table (<code>ALTER TABLE autosch.t1 ADD (note VARCHAR2(20) DEFAULT 'added')</code>). I also inserted a stray row in the PostgreSQL table. Then I reran <code>EXTPGL</code> and pointed the same <code>REPPGL</code>, still with <code>DDL AUTOSCHEMA INCLUDE ALL</code>, at the new file. It did <strong>not</strong> change the existing table:</p>



<pre class="wp-block-code"><code>2026-09-26 05:53:26  INFO    OGG-06511  Using following columns in default map by name: id, name, amount, created.
2026-09-26 05:53:26  WARNING OGG-01004  Canceled grouped transaction on table autosch.t1. Database error 3505685, (SQLState = 23000 SQLError = 3,505,685 SQLErrorHex = 00357e15 SQLErrorText = &#091;Oracle]&#091;ODBC PostgreSQL Wire Protocol driver]&#091;PostgreSQL]ERROR: VERROR; duplicate key value violates unique constraint "t1_pkey"(Detail Key (id)=(1) already exists.; sautosch; tt1; nt1_pkey; File nbtinsert.c; Line 666; Routine _bt_check_unique; )).
2026-09-26 05:53:26  ERROR   OGG-01296  Error mapping from AUTOSCH.T1 to autosch.t1.</code></pre>



<p class="wp-block-paragraph">The new source column <code>NOTE</code> is silently left out of the column mapping: <strong>an initial load does not alter an existing table</strong>. The load then abends on <code>duplicate key value violates unique constraint "t1_pkey"</code>, as the rows are already there. So <code>DDL AUTOSCHEMA INCLUDE ALL</code> creates <strong>missing</strong> tables only.</p>



<p class="wp-block-paragraph">The documentation says that for an initial load trail, <code>DDL AUTOSCHEMA</code> <em>“automatically creates or replaces target tables”</em>. It does not say how to get the replacement. The replicat binary has a <code>RECREATE</code> token, and it is a DDL <strong>operation type</strong>, so it goes in the <code>INCLUDE</code> filter:</p>



<pre class="wp-block-code"><code>DDL AUTOSCHEMA INCLUDE ALL OPTYPE RECREATE</code></pre>



<h3 id="testing-recreate" class="wp-block-heading">Testing <code>RECREATE</code></h3>



<p class="wp-block-paragraph">To see what <code>RECREATE</code> does, I prepare a PostgreSQL table that already exists. It has a column that is not on the source and a row that is not in the load:</p>



<pre class="wp-block-code"><code>CREATE TABLE autosch.t1 (
  id varchar(50) PRIMARY KEY,
  name varchar(50),
  amount numeric(10,2),
  created timestamp(0),
  legacy text
);
INSERT INTO autosch.t1 (id, name, legacy)
VALUES ('99', 'stray row', 'pre-existing');</code></pre>



<p class="wp-block-paragraph">The initial load is delivered the way the documentation describes it:</p>



<ul class="wp-block-list">
<li>A <code>SOURCEISTABLE</code> extract writes the 5 rows of <code>AUTOSCH.T1</code> to the extract file <code>ci</code>.</li>



<li>A distribution path sends <code>ci</code> to the receiver of the PostgreSQL deployment, as the trail <code>di</code>.</li>



<li>A replicat reads the trail <code>di</code> with <code>DDLOPTIONS REPORT</code> and an explicit <code>MAP AUTOSCH.T1, TARGET AUTOSCH.T1;</code>.</li>
</ul>



<p class="wp-block-paragraph">With <code>DDL AUTOSCHEMA INCLUDE ALL</code>, the replicat recognizes the initial load by itself. It builds a <code>RECREATE</code> operation from the table definition record, then <strong>excludes it</strong>: <code>INCLUDE ALL</code> does not cover that operation type.</p>



<pre class="wp-block-code"><code>2026-10-03 13:11:47  INFO    OGG-00489  DDL is of mapped scope, after mapping new operation &#091; /* AutoSchema */ create table "autosch"."t1" (ID varchar(50) not null, NAME varchar(50), AMOUNT decimal(10,2), CREATED timestamp(0), primary key(ID)) (size 150)].
2026-10-03 13:11:47  INFO    OGG-00488  DDL operation excluded &#091;not included by any filter], optype &#091;RECREATE], objtype &#091;TABLE], objowner "autosch", objname "t1".</code></pre>



<p class="wp-block-paragraph">The 5 rows are loaded into the existing table, next to the stray row, and the <code>legacy</code> column is still there:</p>



<pre class="wp-block-code"><code> id |   name    | amount |       created       |    legacy
----+-----------+--------+---------------------+--------------
 1  | row 1     |  10.25 | 2026-09-26 09:11:40 |
 2  | row 2     |  20.50 | 2026-09-26 09:11:40 |
 3  | row 3     |  30.75 | 2026-09-26 09:11:40 |
 4  | row 4     |  41.00 | 2026-09-26 09:11:40 |
 5  | row 5     |  51.25 | 2026-09-26 09:11:40 |
 99 | stray row |        |                     | pre-existing
(6 rows)</code></pre>



<h3 id="second-run-with-the-recreate-filter" class="wp-block-heading">Second run with the <code>RECREATE</code> filter</h3>



<p class="wp-block-paragraph">The parameter file for this second run only differs from the previous one by the <code>OPTYPE RECREATE</code> filter:</p>



<pre class="wp-block-code"><code>REPLICAT REPPGL
TARGETDB ogg_pg_01 USERIDALIAS pgalias DOMAIN OracleGoldenGate
DDLOPTIONS REPORT
DDL AUTOSCHEMA INCLUDE ALL OPTYPE RECREATE
MAP AUTOSCH.T1, TARGET AUTOSCH.T1;</code></pre>



<p class="wp-block-paragraph">With this file and the same table reset, the replicat warns about what it is going to do, then drops the table:</p>



<pre class="wp-block-code"><code>2026-10-03 13:12:34  WARNING OGG-30687  The OPTYPE 'RECREATE' is specified with DDL AUTOSCHEMA option. Please note that the corresponding pre-existing tables will be attempted to be dropped and re-created, only when the REPLICAT is applying initial load trail files. In case of CDC trails, REPLICAT would attempt to create the tables, only when the corresponding tables do not exist already.
2026-10-03 13:12:34  INFO    OGG-00487  DDL operation included &#091;INCLUDE ALL OPTYPE RECREATE], optype &#091;RECREATE], objtype &#091;TABLE], objowner "autosch", objname "t1".
2026-10-03 13:12:34  INFO    OGG-30640  Table autosch.t1 is dropped successfully.
2026-10-03 13:12:34  INFO    OGG-00484  Executing DDL operation.
2026-10-03 13:12:34  INFO    OGG-00483  DDL operation successful.</code></pre>



<p class="wp-block-paragraph">The table is recreated from the table definition record and loaded, and <strong>the <code>legacy</code> column and the stray row are gone</strong>.</p>



<pre class="wp-block-code"><code>                            Table "autosch.t1"
 Column  |              Type              | Collation | Nullable | Default
---------+--------------------------------+-----------+----------+---------
 id      | character varying(50)          |           | not null |
 name    | character varying(50)          |           |          |
 amount  | numeric(10,2)                  |           |          |
 created | timestamp(0) without time zone |           |          |
Indexes:
    "t1_pkey" PRIMARY KEY, btree (id)

 id | name  | amount |       created
----+-------+--------+---------------------
 1  | row 1 |  10.25 | 2026-09-26 09:11:40
 2  | row 2 |  20.50 | 2026-09-26 09:11:40
 3  | row 3 |  30.75 | 2026-09-26 09:11:40
 4  | row 4 |  41.00 | 2026-09-26 09:11:40
 5  | row 5 |  51.25 | 2026-09-26 09:11:40
(5 rows)</code></pre>



<p class="wp-block-paragraph">No parameter tells the replicat that it applies an initial load: a trail written by a <code>SOURCEISTABLE</code> extract is enough. I got the same drop and recreate in three setups. The replicat was added on the trail with <code>EXTTRAIL</code>, added with <code>EXTFILE</code>, or reading a copy of the raw extract file. On a CDC trail, <code>RECREATE</code> does not drop anything, as the warning says.</p>



<h3 id="a-wildcard-map-breaks-recreate-on-postgresql" class="wp-block-heading">A wildcard <code>MAP</code> breaks <code>RECREATE</code> on PostgreSQL</h3>



<p class="wp-block-paragraph">The <code>MAP</code> of the previous test was explicit (<code>MAP AUTOSCH.T1, TARGET AUTOSCH.T1;</code>). The baseline of this post used <code>MAP AUTOSCH.*, TARGET AUTOSCH.*;</code>. I replaced it with the wildcard on the same table and the same trail, and nothing else changed:</p>



<pre class="wp-block-code"><code>REPLICAT REPPGL
TARGETDB ogg_pg_01 USERIDALIAS pgalias DOMAIN OracleGoldenGate
DDLOPTIONS REPORT
DDL AUTOSCHEMA INCLUDE ALL OPTYPE RECREATE
MAP AUTOSCH.*, TARGET AUTOSCH.*;</code></pre>



<p class="wp-block-paragraph">This time, <strong>the replicat never drops the table</strong>. There is no <code>OGG-30640</code> in the report, and the <code>CREATE TABLE</code> that follows abends because the table is still there:</p>



<pre class="wp-block-code"><code>2026-10-03 13:17:02  INFO    OGG-06506  Wildcard MAP resolved (entry AUTOSCH.*): MAP "AUTOSCH"."T1", TARGET AUTOSCH."T1".
2026-10-03 13:17:02  INFO    OGG-00487  DDL operation included &#091;INCLUDE ALL OPTYPE RECREATE], optype &#091;RECREATE], objtype &#091;TABLE], objowner "autosch", objname "T1".
2026-10-03 13:17:02  INFO    OGG-00484  Executing DDL operation.
2026-10-03 13:17:02  ERROR   OGG-00519  Fatal error executing DDL replication: error &#091;Error code &#091;6844183], &#091;Oracle]&#091;ODBC PostgreSQL Wire Protocol driver]&#091;PostgreSQL]ERROR: VERROR; relation "t1" already exists(File heap.c; Line 1167; Routine heap_create_with_catalog; )], no error handler present.
2026-10-03 13:17:02  ERROR   OGG-30538  Fatal error executing DDL statement &#091; /* AutoSchema */ create table "autosch"."t1" (ID varchar(50) not null, NAME varchar(50), AMOUNT decimal(10,2), CREATED timestamp(0), primary key(ID))].</code></pre>



<p class="wp-block-paragraph">The first line of the report explains it. The wildcard is resolved from the table name in the trail, so the target becomes <code>AUTOSCH."T1"</code>: the case of the source (uppercase on Oracle), and quoted. PostgreSQL stores an unquoted name in lowercase, so my existing table is <code>t1</code>, not <code>"T1"</code>. The replicat looks for <code>"T1"</code> to drop it, finds nothing, and the <code>CREATE TABLE</code> that follows hits the real <code>t1</code>. To check this, I created the existing table in PostgreSQL as <code>"T1"</code> (quoted, so uppercase). Then I ran the same wildcard replicat: <strong>this time the drop works</strong>.</p>



<pre class="wp-block-code"><code>2026-10-03 13:27:14  INFO    OGG-06506  Wildcard MAP resolved (entry AUTOSCH.*): MAP "AUTOSCH"."T1", TARGET AUTOSCH."T1".
2026-10-03 13:27:14  INFO    OGG-00487  DDL operation included &#091;INCLUDE ALL OPTYPE RECREATE], optype &#091;RECREATE], objtype &#091;TABLE], objowner "autosch", objname "T1".
2026-10-03 13:27:14  INFO    OGG-30640  Table autosch.T1 is dropped successfully.
2026-10-03 13:27:14  INFO    OGG-00483  DDL operation successful.</code></pre>



<p class="wp-block-paragraph">The names are matched exactly. With an explicit <code>MAP AUTOSCH.T1, TARGET AUTOSCH."T1";</code> on a lowercase <code>t1</code>, the replicat created a second table <code>"T1"</code> and left <code>t1</code> alone. Without quotes, the target of an explicit <code>MAP</code> is lowercased for PostgreSQL, which is why the first test dropped <code>t1</code>. So with <code>RECREATE</code> on PostgreSQL, the target name in the <code>MAP</code> must be spelled exactly like the existing table. Write <strong>one explicit <code>MAP</code> per table</strong>, unquoted for a lowercase table. The <code>RECREATE</code> parameter file with the explicit <code>MAP</code> above does exactly that.</p>



<h2 id="limitations" class="wp-block-heading">Limitations</h2>



<ul class="wp-block-list">
<li><p><strong><code>AUTOSCHEMA</code> is not for Oracle-to-Oracle replication.</strong> Pointing the same <code>DDL AUTOSCHEMA INCLUDE ALL</code> replicat at an Oracle target instead of PostgreSQL abends immediately:</p>
<div class="code-block-wrapper"><pre tabindex="0" data-language="text"><code>2026-09-26 05:54:11  ERROR   OGG-30624  AUTOSCHEMA is not allowed in an Oracle to Oracle replication scenario. Please use the DDL replication to control schema evolution.</code></pre></div>
<p>Between two Oracle databases, regular DDL replication is still the standard tool for schema evolution, and a GoldenGate initial load still has no automatic way to create the target table.</p></li>



<li><p>The trail must be format 19.1 or higher with Table Definition Records.</p></li>



<li><p><code>UNMAPPED</code> and <code>OTHER</code> scope are <strong>not valid with <code>AUTOSCHEMA</code></strong>. Adding <code>INCLUDE UNMAPPED</code> (or <code>INCLUDE OTHER</code>) next to <code>INCLUDE ALL</code> parses fine, but the replicat rejects it at startup:</p>
<div class="code-block-wrapper"><pre tabindex="0" data-language="text"><code>2026-09-26 07:12:06  ERROR   OGG-30689  The 'UNMAPPED' is not a valid DDL SCOPE to be used with Automatic Schema Evolution.</code></pre></div></li>
</ul>



<h2 id="to-summarize" class="wp-block-heading">To summarize</h2>



<ul class="wp-block-list">
<li>Automatic schema evolution is enabled on the replicat with <code>DDL AUTOSCHEMA INCLUDE ALL</code>.</li>



<li>During an initial load from Oracle to PostgreSQL, it creates the missing table and its primary key from the Table Definition Record of the trail (<code>OGG-30633</code>). Then it loads the rows.</li>



<li>The datatypes come from a default mapping: check it first, as a <code>NUMBER</code> primary key became a <code>varchar(50)</code>.</li>



<li>An initial load leaves an existing table alone. A column added on the source is not added on the target, and the rows are loaded into it.</li>



<li><code>DDL AUTOSCHEMA INCLUDE ALL OPTYPE RECREATE</code> drops and recreates an existing table, only when the trail is an initial load trail (<code>OGG-30640</code>). <code>INCLUDE ALL</code> alone excludes that operation (<code>OGG-00488</code>).</li>



<li>Use one explicit <code>MAP</code> per table with <code>RECREATE</code> on PostgreSQL. A wildcard <code>MAP</code> looks for the table under the source name in quotes and misses a lowercase table. The <code>CREATE TABLE</code> then abends with <code>relation "t1" already exists</code>.</li>



<li>It is not available between two Oracle databases. Pointing it at an Oracle target from an Oracle source abends with <code>OGG-30624</code>. An Oracle-to-Oracle initial load still needs the target table to already exist, exactly as before.</li>
</ul>
<p>L’article <a href="https://www.dbi-services.com/blog/goldengate-26ai-automatic-schema-evolution-creating-missing-tables-during-an-initial-load/">GoldenGate 26ai automatic schema evolution: creating missing tables during an initial load</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/goldengate-26ai-automatic-schema-evolution-creating-missing-tables-during-an-initial-load/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 
Lazy Loading (feed)

Served from: www.dbi-services.com @ 2026-11-06 19:42:03 by W3 Total Cache
-->