AutoUpgrade New Features: Create and Use Your Own Gold Images

There are several advantages to using gold images to install new Oracle homes:

  • You know all servers get the exact same Oracle home.
  • You can install faster because you don’t need to apply all the patches.
  • They fit very well with automation.
  • They’re easier to test and work well with configuration management.

When you install an Oracle home using AutoUpgrade, you can also create your own gold images. Later, you can deploy the gold image to new servers.

Create Gold Image

I will use AutoUpgrade to install a new Oracle home with the desired patches and create a gold image.

  1. I’ve already downloaded the patches.
  2. Here’s my config file:
    global.global_log_dir=/home/oracle/autoupgrade/log
    install1.download_folder=/home/oracle/patch-repo
    install1.source_home=/u01/app/oracle/product/19
    install1.target_home=/u01/app/oracle/product/19_32
    install1.create_gold_image=db_home_%RELEASE%_%UPDATE%_%TIMESTAMP%.zip
    install1.patch=recommended
    
    • I’ve already downloaded the patches to download_folder.
    • I want to create a new Oracle home in the target_home location.
    • AutoUpgrade copies the settings from the source_home.
    • After installing the new Oracle home, AutoUpgrade creates a gold image in download_folder. Instead of specifying the name of the gold image, I can also set create_gold_image=yes and let AutoUpgrade generate a name.
    • I set patch=recommended to get the recommended patches: latest Release Update, OPatch, MRP, OJVM, and Data Pump bundle patches.
  3. I start AutoUpgrade to install the new Oracle home:
    java -jar autoupgrade.jar -config install-home.cfg -patch -mode create_home
    
    • In 19c, the installation takes longer because AutoUpgrade must apply the Release Update.
    • In 26ai, this process is much faster because Oracle delivers gold images with the latest Release Update already applied.
  4. When AutoUpgrade completes, I can find the gold image in my download_folder.
  5. It took 15 minutes to install the Oracle home and apply all the patches, plus five minutes to generate the gold image.

Install From Gold Image

Now I move to a different server. I need a new Oracle home and I’ll use the gold image.

  1. Here’s my AutoUpgrade config file:
    global.global_log_dir=/home/oracle/autoupgrade/log
    install1.download_folder=/home/oracle/patch-repo
    install1.source_home=/u01/app/oracle/product/19
    install1.target_home=/u01/app/oracle/product/19_32
    install1.patch=GOLDIMAGE:db_home_19_32_20260819113026.zip
    
    • Notice the patch parameter. I specify the gold image instead of individual patches.
  2. I start AutoUpgrade to install the new Oracle home:
    java -jar autoupgrade.jar -config install-home.cfg -patch -mode create_home
    
    • AutoUpgrade extracts the gold image and performs the installation.
    • There are no patches to apply. The gold image is already up to date.
  3. That’s it! I now have an Oracle home with exactly the same patches.
  4. It took just two minutes to install the gold image. That’s much faster than the 15 minutes it took for a regular installation.

That’s It

If you’re not already using gold images, you should get started. AutoUpgrade makes them easy to create and deploy.

Happy patching!

AutoUpgrade New Features: Download 26ai Release Update Rather Than Gold Image

Starting with Oracle AI Database 26ai, Oracle delivers fully updated gold images in addition to just the Release Update.

This is very convenient because:

  • It significantly shortens the installation time of a new Oracle home because the Release Update has already been applied.
  • It includes an updated OPatch.
  • It includes an updated OCW component.
  • It includes an updated JDK and Perl installation.

AutoUpgrade downloads the gold image automatically and you get to enjoy the much shorter installation time.

But sometimes you want just the Release Update.

Download Release Update Patch File

  • Here’s my AutoUpgrade config file:

    global.global_log_dir=/home/oracle/autoupgrade/logs
    global.keystore=/home/oracle/autoupgrade/keystore
    global.folder=/home/oracle/autoupgrade/patches
    
    patch1.platform=LINUX.X64
    patch1.target_version=26
    patch1.patch=RU
    patch1.gold_image=no
    
    • I set gold_image=no to tell AutoUpgrade to download the Release Update patch instead of the gold image.
  • I start AutoUpgrade in download mode:

    java -jar autoupgrade.jar -patch -config download.cfg -mode download
    
  • AutoUpgrade locates the latest Release Update and starts the download.

    AutoUpgrade Patching 26.4.260701 launched with default internal options
    Processing config file ...
    Loading AutoUpgrade Patching keystore
    AutoUpgrade Patching keystore is loaded
    
    ------------------------------------------------------
    Downloading files to /home/oracle/autoupgrade/patches
    ------------------------------------------------------
    DATABASE RELEASE UPDATE 23.26.3.0.0
        File: p39578879_230000_Linux-x86-64.zip - VALIDATED
    ------------------------------------------------------
    
  • You can distinguish between the Release Update patch and the gold image by their descriptions:

    • Gold image: DATABASE RELEASE UPDATE 23.26.3.0.0(GOLD IMAGE)
    • Patch file: DATABASE RELEASE UPDATE 23.26.3.0.0

That’s It

Gold images are the fastest way to provision a new Oracle home. But when you need the Release Update patch instead, AutoUpgrade lets you choose.

Happy patching!

How to Use Data Pump with Exadata Exascale Storage

Do you know that feeling when you get new toys?

A short while ago, in a server room far, far away …

… a brand-new, shiny Oracle Exadata Exascale was left unguarded, so I decided to try out a few things.

