Oracle Database 12c to 19c - A step-by-step, cross-server upgrade walkthrough

 — RMAN restore + manual catupgrd.sql path.

Oracle 19c is the long-running long-term support release of the 12.2 family and, even with the AI Database line (23ai, 26ai) now established as the forward path, it remains the practical landing zone for many enterprises still on 12c. For estates running 12c (12.2.0.1), an upgrade to 19c is no longer optional — it is the entry ticket to ongoing patching, security fixes, and application certifications, and the natural staging point before any later move to 23ai or 26ai. This post walks through a complete, validated upgrade of a single-instance, non-CDB database from 12.2.0.1 on one server to 19.3.0.0 on another, using a full RMAN backup, cross-server restore, and the manual catupgrd.sql path driven by catctl.pl. Every command, every parameter, and every checkpoint shown below is taken from a real end-to-end run.



1. Why upgrade to 19c, and why this path

Oracle Database 19c is positioned as the long-term support (LTS) release of the 12.2 family — the place where customers consolidate before the next big jump. For an enterprise still on 12.2.0.1, the case for upgrading combines several pressures that are difficult to defer:

  • Vendor support. Continued access to a supported, patched release line — no more deferred CPUs, no more unsupported edge cases.
  • Optimizer and performance improvements. 19c carries the matured optimizer and concurrency enhancements introduced across 18c and 19c.
  • Security and patching. A current PSU/RU cadence and modern security capabilities baked into the base release.
  • Application and infrastructure compatibility. Modern middleware, drivers, and OS combinations target 19c as the floor.
  • Operational reliability. Consolidating on a single LTS line simplifies fleet management, monitoring, and DR.
  • Reduced operational risk. A standardized upgrade procedure replaces ad-hoc remediation against an end-of-life version.

There are several supported ways to get from 12c to 19c — DBUA, AutoUpgrade, in-place dictionary upgrade — but the cross-server, RMAN-restore plus manual catupgrd.sql path covered here is still one of the most common in practice, particularly when the upgrade is also being used as an opportunity to move to new hardware. It is fully scriptable, backup-defended at every step, and leaves the source environment intact until the new system is signed off.

2. Scope, objectives, and what this guide does not cover

Objectives

  • Upgrade an Oracle Database from 12c (12.2.0.1) to 19c (19.3.0.0).
  • Migrate the database to a new server in the same operation.
  • Preserve data integrity and consistency end-to-end.
  • Minimize downtime and operational risk through a planned, RMAN-defended sequence.
  • Land the database on an Oracle-supported long-term release.

In scope

  • Single-instance database upgrade.
  • Cross-server migration (srv5 → srv6).
  • RMAN-based backup and restore.
  • Manual dictionary upgrade using catupgrd.sql driven by catctl.pl.

Out of scope

  • Oracle RAC upgrade.
  • Multitenant (CDB/PDB) conversion.
  • AutoUpgrade or DBUA-driven workflows.

3. Environments — source and target at a glance

The upgrade moves a single non-CDB database named PROD from a 12.2.0.1 home on srv5 to a fresh 19.3.0.0 home on srv6. Both servers run Oracle Linux 7.9. The database name is preserved across the move; only the server, the Oracle home, and the dictionary version change.



4. Prerequisites and pre-flight checklist

Before you touch RMAN>, confirm every item below. Skipping any of these is the single most common cause of a stalled upgrade.

  • Source database is running on Oracle 12c (12.2.0.1).
  • The target server has Oracle Database 19c software installed and patched.
  • Sufficient disk space exists for datafiles, archivelogs, and backup pieces — on both servers.
  • A valid SYSDBA-capable Oracle OS user exists on both ends.
  • The most recent RMAN backup has been verified for success and restorability.
  • Network connectivity is open between source and target (SSH/SCP for the backup transfer).
  • The source database is in ARCHIVELOG mode.

5. The nine-step upgrade methodology

