A while back I wrote about datapatch stores rollback patching files in the database, and the amount of space it may consume over time, especially if you patch frequently. I am fully aware that it took a bit longer, but finally since a few months, there is a relief. You can now Cleanup datapatch patching files in your database, and reclaim the space as well.

Photo by JESHOOTS.COM on Unsplash
A quick recap
In my blog post from January 2024 I shared queries to check the space consumed by previous patches in your database. In brief, to allow seamless rollback of patches especially in Multitenant environments when you unplug/plug PDBs but also for other reasons, the database stored the rollback scripts for each patch run in a compressed zip format. This is good and useful, but unfortunately, it happened with every patch run – and it kept all the previous zip files as well.
As a consequence of this finding we filed a tracking bug, Bug 34674756 – PURGE OLD PATCH METADATA ZIP BLOB FROM DATAPATCH INVENTORY, IF NOT NEEDED, and the datapatch team fixed it – which wasn’t as trivial as one may think.
The fix is available
==> You may want to skip to The Final Blog Post about the topic at this point.
Currently, the fix is available as a one-off patch for 19.24.0. It was scheduled to be included into 19.25.0 Release Update but had got pulled because of a performance topic. It is now scheduled to be included into the following RU. This means, if you want to the fix on top of 19.24.0, you simply download it from MOS. In case it isn’t available on top of your desired Release Update, you need to open a service request (SR), and request a backport on top of your RU for Bug 34674756. Please make sure to include your “opatch lsinventory” with this SR as well.
The performance topic is fixed already as Bug 37055793 – and it is supposed to be included soon.
How to use the fix?
==> You may want to skip to The Final Blog Post about the topic at this point.
If you wonder how the fix gets used, this is very simple and straight forward. You apply the fix, in my case on top of 19.24.0 RU, then you open all your PDBs. As soon as you invoke datapatch -verbose, the associated scripts run, and once the scripts for the fix gets executed, the cleanup will start.
Please be prepared that – depending on the size – it may take a little bit. But since your database can be open and accessible already, this shouldn’t be much of an issue when you apply the fix just by itself.
In my case, all my containers had almost 1GB consumed by the zip files in sys.registry$sqlpatch_ru_info (RUs) and sys.registry$sqlpatch (one-offs).
After datapatch had finished its duties, the cleanup completed its work as well. Only one single ZIP file stayed in my sys.registry$sqlpatch_ru_info – all the other rows were still there but without the zips, hence, not using any space anymore. The same happened for sys.registry$sqlpatch where all the space got released since the patches aren’t even present anymore in my current environment.
Only the runtime may need some improvement – but I know that we are working on this already.
Scripts to check patch consumption within your database
I am sharing the scripts from my previous blog post here again:
Release Updates and Bundles
column patch_id format 9999999999
column ru_version format a15
column lob_size_md format 9999
COLUMN ru_build_ts FORMAT A20
set linesize 100
set pagesize 300
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'MM/DD/YY HH24:MI';
ALTER SESSION SET "_EXCLUDE_SEED_CDB_VIEW" = FALSE;
SELECT
patch_id,
ru_version,
TO_CHAR(ru_build_timestamp, 'MM/DD/YYYY HH24:MI:SS') AS ru_build_ts,
round(dbms_lob.getlength(patch_directory) / 1024 / 1024) lob_size_mb
FROM
sys.registry$sqlpatch_ru_info;
SELECT
con_id, round(sum(dbms_lob.getlength(patch_directory) / 1024 / 1024)) total_lob_size_mba
FROM
containers(sys.registry$sqlpatch_ru_info)
GROUP BY
con_id
ORDER BY
con_id;
One-Off Patches
column patch_id format 9999999999
column lob_size_md format 9999
COLUMN SUBSTR(description,1,40) FORMAT A40
set linesize 100
set pagesize 300
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'MM/DD/YY HH24:MI';
ALTER SESSION SET "_EXCLUDE_SEED_CDB_VIEW" = FALSE;
SELECT
patch_id,
SUBSTR(description,1,40) PATCH_DESCRIPTION,
TO_CHAR(source_build_timestamp, 'MM/DD/YYYY HH24:MI:SS') AS patch_build_ts,
round(dbms_lob.getlength(patch_directory) / 1024 / 1024) lob_size_mb
FROM
sys.registry$sqlpatch
WHERE
patch_type<>'RU';
SELECT
con_id, round(sum(dbms_lob.getlength(patch_directory) / 1024 / 1024)) total_lob_size_mba
FROM
containers(sys.registry$sqlpatch)
GROUP BY
con_id
ORDER BY
con_id;
Further Links and Information
- The Final Blog Post about this topic
- Blog Post: Cleanup Datapatch Patching Files In Your Database
- Blog Post: More about cleaning up datapatch patching files
- Datapatch User Guide (Doc ID 2680521.1)
- Script: check_patches_19.sql
- Script: unload_blob_from_table.sql
- Script: check_patches_size.sql
- Bug 34674756 – PURGE OLD PATCH METADATA ZIP BLOB FROM DATAPATCH INVENTORY, IF NOT NEEDED
- Correct patch to download: 37352486
–Mike
Hi,
i tested the patch:
1st applying the patch; followed by “datapatch -verbose” -> blob shrunk from 1,2GB to 176MB. Success 🙂
2nd i tested the opposite: rollbacked the patch; restored the database from before; “datapatch -verbose” -> blob also shrunk from 1,2GB to 176MB ? Doesn’t make any sense !
-> it looks to me like there’s a bug in the patch-rollback-routines ?
Retried both tests with thorough checking -> the same result every time.
Doesn’t make the patch “bad”, but i will ask Oracle in a SR if this is a bug or a feature 🙂
with kind regards,
Johann
Hi Johann,
I am checking with the patch developer – will get to you asap.
Cheers,
Mike
Hi Johann,
no need to open an SR. Here is the answer from the datapatch architect (and I may add it to the blog post as well):
“Datapatch code is marked as onewaycopy. That means, if datapatch code is updated, it will stay intact until it is updated again to newer version. Rolling back the ‘patch containing datapatch code fix’ doesn’t have any affect on the datapatch code. The patch is marked as rollback success, all the other content apart from datapatch’s code will rollback to previous versions, except for datapatch’s own code. (A user can only Update ‘datapatch code’, he can never “rollback”). Hence the name “onewaycopy”.
The reason for such a design is, to address the issues when running rollback sqls.
Datapatch code always has to remain the latest in OH to address any issues with rollback.
Even though you have “rolled back” the patch, “datapatch code fix “will remain latest and is not rolled back to previous version.
That is why, datapatch was able to shrink the LOB segments even after the said “patch rollback”.
To confirm this, customer could check if “sqlpatch.pm” remained same, before and after the patch rollback. (It should remain same).”
Hope this clarifies it 🙂
Cheers,
Mike (and thanks to Santosh for his detailed explanation)
Hey Mike. Thanks for the article. In doing some testing for this patch I noticed that the readme is missing steps for the installation – specifically about running datapatch. Just a heads up. Thanks.
Hi Devin,
unfortunately, READMEs are generic and don’t tell something about the actual “what to do to use it” part. They only cover how to apply and rollback.
Let me push out another blog post with more information 🙂
Cheers,
Mike
Hi.
We *don’t* run CDB/PDB and tried applying the OneOff 37055793 on RU26, but no luck, datapatch (sqlpatch.pm) gives errors:
Global symbol “%all_drs_rows” requires explicit package name (did you forget to declare “my %all_drs_rows”?) at /u01/app/oracle/product/19.0.0/dbhome_1/sqlpatch/sqlpatch.pm line 2942.
Global symbol “%all_drs_rows” requires explicit package name (did you forget to declare “my %all_drs_rows”?) at /u01/app/oracle/product/19.0.0/dbhome_1/sqlpatch/sqlpatch.pm line 2944.
Global symbol “%all_drsri_rows” requires explicit package name (did you forget to declare “my %all_drsri_rows”?) at /u01/app/oracle/product/19.0.0/dbhome_1/sqlpatch/sqlpatch.pm line 2953.
Global symbol “%all_drsri_rows” requires explicit package name (did you forget to declare “my %all_drsri_rows”?) at /u01/app/oracle/product/19.0.0/dbhome_1/sqlpatch/sqlpatch.pm line 2955.
Global symbol “%current_ru” requires explicit package name (did you forget to declare “my %current_ru”?) at /u01/app/oracle/product/19.0.0/dbhome_1/sqlpatch/sqlpatch.pm line 2966.
Global symbol “%all_patches” requires explicit package name (did you forget to declare “my %all_patches”?) at /u01/app/oracle/product/19.0.0/dbhome_1/sqlpatch/sqlpatch.pm line 2974.
Global symbol “%all_patches” requires explicit package name (did you forget to declare “my %all_patches”?) at /u01/app/oracle/product/19.0.0/dbhome_1/sqlpatch/sqlpatch.pm line 2976.
Global symbol “%all_drs_rows” requires explicit package name (did you forget to declare “my %all_drs_rows”?) at /u01/app/oracle/product/19.0.0/dbhome_1/sqlpatch/sqlpatch.pm line 2980.
Global symbol “%all_drsri_rows” requires explicit package name (did you forget to declare “my %all_drsri_rows”?) at /u01/app/oracle/product/19.0.0/dbhome_1/sqlpatch/sqlpatch.pm line 2981.
Global symbol “%all_patches” requires explicit package name (did you forget to declare “my %all_patches”?) at /u01/app/oracle/product/19.0.0/dbhome_1/sqlpatch/sqlpatch.pm line 2982.
/u01/app/oracle/product/19.0.0/dbhome_1/sqlpatch/sqlpatch.pm has too many errors.
Compilation failed in require at /u01/app/oracle/product/19.0.0/dbhome_1/sqlpatch/sqlpatch.pl line 138.
BEGIN failed–compilation aborted at /u01/app/oracle/product/19.0.0/dbhome_1/sqlpatch/sqlpatch.pl line 138.
Beware – The rollback of this OneOff doesn’t work, sqlpatch.pm is the same after rollback.
We had to restore a backup of ORACLE_HOME get get the original files in $ORACLE_HOME/sqlpatch back.
We will probably wait until this fix is included in a regular RU, we have already run the (working) superceeded OneOff 34674756, so our lobs are not that big yet.
Oh … did you open an SR?
I happily forward it to the development owner.
Thanks,Mike
I opened an SR for this problem too. Patch 37055793 for 19.26 got withdrawn yesterday, and now there should be a new patch for 19.26 released. I still have to download&test it.
regards,
Johann
Thanks Johann – I haven’t received a reply yet but will investigate further.
Cheers,
Mike
Hi Johann,
I think there is a recut, and the patch is available (again). At least MOS shows an update 14+ hours ago as of now.
Thanks,
Mike
No, no time for that, and the “bug” is not that critical for us.
Regards,
Krister
Hi Krister,
I think a recut of the fix will be done – I am investigating further.
Sorry for the inconvenience – cheers,
Mike
If we go to note 2965212.1 in traditional MOS the recommended patch, for Unpublished Bug 34674756, is 37352486
If we look in Cloud MOS the recommended patch is what you recommend in this blog, 37055793 .
Which one is the valid one? both? depending on Cloud or OnPremise?
Hi Jesus,
thanks for the inconvenience, and sorry for the confusion.
I tried to update all blog posts with the correct information but missed this one. Is is certainly:
Bug 37352486 – PROVIDE PURGING OBSOLOTE SQLPATCH METADATA AS AN OPTIONAL FEATURE
which I link from here:
https://mikedietrichde.com/2025/02/28/final-blog-post-about-datapatch-cleanup-and-purge/
But I forgot to update this other blog post, it slipped through the cracks.
Thanks for the pointer – it should be updated by now when you read my reply.
Mike
Hi Mike,
this is an interesting article.
I downloaded the patch p37492905_1926000DBRU_Linux-x86-64 and installed that on top of the DATABASE RELEASE UPDATE 19.26.0.0.0 patch (37260974)
and after that I executed “./datapatch -verbose” in the test databases.
I didn’t got any eror messages but the applied patch didn’t clean up anything.
The old entries are still there
Do I forget something here?
with regards, Henny
Sorry, my fault – I forgot to add the important link to this older blog post.
Please see here:
https://mikedietrichde.com/2025/02/28/final-blog-post-about-datapatch-cleanup-and-purge/
New option with the fix:
./datapatch -purge_old_metadataThis will do the trick.
Cheers,
Mike