Here’s how to export to and import from Exadata Exascale storage.

Prerequisites

  1. I create a directory for the Data Pump dump files pointing to the Exascale vault:
    create directory dumpdir as '@MYVAULT1/CDB26/SALES/DUMPDIR';
    
    • MYVAULT1 is the name of my vault. Notice the @ sign, which denotes Exascale vaults, like + in ASM.
    • I can specify subdirectories (CDB26/SALES/DUMPDIR) to organize my files. However, there’s no concept of directories in Exascale, so these are really path prefixes.
    • Unlike a regular file system or ASM, I don’t have to create the matching file system directory.
  2. I create another directory for my Data Pump log files:
    create directory logdir as '/u02/app/oracle/admin/CDB26/SALES/logdir';
    
    • I can’t use the Exascale directory for log files.
    • Similar to ASM, I can’t store log files in Exascale.
  3. I create the matching file system directory for logdir:
    mkdir -p /u02/app/oracle/admin/CDB26/SALES/logdir
    
    • In Oracle RAC, I should place the directory in a cluster file system or ensure that the folder exists on all nodes.
  4. Finally, I create a user to perform the Data Pump jobs:
    create user dpuser identified by <password>;
    alter user dpuser default tablespace users;
    alter user dpuser quota unlimited on users;
    grant datapump_exp_full_database to dpuser; 
    grant datapump_imp_full_database to dpuser; 
    

Export

  • Here’s how to start an export:

    expdp dpuser/<password>@<connect-string> \
    	dumpfile=dumpdir:myexp%L.dmp \
    	logfile=logdir:myexp-export.log \
    	...
    
    • I write the dump file to my Exascale vault by specifying the database directory (dumpdir) and then the dump file name specification. I’m using the %L wildcard to allow Data Pump to create additional files.
    • Data Pump must write the log file to a real file system, so I use logdir which points to a local file system.
  • At the end of the export, Data Pump writes the names of my dump files:

    04-AUG-26 11:08:36.114: Dump file set for DPUSER.SYS_EXPORT_SCHEMA_01 is:
    04-AUG-26 11:08:36.114:   @MYVAULT1/CDB26/SALES/DUMPDIR/myexp01.dmp
    04-AUG-26 11:08:36.145: Job "DPUSER"."SYS_EXPORT_SCHEMA_01" successfully completed at Tue Aug 4 11:08:36 2026 elapsed 0 00:01:03
    

Import

  • Here’s how to start an import:
    impdp dpuser/<password>@<connect-string> \
    	dumpfile=dumpdir:myexp%L.dmp \
    	logfile=logdir:myexp-import.log \
    	...
    
    • I configure the dumpfile and logfile parameters the same way as in the export.
    • I can use the %L wildcard in the dump file specification for imports as well. Data Pump automatically finds the required dump files.

Pro Tips

Happy Data Pumping!

AutoUpgrade New Features: The Patch Overview

I trust you’ve already used AutoUpgrade to download patches. If not, you’re missing out big time.

Here’s a nifty new feature that makes AutoUpgrade fit better into your automation and gives you an overview of what you’ve downloaded already.

The Download JSON File

  • AutoUpgrade constructs and maintains a JSON file named patches_info.json.
  • AutoUpgrade stores the file in the download folder (config file entry folder).
  • It contains information about all the patches AutoUpgrade has downloaded.
  • The file is cumulative, so it contains information about not just the latest download but also previous ones.

Example

Here’s a sample output:

{
    "patchFolder": "/home/oracle/patches",
    "patches": [
        {
            "description": "DATABASE RELEASE UPDATE 23.26.3.0.0(GOLD IMAGE)",
            "platform": "Linux x86-64",
            "releaseUpdate": "23.26.3.0.0",
            "files": [
                {
                    "name": "p39581612_230000_Linux-x86-64.zip",
                    "checksum": "DDBEBAC94B5F0B7D3FF4910AB01DB8FCDD3390A7",
                    "checksum-256": "2CAFECB11DBDD7F81C55DEE9FC846AA7E8F8C265721C1B8AB4B23EF13F94643D"
                }
            ]
        },
        {
            "description": "OPatch 12.2.0.1.52 for DB 23.0.0.0.0 (Jul 2026)",
            "platform": "Linux x86-64",
            "files": [
                {
                    "name": "p6880880_230000_Linux-x86-64.zip",
                    "checksum": "21EF498D3ECA4E734467A02FD0A799C464008726",
                    "checksum-256": "8C0D19B7774CD2E0443D2FC2BD29D3A784383199BEA3867CD4D39E2D32BFA6CC"
                }
            ]
        }
    ]
}

Checksum

After downloading a file, AutoUpgrade automatically checks the integrity of the file by calculating and verifying the checksum.

If there is a discrepancy, AutoUpgrade deletes the file and informs you.

If you want to verify it manually, you can find the expected checksum in the JSON file.

Human Readable

JSON is good for machines, but bad for humans, so here’s a party trick to beautify the output:

jq -r '
["TYPE","DESCRIPTION","PLATFORM","FILE","SHA1","SHA256"],
(.patches[] |
 [
   (if .description|test("RELEASE UPDATE") then "RU"
    elif .description|test("OPatch") then "OPATCH"
    elif .description|test("OJVM") then "OJVM"
    elif .description|test("DATAPUMP") then "DPBP"
    else "PATCH" end),
   .description,
   (.platform // "Generic"),
   .files[0].name,
   .files[0].checksum,
   .files[0]["checksum-256"]
 ]) | @tsv
' patches_info.json | column -t -s $'\t'

This command turns the JSON file into a tabular format:

TYPE    DESCRIPTION                                      PLATFORM      FILE                               SHA1                                      SHA256
RU      DATABASE RELEASE UPDATE 23.26.3.0.0(GOLD IMAGE)  Linux x86-64  p39581612_230000_Linux-x86-64.zip  DDBEBAC94B5F0B7D3FF4910AB01DB8FCDD3390A7  2CAFECB11DBDD7F81C55DEE9FC846AA7E8F8C265721C1B8AB4B23EF13F94643D
OPATCH  OPatch 12.2.0.1.52 for DB 23.0.0.0.0 (Jul 2026)  Linux x86-64  p6880880_230000_Linux-x86-64.zip   21EF498D3ECA4E734467A02FD0A799C464008726  8C0D19B7774CD2E0443D2FC2BD29D3A784383199BEA3867CD4D39E2D32BFA6CC

Thanks to Abhilash Kumar for the tip.

Happy patching!

Upgrade Encrypted Oracle Database 19c Non-CDB to 26ai and Convert to PDB Using Refreshable Clone PDB

Let me show you how to upgrade an encrypted non-CDB to Oracle AI Database 26ai. Since this release only supports the multitenant architecture, you must also convert it to a PDB.

To preserve the source database for rollback and to minimize downtime, I’ll use AutoUpgrade and refreshable clone PDBs.

How to Upgrade and Convert

AutoUpgrade and refreshable clone PDBs give you the option of moving the database to a different host. If you want to stay on the same host, just imagine source and target hosts are the same.

Start by familiarizing yourself with the restrictions on refreshable clone PDB for non-CDBs.

1. Preparations

I’ve already prepared my database.

I’ve also installed a new Oracle home and created a new CDB, or decided to use an existing one. The CDB can be on the same or a different system than the source non-CDB.

  1. In the source non-CDB, I create a user:
    create user dblinkuser identified by ... ;
    grant create session, 
    create pluggable database, 
    select_catalog_role to dblinkuser;
    grant read on sys.enc$ to dblinkuser;
    
    • I need the user so I can connect from the CDB via a database link.
    • I’ll drop it after the upgrade.
  2. In my target CDB, as SYS, I create a database link connecting to my source non-CDB:
    create database link clonepdb 
    connect to dblinkuser identified by ...
    using 'source-db-alias';
    

2. Analyze

I must run the pre-upgrade analysis on the source system.

  1. I create an AutoUpgrade config file for the analysis. I call it upgrade26-analyze.cfg:
    global.global_log_dir=/home/oracle/autoupgrade/upgrade26-analyze
    upg1.source_home=/u01/app/oracle/product/19
    upg1.target_version=26
    upg1.sid=FTEX
    upg1.target_cdb=CDB26
    upg1.target_is_remote=yes
    
    • Since the target Oracle home doesn’t exist on my source system, I omit target_home.
    • Without the target Oracle home, AutoUpgrade can’t deduce the version I’m upgrading to, so I must specify target_version=26.
    • I specify target_is_remote=yes because the CDB is on another system. This causes AutoUpgrade to skip a few checks that it would normally run.
  2. I start AutoUpgrade in analyze mode:
    java -jar autoupgrade.jar -config upgrade26-analyze.cfg -mode analyze
    
  3. I check the pre-upgrade summary report:
    cd /home/oracle/autoupgrade/kraken/cfgtoollogs/upgrade/auto/status
    vi status.log
    

3. Initial Clone

  1. I create a config file on my target system. I call it upgrade26.cfg:
    global.global_log_dir=/home/oracle/autoupgrade/upgrade26
    global.keystore=/home/oracle/autoupgrade/keystore
    upg1.source_home=/tmp
    upg1.source_base=/u01/app/oracle
    upg1.target_home=/u01/app/oracle/product/26
    upg1.sid=FTEX
    upg1.target_cdb=CDB26
    upg1.source_dblink.FTEX=CLONEPDB 1800
    upg1.target_pdb_name.FTEX=PDB1
    upg1.start_time=19/01/2038 03:14:07
    upg1.parallel_pdb_creation_clause.FTEX=2
    upg1.target_pdb_copy_option.FTEX=file_name_convert=NONE
    upg1.drop_dblink=yes
    
    • I set source_home to /tmp because it doesn’t exist on the target system. I set source_base to the Oracle base of my target system.
    • sid is the database that I want to upgrade and convert.
    • target_cdb is the SID of the database where the non-CDB ends up.
    • source_dblink.<sid> is the name of the database link and the refresh rate in seconds.
    • target_pdb_name.<sid> allows me to rename the database. I strongly recommend that you rename the database if your CDB is on the same system. Otherwise, you’ll have a service name collision to deal with.
    • start_time is when AutoUpgrade performs the final refresh and starts the upgrade/conversion. I set it far out in the future, so I can better control the final refresh using the proceed command. Leave a comment if you know what happens at the specified time. :-)
    • parallel_pdb_creation_clause.<sid> limits the number of parallel processes used by the CDB to make the initial copy. Set it at a reasonable level that doesn’t overload the source database.
    • target_pdb_copy_option.<sid> allows me to specify the location of the data files. I use OMF and let the database decide where to put the files.
    • drop_dblink instructs AutoUpgrade to drop the database link when it’s no longer needed.
  2. I start the AutoUpgrade password console to load the target CDB keystore password:
    java -jar autoupgrade.jar -config upgrade26.cfg -load_password
    
    • This is required because my database is encrypted.
    • AutoUpgrade prompts me for the AutoUpgrade keystore password. This is a password to protect the AutoUpgrade keystore; not the database keystore.
  3. I want to add the database keystore password of the target CDB:
    TDE> add CDB26
    
    • CDB26 is the SID of the database and matches the target_cdb parameter in the config file.
    • I must enter the database keystore password.
  4. I save the database keystore password in the AutoUpgrade keystore:
    TDE> save
    
    • I agree to create an auto-login keystore and enter yes when prompted.
  5. Exit from the password console:
    TDE> exit
    
  6. Next, I start AutoUpgrade in deploy mode:
    java -jar autoupgrade.jar -config upgrade26.cfg -mode deploy
    
    • AutoUpgrade copies the data files over the database link.
    • Rolls the copies of the data files forward with redo from the source non-CDB.
    • There’s no outage. The source database remains open.
  7. Leave the AutoUpgrade session running.
    • Since the session might run for a while, better start it with tmux, screen, or similar.
    • Don’t use nohup because you must be able to interact with AutoUpgrade.
    • If your session disconnects, just restart AutoUpgrade with the same command.