The whole upgrade resolves to nine ordered steps. Everything that follows in this post is one of these steps, broken out with the actual commands and expected output:

  1. Generate a PFILE from the source SPFILE.
  2. Modify initialization parameters for 19c.
  3. Take a full RMAN backup of the source database.
  4. Transfer backup pieces to the target server.
  5. Restore the controlfile and database on the target.
  6. Perform recovery using the archived logs included in the backup.
  7. Start the database in UPGRADE mode.
  8. Run the Oracle upgrade scripts (catupgrd.sql via catctl.pl).
  9. Perform post-upgrade validation.



6. Step 1 — Validate the source 12c database (srv5)

Before anything destructive happens, confirm the source database is healthy and that all of its registered components are VALID. Anything less than VALID here will haunt the dictionary upgrade later.

[oracle@srv5 ~]$ . oraenv
ORACLE_SID = [prod] ? prod
The Oracle base remains unchanged with value /u01/app/oracle

[oracle@srv5 ~]$ sqlplus / as sysdba
SQL*Plus: Release 12.2.0.1.0 Production
Connected to:
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production

SQL> SELECT name, open_mode, database_role FROM v$database;
NAME      OPEN_MODE    DATABASE_ROLE
PROD      READ WRITE   PRIMARY

SQL> SELECT version FROM v$instance;
VERSION
12.2.0.1.0

The registered-components check is the most important one in this step:

SQL> col comp_name for a35;
SQL> SELECT comp_name, version, status FROM dba_registry;
ComponentVersionStatus
Oracle Database Catalog Views12.2.0.1.0VALID
Oracle Database Packages and Types12.2.0.1.0VALID
Oracle XML Database12.2.0.1.0VALID

And finally a quick inventory of datafiles and tablespaces — this is the reference the post-upgrade validation will compare against:

SQL> SELECT COUNT(*) AS datafile_count FROM dba_data_files;
DATAFILE_COUNT
            5

SQL> SELECT ROUND(SUM(bytes)/1024/1024/1024, 2) AS total_db_size_gb
     FROM dba_data_files;

SQL> SELECT tablespace_name, status FROM dba_tablespaces;
TablespaceStatus
SYSTEMONLINE
SYSAUXONLINE
UNDOTBSONLINE
TEMPTSONLINE
USERDATAONLINE
TEST_TSONLINE

7. Step 2 — Take a full RMAN backup of source

The full backup is the safety net for the entire upgrade. It captures the datafiles, archivelogs, and a current controlfile. Two parallel channels make the backup faster and produce backup pieces that are easy to track.

[oracle@srv5 ~]$ mkdir -p /u02/prodbkp
[oracle@srv5 ~]$ rman target /

Recovery Manager: Release 12.2.0.1.0 - Production
connected to target database: PROD (DBID=595760695)

RMAN> run
{
  allocate channel d1 type disk;
  allocate channel d2 type disk;
  backup database format '/u02/prodbkp/%d_%U_fullbkp';
  sql 'alter system switch logfile';
  sql 'alter system switch logfile';
  sql 'alter system switch logfile';
  backup archivelog all format '/u02/prodbkp/%d_%u_arch';
  backup current controlfile format '/u02/prodbkp/%d_%U.ctl';
  release channel d1;
  release channel d2;
}

Why three log switches before the archivelog backup?

The three switch logfile calls force the current redo to be archived so that the backup archivelog all step picks up every transaction up to the point of backup. Without them, the most recent redo stays in the online logs and is lost to the cross-server restore.

A successful run produces a familiar set of backup pieces — datafile sets, archivelog sets, and the controlfile copy:

piece handle=/u02/prodbkp/PROD_184efnl0_1_1_fullbkp   tag=TAG…  (datafiles)
piece handle=/u02/prodbkp/PROD_194efnl0_1_1_fullbkp   tag=TAG…  (datafiles)
piece handle=/u02/prodbkp/PROD_1b4efnlc_arch          tag=TAG…  (archivelogs)
piece handle=/u02/prodbkp/PROD_1c4efnlc_arch          tag=TAG…  (archivelogs)
piece handle=/u02/prodbkp/PROD_1e4efnll_1_1.ctl       tag=TAG…  (controlfile)

8. Step 3 — Transfer backup and prepare srv6

The backup pieces now have to land on the target server, and the directory skeleton on srv6 needs to be in place before any restore is attempted.

# On srv5 — confirm the pieces are present
[oracle@srv5 ~]$ cd /u02/prodbkp/
[oracle@srv5 prodbkp]$ ls
PROD_184efnl0_1_1_fullbkp  PROD_194efnl0_1_1_fullbkp  PROD_1b4efnlc_arch
PROD_1c4efnlc_arch         PROD_1d4efnlk_arch         PROD_1e4efnll_1_1.ctl

# On srv6 — create the destination
[oracle@srv6 ~]$ mkdir -p /u02/prodbkp
[oracle@srv6 ~]$ mkdir -p /u02/oradata/prod/
[oracle@srv6 ~]$ mkdir -p /u02/oradata/prod/diag
[oracle@srv6 ~]$ mkdir -p /u02/arch/prod

# Transfer
[oracle@srv5 prodbkp]$ scp * oracle@srv6:/u02/prodbkp/

By the end of this step srv6 should hold an exact copy of the backup tree, with the supporting oradata and arch directories pre-created so that the restore writes to known locations.

9. Step 4 — Generate and adapt the PFILE for 19c

The 12c SPFILE cannot be reused as-is on a 19c home. The cleanest pattern is to generate a PFILE from the source, transfer it to the target home, and edit it before starting the new instance.

# On srv5 — generate PFILE from SPFILE
SQL> show parameter spfile;
NAME    TYPE    VALUE
spfile  string  /u01/app/oracle/product/12.2.0.1/db_1/dbs/spfileprod.ora

SQL> CREATE PFILE FROM SPFILE;
File created.

[oracle@srv5 ~]$ cd $ORACLE_HOME/dbs
[oracle@srv5 dbs]$ cat initprod.ora

The resulting initprod.ora contains the parameters below — these are the source baseline:

prod.__db_cache_size=293601280
prod.__oracle_base=/u01/app/oracle
prod.__shared_io_pool_size=20971520
*.control_files=/u02/oradata/prod/control1.ctl
*.db_block_size=8192
*.db_cache_size=300M
*.db_name='prod'
*.diagnostic_dest=/u02/oradata/prod/diag
*.instance_name='prod'
*.java_pool_size=50M
*.large_pool_size=50M
*.log_archive_dest_1='location=/u02/arch/prod'
*.log_buffer=4M
*.shared_pool_size=350M
*.undo_management='auto'
*.undo_retention=900
*.undo_tablespace='undotbs'

Push the file across to the 19c home:

[oracle@srv5 dbs]$ scp initprod.ora \
    oracle@srv6:/u01/app/oracle/product/19.0.0/dbhome_1/dbs/

On srv6, open initprod.ora in the 19c dbs directory and confirm — or add — the all-important compatible setting:

*.compatible='19.3.0.0'

Why compatible matters

The compatible parameter governs which 19c features the database will be allowed to use after the upgrade and, just as importantly, the floor to which the database can be downgraded if you ever need to. Setting it to 19.3.0.0 aligns the dictionary upgrade and the runtime feature surface; advancing it later (for example to 19.0.0) is a one-way door.

10. Step 5 — Start the instance and restore the controlfile

With the parameter file ready, set the environment for the 19c home, start the instance in NOMOUNT mode, and restore the controlfile from the backup piece copied earlier.

[oracle@srv6 ~]$ export ORACLE_SID=prod
[oracle@srv6 ~]$ export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1
[oracle@srv6 ~]$ export PATH=$ORACLE_HOME/bin:$PATH

[oracle@srv6 dbs]$ sqlplus / as sysdba
SQL*Plus: Release 19.0.0.0 - Production
Version 19.3.0.0.0

From RMAN, restore the controlfile:

[oracle@srv6 dbs]$ rman target /
Recovery Manager: Release 19.0.0.0 - Production    Version 19.3.0.0.0
connected to target database: PROD (not mounted)

RMAN> RESTORE CONTROLFILE FROM '/u02/prodbkp/PROD_1e4efnll_1_1.ctl';

Starting restore at 21-JAN-24
allocated channel: ORA_DISK_1
channel ORA_DISK_1: restoring control file
channel ORA_DISK_1: restore complete, elapsed time: 00:00:01
output file name=/u02/oradata/prod/control1.ctl
Finished restore at 21-JAN-24