4. Upgrade And Convert

The maintenance window has started, and users have left the database.

  1. On the source system, I run the pre-upgrade fixups on the source database:

    java -jar autoupgrade.jar -config upgrade26-analyze.cfg -mode fixups 
    
    • AutoUpgrade informs me that it can’t run certain preupgrade fixups because the target Oracle home doesn’t exist on the source system. This is expected and ignorable.
  2. Next, I switch to the target system. From the AutoUpgrade console, I instruct AutoUpgrade to proceed with the upgrade:

    upg> proceed -job 100
    
    • AutoUpgrade performs a final refresh.
    • Then, it disconnects the PDB from the source.
    • Then, starts the upgrade and conversion.
  3. While the job progresses, I monitor it:

    upg> lsj -a 30
    
    • The -a 30 option automatically refreshes the information every 30 seconds.
    • I can also use status -job 100 -a 30 to get detailed information about a specific job.
  4. In the end, AutoUpgrade completes the upgrade:

    Job 101 completed
    ------------------- Final Summary --------------------
    Number of databases            [ 1 ]
    
    Jobs finished                  [1]
    Jobs failed                    [0]
    Jobs restored                  [0]
    Jobs pending                   [0]
    
    Please check the summary report at:
    /home/oracle/autoupgrade/upgrade26/cfgtoollogs/upgrade/auto/status/status.html
    /home/oracle/autoupgrade/upgrade26/cfgtoollogs/upgrade/auto/status/status.log
    
    • This includes the post-upgrade checks and fixups.
  5. I review the Autoupgrade Summary Report. The path is printed to the console:

    vi /home/oracle/autoupgrade/upgrade26/cfgtoollogs/upgrade/auto/status/status.log
    
  6. I take care of the post-upgrade tasks.

  7. I update any profiles or scripts that use the database.

  8. I ensure that the source non-CDB is shut down.

    • If source non-CDB and target CDB are on the same system, AutoUpgrade stops the source non-CDB. You can override this using the parameter close_source.
    • When I’m sure I won’t need the source non-CDB anymore, I can delete it.
  9. I can remove the database link user:

    drop user dblinkuser cascade;
    
  10. Recreate the connection services.

  11. If you’ve configured Data Guard on your target CDB, you must restore the PDB on all standbys. Check the appendix for details.

That’s It!

With AutoUpgrade, you can easily upgrade your encrypted non-CDB and convert it to a PDB. For maximum protection, AutoUpgrade lets me preserve the source non-CDB in case I need to roll back.

Check the other blog posts related to upgrade to Oracle AI Database 26ai.

Happy upgrading!

Appendix

What If I Have Data Guard

Refreshable clone PDB doesn’t propagate fully to the standbys. The plug-in operation happens with deferred recovery.

After plug-in on the primary database, the standbys don’t protect the PDB. You must first restore the PDB to each standby database.

Also, check pages 210-221 in Move to Oracle Database 23ai – Everything you need to know about Oracle Multitenant – part 1.

Draining the Source Non-CDB

When your maintenance window starts, you must kick users off.

In a previous blog post, I gave an idea on how you can do that. Whatever you do, don’t restart the source database. It’ll break the refreshable clone PDB.

Does It Work Cross-Platform

Refreshable clone PDB does not work for cross-endian migrations (like AIX to Linux), but cross-platform should work fine (like Windows to Linux).

What If My Database Is A RAC Database?

  • Ensure that all instances can resolve the database link connection identifier and that the database link works from all instances. The CREATE PLUGGABLE DATABASE statement scales out on all instances for the initial cloning.
  • In your config file, when specifying the sid parameter you must use the SID on the node where AutoUpgrade runs. If my database is called DB19 and it runs on host1 which is instance 1 in my cluster, I’d specify:
    upg1.sid=DB191
    
  • Recreate services in the target CDB using srvctl. If you have many services, consider export/import of the services.

Other Config File Parameters

The config file shown above is a basic one. Let me address some of the additional parameters you can use.

  • timezone_upg: AutoUpgrade upgrades the database time zone file after the actual upgrade. This requires an additional restart of the database and might take significant time if you have lots of TIMESTAMP WITH TIME ZONE data. If so, you can postpone the time zone file upgrade or perform it in a more time-efficient manner.

  • before_action / after_action: Extend AutoUpgrade with your own functionality by using scripts before or after the job.

Compatible

During plug-in, the PDB automatically inherits the compatible setting of the target CDB. You don’t have to raise the compatible setting manually.

Typically, the target CDB has a higher compatible and the PDB raises it on plug-in. This means you don’t have the option of downgrading.

If you want to preserve the option of downgrading, be sure to set the compatible parameter in the target CDB to the same value as the source CDB.

Automating AutoUpgrade: Populating the Keystore

A customer had to move hundreds of PDBs using AutoUpgrade and refreshable clone PDBs. As a cool customer, they wanted to automate the entire process.

The PDBs are encrypted, so AutoUpgrade needs the source and target TDE keystore passwords. AutoUpgrade stores these passwords in its own keystore until they are needed.

You can manually load the passwords into the AutoUpgrade keystore using the load password console (-load_password).

But loading passwords manually doesn’t fit very well with automation.

The Solution

For security reasons, there is no native way in AutoUpgrade to load these passwords besides typing them manually.

Loading passwords via response files or command line parameters exposes the sensitive information, e.g., in your command history.

But if you accept the risk, you can load passwords using the expect command. You’ll need to place the passwords in clear-text in a file for a short period. When the loading completes, you can remove the file and the passwords are now safely stored in the AutoUpgrade keystore.

Loading Passwords Using Expect

  1. Here’s my AutoUpgrade config file called sales.cfg:

    global.autoupg_log_dir=/home/oracle/autoupgrade/logs
    global.keystore=/home/oracle/autoupgrade/keystore
    upg1.sid=CDB19
    upg1.target_cdb=CDB26
    upg1.pdbs=SALES
    upg1.source_home=/u01/app/oracle/product/19.0.0.0/dbhome_1
    upg1.target_home=/u01/app/oracle/product/23.0.0.0/dbhome_1
    upg1.target_pdb_copy_option.SALES=file_name_convert=NONE
    upg1.source_dblink.SALES=CLONE_LINK_SALES 300
    upg1.start_time=01/01/2030 08:03:00
    
    • I’ve configured the location of the AutoUpgrade keystore using the global.keystore parameter.
  2. I create an expect file called sales.exp. I set the executable flag and ensures no others can read the file:

    touch /dev/shm/sales.exp
    chmod 700 /dev/shm/sales.exp
    
    • Since the file will contain my passwords, I place it in /dev/shm which is a tmpfs or RAM-based file system. I don’t want the file on persistent storage.
  3. I add the following to my expect file.

    #!/usr/bin/expect
    
    spawn java -jar autoupgrade.jar -config sales.cfg -load_password
    expect "Enter password:"
    send -- "MyS3cr3tPassw0rd#\r"
    expect "Enter password again:\r"
    send -- "MyS3cr3tPassw0rd#\r"
    expect "TDE>\r"
    send -- "add CDB19\r"
    expect "Enter your secret/Password:\r"
    send -- "Databas3CDB19#\r"
    expect "Re-enter your secret/Password:\r"
    send -- "Databas3CDB19#\r"
    expect "TDE>\r"
    send -- "add CDB26\r"
    expect "Enter your secret/Password:\r"
    send -- "Databas3CDB26#\r"
    expect "Re-enter your secret/Password:\r"
    send -- "Databas3CDB26#\r"
    expect "TDE>\r"
    send -- "save\r"
    expect "Select auto-login mode for the AutoUpgrade keystore"
    send -- "YES\r"
    expect "TDE>\r"
    send -- "exit\r"
    expect eof	
    
    • Using the spawn command I start the AutoUpgrade load password console.
    • I can wait for AutoUpgrade to print certain information using the expect command.
    • Then I can send the appropriate response – a command or a password – using the send command.
    • Be sure to add the \r at the end of your commands or passwords. All the passwords end with a # so you can see how the control character is appended.
    • This file contains the passwords in clear-text. Make sure the file is protected with restrictive file permissions.
  4. I start the password loading using expect:

    expect /dev/shm/sales.exp
    
    • expect starts the AutoUpgrade load password console and inputs the passwords when needed.
  5. I remove the expect file:

    rm /dev/shm/sales.exp
    
  6. Now that the keystore is populated with the TDE keystore passwords, I can move on with the process.

MOS Credentials For Patch Downloading

Pardon me for sidetracking a bit.

  • You can use the same approach to populate the keystore with MOS credentials for patch downloading.
  • Here’s an example of an expect file:
    #!/usr/bin/expect
    
    spawn java -jar autoupgrade.jar -patch -config download.cfg -load_password
    expect "Enter password:"
    send -- "MyS3cr3tPassw0rd#\r"
    expect "Enter password again:\r"
    send -- "MyS3cr3tPassw0rd#\r"
    expect "MOS>\r"
    send -- "add -user ash@weyland-yutani.com\r"
    expect "Enter your secret/Password:\r"
    send -- "MyS3cr3tMOSPassw0rd#\r"
    expect "Re-enter your secret/Password:\r"
    send -- "MyS3cr3tMOSPassw0rd#\r"
    expect "MOS>\r"
    send -- "save\r"
    expect "Select auto-login mode for the AutoUpgrade keystore"
    send -- "YES\r"
    expect "MOS>\r"
    send -- "exit\r"
    expect eof
    
    • Replace MyS3cr3tPassw0rd# with the password you want for the AutoUpgrade keystore.
    • Replace ash@weyland-yutani.com with your MOS username.
    • Replace MyS3cr3tMOSPassw0rd# with your MOS password.