11. Step 6 — Restore and recover the database

With the controlfile in place, mount the database, catalog the backup pieces so RMAN knows where they live on the new host, and restore the datafiles. Recovery then replays the archivelogs included in the backup.

RMAN> ALTER DATABASE MOUNT;
Statement processed

RMAN> CATALOG START WITH '/u02/prodbkp/';
searching for all files that match the pattern /u02/prodbkp/

List of Files Unknown to the Database
=====================================
File Name: /u02/prodbkp/PROD_1e4efnll_1_1.ctl
…
Do you really want to catalog the above files (enter YES or NO)? YES
cataloging files...
cataloging done

RMAN> RESTORE DATABASE;

The restore lays the datafiles down at the paths pinned in the controlfile:

channel ORA_DISK_1: restoring datafile 00001 to /u02/oradata/prod/system01.dbf
channel ORA_DISK_1: restoring datafile 00002 to /u02/oradata/prod/sysaux01.dbf
channel ORA_DISK_1: restoring datafile 00003 to /u02/oradata/prod/undotbs01.dbf
channel ORA_DISK_1: restoring datafile 00004 to /u02/oradata/prod/userdata01.dbf
…
Finished restore at 21-JAN-24

Then recover, applying the archivelogs that travelled with the backup:

RMAN> RECOVER DATABASE;

starting media recovery
channel ORA_DISK_1: starting archived log restore to default destination
archived log thread=1 sequence=120
archived log thread=1 sequence=121
archived log thread=1 sequence=122
archived log thread=1 sequence=123
…
unable to find archived log
archived log thread=1 sequence=124

That last "unable to find" line is expected

Recovery rolls forward as far as the available archived logs allow, then stops at the first sequence it cannot find. For a cross-server upgrade where no further redo is being shipped from the source, this is the correct stopping point. The database is then opened with RESETLOGS as part of the upgrade-mode startup, which establishes a new incarnation on srv6.

12. Step 7 — Run the dictionary upgrade with catupgrd.sql

The bytes are now in 19c shape, but the data dictionary is still 12.2. The upgrade itself is performed by catupgrd.sql, executed in parallel via the catctl.pl driver. The driver lists the parameters it is running with — the practical defaults are 4 SQL processes and a single instance:

catctl.pl VERSION:    [19.0.0.0.0]
STATUS:               [Production]
BUILD:                [RDBMS_19.3.0.0.0DBRU_LIUNX.X64_190417]

Number of Cpus              = 1
Database Name               = prod
DataBase Version            = 12.2.0.1.0
Parallel SQL Process Count  = 4

Components in [prod]
  Installed     [CATALOG CATPROC XDB]
  Not Installed [APEX APS CATJAVA CONTEXT DV EM JAVAVM MGW ODM OLS ORDIM OWM
                 RAC SDO WK XML XOQ]

The upgrade then walks through phases 0 through 107, covering catalog, CATPROC, component-specific upgrades (XDB, ORDIM, SDO, ODM, etc.), final scripts, and post-upgrade tasks. The high-level groupings are below — every phase number from 0 to 107 ran in this database:

Phase groupPhase numbersWhat happens
Change scripts0Pre-upgrade change scripts.
Catalog1–5Core SQL, tables and views, final catalog scripts.
CATPROC start & types6–8Initialize CATPROC and load core types.
CATPROC tables and packages9–18Tables, package specs, and procedures (parallel where possible).
CATPROC views and CDB views19–34View rebuilds, CDB-aware view updates.
CATPROC PL/B and DataPump35–41Compiled PL/SQL, then DataPump dictionary fixes.
CATPROC SQL and final42–50Remaining CATPROC SQL plus final RDBMS scripts.
Component upgrades51–96XDB, ORDIM, SDO, ODM/WK/EXF/RUL/XOQ in turn.
Final, migration, post-upgrade97–107Final component scripts, migration, summary, and post-upgrade.

The driver brackets the run with start and end timestamps and a grand total:

Phases [0-107] Start Time:[2024_11_15 20:41:39]
…
Phases [0-107] End Time:  [2024_11_15 21:07:50]
Grand Total Upgrade Time: [0d:0h:26m:12s]

Twenty-six minutes is a reasonable benchmark for a small, single-instance database with the components listed above on a 1-CPU host. The driver also writes a per-phase log under cfgtoollogs/upgrade…, which is the file you reach for if any phase reports a non-zero failure count.

13. Step 8 — Post-upgrade validation

Once catupgrd.sql exits cleanly, restart the database in normal mode and confirm that the version, components, and tablespaces all line up with expectations.

[oracle@srv6 admin]$ sqlplus / as sysdba
SQL*Plus: Release 19.0.0.0 - Production    Version 19.3.0.0.0

SQL> SELECT name, open_mode, database_role FROM v$database;
NAME    OPEN_MODE     DATABASE_ROLE
PROD    READ WRITE    PRIMARY

SQL> SELECT version FROM v$instance;
VERSION
19.3.0.0.0

The dba_registry check is the moment of truth — every component installed on the source should now show a UPGRADED status:

ComponentVersionStatus
Oracle Database Catalog Views19.3.0.0.0UPGRADED
Oracle Database Packages and Types19.3.0.0.0UPGRADED
Oracle Real Application Clusters19.3.0.0.0OPTION OFF
Oracle XML Database19.3.0.0.0UPGRADED*

And the tablespace inventory should match what was captured before the upgrade — same names, all ONLINE:

TablespaceStatus
SYSTEMONLINE
SYSAUXONLINE
UNDOTBSONLINE
TEMPTSONLINE
USERDATAONLINE
TEST_TSONLINE

14. Post-upgrade tasks, validation checklist, and risks

14.1 Post-upgrade tasks

After the dictionary upgrade succeeds, three quick housekeeping steps put the database into a steady production state:

-- Recompile any objects left invalid by the upgrade
SQL> @?/rdbms/admin/utlrp.sql

-- Refresh dictionary statistics
SQL> EXEC DBMS_STATS.GATHER_DICTIONARY_STATS;

-- Final version confirmation
SQL> SELECT version FROM v$instance;

14.2 Validation checklist

  • Database opens successfully in READ WRITE mode.
  • All components show status as UPGRADED in dba_registry.
  • No invalid system objects remain after utlrp.sql.
  • Application connectivity has been tested end-to-end.
  • Backup jobs are validated and re-baselined against the 19c home.

14.3 Risks and mitigations

RiskMitigation
Upgrade failure mid-runFull RMAN backup of the source remains available for full restore on either server.
Missing archivelogs at recovery timeOpen with RESETLOGS after recovery completes; the upgrade-mode startup creates a new incarnation cleanly.
Incompatible parameters on 19cPre-upgrade validation of the PFILE; explicit compatible='19.3.0.0' setting before instance start.
Downtime exceeding the change windowRun the upgrade inside a planned maintenance window; benchmark catupgrd.sql on a clone first.

15. Take-away

A 12c-to-19c cross-server upgrade is, fundamentally, three things bolted into one operation: a clean RMAN restore on a new host, a parameter and home re-alignment, and a dictionary upgrade. None of the three is conceptually difficult, but each one has to be checked off in order — and each one wants its own pre- and post-state recorded so that the validation at the end is a comparison rather than a guess.

The run captured here completed phases 0–107 in 26 minutes 12 seconds for a small non-CDB. Larger, busier databases will take longer, mostly in the CATPROC and component phases — but the shape of the procedure does not change. Follow the nine steps, keep the source intact until the validation checklist comes back clean, and treat the post-upgrade utlrp.sql plus dictionary statistics gather as part of the upgrade rather than as afterthoughts.

What you get on the other side is exactly what 19c LTS is for: a stable platform on a current support cadence, ready to carry production workloads until the next planned move — which, for most estates, now means a follow-on upgrade to Oracle AI Database 23ai or 26ai. That, however, is a story for a different post.

Comments

Popular posts from this blog

Installation of Oracle Applications R12.1.1 on Linux and vmware

Oracle AVDF Installation and Setup Document

Disable Firewall on Oracle Linux 8