That’s It

With expect you can automate the loading of passwords into the AutoUpgrade keystore.

I consider it safer to load passwords manually, but to fully automate the process you must cut a corner.

I suggest using a unique keystore for each of your AutoUpgrade invocations. This makes it easier to automate. AutoUpgrade can also create a shared keystore that you can create once and then distribute to other servers.

Happy upgrading!

Upgrade Oracle Database 11g to 19c Using RMAN Incremental Backups

A customer wanted help upgrading and moving to new hardware:

I have an 11g database running on old hardware. I want to upgrade to 19c and move to new hardware. I can’t use Data Guard to move the database because I can’t install 11g binaries on the new hardware. What do I do?

Data Guard would be the obvious choice, but wasn’t possible here. But we can use RMAN incremental backups.

This allows us to move the database with little downtime and then perform the upgrade.

The Plan

Here’s an overview of the plan:

  1. Perform a level 0 backup of the 11g database.
  2. Restore the backup into a 19c instance on the new hardware.
  3. Prepare the database for upgrade.
  4. Perform a level 1 backup.
  5. Recover the database.
  6. Upgrade the database.

We can perform steps 1-2 in advance and save time during the outage.

Any newer version of Oracle AI Database can restore backups from a previous release. But to upgrade the database, we must be within the limitations for a direct upgrade. Oracle Database 19c supports direct upgrades from 11g.

Step 1: Initial Backup

The database is called UPGR and runs on the old hardware as an 11g database.

  1. On the old system, I start by doing a level 0 backup of the source database:
    RMAN> CONNECT TARGET /
          RUN {
          ALLOCATE CHANNEL c1 DEVICE TYPE DISK;
          ALLOCATE CHANNEL c2 DEVICE TYPE DISK;
    
          BACKUP AS COMPRESSED BACKUPSET
             INCREMENTAL LEVEL 0
             DATABASE
             FORMAT '/u01/app/oracle/backup/L0_%d_%T_%U.bkp'
             PLUS ARCHIVELOG
             FORMAT '/u01/app/oracle/backup/L0_%d_%T_%U.bkp'
             TAG 'LEVEL0';
    
          BACKUP CURRENT CONTROLFILE
             FORMAT '/u01/app/oracle/backup/CTL.bkp'
             TAG 'CONTROLFILE';
    
          RELEASE CHANNEL c1;
          RELEASE CHANNEL c2;
          }
    
    • I store the backups on an NFS share that’s accessible to the new system as well.
    • I’m doing a compressed backup. Be sure you’re licensed for that.
    • You can customize the backup script.
  2. I create a PFile:
    SQL> CREATE PFILE='/u01/app/oracle/backup/pfile.txt'
         FROM SPFILE;
    

Step 2: Configure Instance and Restore

I create a new instance on the new hardware. I must use the same name, UPGR.

  1. On the new hardware, I create a PFile for my target instance. I use the original PFile as a template and make the necessary changes. Here’s the PFile that I’ll use:
    *._cursor_obsolete_threshold=1024
    *.audit_file_dest='/u01/app/oracle/admin/UPGR/adump'
    *.audit_trail='db'
    *.compatible='11.2.0.4.0'
    *.control_files='/u02/oradata/UPGR/controlfile/control01.ctl'
    *.db_block_size=8192
    *.db_create_file_dest='/u02/oradata'
    *.db_name='UPGR'
    *.db_recovery_file_dest_size=12884901888
    *.db_recovery_file_dest='/u02/fast_recovery_area'
    *.diagnostic_dest='/u01/app/oracle'
    *.pga_aggregate_target=1G
    *.sga_target=4G
    
    • I keep the same db_name.
    • Adjust locations, like db_create_file_dest and control_files accordingly.
  2. I create the directory specified by audit_file_dest:
    mkdir -p /u01/app/oracle/admin/UPGR/adump
    
  3. I set the environment and create a new password file:
    export ORACLE_HOME=/u01/app/oracle/product/19
    export ORACLE_BASE=/u01/app/oracle
    export ORACLE_SID=UPGR
    export PATH=$ORACLE_HOME/bin:$PATH
    
    $ORACLE_HOME/bin/orapwd file=orapwUPGR password=MyS3cr3tPassw0rd!
    
  4. Next, I start the new instance in NOMOUNT mode:
    SQL> STARTUP NOMOUNT
    
  5. I restore the control file:
    RMAN> CONNECT TARGET /
          RUN {
          RESTORE CONTROLFILE
             FROM '/u01/app/oracle/backup/CTL.bkp';
          }
    
    • I restore from the NFS share.
  6. Then, I mount the database and start the restore:
    RMAN> CONNECT TARGET /
          ALTER DATABASE MOUNT;
          RUN {
          ALLOCATE CHANNEL c1 DEVICE TYPE DISK;
          ALLOCATE CHANNEL c2 DEVICE TYPE DISK;
    
          SET NEWNAME FOR DATABASE TO NEW;
    
          CATALOG START WITH '/u01/app/oracle/backup/' NOPROMPT;
    
          RESTORE DATABASE;
          SWITCH DATAFILE ALL;
          RECOVER DATABASE UNTIL AVAILABLE REDO;
    
          RELEASE CHANNEL c1;
          RELEASE CHANNEL c2;
          }
    
    • I use Oracle Managed Files (OMF), so I use SET NEWNAME accordingly.
    • I use CATALOG to tell RMAN where to find the backups.
    • I restore the database and recover until there’s no more redo.

Step 3: Prepare for Upgrade

Downtime starts now.

  1. On the source system, I create an AutoUpgrade config file, so I can check the upgrade readiness of my database.
    global.global_log_dir=/home/oracle/autoupgrade-logs
    upg1.source_home=/u01/app/oracle/product/11.2.0.4
    upg1.target_home=/tmp
    upg1.target_version=19
    upg1.sid=UPGR
    
    • I can’t set target_home to the real value because it doesn’t exist on the source server. I use a fake entry instead.
    • AutoUpgrade normally deduces the target_version from target_home, but since it doesn’t exist, I need to specify it manually.
  2. I perform the preupgrade check:
    java -jar autoupgrade.jar -config UPGR.cfg -mode analyze
    
    • Check the preupgrade summary report.
    • I use the latest version of AutoUpgrade.
  3. I run the preupgrade fixups:
    java -jar autoupgrade.jar -config UPGR.cfg -mode fixups
    
  4. Then, I run an incremental backup:
    RMAN> CONNECT TARGET /
          RUN {
          ALLOCATE CHANNEL c1 DEVICE TYPE DISK;
          ALLOCATE CHANNEL c2 DEVICE TYPE DISK;
    
          BACKUP AS COMPRESSED BACKUPSET
             INCREMENTAL LEVEL 1
             DATABASE
             FORMAT '/u01/app/oracle/backup/L1_%d_%T_%U.bkp'
             PLUS ARCHIVELOG
             FORMAT '/u01/app/oracle/backup/L1_%d_%T_%U.bkp'
             TAG 'LEVEL1';
    
          RELEASE CHANNEL c1;
          RELEASE CHANNEL c2;
          }
    
    • The incremental backup captures the last changes to my database.
  5. I shut down the database on the old system.

Step 4: Recover

  1. On the target database, I recover the incremental backup.
    RMAN> CONNECT TARGET /
          RUN {
          ALLOCATE CHANNEL c1 DEVICE TYPE DISK;
          ALLOCATE CHANNEL c2 DEVICE TYPE DISK;
    
          CATALOG START WITH '/u01/app/oracle/backup/' NOPROMPT;
    
          RECOVER DATABASE UNTIL AVAILABLE REDO;
    
          RELEASE CHANNEL c1;
          RELEASE CHANNEL c2;
          }
    
    • I use the CATALOG command to tell RMAN about the new backup.
    • I recover the database until there is no more redo.
  2. Then, I open the database in upgrade mode:
    SQL> ALTER DATABASE OPEN RESETLOGS UPGRADE;
    
    • I use the RESETLOGS clause to create redo logs.
    • I must open in upgrade mode to start the upgrade. If I try to open in normal mode, I’ll get an error ORA-00704: bootstrap process failure.

Step 5: Upgrade

The database is open in upgrade mode. I must perform the upgrade to 19c.

  1. On the target system, I create an AutoUpgrade config file:
    global.global_log_dir=/home/oracle/autoupgrade-logs
    upg1.source_home=/tmp
    upg1.target_home=/u01/app/oracle/product/19
    upg1.sid=UPGR
    
    • I can’t set source_home to the real value because it doesn’t exist on the target server. I use a fake entry instead.
  2. I perform the upgrade:
    java -jar autoupgrade.jar -config UPGR.cfg -mode upgrade
    
    • I start with -mode upgrade to complete the upgrade. Don’t use -mode deploy.
    • I use the latest version of AutoUpgrade.
  3. After a while, the upgrade completes. All done.

That’s it

RMAN incremental backup is a great feature to reduce downtime when Data Guard is not an option. You can restore to a higher release of Oracle AI Database as long as you stay within the limits for a direct upgrade.

You can enhance the procedure:

  • Run additional level 1 incremental backup/restores to minimize the time it takes to perform the last one.
  • Enable Block Change Tracking on the old database to shorten the time it takes to do incremental backups.
  • Run an additional AutoUpgrade preupgrade check days in advance to get early notice on any showstoppers.

Happy upgrading!

How To Avoid ORA-39405 During a Data Pump Import

Why did my Data Pump import fail?

Import: Release 19.0.0.0.0 - Production on Mon Jun 29 06:17:06 2025
Version 19.31.0.0.0

ORA-39002: invalid operation
ORA-39405: Oracle Data Pump does not support importing from a source database with TSTZ version 44 into a target database with TSTZ version 45.

It all comes down to the TIMESTAMP WITH TIME ZONE (TSTZ) data type.

The database uses the timezone file to translate your TSTZ data to the right time. If the Daylight Saving Time (DST) rules change, a newer timezone file ensures that you still get the right data.

Data Pump refuses to import the data because differing timezone file versions could lead to incorrect TSTZ values.

The Solution

Simple: apply at least Release Update 19.27 and install the Data Pump bundle patch.

Data Pump now converts your TSTZ values when needed. During data, loading Data Pump uses an internal function called ORA_DST_CONVERT that does all the magic. The function can convert between both older and newer timezone file versions. Because each row requires a function call during import, you should expect a small performance overhead. I expect it to be negligible.

Remember, you can install the Data Pump bundle patch online without any downtime.

But I Don’t Have Timezone Data?

If your dump file doesn’t contain any TSTZ data, it’s safe to import even if the timezone file versions differ.

Older versions of Data Pump were overly cautious. Even without TSTZ data, they immediately failed when they detected a timezone file mismatch.

Why Does This Happen?

A few years ago, Oracle started shipping newer timezone file versions with every Release Update. It’s convenient and ensures that you always have the latest files installed.

But when you create a database, it gets the latest timezone file automatically. As you create databases over time, they’ll end up using different timezone file versions.

This is when you start seeing ORA-39405 in Data Pump.

In the good, ol’ days, timezone files were separate patches. Most people didn’t install them, so all of their databases ended up using the same timezone file version.

That’s It

If you have different timezone file versions in your source and target databases, Data Pump must convert your TSTZ columns to match the target database’s timezone file version. This requires at least Release Update 19.27 and the Data Pump bundle patch.

Happy importing!

How to Create an OCI PDB with a Specific Time Zone File Version

When you provision a new database in Oracle Cloud Infrastructure (OCI), it always comes with the latest timezone file installed.

But in a recent migration, we wanted a database with a specific timezone file version.

Here’s how you can get a new PDB with a custom timezone file.

Using OCI Tooling Doesn’t Work

The OCI tooling uses a template file to provision the database faster. But the template file comes with the latest timezone file. Timezone files are part of the Release Update, so the newer Release Update you’re on, the newer the timezone file is.

Using template files for provisioning means that you don’t get to choose which version of the timezone file you want in your database. Further, the usual hacks like removing timezone files from the Oracle home or using the environment variable ORA_TZFILE won’t work.

The Solution

I’m going to create a new PDB in an on-prem database, export that PDB to OCI and use that as my new PDB.

  • I start by finding an existing CDB with the desired timezone file, or I create a new one.

    select version from v$timezone_file;
    
    VERSION
    ----------
    44
    
  • I don’t need to check

    • The patch level: I’ll sort out any patch differences with Datapatch in OCI.
    • The components: CDBs in OCI have all components installed.
  • I create a new empty PDB:

    CREATE PLUGGABLE DATABASE PDBTEMPLATE ADMIN USER ADMIN IDENTIFIED BY mys3cr3tpassw0rd!;
    
  • I close and unplug my PDB:

    ALTER PLUGGABLE DATABASE PDBTEMPLATE CLOSE;
    ALTER PLUGGABLE DATABASE PDBTEMPLATE UNPLUG INTO '/home/oracle/pdbtemplate.pdb';
    DROP PLUGGABLE DATABASE PDBTEMPLATE INCLUDING DATAFILES;
    
  • I transfer the PDB to my host in OCI. In my test, the size of the PDB was 600 MB.

  • Now, I can create a new PDB using the archive file:

    CREATE PLUGGABLE DATABASE PDBNEW USING '/home/oracle/pdbtemplate.pdb';
    ALTER PLUGGABLE DATABASE PDBNEW OPEN READ WRITE;
    
    • The PDB probably opens with plug-in violations. I ignore this for now.
  • I need to sort out any patching difference:

    $ORACLE_HOME/OPatch/datapatch -pdbs PDBNEW
    
  • After a restart I check for plug-in violations:

    ALTER PLUGGABLE DATABASE PDBNEW CLOSE IMMEDIATE;
    ALTER PLUGGABLE DATABASE PDBNEW OPEN;
    SELECT TYPE, CAUSE, MESSAGE, ACTION 
    FROM   PDB_PLUG_IN_VIOLATIONS 
    WHERE  NAME='PDBNEW' 
           AND STATUS != 'RESOLVED'
           AND NOT (CAUSE='OPTION' AND TYPE='WARNING' AND MESSAGE LIKE '%PDB installed version NULL%');
    
    TYPE                       CAUSE                                                                                           MESSAGE                     ACTION
    __________ ___________________________ _________________________________________________________________________________________________ __________________________
       WARNING    is encrypted tablespace?    Tablespace SYSTEM is not encrypted. Oracle Cloud mandates all tablespaces should be encrypted.    Encrypt the tablespace.
       WARNING    is encrypted tablespace?    Tablespace SYSAUX is not encrypted. Oracle Cloud mandates all tablespaces should be encrypted.    Encrypt the tablespace.
    
    • The query removes any warnings about components missing in my PDB.
    • I can ignore the warning about missing encryption of SYSTEM and SYSAUX
  • Finally, I create an encryption key (or rotate the key if the PDB is already encrypted):

    ALTER SESSION SET CONTAINER=PDBNEW;
    ADMINISTER KEY MANAGEMENT SET KEY
       FORCE KEYSTORE IDENTIFIED BY <keystore-password>
       WITH BACKUP;
    
  • Last, let’s check the timezone file versions in my CDB:

    ALTER SESSION SET CONTAINER=CDB$ROOT;
    SELECT   CON$NAME, VALUE$ 
    FROM     CONTAINERS(SYS.PROPS$) 
    WHERE    NAME='DST_PRIMARY_TT_VERSION' 
    ORDER BY 1;
    
       CON$NAME    VALUE$
    ___________ _________
    CDB$ROOT    45
    PDBNEW      44
    
    • The CDB uses the latest timezone file version, 45.
    • My new PDB uses an older timezone file, 44.

That’s It!

This workaround enables you to create PDBs with a custom timezone file version. I can use the same approach if I want a PDB with a specific set of components installed.

If there is network connectivity between the source and target CDB, I could also clone the PDBTEMPLATE over a network link.

Happy migrating!